Commit Graph

4 Commits

Author SHA1 Message Date
Tyler 9bca4b608a feat(release): v0.2.0 — batch 837 export, ClaimCard, theme tokens
Backend:

- New POST /api/batches/{id}/export-837: regenerate X12 837 files
  for a list of claim_ids into a ZIP using HCPF file naming standards,
  with a unique interchange/group control number per export. Wire
  the clearhouse Loop 1000A (NM1*41 + PER) and per-payer receiver
  (NM1*40) blocks so the serializer no longer falls back to
  CYCLONE / RECEIVER placeholders.
- /api/parse-837 and /api/parse-835 now surface the server-side
  batch_id in both JSON and NDJSON response shapes so the frontend
  can hit batch-scoped endpoints without an extra listBatches
  round-trip.
- Filename helpers and the 837 serializer updated to match the new
  HCPF envelope; tests cover batch export, parse batch_id, and the
  serializer's control-number uniqueness guarantee.

Frontend:
- New shared components: ClaimCard, ClaimCard837, DominantKpiCard,
  EditorialNote, ExportBar, TickerTape, and a charts/ set
  (BarChart, HBarChart, SegmentedBar, AgingBars).
- New useBatchExport hook driving ExportBar's download flow against
  the new endpoint.
- ClaimDrawer, Lane, and Layout migrated from raw CSS-variable
  colors to Tailwind theme tokens (bg-card, text-foreground,
  border/60, etc.) for consistency with the rest of the instrument
  chrome; the active tab indicator gains a subtle accent glow.
- Upload, Inbox, Batches, BatchDiff, Reconciliation, and Acks pages
  reworked to compose the new shared components and consume the new
  batch-scoped API surface (notably ExportBar wired into Batches).

Tooling / Docs:
- Add audit-uiux.mjs and a docs/goodclaim.x12 sample fixture.
- Update ClaimDrawer testids and add coverage for the new
  components and the useBatchExport hook.

Rolls up into the v0.2.0 release tag.
2026-06-22 11:01:58 -06:00
Tyler b6607b2009 feat(api): harden CORS and surface 500/409 errors with proper CORS headers
Three independent improvements that fix real browser-facing bugs:

1. CORS: allow 127.0.0.1:5173 in addition to localhost:5173. Both
   resolve to the same Vite dev server but CORS treats them as distinct
   origins, so tabs opened via the IP form silently break.
2. CORS: support CYCLONE_ALLOWED_ORIGINS env var (comma-separated) for
   LAN / staging hosts. The middleware reads it at module import.
3. Catch-all exception handler: returns JSON 500 with CORS headers
   instead of a bare Uvicorn text/plain response. Without this, any
   unhandled exception is misreported by browsers as a CORS error
   because the body can't be read without the allow-origin header.
4. IntegrityError → 409: when (batch_id, patient_control_number) is
   UNIQUE-constrained and a duplicate collides, return 409 with the
   batch id instead of letting the exception 500. Same problem as (3)
   for the most common ingest failure mode.

Tests added:
- test_cors_headers_present_for_loopback_ip
- test_cors_extra_origins_via_env (uses importlib.reload because the
  allow-list is built at module import)
2026-06-21 16:29:59 -06:00
Tyler c835996bd6 feat(sp19): security hardening + rich health probe
Three pure-ASGI middlewares close completeness-review gaps §3.1.4
(no body/rate limits) and §3.1.25 (no security headers):

- BodySizeLimitMiddleware — rejects oversized uploads (50 MB
  default, CYCLONE_MAX_BODY_BYTES override). 413 on over-cap
  Content-Length and on chunked reads that cross the cap.
- RateLimitMiddleware — sliding-window per-IP limiter (300/min
  default, CYCLONE_RATE_LIMIT_PER_MIN override). 429 over the
  window. /api/health is exempt.
- SecurityHeadersMiddleware — stamps X-Content-Type-Options,
  X-Frame-Options, Referrer-Policy, Permissions-Policy, and a
  strict Content-Security-Policy on every response.

Every 413/429 also writes a tamper-evident api.request_rejected
event into the SP11 audit chain so an operator can correlate
rejections with the SP18 JSON logs.

GET /api/health is rewritten to return a subsystem snapshot:
DB connectivity (SELECT 1), MFT scheduler state, backup scheduler
state, live pubsub subscriber counts, last batch id + timestamp.
Returns status='degraded' if any subsystem is unhappy; per-subsystem
errors surfaced in the respective dict.

Cyclone.pubsub.EventBus.stats() — new method for live subscriber
counts.

13 new tests (test_security.py) + 1 updated (test_api.py health
endpoint). All 883 backend tests pass.
2026-06-21 10:21:01 -06:00
Tyler 07a8fbb720 feat(backend): FastAPI endpoint with NDJSON streaming and CORS for Vite frontend 2026-06-19 16:35:53 -06:00