Skip to content
11 changes: 11 additions & 0 deletions lib/src/config/app_dependencies.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class AppDependencies {
late final DataRepository<UserContentPreferences>
userContentPreferencesRepository;
late final DataRepository<RemoteConfig> remoteConfigRepository;
late final DataRepository<LocalAd> localAdRepository;
late final EmailRepository emailRepository;

// Services
Expand Down Expand Up @@ -201,6 +202,16 @@ class AppDependencies {

emailRepository = EmailRepository(emailClient: emailClient);

final localAdClient = DataMongodb<LocalAd>(
connectionManager: _mongoDbConnectionManager,
modelName: 'local_ads',
fromJson: LocalAd.fromJson,
toJson: LocalAd.toJson,
searchableFields: ['title'],
logger: Logger('DataMongodb<LocalAd>'),
);
localAdRepository = DataRepository(dataClient: localAdClient);

// 5. Initialize Services
tokenBlacklistService = MongoDbTokenBlacklistService(
connectionManager: _mongoDbConnectionManager,
Expand Down
6 changes: 6 additions & 0 deletions lib/src/rbac/permissions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
6 changes: 6 additions & 0 deletions lib/src/rbac/role_permissions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ final Set<String> _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
Expand Down Expand Up @@ -72,6 +73,11 @@ final Set<String> _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
Expand Down
14 changes: 14 additions & 0 deletions lib/src/registry/data_operation_registry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ class DataOperationRegistry {
.read(id: id, userId: null),
'remote_config': (c, id) =>
c.read<DataRepository<RemoteConfig>>().read(id: id, userId: null),
'local_ad': (c, id) =>
c.read<DataRepository<LocalAd>>().read(id: id, userId: null),
'dashboard_summary': (c, id) =>
c.read<DashboardSummaryService>().getSummary(),
});
Expand Down Expand Up @@ -159,6 +161,9 @@ class DataOperationRegistry {
sort: s,
pagination: p,
),
'local_ad': (c, uid, f, s, p) => c
.read<DataRepository<LocalAd>>()
.readAll(userId: uid, filter: f, sort: s, pagination: p),
});

// --- Register Item Creators ---
Expand Down Expand Up @@ -186,6 +191,10 @@ class DataOperationRegistry {
'remote_config': (c, item, uid) => c
.read<DataRepository<RemoteConfig>>()
.create(item: item as RemoteConfig, userId: uid),
'local_ad': (c, item, uid) => c.read<DataRepository<LocalAd>>().create(
item: item as LocalAd,
userId: uid,
),
});

// --- Register Item Updaters ---
Expand Down Expand Up @@ -228,6 +237,9 @@ class DataOperationRegistry {
'remote_config': (c, id, item, uid) => c
.read<DataRepository<RemoteConfig>>()
.update(id: id, item: item as RemoteConfig, userId: uid),
'local_ad': (c, id, item, uid) => c
.read<DataRepository<LocalAd>>()
.update(id: id, item: item as LocalAd, userId: uid),
});

// --- Register Item Deleters ---
Expand All @@ -251,6 +263,8 @@ class DataOperationRegistry {
.delete(id: id, userId: uid),
'remote_config': (c, id, uid) =>
c.read<DataRepository<RemoteConfig>>().delete(id: id, userId: uid),
'local_ad': (c, id, uid) =>
c.read<DataRepository<LocalAd>>().delete(id: id, userId: uid),
});
}
}
25 changes: 25 additions & 0 deletions lib/src/registry/model_registry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,31 @@ final modelRegistry = <String, ModelConfig<dynamic>>{
type: RequiredPermissionType.unsupported,
),
),
'local_ad': ModelConfig<LocalAd>(
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.
Expand Down
14 changes: 14 additions & 0 deletions lib/src/services/database_seeding_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ class DatabaseSeedingService {
getId: (item) => item.id,
toJson: (item) => item.toJson(),
);
await _seedCollection<LocalAd>(
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.');
}
Expand Down Expand Up @@ -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`
Expand Down
5 changes: 5 additions & 0 deletions routes/_middleware.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ Handler middleware(Handler handler) {
(_) => deps.remoteConfigRepository,
),
)
.use(
provider<DataRepository<LocalAd>>(
(_) => deps.localAdRepository,
),
)
.use(provider<EmailRepository>((_) => deps.emailRepository))
.use(
provider<TokenBlacklistService>(
Expand Down
Loading