import { NextRequest, NextResponse } from "next/server"; import { headers } from "next/headers"; import { createHmac } from "crypto"; import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; /** * Вход в конструктор планнеров (planner.second-brain.ru). * * Своей авторизации у конструктора нет: школа проверяет сессию и доступ, * подписывает короткоживущий токен, конструктор его сверяет тем же секретом. * Формат токена: base64url(payload).base64url(hmacSha256(payload)) — * зеркало packages/web/src/auth.mjs в репозитории планнера. Менять оба конца. */ const PLANNER_URL = process.env.PLANNER_URL ?? "https://planner.second-brain.ru"; const COURSE_SLUG = "planner"; const TTL_SECONDS = 900; const b64 = (input: Buffer | string) => Buffer.from(input).toString("base64url"); export async function GET(req: NextRequest) { const secret = process.env.PLANNER_TOKEN_SECRET; if (!secret) { // Лучше честная ошибка, чем редирект в конструктор с заведомо негодным // токеном: там человек увидит невнятное «подпись не сходится». return NextResponse.json( { error: "PLANNER_TOKEN_SECRET не задан" }, { status: 503 }, ); } const session = await auth.api.getSession({ headers: await headers() }); if (!session?.user) { return NextResponse.redirect(new URL("/login", req.url)); } const enrolled = await prisma.courseEnrollment.findFirst({ where: { userId: session.user.id, course: { slug: COURSE_SLUG } }, select: { expiresAt: true }, }); if (!enrolled) { return NextResponse.redirect(new URL("/dashboard", req.url)); } if (enrolled.expiresAt && enrolled.expiresAt < new Date()) { return NextResponse.redirect(new URL("/dashboard", req.url)); } const payload = { userId: session.user.id, email: session.user.email, exp: Math.floor(Date.now() / 1000) + TTL_SECONDS, }; const body = b64(JSON.stringify(payload)); const signature = b64(createHmac("sha256", secret).update(body).digest()); // Токен во фрагменте, а не в строке запроса: фрагмент не попадает ни в // логи сервера, ни в заголовок Referer при переходах со страницы. return NextResponse.redirect(`${PLANNER_URL}/build.html#token=${body}.${signature}`); }