diff --git a/lib/src/config/app_dependencies.dart b/lib/src/config/app_dependencies.dart index 2667118..76dcff2 100644 --- a/lib/src/config/app_dependencies.dart +++ b/lib/src/config/app_dependencies.dart @@ -59,6 +59,7 @@ class AppDependencies { late final DataRepository userContentPreferencesRepository; late final DataRepository remoteConfigRepository; + late final DataRepository localAdRepository; late final EmailRepository emailRepository; // Services @@ -201,6 +202,16 @@ class AppDependencies { emailRepository = EmailRepository(emailClient: emailClient); + final localAdClient = DataMongodb( + connectionManager: _mongoDbConnectionManager, + modelName: 'local_ads', + fromJson: LocalAd.fromJson, + toJson: LocalAd.toJson, + searchableFields: ['title'], + logger: Logger('DataMongodb'), + ); + localAdRepository = DataRepository(dataClient: localAdClient); + // 5. Initialize Services tokenBlacklistService = MongoDbTokenBlacklistService( connectionManager: _mongoDbConnectionManager, diff --git a/lib/src/rbac/permissions.dart b/lib/src/rbac/permissions.dart index 33821ee..8bd383f 100644 --- a/lib/src/rbac/permissions.dart +++ b/lib/src/rbac/permissions.dart @@ -68,6 +68,12 @@ abstract class Permissions { static const String userPreferenceBypassLimits = 'user_preference.bypass_limits'; + // Local Ad Permissions + static const String localAdCreate = 'local_ad.create'; + static const String localAdRead = 'local_ad.read'; + static const String localAdUpdate = 'local_ad.update'; + static const String localAdDelete = 'local_ad.delete'; + // General System Permissions static const String rateLimitingBypass = 'rate_limiting.bypass'; } diff --git a/lib/src/rbac/role_permissions.dart b/lib/src/rbac/role_permissions.dart index ec892c1..a6660ef 100644 --- a/lib/src/rbac/role_permissions.dart +++ b/lib/src/rbac/role_permissions.dart @@ -14,6 +14,7 @@ final Set _appGuestUserPermissions = { Permissions.userContentPreferencesReadOwned, Permissions.userContentPreferencesUpdateOwned, Permissions.remoteConfigRead, + Permissions.localAdRead, // Allows a user to update their own User object. This is essential for // features like updating the `feedActionStatus` (e.g., when a user // dismisses an in-feed prompt, etc). The endpoint handler ensures only @@ -72,6 +73,11 @@ final Set _dashboardAdminPermissions = { Permissions.remoteConfigUpdate, Permissions.remoteConfigDelete, Permissions.userPreferenceBypassLimits, + // Added localAd CRUD permissions for admins + Permissions.localAdCreate, + Permissions.localAdRead, + Permissions.localAdUpdate, + Permissions.localAdDelete, }; /// Defines the mapping between user roles (both app and dashboard) and the diff --git a/lib/src/registry/data_operation_registry.dart b/lib/src/registry/data_operation_registry.dart index 9f45053..c8cb253 100644 --- a/lib/src/registry/data_operation_registry.dart +++ b/lib/src/registry/data_operation_registry.dart @@ -108,6 +108,8 @@ class DataOperationRegistry { .read(id: id, userId: null), 'remote_config': (c, id) => c.read>().read(id: id, userId: null), + 'local_ad': (c, id) => + c.read>().read(id: id, userId: null), 'dashboard_summary': (c, id) => c.read().getSummary(), }); @@ -159,6 +161,9 @@ class DataOperationRegistry { sort: s, pagination: p, ), + 'local_ad': (c, uid, f, s, p) => c + .read>() + .readAll(userId: uid, filter: f, sort: s, pagination: p), }); // --- Register Item Creators --- @@ -186,6 +191,10 @@ class DataOperationRegistry { 'remote_config': (c, item, uid) => c .read>() .create(item: item as RemoteConfig, userId: uid), + 'local_ad': (c, item, uid) => c.read>().create( + item: item as LocalAd, + userId: uid, + ), }); // --- Register Item Updaters --- @@ -228,6 +237,9 @@ class DataOperationRegistry { 'remote_config': (c, id, item, uid) => c .read>() .update(id: id, item: item as RemoteConfig, userId: uid), + 'local_ad': (c, id, item, uid) => c + .read>() + .update(id: id, item: item as LocalAd, userId: uid), }); // --- Register Item Deleters --- @@ -251,6 +263,8 @@ class DataOperationRegistry { .delete(id: id, userId: uid), 'remote_config': (c, id, uid) => c.read>().delete(id: id, userId: uid), + 'local_ad': (c, id, uid) => + c.read>().delete(id: id, userId: uid), }); } } diff --git a/lib/src/registry/model_registry.dart b/lib/src/registry/model_registry.dart index 427e257..645c870 100644 --- a/lib/src/registry/model_registry.dart +++ b/lib/src/registry/model_registry.dart @@ -362,6 +362,31 @@ final modelRegistry = >{ type: RequiredPermissionType.unsupported, ), ), + 'local_ad': ModelConfig( + fromJson: LocalAd.fromJson, + getId: (ad) => (ad as dynamic).id as String, // Corrected to access id + getOwnerId: null, // LocalAd is a global resource, not user-owned + getCollectionPermission: const ModelActionPermission( + type: RequiredPermissionType.specificPermission, + permission: Permissions.localAdRead, + ), + getItemPermission: const ModelActionPermission( + type: RequiredPermissionType.specificPermission, + permission: Permissions.localAdRead, + ), + postPermission: const ModelActionPermission( + type: RequiredPermissionType.adminOnly, + permission: Permissions.localAdCreate, + ), + putPermission: const ModelActionPermission( + type: RequiredPermissionType.adminOnly, + permission: Permissions.localAdUpdate, + ), + deletePermission: const ModelActionPermission( + type: RequiredPermissionType.adminOnly, + permission: Permissions.localAdDelete, + ), + ), }; /// Type alias for the ModelRegistry map for easier provider usage. diff --git a/lib/src/services/database_seeding_service.dart b/lib/src/services/database_seeding_service.dart index 1c4636b..8a8d14f 100644 --- a/lib/src/services/database_seeding_service.dart +++ b/lib/src/services/database_seeding_service.dart @@ -45,6 +45,13 @@ class DatabaseSeedingService { getId: (item) => item.id, toJson: (item) => item.toJson(), ); + await _seedCollection( + collectionName: 'local_ads', + fixtureData: localAdsFixturesData, + getId: (item) => (item as dynamic).id as String, + // ignore: unnecessary_lambdas + toJson: (item) => LocalAd.toJson(item), + ); _log.info('Database seeding process completed.'); } @@ -131,6 +138,13 @@ class DatabaseSeedingService { .collection('countries') .createIndex(keys: {'name': 1}, name: 'countries_name_index'); + /// Index for searching local ads by adType. + /// This index supports efficient queries and filtering on the 'adType' field + /// of local ad documents. + await _db + .collection('local_ads') + .createIndex(keys: {'adType': 1}, name: 'local_ads_adType_index'); + // --- TTL and Unique Indexes via runCommand --- // The following indexes are created using the generic `runCommand` because // they require specific options not exposed by the simpler `createIndex` diff --git a/routes/_middleware.dart b/routes/_middleware.dart index 51acc21..e21a84c 100644 --- a/routes/_middleware.dart +++ b/routes/_middleware.dart @@ -127,6 +127,11 @@ Handler middleware(Handler handler) { (_) => deps.remoteConfigRepository, ), ) + .use( + provider>( + (_) => deps.localAdRepository, + ), + ) .use(provider((_) => deps.emailRepository)) .use( provider(