Hive, requestlog, etc
This commit is contained in:
parent
227d7871c2
commit
7e347a70c2
10
flutter/Makefile
Normal file
10
flutter/Makefile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
run:
|
||||||
|
dart run build_runner build
|
||||||
|
flutter run
|
||||||
|
|
||||||
|
|
||||||
|
gen:
|
||||||
|
dart run build_runner build
|
||||||
|
|
@ -11,7 +11,7 @@
|
|||||||
- https://pub.dev/packages/sqflite
|
- https://pub.dev/packages/sqflite
|
||||||
- https://pub.dev/packages/sqflite_common_ffi
|
- https://pub.dev/packages/sqflite_common_ffi
|
||||||
|
|
||||||
|
- https://pub.dev/packages/hive
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:fl_toast/fl_toast.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:simplecloudnotifier/models/api_error.dart';
|
||||||
import 'package:simplecloudnotifier/models/key_token_auth.dart';
|
import 'package:simplecloudnotifier/models/key_token_auth.dart';
|
||||||
import 'package:simplecloudnotifier/models/user.dart';
|
import 'package:simplecloudnotifier/models/user.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/globals.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/request_log.dart';
|
||||||
|
|
||||||
import '../models/channel.dart';
|
import '../models/channel.dart';
|
||||||
import '../models/message.dart';
|
import '../models/message.dart';
|
||||||
@ -21,69 +26,141 @@ enum ChannelSelector {
|
|||||||
class APIClient {
|
class APIClient {
|
||||||
static const String _base = 'https://simplecloudnotifier.de/api/v2';
|
static const String _base = 'https://simplecloudnotifier.de/api/v2';
|
||||||
|
|
||||||
static Future<bool> verifyToken(String uid, String tok) async {
|
static Future<T> _request<T>({
|
||||||
final uri = Uri.parse('$_base/users/$uid');
|
required String name,
|
||||||
final response = await http.get(uri, headers: {'Authorization': 'SCN $tok'});
|
required String method,
|
||||||
|
required String relURL,
|
||||||
|
Map<String, String>? query,
|
||||||
|
required T Function(Map<String, dynamic> json)? fn,
|
||||||
|
dynamic jsonBody,
|
||||||
|
KeyTokenAuth? auth,
|
||||||
|
Map<String, String>? header,
|
||||||
|
}) async {
|
||||||
|
final t0 = DateTime.now();
|
||||||
|
|
||||||
return (response.statusCode == 200);
|
final uri = Uri.parse('$_base/$relURL').replace(queryParameters: query ?? {});
|
||||||
}
|
|
||||||
|
|
||||||
static Future<User> getUser(String uid, String tok) async {
|
final req = http.Request(method, uri);
|
||||||
final uri = Uri.parse('$_base/users/$uid');
|
|
||||||
final response = await http.get(uri, headers: {'Authorization': 'SCN $tok'});
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (jsonBody != null) {
|
||||||
throw Exception('API request failed');
|
req.body = jsonEncode(jsonBody);
|
||||||
|
req.headers['Content-Type'] = 'application/json';
|
||||||
}
|
}
|
||||||
|
|
||||||
return User.fromJson(jsonDecode(response.body));
|
if (auth != null) {
|
||||||
|
req.headers['Authorization'] = 'SCN ${auth.token}';
|
||||||
|
}
|
||||||
|
|
||||||
|
req.headers['User-Agent'] = 'simplecloudnotifier/flutter/${Globals().platform.replaceAll(' ', '_')} ${Globals().version}+${Globals().buildNumber}';
|
||||||
|
|
||||||
|
if (header != null && !header.isEmpty) {
|
||||||
|
req.headers.addAll(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
int responseStatusCode = 0;
|
||||||
|
String responseBody = '';
|
||||||
|
Map<String, String> responseHeaders = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await req.send();
|
||||||
|
responseBody = await response.stream.bytesToString();
|
||||||
|
responseStatusCode = response.statusCode;
|
||||||
|
responseHeaders = response.headers;
|
||||||
|
} catch (exc, trace) {
|
||||||
|
RequestLog.addRequestException(name, t0, method, uri, req.body, req.headers, exc, trace);
|
||||||
|
showPlatformToast(child: Text('Request "${name}" is fehlgeschlagen'), context: ToastProvider.context);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseStatusCode != 200) {
|
||||||
|
try {
|
||||||
|
final apierr = APIError.fromJson(jsonDecode(responseBody));
|
||||||
|
|
||||||
|
RequestLog.addRequestAPIError(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders, apierr);
|
||||||
|
showPlatformToast(child: Text('Request "${name}" is fehlgeschlagen'), context: ToastProvider.context);
|
||||||
|
throw Exception(apierr.message);
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
RequestLog.addRequestErrorStatuscode(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders);
|
||||||
|
showPlatformToast(child: Text('Request "${name}" is fehlgeschlagen'), context: ToastProvider.context);
|
||||||
|
throw Exception('API request failed with status code ${responseStatusCode}');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final data = jsonDecode(responseBody);
|
||||||
|
|
||||||
|
if (fn != null) {
|
||||||
|
final result = fn(data);
|
||||||
|
RequestLog.addRequestSuccess(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
RequestLog.addRequestSuccess(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders);
|
||||||
|
return null as T;
|
||||||
|
}
|
||||||
|
} catch (exc, trace) {
|
||||||
|
RequestLog.addRequestDecodeError(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders, exc, trace);
|
||||||
|
showPlatformToast(child: Text('Request "${name}" is fehlgeschlagen'), context: ToastProvider.context);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================================================================================================================
|
||||||
|
|
||||||
|
static Future<bool> verifyToken(String uid, String tok) async {
|
||||||
|
try {
|
||||||
|
await _request<void>(
|
||||||
|
name: 'verifyToken',
|
||||||
|
method: 'GET',
|
||||||
|
relURL: '/users/$uid',
|
||||||
|
fn: null,
|
||||||
|
auth: KeyTokenAuth(userId: uid, token: tok),
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<User> getUser(KeyTokenAuth auth, String uid) async {
|
||||||
|
return await _request(
|
||||||
|
name: 'getUser',
|
||||||
|
method: 'GET',
|
||||||
|
relURL: 'users/$uid',
|
||||||
|
fn: User.fromJson,
|
||||||
|
auth: auth,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<ChannelWithSubscription>> getChannelList(KeyTokenAuth auth, ChannelSelector sel) async {
|
static Future<List<ChannelWithSubscription>> getChannelList(KeyTokenAuth auth, ChannelSelector sel) async {
|
||||||
var url = '$_base/users/${auth.userId}/channels?selector=${sel.apiKey}';
|
return await _request(
|
||||||
final uri = Uri.parse(url);
|
name: 'getChannelList',
|
||||||
|
method: 'GET',
|
||||||
final response = await http.get(uri, headers: {'Authorization': 'SCN ${auth.token}'});
|
relURL: 'users/${auth.userId}/channels',
|
||||||
|
query: {'selector': sel.apiKey},
|
||||||
if (response.statusCode != 200) {
|
fn: (json) => ChannelWithSubscription.fromJsonArray(json['channels']),
|
||||||
throw Exception('API request failed');
|
auth: auth,
|
||||||
}
|
);
|
||||||
|
|
||||||
final data = jsonDecode(response.body);
|
|
||||||
|
|
||||||
return data['channels'].map<ChannelWithSubscription>((e) => ChannelWithSubscription.fromJson(e)).toList() as List<ChannelWithSubscription>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<(String, List<Message>)> getMessageList(KeyTokenAuth auth, String pageToken, int? pageSize) async {
|
static Future<(String, List<Message>)> getMessageList(KeyTokenAuth auth, String pageToken, int? pageSize) async {
|
||||||
var url = '$_base/messages?next_page_token=$pageToken';
|
return await _request(
|
||||||
if (pageSize != null) {
|
name: 'getMessageList',
|
||||||
url += '&page_size=$pageSize';
|
method: 'GET',
|
||||||
}
|
relURL: 'messages',
|
||||||
final uri = Uri.parse(url);
|
query: {'next_page_token': pageToken, if (pageSize != null) 'page_size': pageSize.toString()},
|
||||||
|
fn: (json) => Message.fromPaginatedJsonArray(json, 'messages', 'next_page_token'),
|
||||||
final response = await http.get(uri, headers: {'Authorization': 'SCN ${auth.token}'});
|
auth: auth,
|
||||||
|
);
|
||||||
if (response.statusCode != 200) {
|
|
||||||
throw Exception('API request failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
final data = jsonDecode(response.body);
|
|
||||||
|
|
||||||
final npt = data['next_page_token'] as String;
|
|
||||||
|
|
||||||
final messages = data['messages'].map<Message>((e) => Message.fromJson(e)).toList() as List<Message>;
|
|
||||||
|
|
||||||
return (npt, messages);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Message> getMessage(KeyTokenAuth auth, String msgid) async {
|
static Future<Message> getMessage(KeyTokenAuth auth, String msgid) async {
|
||||||
final uri = Uri.parse('$_base/messages/$msgid');
|
return await _request(
|
||||||
final response = await http.get(uri, headers: {'Authorization': 'SCN ${auth.token}'});
|
name: 'getMessage',
|
||||||
|
method: 'GET',
|
||||||
if (response.statusCode != 200) {
|
relURL: 'messages/$msgid',
|
||||||
throw Exception('API request failed');
|
query: {},
|
||||||
}
|
fn: Message.fromJson,
|
||||||
|
auth: auth,
|
||||||
return Message.fromJson(jsonDecode(response.body));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,34 @@
|
|||||||
|
import 'package:fl_toast/fl_toast.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/state/database.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
|
||||||
import 'package:simplecloudnotifier/nav_layout.dart';
|
import 'package:simplecloudnotifier/nav_layout.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_theme.dart';
|
import 'package:simplecloudnotifier/state/app_theme.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/globals.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/request_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/user_account.dart';
|
import 'package:simplecloudnotifier/state/user_account.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
await SCNDatabase.create();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
await Hive.initFlutter();
|
||||||
|
await Globals().init();
|
||||||
|
|
||||||
|
Hive.registerAdapter(SCNRequestAdapter());
|
||||||
|
Hive.registerAdapter(SCNLogAdapter());
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Hive.openBox<SCNRequest>('scn-requests');
|
||||||
|
await Hive.openBox<SCNLog>('scn-logs');
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
Hive.deleteBoxFromDisk('scn-requests');
|
||||||
|
Hive.deleteBoxFromDisk('scn-logs');
|
||||||
|
await Hive.openBox<SCNRequest>('scn-requests');
|
||||||
|
await Hive.openBox<SCNLog>('scn-logs');
|
||||||
|
}
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
@ -31,7 +52,7 @@ class SCNApp extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Provider.of<UserAccount>(context); // ensure UserAccount is loaded
|
Provider.of<UserAccount>(context); // ensure UserAccount is loaded (unneccessary if lazy: false is set in MultiProvider ??)
|
||||||
|
|
||||||
return Consumer<AppTheme>(
|
return Consumer<AppTheme>(
|
||||||
builder: (context, appTheme, child) => MaterialApp(
|
builder: (context, appTheme, child) => MaterialApp(
|
||||||
@ -40,7 +61,7 @@ class SCNApp extends StatelessWidget {
|
|||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue, brightness: appTheme.darkMode ? Brightness.dark : Brightness.light),
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue, brightness: appTheme.darkMode ? Brightness.dark : Brightness.light),
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
home: const SCNNavLayout(),
|
home: const ToastProvider(child: SCNNavLayout()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
22
flutter/lib/models/api_error.dart
Normal file
22
flutter/lib/models/api_error.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
class APIError {
|
||||||
|
final String success;
|
||||||
|
final String error;
|
||||||
|
final String errhighlight;
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
const APIError({
|
||||||
|
required this.success,
|
||||||
|
required this.error,
|
||||||
|
required this.errhighlight,
|
||||||
|
required this.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory APIError.fromJson(Map<String, dynamic> json) {
|
||||||
|
return APIError(
|
||||||
|
success: json['success'],
|
||||||
|
error: json['error'],
|
||||||
|
errhighlight: json['errhighlight'],
|
||||||
|
message: json['message'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -24,31 +24,17 @@ class Channel {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Channel.fromJson(Map<String, dynamic> json) {
|
factory Channel.fromJson(Map<String, dynamic> json) {
|
||||||
return switch (json) {
|
return Channel(
|
||||||
{
|
channelID: json['channel_id'],
|
||||||
'channel_id': String channelID,
|
ownerUserID: json['owner_user_id'],
|
||||||
'owner_user_id': String ownerUserID,
|
internalName: json['internal_name'],
|
||||||
'internal_name': String internalName,
|
displayName: json['display_name'],
|
||||||
'display_name': String displayName,
|
descriptionName: json['description_name'],
|
||||||
'description_name': String? descriptionName,
|
subscribeKey: json['subscribe_key'],
|
||||||
'subscribe_key': String? subscribeKey,
|
timestampCreated: json['timestamp_created'],
|
||||||
'timestamp_created': String timestampCreated,
|
timestampLastSent: json['timestamp_lastsent'],
|
||||||
'timestamp_lastsent': String? timestampLastSent,
|
messagesSent: json['messages_sent'],
|
||||||
'messages_sent': int messagesSent,
|
);
|
||||||
} =>
|
|
||||||
Channel(
|
|
||||||
channelID: channelID,
|
|
||||||
ownerUserID: ownerUserID,
|
|
||||||
internalName: internalName,
|
|
||||||
displayName: displayName,
|
|
||||||
descriptionName: descriptionName,
|
|
||||||
subscribeKey: subscribeKey,
|
|
||||||
timestampCreated: timestampCreated,
|
|
||||||
timestampLastSent: timestampLastSent,
|
|
||||||
messagesSent: messagesSent,
|
|
||||||
),
|
|
||||||
_ => throw const FormatException('Failed to decode Channel.'),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,32 +55,21 @@ class ChannelWithSubscription extends Channel {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory ChannelWithSubscription.fromJson(Map<String, dynamic> json) {
|
factory ChannelWithSubscription.fromJson(Map<String, dynamic> json) {
|
||||||
return switch (json) {
|
return ChannelWithSubscription(
|
||||||
{
|
channelID: json['channel_id'],
|
||||||
'channel_id': String channelID,
|
ownerUserID: json['owner_user_id'],
|
||||||
'owner_user_id': String ownerUserID,
|
internalName: json['internal_name'],
|
||||||
'internal_name': String internalName,
|
displayName: json['display_name'],
|
||||||
'display_name': String displayName,
|
descriptionName: json['description_name'],
|
||||||
'description_name': String? descriptionName,
|
subscribeKey: json['subscribe_key'],
|
||||||
'subscribe_key': String? subscribeKey,
|
timestampCreated: json['timestamp_created'],
|
||||||
'timestamp_created': String timestampCreated,
|
timestampLastSent: json['timestamp_lastsent'],
|
||||||
'timestamp_lastsent': String? timestampLastSent,
|
messagesSent: json['messages_sent'],
|
||||||
'messages_sent': int messagesSent,
|
subscription: Subscription.fromJson(json['subscription']),
|
||||||
'subscription': dynamic subscription,
|
);
|
||||||
} =>
|
}
|
||||||
ChannelWithSubscription(
|
|
||||||
channelID: channelID,
|
static List<ChannelWithSubscription> fromJsonArray(List<dynamic> jsonArr) {
|
||||||
ownerUserID: ownerUserID,
|
return jsonArr.map<ChannelWithSubscription>((e) => ChannelWithSubscription.fromJson(e)).toList();
|
||||||
internalName: internalName,
|
|
||||||
displayName: displayName,
|
|
||||||
descriptionName: descriptionName,
|
|
||||||
subscribeKey: subscribeKey,
|
|
||||||
timestampCreated: timestampCreated,
|
|
||||||
timestampLastSent: timestampLastSent,
|
|
||||||
messagesSent: messagesSent,
|
|
||||||
subscription: Subscription.fromJson(subscription),
|
|
||||||
),
|
|
||||||
_ => throw const FormatException('Failed to decode Channel.'),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,38 +30,28 @@ class Message {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Message.fromJson(Map<String, dynamic> json) {
|
factory Message.fromJson(Map<String, dynamic> json) {
|
||||||
return switch (json) {
|
return Message(
|
||||||
{
|
messageID: json['message_id'],
|
||||||
'message_id': String messageID,
|
senderUserID: json['sender_user_id'],
|
||||||
'sender_user_id': String senderUserID,
|
channelInternalName: json['channel_internal_name'],
|
||||||
'channel_internal_name': String channelInternalName,
|
channelID: json['channel_id'],
|
||||||
'channel_id': String channelID,
|
senderName: json['sender_name'],
|
||||||
'sender_name': String? senderName,
|
senderIP: json['sender_ip'],
|
||||||
'sender_ip': String senderIP,
|
timestamp: json['timestamp'],
|
||||||
'timestamp': String timestamp,
|
title: json['title'],
|
||||||
'title': String title,
|
content: json['content'],
|
||||||
'content': String? content,
|
priority: json['priority'],
|
||||||
'priority': int priority,
|
userMessageID: json['usr_message_id'],
|
||||||
'usr_message_id': String? userMessageID,
|
usedKeyID: json['used_key_id'],
|
||||||
'used_key_id': String usedKeyID,
|
trimmed: json['trimmed'],
|
||||||
'trimmed': bool trimmed,
|
);
|
||||||
} =>
|
}
|
||||||
Message(
|
|
||||||
messageID: messageID,
|
static fromPaginatedJsonArray(Map<String, dynamic> data, String keyMessages, String keyToken) {
|
||||||
senderUserID: senderUserID,
|
final npt = data[keyToken] as String;
|
||||||
channelInternalName: channelInternalName,
|
|
||||||
channelID: channelID,
|
final messages = (data[keyMessages] as List<dynamic>).map<Message>((e) => Message.fromJson(e)).toList();
|
||||||
senderName: senderName,
|
|
||||||
senderIP: senderIP,
|
return (npt, messages);
|
||||||
timestamp: timestamp,
|
|
||||||
title: title,
|
|
||||||
content: content,
|
|
||||||
priority: priority,
|
|
||||||
userMessageID: userMessageID,
|
|
||||||
usedKeyID: usedKeyID,
|
|
||||||
trimmed: trimmed,
|
|
||||||
),
|
|
||||||
_ => throw const FormatException('Failed to decode Message.'),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,26 +18,14 @@ class Subscription {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Subscription.fromJson(Map<String, dynamic> json) {
|
factory Subscription.fromJson(Map<String, dynamic> json) {
|
||||||
return switch (json) {
|
return Subscription(
|
||||||
{
|
subscriptionID: json['subscription_id'],
|
||||||
'subscription_id': String subscriptionID,
|
subscriberUserID: json['subscriber_user_id'],
|
||||||
'subscriber_user_id': String subscriberUserID,
|
channelOwnerUserID: json['channel_owner_user_id'],
|
||||||
'channel_owner_user_id': String channelOwnerUserID,
|
channelID: json['channel_id'],
|
||||||
'channel_id': String channelID,
|
channelInternalName: json['channel_internal_name'],
|
||||||
'channel_internal_name': String channelInternalName,
|
timestampCreated: json['timestamp_created'],
|
||||||
'timestamp_created': String timestampCreated,
|
confirmed: json['confirmed'],
|
||||||
'confirmed': bool confirmed,
|
);
|
||||||
} =>
|
|
||||||
Subscription(
|
|
||||||
subscriptionID: subscriptionID,
|
|
||||||
subscriberUserID: subscriberUserID,
|
|
||||||
channelOwnerUserID: channelOwnerUserID,
|
|
||||||
channelID: channelID,
|
|
||||||
channelInternalName: channelInternalName,
|
|
||||||
timestampCreated: timestampCreated,
|
|
||||||
confirmed: confirmed,
|
|
||||||
),
|
|
||||||
_ => throw const FormatException('Failed to decode Subscription.'),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,48 +40,25 @@ class User {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory User.fromJson(Map<String, dynamic> json) {
|
factory User.fromJson(Map<String, dynamic> json) {
|
||||||
return switch (json) {
|
return User(
|
||||||
{
|
userID: json['user_id'],
|
||||||
'user_id': String userID,
|
username: json['username'],
|
||||||
'username': String? username,
|
timestampCreated: json['timestamp_created'],
|
||||||
'timestamp_created': String timestampCreated,
|
timestampLastRead: json['timestamp_lastread'],
|
||||||
'timestamp_lastread': String? timestampLastRead,
|
timestampLastSent: json['timestamp_lastsent'],
|
||||||
'timestamp_lastsent': String? timestampLastSent,
|
messagesSent: json['messages_sent'],
|
||||||
'messages_sent': int messagesSent,
|
quotaUsed: json['quota_used'],
|
||||||
'quota_used': int quotaUsed,
|
quotaRemaining: json['quota_remaining'],
|
||||||
'quota_remaining': int quotaRemaining,
|
quotaPerDay: json['quota_max'],
|
||||||
'quota_max': int quotaPerDay,
|
isPro: json['is_pro'],
|
||||||
'is_pro': bool isPro,
|
defaultChannel: json['default_channel'],
|
||||||
'default_channel': String defaultChannel,
|
maxBodySize: json['max_body_size'],
|
||||||
'max_body_size': int maxBodySize,
|
maxTitleLength: json['max_title_length'],
|
||||||
'max_title_length': int maxTitleLength,
|
defaultPriority: json['default_priority'],
|
||||||
'default_priority': int defaultPriority,
|
maxChannelNameLength: json['max_channel_name_length'],
|
||||||
'max_channel_name_length': int maxChannelNameLength,
|
maxChannelDescriptionLength: json['max_channel_description_length'],
|
||||||
'max_channel_description_length': int maxChannelDescriptionLength,
|
maxSenderNameLength: json['max_sender_name_length'],
|
||||||
'max_sender_name_length': int maxSenderNameLength,
|
maxUserMessageIDLength: json['max_user_message_id_length'],
|
||||||
'max_user_message_id_length': int maxUserMessageIDLength,
|
);
|
||||||
} =>
|
|
||||||
User(
|
|
||||||
userID: userID,
|
|
||||||
username: username,
|
|
||||||
timestampCreated: timestampCreated,
|
|
||||||
timestampLastRead: timestampLastRead,
|
|
||||||
timestampLastSent: timestampLastSent,
|
|
||||||
messagesSent: messagesSent,
|
|
||||||
quotaUsed: quotaUsed,
|
|
||||||
quotaRemaining: quotaRemaining,
|
|
||||||
quotaPerDay: quotaPerDay,
|
|
||||||
isPro: isPro,
|
|
||||||
defaultChannel: defaultChannel,
|
|
||||||
maxBodySize: maxBodySize,
|
|
||||||
maxTitleLength: maxTitleLength,
|
|
||||||
defaultPriority: defaultPriority,
|
|
||||||
maxChannelNameLength: maxChannelNameLength,
|
|
||||||
maxChannelDescriptionLength: maxChannelDescriptionLength,
|
|
||||||
maxSenderNameLength: maxSenderNameLength,
|
|
||||||
maxUserMessageIDLength: maxUserMessageIDLength,
|
|
||||||
),
|
|
||||||
_ => throw const FormatException('Failed to decode User.'),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
flutter/lib/pages/debug/debug_logs.dart
Normal file
13
flutter/lib/pages/debug/debug_logs.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DebugLogsPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_DebugLogsPageState createState() => _DebugLogsPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DebugLogsPageState extends State<DebugLogsPage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(/* Add your UI components here */);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/pages/debug/debug_colors.dart';
|
import 'package:simplecloudnotifier/pages/debug/debug_colors.dart';
|
||||||
|
import 'package:simplecloudnotifier/pages/debug/debug_logs.dart';
|
||||||
import 'package:simplecloudnotifier/pages/debug/debug_persistence.dart';
|
import 'package:simplecloudnotifier/pages/debug/debug_persistence.dart';
|
||||||
import 'package:simplecloudnotifier/pages/debug/debug_requests.dart';
|
import 'package:simplecloudnotifier/pages/debug/debug_requests.dart';
|
||||||
|
|
||||||
@ -9,13 +10,14 @@ class DebugMainPage extends StatefulWidget {
|
|||||||
_DebugMainPageState createState() => _DebugMainPageState();
|
_DebugMainPageState createState() => _DebugMainPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DebugMainPageSubPage { colors, requests, persistence }
|
enum DebugMainPageSubPage { colors, requests, persistence, logs }
|
||||||
|
|
||||||
class _DebugMainPageState extends State<DebugMainPage> {
|
class _DebugMainPageState extends State<DebugMainPage> {
|
||||||
final Map<DebugMainPageSubPage, Widget> _subpages = {
|
final Map<DebugMainPageSubPage, Widget> _subpages = {
|
||||||
DebugMainPageSubPage.colors: DebugColorsPage(),
|
DebugMainPageSubPage.colors: DebugColorsPage(),
|
||||||
DebugMainPageSubPage.requests: DebugRequestsPage(),
|
DebugMainPageSubPage.requests: DebugRequestsPage(),
|
||||||
DebugMainPageSubPage.persistence: DebugPersistencePage(),
|
DebugMainPageSubPage.persistence: DebugPersistencePage(),
|
||||||
|
DebugMainPageSubPage.logs: DebugLogsPage(),
|
||||||
};
|
};
|
||||||
|
|
||||||
DebugMainPageSubPage _subPage = DebugMainPageSubPage.colors;
|
DebugMainPageSubPage _subPage = DebugMainPageSubPage.colors;
|
||||||
@ -52,6 +54,7 @@ class _DebugMainPageState extends State<DebugMainPage> {
|
|||||||
ButtonSegment<DebugMainPageSubPage>(value: DebugMainPageSubPage.colors, label: Text('Theme')),
|
ButtonSegment<DebugMainPageSubPage>(value: DebugMainPageSubPage.colors, label: Text('Theme')),
|
||||||
ButtonSegment<DebugMainPageSubPage>(value: DebugMainPageSubPage.requests, label: Text('Requests')),
|
ButtonSegment<DebugMainPageSubPage>(value: DebugMainPageSubPage.requests, label: Text('Requests')),
|
||||||
ButtonSegment<DebugMainPageSubPage>(value: DebugMainPageSubPage.persistence, label: Text('Persistence')),
|
ButtonSegment<DebugMainPageSubPage>(value: DebugMainPageSubPage.persistence, label: Text('Persistence')),
|
||||||
|
ButtonSegment<DebugMainPageSubPage>(value: DebugMainPageSubPage.logs, label: Text('Logs')),
|
||||||
],
|
],
|
||||||
selected: <DebugMainPageSubPage>{_subPage},
|
selected: <DebugMainPageSubPage>{_subPage},
|
||||||
onSelectionChanged: (Set<DebugMainPageSubPage> v) {
|
onSelectionChanged: (Set<DebugMainPageSubPage> v) {
|
||||||
|
87
flutter/lib/pages/debug/debug_request_view.dart
Normal file
87
flutter/lib/pages/debug/debug_request_view.dart
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import 'package:fl_toast/fl_toast.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/request_log.dart';
|
||||||
|
|
||||||
|
class DebugRequestViewPage extends StatelessWidget {
|
||||||
|
final SCNRequest request;
|
||||||
|
|
||||||
|
DebugRequestViewPage({required this.request});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SCNScaffold(
|
||||||
|
title: 'Request',
|
||||||
|
showSearch: false,
|
||||||
|
showDebug: false,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
...buildRow(context, "Name", request.name),
|
||||||
|
...buildRow(context, "Timestamp (Start)", request.timestampStart.toString()),
|
||||||
|
...buildRow(context, "Timestamp (End)", request.timestampEnd.toString()),
|
||||||
|
...buildRow(context, "Duration", request.timestampEnd.difference(request.timestampStart).toString()),
|
||||||
|
Divider(),
|
||||||
|
...buildRow(context, "Method", request.method),
|
||||||
|
...buildRow(context, "URL", request.url),
|
||||||
|
if (request.requestHeaders.isNotEmpty) ...buildRow(context, "Request->Headers", request.requestHeaders.entries.map((v) => '${v.key} = ${v.value}').join('\n')),
|
||||||
|
if (request.requestBody != '') ...buildRow(context, "Request->Body", request.requestBody),
|
||||||
|
Divider(),
|
||||||
|
if (request.responseStatusCode != 0) ...buildRow(context, "Response->Statuscode", request.responseStatusCode.toString()),
|
||||||
|
if (request.responseBody != '') ...buildRow(context, "Reponse->Body", request.responseBody),
|
||||||
|
if (request.responseHeaders.isNotEmpty) ...buildRow(context, "Reponse->Headers", request.responseHeaders.entries.map((v) => '${v.key} = ${v.value}').join('\n')),
|
||||||
|
Divider(),
|
||||||
|
if (request.error != '') ...buildRow(context, "Error", request.error),
|
||||||
|
if (request.stackTrace != '') ...buildRow(context, "Stacktrace", request.stackTrace),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> buildRow(BuildContext context, String title, String value) {
|
||||||
|
return [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 8.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(title, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: FaIcon(
|
||||||
|
FontAwesomeIcons.copy,
|
||||||
|
),
|
||||||
|
iconSize: 14,
|
||||||
|
padding: EdgeInsets.fromLTRB(0, 0, 4, 0),
|
||||||
|
constraints: BoxConstraints(),
|
||||||
|
onPressed: () {
|
||||||
|
Clipboard.setData(new ClipboardData(text: value));
|
||||||
|
showPlatformToast(child: Text('Copied to clipboard'), context: ToastProvider.context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card.filled(
|
||||||
|
shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(0)),
|
||||||
|
color: request.type == 'SUCCESS' ? null : Theme.of(context).colorScheme.errorContainer,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 6.0),
|
||||||
|
child: SelectableText(
|
||||||
|
value,
|
||||||
|
minLines: 1,
|
||||||
|
maxLines: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:simplecloudnotifier/pages/debug/debug_request_view.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/request_log.dart';
|
||||||
|
|
||||||
class DebugRequestsPage extends StatefulWidget {
|
class DebugRequestsPage extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -6,8 +10,82 @@ class DebugRequestsPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DebugRequestsPageState extends State<DebugRequestsPage> {
|
class _DebugRequestsPageState extends State<DebugRequestsPage> {
|
||||||
|
Box<SCNRequest> requestsBox = Hive.box<SCNRequest>('scn-requests');
|
||||||
|
|
||||||
|
static final _dateFormat = DateFormat('yyyy-MM-dd kk:mm');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(/* Add your UI components here */);
|
return Container(
|
||||||
|
child: ValueListenableBuilder(
|
||||||
|
valueListenable: requestsBox.listenable(),
|
||||||
|
builder: (context, Box<SCNRequest> box, _) {
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: requestsBox.length,
|
||||||
|
itemBuilder: (context, listIndex) {
|
||||||
|
final req = requestsBox.getAt(requestsBox.length - listIndex - 1)!;
|
||||||
|
if (req.type == 'SUCCESS') {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 2.0),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => DebugRequestViewPage(request: req))),
|
||||||
|
child: ListTile(
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 120,
|
||||||
|
child: Text(_dateFormat.format(req.timestampStart), style: TextStyle(fontSize: 12)),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(req.name, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
SizedBox(width: 2),
|
||||||
|
Text('${req.timestampEnd.difference(req.timestampStart).inMilliseconds}ms', style: TextStyle(fontSize: 12)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subtitle: Text(req.type),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 2.0),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => DebugRequestViewPage(request: req))),
|
||||||
|
child: ListTile(
|
||||||
|
tileColor: Theme.of(context).colorScheme.errorContainer,
|
||||||
|
textColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 120,
|
||||||
|
child: Text(_dateFormat.format(req.timestampStart), style: TextStyle(fontSize: 12)),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(req.name, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
SizedBox(width: 2),
|
||||||
|
Text('${req.timestampEnd.difference(req.timestampStart).inMilliseconds}ms', style: TextStyle(fontSize: 12)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(req.type),
|
||||||
|
Text(
|
||||||
|
req.error,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,29 @@
|
|||||||
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
|
||||||
|
part 'application_log.g.dart';
|
||||||
|
|
||||||
|
class ApplicationLog {}
|
||||||
|
|
||||||
|
enum SCNLogLevel { debug, info, warning, error, fatal }
|
||||||
|
|
||||||
|
@HiveType(typeId: 101)
|
||||||
|
class SCNLog extends HiveObject {
|
||||||
|
@HiveField(0)
|
||||||
|
final DateTime timestamp;
|
||||||
|
@HiveField(1)
|
||||||
|
final SCNLogLevel level;
|
||||||
|
@HiveField(2)
|
||||||
|
final String message;
|
||||||
|
@HiveField(3)
|
||||||
|
final String additional;
|
||||||
|
@HiveField(4)
|
||||||
|
final String trace;
|
||||||
|
|
||||||
|
SCNLog(
|
||||||
|
this.timestamp,
|
||||||
|
this.level,
|
||||||
|
this.message,
|
||||||
|
this.additional,
|
||||||
|
this.trace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
53
flutter/lib/state/application_log.g.dart
Normal file
53
flutter/lib/state/application_log.g.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'application_log.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// TypeAdapterGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
class SCNLogAdapter extends TypeAdapter<SCNLog> {
|
||||||
|
@override
|
||||||
|
final int typeId = 101;
|
||||||
|
|
||||||
|
@override
|
||||||
|
SCNLog read(BinaryReader reader) {
|
||||||
|
final numOfFields = reader.readByte();
|
||||||
|
final fields = <int, dynamic>{
|
||||||
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
|
};
|
||||||
|
return SCNLog(
|
||||||
|
fields[0] as DateTime,
|
||||||
|
fields[1] as SCNLogLevel,
|
||||||
|
fields[2] as String,
|
||||||
|
fields[3] as String,
|
||||||
|
fields[4] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void write(BinaryWriter writer, SCNLog obj) {
|
||||||
|
writer
|
||||||
|
..writeByte(5)
|
||||||
|
..writeByte(0)
|
||||||
|
..write(obj.timestamp)
|
||||||
|
..writeByte(1)
|
||||||
|
..write(obj.level)
|
||||||
|
..writeByte(2)
|
||||||
|
..write(obj.message)
|
||||||
|
..writeByte(3)
|
||||||
|
..write(obj.additional)
|
||||||
|
..writeByte(4)
|
||||||
|
..write(obj.trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => typeId.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is SCNLogAdapter &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
typeId == other.typeId;
|
||||||
|
}
|
@ -1,46 +0,0 @@
|
|||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:path/path.dart' as path;
|
|
||||||
|
|
||||||
class SCNDatabase {
|
|
||||||
static SCNDatabase? instance = null;
|
|
||||||
|
|
||||||
final Database _db;
|
|
||||||
|
|
||||||
SCNDatabase._(this._db) {}
|
|
||||||
|
|
||||||
static create() async {
|
|
||||||
var docPath = await getApplicationDocumentsDirectory();
|
|
||||||
var dbpath = path.join(docPath.absolute.path, 'scn.db');
|
|
||||||
|
|
||||||
if (Platform.isWindows || Platform.isLinux) {
|
|
||||||
sqfliteFfiInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
var db = await databaseFactoryFfi.openDatabase(dbpath,
|
|
||||||
options: OpenDatabaseOptions(
|
|
||||||
version: 1,
|
|
||||||
onCreate: (db, version) async {
|
|
||||||
initDatabase(db);
|
|
||||||
},
|
|
||||||
onUpgrade: (db, oldVersion, newVersion) async {
|
|
||||||
upgradeDatabase(db, oldVersion, newVersion);
|
|
||||||
},
|
|
||||||
));
|
|
||||||
|
|
||||||
return instance = SCNDatabase._(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void initDatabase(Database db) async {
|
|
||||||
await db.execute('CREATE TABLE requests (id INTEGER PRIMARY KEY, timestamp DATETIME, name TEXT, url TEXT, response_code INTEGER, response TEXT, status TEXT)');
|
|
||||||
|
|
||||||
await db.execute('CREATE TABLE logs (id INTEGER PRIMARY KEY, timestamp DATETIME, level TEXT, text TEXT, additional TEXT)');
|
|
||||||
|
|
||||||
await db.execute('CREATE TABLE messages (message_id INTEGER PRIMARY KEY, receive_timestamp DATETIME, channel_id TEXT, timestamp TEXT, data JSON)');
|
|
||||||
}
|
|
||||||
|
|
||||||
static void upgradeDatabase(Database db, int oldVersion, int newVersion) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
31
flutter/lib/state/globals.dart
Normal file
31
flutter/lib/state/globals.dart
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
||||||
|
class Globals {
|
||||||
|
static final Globals _singleton = Globals._internal();
|
||||||
|
|
||||||
|
factory Globals() {
|
||||||
|
return _singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
Globals._internal();
|
||||||
|
|
||||||
|
String appName = '';
|
||||||
|
String packageName = '';
|
||||||
|
String version = '';
|
||||||
|
String buildNumber = '';
|
||||||
|
String platform = '';
|
||||||
|
String hostname = '';
|
||||||
|
|
||||||
|
init() async {
|
||||||
|
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||||
|
|
||||||
|
this.appName = packageInfo.appName;
|
||||||
|
this.packageName = packageInfo.packageName;
|
||||||
|
this.version = packageInfo.version;
|
||||||
|
this.buildNumber = packageInfo.buildNumber;
|
||||||
|
this.platform = Platform.operatingSystem;
|
||||||
|
this.hostname = Platform.localHostname;
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,144 @@
|
|||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:simplecloudnotifier/models/api_error.dart';
|
||||||
|
|
||||||
|
part 'request_log.g.dart';
|
||||||
|
|
||||||
|
class RequestLog {
|
||||||
|
static void addRequestException(String name, DateTime tStart, String method, Uri uri, String reqbody, Map<String, String> reqheaders, dynamic e, StackTrace trace) {
|
||||||
|
Hive.box<SCNRequest>('scn-requests').add(SCNRequest(
|
||||||
|
timestampStart: tStart,
|
||||||
|
timestampEnd: DateTime.now(),
|
||||||
|
name: name,
|
||||||
|
method: method,
|
||||||
|
url: uri.toString(),
|
||||||
|
requestHeaders: reqheaders,
|
||||||
|
requestBody: reqbody,
|
||||||
|
responseStatusCode: 0,
|
||||||
|
responseHeaders: {},
|
||||||
|
responseBody: '',
|
||||||
|
type: 'EXCEPTION',
|
||||||
|
error: (e is Exception) ? e.toString() : '$e',
|
||||||
|
stackTrace: trace.toString(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addRequestAPIError(String name, DateTime t0, String method, Uri uri, String reqbody, Map<String, String> reqheaders, int responseStatusCode, String responseBody, Map<String, String> responseHeaders, APIError apierr) {
|
||||||
|
Hive.box<SCNRequest>('scn-requests').add(SCNRequest(
|
||||||
|
timestampStart: t0,
|
||||||
|
timestampEnd: DateTime.now(),
|
||||||
|
name: name,
|
||||||
|
method: method,
|
||||||
|
url: uri.toString(),
|
||||||
|
requestHeaders: reqheaders,
|
||||||
|
requestBody: reqbody,
|
||||||
|
responseStatusCode: responseStatusCode,
|
||||||
|
responseHeaders: responseHeaders,
|
||||||
|
responseBody: responseBody,
|
||||||
|
type: 'API_ERROR',
|
||||||
|
error: apierr.message,
|
||||||
|
stackTrace: '',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addRequestErrorStatuscode(String name, DateTime t0, String method, Uri uri, String reqbody, Map<String, String> reqheaders, int responseStatusCode, String responseBody, Map<String, String> responseHeaders) {
|
||||||
|
Hive.box<SCNRequest>('scn-requests').add(SCNRequest(
|
||||||
|
timestampStart: t0,
|
||||||
|
timestampEnd: DateTime.now(),
|
||||||
|
name: name,
|
||||||
|
method: method,
|
||||||
|
url: uri.toString(),
|
||||||
|
requestHeaders: reqheaders,
|
||||||
|
requestBody: reqbody,
|
||||||
|
responseStatusCode: responseStatusCode,
|
||||||
|
responseHeaders: responseHeaders,
|
||||||
|
responseBody: responseBody,
|
||||||
|
type: 'ERROR_STATUSCODE',
|
||||||
|
error: 'API request failed with status code $responseStatusCode',
|
||||||
|
stackTrace: '',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addRequestSuccess(String name, DateTime t0, String method, Uri uri, String reqbody, Map<String, String> reqheaders, int responseStatusCode, String responseBody, Map<String, String> responseHeaders) {
|
||||||
|
Hive.box<SCNRequest>('scn-requests').add(SCNRequest(
|
||||||
|
timestampStart: t0,
|
||||||
|
timestampEnd: DateTime.now(),
|
||||||
|
name: name,
|
||||||
|
method: method,
|
||||||
|
url: uri.toString(),
|
||||||
|
requestHeaders: reqheaders,
|
||||||
|
requestBody: reqbody,
|
||||||
|
responseStatusCode: responseStatusCode,
|
||||||
|
responseHeaders: responseHeaders,
|
||||||
|
responseBody: responseBody,
|
||||||
|
type: 'SUCCESS',
|
||||||
|
error: '',
|
||||||
|
stackTrace: '',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addRequestDecodeError(String name, DateTime t0, String method, Uri uri, String reqbody, Map<String, String> reqheaders, int responseStatusCode, String responseBody, Map<String, String> responseHeaders, Object exc, StackTrace trace) {
|
||||||
|
Hive.box<SCNRequest>('scn-requests').add(SCNRequest(
|
||||||
|
timestampStart: t0,
|
||||||
|
timestampEnd: DateTime.now(),
|
||||||
|
name: name,
|
||||||
|
method: method,
|
||||||
|
url: uri.toString(),
|
||||||
|
requestHeaders: reqheaders,
|
||||||
|
requestBody: reqbody,
|
||||||
|
responseStatusCode: responseStatusCode,
|
||||||
|
responseHeaders: responseHeaders,
|
||||||
|
responseBody: responseBody,
|
||||||
|
type: 'DECODE_ERROR',
|
||||||
|
error: (exc is Exception) ? exc.toString() : '$exc',
|
||||||
|
stackTrace: trace.toString(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@HiveType(typeId: 100)
|
||||||
|
class SCNRequest extends HiveObject {
|
||||||
|
@HiveField(0)
|
||||||
|
final DateTime timestampStart;
|
||||||
|
@HiveField(1)
|
||||||
|
final DateTime timestampEnd;
|
||||||
|
@HiveField(2)
|
||||||
|
final String name;
|
||||||
|
@HiveField(3)
|
||||||
|
final String type;
|
||||||
|
@HiveField(4)
|
||||||
|
final String error;
|
||||||
|
@HiveField(5)
|
||||||
|
final String stackTrace;
|
||||||
|
|
||||||
|
@HiveField(6)
|
||||||
|
final String method;
|
||||||
|
@HiveField(7)
|
||||||
|
final String url;
|
||||||
|
@HiveField(8)
|
||||||
|
final Map<String, String> requestHeaders;
|
||||||
|
@HiveField(12)
|
||||||
|
final String requestBody;
|
||||||
|
|
||||||
|
@HiveField(9)
|
||||||
|
final int responseStatusCode;
|
||||||
|
@HiveField(10)
|
||||||
|
final Map<String, String> responseHeaders;
|
||||||
|
@HiveField(11)
|
||||||
|
final String responseBody;
|
||||||
|
|
||||||
|
SCNRequest({
|
||||||
|
required this.timestampStart,
|
||||||
|
required this.timestampEnd,
|
||||||
|
required this.name,
|
||||||
|
required this.method,
|
||||||
|
required this.url,
|
||||||
|
required this.requestHeaders,
|
||||||
|
required this.requestBody,
|
||||||
|
required this.responseStatusCode,
|
||||||
|
required this.responseHeaders,
|
||||||
|
required this.responseBody,
|
||||||
|
required this.type,
|
||||||
|
required this.error,
|
||||||
|
required this.stackTrace,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
77
flutter/lib/state/request_log.g.dart
Normal file
77
flutter/lib/state/request_log.g.dart
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'request_log.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// TypeAdapterGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
class SCNRequestAdapter extends TypeAdapter<SCNRequest> {
|
||||||
|
@override
|
||||||
|
final int typeId = 100;
|
||||||
|
|
||||||
|
@override
|
||||||
|
SCNRequest read(BinaryReader reader) {
|
||||||
|
final numOfFields = reader.readByte();
|
||||||
|
final fields = <int, dynamic>{
|
||||||
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
|
};
|
||||||
|
return SCNRequest(
|
||||||
|
timestampStart: fields[0] as DateTime,
|
||||||
|
timestampEnd: fields[1] as DateTime,
|
||||||
|
name: fields[2] as String,
|
||||||
|
method: fields[6] as String,
|
||||||
|
url: fields[7] as String,
|
||||||
|
requestHeaders: (fields[8] as Map).cast<String, String>(),
|
||||||
|
requestBody: fields[12] as String,
|
||||||
|
responseStatusCode: fields[9] as int,
|
||||||
|
responseHeaders: (fields[10] as Map).cast<String, String>(),
|
||||||
|
responseBody: fields[11] as String,
|
||||||
|
type: fields[3] as String,
|
||||||
|
error: fields[4] as String,
|
||||||
|
stackTrace: fields[5] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void write(BinaryWriter writer, SCNRequest obj) {
|
||||||
|
writer
|
||||||
|
..writeByte(13)
|
||||||
|
..writeByte(0)
|
||||||
|
..write(obj.timestampStart)
|
||||||
|
..writeByte(1)
|
||||||
|
..write(obj.timestampEnd)
|
||||||
|
..writeByte(2)
|
||||||
|
..write(obj.name)
|
||||||
|
..writeByte(3)
|
||||||
|
..write(obj.type)
|
||||||
|
..writeByte(4)
|
||||||
|
..write(obj.error)
|
||||||
|
..writeByte(5)
|
||||||
|
..write(obj.stackTrace)
|
||||||
|
..writeByte(6)
|
||||||
|
..write(obj.method)
|
||||||
|
..writeByte(7)
|
||||||
|
..write(obj.url)
|
||||||
|
..writeByte(8)
|
||||||
|
..write(obj.requestHeaders)
|
||||||
|
..writeByte(12)
|
||||||
|
..write(obj.requestBody)
|
||||||
|
..writeByte(9)
|
||||||
|
..write(obj.responseStatusCode)
|
||||||
|
..writeByte(10)
|
||||||
|
..write(obj.responseHeaders)
|
||||||
|
..writeByte(11)
|
||||||
|
..write(obj.responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => typeId.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is SCNRequestAdapter &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
typeId == other.typeId;
|
||||||
|
}
|
@ -71,7 +71,7 @@ class UserAccount extends ChangeNotifier {
|
|||||||
throw Exception('Not authenticated');
|
throw Exception('Not authenticated');
|
||||||
}
|
}
|
||||||
|
|
||||||
final user = await APIClient.getUser(_auth!.userId, _auth!.token);
|
final user = await APIClient.getUser(_auth!, _auth!.userId);
|
||||||
|
|
||||||
setUser(user);
|
setUser(user);
|
||||||
|
|
||||||
|
@ -5,11 +5,13 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import package_info_plus
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
|
@ -1,6 +1,30 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
_fe_analyzer_shared:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: _fe_analyzer_shared
|
||||||
|
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "67.0.0"
|
||||||
|
analyzer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: analyzer
|
||||||
|
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.4.1"
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -17,6 +41,70 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
build:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build
|
||||||
|
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
build_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_config
|
||||||
|
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
build_daemon:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_daemon
|
||||||
|
sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.1"
|
||||||
|
build_resolvers:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_resolvers
|
||||||
|
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2"
|
||||||
|
build_runner:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: build_runner
|
||||||
|
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.9"
|
||||||
|
build_runner_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_runner_core
|
||||||
|
sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.3.0"
|
||||||
|
built_collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_collection
|
||||||
|
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.1"
|
||||||
|
built_value:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_value
|
||||||
|
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.9.2"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -25,6 +113,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
checked_yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: checked_yaml
|
||||||
|
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -33,6 +129,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
|
code_builder:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: code_builder
|
||||||
|
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.10.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -41,6 +145,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
|
convert:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: convert
|
||||||
|
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.3"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -49,6 +169,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.8"
|
version: "1.0.8"
|
||||||
|
dart_style:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dart_style
|
||||||
|
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.6"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -73,6 +201,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0"
|
version: "7.0.0"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
fl_toast:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fl_toast
|
||||||
|
sha256: "0f7bbce90d1b75463a414c6a5476e45bd93fa7c4adccce1690076f4d1ef77c42"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -111,6 +255,54 @@ packages:
|
|||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "10.7.0"
|
version: "10.7.0"
|
||||||
|
frontend_server_client:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: frontend_server_client
|
||||||
|
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
|
glob:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: glob
|
||||||
|
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
graphs:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: graphs
|
||||||
|
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
hive:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: hive
|
||||||
|
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.3"
|
||||||
|
hive_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: hive_flutter
|
||||||
|
sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
hive_generator:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: hive_generator
|
||||||
|
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -119,6 +311,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.1"
|
||||||
|
http_multi_server:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_multi_server
|
||||||
|
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -143,6 +343,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.19.0"
|
version: "0.19.0"
|
||||||
|
io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: io
|
||||||
|
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.1"
|
||||||
|
json_annotation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: json_annotation
|
||||||
|
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.9.0"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -175,6 +399,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "4.0.0"
|
||||||
|
logging:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: logging
|
||||||
|
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -199,6 +431,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.11.0"
|
||||||
|
mime:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -207,6 +447,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
package_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_config
|
||||||
|
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
package_info_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: package_info_plus
|
||||||
|
sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.0.0"
|
||||||
|
package_info_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_info_plus_platform_interface
|
||||||
|
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -279,6 +543,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
|
pool:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pool
|
||||||
|
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.1"
|
||||||
provider:
|
provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -287,6 +559,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.2"
|
version: "6.1.2"
|
||||||
|
pub_semver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pub_semver
|
||||||
|
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
pubspec_parse:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pubspec_parse
|
||||||
|
sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.3"
|
||||||
qr:
|
qr:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -359,6 +647,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.3.2"
|
||||||
|
shelf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf
|
||||||
|
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
shelf_web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_web_socket
|
||||||
|
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -372,6 +676,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.12"
|
version: "0.2.12"
|
||||||
|
source_gen:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_gen
|
||||||
|
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.0"
|
||||||
|
source_helper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_helper
|
||||||
|
sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.4"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -380,30 +700,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
sqflite_common:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sqflite_common
|
|
||||||
sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.5.4"
|
|
||||||
sqflite_common_ffi:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: sqflite_common_ffi
|
|
||||||
sha256: "4d6137c29e930d6e4a8ff373989dd9de7bac12e3bc87bce950f6e844e8ad3bb5"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.3"
|
|
||||||
sqlite3:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sqlite3
|
|
||||||
sha256: b384f598b813b347c5a7e5ffad82cbaff1bec3d1561af267041e66f6f0899295
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.3"
|
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -420,6 +716,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
stream_transform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_transform
|
||||||
|
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -428,14 +732,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
synchronized:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: synchronized
|
|
||||||
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.0+1"
|
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -452,6 +748,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.1"
|
version: "0.6.1"
|
||||||
|
timing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: timing
|
||||||
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -540,6 +844,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "13.0.0"
|
version: "13.0.0"
|
||||||
|
watcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: watcher
|
||||||
|
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -548,6 +860,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "0.5.1"
|
||||||
|
web_socket_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket_channel
|
||||||
|
sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.5"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -564,6 +884,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
|
yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: yaml
|
||||||
|
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.3.0 <4.0.0"
|
dart: ">=3.3.0 <4.0.0"
|
||||||
flutter: ">=3.19.0"
|
flutter: ">=3.19.0"
|
||||||
|
@ -1,40 +1,16 @@
|
|||||||
name: simplecloudnotifier
|
name: simplecloudnotifier
|
||||||
description: "A new Flutter project."
|
description: "A new Flutter project."
|
||||||
# The following line prevents the package from being accidentally published to
|
publish_to: 'none'
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|
||||||
|
|
||||||
# The following defines the version and build number for your application.
|
|
||||||
# A version number is three numbers separated by dots, like 1.2.43
|
|
||||||
# followed by an optional build number separated by a +.
|
|
||||||
# Both the version and the builder number may be overridden in flutter
|
|
||||||
# build by specifying --build-name and --build-number, respectively.
|
|
||||||
# In Android, build-name is used as versionName while build-number used as versionCode.
|
|
||||||
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
|
||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
|
|
||||||
# Read more about iOS versioning at
|
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
|
||||||
version: 2.0.0+100
|
version: 2.0.0+100
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.2.6 <4.0.0'
|
sdk: '>=3.2.6 <4.0.0'
|
||||||
|
|
||||||
# Dependencies specify other packages that your package needs in order to work.
|
|
||||||
# To automatically upgrade your package dependencies to the latest versions
|
|
||||||
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
|
||||||
# dependencies can be manually updated by changing the version numbers below to
|
|
||||||
# the latest version available on pub.dev. To see which dependencies have newer
|
|
||||||
# versions available, run `flutter pub outdated`.
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
font_awesome_flutter: '>= 4.7.0'
|
font_awesome_flutter: '>= 4.7.0'
|
||||||
|
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
http: ^1.2.0
|
http: ^1.2.0
|
||||||
provider: ^6.1.1
|
provider: ^6.1.1
|
||||||
@ -43,8 +19,11 @@ dependencies:
|
|||||||
url_launcher: ^6.2.4
|
url_launcher: ^6.2.4
|
||||||
infinite_scroll_pagination: ^4.0.0
|
infinite_scroll_pagination: ^4.0.0
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
sqflite_common_ffi: ^2.3.3
|
|
||||||
path_provider: ^2.1.3
|
path_provider: ^2.1.3
|
||||||
|
hive: ^2.2.3
|
||||||
|
hive_flutter: ^1.1.0
|
||||||
|
package_info_plus: ^8.0.0
|
||||||
|
fl_toast: ^3.2.0
|
||||||
|
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
@ -56,51 +35,9 @@ dev_dependencies:
|
|||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
# The "flutter_lints" package below contains a set of recommended lints to
|
|
||||||
# encourage good coding practices. The lint set provided by the package is
|
|
||||||
# activated in the `analysis_options.yaml` file located at the root of your
|
|
||||||
# package. See that file for information about deactivating specific lint
|
|
||||||
# rules and activating additional ones.
|
|
||||||
flutter_lints: ^4.0.0
|
flutter_lints: ^4.0.0
|
||||||
|
hive_generator: ^2.0.1
|
||||||
|
build_runner: ^2.1.4
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
|
||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
||||||
# The following line ensures that the Material Icons font is
|
|
||||||
# included with your application, so that you can use the icons in
|
|
||||||
# the material Icons class.
|
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
|
||||||
# assets:
|
|
||||||
# - images/a_dot_burr.jpeg
|
|
||||||
# - images/a_dot_ham.jpeg
|
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
|
||||||
|
|
||||||
# For details regarding adding assets from package dependencies, see
|
|
||||||
# https://flutter.dev/assets-and-images/#from-packages
|
|
||||||
|
|
||||||
# To add custom fonts to your application, add a fonts section here,
|
|
||||||
# in this "flutter" section. Each entry in this list should have a
|
|
||||||
# "family" key with the font family name, and a "fonts" key with a
|
|
||||||
# list giving the asset and other descriptors for the font. For
|
|
||||||
# example:
|
|
||||||
# fonts:
|
|
||||||
# - family: Schyler
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/Schyler-Regular.ttf
|
|
||||||
# - asset: fonts/Schyler-Italic.ttf
|
|
||||||
# style: italic
|
|
||||||
# - family: Trajan Pro
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/TrajanPro.ttf
|
|
||||||
# - asset: fonts/TrajanPro_Bold.ttf
|
|
||||||
# weight: 700
|
|
||||||
#
|
|
||||||
# For details regarding fonts from package dependencies,
|
|
||||||
# see https://flutter.dev/custom-fonts/#from-packages
|
|
||||||
|
Loading…
Reference in New Issue
Block a user