Initial implementation of Bluetooth toy control app
Next.js app with a browser-side buttplug/buttplug-wasm control layer (server never touches real-time device commands), SQLite storage via Drizzle, single-secret auth, recordings/replay with device remapping, a usage stats dashboard, Docker deployment, and Gitea CI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W8WkFF5ppURBAB918593Eb
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import Database from "better-sqlite3";
|
||||
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { dirname } from "node:path";
|
||||
import { getEnv } from "@/lib/env";
|
||||
import * as schema from "./schema";
|
||||
|
||||
const globalForDb = globalThis as unknown as { __bpSqlite?: Database.Database };
|
||||
|
||||
function openDatabase(): Database.Database {
|
||||
if (globalForDb.__bpSqlite) return globalForDb.__bpSqlite;
|
||||
|
||||
const path = getEnv().DATABASE_PATH;
|
||||
mkdirSync(dirname(path), { recursive: true });
|
||||
|
||||
const instance = new Database(path);
|
||||
instance.pragma("journal_mode = WAL");
|
||||
instance.pragma("foreign_keys = ON");
|
||||
|
||||
// Reused across hot-reloads in dev so we don't leak file handles / open a
|
||||
// fresh WAL-mode connection on every request.
|
||||
globalForDb.__bpSqlite = instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Lazy by design: `next build` imports this module's whole graph while
|
||||
// collecting route metadata, even for force-dynamic pages that are never
|
||||
// actually rendered at build time. Opening the DB (and requiring env vars)
|
||||
// as a module-level side effect would make `pnpm build` fail in CI, where
|
||||
// no secrets are configured. The proxy defers both until first real query.
|
||||
export const sqlite = new Proxy({} as Database.Database, {
|
||||
get(_target, prop) {
|
||||
// drizzle-orm's `isConfig()` probes `.constructor.name` on its first
|
||||
// argument to tell a client instance from a plain config object - that
|
||||
// introspection must not itself trigger opening the real connection.
|
||||
if (prop === "constructor") return Database;
|
||||
const instance = openDatabase();
|
||||
const value = Reflect.get(instance, prop, instance);
|
||||
return typeof value === "function" ? value.bind(instance) : value;
|
||||
},
|
||||
}) as Database.Database;
|
||||
|
||||
export const db = drizzle(sqlite, { schema });
|
||||
Reference in New Issue
Block a user