69 Commits

Author SHA1 Message Date
99819b70ff added caddy-proxy-manager for testing
Some checks failed
Build and Push Docker Images (Trusted) / build-and-push (., docker/caddy/Dockerfile, caddy) (push) Has been cancelled
Build and Push Docker Images (Trusted) / build-and-push (., docker/l4-port-manager/Dockerfile, l4-port-manager) (push) Has been cancelled
Build and Push Docker Images (Trusted) / build-and-push (., docker/web/Dockerfile, web) (push) Has been cancelled
Tests / test (push) Has been cancelled
2026-04-21 22:49:08 +00:00
fuomag9
4c5ad53370 Fix inflated certificate counts in dashboard and certificates page
The dashboard overview counted all proxy hosts without a cert as ACME
certs, ignoring wildcard deduplication. The certificates page only
deduplicated the current page of results (25 rows) but used a full
count(*) for the total, so hosts on other pages covered by wildcards
were never subtracted.

Now both pages fetch all ACME hosts, apply full deduplication (cert
wildcard coverage + ACME wildcard collapsing), then paginate/count
from the deduplicated result.

Also fixes a strict-mode violation in the E2E test where DataTable's
dual mobile/desktop rendering caused getByText to match two elements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 21:01:39 +02:00
fuomag9
6515da666f Fix duplicate ACME entries for subdomains covered by wildcard ACME host
The previous fix (92fa1cb) only collapsed subdomains when an explicit
managed/imported certificate had a wildcard. When all hosts use Caddy
Auto (no certificate assigned), the wildcard ACME host was not checked
against sibling subdomain hosts, so each showed as a separate entry.

Also adds a startup migration to rename the stored IONOS DNS credential
key from api_token to auth_api_token for users who configured IONOS
before ef62ef2.

Closes #110

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 18:04:38 +02:00
fuomag9
96bac86934 Fix L4 port manager failing to recreate caddy after Docker restart
The sidecar's `docker compose up` command lacked `--pull never`, so
Docker Compose would attempt to pull the caddy image from ghcr.io when
the local image was missing or stale. Since the sidecar has no registry
credentials this failed with 403 Forbidden.

Closes #117

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 11:35:12 +02:00
fuomag9
dbfc340ea4 Fix logout redirect to 0.0.0.0 instead of configured BASE_URL
Closes #113

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 09:51:51 +02:00
fuomag9
7d61528dad Fix login rejection for usernames containing hyphens
better-auth's default username validator only allows [a-zA-Z0-9_.],
rejecting hyphens with a generic "invalid username or password" error.
Added a custom validator that also permits hyphens.

Closes #112

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 12:15:18 +02:00
fuomag9
92fa1cb9d8 Fix duplicate certificate display for wildcard-covered subdomains
When a wildcard cert (e.g. *.domain.de) existed and a proxy host was created
for a subdomain (e.g. sub.domain.de) without explicitly linking it, the
certificates page showed it as a separate ACME entry. Now hosts covered by
an existing wildcard cert are attributed to that cert's "Used by" list instead.

Closes #110

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 12:10:17 +02:00
fuomag9
2c70f2859a Add multi-provider DNS registry for ACME DNS-01 challenges
Replace hardcoded Cloudflare DNS-01 with a data-driven provider registry
supporting 11 providers (Cloudflare, Route 53, DigitalOcean, Duck DNS,
Hetzner, Vultr, Porkbun, GoDaddy, Namecheap, OVH, Linode). Users can
configure multiple providers with encrypted credentials and select a
default. Per-certificate provider override is supported via providerOptions.

- Add src/lib/dns-providers.ts with provider definitions, credential
  encrypt/decrypt, and Caddy config builder
- Change DnsProviderSettings to multi-provider format with default selection
- Auto-migrate legacy Cloudflare settings on startup (db.ts)
- Normalize old single-provider format on read (getDnsProviderSettings)
- Refactor buildTlsAutomation() to use provider registry
- Add GET /api/v1/dns-providers endpoint for provider discovery
- Add dns-provider settings group to REST API and instance sync
- Replace Cloudflare settings card with multi-provider UI (add/remove
  providers, set default, dynamic credential forms)
