rebreak-monorepo/backend/server/api/protection/webcontent-domains.get.ts
chahinebrini f555c5e4d8 feat(vip): Tunesien (TN) als VIP-Land + kuratierte Starter-Liste
TN-User fielen bisher mangels TN-Liste auf die DE-Liste zurück. Jetzt
eigene (kurze) TN-Starter-Liste: mbet216.com, 2xbet365.com, cesar365.com,
icombet.com, unibet365.net (von einem TN-Test-User gemeldet).

TN in COUNTRY_KEYS (webcontent-Endpoint) + VIP_COUNTRIES (Geräte-Region-
Auflösung + Add-Check). Native Region-Logik ist generisch (Locale.region
→ JSON-Key) — kein Native-Code nötig. gambling-domains.json _meta v3.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 20:52:20 +02:00

81 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import gamblingDomains from "../../data/gambling-domains.json";
import { getWebCustomDomains } from "../../db/domains";
const COUNTRY_KEYS = ["DE", "GB", "FR", "TN"] as const;
type CountryKey = (typeof COUNTRY_KEYS)[number];
const GLOBAL_LISTS = gamblingDomains as unknown as Record<string, string[]>;
const MAX_PER_COUNTRY = 50;
// Hybrid-Reservierung: die Top-N kuratierten Gambling-Domains pro Land sind
// FEST garantiert — ein User kann sie nicht mit eigenen Custom-Domains aus
// seinem Layer-2-Zweitschutz verdrängen. Custom-Domains werden daher hart auf
// (50 RESERVED_CURATED) gekappt. Voraussetzung: gambling-domains.json ist
// nach Relevanz sortiert (die ersten RESERVED_CURATED = die wichtigsten).
const RESERVED_CURATED = 20;
const MAX_CUSTOM = MAX_PER_COUNTRY - RESERVED_CURATED; // 30
/**
* GET /api/protection/webcontent-domains
*
* Liefert die VIP-Domain-Liste für den WebKit-webContent-Filter (Layer 2).
* Pro User personalisiert, Hybrid-Komposition pro Land:
* 1. Custom-Web-Domains (pending zuerst, dann approved) — gekappt auf 30
* 2. kuratierte Gambling-Liste — füllt den Rest bis 50 auf
* → dedupliziert → hart auf 50 gekappt (Apple-Limit).
*
* Damit sind immer ≥ 20 kuratierte Top-Domains im Zweitschutz garantiert,
* egal wie viele Custom-Domains der User angesammelt hat.
* Response-Shape ist identisch mit der statischen Version — iOS parst das unverändert.
*
* Lade-Mechanismus: direkter JSON-Import (build-time gebundelt via Nitro-Bundler).
* Kein serverAssets/useStorage — kein extra Laufzeit-I/O, kein globales
* backend/data/-Verzeichnis nötig.
*
* Pflege: backend/server/data/gambling-domains.json editieren,
* _meta.version hochzählen, _meta.updatedAt setzen, dann neu deployen.
*/
export default defineEventHandler(async (event) => {
const user = await requireUser(event);
// Custom Web-Domains des Users laden — parallel zu allen Country-Listen
const userWebDomains = await getWebCustomDomains(user.id);
// Custom-Domains hart auf 30 kappen — die ersten 30 sind die höchst-
// priorisierten (getWebCustomDomains liefert pending zuerst, dann approved
// neueste-zuerst). Die restlichen 20 Slots bleiben für die kuratierte Liste.
const cappedCustom = userWebDomains.slice(0, MAX_CUSTOM);
// Dedup-Set NUR über die gekappten Customs — eine kuratierte Domain, die
// einer aus dem 30-Cap GEFLOGENEN Custom-Domain entspricht, soll über die
// kuratierte Auffüllung wieder reinkommen (sie ist ja eine Top-Domain).
const cappedCustomSet = new Set(cappedCustom);
// Pro Country: Custom-Domains vorne, dann globale Auffüllung, dedup, cap 50
const composed: Record<CountryKey, string[]> = {} as Record<
CountryKey,
string[]
>;
for (const country of COUNTRY_KEYS) {
const globalList: string[] = GLOBAL_LISTS[country] ?? [];
// Gekappte Custom-Domains zuerst (bereits dedupliziert da aus DB)
const merged: string[] = [...cappedCustom];
// Kuratierte Domains auffüllen — nur wenn noch nicht durch Custom drin
for (const domain of globalList) {
if (!cappedCustomSet.has(domain)) {
merged.push(domain);
}
}
composed[country] = merged.slice(0, MAX_PER_COUNTRY);
}
return {
_meta: gamblingDomains._meta,
...composed,
};
});