export type Plan = "free" | "pro" | "legend"; export type VoiceProvider = "elevenlabs" | "openai" | "google" | "cartesia" | "azure"; export interface VoiceConfig { /** TTS-Provider für diesen Plan */ provider: VoiceProvider; /** Provider-spezifische Model-ID (optional) */ model?: string; /** Provider-spezifische Voice-ID (optional — fällt auf provider-default zurück) */ voiceId?: string; /** Tages-Quota in Sekunden. 0 = unlimited */ dailyQuotaSeconds: number; } export interface PlanLimits { // ─── Custom Domains ────────────────────────────────────────────────────── /** Max. eigene Domains (Infinity = unbegrenzt) */ customDomains: number; /** Freigeschaltete Domain-Slots füllen sich wieder auf (Community-Promotion) */ domainRefill: boolean; // ─── Mail-Accounts ─────────────────────────────────────────────────────── /** Max. aktive Mail-Agenten (Infinity = unbegrenzt) */ mailAgents: number; /** Erlaubte Scan-Intervalle in Stunden */ mailIntervalOptions: number[]; // ─── Globale Blocklist ─────────────────────────────────────────────────── /** * 'curated' = kleiner Stub der bekanntesten Casino-Domains (Free). * 'full' = vollständige HaGeZi/ReBreak-Liste (~208k). * * Der Stub selbst ist in server/utils/curated-blocklist.ts definiert. * Die echte ~1-2k HaGeZi-Subset-Liste ist ein separates Daten-Ticket. */ globalBlocklist: "curated" | "full"; // ─── Community ─────────────────────────────────────────────────────────── /** Darf in der Community posten */ canPost: boolean; /** Darf Gruppen gründen */ canCreateGroup: boolean; /** Darf Domains direkt zur ReBreak Blocklist hinzufügen */ canAddToBlocklist: boolean; // ─── Geräte (zwei getrennte Konzepte!) ─────────────────────────────────── /** * Max. parallel eingeloggte App-Geräte pro Account (Anti-Account-Sharing). * Bezieht sich auf UserDevice (iOS/Android-App-Instanzen). */ maxAppDevices: number; /** * Max. zusätzliche Geräte (Mac/Windows) die per DNS-Profil geschützt werden. * Bezieht sich auf ProtectedDevice (Legend-only Feature). * 0 = Feature nicht verfügbar. */ maxProtectedDevices: number; // ─── AI-Coach ──────────────────────────────────────────────────────────── /** Primäres OpenRouter/Groq-Modell für KI-Coach */ aiModel: string; /** Fallback-Modelle (werden der Reihe nach versucht wenn primary fehlschlägt) */ aiModelFallbacks: Array<{ provider: "groq" | "openrouter"; model: string }>; /** AI-Provider: groq (Free/Pro) oder openrouter (Legend/Claude) */ aiProvider: "groq" | "openrouter"; // ─── TTS ───────────────────────────────────────────────────────────────── /** * Voice-Config: welcher TTS-Provider + Quota. * * Provider-Mapping (Cost-Reference 2026-05): * Free → Google TTS Neural2 (~$4/1M chars, 60s/day cap) * Pro → Cartesia Sonic-2 (~$4/1M chars, 300s/day cap, ~75ms first-byte) * Legend → ElevenLabs Turbo v2.5 (~$30/1M chars, unlimited) */ voice: VoiceConfig; } export const PLAN_LIMITS: Record = { free: { customDomains: 5, domainRefill: false, mailAgents: 1, mailIntervalOptions: [4], globalBlocklist: "curated", canPost: true, canCreateGroup: false, canAddToBlocklist: false, maxAppDevices: 1, maxProtectedDevices: 0, aiModel: "llama-3.1-8b-instant", aiModelFallbacks: [ { provider: "groq", model: "llama-3.3-70b-versatile" }, { provider: "groq", model: "gemma2-9b-it" }, { provider: "openrouter", model: "meta-llama/llama-3.1-8b-instruct" }, ], aiProvider: "groq", voice: { provider: "google", model: "de-DE-Neural2-F", // Google Cloud TTS Neural2 — natural, ~$4/1M chars dailyQuotaSeconds: 60, // 1 Minute/Tag }, }, pro: { customDomains: 5, domainRefill: true, mailAgents: 3, mailIntervalOptions: [1, 4, 8], globalBlocklist: "full", canPost: true, canCreateGroup: false, canAddToBlocklist: false, maxAppDevices: 1, maxProtectedDevices: 0, aiModel: "llama-3.3-70b-versatile", aiModelFallbacks: [ { provider: "groq", model: "llama-3.1-8b-instant" }, { provider: "openrouter", model: "meta-llama/llama-3.3-70b-instruct" }, ], aiProvider: "groq", voice: { provider: "cartesia", model: "sonic-2", // Cartesia Sonic-2 — ~75ms TTFT, native German, ~$4/1M chars dailyQuotaSeconds: 300, // 5 Minuten/Tag }, }, legend: { customDomains: 10, domainRefill: true, mailAgents: Infinity, mailIntervalOptions: [1, 4, 8], globalBlocklist: "full", canPost: true, canCreateGroup: true, canAddToBlocklist: true, maxAppDevices: 3, maxProtectedDevices: 2, // "+2 weitere Geräte" (§0.5) aiModel: "anthropic/claude-3.5-haiku", aiModelFallbacks: [ { provider: "openrouter", model: "anthropic/claude-3-haiku" }, { provider: "groq", model: "llama-3.3-70b-versatile" }, ], aiProvider: "openrouter", voice: { provider: "elevenlabs", model: "eleven_turbo_v2_5", // ElevenLabs Turbo v2.5 — premium, ~$30/1M chars dailyQuotaSeconds: 0, // 0 = unlimited }, }, }; export function getPlanLimits(plan: string): PlanLimits { // Legacy-Pläne auf neue Namen mappen if (plan === "premium") return PLAN_LIMITS.legend; if (plan === "standard") return PLAN_LIMITS.pro; return PLAN_LIMITS[(plan as Plan) ?? "free"] ?? PLAN_LIMITS.free; } /** * Kuratierter Stub der bekanntesten Glücksspiel-Domains für Free-User. * Diese Liste ist der Mechanismus — der echte ~1-2k HaGeZi-Subset * ist ein separates Daten-Ticket (TODO: Daten-Ticket anlegen). * * Wird in DNS-Blocklist-Endpoints und scan-internal verwendet wenn * limits.globalBlocklist === 'curated'. */ export const CURATED_BLOCKLIST_STUB: string[] = [ // DE / Offshore-Klassiker (Top-Tier-Traffic) "betway.com", "bet365.com", "888casino.com", "pokerstars.com", "williamhill.com", "bwin.com", "unibet.com", "partypoker.com", "casinoclub.com", "interwetten.com", "tipico.de", "betsson.com", "casumo.com", "leovegas.com", "mr-green.com", "jackpot.de", "sunmaker.com", "stargames.com", "mybet.com", "winner.com", "ladbrokes.com", "coral.co.uk", "paddypower.com", "betfair.com", "mrvegas.com", "slotsmillion.com", "casinoeuropa.com", "netbet.com", "platincasino.com", "euslot.com", ];