Add Obsidian Wrapped feature (inside-LMS, client-side vault parsing)

- WrappedRun Prisma model + migration (aggregates only, no note names/text)
- /wrapped authed route: FSA + webkitdirectory + drag-drop reader, Web Worker parser
- archetype + score heuristics, ДС-2 result card
- POST /api/wrapped/run (userId from session), public /share/wrapped/[token]
- next/og OG image (Satori+resvg-wasm, alpine-safe) + Fira Mono ttf
- middleware: /share/wrapped public

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-23 14:19:59 +05:00
parent 1cde567dbe
commit 06b8d9f64b
14 changed files with 790 additions and 1 deletions
+21
View File
@@ -45,6 +45,27 @@ model User {
questions StudentQuestion[]
closedQuestions StudentQuestion[] @relation("QuestionClosedBy")
questionMessages StudentQuestionMessage[]
wrappedRuns WrappedRun[]
}
// Obsidian Wrapped — агрегаты прогона (БЕЗ имён/текстов заметок; обработка волта 100% client-side)
model WrappedRun {
id String @id @default(cuid())
userId String
period String @default("year") // year | quarter | all
noteCount Int
linkCount Int
tagCount Int
topTag String?
archetype String? // architect | gardener | librarian | pragmatist
score Int @default(0) // 0..100
shareToken String @unique // публичная ссылка /share/wrapped/[token]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
model Session {