Files
lms-sb/src/lib/clean-pdf/access.ts
T

61 lines
2.0 KiB
TypeScript

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<boolean> {
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<number> {
return prisma.toolUsage.count({
where: { userId, tool: PDF_TOOL_ID, createdAt: { gte: monthStartUtc(new Date()) } },
});
}
export async function getBurstUsage(userId: string): Promise<number> {
return prisma.toolUsage.count({
where: { userId, tool: PDF_TOOL_ID, createdAt: { gte: new Date(Date.now() - BURST_WINDOW_MS) } },
});
}