Add PdfApiKey model and key storage helpers

Adds a Prisma model for per-student Clean PDF API keys plus a
hand-written migration (no local Postgres to run `migrate dev`
against). getOrCreatePdfKey/regenerateKey wrap the model with
lazy-creation and rotation logic on top of generatePdfKey().
This commit is contained in:
2026-07-06 11:53:31 +05:00
parent 1d5b4f9251
commit b49680c118
3 changed files with 48 additions and 0 deletions
@@ -0,0 +1,14 @@
-- Чистый PDF — персональный API-ключ студента (Zotero/curl)
CREATE TABLE "PdfApiKey" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"key" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "PdfApiKey_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "PdfApiKey_userId_key" ON "PdfApiKey"("userId");
CREATE UNIQUE INDEX "PdfApiKey_key_key" ON "PdfApiKey"("key");
ALTER TABLE "PdfApiKey" ADD CONSTRAINT "PdfApiKey_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+11
View File
@@ -47,6 +47,7 @@ model User {
questionMessages StudentQuestionMessage[] questionMessages StudentQuestionMessage[]
wrappedRuns WrappedRun[] wrappedRuns WrappedRun[]
toolUsages ToolUsage[] toolUsages ToolUsage[]
pdfApiKey PdfApiKey?
} }
// Obsidian Wrapped — агрегаты прогона (БЕЗ имён/текстов заметок; обработка волта 100% client-side) // Obsidian Wrapped — агрегаты прогона (БЕЗ имён/текстов заметок; обработка волта 100% client-side)
@@ -82,6 +83,16 @@ model ToolUsage {
@@index([createdAt]) @@index([createdAt])
} }
// Чистый PDF — персональный API-ключ студента (Zotero/curl)
model PdfApiKey {
id String @id @default(cuid())
userId String @unique
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
key String @unique // sbpdf_<random>, показывается студенту на /tools/clean-pdf
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Session { model Session {
id String @id @default(cuid()) id String @id @default(cuid())
userId String userId String
+23
View File
@@ -0,0 +1,23 @@
import { prisma } from "@/lib/prisma";
import { generatePdfKey } from "@/lib/clean-pdf/api-key";
export async function getOrCreatePdfKey(userId: string): Promise<string> {
const existing = await prisma.pdfApiKey.findUnique({ where: { userId } });
if (existing) return existing.key;
const created = await prisma.pdfApiKey.upsert({
where: { userId },
create: { userId, key: generatePdfKey() },
update: {},
});
return created.key;
}
export async function regenerateKey(userId: string): Promise<string> {
const key = generatePdfKey();
await prisma.pdfApiKey.upsert({
where: { userId },
create: { userId, key },
update: { key },
});
return key;
}