Owner login
Enter the admin password you set in Cloudflare.
Appointment history
A running record of every booking and quote as it arrives, and every status change you make (confirmed, done, cancelled) — newest first.
Spam shield
Limits how often one visitor (by IP address and device fingerprint) can submit bookings, quotes, or test alerts. Blocked attempts appear in the activity log below. Changes apply instantly — no re-deploy needed.
Recent activity
Visits, bookings, logins, and blocked spam — with device, location, and IP. Entries are kept for 60 days, then deleted automatically. You'll also get a phone alert the first time any new IP logs into this dashboard.
System checkup
Tests every piece of your deployment. Customers never see this — the public help page only shows your phone number.
1 · The brain is running
- Pages project → Deployments → Create deployment.
- Drag the site folder itself — never individual files.
- Confirm the file list shows _worker.js before deploying.
- Deploy, wait ~30 seconds, re-run the checkup.
2 · Database connected
- Pages project → Settings → Bindings → Add → D1 database.
- Variable name exactly DB, database cnc-db. Save.
- Re-deploy — settings only apply to new deployments.
3 · Database tables exist
- Sidebar → Storage & Databases → D1 → cnc-db → Console.
- Paste and run the script below, then re-run the checkup.
CREATE TABLE IF NOT EXISTS requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL, service TEXT, name TEXT, phone TEXT,
address TEXT, notes TEXT, date TEXT, slot TEXT,
status TEXT DEFAULT 'new',
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS hours (
dow INTEGER PRIMARY KEY, enabled INTEGER DEFAULT 1,
open TEXT DEFAULT '08:00', close TEXT DEFAULT '18:00'
);
INSERT OR IGNORE INTO hours (dow, enabled, open, close) VALUES
(0,0,'08:00','18:00'),(1,1,'08:00','18:00'),(2,1,'08:00','18:00'),
(3,1,'08:00','18:00'),(4,1,'08:00','18:00'),(5,1,'08:00','18:00'),
(6,1,'08:00','16:00');
CREATE TABLE IF NOT EXISTS blocked (date TEXT PRIMARY KEY, reason TEXT);
CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kind TEXT NOT NULL,
ip TEXT, fp TEXT, ua TEXT, device TEXT, country TEXT, city TEXT, path TEXT, detail TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_events_created ON events(created_at);
CREATE INDEX IF NOT EXISTS idx_events_kind_ip ON events(kind, ip);
CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT);
INSERT OR IGNORE INTO settings (key, value) VALUES
('rate_limit_max','3'),('rate_limit_window_min','5');4 · Admin password set
- Pages project → Settings → Environment variables → Add ADMIN_PASSWORD. Encrypt if offered.
- Re-deploy, then re-run.
5 · Phone alerts configured
- Pages project → Settings → Environment variables → Add NTFY_TOPIC (unguessable value).
- Re-deploy. Subscribe to that exact topic in the ntfy app.
Weekly hours
Customers can book 2-hour arrival windows inside these hours. Uncheck a day to close it.
Blocked dates
Going to a family event or fully booked offline? Block the whole day.