feat: implement notification system with Mailgun email integration

- Add notifications table with migration
- Create NotificationService with area-of-interest matching
- Integrate Mailgun for email delivery
- Add notification API endpoints (get, mark as read, unread count)
- Create scheduled jobs for event reminders and cleanup
- Build NotificationBell component for frontend
- Add notification triggers for reports and events
- Implement comprehensive test script
- Update Header with notification bell
- Add email templates with HTML formatting

Requirements satisfied:
- 10.1: Users can define areas of interest (1-50km radius)
- 10.2: Notifications sent within 10 minutes
- 10.3: Notifications for new reports/events in user area
- 10.4: Configurable notification preferences (email/in-app)

Task 33 completed
This commit is contained in:
2025-11-17 00:31:23 +00:00
parent 052ba46bdc
commit d61c21c54d
21 changed files with 1716 additions and 3 deletions

View File

@@ -0,0 +1,25 @@
-- CreateTable
CREATE TABLE `notifications` (
`id` VARCHAR(36) NOT NULL,
`user_id` VARCHAR(36) NOT NULL,
`type` ENUM('new_report', 'new_event', 'event_reminder', 'report_verified', 'report_disputed') NOT NULL,
`title` VARCHAR(255) NOT NULL,
`message` TEXT NOT NULL,
`related_id` VARCHAR(36) NULL COMMENT 'ID of related report or event',
`related_type` ENUM('report', 'event') NULL,
`latitude` DECIMAL(10, 8) NULL,
`longitude` DECIMAL(11, 8) NULL,
`read` BOOLEAN NOT NULL DEFAULT false,
`email_sent` BOOLEAN NOT NULL DEFAULT false,
`created_at` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
INDEX `idx_user_id`(`user_id`),
INDEX `idx_created_at`(`created_at`),
INDEX `idx_read`(`read`),
INDEX `idx_type`(`type`),
INDEX `idx_lat_lng`(`latitude`, `longitude`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `notifications` ADD CONSTRAINT `notifications_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION;

View File

@@ -27,6 +27,7 @@ model User {
reports Report[]
cleanedReports Report[] @relation("CleanedByUser")
verifications Verification[]
notifications Notification[]
@@index([clerkId], map: "idx_clerk_id")
@@index([email], map: "idx_email")
@@ -198,3 +199,39 @@ enum reports_verification_status {
verified
disputed
}
model Notification {
id String @id @default(dbgenerated("(uuid())")) @db.VarChar(36)
userId String @map("user_id") @db.VarChar(36)
type notifications_type
title String @db.VarChar(255)
message String @db.Text
relatedId String? @map("related_id") @db.VarChar(36)
relatedType notifications_related_type? @map("related_type")
latitude Decimal? @db.Decimal(10, 8)
longitude Decimal? @db.Decimal(11, 8)
read Boolean @default(false)
emailSent Boolean @default(false) @map("email_sent")
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "notifications_ibfk_1")
@@index([userId], map: "idx_user_id")
@@index([createdAt], map: "idx_created_at")
@@index([read], map: "idx_read")
@@index([type], map: "idx_type")
@@index([latitude, longitude], map: "idx_lat_lng")
@@map("notifications")
}
enum notifications_type {
new_report
new_event
event_reminder
report_verified
report_disputed
}
enum notifications_related_type {
report
event
}