diff --git a/.gitignore b/.gitignore index b9f8d23..315d974 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,11 @@ yarn-error.log* # local env files .env*.local +# SQLite database files +/data/*.db +/data/*.db-shm +/data/*.db-wal + # vercel .vercel diff --git a/drizzle.app.config.ts b/drizzle.app.config.ts new file mode 100644 index 0000000..3e3ede5 --- /dev/null +++ b/drizzle.app.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + dialect: "sqlite", + schema: "./src/db/schema/index.ts", + out: "./src/db/migrations", + dbCredentials: { + url: process.env.APP_DB_PATH + ? `file:${process.env.APP_DB_PATH}` + : "file:./data/app.db", + }, +}); diff --git a/instrumentation.ts b/instrumentation.ts new file mode 100644 index 0000000..e8982e3 --- /dev/null +++ b/instrumentation.ts @@ -0,0 +1,7 @@ +export async function register() { + // Only run on the Node.js server, not in the Edge runtime or client + if (process.env.NEXT_RUNTIME === "nodejs") { + const { runMigrations } = await import("./src/db"); + await runMigrations(); + } +} diff --git a/package.json b/package.json index 75dd287..9301dc6 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,11 @@ "dev": "next dev", "build": "next build", "start": "next start -p ${PORT:=3000} -H ${HOST:=0.0.0.0}", - "lint": "next lint" + "lint": "next lint", + "db:generate": "drizzle-kit generate --config=drizzle.app.config.ts", + "db:migrate": "drizzle-kit migrate --config=drizzle.app.config.ts", + "db:studio": "drizzle-kit studio --config=drizzle.app.config.ts", + "db:push": "drizzle-kit push --config=drizzle.app.config.ts" }, "author": { "name": "Varun Raj Manoharan", diff --git a/src/db/index.ts b/src/db/index.ts new file mode 100644 index 0000000..ea91391 --- /dev/null +++ b/src/db/index.ts @@ -0,0 +1,20 @@ +import { createClient } from "@libsql/client"; +import { drizzle } from "drizzle-orm/libsql"; +import { migrate } from "drizzle-orm/libsql/migrator"; +import path from "path"; +import * as schema from "./schema"; + +const DB_PATH = + process.env.APP_DB_PATH || path.join(process.cwd(), "data", "app.db"); + +const client = createClient({ url: `file:${DB_PATH}` }); + +export const appDb = drizzle(client, { schema }); + +// Run pending migrations automatically on startup. +// migrate() is idempotent — safe to call on every boot. +export async function runMigrations() { + await migrate(appDb, { + migrationsFolder: path.join(process.cwd(), "src", "db", "migrations"), + }); +} diff --git a/src/db/migrations/meta/_journal.json b/src/db/migrations/meta/_journal.json new file mode 100644 index 0000000..7e7f287 --- /dev/null +++ b/src/db/migrations/meta/_journal.json @@ -0,0 +1,5 @@ +{ + "version": "5", + "dialect": "sqlite", + "entries": [] +} diff --git a/src/db/schema/index.ts b/src/db/schema/index.ts new file mode 100644 index 0000000..a58fe37 --- /dev/null +++ b/src/db/schema/index.ts @@ -0,0 +1 @@ +export * from "./settings.schema"; diff --git a/src/db/schema/settings.schema.ts b/src/db/schema/settings.schema.ts new file mode 100644 index 0000000..463a2ee --- /dev/null +++ b/src/db/schema/settings.schema.ts @@ -0,0 +1,9 @@ +import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; + +export const settings = sqliteTable("settings", { + key: text("key").primaryKey(), + value: text("value").notNull(), + updatedAt: integer("updated_at", { mode: "timestamp" }).$onUpdate( + () => new Date() + ), +});