chahinebrini 704958320b refactor(domains): gemeinsamer 10/20-Slot-Pool, Free-Tier entfernt
Custom-Domain-Slots sind jetzt EIN gemeinsamer Pool für web + mail
(Pro 10 / Legend 20) statt getrennter web/mail-Buckets. Free-Tier ist
entfallen — PLAN_LIMITS hat nur noch pro + legend, getPlanLimits
defaultet auf pro.

Backend:
- plan-features: customDomains ist eine Zahl (CustomDomainLimits weg)
- index.post: Slot-Check gegen Gesamt-Count, Fehler einheitlich LIMIT_REACHED
- index.get: liefert { items, count, limit }
- change-preview + coach/message an die neue Form angepasst

Frontend:
- useCustomDomains: count/limit (Zahlen) statt countsByType/limits
- AddDomainSheet: ein generischer Limit-Hinweis (error_limit_reached)

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

178 lines
6.6 KiB
TypeScript

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 — EIN gemeinsamer Pool für web + mail (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;
}
// Free-Tier ist entfallen — es gibt nur noch Pro + Legend.
export const PLAN_LIMITS: Record<Exclude<Plan, "free">, PlanLimits> = {
pro: {
customDomains: 10,
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: 20,
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 {
// Free-Tier ist entfallen — alles außer Legend bekommt Pro-Limits.
// Legacy-Namen: premium → legend, standard → pro.
if (plan === "legend" || plan === "premium") return PLAN_LIMITS.legend;
return PLAN_LIMITS.pro;
}
/**
* 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",
];