118 lines
3.2 KiB
TypeScript
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) };
|
|
}
|