group_model.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. import 'package:flutter/widgets.dart';
  2. import 'package:flutter_hbb/common.dart';
  3. import 'package:flutter_hbb/common/hbbs/hbbs.dart';
  4. import 'package:flutter_hbb/common/widgets/peers_view.dart';
  5. import 'package:flutter_hbb/models/model.dart';
  6. import 'package:flutter_hbb/models/peer_model.dart';
  7. import 'package:flutter_hbb/models/platform_model.dart';
  8. import 'package:get/get.dart';
  9. import 'dart:convert';
  10. import '../utils/http_service.dart' as http;
  11. class GroupModel {
  12. final RxBool groupLoading = false.obs;
  13. final RxString groupLoadError = "".obs;
  14. final RxList<DeviceGroupPayload> deviceGroups = RxList.empty(growable: true);
  15. final RxList<UserPayload> users = RxList.empty(growable: true);
  16. final RxList<Peer> peers = RxList.empty(growable: true);
  17. final RxBool isSelectedDeviceGroup = false.obs;
  18. final RxString selectedAccessibleItemName = ''.obs;
  19. final RxString searchAccessibleItemNameText = ''.obs;
  20. WeakReference<FFI> parent;
  21. var initialized = false;
  22. var _cacheLoadOnceFlag = false;
  23. var _statusCode = 200;
  24. bool get emtpy => deviceGroups.isEmpty && users.isEmpty && peers.isEmpty;
  25. late final Peers peersModel;
  26. GroupModel(this.parent) {
  27. peersModel = Peers(
  28. name: PeersModelName.group,
  29. getInitPeers: () => peers,
  30. loadEvent: LoadEvent.group);
  31. }
  32. Future<void> pull({force = true, quiet = false}) async {
  33. if (bind.isDisableGroupPanel()) return;
  34. if (!gFFI.userModel.isLogin || groupLoading.value) return;
  35. if (gFFI.userModel.networkError.isNotEmpty) return;
  36. if (!force && initialized) return;
  37. if (!quiet) {
  38. groupLoading.value = true;
  39. groupLoadError.value = "";
  40. }
  41. try {
  42. await _pull();
  43. } catch (_) {}
  44. groupLoading.value = false;
  45. initialized = true;
  46. platformFFI.tryHandle({'name': LoadEvent.group});
  47. if (_statusCode == 401) {
  48. gFFI.userModel.reset(resetOther: true);
  49. } else {
  50. _saveCache();
  51. }
  52. }
  53. Future<void> _pull() async {
  54. List<DeviceGroupPayload> tmpDeviceGroups = List.empty(growable: true);
  55. if (!await _getDeviceGroups(tmpDeviceGroups)) {
  56. // old hbbs doesn't support this api
  57. // return;
  58. }
  59. tmpDeviceGroups.sort((a, b) => a.name.compareTo(b.name));
  60. List<UserPayload> tmpUsers = List.empty(growable: true);
  61. if (!await _getUsers(tmpUsers)) {
  62. return;
  63. }
  64. List<Peer> tmpPeers = List.empty(growable: true);
  65. if (!await _getPeers(tmpPeers)) {
  66. return;
  67. }
  68. deviceGroups.value = tmpDeviceGroups;
  69. // me first
  70. var index = tmpUsers
  71. .indexWhere((user) => user.name == gFFI.userModel.userName.value);
  72. if (index != -1) {
  73. var user = tmpUsers.removeAt(index);
  74. tmpUsers.insert(0, user);
  75. }
  76. users.value = tmpUsers;
  77. if (!users.any((u) => u.name == selectedAccessibleItemName.value) &&
  78. !deviceGroups.any((d) => d.name == selectedAccessibleItemName.value)) {
  79. selectedAccessibleItemName.value = '';
  80. }
  81. // recover online
  82. final oldOnlineIDs = peers.where((e) => e.online).map((e) => e.id).toList();
  83. peers.value = tmpPeers;
  84. peers
  85. .where((e) => oldOnlineIDs.contains(e.id))
  86. .map((e) => e.online = true)
  87. .toList();
  88. groupLoadError.value = '';
  89. }
  90. Future<bool> _getDeviceGroups(
  91. List<DeviceGroupPayload> tmpDeviceGroups) async {
  92. final api = "${await bind.mainGetApiServer()}/api/device-group/accessible";
  93. try {
  94. var uri0 = Uri.parse(api);
  95. final pageSize = 100;
  96. var total = 0;
  97. int current = 0;
  98. do {
  99. current += 1;
  100. var uri = Uri(
  101. scheme: uri0.scheme,
  102. host: uri0.host,
  103. path: uri0.path,
  104. port: uri0.port,
  105. queryParameters: {
  106. 'current': current.toString(),
  107. 'pageSize': pageSize.toString(),
  108. });
  109. final resp = await http.get(uri, headers: getHttpHeaders());
  110. _statusCode = resp.statusCode;
  111. Map<String, dynamic> json =
  112. _jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode);
  113. if (json.containsKey('error')) {
  114. throw json['error'];
  115. }
  116. if (resp.statusCode != 200) {
  117. throw 'HTTP ${resp.statusCode}';
  118. }
  119. if (json.containsKey('total')) {
  120. if (total == 0) total = json['total'];
  121. if (json.containsKey('data')) {
  122. final data = json['data'];
  123. if (data is List) {
  124. for (final user in data) {
  125. final u = DeviceGroupPayload.fromJson(user);
  126. int index = tmpDeviceGroups.indexWhere((e) => e.name == u.name);
  127. if (index < 0) {
  128. tmpDeviceGroups.add(u);
  129. } else {
  130. tmpDeviceGroups[index] = u;
  131. }
  132. }
  133. }
  134. }
  135. }
  136. } while (current * pageSize < total);
  137. return true;
  138. } catch (err) {
  139. debugPrint('get accessible device groups: $err');
  140. // old hbbs doesn't support this api
  141. // groupLoadError.value =
  142. // '${translate('pull_group_failed_tip')}: ${translate(err.toString())}';
  143. }
  144. return false;
  145. }
  146. Future<bool> _getUsers(List<UserPayload> tmpUsers) async {
  147. final api = "${await bind.mainGetApiServer()}/api/users";
  148. try {
  149. var uri0 = Uri.parse(api);
  150. final pageSize = 100;
  151. var total = 0;
  152. int current = 0;
  153. do {
  154. current += 1;
  155. var uri = Uri(
  156. scheme: uri0.scheme,
  157. host: uri0.host,
  158. path: uri0.path,
  159. port: uri0.port,
  160. queryParameters: {
  161. 'current': current.toString(),
  162. 'pageSize': pageSize.toString(),
  163. 'accessible': '',
  164. 'status': '1',
  165. });
  166. final resp = await http.get(uri, headers: getHttpHeaders());
  167. _statusCode = resp.statusCode;
  168. Map<String, dynamic> json =
  169. _jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode);
  170. if (json.containsKey('error')) {
  171. if (json['error'] == 'Admin required!' ||
  172. json['error']
  173. .toString()
  174. .contains('ambiguous column name: status')) {
  175. throw translate('upgrade_rustdesk_server_pro_to_{1.1.10}_tip');
  176. } else {
  177. throw json['error'];
  178. }
  179. }
  180. if (resp.statusCode != 200) {
  181. throw 'HTTP ${resp.statusCode}';
  182. }
  183. if (json.containsKey('total')) {
  184. if (total == 0) total = json['total'];
  185. if (json.containsKey('data')) {
  186. final data = json['data'];
  187. if (data is List) {
  188. for (final user in data) {
  189. final u = UserPayload.fromJson(user);
  190. int index = tmpUsers.indexWhere((e) => e.name == u.name);
  191. if (index < 0) {
  192. tmpUsers.add(u);
  193. } else {
  194. tmpUsers[index] = u;
  195. }
  196. }
  197. }
  198. }
  199. }
  200. } while (current * pageSize < total);
  201. return true;
  202. } catch (err) {
  203. debugPrint('get accessible users: $err');
  204. groupLoadError.value =
  205. '${translate('pull_group_failed_tip')}: ${translate(err.toString())}';
  206. }
  207. return false;
  208. }
  209. Future<bool> _getPeers(List<Peer> tmpPeers) async {
  210. try {
  211. final api = "${await bind.mainGetApiServer()}/api/peers";
  212. var uri0 = Uri.parse(api);
  213. final pageSize = 100;
  214. var total = 0;
  215. int current = 0;
  216. do {
  217. current += 1;
  218. var queryParameters = {
  219. 'current': current.toString(),
  220. 'pageSize': pageSize.toString(),
  221. 'accessible': '',
  222. 'status': '1',
  223. };
  224. var uri = Uri(
  225. scheme: uri0.scheme,
  226. host: uri0.host,
  227. path: uri0.path,
  228. port: uri0.port,
  229. queryParameters: queryParameters);
  230. final resp = await http.get(uri, headers: getHttpHeaders());
  231. _statusCode = resp.statusCode;
  232. Map<String, dynamic> json =
  233. _jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode);
  234. if (json.containsKey('error')) {
  235. throw json['error'];
  236. }
  237. if (resp.statusCode != 200) {
  238. throw 'HTTP ${resp.statusCode}';
  239. }
  240. if (json.containsKey('total')) {
  241. if (total == 0) total = json['total'];
  242. if (json.containsKey('data')) {
  243. final data = json['data'];
  244. if (data is List) {
  245. for (final p in data) {
  246. final peerPayload = PeerPayload.fromJson(p);
  247. final peer = PeerPayload.toPeer(peerPayload);
  248. int index = tmpPeers.indexWhere((e) => e.id == peer.id);
  249. if (index < 0) {
  250. tmpPeers.add(peer);
  251. } else {
  252. tmpPeers[index] = peer;
  253. }
  254. }
  255. }
  256. }
  257. }
  258. } while (current * pageSize < total);
  259. return true;
  260. } catch (err) {
  261. debugPrint('get accessible peers: $err');
  262. groupLoadError.value =
  263. '${translate('pull_group_failed_tip')}: ${translate(err.toString())}';
  264. }
  265. return false;
  266. }
  267. Map<String, dynamic> _jsonDecodeResp(String body, int statusCode) {
  268. try {
  269. Map<String, dynamic> json = jsonDecode(body);
  270. return json;
  271. } catch (e) {
  272. final err = body.isNotEmpty && body.length < 128 ? body : e.toString();
  273. if (statusCode != 200) {
  274. throw 'HTTP $statusCode, $err';
  275. }
  276. throw err;
  277. }
  278. }
  279. void _saveCache() {
  280. try {
  281. final map = (<String, dynamic>{
  282. "access_token": bind.mainGetLocalOption(key: 'access_token'),
  283. "device_groups": deviceGroups.map((e) => e.toGroupCacheJson()).toList(),
  284. "users": users.map((e) => e.toGroupCacheJson()).toList(),
  285. 'peers': peers.map((e) => e.toGroupCacheJson()).toList()
  286. });
  287. bind.mainSaveGroup(json: jsonEncode(map));
  288. } catch (e) {
  289. debugPrint('group save:$e');
  290. }
  291. }
  292. Future<void> loadCache() async {
  293. try {
  294. if (_cacheLoadOnceFlag || groupLoading.value || initialized) return;
  295. _cacheLoadOnceFlag = true;
  296. final access_token = bind.mainGetLocalOption(key: 'access_token');
  297. if (access_token.isEmpty) return;
  298. final cache = await bind.mainLoadGroup();
  299. if (groupLoading.value) return;
  300. final data = jsonDecode(cache);
  301. if (data == null || data['access_token'] != access_token) return;
  302. deviceGroups.clear();
  303. users.clear();
  304. peers.clear();
  305. if (data['device_groups'] is List) {
  306. for (var u in data['device_groups']) {
  307. deviceGroups.add(DeviceGroupPayload.fromJson(u));
  308. }
  309. }
  310. if (data['users'] is List) {
  311. for (var u in data['users']) {
  312. users.add(UserPayload.fromJson(u));
  313. }
  314. }
  315. if (data['peers'] is List) {
  316. for (final peer in data['peers']) {
  317. peers.add(Peer.fromJson(peer));
  318. }
  319. }
  320. } catch (e) {
  321. debugPrint("load group cache: $e");
  322. }
  323. }
  324. reset() async {
  325. groupLoadError.value = '';
  326. deviceGroups.clear();
  327. users.clear();
  328. peers.clear();
  329. selectedAccessibleItemName.value = '';
  330. await bind.mainClearGroup();
  331. }
  332. }