123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- import 'dart:async';
- import 'dart:convert';
- import 'package:bot_toast/bot_toast.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_hbb/common/hbbs/hbbs.dart';
- import 'package:flutter_hbb/models/ab_model.dart';
- import 'package:get/get.dart';
- import '../common.dart';
- import '../utils/http_service.dart' as http;
- import 'model.dart';
- import 'platform_model.dart';
- bool refreshingUser = false;
- class UserModel {
- final RxString userName = ''.obs;
- final RxBool isAdmin = false.obs;
- final RxString networkError = ''.obs;
- bool get isLogin => userName.isNotEmpty;
- WeakReference<FFI> parent;
- UserModel(this.parent) {
- userName.listen((p0) {
- // When user name becomes empty, show login button
- // When user name becomes non-empty:
- // For _updateLocalUserInfo, network error will be set later
- // For login success, should clear network error
- networkError.value = '';
- });
- }
- void refreshCurrentUser() async {
- if (bind.isDisableAccount()) return;
- networkError.value = '';
- final token = bind.mainGetLocalOption(key: 'access_token');
- if (token == '') {
- await updateOtherModels();
- return;
- }
- _updateLocalUserInfo();
- final url = await bind.mainGetApiServer();
- final body = {
- 'id': await bind.mainGetMyId(),
- 'uuid': await bind.mainGetUuid()
- };
- if (refreshingUser) return;
- try {
- refreshingUser = true;
- final http.Response response;
- try {
- response = await http.post(Uri.parse('$url/api/currentUser'),
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': 'Bearer $token'
- },
- body: json.encode(body));
- } catch (e) {
- networkError.value = e.toString();
- rethrow;
- }
- refreshingUser = false;
- final status = response.statusCode;
- if (status == 401 || status == 400) {
- reset(resetOther: status == 401);
- return;
- }
- final data = json.decode(utf8.decode(response.bodyBytes));
- final error = data['error'];
- if (error != null) {
- throw error;
- }
- final user = UserPayload.fromJson(data);
- _parseAndUpdateUser(user);
- } catch (e) {
- debugPrint('Failed to refreshCurrentUser: $e');
- } finally {
- refreshingUser = false;
- await updateOtherModels();
- }
- }
- static Map<String, dynamic>? getLocalUserInfo() {
- final userInfo = bind.mainGetLocalOption(key: 'user_info');
- if (userInfo == '') {
- return null;
- }
- try {
- return json.decode(userInfo);
- } catch (e) {
- debugPrint('Failed to get local user info "$userInfo": $e');
- }
- return null;
- }
- _updateLocalUserInfo() {
- final userInfo = getLocalUserInfo();
- if (userInfo != null) {
- userName.value = userInfo['name'];
- }
- }
- Future<void> reset({bool resetOther = false}) async {
- await bind.mainSetLocalOption(key: 'access_token', value: '');
- await bind.mainSetLocalOption(key: 'user_info', value: '');
- if (resetOther) {
- await gFFI.abModel.reset();
- await gFFI.groupModel.reset();
- }
- userName.value = '';
- }
- _parseAndUpdateUser(UserPayload user) {
- userName.value = user.name;
- isAdmin.value = user.isAdmin;
- bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(user));
- }
- // update ab and group status
- static Future<void> updateOtherModels() async {
- await Future.wait([
- gFFI.abModel.pullAb(force: ForcePullAb.listAndCurrent, quiet: false),
- gFFI.groupModel.pull()
- ]);
- }
- Future<void> logOut({String? apiServer}) async {
- final tag = gFFI.dialogManager.showLoading(translate('Waiting'));
- try {
- final url = apiServer ?? await bind.mainGetApiServer();
- final authHeaders = getHttpHeaders();
- authHeaders['Content-Type'] = "application/json";
- await http
- .post(Uri.parse('$url/api/logout'),
- body: jsonEncode({
- 'id': await bind.mainGetMyId(),
- 'uuid': await bind.mainGetUuid(),
- }),
- headers: authHeaders)
- .timeout(Duration(seconds: 2));
- } catch (e) {
- debugPrint("request /api/logout failed: err=$e");
- } finally {
- await reset(resetOther: true);
- gFFI.dialogManager.dismissByTag(tag);
- }
- }
- /// throw [RequestException]
- Future<LoginResponse> login(LoginRequest loginRequest) async {
- final url = await bind.mainGetApiServer();
- final resp = await http.post(Uri.parse('$url/api/login'),
- body: jsonEncode(loginRequest.toJson()));
- final Map<String, dynamic> body;
- try {
- body = jsonDecode(utf8.decode(resp.bodyBytes));
- } catch (e) {
- debugPrint("login: jsonDecode resp body failed: ${e.toString()}");
- if (resp.statusCode != 200) {
- BotToast.showText(
- contentColor: Colors.red, text: 'HTTP ${resp.statusCode}');
- }
- rethrow;
- }
- if (resp.statusCode != 200) {
- throw RequestException(resp.statusCode, body['error'] ?? '');
- }
- if (body['error'] != null) {
- throw RequestException(0, body['error']);
- }
- return getLoginResponseFromAuthBody(body);
- }
- LoginResponse getLoginResponseFromAuthBody(Map<String, dynamic> body) {
- final LoginResponse loginResponse;
- try {
- loginResponse = LoginResponse.fromJson(body);
- } catch (e) {
- debugPrint("login: jsonDecode LoginResponse failed: ${e.toString()}");
- rethrow;
- }
- if (loginResponse.user != null) {
- _parseAndUpdateUser(loginResponse.user!);
- }
- return loginResponse;
- }
- static Future<List<dynamic>> queryOidcLoginOptions() async {
- try {
- final url = await bind.mainGetApiServer();
- if (url.trim().isEmpty) return [];
- final resp = await http.get(Uri.parse('$url/api/login-options'));
- final List<String> ops = [];
- for (final item in jsonDecode(resp.body)) {
- ops.add(item as String);
- }
- for (final item in ops) {
- if (item.startsWith('common-oidc/')) {
- return jsonDecode(item.substring('common-oidc/'.length));
- }
- }
- return ops
- .where((item) => item.startsWith('oidc/'))
- .map((item) => {'name': item.substring('oidc/'.length)})
- .toList();
- } catch (e) {
- debugPrint(
- "queryOidcLoginOptions: jsonDecode resp body failed: ${e.toString()}");
- return [];
- }
- }
- }
|