import { prisma } from "@/lib/prisma"; // НЕ "clean-pdf": так CopyButton логирует копирования — они не должны тратить лимит export const PDF_TOOL_ID = "clean-pdf-generate"; export const BURST_LIMIT = 5; // успешных генераций в минуту const BURST_WINDOW_MS = 60_000; export function decidePaidAccess(input: { role: string; banned: boolean; enrollments: { courseSlug: string; expiresAt: Date | null }[]; freeSlug: string; now?: Date; }): boolean { const now = input.now ?? new Date(); if (input.banned) return false; if (input.role === "admin" || input.role === "curator") return true; return input.enrollments.some( (e) => e.courseSlug !== input.freeSlug && (e.expiresAt === null || e.expiresAt > now), ); } export async function hasPaidAccess(userId: string): Promise { const user = await prisma.user.findUnique({ where: { id: userId }, select: { role: true, banned: true, enrollments: { select: { expiresAt: true, course: { select: { slug: true } } } }, }, }); if (!user) return false; return decidePaidAccess({ role: user.role, banned: user.banned ?? false, freeSlug: process.env.FREE_COURSE_SLUG ?? "obsidian-start", enrollments: user.enrollments.map((e) => ({ courseSlug: e.course.slug, expiresAt: e.expiresAt })), }); } export function monthStartUtc(now: Date): Date { return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)); } export function getMonthlyLimit(): number { const n = Number(process.env.PDF_MONTHLY_LIMIT); return Number.isFinite(n) && n > 0 ? n : 100; } export async function getMonthlyUsage(userId: string): Promise { return prisma.toolUsage.count({ where: { userId, tool: PDF_TOOL_ID, createdAt: { gte: monthStartUtc(new Date()) } }, }); } export async function getBurstUsage(userId: string): Promise { return prisma.toolUsage.count({ where: { userId, tool: PDF_TOOL_ID, createdAt: { gte: new Date(Date.now() - BURST_WINDOW_MS) } }, }); }