Skip to content

Refactor use mongodb objectid isntead of UUID #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 20, 2025
3 changes: 0 additions & 3 deletions lib/src/config/app_dependencies.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -173,7 +172,6 @@ class AppDependencies {
authTokenService = JwtAuthTokenService(
userRepository: userRepository,
blacklistService: tokenBlacklistService,
uuidGenerator: const Uuid(),
log: Logger('JwtAuthTokenService'),
);
verificationCodeStorageService = InMemoryVerificationCodeStorageService();
Expand All @@ -186,7 +184,6 @@ class AppDependencies {
emailRepository: emailRepository,
userAppSettingsRepository: userAppSettingsRepository,
userContentPreferencesRepository: userContentPreferencesRepository,
uuidGenerator: const Uuid(),
log: Logger('AuthService'),
);
dashboardSummaryService = DashboardSummaryService(
Expand Down
12 changes: 5 additions & 7 deletions lib/src/services/auth_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -25,7 +25,6 @@ class AuthService {
required HtDataRepository<UserContentPreferences>
userContentPreferencesRepository,
required PermissionService permissionService,
required Uuid uuidGenerator,
required Logger log,
}) : _userRepository = userRepository,
_authTokenService = authTokenService,
Expand All @@ -34,7 +33,6 @@ class AuthService {
_emailRepository = emailRepository,
_userAppSettingsRepository = userAppSettingsRepository,
_userContentPreferencesRepository = userContentPreferencesRepository,
_uuid = uuidGenerator,
_log = log;

final HtDataRepository<User> _userRepository;
Expand All @@ -46,7 +44,6 @@ class AuthService {
_userContentPreferencesRepository;
final PermissionService _permissionService;
final Logger _log;
final Uuid _uuid;

/// Initiates the email sign-in process.
///
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(),
Expand Down
10 changes: 3 additions & 7 deletions lib/src/services/jwt_auth_token_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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<User> userRepository,
required TokenBlacklistService blacklistService,
required Uuid uuidGenerator,
required Logger log,
}) : _userRepository = userRepository,
_blacklistService = blacklistService,
_uuid = uuidGenerator,
_log = log;

final HtDataRepository<User> _userRepository;
final TokenBlacklistService _blacklistService;
final Uuid _uuid;
final Logger _log;

// --- Configuration ---
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 2 additions & 4 deletions routes/_middleware.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -55,8 +55,7 @@ Handler middleware(Handler handler) {
_log.info(
'[REQ_LIFECYCLE] Request received. Generating RequestId...',
);
final uuid = context.read<Uuid>();
final requestId = RequestId(uuid.v4());
final requestId = RequestId(ObjectId().oid);
_log.info('[REQ_LIFECYCLE] RequestId generated: ${requestId.id}');
return innerHandler(context.provide<RequestId>(() => requestId));
};
Expand All @@ -76,7 +75,6 @@ Handler middleware(Handler handler) {
final deps = AppDependencies.instance;
return handler
.use(provider<ModelRegistryMap>((_) => modelRegistry))
.use(provider<Uuid>((_) => const Uuid()))
.use(
provider<HtDataRepository<Headline>>(
(_) => deps.headlineRepository,
Expand Down
3 changes: 3 additions & 0 deletions routes/api/v1/data/[id]/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ Future<Response> _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 {
Expand Down
7 changes: 7 additions & 0 deletions routes/api/v1/data/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -152,6 +153,12 @@ Future<Response> _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);
Expand Down
Loading