diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 11e5b6e9..ab7ed984 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -188,6 +188,7 @@ model User { initiatedBackups DatabaseBackup[] @relation("BackupInitiatedBy") restoredBackups DatabaseBackup[] @relation("BackupRestoredBy") emailStatus EmailStatus @default(ACTIVE) @map("email_status") + fcmToken String? @map("fcm_token") notifications Notification[] linkClicks LinkClick[] emailEngagements EmailEngagement[] @@ -490,6 +491,7 @@ model ActivityLog { @@index([userId]) @@index([action]) @@index([createdAt]) + @@index([scheduledAt]) @@index([entityType, entityId]) @@map("activity_logs") } @@ -636,6 +638,7 @@ model SearchAnalytics { @@index([userId]) @@index([queryId]) @@index([createdAt]) + @@index([scheduledAt]) @@index([hasResults]) @@index([converted]) @@map("search_analytics") @@ -699,12 +702,17 @@ model Notification { metadata Json? createdAt DateTime @default(now()) @map("created_at") readAt DateTime? @map("read_at") + scheduledAt DateTime? @map("scheduled_at") + isRecurring Boolean @default(false) @map("is_recurring") + cron String? @map("cron") + timezone String @default("UTC") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@index([status]) @@index([createdAt]) + @@index([scheduledAt]) @@map("notifications") } @@ -722,6 +730,7 @@ model LinkClick { @@index([url]) @@index([userId]) @@index([createdAt]) + @@index([scheduledAt]) @@map("link_clicks") } @@ -740,6 +749,7 @@ model EmailEngagement { @@index([userId]) @@index([trackingId]) @@index([createdAt]) + @@index([scheduledAt]) @@map("email_engagements") } diff --git a/src/notifications/notifications.service.ts b/src/notifications/notifications.service.ts index b897852f..a5728d22 100644 --- a/src/notifications/notifications.service.ts +++ b/src/notifications/notifications.service.ts @@ -28,6 +28,12 @@ export class NotificationsService { }); // 2. Try real-time delivery + // FCM Push Integration + const user = await this.prisma.user.findUnique({ where: { id: userId }, select: { fcmToken: true } }); + if (user?.fcmToken) { + console.log(Sending FCM notification to token: \); + // In production, use admin.messaging().send() here + } const delivered = this.gateway.sendToUser(userId, 'notification', notification); if (delivered) { @@ -70,4 +76,33 @@ export class NotificationsService { } } } + + async scheduleNotification( + userId: string, + title: string, + message: string, + type: string, + scheduleData: { scheduledAt: Date; isRecurring?: boolean; cron?: string; timezone?: string }, + ) { + return this.prisma.notification.create({ + data: { + userId, + title, + message, + type, + status: 'PENDING', + ...scheduleData, + }, + }); + } + + async cancelScheduledNotification(id: string) { + return this.prisma.notification.deleteMany({ + where: { + id, + status: 'PENDING', + scheduledAt: { not: null }, + }, + }); + } }