From 37e168e46b801743f0412b589e9d7a04c86ef4c8 Mon Sep 17 00:00:00 2001 From: Nate Anderson <nate.anderson@vasion.com> Date: Thu, 30 Jan 2025 22:17:32 -0700 Subject: [PATCH] WIP auth, added Drift for database and refined shared_models for data exchange --- backend/analysis_options.yaml | 2 + backend/build.yaml | 14 ++ backend/lib/authenticator.dart | 49 ++++ backend/lib/database.dart | 53 ++++ backend/lib/service/db_access.dart | 24 ++ backend/main.dart | 17 ++ backend/pubspec.lock | 275 ++++++++++++++++++++- backend/pubspec.yaml | 8 + backend/routes/[roomCode]/_middleware.dart | 17 ++ backend/routes/[roomCode]/join.dart | 5 + backend/routes/_middleware.dart | 15 ++ backend/routes/auth/index.dart | 45 ++++ shared_models/lib/user.dart | 31 +++ shared_models/pubspec.lock | 88 +++++++ shared_models/pubspec.yaml | 1 + 15 files changed, 642 insertions(+), 2 deletions(-) create mode 100644 backend/build.yaml create mode 100644 backend/lib/authenticator.dart create mode 100644 backend/lib/database.dart create mode 100644 backend/lib/service/db_access.dart create mode 100644 backend/main.dart create mode 100644 backend/routes/[roomCode]/_middleware.dart create mode 100644 backend/routes/[roomCode]/join.dart create mode 100644 backend/routes/_middleware.dart create mode 100644 backend/routes/auth/index.dart create mode 100644 shared_models/lib/user.dart diff --git a/backend/analysis_options.yaml b/backend/analysis_options.yaml index ac85346..51f78f8 100644 --- a/backend/analysis_options.yaml +++ b/backend/analysis_options.yaml @@ -4,6 +4,8 @@ linter: file_names: false camel_case_types: true prefer_single_quotes: true + public_member_api_docs: false + lines_longer_than_80_chars: false analyzer: exclude: diff --git a/backend/build.yaml b/backend/build.yaml new file mode 100644 index 0000000..afefcdc --- /dev/null +++ b/backend/build.yaml @@ -0,0 +1,14 @@ +targets: + $default: + builders: + drift_dev: + options: + # The directory where the test files are stored: + test_dir: drift/test/ # (default) + # The directory where the schema files are stored: + schema_dir: drift/schemas/ # (default) + databases: + db: lib/database.dart + # Optional: Add more databases + # another_db: lib/database2.dart + diff --git a/backend/lib/authenticator.dart b/backend/lib/authenticator.dart new file mode 100644 index 0000000..d1f44b1 --- /dev/null +++ b/backend/lib/authenticator.dart @@ -0,0 +1,49 @@ +import 'dart:io'; + +import 'package:backend/database.dart'; +import 'package:backend/service/db_access.dart'; +import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; + +final jwtSecret = _getSecret(); + +class Authenticator { + Future<String?> generateToken({required String username}) async { + final newUser = await Db.createUser(username: username); + if (newUser == null) return null; + + final jwt = JWT( + { + 'uid': newUser.uuid, + }, + ); + + return jwt.sign(SecretKey(jwtSecret)); + } + + Future<User?> verifyToken( + String token, + ) async { + try { + final payload = JWT.verify( + token, + SecretKey(jwtSecret), + ); + + final payloadData = payload.payload as Map<String, dynamic>; + + final uuid = payloadData['uuid'] as String; + return await Db.getUser(uuid); + } catch (e) { + return null; + } + } +} + +String _getSecret() { + final secret = Platform.environment['JWT_TOKEN_SECRET']; + if (secret == null || secret.isEmpty) { + throw Exception('JWT secret not configured. Define JWT_TOKEN_SECRET in environment.'); + } else { + return secret; + } +} diff --git a/backend/lib/database.dart b/backend/lib/database.dart new file mode 100644 index 0000000..65fad21 --- /dev/null +++ b/backend/lib/database.dart @@ -0,0 +1,53 @@ +import 'dart:io'; + +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; + +part 'database.g.dart'; + +class Users extends Table { + TextColumn get uuid => text().unique()(); + TextColumn get gameRoom => text().references(GameRooms, #uuid).nullable()(); + TextColumn get name => text().withLength(min: 2, max: 32)(); + DateTimeColumn get createdAt => dateTime().nullable()(); +} + +enum GameStatus { + opened, + running, + closed, +} + +class GameRooms extends Table { + TextColumn get uuid => text().unique()(); + TextColumn get status => textEnum<GameStatus>()(); + DateTimeColumn get createdAt => dateTime().nullable()(); +} + +@DriftDatabase(tables: [Users, GameRooms]) +class AppDatabase extends _$AppDatabase { + AppDatabase() : super(_openConnection()); + + @override + int get schemaVersion => 1; + + static QueryExecutor _openConnection() { + return NativeDatabase.createInBackground(File('./backend.db')); + } + + @override + MigrationStrategy get migration { + return MigrationStrategy( + beforeOpen: (details) async { + // Statements to run to make sqlite performant, running in WAL mode, etc + await customStatement('PRAGMA foreign_keys = ON'); + await customStatement('PRAGMA journal_mode=WAL'); + await customStatement('PRAGMA busy_timeout=5000'); + await customStatement('PRAGMA synchronous=NORMAL'); + await customStatement('PRAGMA cache_size=10000'); + await customStatement('PRAGMA temp_store=MEMORY'); + await customStatement('PRAGMA mmap_size=268435456'); + }, + ); + } +} diff --git a/backend/lib/service/db_access.dart b/backend/lib/service/db_access.dart new file mode 100644 index 0000000..b559722 --- /dev/null +++ b/backend/lib/service/db_access.dart @@ -0,0 +1,24 @@ +import 'package:backend/database.dart'; +import 'package:drift/drift.dart'; +import 'package:logging/logging.dart'; +import 'package:uuid/uuid.dart'; + +final log = Logger('Db'); + +class Db { + static Future<User> getUser(String uuid) { + log.finer('Getting user $uuid'); + return AppDatabase().managers.users.filter((f) => f.uuid.equals(uuid)).get().then((u) => u.first); + } + + static Future<User?> createUser({required String username}) => AppDatabase() + .managers + .users + .createReturningOrNull( + (o) => o(createdAt: Value(DateTime.now()), uuid: const Uuid().v4(), name: username), + ) + .catchError((Object err) { + log.severe('Failed to create user', err, StackTrace.current); + throw Exception(err.toString()); + }); +} diff --git a/backend/main.dart b/backend/main.dart new file mode 100644 index 0000000..a3ef3f4 --- /dev/null +++ b/backend/main.dart @@ -0,0 +1,17 @@ +import 'dart:io'; + +import 'package:dart_frog/dart_frog.dart'; +import 'package:logging/logging.dart'; + +Future<HttpServer> run(Handler handler, InternetAddress ip, int port) { + // 1. Execute any custom code prior to starting the server... + + final String logLevel = Platform.environment['LOG_LEVEL'] ?? 'INFO'; + Logger.root.level = + Level.LEVELS.firstWhere((l) => l.name == logLevel, orElse: () => Level.INFO); // defaults to Level.INFO + Logger.root.onRecord.listen((record) { + stdout.writeln('${record.level.name}: ${record.time}: ${record.message}'); + }); + + return serve(handler, ip, port); +} diff --git a/backend/pubspec.lock b/backend/pubspec.lock index 91472f6..86881c6 100644 --- a/backend/pubspec.lock +++ b/backend/pubspec.lock @@ -14,6 +14,14 @@ packages: description: dart source: sdk version: "0.3.3" + adaptive_number: + dependency: transitive + description: + name: adaptive_number + sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77" + url: "https://pub.dev" + source: hosted + version: "1.0.0" analyzer: dependency: transitive description: @@ -46,6 +54,110 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "294a2edaf4814a378725bfe6358210196f5ea37af89ecd81bfa32960113d4948" + url: "https://pub.dev" + source: hosted + version: "4.0.3" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "99d3980049739a985cf9b21f30881f46db3ebc62c5b8d5e60e27440876b1ba1e" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "74691599a5bc750dc96a6b4bfd48f7d9d66453eab04c7f4063134800d6a5c573" + url: "https://pub.dev" + source: hosted + version: "2.4.14" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" + url: "https://pub.dev" + source: hosted + version: "8.0.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: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2" + url: "https://pub.dev" + source: hosted + version: "8.9.3" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + url: "https://pub.dev" + source: hosted + version: "1.4.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + url: "https://pub.dev" + source: hosted + version: "4.10.1" collection: dependency: transitive description: @@ -86,6 +198,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + dart_frog_auth: + dependency: "direct main" + description: + name: dart_frog_auth + sha256: a4c05e6764a82f8997dee9fb68661a473afea92527a2dd8a6677e5a1b454b463 + url: "https://pub.dev" + source: hosted + version: "1.2.0" + dart_jsonwebtoken: + dependency: "direct main" + description: + name: dart_jsonwebtoken + sha256: "06e02e18827d047f206e1051c15b493c9c29a2dba0f9b2a905d73748dec4f931" + url: "https://pub.dev" + source: hosted + version: "2.16.0" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + drift: + dependency: "direct main" + description: + name: drift + sha256: "76f23535e19a9f2be92f954e74d8802e96f526e5195d7408c1a20f6659043941" + url: "https://pub.dev" + source: hosted + version: "2.24.0" + drift_dev: + dependency: "direct dev" + description: + name: drift_dev + sha256: d1d90b0d55b22de412b77186f3bf3179a4b7e2acc4c8fb3a7aaf28a01abc194b + url: "https://pub.dev" + source: hosted + version: "2.24.0" + ed25519_edwards: + dependency: transitive + description: + name: ed25519_edwards + sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" file: dependency: transitive description: @@ -94,6 +262,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" frontend_server_client: dependency: transitive description: @@ -110,6 +286,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" hotreloader: dependency: transitive description: @@ -158,8 +342,16 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.1" - logging: + json_annotation: dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + logging: + dependency: "direct main" description: name: logging sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 @@ -230,6 +422,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" pool: dependency: transitive description: @@ -246,6 +446,29 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.5" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + shared_models: + dependency: "direct main" + description: + path: "../shared_models" + relative: true + source: path + version: "1.0.0" shelf: dependency: transitive description: @@ -286,6 +509,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" + url: "https://pub.dev" + source: hosted + version: "2.0.0" source_map_stack_trace: dependency: transitive description: @@ -310,6 +541,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sqlite3: + dependency: "direct main" + description: + name: sqlite3 + sha256: "35d3726fe18ab1463403a5cc8d97dbc81f2a0b08082e8173851363fcc97b6627" + url: "https://pub.dev" + source: hosted + version: "2.7.2" + sqlparser: + dependency: transitive + description: + name: sqlparser + sha256: "27dd0a9f0c02e22ac0eb42a23df9ea079ce69b52bb4a3b478d64e0ef34a263ee" + url: "https://pub.dev" + source: hosted + version: "0.41.0" stack_trace: dependency: transitive description: @@ -374,6 +629,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.8" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" typed_data: dependency: transitive description: @@ -382,6 +645,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" very_good_analysis: dependency: "direct dev" description: @@ -447,4 +718,4 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.5.0 <4.0.0" + dart: ">=3.6.0 <4.0.0" diff --git a/backend/pubspec.yaml b/backend/pubspec.yaml index 0c04b9e..bb327b2 100644 --- a/backend/pubspec.yaml +++ b/backend/pubspec.yaml @@ -8,10 +8,18 @@ environment: dependencies: dart_frog: ^1.1.0 + dart_frog_auth: ^1.2.0 + dart_jsonwebtoken: ^2.16.0 + drift: ^2.24.0 + logging: ^1.3.0 shared_models: path: ../shared_models + sqlite3: ^2.7.2 + uuid: ^4.5.1 dev_dependencies: + build_runner: ^2.4.14 + drift_dev: ^2.24.0 mocktail: ^1.0.3 test: ^1.25.5 very_good_analysis: ^5.1.0 diff --git a/backend/routes/[roomCode]/_middleware.dart b/backend/routes/[roomCode]/_middleware.dart new file mode 100644 index 0000000..8b435b4 --- /dev/null +++ b/backend/routes/[roomCode]/_middleware.dart @@ -0,0 +1,17 @@ +import 'package:backend/authenticator.dart'; +import 'package:backend/database.dart'; +import 'package:dart_frog/dart_frog.dart'; +import 'package:dart_frog_auth/dart_frog_auth.dart'; + +Handler middleware(Handler handler) { + return handler.use( + bearerAuthentication<User>( + authenticator: (context, token) async { + final authenticator = context.read<Authenticator>(); + return authenticator.verifyToken(token); + }, + // says to apply the middleware to all routes + applies: (_) async => true, + ), + ); +} diff --git a/backend/routes/[roomCode]/join.dart b/backend/routes/[roomCode]/join.dart new file mode 100644 index 0000000..9d95a7c --- /dev/null +++ b/backend/routes/[roomCode]/join.dart @@ -0,0 +1,5 @@ +import 'package:dart_frog/dart_frog.dart'; + +Response onRequest(RequestContext context, String roomCode) { + return Response(body: 'Joined $roomCode!'); +} diff --git a/backend/routes/_middleware.dart b/backend/routes/_middleware.dart new file mode 100644 index 0000000..5438077 --- /dev/null +++ b/backend/routes/_middleware.dart @@ -0,0 +1,15 @@ +// lib/routes/tasks/_middleware.dart +import 'package:dart_frog/dart_frog.dart'; +import 'package:logging/logging.dart'; + +final log = Logger(''); + +Handler middleware(Handler handler) { + return handler.use( + (handler) => (context) async { + final request = context.request; + log.info('${request.method.value} ${request.uri.path}'); + return await handler(context); + }, + ); +} diff --git a/backend/routes/auth/index.dart b/backend/routes/auth/index.dart new file mode 100644 index 0000000..08e47bc --- /dev/null +++ b/backend/routes/auth/index.dart @@ -0,0 +1,45 @@ +import 'dart:io'; + +import 'package:backend/authenticator.dart'; +import 'package:dart_frog/dart_frog.dart'; +import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; +import 'package:shared_models/user.dart'; + +Future<Response> onRequest(RequestContext context) async { + // Only allow POST requests + if (context.request.method != HttpMethod.post) { + return Response(statusCode: HttpStatus.methodNotAllowed); + } + + try { + // Parse the request body + final body = await context.request.json() as Map<String, dynamic>; + final createUserReq = CreateUserRequest.fromJson(body); + + // Generate token + final authenticator = context.read<Authenticator>(); + final token = await authenticator.generateToken(username: createUserReq.username); + + if (token == null) { + return Response.json( + statusCode: HttpStatus.internalServerError, + body: {'error': 'Failed to generate token'}, + ); + } + + // Return the token + return Response.json( + body: {'token': token}, + ); + } on JWTParseException { + return Response.json( + statusCode: HttpStatus.badRequest, + body: {'error': 'Username is required'}, + ); + } catch (e) { + return Response.json( + statusCode: HttpStatus.internalServerError, + body: {'error': 'Internal server error'}, + ); + } +} diff --git a/shared_models/lib/user.dart b/shared_models/lib/user.dart new file mode 100644 index 0000000..43d4464 --- /dev/null +++ b/shared_models/lib/user.dart @@ -0,0 +1,31 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'user.g.dart'; + +@JsonSerializable() +class User { + final String id; + final String name; + final String? roomId; + + User({ + required this.id, + required this.name, + this.roomId, + }); + + factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); + + Map<String, dynamic> toJson() => _$UserToJson(this); +} + +@JsonSerializable() +class CreateUserRequest { + final String username; + + CreateUserRequest({required this.username}); + + factory CreateUserRequest.fromJson(Map<String, dynamic> json) => _$CreateUserRequestFromJson(json); + + Map<String, dynamic> toJson() => _$CreateUserRequestToJson(this); +} diff --git a/shared_models/pubspec.lock b/shared_models/pubspec.lock index bb12c5f..af97ed3 100644 --- a/shared_models/pubspec.lock +++ b/shared_models/pubspec.lock @@ -62,6 +62,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "294a2edaf4814a378725bfe6358210196f5ea37af89ecd81bfa32960113d4948" + url: "https://pub.dev" + source: hosted + version: "4.0.3" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "99d3980049739a985cf9b21f30881f46db3ebc62c5b8d5e60e27440876b1ba1e" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "74691599a5bc750dc96a6b4bfd48f7d9d66453eab04c7f4063134800d6a5c573" + url: "https://pub.dev" + source: hosted + version: "2.4.14" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" + url: "https://pub.dev" + source: hosted + version: "8.0.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: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2" + url: "https://pub.dev" + source: hosted + version: "8.9.3" checked_yaml: dependency: transitive description: @@ -70,6 +118,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + url: "https://pub.dev" + source: hosted + version: "4.10.1" collection: dependency: transitive description: @@ -118,6 +174,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" frontend_server_client: dependency: transitive description: @@ -134,6 +198,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" http_multi_server: dependency: transitive description: @@ -366,6 +438,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" string_scanner: dependency: transitive description: @@ -406,6 +486,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.8" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" typed_data: dependency: transitive description: diff --git a/shared_models/pubspec.yaml b/shared_models/pubspec.yaml index 4fb1fbd..840151c 100644 --- a/shared_models/pubspec.yaml +++ b/shared_models/pubspec.yaml @@ -11,5 +11,6 @@ dependencies: dev_dependencies: json_serializable: ^6.9.3 + build_runner: ^2.4.14 lints: ^5.0.0 test: ^1.24.0