From f19d00017aad6224dab265d14534bb4fd07ffa52 Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Sat, 16 May 2026 02:42:42 +0200 Subject: [PATCH] feat: pre-check global blocklist on add + collapse Mails on load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User found that adding bet365.com (which is in the 208k global filter) silently took a custom-domain slot — they paid a slot for something the global blocklist already covered. Two pieces: 1. backend/custom-domains/index.post.ts: before any slot-limit check or DB insert, look the domain up in blocklist_domain (active rows). If present, return 200 { alreadyGlobal: true, domain }. No row gets written, no slot consumed. The existing frontend hook + AddSheet already handle the alreadyGlobal flag — they surface the "bereits global blockiert" alert and don't refresh as if the entry landed in the user's list. 2. blocker.tsx default mailOpen state flipped from true to false so the Eigene Mails section starts collapsed on page load. Domains stays the primary affordance; mail-patterns are an opt-in expansion. --- apps/rebreak-native/app/(app)/blocker.tsx | 2 +- backend/server/api/custom-domains/index.post.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) 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");