- Add self-hosted push notifications using VAPID (no external services) - Create PushSubscription model in database - Implement server-side push logic with web-push library - Add notification helpers for list events (items, invitations, collaborators) - Create NotificationSettings UI component for profile page - Add usePushNotifications React hook for subscription management - Create API routes for subscription management and VAPID key - Update service worker with push event handlers - Integrate notifications into item actions (add, remove, complete) - Integrate notifications into sharing actions (invite, accept, remove) - Add VAPID key generation script - Add comprehensive documentation and setup guides Notifications are sent when: - Items are added/removed/completed in shared lists - Users are invited to lists - Invitations are accepted - Collaborators are removed from lists Works on iOS 16.4+ (when installed to home screen), Android, and desktop browsers.
114 lines
3.4 KiB
Plaintext
114 lines
3.4 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "mysql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
email String @unique
|
|
name String?
|
|
passwordHash String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
ownedLists List[] @relation("ListOwner")
|
|
collaborations Collaborator[]
|
|
sentInvitations Invitation[] @relation("InvitationInviter")
|
|
receivedInvitations Invitation[] @relation("InvitationReceiver")
|
|
pushSubscriptions PushSubscription[]
|
|
|
|
@@index([email])
|
|
}
|
|
|
|
model PushSubscription {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
endpoint String @db.Text
|
|
p256dh String @db.Text
|
|
auth String @db.Text
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, endpoint(255)])
|
|
@@index([userId])
|
|
}
|
|
|
|
model List {
|
|
id String @id @default(cuid())
|
|
title String
|
|
color String @default("#3B82F6")
|
|
ownerId String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
owner User @relation("ListOwner", fields: [ownerId], references: [id], onDelete: Cascade)
|
|
items Item[]
|
|
collaborators Collaborator[]
|
|
invitations Invitation[]
|
|
|
|
@@index([ownerId])
|
|
@@index([ownerId, createdAt])
|
|
}
|
|
|
|
model Collaborator {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
listId String
|
|
permission String // 'editor' or 'viewer'
|
|
createdAt DateTime @default(now())
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
list List @relation(fields: [listId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, listId])
|
|
@@index([userId])
|
|
@@index([listId])
|
|
@@index([listId, permission])
|
|
}
|
|
|
|
model Invitation {
|
|
id String @id @default(cuid())
|
|
email String
|
|
listId String
|
|
inviterId String
|
|
permission String // 'editor' or 'viewer'
|
|
status String @default("pending") // 'pending', 'accepted', 'declined'
|
|
createdAt DateTime @default(now())
|
|
expiresAt DateTime
|
|
list List @relation(fields: [listId], references: [id], onDelete: Cascade)
|
|
inviter User @relation("InvitationInviter", fields: [inviterId], references: [id], onDelete: Cascade)
|
|
receiver User? @relation("InvitationReceiver", fields: [email], references: [email], onDelete: Cascade)
|
|
|
|
@@index([email])
|
|
@@index([listId])
|
|
@@index([inviterId])
|
|
@@index([status])
|
|
@@index([email, status])
|
|
@@index([listId, status])
|
|
}
|
|
|
|
model Item {
|
|
id String @id @default(cuid())
|
|
title String
|
|
description String?
|
|
completed Boolean @default(false)
|
|
priority String @default("medium") // low, medium, high
|
|
dueDate DateTime?
|
|
tags Json @default("[]")
|
|
notes String? @db.Text
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
listId String
|
|
list List @relation(fields: [listId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([listId])
|
|
@@index([completed])
|
|
@@index([priority])
|
|
@@index([dueDate])
|
|
@@index([createdAt])
|
|
@@index([listId, completed])
|
|
@@index([listId, priority])
|
|
}
|