feat(protection): VIP umfasst auch approved Custom-Domains

getWebCustomDomains schliesst nur noch 'rejected' aus — 'approved' Domains
bleiben in der Layer-2-VIP (Zweitschutz, falls Layer 1 aus ist). Reihenfolge:
pending zuerst (keine Layer-1-Deckung → duerfen nie aus dem 50er-Cap fallen),
dann approved neueste-zuerst (Ueberlauf = aelteste approved, via Layer 1 gedeckt).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-05-21 22:44:19 +02:00
parent ced749018b
commit a713070d25

View File

@ -22,24 +22,35 @@ export const CUSTOM_DOMAIN_TYPES: CustomDomainType[] = [
// ─── Custom Domains ─────────────────────────────────────────────────────────── // ─── Custom Domains ───────────────────────────────────────────────────────────
/** /**
* Gibt die aktiven Web-Custom-Domains eines Users zurück (nur type='web'). * Web-Custom-Domains eines Users für die Layer-2-VIP-Komposition (type='web').
* Status 'approved' und 'rejected' werden ausgeschlossen: * Nur 'rejected' wird ausgeschlossen 'approved' Domains BLEIBEN in der VIP:
* - approved Domain ist in der globalen Blocklist, kein Slot mehr nötig * Layer 2 ist der Zweitschutz für den Fall, dass Layer 1 (VPN/URL-Filter) aus
* - rejected Domain wurde abgelehnt und Slot wurde freigegeben * ist. Eine approved Domain ist zwar in der globalen Layer-1-Blocklist, muss
* Wird von GET /api/protection/webcontent-domains für die VIP-Komposition genutzt. * aber auch in Layer 2 gedeckt sein.
*
* Reihenfolge = Priorität für den 50er-Cap im Endpoint:
* 1. pending zuerst KEINE Layer-1-Deckung, die VIP ist ihre einzige
* Absicherung dürfen nie aus dem Cap fallen ( Slot-Limit, passen immer).
* 2. approved danach, neueste zuerst bei Überlauf fallen die ältesten
* approved weg (via Layer 1 weiter gedeckt, daher vertretbar).
*
* Wird von GET /api/protection/webcontent-domains genutzt.
*/ */
export async function getWebCustomDomains(userId: string): Promise<string[]> { export async function getWebCustomDomains(userId: string): Promise<string[]> {
const db = usePrisma(); const db = usePrisma();
const rows = await db.userCustomDomain.findMany({ // pending = alles außer approved/rejected — älteste zuerst (passen alle rein)
where: { const pending = await db.userCustomDomain.findMany({
userId, where: { userId, type: "web", status: { notIn: ["approved", "rejected"] } },
type: "web",
status: { notIn: ["approved", "rejected"] },
},
orderBy: { addedAt: "asc" }, orderBy: { addedAt: "asc" },
select: { domain: true }, select: { domain: true },
}); });
return rows.map((r) => r.domain); // approved — neueste zuerst, damit bei Cap-Überlauf die ältesten wegfallen
const approved = await db.userCustomDomain.findMany({
where: { userId, type: "web", status: "approved" },
orderBy: { addedAt: "desc" },
select: { domain: true },
});
return [...pending.map((r) => r.domain), ...approved.map((r) => r.domain)];
} }
export async function getUserCustomDomains(userId: string) { export async function getUserCustomDomains(userId: string) {