From 5417796f3f9cd5652268e7129d76aebea0616f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Fri, 18 Apr 2025 19:14:36 +0200 Subject: [PATCH] do a few more remaining todos --- flutter/lib/api/api_client.dart | 10 ++-- flutter/lib/main.dart | 2 +- flutter/lib/pages/debug/debug_actions.dart | 54 ++++++++++++++++++- flutter/lib/pages/debug/debug_main.dart | 6 +-- .../lib/pages/debug/debug_request_view.dart | 20 ++++++- flutter/lib/state/app_auth.dart | 4 ++ flutter/lib/state/application_log.dart | 53 ++++++------------ flutter/lib/state/fb_message.dart | 10 +++- flutter/lib/state/request_log.dart | 22 +++++--- 9 files changed, 123 insertions(+), 58 deletions(-) diff --git a/flutter/lib/api/api_client.dart b/flutter/lib/api/api_client.dart index b89db78..3d53769 100644 --- a/flutter/lib/api/api_client.dart +++ b/flutter/lib/api/api_client.dart @@ -210,16 +210,16 @@ class APIClient { ); } - static Future updateClient(TokenSource auth, String clientID, String fcmToken, String agentModel, String? name, String agentVersion) async { + static Future updateClient(TokenSource auth, String clientID, {String? fcmToken, String? agentModel, String? name, String? agentVersion}) async { return await _request( name: 'updateClient', method: 'PUT', relURL: 'users/${auth.getUserID()}/clients/$clientID', jsonBody: { - 'fcm_token': fcmToken, - 'agent_model': agentModel, - 'agent_version': agentVersion, - 'name': name, + if (fcmToken != null) 'fcm_token': fcmToken, + if (agentModel != null) 'agent_model': agentModel, + if (agentVersion != null) 'agent_version': agentVersion, + if (name != null) 'name': name, }, fn: Client.fromJson, authToken: auth.getToken(), diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 99cec8c..2cdda2d 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -295,7 +295,7 @@ void setFirebaseToken(String fcmToken) async { acc.setClientAndClientID(newClient); await acc.save(); } else { - final newClient = await APIClient.updateClient(acc, client.clientID, fcmToken, Globals().deviceModel, Globals().hostname, Globals().version); + final newClient = await APIClient.updateClient(acc, client.clientID, fcmToken: fcmToken, agentModel: Globals().deviceModel, name: Globals().hostname, agentVersion: Globals().version); acc.setClientAndClientID(newClient); await acc.save(); } diff --git a/flutter/lib/pages/debug/debug_actions.dart b/flutter/lib/pages/debug/debug_actions.dart index aaef251..4b45fc5 100644 --- a/flutter/lib/pages/debug/debug_actions.dart +++ b/flutter/lib/pages/debug/debug_actions.dart @@ -1,4 +1,9 @@ +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:simplecloudnotifier/api/api_client.dart'; +import 'package:simplecloudnotifier/state/app_auth.dart'; +import 'package:simplecloudnotifier/state/application_log.dart'; import 'package:simplecloudnotifier/utils/notifier.dart'; import 'package:simplecloudnotifier/utils/toaster.dart'; import 'package:simplecloudnotifier/utils/ui.dart'; @@ -48,6 +53,12 @@ class _DebugActionsPageState extends State { text: 'Show Simple Notification', ), SizedBox(height: 20), + UI.button( + big: false, + onPressed: _copyToken, + text: 'Query+Copy FCM Token', + ), + SizedBox(height: 4), UI.button( big: false, onPressed: _sendTokenToServer, @@ -66,7 +77,46 @@ class _DebugActionsPageState extends State { ); } - void _sendTokenToServer() { - //TODO + void _sendTokenToServer() async { + try { + final auth = AppAuth(); + + final clientID = auth.getClientID(); + if (clientID == null) { + Toaster.error("Error", "No Client set"); + return; + } + + final fcmToken = await FirebaseMessaging.instance.getToken(); + if (fcmToken == null) { + Toaster.error("Error", "No FCM token returned from Firebase"); + return; + } + + var newClient = await APIClient.updateClient(auth, clientID, fcmToken: fcmToken); + auth.setClientAndClientID(newClient); + + Toaster.success("Success", "Token sent to server"); + } catch (exc, trace) { + Toaster.error("Error", "An error occurred while sending the token: ${exc.toString()}"); + ApplicationLog.error("An error occurred while sending the token: ${exc.toString()}", trace: trace); + } + } + + void _copyToken() async { + try { + final fcmToken = await FirebaseMessaging.instance.getToken(); + if (fcmToken == null) { + Toaster.error("Error", "No FCM token returned from Firebase"); + return; + } + + Clipboard.setData(new ClipboardData(text: fcmToken)); + Toaster.info("Clipboard", 'Copied text to Clipboard'); + print('================= [CLIPBOARD] =================\n${fcmToken}\n================= [/CLIPBOARD] ================='); + } catch (exc, trace) { + Toaster.error("Error", "An error occurred while sending the token: ${exc.toString()}"); + ApplicationLog.error("An error occurred while sending the token: ${exc.toString()}", trace: trace); + } } } diff --git a/flutter/lib/pages/debug/debug_main.dart b/flutter/lib/pages/debug/debug_main.dart index 74a7560..c94df2e 100644 --- a/flutter/lib/pages/debug/debug_main.dart +++ b/flutter/lib/pages/debug/debug_main.dart @@ -23,7 +23,7 @@ class _DebugMainPageState extends State { DebugMainPageSubPage.actions: DebugActionsPage(), }; - DebugMainPageSubPage _subPage = DebugMainPageSubPage.colors; + DebugMainPageSubPage _subPage = DebugMainPageSubPage.logs; @override Widget build(BuildContext context) { @@ -53,11 +53,11 @@ class _DebugMainPageState extends State { return SegmentedButton( showSelectedIcon: false, segments: const >[ - ButtonSegment(value: DebugMainPageSubPage.colors, icon: Icon(FontAwesomeIcons.solidPaintRoller, size: 14)), + ButtonSegment(value: DebugMainPageSubPage.logs, icon: Icon(FontAwesomeIcons.solidFileLines, size: 14)), ButtonSegment(value: DebugMainPageSubPage.actions, icon: Icon(FontAwesomeIcons.solidHammer, size: 14)), ButtonSegment(value: DebugMainPageSubPage.requests, icon: Icon(FontAwesomeIcons.solidNetworkWired, size: 14)), ButtonSegment(value: DebugMainPageSubPage.persistence, icon: Icon(FontAwesomeIcons.solidDatabase, size: 14)), - ButtonSegment(value: DebugMainPageSubPage.logs, icon: Icon(FontAwesomeIcons.solidFileLines, size: 14)), + ButtonSegment(value: DebugMainPageSubPage.colors, icon: Icon(FontAwesomeIcons.solidPaintRoller, size: 14)), ], style: ButtonStyle( padding: WidgetStateProperty.all(EdgeInsets.fromLTRB(0, 0, 0, 0)), diff --git a/flutter/lib/pages/debug/debug_request_view.dart b/flutter/lib/pages/debug/debug_request_view.dart index 0ca48fc..65b6d55 100644 --- a/flutter/lib/pages/debug/debug_request_view.dart +++ b/flutter/lib/pages/debug/debug_request_view.dart @@ -32,7 +32,25 @@ class _DebugRequestViewPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.start, - children: [...buildRow(context, "name", "Name", widget.request.name), ...buildRow(context, "timestampStart", "Timestamp (Start)", widget.request.timestampStart.toString()), ...buildRow(context, "timestampEnd", "Timestamp (End)", widget.request.timestampEnd.toString()), ...buildRow(context, "duration", "Duration", widget.request.timestampEnd.difference(widget.request.timestampStart).toString()), Divider(), ...buildRow(context, "method", "Method", widget.request.method), ...buildRow(context, "url", "URL", widget.request.url, mono: true), if (widget.request.requestHeaders.isNotEmpty) ...buildRow(context, "request_headers", "Request->Headers", widget.request.requestHeaders.entries.map((v) => '${v.key} = ${v.value}').join('\n'), mono: true), if (widget.request.requestBody != '') ...buildRow(context, "request_body", "Request->Body", widget.request.requestBody, mono: true, json: true), Divider(), if (widget.request.responseStatusCode != 0) ...buildRow(context, "response_statuscode", "Response->Statuscode", widget.request.responseStatusCode.toString()), if (widget.request.responseBody != '') ...buildRow(context, "response_body", "Reponse->Body", widget.request.responseBody, mono: true, json: true), if (widget.request.responseHeaders.isNotEmpty) ...buildRow(context, "response_headers", "Reponse->Headers", widget.request.responseHeaders.entries.map((v) => '${v.key} = ${v.value}').join('\n'), mono: true, json: true), Divider(), if (widget.request.error != '') ...buildRow(context, "error", "Error", widget.request.error, mono: true), if (widget.request.stackTrace != '') ...buildRow(context, "trace", "Stacktrace", widget.request.stackTrace, mono: true), Divider(), UI.button(text: "Copy as curl", onPressed: _copyCurl, tonal: true)], + children: [ + ...buildRow(context, "name", "Name", widget.request.name), + ...buildRow(context, "timestampStart", "Timestamp (Start)", widget.request.timestampStart.toString()), + ...buildRow(context, "timestampEnd", "Timestamp (End)", widget.request.timestampEnd.toString()), + ...buildRow(context, "duration", "Duration", widget.request.timestampEnd.difference(widget.request.timestampStart).toString()), + Divider(), + ...buildRow(context, "method", "Method", widget.request.method), + ...buildRow(context, "url", "URL", widget.request.url, mono: true), + if (widget.request.requestHeaders.isNotEmpty) ...buildRow(context, "request_headers", "Request->Headers", widget.request.requestHeaders.entries.map((v) => '${v.key} = ${v.value}').join('\n'), mono: true), + if (widget.request.requestBody != '') ...buildRow(context, "request_body", "Request->Body", widget.request.requestBody, mono: true, json: true), + UI.button(text: "Copy request as curl", onPressed: _copyCurl, tonal: true), + Divider(), + if (widget.request.responseStatusCode != 0) ...buildRow(context, "response_statuscode", "Response->Statuscode", widget.request.responseStatusCode.toString()), + if (widget.request.responseBody != '') ...buildRow(context, "response_body", "Reponse->Body", widget.request.responseBody, mono: true, json: true), + if (widget.request.responseHeaders.isNotEmpty) ...buildRow(context, "response_headers", "Reponse->Headers", widget.request.responseHeaders.entries.map((v) => '${v.key} = ${v.value}').join('\n'), mono: true, json: false), + Divider(), + if (widget.request.error != '') ...buildRow(context, "error", "Error", widget.request.error, mono: true), + if (widget.request.stackTrace != '') ...buildRow(context, "trace", "Stacktrace", widget.request.stackTrace, mono: true), + ], ), ), ), diff --git a/flutter/lib/state/app_auth.dart b/flutter/lib/state/app_auth.dart index cb74d8b..f41b1c9 100644 --- a/flutter/lib/state/app_auth.dart +++ b/flutter/lib/state/app_auth.dart @@ -229,4 +229,8 @@ class AppAuth extends ChangeNotifier implements TokenSource { String getUserID() { return _userID!; } + + String? getClientID() { + return _clientID; + } } diff --git a/flutter/lib/state/application_log.dart b/flutter/lib/state/application_log.dart index 0f21e33..3246e0a 100644 --- a/flutter/lib/state/application_log.dart +++ b/flutter/lib/state/application_log.dart @@ -10,69 +10,44 @@ import 'package:path/path.dart' as path; part 'application_log.g.dart'; class ApplicationLog { - //TODO max size, auto clear old + static const MAX_SIZE = 2048; static void debug(String message, {String? additional, StackTrace? trace}) { (additional != null && additional != '') ? print('[DEBUG] ${message}: ${additional}') : print('[DEBUG] ${message}'); - if (!Hive.isBoxOpen('scn-logs')) return; - Hive.box('scn-logs').add(SCNLog( - id: Xid().toString(), - timestamp: DateTime.now(), - level: SCNLogLevel.debug, - message: message, - additional: additional ?? '', - trace: trace?.toString() ?? '', - )); + _logToBox(SCNLogLevel.debug, message, additional, trace); } static void info(String message, {String? additional, StackTrace? trace}) { (additional != null && additional != '') ? print('[INFO] ${message}: ${additional}') : print('[INFO] ${message}'); - if (!Hive.isBoxOpen('scn-logs')) return; - Hive.box('scn-logs').add(SCNLog( - id: Xid().toString(), - timestamp: DateTime.now(), - level: SCNLogLevel.info, - message: message, - additional: additional ?? '', - trace: trace?.toString() ?? '', - )); + _logToBox(SCNLogLevel.info, message, additional, trace); } static void warn(String message, {String? additional, StackTrace? trace}) { (additional != null && additional != '') ? print('[WARN] ${message}: ${additional}') : print('[WARN] ${message}'); - if (!Hive.isBoxOpen('scn-logs')) return; - Hive.box('scn-logs').add(SCNLog( - id: Xid().toString(), - timestamp: DateTime.now(), - level: SCNLogLevel.warning, - message: message, - additional: additional ?? '', - trace: trace?.toString() ?? '', - )); + _logToBox(SCNLogLevel.warning, message, additional, trace); } static void error(String message, {String? additional, StackTrace? trace}) { (additional != null && additional != '') ? print('[ERROR] ${message}: ${additional}') : print('[ERROR] ${message}'); - if (!Hive.isBoxOpen('scn-logs')) return; - Hive.box('scn-logs').add(SCNLog( - id: Xid().toString(), - timestamp: DateTime.now(), - level: SCNLogLevel.error, - message: message, - additional: additional ?? '', - trace: trace?.toString() ?? '', - )); + _logToBox(SCNLogLevel.error, message, additional, trace); } static void fatal(String message, {String? additional, StackTrace? trace}) { (additional != null && additional != '') ? print('[FATAL] ${message}: ${additional}') : print('[FATAL] ${message}'); + _logToBox(SCNLogLevel.fatal, message, additional, trace); + } + + static void _logToBox(SCNLogLevel lvl, String message, String? additional, StackTrace? trace) { if (!Hive.isBoxOpen('scn-logs')) return; - Hive.box('scn-logs').add(SCNLog( + + final box = Hive.box('scn-logs'); + + box.add(SCNLog( id: Xid().toString(), timestamp: DateTime.now(), level: SCNLogLevel.fatal, @@ -80,6 +55,8 @@ class ApplicationLog { additional: additional ?? '', trace: trace?.toString() ?? '', )); + + while (box.length > MAX_SIZE) box.deleteAt(0); } static void writeRawFailure(String message, Map extraData) async { diff --git a/flutter/lib/state/fb_message.dart b/flutter/lib/state/fb_message.dart index ea913c1..47c93ee 100644 --- a/flutter/lib/state/fb_message.dart +++ b/flutter/lib/state/fb_message.dart @@ -5,10 +5,16 @@ import 'package:simplecloudnotifier/state/interfaces.dart'; part 'fb_message.g.dart'; class FBMessageLog { - //TODO max size, auto clear old + static const MAX_SIZE = 512; static void insert(RemoteMessage msg) { - Hive.box('scn-fb-messages').add(FBMessage.fromRemoteMessage(msg)); + if (!Hive.isBoxOpen('scn-fb-messages')) return; + + final box = Hive.box('scn-fb-messages'); + + box.add(FBMessage.fromRemoteMessage(msg)); + + while (box.length > MAX_SIZE) box.deleteAt(0); } } diff --git a/flutter/lib/state/request_log.dart b/flutter/lib/state/request_log.dart index 233fe68..99cc0ca 100644 --- a/flutter/lib/state/request_log.dart +++ b/flutter/lib/state/request_log.dart @@ -6,10 +6,10 @@ import 'package:xid/xid.dart'; part 'request_log.g.dart'; class RequestLog { - //TODO max size, auto clear old + static const MAX_SIZE = 1024; static void addRequestException(String name, DateTime tStart, String method, Uri uri, String reqbody, Map reqheaders, dynamic e, StackTrace trace) { - Hive.box('scn-requests').add(SCNRequest( + _logToBox(SCNRequest( id: Xid().toString(), timestampStart: tStart, timestampEnd: DateTime.now(), @@ -28,7 +28,7 @@ class RequestLog { } static void addRequestAPIError(String name, DateTime t0, String method, Uri uri, String reqbody, Map reqheaders, int responseStatusCode, String responseBody, Map responseHeaders, APIError apierr) { - Hive.box('scn-requests').add(SCNRequest( + _logToBox(SCNRequest( id: Xid().toString(), timestampStart: t0, timestampEnd: DateTime.now(), @@ -47,7 +47,7 @@ class RequestLog { } static void addRequestErrorStatuscode(String name, DateTime t0, String method, Uri uri, String reqbody, Map reqheaders, int responseStatusCode, String responseBody, Map responseHeaders) { - Hive.box('scn-requests').add(SCNRequest( + _logToBox(SCNRequest( id: Xid().toString(), timestampStart: t0, timestampEnd: DateTime.now(), @@ -66,7 +66,7 @@ class RequestLog { } static void addRequestSuccess(String name, DateTime t0, String method, Uri uri, String reqbody, Map reqheaders, int responseStatusCode, String responseBody, Map responseHeaders) { - Hive.box('scn-requests').add(SCNRequest( + _logToBox(SCNRequest( id: Xid().toString(), timestampStart: t0, timestampEnd: DateTime.now(), @@ -85,7 +85,7 @@ class RequestLog { } static void addRequestDecodeError(String name, DateTime t0, String method, Uri uri, String reqbody, Map reqheaders, int responseStatusCode, String responseBody, Map responseHeaders, Object exc, StackTrace trace) { - Hive.box('scn-requests').add(SCNRequest( + _logToBox(SCNRequest( id: Xid().toString(), timestampStart: t0, timestampEnd: DateTime.now(), @@ -102,6 +102,16 @@ class RequestLog { stackTrace: trace.toString(), )); } + + static void _logToBox(SCNRequest v) { + if (!Hive.isBoxOpen('scn-requests')) return; + + final box = Hive.box('scn-requests'); + + box.add(v); + + while (box.length > MAX_SIZE) box.deleteAt(0); + } } @HiveType(typeId: 100)