import * as config from './config'; import * as sqlite from 'bun:sqlite'; import { EVENT, SESSION } from './enums'; export const sessions = {}; export const desks = {}; let db = null; const queries = {}; export function startup() { const path = `${config.DATADIR}/db.sqlite`; db = new sqlite.Database(path, { create: true }); db.query(`CREATE TABLE IF NOT EXISTS desks ( id INTEGER PRIMARY KEY, sn INTEGER, title TEXT );`).run(); db.query(`CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, login TEXT );`).run(); db.query(`CREATE TABLE IF NOT EXISTS sessions ( id INTEGER PRIMARY KEY, user_id INTEGER, desk_id INTEGER, lsn INTEGER, FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE NO ACTION, FOREIGN KEY (desk_id) REFERENCES desks (id) ON DELETE CASCADE ON UPDATE NO ACTION );`).run(); db.query(`CREATE TABLE IF NOT EXISTS images ( id INTEGER PRIMARY KEY, desk_id INTEGER, FOREIGN KEY (desk_id) REFERENCES desks (id) ON DELETE CASCADE ON UPDATE NO ACTION );`).run(); db.query(`CREATE TABLE IF NOT EXISTS strokes ( id INTEGER PRIMARY KEY, desk_id INTEGER, points BLOB, FOREIGN KEY (desk_id) REFERENCES desks (id) ON DELETE CASCADE ON UPDATE NO ACTION );`).run(); db.query(`CREATE TABLE IF NOT EXISTS events ( id INTEGER PRIMARY KEY, type INTEGER, desk_id INTEGER, user_id INTEGER, stroke_id INTEGER, image_id INTEGER, x INTEGER, y INTEGER, FOREIGN KEY (desk_id) REFERENCES desks (id) ON DELETE CASCADE ON UPDATE NO ACTION FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE NO ACTION FOREIGN KEY (stroke_id) REFERENCES strokes (id) ON DELETE CASCADE ON UPDATE NO ACTION FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE ON UPDATE NO ACTION );`).run(); db.query(`CREATE INDEX IF NOT EXISTS idx_events_desk_id ON events (desk_id); `).run(); db.query(`CREATE INDEX IF NOT EXISTS idx_strokes_desk_id ON strokes (desk_id); `).run(); const res1 = db.query('SELECT COUNT(id) as count FROM desks').get(); const res2 = db.query('SELECT COUNT(id) as count FROM events').get(); const res3 = db.query('SELECT COUNT(id) as count FROM strokes').get(); const res4 = db.query('SELECT COUNT(id) as count FROM users').get(); const res5 = db.query('SELECT COUNT(id) as count FROM sessions').get(); const res6 = db.query('SELECT COUNT(id) as count FROM images').get(); queries.desks = db.query('SELECT id, sn FROM desks'); queries.events = db.query('SELECT * FROM events'); queries.sessions = db.query('SELECT id, lsn, user_id, desk_id FROM sessions'); queries.strokes = db.query('SELECT id, points FROM strokes'); queries.empty_desk = db.query('INSERT INTO desks (id, title, sn) VALUES (?1, ?2, 0)'); queries.desk_strokes = db.query('SELECT id, points FROM strokes WHERE desk_id = ?1'); queries.put_desk_stroke = db.query('INSERT INTO strokes (id, desk_id, points) VALUES (?1, ?2, ?3)'); queries.clear_desk_events = db.query('DELETE FROM events WHERE desk_id = ?1'); queries.set_desk_sn = db.query('UPDATE desks SET sn = ?1 WHERE id = ?2'); queries.save_session_lsn = db.query('UPDATE sessions SET lsn = ?1 WHERE id = ?2'); queries.create_session = db.query('INSERT INTO sessions (id, lsn, user_id, desk_id) VALUES (?1, 0, ?2, ?3)'); queries.create_user = db.query('INSERT INTO users (id, login) VALUES (?1, ?2)'); queries.put_event = db.query('INSERT INTO events (type, desk_id, user_id, stroke_id, image_id, x, y) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)'); queries.put_image = db.query('INSERT INTO images (id, desk_id) VALUES (?1, ?2)'); console.log(`Storing data in ${path}`); console.log(`Entity count at startup: ${res1.count} desks ${res2.count} events ${res3.count} strokes ${res4.count} users ${res5.count} sessions ${res6.count} images` ); const stored_desks = get_desks(); const stored_events = get_events(); const stored_strokes = get_strokes(); const stored_sessions = get_sessions(); const stroke_dict = {}; for (const stroke of stored_strokes) { stroke.points = new Uint16Array(stroke.points.buffer); stroke_dict[stroke.id] = stroke; } for (const desk of stored_desks) { desks[desk.id] = desk; desks[desk.id].events = []; } for (const event of stored_events) { if (event.type === EVENT.STROKE) { event.points = stroke_dict[event.stroke_id].points; } desks[event.desk_id].events.push(event); } for (const session of stored_sessions) { session.state = SESSION.CLOSED; session.ws = null; sessions[session.id] = session; } } export function get_strokes() { return queries.strokes.all(); } export function get_sessions() { return queries.sessions.all(); } export function get_desks() { return queries.desks.all(); } export function get_events() { return queries.events.all(); } export function get_desk_strokes(desk_id) { return queries.desk_strokes.all(desk_id); } export function put_event(event) { return queries.put_event.get(event.type, event.desk_id || 0, event.user_id || 0, event.stroke_id || 0, event.image_id || 0, event.x || 0, event.y || 0); } export function put_stroke(stroke_id, desk_id, points) { return queries.put_desk_stroke.get(stroke_id, desk_id, new Uint8Array(points.buffer, points.byteOffset, points.byteLength)); } export function clear_events(desk_id) { return queries.clear_desk_events.get(desk_id); } export function create_desk(desk_id, title = 'untitled') { return queries.empty_desk.get(desk_id, title); } export function save_desk_sn(desk_id, sn) { return queries.set_desk_sn.get(sn, desk_id); } export function create_session(session) { return queries.create_session.get(session.id, session.user_id, session.desk_id); } export function create_user(user) { return queries.create_user.get(user.id, user.login); } export function save_session_lsn(session_id, lsn) { return queries.save_session_lsn.get(lsn, session_id); } export function put_image(image_id, desk_id) { return queries.put_image.get(image_id, desk_id); }