6.1 KiB
6.1 KiB
Refinement Phase 0 — Local end-to-end bring-up & the integration seam — Report (2026-07-12)
What was built
Removed the three hard integration blockers so the client and server actually talk on one machine, and proved it with one real authenticated round-trip. Plumbing only — no business logic, money path, auth crypto, or envelope shape changed.
- CORS (§3.1). New
Baya.WebFramework/ServiceConfiguration/CorsServiceExtension.cs—AddCorsPolicies(configuration)builds the named policyBalinyaarWebClientfromCors:AllowedOrigins(string array), defaulting tohttp://localhost:3000when unset. Allows exactly the four headers the client sends (Authorization,Content-Type,Accept-Language,Idempotency-Key) +AllowAnyMethod(); noAllowCredentials()(the client uses a bearer header, not a cookie). Registered in the service chain andapp.UseCors(CorsServiceExtension.PolicyName)placed afterUseRouting()and beforeUseRateLimiter()so pre-flightOPTIONSis answered before the limiter/auth run. - Local database story (§3.2).
server/docker-compose.ymlrewritten to a single-purpose SQL Server 2022 (Developer edition) onlocalhost:1433with a dev-onlyMSSQL_SA_PASSWORD+ a named volume + healthcheck. (The old compose referenced an unbuildablebobby-bayaapp image and exposed1435.) The committedappsettings.json/appsettings.Development.jsonconnection strings (SqlServer+logDb) are now non-working placeholders (Password=SET_VIA_USER_SECRETS_OR_ENV); the real local value comes fromdotnet user-secrets. Added<UserSecretsId>baya-web-api</UserSecretsId>to the API csproj (it was missing — user-secrets weren't actually wired before). - Development-only OTP retrieval (§3.3).
GET /api/v1/dev/last_otp/{phone}(DevController) returns the most recent OTP for a phone so a browser / e2e flow can log in without an SMS gateway. Backed by a new Development-onlyDevOtpStore+DevCapturingSmsSender(anISmsSenderdecorator that captures the code then delegates to the log-onlyLoggingSmsSender), wired only in Development byAddDevelopmentOtpCapture(). The endpoint 404s in every non-Development environment and the capture is not even registered there — two independent guarantees it can't leak a code. It does not weaken theotprate-limit or the per-phone resend window. - Client env & runbook (§3.4). Verified
client/.env.developmentalready hasNEXT_PUBLIC_API_URL = https://localhost:5002(matches the API's bound URL) — no client code changed, no mock flag flipped. Wrotedev/post-phase/refinement/RUNBOOK.md— the copy-pasteable "run the whole app locally" procedure (dev-cert trust, compose, user-secrets, both run commands, OTP-from-logs / dev-endpoint, DevTools verification, troubleshooting).
What is now testable (and exactly how)
- Automated (in the suite, +11 tests → 369 total): 3 new
Baya.Test.Apiintegration tests (CorsAndDevBringUpTests): a pre-flightOPTIONS /api/v1/auth/request_otpfromhttp://localhost:3000reflectsAccess-Control-Allow-Origin: http://localhost:3000; the same from a foreign origin gets no allow-origin header;GET /api/v1/dev/last_otp/...returns 404 in the (non-Development) test host. Plus 8Baya.Test.Foundationunit tests (DevOtpBringUpTests) covering the store's capture/latest/ spelling-insensitive lookup + the decorator's capture-and-still-delegate behaviour. - Manual (the §7 proof): follow
RUNBOOK.md—docker compose up -d, set the user-secret,dotnet run,npm run dev, open/fa/login, request an OTP, read the code from the server console (orGET /api/v1/dev/last_otp/{phone}), submit → land on the customer home with 200s on/auth/request_otp,/auth/verify_otp,/api/v1/meand no CORS error. Negative check: removeapp.UseCors(...)→ the same flow fails with a CORS error.
What is mocked / waiting on a real service
- No new seam. The existing
ISmsSender(LoggingSmsSender) stays the interim OTP channel (logs the code); itsmocks-registry.mdrow is updated to note the Development-onlyDevCapturingSmsSender+/dev/last_otpaffordance. Real SMS is Refinement Phase 8. - The
DevOtpStore/DevCapturingSmsSender//dev/last_otpendpoint are a Development-only dev affordance, not a seam (per the phase §4) — superseded by real SMS in Phase 8.
Contracts
- None produced. This phase ships plumbing (CORS middleware) + a Development-only diagnostic endpoint the
frontend does not consume as a contract, so no
dev/contracts/domains/*.mdwas written and theswagger.v1.jsonsnapshot was not regenerated (the only new path is the dev-only helper Phase 8 removes — regenerating would add churn for a path no client binds to). Auth remains the one real domain the frontend consumes, unchanged.
Docs updated
server/CLAUDE.md"Startup wiring" — addedAddCorsPolicies(config)to the registration list, the Development-onlyAddDevelopmentOtpCapture()note, andCORSin the pipeline order (after routing, before the rate limiter). Project map — noted the Development-onlyDevcontroller.dev/post-phase/refinement/RUNBOOK.md— new local-run runbook.dev/shared-working-context/reports/mocks-registry.md—ISmsSenderrow updated.
Follow-ups for later phases
- Phase 1 — local-dev demo seed (nurses/variants/search rows) so discovery/booking aren't empty on the real path.
- Phase 4 — flip the 21
USE_*_MOCKflags (this phase changed none). - Phase 5 — rotate the leaked remote
sacredentials + the dev-gradeIdentitySettingskeys (still committed as dev placeholders here); set real productionCors:AllowedOrigins. - Phase 8 — real SMS gateway; removes the
/dev/last_otphelper + theDevCapturingSmsSenderdecorator. - Note for deployed envs:
Cors:AllowedOriginsmust list the real web origin(s); an empty array falls back to the localhost dev origin (safe — a real user's origin won't match, so cross-origin is effectively denied until configured).