- API token model (SHA-256 hashed, debounced lastUsedAt) with Bearer auth
- Dual auth middleware (session + API token) in src/lib/api-auth.ts
- 23 REST endpoints under /api/v1/ covering all functionality:
tokens, proxy-hosts, l4-proxy-hosts, certificates, ca-certificates,
client-certificates, access-lists, settings, instances, users,
audit-log, caddy/apply
- OpenAPI 3.1 spec at /api/v1/openapi.json with fully typed schemas
- Swagger UI docs page at /api-docs in the dashboard
- API token management integrated into the Profile page
- Fix: next build now works under Node.js (bun:sqlite aliased to better-sqlite3)
- 89 new API route unit tests + 11 integration tests (592 total)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove gradient heading in OverviewClient (violet-to-cyan gradient replaced with plain bold text)
- Remove hardcoded dark navy backgrounds from activity items (replaced with Card + Separator list)
- Stat cards now use shadcn CardHeader/CardContent pattern with small icons + big number
- Sidebar logo: replace violet text with violet pill icon + plain foreground text
- Active nav item: use bg-primary/10 text-primary (subtle violet tint) instead of bg-accent
- Move theme toggle + sign out into sidebar footer row (no more floating top-right toggle)
- Mobile header brand name: remove text-primary, use plain font-semibold
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New l4_proxy_hosts table and Drizzle migration (0015)
- Full CRUD model layer with validation, audit logging, and Caddy config
generation (buildL4Servers integrating into buildCaddyDocument)
- Server actions, paginated list page, create/edit/delete dialogs
- L4 port manager sidecar (docker/l4-port-manager) that auto-recreates
the caddy container when port mappings change via a trigger file
- Auto-detects Docker Compose project name from caddy container labels
- Supports both named-volume and bind-mount (COMPOSE_HOST_DIR) deployments
- getL4PortsStatus simplified: status file is sole source of truth,
trigger files deleted after processing to prevent stuck 'Waiting' banner
- Navigation entry added (CableIcon)
- Tests: unit (entrypoint.sh invariants + validation), integration (ports
lifecycle + caddy config), E2E (CRUD + functional routing)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create tests/e2e/mobile/mobile-layout.spec.ts with 8 tests covering
AppBar/hamburger visibility, drawer open/close, mobile card rendering,
PageHeader button layout, dialog width, card actions, and analytics overflow.
- Fix AnalyticsClient: make Autocomplete full-width on mobile, add
overflow:hidden to outer Stack to prevent body scrollWidth growth.
- Fix WorldMapInner: remove hard-coded minWidth:400 that caused 73px
horizontal overflow on 393px iPhone 15 viewport.
- Fix DashboardLayoutClient: add overflowX:hidden to main content area
to contain chart library elements that exceed viewport width.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move WAF config (enable, CRS, custom directives, templates) from
Settings page into a new Settings tab on the WAF page
- WAF page now has three tabs: Events | Suppressed Rules | Settings
- Rename nav item from "WAF Events" to "WAF", route /waf-events → /waf
- Fix excluded_rule_ids preservation: no longer wiped when form field
is absent (Settings tab omits the hidden field intentionally)
- Allow pre-adding suppressed rules even when WAF is disabled
- Reorder sidebar: Overview, Proxy Hosts, Access Lists, Certificates,
WAF, Analytics, Audit Log, Settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SwapHorizIcon→Proxy Hosts, VpnKeyIcon→Access Lists, SecurityIcon→Certificates,
HistoryIcon→Audit Log. Nav now uses the exact same icons as the overview stat
cards so identical sections share identical icons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DnsIcon→RouterIcon, SecurityIcon→LockIcon, ShieldIcon→WorkspacePremiumIcon,
HistoryIcon→AssignmentIcon — all now match the visual weight of Dashboard,
BarChart, and Settings icons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Revert 2-column squircle grid back to original list layout —
same space usage as before, just with squircle-style border radius
on each ListItemButton instead of sharp rectangle edges.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace full-width ListItemButton rows with a 2-column grid of
square tiles (borderRadius: 22%, aspectRatio: 1) containing a
centred icon and small label. Active tile uses primary.main fill.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Parse Caddy access logs every 30s into traffic_events SQLite table
- GeoIP country lookup via maxmind (GeoLite2-Country.mmdb)
- 90-day retention with automatic purge
- Analytics page with interval (24h/7d/30d) and per-host filtering:
- Stats cards: total requests, unique IPs, blocked count, block rate
- Requests-over-time area chart (ApexCharts)
- SVG world choropleth map (d3-geo + topojson-client, React 19 compatible)
- Top countries table with flag emojis
- HTTP protocol donut chart
- Top user agents horizontal bar chart
- Recent blocked requests table with pagination
- Traffic (24h) summary card on Overview page linking to analytics
- 7 authenticated API routes under /api/analytics/
- Share caddy-logs volume with web container (read-only)
- group_add caddy GID to web container for log file read access
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>