Commit Graph

36 Commits

Author SHA1 Message Date
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
fuomag9
fc680d4171 fix: use bun:sqlite in production, better-sqlite3 as test-only devDep
Production (Docker): src/lib/db.ts now uses bun:sqlite + drizzle-orm/bun-sqlite.
No native addon compilation needed — bun:sqlite is a Bun built-in. The Dockerfile
drops all native build tools (python3, make, g++) and uses --ignore-scripts.

Tests (Vitest/Node.js): bun:sqlite is unavailable under Node.js, so:
- tests/helpers/db.ts keeps better-sqlite3 + drizzle-orm/better-sqlite3 for
  integration tests that need a real in-memory SQLite
- vitest.config.ts aliases bun:sqlite → a thin better-sqlite3 shim and
  drizzle-orm/bun-sqlite → drizzle-orm/better-sqlite3 for unit tests that
  transitively import src/lib/db.ts without executing any queries
- better-sqlite3 stays as a devDependency (test-only, not built in Docker)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 11:53:33 +01:00
fuomag9
4b5323a7bf feat: add structured redirects and path prefix rewrite for proxy hosts
Adds two new UI-configurable Caddy patterns that previously required raw JSON:
- Per-path redirect rules (from/to/status) emitted as a subroute handler before
  auth so .well-known paths work without login; supports full URLs, cross-domain
  targets, and wildcard path patterns (e.g. /.well-known/*)
- Path prefix rewrite that prepends a segment to every request before proxying
  (e.g. /recipes → upstream sees /recipes/original/path)

Config is stored in the existing meta JSON column (no schema migration). Includes
integration tests for meta serialization and E2E functional tests against a real
Caddy instance covering relative/absolute destinations, all 3xx status codes, and
various wildcard combinations. Adds traefik/whoami to the test stack to verify
rewritten paths actually reach the upstream.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 17:53:33 +01:00
fuomag9
ea26c57859 add mTLS tests 2026-03-14 16:42:14 +01:00
fuomag9
73c90894b1 Handle wildcard proxy hosts and stabilize test coverage
- accept wildcard proxy host domains like *.example.com with validation and normalization
- make exact hosts win over overlapping wildcards in generated routes and TLS policies
- add unit coverage for host-pattern priority and wildcard domain handling
- add a single test:all entry point and clean up lint/typecheck issues so the suite runs cleanly
- run mobile layout Playwright checks under both chromium and mobile-iphone
2026-03-14 01:03:34 +01:00
fuomag9
27b7fadacc test: restrict mobile-iphone project to mobile tests only 2026-03-12 09:07:15 +01:00
fuomag9
6e8db4ec39 test: add mobile layout E2E tests for iPhone 15
- 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>
2026-03-12 09:04:58 +01:00
fuomag9
bfcc24eac0 test: add iPhone 15 Playwright project 2026-03-12 01:33:17 +01:00
fuomag9
26fcf8ca90 fix WAF silently dropping WebSocket upgrade requests
When allowWebsocket=true and WAF is enabled, the WAF handler sits first
in the handler chain and processes the initial HTTP upgrade request
(GET + Upgrade: websocket). If any rule matches, Coraza can block the
handshake before SecAuditEngine captures it — producing no log entry
and an unexplained connection failure from the client's perspective.

Fix: when allowWebsocket=true, prepend a phase:1 SecLang rule that
matches Upgrade: websocket (case-insensitive) and turns the rule engine
off for that transaction via ctl:ruleEngine=off. After the 101
Switching Protocols response the connection becomes a raw WebSocket
tunnel that the WAF cannot inspect anyway, so this bypass has no impact
on normal HTTP traffic through the same host.

The rule is inserted before OWASP CRS includes so it always fires first
regardless of which ruleset is loaded.

Add 9 unit tests in caddy-waf.test.ts covering: bypass present/absent,
phase:1 placement, case-insensitive regex, nolog/noauditlog flags,
ordering before CRS, and compatibility with custom directives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 23:14:12 +01:00
fuomag9
d6df70ab5f fix WAF merge mode ignoring host.enabled=false, extract resolveEffectiveWaf
Bug: when a proxy host had per-host WAF explicitly disabled (enabled:false)
with waf_mode:"merge" (or no waf_mode set), resolveEffectiveWaf entered the
merge branch and returned enabled:true unconditionally, applying the global
WAF to a host the user had opted out of.

Fix: add `if (host.enabled === false) return null` at the top of the merge
branch. Explicit opt-out now takes precedence over the global setting
regardless of mode. The override mode already handled this correctly.

Also extract resolveEffectiveWaf from caddy.ts into caddy-waf.ts so it
can be unit tested. Add 12 new tests covering no-config fallback,
merge opt-out regression, merge settings combination, and override mode.

What runs without OWASP CRS: only SecRuleEngine + audit directives +
any custom_directives. The @coraza.conf-recommended and CRS includes
are gated behind load_owasp_crs (fixed in previous commit).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 21:39:00 +01:00
fuomag9
b5bb668bd9 test: suppress console output noise in vitest
Add onConsoleLog: () => false to vitest config to silence expected
warn/error calls from production code when tests deliberately feed bad
input (e.g. parseJson with malformed JSON). Tests can still use
vi.spyOn(console, ...) to assert on console calls explicitly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 02:39:29 +01:00
fuomag9
f0825d6497 fix WAF crash when enabled without OWASP CRS, add regression tests
The WAF handler always prepended 'Include @coraza.conf-recommended' to the
SecLang directives regardless of load_owasp_crs. The @-prefixed paths only
resolve from the embedded coraza-coreruleset filesystem, which the Caddy
WAF plugin mounts only when load_owasp_crs=true. Without it Caddy fails:
  "failed to readfile: open @coraza.conf-recommended: no such file or directory"

Fix: gate all @-prefixed Include directives behind load_owasp_crs.

Also extract buildWafHandler from caddy.ts into caddy-waf.ts so it can be
unit tested in isolation, and add tests/unit/caddy-waf.test.ts (19 tests)
covering the regression, CRS include ordering, excluded rule IDs, and
handler structure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 01:19:10 +01:00
fuomag9
fd847e7eb5 fix mTLS cross-CA isolation bug, add instance-sync and mTLS tests
Extract pemToBase64Der and buildClientAuthentication from caddy.ts into
a new caddy-mtls.ts module, adding groupMtlsDomainsByCaSet to group mTLS
domains by their CA fingerprint before building TLS connection policies.

Previously all mTLS domains sharing a cert type (auto-managed, imported,
or managed) were grouped into a single policy, causing CA union: a client
cert from CA_B could authenticate against a host that only trusted CA_A.
The fix creates one policy per unique CA set, ensuring strict per-host
CA isolation across all three TLS policy code paths.

Also adds:
- tests/unit/caddy-mtls.test.ts (26 tests) covering pemToBase64Der,
  buildClientAuthentication, groupMtlsDomainsByCaSet, and cross-CA
  isolation regression tests
- tests/unit/instance-sync-env.test.ts (33 tests) for the five pure
  env-reading functions in instance-sync.ts
- tests/integration/instance-sync.test.ts (16 tests) for
  buildSyncPayload and applySyncPayload using an in-memory SQLite db
- Fix tests/helpers/db.ts to use a relative import for db/schema so it
  works inside vi.mock factory dynamic imports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 18:32:52 +01:00
fuomag9
e5ba3e1ed9 refractor code to allow more tests 2026-03-07 16:53:36 +01:00
fuomag9
f85c425ac1 fixed more tests 2026-03-07 11:22:54 +01:00
fuomag9
543b598d78 fixed tests more 2026-03-07 02:28:16 +01:00
fuomag9
2537fc8a14 fix tests 2026-03-07 02:16:35 +01:00
fuomag9
6e987e2c90 fix more tests paths 2026-03-07 02:11:45 +01:00
fuomag9
77e9a7d2f1 fix test docker 2026-03-07 02:08:21 +01:00
fuomag9
3572b482e8 added tests 2026-03-07 02:02:14 +01:00