Внутренний метод /api/internal/access-check: есть ли у почты доступ к курсу

Для бота выдачи SB Access (payment-router): старый покупатель Second Brain
Vault оставляет почту на /tg/link, ключ входа в бота выдаётся, только если
доступ к курсу действительно есть. Тот же общий секрет, что у grant;
ответ только да/нет, почта без учёта регистра, истёкший доступ не считается.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-13 15:03:24 +05:00
co-authored by Claude Opus 5.5
parent 0a5368577c
commit 83f34ea679
3 changed files with 94 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
import { describe, it, expect } from "vitest";
import { accessWhere, isAuthorized, parseAccessCheck } from "../access-check";
describe("access-check", () => {
it("нормализует почту и требует оба поля", () => {
expect(parseAccessCheck({ email: " A@Example.COM ", course_slug: "second-brain-vault" }))
.toEqual({ email: "a@example.com", courseSlug: "second-brain-vault" });
expect(parseAccessCheck({ email: "a@example.com" })).toBeNull();
expect(parseAccessCheck({ course_slug: "x" })).toBeNull();
expect(parseAccessCheck({ email: "без-собаки", course_slug: "x" })).toBeNull();
expect(parseAccessCheck(null)).toBeNull();
expect(parseAccessCheck({ email: "a".repeat(250) + "@b.ru", course_slug: "x" })).toBeNull();
});
it("условие: почта без учёта регистра, курс по slug, доступ не истёк", () => {
const now = new Date("2026-10-13T00:00:00Z");
const w = accessWhere({ email: "a@example.com", courseSlug: "second-brain-vault" }, now);
expect(w.user.email).toEqual({ equals: "a@example.com", mode: "insensitive" });
expect(w.course).toEqual({ slug: "second-brain-vault" });
expect(w.OR).toEqual([{ expiresAt: null }, { expiresAt: { gt: now } }]);
});
it("без секрета или с чужим — отказ", () => {
expect(isAuthorized("s", "s")).toBe(true);
expect(isAuthorized("s", undefined)).toBe(false);
expect(isAuthorized("s", "")).toBe(false);
expect(isAuthorized(null, "s")).toBe(false);
expect(isAuthorized("x", "s")).toBe(false);
});
});