My-Weekly-ToDo-List/prisma/schema.prisma
mARTin-B78 8958df03ce feat: focus-triggered MS To-Do delta sync for near-instant change reflection
Pull-sync now runs on visibilitychange + window focus (in addition to mount
and the slow 15-min safety-net interval), throttled to one call per 5s. The
moment the user returns to the tab the app reconciles, so an Outlook-side
checkbox/star/title change shows up within a second of refocusing the tab
instead of after a 15-minute wait.

To keep this cheap, the Microsoft Graph delta endpoint
(/me/todo/lists/{id}/tasks/delta) is used when we have a stored deltaToken
for a synced list — typically a few hundred bytes per call when nothing
changed. Tokens are persisted on SomedayList.syncDeltaToken (new column,
migration applied) and refreshed each call. On 410 Gone (token expired
>30 days) we fall back to a full fetch and prime a new chain. Delta also
brings remote deletions (@removed) so they propagate locally without
needing a full list scan.

v1.104.0

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 21:58:00 +02:00

388 lines
16 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}
model User {
id String @id @default(cuid())
email String @unique
passwordHash String?
name String?
image String?
emailVerified DateTime?
verifiedAt DateTime?
emailVerificationToken String?
emailVerificationExpires DateTime?
passwordResetToken String?
passwordResetExpires DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
timezone String @default("UTC")
autoRolling Boolean @default(false)
protectEventTimes Boolean @default(false)
language String @default("de")
dateFormat String @default("yyyy-MM-dd")
timeFormat String @default("24h")
startHour Int @default(8)
endHour Int @default(18)
showNextTask Boolean @default(false)
calendarEditMode Boolean @default(false)
focusTimerDuration Int @default(25)
focusBreakDuration Int @default(5)
showTimeGrid Boolean @default(true)
showSomeday Boolean @default(true)
showAllDayEvents Boolean @default(true)
showSchedule Boolean @default(true)
cellDuration Int @default(30)
viewStyle String @default("simple")
viewDays Int @default(7)
fontSize String @default("M")
goalFallbackType String @default("quote")
goalDefaultSentence String @default("goal of the week")
goalFontFamily String? @default("Lato")
goalFontSize String? @default("1rem")
goalFontWeight String? @default("500")
headlineFont String @default("Oswald")
headlineFontSize String? @default("1.5rem")
headlineFontWeight String? @default("900")
dateFontFamily String? @default("Inter")
dateFontSize String? @default("0.65rem")
dateFontWeight String? @default("400")
timeTaskFontFamily String? @default("Inter")
timeTaskFontSize String? @default("0.75rem")
timeTaskFontWeight String? @default("500")
bodyFont String @default("Inter")
taskFontFamily String? @default("Inter")
taskFontSize String? @default("0.9rem")
taskFontWeight String? @default("400")
eventFontFamily String? @default("Inter")
eventFontSize String? @default("0.85rem")
eventFontWeight String? @default("400")
lightTheme Json?
darkTheme Json?
fontWeight String @default("400")
weekdayColor String? @default("#0ea5e9")
dateColor String? @default("#888888")
taskColor String? @default("#333333")
todayHighlightColor String? @default("#f0fafa")
weekendColorSat String? @default("#ffc107")
weekendColorSun String? @default("#dc2626")
pastDayColor String? @default("#a6a6a7")
goalScope String @default("week")
dateLayout String @default("right")
mobileDateLayout String @default("below")
dateAlignment String @default("center")
dateVerticalAlign String? @default("middle")
headerDisplay String? @default("kw")
headerCustomFormat String? @default("KW WW | YYYY")
hourLabelFormat String @default("short")
showSubHourSlots Boolean @default(true)
allDayPosition String @default("above")
cwColor String? @default("#333333")
cwFontFamily String? @default("Oswald")
cwFontSize String? @default("1.5rem")
cwFontWeight String? @default("700")
yearColor String? @default("#333333")
yearFontFamily String? @default("Oswald")
yearFontSize String? @default("1.5rem")
yearFontWeight String? @default("700")
showTaskCheckboxes Boolean @default(false)
showProjectIcons Boolean @default(false)
showPriorityIcons Boolean @default(true)
priorityStyle String @default("eisenhower")
weekStartDay Int @default(1)
emailVerificationCode String?
dayHeaderGap String? @default("0.75em")
accountNumber Int @unique @default(autoincrement())
showCompletedTasks Boolean @default(true)
showLines Boolean @default(true)
weekdayFormat String? @default("long")
weekdayCase String? @default("capitalize")
customWeekdayNames String? @default("")
mobileFontScale Float @default(1.0)
startDayOffset Int @default(-1)
quoteSourceUrls String[] @default([])
quoteLanguages String[] @default(["en", "de"])
kanbanStages String?
notificationsEnabled Boolean @default(false)
weatherEnabled Boolean @default(false)
weatherLat Float?
weatherLocation String?
weatherLon Float?
viewSettings Json?
weatherRecentCities Json?
hasCompletedOnboarding Boolean @default(true)
showCalendarProviderIcon Boolean @default(false)
headerCurrentDayFormat String? @default("DDD, DD. MMMM YYYY")
mobileLandscapeHeaderDisplay String? @default("kw")
mobilePortraitHeaderDisplay String? @default("current_day")
accounts Account[]
cachedCalendarEvents CachedCalendarEvent[]
calendarConnections CalendarConnection[]
calendarSyncRules CalendarSyncRule[]
projects Project[]
pushSubscriptions PushSubscription[]
sentNotifications SentNotification[]
sessions Session[]
somedayLists SomedayList[]
tasks Task[]
weeklyGoals WeeklyGoal[]
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
ext_expires_in Int?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}
model Task {
id String @id @default(cuid())
userId String
title String
description String?
markdownContent String?
completed Boolean @default(false)
isRolling Boolean @default(false)
order Int @default(0)
dayOfWeek Int?
scheduledDate DateTime?
somedayListId String?
originalDate DateTime?
startTime String?
endTime String?
isRecurring Boolean @default(false)
recurrenceInterval Int?
recurrenceUnit String?
recurrenceTime String?
recurrenceEndDate DateTime?
recurrenceDays String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
externalId String?
externalProvider String?
externalListId String?
lastSyncedAt DateTime?
deletedAt DateTime?
parentTaskId String?
somedaySlotIndex Int?
projectId String?
kanbanStage String?
url String?
urgency Boolean?
importance Boolean?
priority String?
delegatedTo String?
delegationNote String?
parent Task? @relation("SubTasks", fields: [parentTaskId], references: [id], onDelete: Cascade)
subTasks Task[] @relation("SubTasks")
project Project? @relation(fields: [projectId], references: [id])
somedayList SomedayList? @relation(fields: [somedayListId], references: [id])
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, dayOfWeek])
@@index([userId, scheduledDate])
@@index([userId, somedayListId])
@@index([userId, externalId])
@@index([userId, deletedAt])
@@index([userId, projectId])
@@index([parentTaskId])
}
model SomedayList {
id String @id @default(cuid())
userId String
title String
order Int @default(0)
tab String?
color String?
icon String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
externalId String?
externalProvider String?
lastSyncedAt DateTime?
syncDeltaToken String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
tasks Task[]
@@index([userId])
}
model CalendarConnection {
id String @id @default(cuid())
userId String
provider String
accessToken String
refreshToken String?
expiresAt DateTime?
calendars Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
cachedEvents CachedCalendarEvent[]
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model CachedCalendarEvent {
id String @id @default(cuid())
userId String
externalId String
connectionId String
provider String
calendarId String
calendarTitle String
calendarColor String?
title String
description String?
location String?
startDateTime DateTime?
startDate String?
endDateTime DateTime?
endDate String?
weekStart DateTime
syncedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
url String?
recurringEventId String?
isRecurring Boolean @default(false)
reminders Json?
busyStatus String?
connection CalendarConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, externalId, provider])
@@index([userId, startDateTime])
@@index([userId, startDate])
@@index([connectionId, weekStart])
}
model WeeklyGoal {
id String @id @default(cuid())
userId String
weekStart DateTime
text String @default("your goal of this week")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, weekStart])
@@index([userId])
}
model Project {
id String @id @default(cuid())
userId String
name String
icon String?
color String?
description String?
order Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
tasks Task[]
@@index([userId])
}
model PushSubscription {
id String @id @default(cuid())
userId String
endpoint String @unique
p256dh String
auth String
userAgent String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
model CalendarSyncRule {
id String @id @default(cuid())
userId String
name String @default("")
enabled Boolean @default(true)
direction String @default("one-way")
sourceConnectionId String
sourceCalendarId String
targetConnectionId String
targetCalendarId String
syncDescription Boolean @default(true)
syncLocation Boolean @default(true)
syncRecurring Boolean @default(false)
titlePrefix String @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lastSyncedAt DateTime?
mappings CalendarSyncMapping[]
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
model CalendarSyncMapping {
id String @id @default(cuid())
ruleId String
userId String
sourceEventId String
sourceProvider String
targetEventId String
targetProvider String
targetCalendarId String
sourceFingerprint String
lastSyncedAt DateTime @default(now())
rule CalendarSyncRule @relation(fields: [ruleId], references: [id], onDelete: Cascade)
@@unique([ruleId, sourceEventId])
@@index([userId, targetEventId, targetProvider])
@@index([ruleId])
}
model SentNotification {
id String @id @default(cuid())
userId String
eventId String
minutes Int
sentAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, eventId, minutes])
@@index([sentAt])
}