Repository navigation
Commit 50f8534
feat: local database access via void db --remote (#170)
* feat(api): add project_database_credentials table and helpers
Adds the project_database_credentials table (one row per project, PK on
project_id, ON DELETE CASCADE) to store encrypted database connection
strings. Includes the drizzle migration (0019), an updated snapshot, and
three db.ts helpers: upsertProjectDatabaseUrl, getProjectDatabaseUrl, and
deleteProjectDatabaseUrl.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(api): align project_database_credentials helpers with peer pattern
- `upsertProjectDatabaseUrl`: add `.returning()` and return
`Promise<ProjectDatabaseCredentials>` instead of `Promise<void>`,
matching `upsertProjectSecret`.
- `getProjectDatabaseUrl`: narrow return type from an inline object
literal to `Promise<ProjectDatabaseCredentials | null>`, matching
`getProjectSecret`.
- Import `ProjectDatabaseCredentials` from `./types.ts`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(api): encrypt+persist database URL at set-url time, expose GET endpoint
- POST /:id/database and PUT /:id/database-url now encrypt the connection
string with AES-GCM (using encryptProjectSecret) and upsert it into
project_database_credentials before calling Cloudflare Hyperdrive, so
the URL is never unrecoverable if Hyperdrive succeeds but DB write fails.
- New GET /:id/database-url decrypts and returns { url } to the project
owner; returns 404 with database_url_not_stored error when no row exists.
- Integration tests cover: round-trip via POST and PUT, last-write-wins
upsert, ownership 403, and structured 404.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(api): return 403 (not 404) on database-url ownership mismatch
Split the combined `!project || wrong-owner` guard in GET /:id/database-url
into two separate checks — 404 when the project does not exist, 403 when it
exists but belongs to a different user. Matches the pattern used by all other
ownership-gated endpoints in projects.ts (e.g. /resources, /logs).
Update the corresponding test assertion from 404 to 403.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump framework (getDatabaseUrl + db --remote args)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump framework (db execute --remote)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump framework (db execute --remote test coverage)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump framework (db execute --remote ordering fix)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump framework (db studio --remote + docs)
Points framework submodule at de247c3 which adds --remote support to
`void db studio` (PG only), updates help.ts and db-args.ts descriptions,
adds full docs in reference/cli.md, and includes 9 new unit tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump framework (test type fixes)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump framework (db --remote polish)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(api): persist database URL only after Cloudflare accepts it
CF is the source of truth for production routing; the stored credential
is the URL CF accepted and is now serving. Previously both POST
/:id/database and PUT /:id/database-url wrote to project_database_credentials
before calling Cloudflare, so a CF rejection would leave a stale row
that GET /:id/database-url (used by the CLI for remote access) would
serve — diverging the CLI from what production Hyperdrive actually routes.
The fix calls CF first and only persists on success. If the upsert fails
after CF succeeds (rare), a 500 is returned instructing the user to
re-run `void db set-url`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump framework (remote-db safety hardening)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(api): update orphaned Hyperdrive before persisting URL in isAlreadyExistsError paths
In both POST /:id/database and PUT /:id/database-url, the isAlreadyExistsError
recovery branch retrieved an existing Hyperdrive ID (via getHyperdriveByName) but
never pushed the submitted connectionString to CF before storing it locally. This
could cause GET /:id/database-url to return a URL that diverged from what CF was
actually routing.
Fix: call updateHyperdrive with the found ID and submitted connectionString before
falling through to the persist block, ensuring CF and the stored credential stay in
sync. Also add updateHyperdrive to the POST handler's dynamic import.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* style(api): apply vp fmt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: bump framework to main (db --remote merged via #213)
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>1 parent 002e147 commit 50f8534
9 files changed
Lines changed: 1845 additions & 3 deletions
File tree
- packages/api
- drizzle
- meta
- src
- routes
- test/integration
- docs/guide/env-vars.md+4-1
- docs/integrations/cloudflare.md+3-2
- docs/reference/api.md+42-30
- docs/reference/cli.md+46-28
- docs/reference/config.md+1-1
- docs/reference/resource-inference.md+11-11
- packages/create-void/package.json+1-1
- packages/md/package.json+1-1
- packages/react/package.json+1-1
- packages/solid/package.json+1-1
- packages/svelte/package.json+1-1
- packages/void/package.json+21-1
- packages/void/src/cli/client.ts+14
- packages/void/src/cli/db-args.ts+21-6
- packages/void/src/cli/db.ts+254-7
- packages/void/src/cli/deploy-setup.ts+3-1
- packages/void/src/cli/deploy.ts+1
- packages/void/src/cli/help.ts+38-5
- packages/void/src/cli/init.ts+20
- packages/void/src/cli/link.ts+8-5
- packages/void/src/cli/preset.ts+10-24
- packages/void/src/cli/prompt.ts+13-2
- packages/void/src/cli/wrapper.ts+32-20
- packages/void/src/index.ts+23-15
- packages/void/src/inference/walk.ts+9-2
- packages/void/src/migrations/runner.ts+23-4
- packages/void/src/pages/dev-css-assets.ts+9-10
- packages/void/src/plugin-binding-runtime.ts+69
- packages/void/src/plugin-drizzle.ts+3-1
- packages/void/src/plugin-env-schema.ts+46-1
- packages/void/src/plugin-routing.ts+43-15
- packages/void/src/router/compile.ts+36-14
- packages/void/src/runtime/client-react.ts+4
- packages/void/src/runtime/client-solid.ts+4
- packages/void/src/runtime/client-svelte.ts+4
- packages/void/src/runtime/client-vue.ts+4
- packages/void/src/runtime/remote/index.ts+10-4
- packages/void/src/shared/package-json.ts+73
- packages/void/test/integration/cli-help.test.ts+1-1
- packages/void/test/integration/plugin-env-schema.test.ts+70
- packages/void/test/unit/auth-client-frameworks.test.ts+67
- packages/void/test/unit/client-subpath-exports.test.ts+32
- packages/void/test/unit/compile-auth.test.ts+21-2
- packages/void/test/unit/compile.test.ts+18
- packages/void/test/unit/db-args.test.ts+28-1
- packages/void/test/unit/db-execute-remote.test.ts+693
- packages/void/test/unit/db-studio-remote.test.ts+305
- packages/void/test/unit/deploy-project-setup.test.ts+8-1
- packages/void/test/unit/get-database-url.test.ts+88
- packages/void/test/unit/inference.test.ts+18
- packages/void/test/unit/init.test.ts+121-1
- packages/void/test/unit/link.test.ts+120
- packages/void/test/unit/pages-dev-css-assets.test.ts+93-1
- packages/void/test/unit/plugin-binding-runtime.test.ts+46
- packages/void/test/unit/plugin-dev-asset-routing.test.ts+65-3
- packages/void/test/unit/plugin-drizzle.test.ts+24
- packages/void/test/unit/plugin-inference.test.ts+16
- packages/void/test/unit/plugin-routing.test.ts+35
- packages/void/test/unit/remote/env.test.ts+19
- packages/void/test/unit/runner-sqlite.test.ts+56
- packages/void/test/unit/wrapper.test.ts+28
- packages/void/vite.config.ts+4
- packages/vue/package.json+1-1
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
0 commit comments