06b8d9f64b
- 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>
48 lines
1.7 KiB
TypeScript
48 lines
1.7 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { headers } from "next/headers";
|
|
import { auth } from "@/lib/auth";
|
|
import { prisma } from "@/lib/prisma";
|
|
import { randomBytes } from "crypto";
|
|
|
|
// Пишет агрегаты прогона Wrapped. userId — строго из сессии, не из тела.
|
|
// Имена/тексты заметок сюда не приходят (только числа/строки-агрегаты).
|
|
export async function POST(req: Request) {
|
|
const session = await auth.api.getSession({ headers: await headers() });
|
|
if (!session) return NextResponse.json({ error: "unauthorized" }, { status: 401 });
|
|
|
|
let b: {
|
|
period?: string;
|
|
noteCount?: number;
|
|
linkCount?: number;
|
|
tagCount?: number;
|
|
topTag?: string | null;
|
|
archetype?: string | null;
|
|
score?: number;
|
|
};
|
|
try {
|
|
b = await req.json();
|
|
} catch {
|
|
return NextResponse.json({ error: "bad json" }, { status: 400 });
|
|
}
|
|
|
|
const period = ["year", "quarter", "all"].includes(String(b.period)) ? String(b.period) : "year";
|
|
const VALID = ["architect", "gardener", "librarian", "pragmatist"];
|
|
|
|
const run = await prisma.wrappedRun.create({
|
|
data: {
|
|
userId: session.user.id,
|
|
period,
|
|
noteCount: Number(b.noteCount) || 0,
|
|
linkCount: Number(b.linkCount) || 0,
|
|
tagCount: Number(b.tagCount) || 0,
|
|
topTag: b.topTag ? String(b.topTag).slice(0, 60) : null,
|
|
archetype: VALID.includes(String(b.archetype)) ? String(b.archetype) : null,
|
|
score: Math.max(0, Math.min(100, Number(b.score) || 0)),
|
|
shareToken: randomBytes(9).toString("base64url"),
|
|
},
|
|
select: { shareToken: true },
|
|
});
|
|
|
|
return NextResponse.json({ shareToken: run.shareToken });
|
|
}
|