Files
memer/backend/src/db.ts

86 lines
2.9 KiB
TypeScript
Raw Normal View History

2026-03-28 01:06:30 -05:00
import Database from 'better-sqlite3';
import path from 'path';
import fs from 'fs';
const DATA_DIR = process.env.DATA_DIR ?? '/data';
const DB_DIR = path.join(DATA_DIR, 'db');
const DB_PATH = path.join(DB_DIR, 'memer.db');
fs.mkdirSync(DB_DIR, { recursive: true });
const db = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');
2026-03-28 01:34:27 -05:00
// Core tables
2026-03-28 01:06:30 -05:00
db.exec(`
2026-03-28 01:34:27 -05:00
CREATE TABLE IF NOT EXISTS collections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
is_default INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
2026-03-28 01:06:30 -05:00
CREATE TABLE IF NOT EXISTS memes (
2026-03-28 01:34:27 -05:00
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
description TEXT,
file_path TEXT NOT NULL,
file_name TEXT NOT NULL,
file_size INTEGER NOT NULL,
mime_type TEXT NOT NULL,
width INTEGER NOT NULL,
height INTEGER NOT NULL,
parent_id TEXT REFERENCES memes(id) ON DELETE CASCADE,
collection_id INTEGER REFERENCES collections(id) ON DELETE SET NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
2026-03-28 01:06:30 -05:00
);
CREATE TABLE IF NOT EXISTS tags (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL COLLATE NOCASE
);
CREATE TABLE IF NOT EXISTS meme_tags (
meme_id TEXT NOT NULL REFERENCES memes(id) ON DELETE CASCADE,
tag_id INTEGER NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (meme_id, tag_id)
);
CREATE INDEX IF NOT EXISTS idx_memes_parent_id ON memes(parent_id);
CREATE INDEX IF NOT EXISTS idx_memes_created_at ON memes(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_meme_tags_meme_id ON meme_tags(meme_id);
CREATE INDEX IF NOT EXISTS idx_meme_tags_tag_id ON meme_tags(tag_id);
`);
2026-03-28 01:34:27 -05:00
// Migration: add collection_id column if upgrading from earlier schema
2026-03-28 01:37:41 -05:00
// Must run BEFORE creating the index on that column
2026-03-28 01:34:27 -05:00
const memesCols = db.prepare('PRAGMA table_info(memes)').all() as { name: string }[];
if (!memesCols.find((c) => c.name === 'collection_id')) {
db.exec('ALTER TABLE memes ADD COLUMN collection_id INTEGER REFERENCES collections(id) ON DELETE SET NULL');
}
2026-03-28 01:37:41 -05:00
// Create index after the column is guaranteed to exist (handles both fresh and migrated DBs)
db.exec('CREATE INDEX IF NOT EXISTS idx_memes_collection_id ON memes(collection_id)');
2026-03-28 01:34:27 -05:00
// Seed the default UNSORTED collection
const defaultCollection = db
.prepare('SELECT id FROM collections WHERE is_default = 1')
.get() as { id: number } | undefined;
if (!defaultCollection) {
db.prepare("INSERT INTO collections (name, is_default) VALUES ('Unsorted', 1)").run();
}
const unsorted = db
.prepare('SELECT id FROM collections WHERE is_default = 1')
.get() as { id: number };
// Assign any existing memes with no collection to UNSORTED
db.prepare('UPDATE memes SET collection_id = ? WHERE collection_id IS NULL').run(unsorted.id);
export const UNSORTED_ID = unsorted.id;
2026-03-28 01:06:30 -05:00
export default db;