Files
cats/server/src/db.ts

118 lines
3.2 KiB
TypeScript

import initSqlJs, { Database as SqlJsDatabase } from 'sql.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import bcrypt from 'bcryptjs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const dbPath = path.join(__dirname, '..', 'data.db');
let db: SqlJsDatabase;
export async function initDb(): Promise<SqlJsDatabase> {
const SQL = await initSqlJs();
if (fs.existsSync(dbPath)) {
const buffer = fs.readFileSync(dbPath);
db = new SQL.Database(buffer);
} else {
db = new SQL.Database();
}
db.run('PRAGMA foreign_keys = ON');
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
points INTEGER DEFAULT 0,
is_admin INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
)
`);
// Add is_admin column if missing (existing DB migration)
try { db.run('ALTER TABLE users ADD COLUMN is_admin INTEGER DEFAULT 0'); } catch {}
// Auto-create admin if not exists
const admin = queryOne('SELECT id FROM users WHERE username = ?', ['admin']);
if (!admin) {
const hash = bcrypt.hashSync('admin123', 10);
execute('INSERT INTO users (username, password_hash, is_admin) VALUES (?, ?, 1)', ['admin', hash]);
console.log('Admin user created: admin / admin123');
}
db.run(`
CREATE TABLE IF NOT EXISTS cats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id),
image_url TEXT NOT NULL,
caption TEXT DEFAULT '',
created_at TEXT DEFAULT (datetime('now'))
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS likes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
cat_id INTEGER NOT NULL REFERENCES cats(id),
user_id INTEGER NOT NULL REFERENCES users(id),
UNIQUE(cat_id, user_id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
cat_id INTEGER NOT NULL REFERENCES cats(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id),
text TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
)
`);
saveDb();
return db;
}
export function saveDb(): void {
const data = db.export();
const buffer = Buffer.from(data);
fs.writeFileSync(dbPath, buffer);
}
export function getDb(): SqlJsDatabase {
return db;
}
export function queryOne(sql: string, params: any[] = []): any {
const stmt = db.prepare(sql);
stmt.bind(params);
const result = stmt.step() ? stmt.getAsObject() : null;
stmt.free();
return result;
}
export function queryAll(sql: string, params: any[] = []): any[] {
const stmt = db.prepare(sql);
stmt.bind(params);
const results: any[] = [];
while (stmt.step()) {
results.push(stmt.getAsObject());
}
stmt.free();
return results;
}
export function execute(sql: string, params: any[] = []): { changes: number; lastInsertRowid: number } {
const stmt = db.prepare(sql);
stmt.bind(params);
stmt.step();
const lastId = db.exec('SELECT last_insert_rowid() AS id')[0]?.values[0][0] || 0;
const changes = db.getRowsModified();
stmt.free();
saveDb();
return { changes, lastInsertRowid: Number(lastId) };
}