Files
zpan/shared
Jasper Van 3e6d3ee63b feat: v2.4.0 T5 — /api/ihost/config + Cloudflare for SaaS integration (#316)
* feat: add /api/ihost/config endpoint with Cloudflare for SaaS integration

- Add CfCustomHostnamesClient service (thin CF API wrapper; no-op when CF env vars absent)
- Add /api/ihost/config route (GET/PUT/DELETE) following email-config pattern
- GET lazily refreshes domain verification from CF; PUT upserts config, registers/deregisters CF hostnames; DELETE best-effort CF cleanup + row removal
- PUT rejects enabled=false (must use DELETE); validates customDomain hostname format; validates refererAllowlist entries as URL origins; catches unique constraint → 409
- Add putIhostConfigSchema and IhostConfigResponse to shared schemas/types
- Mount route in app.ts under /api/ihost/config
- Add image_hosting_configs and image_hostings tables to test setup SQL
- Add 22 integration tests covering all acceptance criteria
- Update v2.4.md roadmap with config API notes; add docs/ihost-custom-domain-node.md

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix(ihost-config): restrict PUT/DELETE to owner role, add CF client unit tests, fix CodeQL URL check

- Change requireTeamRole('editor') → requireTeamRole('owner') on PUT and DELETE (spec requires owner/admin only)
- Add explicit editor-role 403 tests for PUT and DELETE
- Add server/services/cf-custom-hostnames.test.ts: 16 unit tests covering register/getStatus/delete success, 409/4xx/network errors, no-op behavior, createCfClient factory
- Add integration tests: GET domainStatus=verified, domainStatus=none, refererAllowlist JSON parsing, CF lazy verification active/pending paths, dnsInstructions CNAME vs manual, APP_HOST rejection, CF register on PUT, CF delete+register on domain change, CF 409 from register, clear refererAllowlist, DELETE best-effort CF cleanup (success + fail-graceful)
- Replace .includes('cloudflare.com') with new URL(url).host === 'api.cloudflare.com' to fix CodeQL CWE-20 incomplete URL substring sanitization
- Make createTestApp accept optional envOverrides to enable CF-configured integration tests

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* test(ihost-config): add coverage for uncovered error paths to reach 95%

Add 4 targeted integration tests that cover the previously-uncovered
branches in server/routes/ihost-config.ts:
- PUT INSERT: CF register() throws non-CfConflict error → propagates
- PUT UPDATE: CF delete() fails (best-effort console.warn) → request succeeds
- PUT UPDATE: CF register() throws non-CfConflict error → propagates
- PUT UPDATE: DB unique constraint on UPDATE → 409 (org2 steals org1 domain)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 04:00:17 -04:00
..