diff --git a/lib/src/config/app_dependencies.dart b/lib/src/config/app_dependencies.dart index 2fd2018..7f79b38 100644 --- a/lib/src/config/app_dependencies.dart +++ b/lib/src/config/app_dependencies.dart @@ -15,7 +15,6 @@ import 'package:ht_email_inmemory/ht_email_inmemory.dart'; import 'package:ht_email_repository/ht_email_repository.dart'; import 'package:ht_shared/ht_shared.dart'; import 'package:logging/logging.dart'; -import 'package:uuid/uuid.dart'; /// {@template app_dependencies} /// A singleton class responsible for initializing and providing all application @@ -173,7 +172,6 @@ class AppDependencies { authTokenService = JwtAuthTokenService( userRepository: userRepository, blacklistService: tokenBlacklistService, - uuidGenerator: const Uuid(), log: Logger('JwtAuthTokenService'), ); verificationCodeStorageService = InMemoryVerificationCodeStorageService(); @@ -186,7 +184,6 @@ class AppDependencies { emailRepository: emailRepository, userAppSettingsRepository: userAppSettingsRepository, userContentPreferencesRepository: userContentPreferencesRepository, - uuidGenerator: const Uuid(), log: Logger('AuthService'), ); dashboardSummaryService = DashboardSummaryService( diff --git a/lib/src/services/auth_service.dart b/lib/src/services/auth_service.dart index df98aad..acd4e9d 100644 --- a/lib/src/services/auth_service.dart +++ b/lib/src/services/auth_service.dart @@ -6,7 +6,7 @@ import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_email_repository/ht_email_repository.dart'; import 'package:ht_shared/ht_shared.dart'; import 'package:logging/logging.dart'; -import 'package:uuid/uuid.dart'; +import 'package:mongo_dart/mongo_dart.dart'; /// {@template auth_service} /// Service responsible for orchestrating authentication logic on the backend. @@ -25,7 +25,6 @@ class AuthService { required HtDataRepository userContentPreferencesRepository, required PermissionService permissionService, - required Uuid uuidGenerator, required Logger log, }) : _userRepository = userRepository, _authTokenService = authTokenService, @@ -34,7 +33,6 @@ class AuthService { _emailRepository = emailRepository, _userAppSettingsRepository = userAppSettingsRepository, _userContentPreferencesRepository = userContentPreferencesRepository, - _uuid = uuidGenerator, _log = log; final HtDataRepository _userRepository; @@ -46,7 +44,6 @@ class AuthService { _userContentPreferencesRepository; final PermissionService _permissionService; final Logger _log; - final Uuid _uuid; /// Initiates the email sign-in process. /// @@ -212,7 +209,7 @@ class AuthService { // All new users created via the public API get the standard role. // Admin users must be provisioned out-of-band (e.g., via fixtures). user = User( - id: _uuid.v4(), + id: ObjectId().oid, email: email, appRole: AppUserRole.standardUser, dashboardRole: DashboardUserRole.none, @@ -268,11 +265,12 @@ class AuthService { // 1. Create anonymous user User user; try { + final newId = ObjectId().oid; user = User( - id: _uuid.v4(), + id: newId, // Use a unique placeholder email for anonymous users to satisfy the // non-nullable email constraint. - email: '${_uuid.v4()}@anonymous.com', + email: '$newId@anonymous.com', appRole: AppUserRole.guestUser, dashboardRole: DashboardUserRole.none, createdAt: DateTime.now(), diff --git a/lib/src/services/jwt_auth_token_service.dart b/lib/src/services/jwt_auth_token_service.dart index 5f7774f..4aaa86a 100644 --- a/lib/src/services/jwt_auth_token_service.dart +++ b/lib/src/services/jwt_auth_token_service.dart @@ -4,7 +4,7 @@ import 'package:ht_api/src/services/token_blacklist_service.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_shared/ht_shared.dart'; import 'package:logging/logging.dart'; -import 'package:uuid/uuid.dart'; +import 'package:mongo_dart/mongo_dart.dart'; /// {@template jwt_auth_token_service} /// An implementation of [AuthTokenService] using JSON Web Tokens (JWT). @@ -19,20 +19,16 @@ class JwtAuthTokenService implements AuthTokenService { /// - [userRepository]: To fetch user details after validating the token's /// subject claim. /// - [blacklistService]: To manage the blacklist of invalidated tokens. - /// - [uuidGenerator]: For creating unique JWT IDs (jti). const JwtAuthTokenService({ required HtDataRepository userRepository, required TokenBlacklistService blacklistService, - required Uuid uuidGenerator, required Logger log, }) : _userRepository = userRepository, _blacklistService = blacklistService, - _uuid = uuidGenerator, _log = log; final HtDataRepository _userRepository; final TokenBlacklistService _blacklistService; - final Uuid _uuid; final Logger _log; // --- Configuration --- @@ -61,7 +57,7 @@ class JwtAuthTokenService implements AuthTokenService { 'exp': expiry.millisecondsSinceEpoch ~/ 1000, // Expiration Time 'iat': now.millisecondsSinceEpoch ~/ 1000, // Issued At 'iss': _issuer, // Issuer - 'jti': _uuid.v4(), // JWT ID (for potential blacklisting) + 'jti': ObjectId().oid, // JWT ID (for potential blacklisting) // Custom claims (optional, include what's useful) 'email': user.email, // Kept for convenience // Embed the new enum-based roles. Use .name for string value. @@ -70,7 +66,7 @@ class JwtAuthTokenService implements AuthTokenService { }, issuer: _issuer, subject: user.id, - jwtId: _uuid.v4(), // Re-setting jti here for clarity if needed + jwtId: ObjectId().oid, // Re-setting jti here for clarity if needed ); // Sign the token using HMAC-SHA256 diff --git a/pubspec.yaml b/pubspec.yaml index 96e31e9..b4fe4dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,7 +39,6 @@ dependencies: meta: ^1.16.0 mongo_dart: ^0.10.5 shelf_cors_headers: ^0.1.5 - uuid: ^4.5.1 dev_dependencies: mocktail: ^1.0.3 diff --git a/routes/_middleware.dart b/routes/_middleware.dart index d93bd9d..3984ed0 100644 --- a/routes/_middleware.dart +++ b/routes/_middleware.dart @@ -14,7 +14,7 @@ import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_email_repository/ht_email_repository.dart'; import 'package:ht_shared/ht_shared.dart'; import 'package:logging/logging.dart'; -import 'package:uuid/uuid.dart'; +import 'package:mongo_dart/mongo_dart.dart'; // --- Middleware Definition --- final _log = Logger('RootMiddleware'); @@ -55,8 +55,7 @@ Handler middleware(Handler handler) { _log.info( '[REQ_LIFECYCLE] Request received. Generating RequestId...', ); - final uuid = context.read(); - final requestId = RequestId(uuid.v4()); + final requestId = RequestId(ObjectId().oid); _log.info('[REQ_LIFECYCLE] RequestId generated: ${requestId.id}'); return innerHandler(context.provide(() => requestId)); }; @@ -76,7 +75,6 @@ Handler middleware(Handler handler) { final deps = AppDependencies.instance; return handler .use(provider((_) => modelRegistry)) - .use(provider((_) => const Uuid())) .use( provider>( (_) => deps.headlineRepository, diff --git a/routes/api/v1/data/[id]/index.dart b/routes/api/v1/data/[id]/index.dart index 11b90dc..b390ebe 100644 --- a/routes/api/v1/data/[id]/index.dart +++ b/routes/api/v1/data/[id]/index.dart @@ -185,6 +185,9 @@ Future _handlePut( throw const BadRequestException('Missing or invalid request body.'); } + // Standardize timestamp before model creation + requestBody['updatedAt'] = DateTime.now().toUtc().toIso8601String(); + // Deserialize using ModelConfig's fromJson, catching TypeErrors locally dynamic itemToUpdate; try { diff --git a/routes/api/v1/data/index.dart b/routes/api/v1/data/index.dart index eef39d3..6a5fd42 100644 --- a/routes/api/v1/data/index.dart +++ b/routes/api/v1/data/index.dart @@ -7,6 +7,7 @@ import 'package:ht_api/src/rbac/permission_service.dart'; import 'package:ht_api/src/registry/model_registry.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; import 'package:ht_shared/ht_shared.dart'; +import 'package:mongo_dart/mongo_dart.dart'; /// Handles requests for the /api/v1/data collection endpoint. /// Dispatches requests to specific handlers based on the HTTP method. @@ -152,6 +153,12 @@ Future _handlePost(RequestContext context) async { throw const BadRequestException('Missing or invalid request body.'); } + // Standardize ID and timestamps before model creation + final now = DateTime.now().toUtc().toIso8601String(); + requestBody['id'] = ObjectId().oid; + requestBody['createdAt'] = now; + requestBody['updatedAt'] = now; + dynamic itemToCreate; try { itemToCreate = modelConfig.fromJson(requestBody);