diff --git a/apps/rebreak-native/app/(app)/blocker.tsx b/apps/rebreak-native/app/(app)/blocker.tsx index 8b87b23..ef80161 100644 --- a/apps/rebreak-native/app/(app)/blocker.tsx +++ b/apps/rebreak-native/app/(app)/blocker.tsx @@ -61,7 +61,7 @@ export default function BlockerScreen() { }, [refreshDomains, syncBlocklist, refresh]); useDomainSubmissionRealtime(onDomainChange, true); - const [mailOpen, setMailOpen] = useState(true); + const [mailOpen, setMailOpen] = useState(false); // AddSheet state: tracks which section opened it const [addSheetOpen, setAddSheetOpen] = useState(false); diff --git a/backend/server/api/custom-domains/index.post.ts b/backend/server/api/custom-domains/index.post.ts index 13f42aa..94538be 100644 --- a/backend/server/api/custom-domains/index.post.ts +++ b/backend/server/api/custom-domains/index.post.ts @@ -7,6 +7,7 @@ import { } from "../../db/domains"; import { getProfile } from "../../db/profile"; import { getPlanLimits } from "../../utils/plan-features"; +import { usePrisma } from "../../utils/prisma"; // Regex: Domain muss mindestens eine TLD haben (z.B. "casino.de", "x.co.uk") const DOMAIN_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/; @@ -128,6 +129,20 @@ export default defineEventHandler(async (event) => { }); } + // Pre-check: domain already on the global blocklist? Don't burn a slot for + // something the 208k-domain global filter already covers. Return 200 with a + // flag so the frontend can surface "already protected, no slot needed" + // without the user paying for it. mail_domain is included in the same check + // because mail-domains land in the same blocklist set the daemon scans. + const db = usePrisma(); + const globalMatch = await db.blocklistDomain.findFirst({ + where: { domain: value, isActive: true }, + select: { domain: true }, + }); + if (globalMatch) { + return { alreadyGlobal: true, domain: value }; + } + // Per-type Slot-Limit prüfen const profile = await getProfile(user.id); const limits = getPlanLimits(profile?.plan ?? "free");