- Add 10 DNS provider modules to Caddy Dockerfile
- Update OpenAPI spec, E2E tests, and unit test mocks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 18:01:16 +02:00
fuomag9
60633bf6c3 Fix unused variable lint error in api-security test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 10:28:48 +02:00
fuomag9
8f4c24119e Add excluded paths support for forward auth (fixes #108)
Allow users to exclude specific paths from Authentik/CPM forward auth
protection. When excluded_paths is set, all paths require authentication
EXCEPT the excluded ones — useful for apps like Navidrome that need
/share/* and /rest/* to bypass auth.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 10:11:35 +02:00
fuomag9
1472f84c15 Create disabled-user.spec.ts 2026-04-12 23:25:28 +02:00
fuomag9
7f4a268cf7 Fix flaky E2E tests: strict mode violations, OAuth redirect, parallelism
- Set workers: 1 to eliminate parallelism race conditions
- Fix groups test: use .first() for "0 members" assertion
- Fix access-control helper: match by name instead of generic "Delete List"
- Fix forward-auth-oauth: target Dex button specifically, handle /login in Dex URL
- Add comprehensive API security E2E tests (316 tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 23:17:49 +02:00
fuomag9
a5c0df6a9c Add comprehensive API security E2E tests
Tests every /api/v1/ endpoint (86 endpoints × 4 auth levels = 316 tests):
- Unauthenticated requests → 401 on all endpoints
- User role → 403 on admin-only endpoints
- Viewer role → 403 on admin-only endpoints
- Admin role → allowed on all endpoints
- Cross-user isolation: users cannot access other users' profiles

Uses Bearer API tokens (created directly in DB) to avoid
Better Auth rate limiting during test execution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:29:13 +02:00
fuomag9
3a16d6e9b1 Replace next-auth with Better Auth, migrate DB columns to camelCase
- Replace next-auth v5 beta with better-auth v1.6.2 (stable releases)
- Add multi-provider OAuth support with admin UI configuration
- New oauthProviders table with encrypted secrets (AES-256-GCM)
- Env var bootstrap (OAUTH_*) syncs to DB, UI-created providers fully editable
- OAuth provider REST API: GET/POST/PUT/DELETE /api/v1/oauth-providers
- Settings page "Authentication Providers" section for admin management
- Account linking uses new accounts table (multi-provider per user)
- Username plugin for credentials sign-in (replaces email@localhost pattern)
- bcrypt password compatibility (existing hashes work)
- Database-backed sessions via Kysely adapter (bun:sqlite direct)
- Configurable rate limiting via AUTH_RATE_LIMIT_* env vars
- All DB columns migrated from snake_case to camelCase
- All TypeScript types/models migrated to camelCase properties
- Removed casing: "snake_case" from Drizzle config
- Callback URL format: {baseUrl}/api/auth/oauth2/callback/{providerId}
- package-lock.json removed and gitignored (using bun.lock)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 21:11:48 +02:00
fuomag9
f007f2df0c Use safe test-range CIDRs in geoblock E2E tests to prevent worker interference
Replace 0.0.0.0/0 with RFC 5737 test ranges (198.51.100.0/24, etc.) in
persistence tests so saving geoblock rules to Caddy doesn't block real
traffic for concurrent test workers. The LAN Only preset save test uses
the API to verify saved values and immediately resets, minimizing the
window where block-all is active.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 19:16:49 +02:00
fuomag9
f9169c2ab2 Fix geo blocking form losing rules when switching tabs or collapsing accordion
Radix UI Tabs and Accordion unmount inactive/closed content from the DOM
by default. This caused hidden form inputs to be missing from FormData on
submit — saving while on the Block tab wiped all Allow rules (and vice
versa), and saving with the advanced accordion collapsed wiped redirect
URL, trusted proxies, and response settings.

Fix by adding forceMount to TabsContent and AccordionContent so all form
fields remain in the DOM regardless of which tab/panel is visible.

Also adds E2E regression tests covering both scenarios plus the RFC1918
preset, with proper afterEach cleanup to prevent test interference with
concurrent workers.

Fixes #99

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:34:31 +02:00
fuomag9
2dea2af20f Add E2E container health tests to catch crash-looping sidecars
Verifies all Docker containers in the test stack are running and healthy,
including a restart-count check on the l4-port-manager to detect permission
errors or other crash-loop scenarios that previously went unnoticed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 13:26:17 +02:00
fuomag9
5d0b4837d8 Security hardening: fix SQL injection, WAF bypass, placeholder injection, and more
- C1: Replace all ClickHouse string interpolation with parameterized queries
  (query_params) to eliminate SQL injection in analytics endpoints
- C3: Strip Caddy placeholder patterns from redirect rules, protected paths,
  and Authentik auth endpoint to prevent config injection
- C4: Replace WAF custom directive blocklist with allowlist approach — only
  SecRule/SecAction/SecMarker/SecDefaultAction permitted; block ctl:ruleEngine
  and Include directives
- H2: Validate GCM authentication tag is exactly 16 bytes before decryption
- H3: Validate forward auth redirect URIs (scheme, no credentials) to prevent
  open redirects
- H4: Switch 11 analytics/WAF/geoip endpoints from session-only requireAdmin
  to requireApiAdmin supporting both Bearer token and session auth
- H5: Add input validation for instance-mode (whitelist) and sync-token
  (32-char minimum) in settings API
- M1: Add non-root user to l4-port-manager Dockerfile
- M5: Document Caddy admin API binding security rationale
- Document C2 (custom config injection) and H1 (SSRF via upstreams) as
  intentional admin features

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 12:13:50 +02:00
fuomag9
e1c97038d4 Migrate analytics from SQLite to ClickHouse
SQLite was too slow for analytical aggregations on traffic_events and
waf_events (millions of rows, GROUP BY, COUNT DISTINCT). ClickHouse is
a columnar OLAP database purpose-built for this workload.

- Add ClickHouse container to Docker Compose with health check
- Create src/lib/clickhouse/client.ts with singleton client, table DDL,
  insert helpers, and all analytics query functions
- Update log-parser.ts and waf-log-parser.ts to write to ClickHouse
- Remove purgeOldEntries — ClickHouse TTL handles 90-day retention
- Rewrite analytics-db.ts and waf-events.ts to query ClickHouse
- Remove trafficEvents/wafEvents from SQLite schema, add migration
- CLICKHOUSE_PASSWORD is required (no hardcoded default)
- Update .env.example, README, and test infrastructure

API response shapes are unchanged — no frontend modifications needed.
Parse state (file offsets) remains in SQLite.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 00:05:38 +02:00
fuomag9
833284efb1 Add forward auth E2E tests with Dex OIDC provider
- Add Dex OIDC provider to test Docker Compose stack with static test
  users (alice, bob) and pre-configured OAuth client
- Add forward-auth.spec.ts: credential-based forward auth flow tests
  (redirect, portal form, login, session cookie, forged cookie rejection)
- Add forward-auth-oauth.spec.ts: full OAuth forward auth flow tests
  including user-based access (allowed/denied), group-based access,
  access revocation, and credential login coexisting with OAuth
- Add waitForStatus helper for polling specific HTTP status codes
- Expand portal.spec.ts with OAuth button visibility, URI scheme
  rejection, and strict alert selector tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 21:10:28 +02:00
fuomag9
1ea6add989 Increase timeout for proxy hosts sort button visibility check
Intermittent failure — the default 5s wasn't enough when the page
loaded slowly during a long E2E run (227/228 passed).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:51:57 +02:00
fuomag9
2f12475ab0 Fix E2E test locator ambiguity and lint errors
- dashboard.spec.ts: anchor regex /^\d+\s+Proxy Hosts/ to not match
  "L4 Proxy Hosts" sidebar link
- role-access.spec.ts: use exact: true for "Proxy Hosts" link
- users.spec.ts: match any user count (/\d+ users?/) since other test
  suites create additional users
- groups.spec.ts: remove unused emptyText variable
- link-account.spec.ts: remove unused context parameter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 09:59:11 +02:00
fuomag9
785cfb6cc5 Fix dashboard stat card selector and use Bun.password.hash for test users
- dashboard: Match stat cards via link role with count+label pattern
  to avoid matching subtitle paragraph containing "certificates"
- role-access: Use Bun.password.hash (built-in bcrypt) instead of
  bcryptjs which is not installed in the production container

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:04:09 +02:00
fuomag9
bc5658f164 Fix 7 E2E test failures from strict mode violations and environment issues
- api-docs: Don't rely on CDN-loaded Swagger UI class in test env
- dashboard: Use `p` locator for stat card labels to avoid matching nav
- groups: Scope add-member click to bordered container to avoid nav match
- link-account: Remove assertion on error= URL param (not always present)
- portal: Use exact:true for "Sign in" button (OAuth button also matches)
- role-access: Use ESM imports in bun -e script, use getByLabel for login
  fields, increase waitForURL timeout, use exact button match

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:01:15 +02:00
fuomag9
7fe6b10788 Add E2E tests for untested pages and enforce role-based access control
Allow non-admin users (user/viewer) to access / and /profile while
blocking admin-only pages. The dashboard layout now uses requireUser()
instead of requireAdmin(), and the sidebar filters nav items by role.
Non-admin users see a minimal welcome page without stat cards.

New test files (86 tests across 7 files):
- dashboard, users, groups, api-docs, portal, link-account specs
- role-access spec with full RBAC coverage for all 3 roles

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:58:22 +02:00
fuomag9
155268a180 Fix 19 ESLint unused-variable errors across source and test files
Remove unused imports, functions, and variables flagged by
@typescript-eslint/no-unused-vars and no-useless-assignment rules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:32:54 +02:00
fuomag9
0542ed56cb Fix mTLS fail-closed bypass when all certs for a CA are revoked
Two bugs caused mTLS to be silently disabled when all issued client
certificates for a CA were revoked:

1. New cert-based trust model (caddy.ts): When deriving CA IDs from
   trusted cert IDs, revoked certs were invisible (active-only query),
   causing derivedCaIds to be empty and the domain to be dropped from
   mTlsDomainMap entirely — no mTLS policy at all. Fix by falling back
   to a cert-ID-to-CA-ID lookup that includes revoked certs, keeping the
   domain in the map so it gets a fail-closed policy.

2. Legacy CA-based model (caddy-mtls.ts): buildClientAuthentication
   returned null when all certs were revoked, relying on Caddy's
   experimental "drop" TLS connection policy field which didn't work
   reliably. Fix by pinning to the CA cert itself as a trusted_leaf_certs
   entry — no client cert can hash-match a CA certificate (and presenting
   the CA cert would require its private key, already a full compromise).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:31:12 +02:00
fuomag9
881992b6cc Restrict analytics, GeoIP status, and OpenAPI spec to admin role
Pentest found that all 8 analytics API endpoints, the GeoIP status
endpoint, and the OpenAPI spec were accessible to any authenticated
user. Since the user role should only have access to forward auth
and self-service, these are now admin-only.

- analytics/*: requireUser → requireAdmin
- geoip-status: requireUser → requireAdmin
- openapi.json: add requireApiAdmin + change Cache-Control to private
- analytics/api-docs pages: requireUser → requireAdmin (defense-in-depth)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:02:13 +02:00
fuomag9
94efaad5dd Add user management admin page with role, status, and profile editing
- New /users page with search, inline editing, role/status changes, and deletion
- Model: added updateUserRole, updateUserStatus, deleteUser functions
- API: PUT /api/v1/users/[id] now supports role and status fields, added DELETE
- Safety: cannot change own role/status or delete own account

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:40:10 +02:00
fuomag9
03c8f40417 Add forward auth portal — CPM as built-in IdP replacing Authentik
CPM can now act as its own forward auth provider for proxied sites.
Users authenticate at a login portal (credentials or OAuth) and Caddy
gates access via a verify subrequest, eliminating the need for external
IdPs like Authentik.

Key components:
- Forward auth flow: verify endpoint, exchange code callback, login portal
- User groups with membership management
- Per-proxy-host access control (users and/or groups)
- Caddy config generation for forward_auth handler + callback route
- OAuth and credential login on the portal page
- Admin UI: groups page, inline user/group assignment in proxy host form
- REST API: /api/v1/groups, /api/v1/forward-auth-sessions, per-host access
- Integration tests for groups and forward auth schema

Also fixes mTLS E2E test selectors broken by the RBAC refactor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:32:17 +02:00
fuomag9
277ae6e79c Add mTLS RBAC with path-based access control, role/cert trust model, and comprehensive tests
Implements full role-based access control for mTLS client certificates:
- Database: mtls_roles, mtls_certificate_roles, mtls_access_rules tables with migration
- Models: CRUD for roles, cert-role assignments, path-based access rules
- Caddy config: HTTP-layer RBAC enforcement via CEL fingerprint matching in subroutes
- New trust model: select individual certs or entire roles instead of CAs (derives CAs automatically)
- REST API: /api/v1/mtls-roles, cert assignments, proxy-host access rules endpoints
- UI: Roles management tab (card-based), cert/role trust picker, inline RBAC rule editor
- Fix: dialog autoclose bug after creating proxy host (key-based remount)
- Tests: 85 new tests (785 total) covering models, schema, RBAC route generation, leaf override, edge cases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:40:21 +02:00
fuomag9
b9a88c4330 fix: remove ACME cert scanning to eliminate caddy-data permission issue (#88)
Caddy's certmagic creates storage dirs with hardcoded 0700 permissions,
making the web container's supplementary group membership ineffective.
Rather than working around this with ACLs or chmod hacks, remove the
feature entirely — it was cosmetic (issuer/expiry display) for certs
that Caddy auto-manages anyway.

Also bump access list dropdown timeout from 5s to 10s to fix flaky E2E test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:34:18 +02:00
fuomag9
71502e4879 test: full pipeline integration test for geo-blocked request analytics
Tests the complete chain: raw Caddy log lines → collectBlockedSignatures
→ parseLine → INSERT into DB → all analytics queries (summary, countries,
timeline, blocked events list) return correct blocked counts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 00:13:05 +02:00
fuomag9
e26d7a2c3f feat: improve LocationRulesFields UI and add unit tests for buildLocationReverseProxy
- Replace textarea with per-upstream rows (protocol dropdown + address input),
  matching the existing UpstreamInput component pattern
- Export buildLocationReverseProxy for testing
- Add 14 unit tests covering: dial formatting, HTTPS/TLS transport,
  host header preservation, path sanitization, IPv6, mixed upstreams

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:51:08 +01:00
fuomag9
55f4ba4e80 fix: make location rules integration test honest about what it tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 14:51:08 +01:00
fuomag9
0f9bd04ec7 feat: add LocationRule type and model layer support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 14:51:08 +01:00
fuomag9
7a12ecf2fe fix: E2E select dropdown reliability and stale eslint-disable directives
- Fix Radix Select interaction in proxy host E2E helper: scroll trigger
  into view and wait for option visibility before clicking (fixes flaky
  access-control.spec.ts timeout)
- Apply same fix to certificate selector for consistency
- Remove stale eslint-disable directives from pre-existing test files
  (now covered by test-wide eslint config override)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:50:14 +01:00
fuomag9
d9806e84e6 fix: resolve lint and typecheck errors in API routes and tests
- Remove unused imports (users, and) from api-tokens model
- Fix password_hash destructure lint error in user routes
- Fix apiErrorResponse mock pattern in all 12 test files (use instanceof)
- Remove stale eslint-disable directives from test files
- Add eslint override for tests (no-explicit-any, no-require-imports)
- Fix unused vars in settings and tokens tests
- Fix unused tokenB in integration test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:33:47 +01:00
fuomag9
28f61082ce test: comprehensive API test coverage with full input variations
- OpenAPI endpoint: 5 tests (spec structure, paths, headers, schemas)
- Proxy hosts: POST/PUT/GET with all nested fields (authentik, load_balancer,
  dns_resolver, geoblock, waf, mtls, redirects, rewrite) + error cases
- L4 proxy hosts: TCP+TLS, UDP, matcher/protocol variations + error cases
- Certificates: managed with provider_options, imported with PEM fields
- Client certificates: all required fields, revoke with revoked_at
- Access lists: seed users, entry add with username/password + error cases
- Settings: GET+PUT for all 11 groups (was 3), full data shapes
- API auth: empty Bearer, CSRF session vs Bearer, apiErrorResponse variants
- API tokens integration: cross-user isolation, admin visibility, inactive user
- CA certificates: PUT/DELETE error cases

646 tests total (54 new)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:15:22 +01:00
fuomag9
de28478a42 feat: add comprehensive REST API with token auth, OpenAPI docs, and full test coverage
- 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>
2026-03-26 09:45:45 +01:00
fuomag9
a2fefb5a07 fix: use .last() for empty state check in L4 hosts E2E test
DataTable renders mobile (block md:hidden) and desktop (hidden md:block)
variants. .first() resolved to the hidden mobile element at desktop viewport.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 09:35:48 +01:00
fuomag9
452bb6eb78 fix: unskip all E2E tests, fix L4 empty state and analytics mobile overflow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 08:30:25 +01:00
fuomag9
2d081372f0 fix: L4 UDP proxy routing and TCP disable/re-enable test reliability
- Add udp/ prefix to upstream dial addresses for UDP proxy hosts
  (Caddy L4 requires udp/ prefix on both listen and dial for UDP)
- Fix TCP "disabled host" test to check data echo instead of connection
  refusal (Docker port mapping always accepts TCP handshake)
- Add waitForTcpRoute before "both ports" test to handle re-enable timing
- Increase UDP route wait timeout to 30s for listener startup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 08:10:02 +01:00
fuomag9
6b297a11ad fix: update mTLS E2E test selectors for shadcn UI components
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 07:53:50 +01:00
fuomag9
7261fa24d8 fix: add udp/ prefix to Caddy L4 UDP listen addresses and fix E2E test selectors
- Fix Caddy L4 config to use "udp/:PORT" listen syntax for UDP proxy hosts
  (previously used bare ":PORT" which Caddy treated as TCP)
- Fix TCP unused port test to check data echo instead of connection refusal
  (Docker port mapping accepts TCP handshake even without a Caddy listener)
- Fix mTLS import test to wait for sheet close and scope cert name to table
- Fix CA certificate generate test to scope name assertion to table
- Remaining L4 routing test failures are infrastructure issues with Docker
  port forwarding and Caddy L4 UDP listener startup timing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:37:39 +01:00
fuomag9
9c60d11c2c feat: improve UI contrast, dark mode, dialog sizing, color coherence, and add table sorting
- Fix dialog scrollability (flex layout + max-h-[90dvh]) and increase L4 dialog to lg width
- Add styled enable card to L4 dialog matching proxy host pattern
- Unify section colors across proxy host and L4 dialogs (cyan=LB, emerald=DNS, violet=upstream DNS, rose=geo, amber=mTLS)
- Improve light mode contrast: muted-foreground oklch 0.552→0.502, remove opacity modifiers on secondary text
- Improve dark mode: boost muted-foreground to 0.85, increase border opacity 10%→16%, input 15%→20%
- Add bg-card to DataTable wrapper and bg-muted/40 to table headers for surface hierarchy
- Add semantic badge variants (success, warning, info, muted) and StatusChip dark mode fix
- Add server-side sortable columns to Proxy Hosts and L4 Proxy Hosts (name, upstream, status, protocol, listen)
- Add sortKey to DataTable Column type with clickable sort headers (ArrowUp/Down indicators, URL param driven)
- Fix E2E test selectors for shadcn UI (label associations, combobox roles, dropdown menus, mobile drawer)
- Add htmlFor/id to proxy host form fields and aria-labels to select triggers for accessibility
- Add sorting E2E tests for both proxy host pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:17:56 +01:00
fuomag9
0f7676be14 fix: force linux/amd64 for tcp-echo in test compose (arm64 host compat)
cjimti/go-echo has no arm64 image; explicit platform lets Docker pull
and run it via Rosetta/qemu on Apple Silicon without unhealthy warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 00:45:57 +01:00
fuomag9
b5ef7aab7b fix: resolve all lint errors from L4 feature
- Remove unused Box/Collapse from L4PortsApplyBanner
- Remove unused Stack from RedirectsFields
- Remove unused updateL4ProxyHost import from validation test
- Add eslint-disable-next-line for require() in vi.hoisted() blocks
  (necessary pattern since vi.hoisted runs before ESM imports)
- Add file-level eslint-disable no-explicit-any for test files that
  intentionally pass invalid types to test validation logic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 00:37:52 +01:00
fuomag9
00c9bff8b4 feat: instant banner refresh on L4 mutations + master-slave L4 sync
Banner (L4PortsApplyBanner):
- Accept refreshSignal prop; re-fetch /api/l4-ports when it changes
- Signal fires immediately after create/edit/delete/toggle in L4ProxyHostsClient
  without waiting for a page reload

Master-slave replication (instance-sync):
- Add l4ProxyHosts to SyncPayload.data (optional for backward compat
  with older master instances that don't include it)
- buildSyncPayload: query and include l4ProxyHosts, sanitize ownerUserId
- applySyncPayload: clear and re-insert l4ProxyHosts in transaction;
  call applyL4Ports() if port diff requires it so the slave's sidecar
  recreates caddy with the correct ports
- Sync route: add isL4ProxyHost validator; backfill missing field from
  old masters; validate array when present

Tests (25 new tests):
- instance-sync.test.ts: buildSyncPayload includes L4 data, sanitizes ownerUserId;
  applySyncPayload replaces L4 hosts, handles missing field, writes trigger
  when ports differ, skips trigger when ports already match
- l4-ports-apply-banner.test.ts: banner refreshSignal contract + client
  increments counter on all mutation paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 00:22:44 +01:00
fuomag9
3a4a4d51cf feat: add L4 (TCP/UDP) proxy host support via caddy-l4
- 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>
2026-03-22 00:11:16 +01:00