feat: add SQLite database and persistent task schema

This commit is contained in:
Mpho10111
2026-08-11 20:51:10 +02:00
parent 77fe7cfdac
commit 63833efb7a
4 changed files with 76 additions and 0 deletions

4
.gitignore vendored
View File

@@ -17,6 +17,10 @@
/.next/
/out/
# local database
/data/*.sqlite
/data/*.sqlite-*
# production
/build

17
db/schema.sql Normal file
View File

@@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL CHECK (length(trim(title)) > 0),
description TEXT NOT NULL DEFAULT '',
due_date TEXT NOT NULL CHECK (due_date GLOB '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'),
topic TEXT NOT NULL CHECK (length(trim(topic)) > 0),
status TEXT NOT NULL DEFAULT 'Todo' CHECK (status IN ('Todo', 'In-Progress', 'Complete')),
archived_at TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_tasks_active_status
ON tasks (archived_at, status, due_date);
CREATE INDEX IF NOT EXISTS idx_tasks_topic
ON tasks (topic COLLATE NOCASE);

View File

@@ -0,0 +1,36 @@
import fs from "node:fs";
import path from "node:path";
import { DatabaseSync } from "node:sqlite";
type DatabaseGlobal = typeof globalThis & {
plannerDatabase?: DatabaseSync;
plannerDatabasePath?: string;
};
const schemaPath = path.join(process.cwd(), "db", "schema.sql");
const defaultDatabasePath = path.join(process.cwd(), "data", "planner.sqlite");
export function getDatabase() {
const databasePath = process.env.TASKS_DB_PATH ?? defaultDatabasePath;
const globalDatabase = globalThis as DatabaseGlobal;
if (
globalDatabase.plannerDatabase &&
globalDatabase.plannerDatabasePath === databasePath
) {
return globalDatabase.plannerDatabase;
}
fs.mkdirSync(path.dirname(databasePath), { recursive: true });
const database = new DatabaseSync(databasePath);
const schema = fs.readFileSync(schemaPath, "utf8");
database.exec("PRAGMA foreign_keys = ON;");
database.exec(schema);
globalDatabase.plannerDatabase = database;
globalDatabase.plannerDatabasePath = databasePath;
return database;
}

19
types/node-sqlite.d.ts vendored Normal file
View File

@@ -0,0 +1,19 @@
declare module "node:sqlite" {
export type RunResult = {
changes: number;
lastInsertRowid: number | bigint;
};
export class StatementSync {
all(...params: unknown[]): unknown[];
get(...params: unknown[]): unknown;
run(...params: unknown[]): RunResult;
}
export class DatabaseSync {
constructor(location?: string | URL, options?: Record<string, unknown>);
close(): void;
exec(sql: string): void;
prepare(sql: string): StatementSync;
}
}