feat(worker): Phase 2a — port asset read routes to Hono + Drizzle/Hyperdrive#36
Conversation
…erdrive Routes ported (GET only): - GET /api/assets (list with filters) - GET /api/assets/stats (aggregates) - GET /api/assets/:id (ownership-checked) - GET /api/assets/:assetId/evidence - GET /api/assets/:assetId/timeline Ownership key decision: assets.userId === claims.chitty_id (ChittyAuth era). Express used 'chitty_' + req.auth.userId (Replit Auth era). This is an intentional semantic alignment, not a bug. Documented in PR for review. DB adapter: drizzle-orm/postgres-js + Hyperdrive (per Cloudflare guidance). Per-request instantiation; Hyperdrive pools externally. Test seam: registerAssetRoutes(app, authMiddleware) factory pattern lets integration tests inject a pass-through middleware (zero production code change for the auth bypass). Data path is fully real against Neon. Canonical: @canon annotations on all new files; P/L/T/E/A enumerated in env. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6cb5fd6c37
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // ----------------------------------------------------------------- | ||
| app.get("/assets", authMiddleware, async (c) => { | ||
| const claims = c.get("claims"); | ||
| const userId = claims.chitty_id; |
There was a problem hiding this comment.
Query assets by legacy user key until ID migration completes
Using claims.chitty_id as assets.userId here breaks compatibility with the active write path: server/routes.ts still writes userId as chitty_${req.auth.userId} for asset/evidence/timeline records, and shared/schema.ts still defines assets.userId as FK to users.id (not users.chitty_id). In environments with existing legacy rows, these new read routes will return empty/404 for valid owner data until a full backfill/column migration is complete, so this route should either translate to the legacy key or join through users.chitty_id during the transition.
Useful? React with 👍 / 👎.
| app.get("/assets", authMiddleware, async (c) => { | ||
| const claims = c.get("claims"); | ||
| const userId = claims.chitty_id; | ||
| const db = getDb(c.env.CHITTYASSETS_DB.connectionString); |
There was a problem hiding this comment.
Guard missing Hyperdrive binding before dereferencing
All migrated handlers dereference c.env.CHITTYASSETS_DB.connectionString unconditionally, which will throw at runtime when the binding is not configured for the current worker environment (for example non-production/default envs in this repo config). That converts otherwise valid requests into 500s before any auth/query logic runs; add an explicit binding check and a controlled error response (or environment-specific fallback) before calling getDb().
Useful? React with 👍 / 👎.
| if (!TEST_DB_URL) { | ||
| throw new Error( | ||
| "TEST_DB_URL must be set to an ephemeral Neon branch connection string.", |
There was a problem hiding this comment.
Skip DB integration suite when TEST_DB_URL is absent
This file throws at module-load time when TEST_DB_URL is unset, so a normal npm test run fails before any tests execute in environments that do not provision a Neon branch URL. Because this suite is included by the repo-wide Vitest pattern, the hard throw makes unrelated test runs fail by default; gate the suite with describe.skipIf(...) (or similar) instead of throwing at import time.
Useful? React with 👍 / 👎.
| if (ASSET_ID_1) { | ||
| await sql`DELETE FROM assets WHERE id = ${ASSET_ID_1}`; | ||
| } | ||
| await sql`DELETE FROM users WHERE id = ${OWNER_CHITTY_ID}`; |
There was a problem hiding this comment.
Prevent teardown from deleting pre-existing shared user
The suite uses a fixed OWNER_CHITTY_ID and inserts with onConflictDoNothing(), so if that ID already exists in the shared Neon branch, the test reuses someone else’s row and then unconditionally deletes it in teardown. In shared/dev environments this can remove non-test data and break other tests or manual QA; generate a per-run owner ID (or track whether this test created the user) before issuing the final delete.
Useful? React with 👍 / 👎.
Summary
Phase 2a of the Express→Hono migration. Ports 5 read-only GET routes for the asset domain to the Cloudflare Worker (Hono + Drizzle/Hyperdrive).
Stacks on #34 (schema canonical remediation) → #33 (Phase 1 Hono skeleton).
Routes Ported
Express
server/routes.tsis untouched. New Hono routes mount BEFORE the 501 catch-all inworker/src/index.ts, so all other/api/*paths still returnnot_yet_migrated.Decided Unilaterally
1. Ownership Key Change
Express used
assets.userId === "chitty_" + req.auth.userId(Replit Auth era prefix). Hono usesassets.userId === claims.chitty_id(ChittyAuth era, canonicalVV-G-LLL-SSSS-P-YM-C-X). Intentional semantic alignment, not a bug. Production data migrated to ChittyAuth will use chitty_id directly as user_id. Documented inworker/src/routes/assets.tsheader.2. Drizzle Adapter
drizzle-orm/postgres-js+postgrespackage — Cloudflare-recommended for Hyperdrive (TCP via proxy, no WebSocket shim needed).@neondatabase/serverlessis not used in the Worker. Addspostgres@^3.4.9to deps.3. Test Auth Seam
registerAssetRoutes(app, authMiddleware)factory pattern lets integration tests inject a pass-through middleware instead ofrequireChittyAuth. Zero production code change for the auth bypass — production instantiation (export const assetRoutes) uses the realrequireChittyAuth. Data path is fully real.4. No Miniflare
Direct
app.request()on the Hono app with env stub. Avoids the Hyperdrive-in-local-dev gap. Real DB calls go to Neon.5. Test Branch Strategy
The Neon MCP
create_branchonly branches from the project default, which lacks the Phase 1.5 schema. Rather than re-applying the schema (drizzle-kit 0.18 lacks apushsubcommand on this repo), tests run directly against the existing Phase 1.5 preview branchbr-spring-star-aky6u1mcwith unique chitty_id-scoped fixtures and full afterAll cleanup. Future Phase 2b can adopt full per-run branches once drizzle-kit is upgraded.Validation Evidence
TypeCheck (
npm run check)Zero NEW errors in
worker/src/**and the new test file. All 85 remaining errors are pre-existing inclient/andserver/(Replit-era code untouched by this PR).Integration Tests
All 12 tests pass against Neon branch
br-spring-star-aky6u1mc:Sample SQL via Neon MCP
run_sql(on br-spring-star-aky6u1mc)The Drizzle-emitted shape for
GET /api/assetsownership filter:Result (after seeding one fixture row):
[ { "id": "731f3ea9-08fe-40de-a934-45e728391023", "user_id": "01-A-CHT-ASST-P-5A-1-X", "name": "PR Evidence Sample — Patek Philippe", "asset_type": "jewelry", "current_value": "42500.00", "verification_status": "verified" } ]Confirms the query parses, executes against Neon, and returns the expected row shape. Row was deleted after capture.
Wrangler Dry-Run
Hyperdrive binding
CHITTYASSETS_DBis wired and active in production env. No missing bindings.Canonical Compliance
@canon: chittycanon://core/services/chittyassetson all new filesENTITY_TYPES = ["P", "L", "T", "E", "A"]retained inenv.ts— all five P/L/T/E/A present (no omission of Authority)chitty_prefix workaroundStack
PR #36 (this) → #34 → #33 (base:
chore/schema-canonical-remediation)Test Plan
🤖 Generated with Claude Code