feat(db): drizzle schema (settings, sessions) + boot migrations

This commit is contained in:
marcuspaico
2026-08-17 14:04:56 -07:00
parent caa9af2f40
commit 0f96d5e3b8
7 changed files with 148 additions and 0 deletions

7
server/drizzle.config.ts Normal file
View File

@@ -0,0 +1,7 @@
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "sqlite",
schema: "./src/db/schema.ts",
out: "./drizzle",
});

View File

@@ -0,0 +1,10 @@
CREATE TABLE `sessions` (
`id` text PRIMARY KEY NOT NULL,
`created_at` integer NOT NULL,
`expires_at` integer NOT NULL
);
--> statement-breakpoint
CREATE TABLE `settings` (
`key` text PRIMARY KEY NOT NULL,
`value` text NOT NULL
);

View File

@@ -0,0 +1,73 @@
{
"version": "6",
"dialect": "sqlite",
"id": "383d7bd7-3ebf-456e-8c5c-f39cebab14c4",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"sessions": {
"name": "sessions",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires_at": {
"name": "expires_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"settings": {
"name": "settings",
"columns": {
"key": {
"name": "key",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"value": {
"name": "value",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1787000672259,
"tag": "0000_init",
"breakpoints": true
}
]
}

17
server/src/db/index.ts Normal file
View File

@@ -0,0 +1,17 @@
import { Database } from "bun:sqlite";
import { drizzle } from "drizzle-orm/bun-sqlite";
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
import { mkdirSync } from "node:fs";
import { join } from "node:path";
import * as schema from "./schema";
export type Db = ReturnType<typeof openDb>;
export function openDb(dataDir: string) {
mkdirSync(dataDir, { recursive: true });
const sqlite = new Database(join(dataDir, "helios.db"), { create: true });
sqlite.exec("PRAGMA journal_mode = WAL");
const db = drizzle(sqlite, { schema });
migrate(db, { migrationsFolder: join(import.meta.dir, "../../drizzle") });
return db;
}

12
server/src/db/schema.ts Normal file
View File

@@ -0,0 +1,12 @@
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
export const settings = sqliteTable("settings", {
key: text("key").primaryKey(),
value: text("value").notNull(),
});
export const sessions = sqliteTable("sessions", {
id: text("id").primaryKey(),
createdAt: integer("created_at").notNull(),
expiresAt: integer("expires_at").notNull(),
});

16
server/test/db.test.ts Normal file
View File

@@ -0,0 +1,16 @@
import { describe, expect, test } from "bun:test";
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { openDb } from "../src/db";
import { settings } from "../src/db/schema";
describe("db", () => {
test("opens, migrates, round-trips a setting", async () => {
const dir = mkdtempSync(join(tmpdir(), "helios-"));
const db = openDb(dir);
await db.insert(settings).values({ key: "llm_base_url", value: "https://openrouter.ai/api/v1" });
const rows = await db.select().from(settings);
expect(rows).toEqual([{ key: "llm_base_url", value: "https://openrouter.ai/api/v1" }]);
});
});