From 6148e889aab157c6aa0ce05a6acb7ec03b03cf6c Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 21 Aug 2024 15:25:50 +0800 Subject: [PATCH] :goal_net: Better unauthorized exceptions --- lib/bootstrapper.dart | 22 +++++++------ lib/exceptions/unauthorized.dart | 6 ++++ lib/exts.dart | 9 ++++-- lib/providers/account_status.dart | 10 +++--- lib/providers/auth.dart | 3 +- lib/providers/call.dart | 3 +- lib/providers/content/attachment.dart | 11 ++++--- lib/providers/content/channel.dart | 19 +++++------ lib/providers/content/posts.dart | 3 +- lib/providers/content/realm.dart | 7 ++-- lib/providers/websocket.dart | 32 +++++++++---------- lib/screens/account/personalize.dart | 4 +-- lib/translations/en_us.dart | 2 ++ lib/translations/zh_cn.dart | 1 + .../attachments/attachment_attr_editor.dart | 4 +-- 15 files changed, 79 insertions(+), 57 deletions(-) create mode 100644 lib/exceptions/unauthorized.dart diff --git a/lib/bootstrapper.dart b/lib/bootstrapper.dart index 3544d9c..a79d1fa 100644 --- a/lib/bootstrapper.dart +++ b/lib/bootstrapper.dart @@ -112,15 +112,19 @@ class _BootstrapperShellState extends State { label: 'bsPreparingData', action: () async { final AuthProvider auth = Get.find(); - await Future.wait([ - Get.find().refreshAvailableStickers(), - if (auth.isAuthorized.isTrue) - Get.find().refreshAvailableChannel(), - if (auth.isAuthorized.isTrue) - Get.find().refreshRelativeList(), - if (auth.isAuthorized.isTrue) - Get.find().refreshAvailableRealms(), - ]); + try { + await Future.wait([ + Get.find().refreshAvailableStickers(), + if (auth.isAuthorized.isTrue) + Get.find().refreshAvailableChannel(), + if (auth.isAuthorized.isTrue) + Get.find().refreshRelativeList(), + if (auth.isAuthorized.isTrue) + Get.find().refreshAvailableRealms(), + ]); + } catch (e) { + context.showErrorDialog(e); + } }, ), ( diff --git a/lib/exceptions/unauthorized.dart b/lib/exceptions/unauthorized.dart new file mode 100644 index 0000000..e888ade --- /dev/null +++ b/lib/exceptions/unauthorized.dart @@ -0,0 +1,6 @@ +class UnauthorizedException implements Exception { + const UnauthorizedException(); + + @override + String toString() => 'Unauthorized'; +} diff --git a/lib/exts.dart b/lib/exts.dart index 484b1ae..c33a01b 100644 --- a/lib/exts.dart +++ b/lib/exts.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:solian/exceptions/unauthorized.dart'; extension SolianExtenions on BuildContext { void showSnackbar(String content, {SnackBarAction? action}) { @@ -48,15 +49,17 @@ extension SolianExtenions on BuildContext { } Future showErrorDialog(dynamic exception) { - var stack = StackTrace.current; - var stackTrace = '$stack'; + Widget content = Text(exception.toString().capitalize!); + if (exception is UnauthorizedException) { + content = Text('errorHappenedUnauthorized'.tr); + } return showDialog( useRootNavigator: true, context: this, builder: (ctx) => AlertDialog( title: Text('errorHappened'.tr), - content: Text('${exception.toString().capitalize!}\n\nStack Trace: $stackTrace'), + content: content, actions: [ TextButton( onPressed: () => Navigator.pop(ctx), diff --git a/lib/providers/account_status.dart b/lib/providers/account_status.dart index c519348..27fab17 100644 --- a/lib/providers/account_status.dart +++ b/lib/providers/account_status.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/models/account_status.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/services.dart'; @@ -33,15 +34,14 @@ class StatusProvider extends GetConnect { Future getCurrentStatus() async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('auth'); return await client.get('/users/me/status'); } - Future getSomeoneStatus(String name) => - get('/users/$name/status'); + Future getSomeoneStatus(String name) => get('/users/$name/status'); Future setStatus( String type, @@ -53,7 +53,7 @@ class StatusProvider extends GetConnect { DateTime? clearAt, }) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('auth'); @@ -82,7 +82,7 @@ class StatusProvider extends GetConnect { Future clearStatus() async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('auth'); diff --git a/lib/providers/auth.dart b/lib/providers/auth.dart index 622be6b..1f96766 100644 --- a/lib/providers/auth.dart +++ b/lib/providers/auth.dart @@ -7,6 +7,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:get/get.dart'; import 'package:get/get_connect/http/src/request/request.dart'; import 'package:solian/controllers/chat_events_controller.dart'; +import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/providers/websocket.dart'; import 'package:solian/services.dart'; @@ -128,7 +129,7 @@ class AuthProvider extends GetConnect { } Future ensureCredentials() async { - if (isAuthorized.isFalse) throw Exception('unauthorized'); + if (isAuthorized.isFalse) throw const UnauthorizedException(); if (credentials == null) await loadCredentials(); if (credentials!.isExpired) { diff --git a/lib/providers/call.dart b/lib/providers/call.dart index 0d60efd..80c436e 100644 --- a/lib/providers/call.dart +++ b/lib/providers/call.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:solian/exceptions/unauthorized.dart'; import 'package:livekit_client/livekit_client.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:solian/models/call.dart'; @@ -88,7 +89,7 @@ class ChatCallProvider extends GetxController { Future<(String, String)> getRoomToken() async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('messaging'); diff --git a/lib/providers/content/attachment.dart b/lib/providers/content/attachment.dart index 20459c4..45b8bdd 100644 --- a/lib/providers/content/attachment.dart +++ b/lib/providers/content/attachment.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:get/get.dart'; +import 'package:solian/exceptions/unauthorized.dart'; import 'package:path/path.dart'; import 'package:solian/models/attachment.dart'; import 'package:solian/models/pagination.dart'; @@ -89,7 +90,7 @@ class AttachmentProvider extends GetConnect { Map? metadata, ) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient( 'uc', @@ -131,7 +132,7 @@ class AttachmentProvider extends GetConnect { Map? metadata, ) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('uc'); @@ -169,7 +170,7 @@ class AttachmentProvider extends GetConnect { String cid, ) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient( 'uc', @@ -193,7 +194,7 @@ class AttachmentProvider extends GetConnect { bool isMature = false, }) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('files'); @@ -211,7 +212,7 @@ class AttachmentProvider extends GetConnect { Future deleteAttachment(int id) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('files'); diff --git a/lib/providers/content/channel.dart b/lib/providers/content/channel.dart index ce173ac..3dbf0a7 100644 --- a/lib/providers/content/channel.dart +++ b/lib/providers/content/channel.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/models/channel.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/widgets/account/relative_select.dart'; @@ -16,7 +17,7 @@ class ChannelProvider extends GetxController { Future refreshAvailableChannel() async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); isLoading.value = true; final resp = await listAvailableChannel(); @@ -29,7 +30,7 @@ class ChannelProvider extends GetxController { Future getChannel(String alias, {String realm = 'global'}) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('messaging'); @@ -44,7 +45,7 @@ class ChannelProvider extends GetxController { Future getMyChannelProfile(String alias, {String realm = 'global'}) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('messaging'); @@ -59,7 +60,7 @@ class ChannelProvider extends GetxController { Future getChannelOngoingCall(String alias, {String realm = 'global'}) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('messaging'); @@ -75,7 +76,7 @@ class ChannelProvider extends GetxController { Future listChannel({String scope = 'global'}) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('messaging'); @@ -89,7 +90,7 @@ class ChannelProvider extends GetxController { Future listAvailableChannel({String realm = 'global'}) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('messaging'); @@ -103,7 +104,7 @@ class ChannelProvider extends GetxController { Future createChannel(String scope, dynamic payload) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('messaging'); @@ -118,7 +119,7 @@ class ChannelProvider extends GetxController { Future createDirectChannel( BuildContext context, String scope) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final related = await showModalBottomSheet( useRootNavigator: true, @@ -149,7 +150,7 @@ class ChannelProvider extends GetxController { Future updateChannel(String scope, int id, dynamic payload) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final client = auth.configureClient('messaging'); diff --git a/lib/providers/content/posts.dart b/lib/providers/content/posts.dart index 5d6744e..332de98 100644 --- a/lib/providers/content/posts.dart +++ b/lib/providers/content/posts.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/services.dart'; @@ -36,7 +37,7 @@ class PostProvider extends GetConnect { Future listDraft(int page) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw UnauthorizedException(); final queries = [ 'take=${10}', diff --git a/lib/providers/content/realm.dart b/lib/providers/content/realm.dart index 9432e57..f63bf21 100644 --- a/lib/providers/content/realm.dart +++ b/lib/providers/content/realm.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/models/realm.dart'; import 'package:solian/providers/auth.dart'; @@ -8,7 +9,7 @@ class RealmProvider extends GetxController { Future refreshAvailableRealms() async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); isLoading.value = true; final resp = await listAvailableRealm(); @@ -21,7 +22,7 @@ class RealmProvider extends GetxController { Future getRealm(String alias) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('auth'); @@ -35,7 +36,7 @@ class RealmProvider extends GetxController { Future listAvailableRealm() async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('auth'); diff --git a/lib/providers/websocket.dart b/lib/providers/websocket.dart index 91ca987..abcc4c8 100644 --- a/lib/providers/websocket.dart +++ b/lib/providers/websocket.dart @@ -50,31 +50,31 @@ class WebSocketProvider extends GetxController { } final AuthProvider auth = Get.find(); - await auth.ensureCredentials(); - - if (auth.credentials == null) await auth.loadCredentials(); - - final uri = Uri.parse(ServiceFinder.buildUrl( - 'dealer', - '/api/ws?tk=${auth.credentials!.accessToken}', - ).replaceFirst('http', 'ws')); - - isConnecting.value = true; try { + await auth.ensureCredentials(); + + final uri = Uri.parse(ServiceFinder.buildUrl( + 'dealer', + '/api/ws?tk=${auth.credentials!.accessToken}', + ).replaceFirst('http', 'ws')); + + isConnecting.value = true; + websocket = WebSocketChannel.connect(uri); await websocket?.ready; - } catch (e) { + listen(); + + isConnected.value = true; + } catch (err) { + log('Unable connect dealer via websocket... $err'); if (!noRetry) { await auth.refreshCredentials(); return connect(noRetry: true); } + } finally { + isConnecting.value = false; } - - listen(); - - isConnected.value = true; - isConnecting.value = false; } void disconnect() { diff --git a/lib/screens/account/personalize.dart b/lib/screens/account/personalize.dart index ec02c92..f813bf1 100644 --- a/lib/screens/account/personalize.dart +++ b/lib/screens/account/personalize.dart @@ -109,11 +109,11 @@ class _PersonalizeScreenState extends State { setState(() => _isBusy = true); - final AttachmentProvider provider = Get.find(); + final AttachmentProvider attach = Get.find(); Attachment? attachResult; try { - attachResult = await provider.createAttachmentDirectly( + attachResult = await attach.createAttachmentDirectly( await file.readAsBytes(), file.path, 'avatar', diff --git a/lib/translations/en_us.dart b/lib/translations/en_us.dart index 7e3c35f..db05ab7 100644 --- a/lib/translations/en_us.dart +++ b/lib/translations/en_us.dart @@ -41,6 +41,8 @@ const i18nEnglish = { 'openInBrowser': 'Open in browser', 'notification': 'Notification', 'errorHappened': 'An error occurred', + 'errorHappenedUnauthorized': + 'Unauthorized request, please sign in or try resign in.', 'forgotPassword': 'Forgot password', 'email': 'Email', 'username': 'Username', diff --git a/lib/translations/zh_cn.dart b/lib/translations/zh_cn.dart index 7f54f02..b471204 100644 --- a/lib/translations/zh_cn.dart +++ b/lib/translations/zh_cn.dart @@ -41,6 +41,7 @@ const i18nSimplifiedChinese = { 'openInBrowser': '在浏览器中打开', 'notification': '通知', 'errorHappened': '发生错误了', + 'errorHappenedUnauthorized': '未经授权的请求,请登录或尝试重新登录。', 'forgotPassword': '忘记密码', 'email': '邮件地址', 'username': '用户名', diff --git a/lib/widgets/attachments/attachment_attr_editor.dart b/lib/widgets/attachments/attachment_attr_editor.dart index 642aedc..8a727c8 100644 --- a/lib/widgets/attachments/attachment_attr_editor.dart +++ b/lib/widgets/attachments/attachment_attr_editor.dart @@ -28,11 +28,11 @@ class _AttachmentAttrEditorDialogState bool _isMature = false; Future _updateAttachment() async { - final AttachmentProvider provider = Get.find(); + final AttachmentProvider attach = Get.find(); setState(() => _isBusy = true); try { - final resp = await provider.updateAttachment( + final resp = await attach.updateAttachment( widget.item.id, _altController.value.text, isMature: _isMature,