diff --git a/.env.example b/.env.example index ced41bb..f97cfd0 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,3 @@ -# Drizzle -DATABASE_URL=file:local.db - ORIGIN="" # Better Auth diff --git a/README.md b/README.md index 2580988..420c3bc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Clearity is a workspace, client, and contract management platform for managed office operations. It tracks clients, contacts, addresses, rooms, services, bookings, contracts, invoices, and workspace admins from a SvelteKit dashboard. -The app is built with SvelteKit, Svelte 5, shadcn-svelte, Superforms, Better Auth, Drizzle ORM, SQLite/libSQL for local development, Cloudflare D1 for production, and Cloudflare Workers. +The app is built with SvelteKit, Svelte 5, shadcn-svelte, Superforms, Better Auth, Drizzle ORM, Cloudflare D1, and Cloudflare Workers. ## Development @@ -15,7 +15,6 @@ pnpm install Create a local `.env` file: ```sh -DATABASE_URL=file:local.db ORIGIN=http://localhost:5173 BETTER_AUTH_SECRET=replace-with-a-long-random-secret ``` @@ -29,7 +28,7 @@ openssl rand -base64 32 Apply database migrations: ```sh -DATABASE_URL=file:local.db pnpm db:migrate +pnpm db:migrate:local ``` Start the development server: @@ -47,8 +46,8 @@ pnpm check # Wrangler types, SvelteKit sync, and svelte-check pnpm lint # Prettier check and ESLint pnpm format # Format the codebase pnpm db:generate # Generate Drizzle migrations after schema changes -pnpm db:migrate # Apply Drizzle migrations to DATABASE_URL -pnpm db:studio # Open Drizzle Studio +pnpm db:migrate:local # Apply migrations to the local D1 database +pnpm db:migrate:remote # Apply migrations to the remote D1 database pnpm gen # Regenerate Cloudflare Worker types ``` @@ -56,40 +55,27 @@ pnpm gen # Regenerate Cloudflare Worker types Required: -- `DATABASE_URL`: SQLite/libSQL connection URL for local development. Use `file:local.db`. - `ORIGIN`: Public app origin, for example `http://localhost:5173` locally or `https://clearity.example.com` in production. - `BETTER_AUTH_SECRET`: Secret used by Better Auth. +- Cloudflare D1 binding `DB`, configured in `wrangler.jsonc`. -Production also requires the Cloudflare D1 binding named `DB`, configured in `wrangler.jsonc`. Do not set `DATABASE_URL` in production unless you intentionally want to use a hosted libSQL database instead of D1. +Local development uses the same `DB` binding shape as production. SvelteKit's Cloudflare adapter populates `platform.env.DB` from `wrangler.jsonc` during dev and preview, and Wrangler stores local D1 data in its local state directory. ## Database The application schema lives in `src/lib/server/db`. Drizzle migration files live in `drizzle`. -Runtime database selection is: - -- If `DATABASE_URL` is set, Clearity uses the libSQL driver. -- If `DATABASE_URL` is not set and the Cloudflare `DB` binding exists, Clearity uses Drizzle's Cloudflare D1 driver. - -The fastest local database is a SQLite file: +Clearity always uses Drizzle's Cloudflare D1 driver through the `DB` binding. Apply migrations to the local D1 database before starting the app: ```sh -DATABASE_URL=file:local.db pnpm db:migrate +pnpm db:migrate:local ``` For schema changes: ```sh pnpm db:generate -DATABASE_URL=file:local.db pnpm db:migrate -``` - -To test against Wrangler's local D1 simulator instead, leave `DATABASE_URL` unset, apply migrations locally, and run the app through the Cloudflare Worker build: - -```sh -pnpm build -pnpm wrangler d1 migrations apply clearity-production --local -pnpm wrangler dev +pnpm db:migrate:local ``` ## Production Deployment @@ -143,8 +129,6 @@ pnpm wrangler secret put ORIGIN pnpm wrangler secret put BETTER_AUTH_SECRET ``` -Production uses the `DB` binding by default. `DATABASE_URL` is only needed as a production secret if you intentionally deploy against a hosted libSQL database instead of D1. - Build and deploy: ```sh diff --git a/drizzle.config.ts b/drizzle.config.ts index 317f310..55e8beb 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,11 +1,8 @@ import { defineConfig } from 'drizzle-kit'; -if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set'); - export default defineConfig({ schema: './src/lib/server/db/schema.ts', dialect: 'sqlite', - dbCredentials: { url: process.env.DATABASE_URL }, verbose: true, strict: true }); diff --git a/package.json b/package.json index abfa2ee..cd841e9 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,9 @@ "lint": "prettier --check . && eslint .", "format": "prettier --write .", "gen": "wrangler types", - "db:push": "drizzle-kit push", "db:generate": "drizzle-kit generate", - "db:migrate": "drizzle-kit migrate", + "db:migrate:local": "wrangler d1 migrations apply clearity-production --local", + "db:migrate:remote": "wrangler d1 migrations apply clearity-production --remote", "auth:schema": "better-auth generate --config src/lib/server/auth.ts --output src/lib/server/db/auth.schema.ts --yes" }, "devDependencies": { @@ -24,7 +24,6 @@ "@eslint/js": "latest", "@fontsource-variable/geist": "^5.2.9", "@internationalized/date": "^3.12.1", - "@libsql/client": "^0.17.2", "@lucide/svelte": "^1.16.0", "@sveltejs/adapter-cloudflare": "^7.2.8", "@sveltejs/kit": "^2.57.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9ff036..d6c20b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,9 +23,6 @@ importers: '@internationalized/date': specifier: ^3.12.1 version: 3.12.1 - '@libsql/client': - specifier: ^0.17.2 - version: 0.17.3 '@lucide/svelte': specifier: ^1.16.0 version: 1.16.0(svelte@5.55.9(@typescript-eslint/types@8.59.4)) @@ -4759,10 +4756,12 @@ snapshots: transitivePeerDependencies: - bufferutil - utf-8-validate + optional: true '@libsql/core@0.17.3': dependencies: js-base64: 3.7.8 + optional: true '@libsql/darwin-arm64@0.5.29': optional: true @@ -4777,6 +4776,7 @@ snapshots: transitivePeerDependencies: - bufferutil - utf-8-validate + optional: true '@libsql/isomorphic-ws@0.1.5': dependencies: @@ -4785,6 +4785,7 @@ snapshots: transitivePeerDependencies: - bufferutil - utf-8-validate + optional: true '@libsql/linux-arm-gnueabihf@0.5.29': optional: true @@ -4823,7 +4824,8 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@neon-rs/load@0.0.4': {} + '@neon-rs/load@0.0.4': + optional: true '@noble/ciphers@2.2.0': {} @@ -5072,6 +5074,7 @@ snapshots: '@types/ws@8.18.1': dependencies: '@types/node': 24.12.4 + optional: true '@typeschema/class-validator@0.3.0(@types/json-schema@7.0.15)(class-validator@0.14.4)': dependencies: @@ -5557,7 +5560,8 @@ snapshots: destr@2.0.5: {} - detect-libc@2.0.2: {} + detect-libc@2.0.2: + optional: true detect-libc@2.1.2: {} @@ -5976,7 +5980,8 @@ snapshots: jose@6.2.3: {} - js-base64@3.7.8: {} + js-base64@3.7.8: + optional: true js-tokens@4.0.0: {} @@ -6066,6 +6071,7 @@ snapshots: '@libsql/linux-x64-gnu': 0.5.29 '@libsql/linux-x64-musl': 0.5.29 '@libsql/win32-x64-msvc': 0.5.29 + optional: true lightningcss-android-arm64@1.32.0: optional: true @@ -6356,7 +6362,8 @@ snapshots: prettier@3.8.3: {} - promise-limit@2.7.0: {} + promise-limit@2.7.0: + optional: true prompts@2.4.2: dependencies: @@ -6878,7 +6885,8 @@ snapshots: ws@8.20.1: {} - ws@8.21.0: {} + ws@8.21.0: + optional: true wsl-utils@0.1.0: dependencies: diff --git a/src/lib/server/db/index.ts b/src/lib/server/db/index.ts index a20616e..91516b3 100644 --- a/src/lib/server/db/index.ts +++ b/src/lib/server/db/index.ts @@ -1,19 +1,12 @@ -import { env } from '$env/dynamic/private'; import { getRequestEvent } from '$app/server'; -import { createClient, type Client } from '@libsql/client'; import { drizzle as drizzleD1, type DrizzleD1Database } from 'drizzle-orm/d1'; -import { drizzle as drizzleLibSQL, type LibSQLDatabase } from 'drizzle-orm/libsql'; import * as schema from './schema'; type Schema = typeof schema; -type LocalDatabase = LibSQLDatabase & { $client: Client }; -type D1DatabaseClient = DrizzleD1Database & { $client: D1Database }; -export type Database = LocalDatabase; -export type RuntimeDatabase = LocalDatabase | D1DatabaseClient; +export type Database = DrizzleD1Database & { $client: D1Database }; -let localDb: LocalDatabase | undefined; -const d1Databases = new WeakMap(); +const d1Databases = new WeakMap(); function getD1Binding() { try { @@ -23,17 +16,6 @@ function getD1Binding() { } } -function getLocalDb() { - if (localDb) return localDb; - if (!env.DATABASE_URL) - throw new Error('DATABASE_URL is not set and no Cloudflare D1 DB binding is available'); - - const client = createClient({ url: env.DATABASE_URL }); - localDb = drizzleLibSQL(client, { schema }); - - return localDb; -} - function getD1Db(binding: D1Database) { const cached = d1Databases.get(binding); if (cached) return cached; @@ -44,13 +26,11 @@ function getD1Db(binding: D1Database) { return database; } -export function getDb(): RuntimeDatabase { - if (env.DATABASE_URL) return getLocalDb(); - +export function getDb(): Database { const d1Binding = getD1Binding(); if (d1Binding) return getD1Db(d1Binding); - return getLocalDb(); + throw new Error('Cloudflare D1 DB binding is not available'); } export const db = new Proxy({} as Database, { diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index eb711af..e8a4b20 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,9 +1,11 @@ /* eslint-disable */ -// Generated by Wrangler by running `wrangler types` (hash: f6e835394237f01fba038726e2d4f9ae) +// Generated by Wrangler by running `wrangler types` (hash: 36a638c2dc42c34e7d5ddc208fd03581) // Runtime types generated with workerd@1.20260521.1 2026-05-23 nodejs_als interface __BaseEnv_Env { DB: D1Database; ASSETS: Fetcher; + ORIGIN: string; + BETTER_AUTH_SECRET: string; } declare namespace Cloudflare { interface Env extends __BaseEnv_Env {} diff --git a/wrangler.jsonc b/wrangler.jsonc index 35b7afb..d4f82cc 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -16,6 +16,9 @@ "migrations_dir": "drizzle" } ], + "secrets": { + "required": ["ORIGIN", "BETTER_AUTH_SECRET"] + }, "workers_dev": true, "preview_urls": true }