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:
@@ -0,0 +1,42 @@
|
||||
import { ImageResponse } from "next/og";
|
||||
import { readFile } from "fs/promises";
|
||||
import path from "path";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { wrappedCard } from "@/lib/wrapped-card";
|
||||
import type { Archetype, Period } from "@/app/(student)/wrapped/_client/types";
|
||||
|
||||
export const size = { width: 1080, height: 1920 };
|
||||
export const contentType = "image/png";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
// Серверный PNG 1080×1920 карточки Wrapped по shareToken (для OG-превью в соцсетях
|
||||
// и для скачивания участником). Рендерится через next/og (Satori + resvg-wasm).
|
||||
export default async function OG({ params }: { params: Promise<{ token: string }> }) {
|
||||
const { token } = await params;
|
||||
const run = await prisma.wrappedRun.findUnique({ where: { shareToken: token } });
|
||||
|
||||
const [reg, med] = await Promise.all([
|
||||
readFile(path.join(process.cwd(), "public/fonts/FiraMono-Regular.ttf")),
|
||||
readFile(path.join(process.cwd(), "public/fonts/FiraMono-Medium.ttf")),
|
||||
]);
|
||||
|
||||
const data = run
|
||||
? {
|
||||
noteCount: run.noteCount,
|
||||
linkCount: run.linkCount,
|
||||
tagCount: run.tagCount,
|
||||
topTag: run.topTag,
|
||||
archetype: run.archetype as Archetype | null,
|
||||
score: run.score,
|
||||
period: run.period as Period,
|
||||
}
|
||||
: { noteCount: 0, linkCount: 0, tagCount: 0, topTag: null, archetype: null, score: 0, period: "year" as Period };
|
||||
|
||||
return new ImageResponse(wrappedCard(data), {
|
||||
...size,
|
||||
fonts: [
|
||||
{ name: "Fira Mono", data: reg, weight: 400, style: "normal" as const },
|
||||
{ name: "Fira Mono", data: med, weight: 500, style: "normal" as const },
|
||||
],
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user