Browse Source

Remove users from backend, leave only sessions. Simplify storage

infinite
A.Olokhtonov 2 years ago
parent
commit
dec07b4edc
  1. 3
      server/config.js
  2. 5
      server/enums.js
  3. 8
      server/http.js
  4. 119
      server/recv.js
  5. 41
      server/send.js
  6. 10
      server/server.js
  7. 156
      server/storage.js

3
server/config.js

@ -2,4 +2,5 @@ export const HOST = '127.0.0.1'; @@ -2,4 +2,5 @@ export const HOST = '127.0.0.1';
export const PORT = 3003;
export const DATADIR = 'data';
export const SYNC_TIMEOUT = 1000;
export const IMAGEDIR = 'images';
export const IMAGEDIR = 'images';
export const DEBUG_PRINT = true;

5
server/enums.js

@ -23,9 +23,4 @@ export const MESSAGE = Object.freeze({ @@ -23,9 +23,4 @@ export const MESSAGE = Object.freeze({
FULL: 103,
FIRE: 104,
JOIN: 105,
});
export const SNS = Object.freeze({
DESK: 1,
SESSION: 2,
});

8
server/http.js

@ -7,13 +7,11 @@ export async function route(req) { @@ -7,13 +7,11 @@ export async function route(req) {
if (url.pathname === '/api/image') {
const desk_id = url.searchParams.get('deskId') || '0';
const formData = await req.formData();
const file = formData.get('file');
const formdata = await req.formData();
const file = formdata.get('file');
const image_id = math.fast_random32();
await Bun.write(config.IMAGEDIR + '/' + image_id, file);
storage.put_image(image_id, desk_id);
Bun.write(config.IMAGEDIR + '/' + image_id, file);
return new Response(image_id);
} else if (url.pathname === '/api/ping') {

119
server/recv.js

@ -2,9 +2,10 @@ import * as des from './deserializer'; @@ -2,9 +2,10 @@ import * as des from './deserializer';
import * as send from './send';
import * as math from './math';
import * as storage from './storage';
import * as config from './config';
import { SESSION, MESSAGE, EVENT } from './enums';
import { sessions, desks } from './storage';
import { sessions, desks, queries } from './storage';
// Session ACKed events up to SN
function recv_ack(d, session) {
@ -13,45 +14,20 @@ function recv_ack(d, session) { @@ -13,45 +14,20 @@ function recv_ack(d, session) {
session.state = SESSION.READY;
session.sn = sn;
console.log(`ack ${sn} in`);
}
function handle_event(session, event) {
switch (event.type) {
case EVENT.STROKE: {
event.stroke_id = math.fast_random32();
storage.put_stroke(event.stroke_id, session.desk_id, event.points, event.width, event.color);
storage.put_event(event);
break;
}
case EVENT.ERASER:
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE:
case EVENT.UNDO: {
storage.put_event(event);
break;
}
default: {
console.error('fuck');
console.trace();
process.exit(1);
}
}
if (config.DEBUG_PRINT) console.log(`ack ${sn} in`);
}
async function recv_syn(d, session) {
const lsn = des.u32(d);
const count = des.u32(d);
console.log(`syn ${lsn} in, total size = ${d.size}`);
if (config.DEBUG_PRINT) console.log(`syn ${lsn} in, total size = ${d.size}`);
const we_expect = lsn - session.lsn;
const first = count - we_expect;
const events = [];
console.log(`we expect ${we_expect}, count ${count}`);
if (config.DEBUG_PRINT) console.log(`we expect ${we_expect}, count ${count}`);
for (let i = 0; i < count; ++i) {
const event = des.event(d);
@ -67,8 +43,15 @@ async function recv_syn(d, session) { @@ -67,8 +43,15 @@ async function recv_syn(d, session) {
desks[session.desk_id].events.push(...events);
session.lsn = lsn;
storage.save_desk_sn(session.desk_id, desks[session.desk_id].sn);
storage.save_session_lsn(session.id, lsn);
storage.queries.update_desk_sn.run({
'$id': session.desk_id,
'$sn': desks[session.desk_id].sn
});
storage.queries.update_session_lsn.run({
'$id': session.id,
'$lsn': lsn
});
send.send_ack(session.ws, lsn);
send.sync_desk(session.desk_id);
@ -79,6 +62,30 @@ function recv_fire(d, session) { @@ -79,6 +62,30 @@ function recv_fire(d, session) {
event.user_id = session.user_id;
switch (event.type) {
case EVENT.SET_COLOR: {
session.color = event.color;
storage.queries.update_session_color.run({
'$id': session.id,
'$color': event.color
});
break;
}
case EVENT.SET_WIDTH: {
session.width = event.width;
storage.queries.update_session_width.run({
'$id': session.id,
'width': event.width
});
break;
}
}
for (const sid in sessions) {
const other = sessions[sid];
@ -98,6 +105,56 @@ function recv_fire(d, session) { @@ -98,6 +105,56 @@ function recv_fire(d, session) {
}
}
function handle_event(session, event) {
switch (event.type) {
case EVENT.STROKE: {
event.stroke_id = math.fast_random32();
storage.queries.insert_stroke.run({
'$id': event.stroke_id,
'$width': event.width,
'$color': event.color,
'$points': event.points
});
storage.queries.insert_event.run({
'$type': event.type,
'$desk_id': session.desk_id,
'$session_id': session.id,
'$stroke_id': event.stroke_id,
'$image_id': 0,
'$x': 0,
'$y': 0,
});
break;
}
case EVENT.ERASER:
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE:
case EVENT.UNDO: {
storage.queries.insert_event.run({
'$type': event.type,
'$desk_id': session.desk_id,
'$session_id': session.id,
'$stroke_id': event.stroke_id || 0,
'$image_id': event.image_id || 0,
'$x': event.x || 0,
'$y': event.y || 0,
});
break;
}
default: {
console.error('fuck');
console.trace();
process.exit(1);
}
}
}
export async function handle_message(ws, d) {
if (!(ws.data.session_id in sessions)) {
return;

41
server/send.js

@ -71,10 +71,14 @@ function create_session(ws, desk_id) { @@ -71,10 +71,14 @@ function create_session(ws, desk_id) {
sn: 0,
lsn: 0,
ws: ws,
color: 0x00,
width: 8,
};
storage.create_user(user);
storage.create_session(session);
storage.queries.insert_session.run({
'$id': session.id,
'$desk_id': desk_id
});
sessions[session.id] = session;
@ -91,7 +95,7 @@ export async function send_init(ws) { @@ -91,7 +95,7 @@ export async function send_init(ws) {
const desk = desks[desk_id];
let opcode = MESSAGE.INIT;
let size = 1 + 4 + 4 + 4 + 4; // opcode + user_id + lsn + event count + stroke count
let size = 1 + 4 + 4 + 4 + 4 + 4; // opcode + user_id + lsn + event count + stroke count + user count
let session = null;
if (session_id in sessions && sessions[session_id].desk_id == desk_id) {
@ -107,8 +111,20 @@ export async function send_init(ws) { @@ -107,8 +111,20 @@ export async function send_init(ws) {
session.ws = ws;
session.sn = 0; // Always re-send everything on reconnect
session.state = SESSION.OPENED;
session.color = 0x00;
session.width = 8;
console.log(`session ${session.id} opened`);
if (config.DEBUG_PRINT) console.log(`session ${session.id} opened`);
let user_count = 0;
for (const sid in sessions) {
const other_session = sessions[sid];
if (other_session.id !== session_id && other_session.desk_id === desk_id) {
++user_count;
size += 4 + 4 + 2; // user id + color + width
}
}
for (const event of desk.events) {
size += event_size(event);
@ -125,6 +141,17 @@ export async function send_init(ws) { @@ -125,6 +141,17 @@ export async function send_init(ws) {
}
ser.u32(s, desk.events.length);
ser.u32(s, user_count);
for (const sid in sessions) {
const other_session = sessions[sid];
if (other_session.id !== session_id && other_session.desk_id === desk_id) {
// console.log(other_session.id, other_session.color, other_session.width);
ser.u32(s, other_session.id);
ser.u32(s, other_session.color);
ser.u16(s, other_session.width);
}
}
for (const event of desk.events) {
ser.event(s, event);
@ -144,7 +171,7 @@ export function send_ack(ws, lsn) { @@ -144,7 +171,7 @@ export function send_ack(ws, lsn) {
ser.u8(s, MESSAGE.ACK);
ser.u32(s, lsn);
console.log(`ack ${lsn} out`);
if (config.DEBUG_PRINT) console.log(`ack ${lsn} out`);
ws.send(s.buffer);
}
@ -182,7 +209,7 @@ async function sync_session(session_id) { @@ -182,7 +209,7 @@ async function sync_session(session_id) {
let count = desk.sn - session.sn;
if (count === 0) {
console.log('client ACKed all events');
if (config.DEBUG_PRINT) console.log('client ACKed all events');
return;
}
@ -202,7 +229,7 @@ async function sync_session(session_id) { @@ -202,7 +229,7 @@ async function sync_session(session_id) {
ser.event(s, event);
}
console.debug(`syn ${desk.sn} out`);
if (config.DEBUG_PRINT) console.log(`syn ${desk.sn} out`);
await session.ws.send(s.buffer);

10
server/server.js

@ -1,13 +1,11 @@ @@ -1,13 +1,11 @@
import * as config from './config';
import * as storage from './storage';
import * as http_server from './http';
import * as math from './math';
import * as ser from './serializer';
import * as des from './deserializer';
import * as send from './send';
import * as recv from './recv';
import { MESSAGE, EVENT, SESSION, SNS } from './enums';
import { MESSAGE, EVENT, SESSION } from './enums';
import { sessions, desks } from './storage';
export function startup() {
@ -29,7 +27,11 @@ export function startup() { @@ -29,7 +27,11 @@ export function startup() {
events: [],
};
storage.create_desk(desk_id);
storage.queries.insert_desk.run({
'$id': desk_id,
'$title': `Desk ${desk_id}`
});
desks[desk_id] = desk;
}

156
server/storage.js

@ -3,11 +3,12 @@ import * as sqlite from 'bun:sqlite'; @@ -3,11 +3,12 @@ import * as sqlite from 'bun:sqlite';
import { EVENT, SESSION } from './enums';
// In-memory views
export const sessions = {};
export const desks = {};
export const queries = {};
let db = null;
const queries = {};
export function startup() {
const path = `${config.DATADIR}/db.sqlite`;
@ -20,31 +21,12 @@ export function startup() { @@ -20,31 +21,12 @@ export function startup() {
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,
color INTEGER,
width INTEGER,
FOREIGN KEY (desk_id)
REFERENCES desks (id)
@ -54,22 +36,16 @@ export function startup() { @@ -54,22 +36,16 @@ export function startup() {
db.query(`CREATE TABLE IF NOT EXISTS strokes (
id INTEGER PRIMARY KEY,
desk_id INTEGER,
points BLOB,
width INTEGER,
color INTEGER,
FOREIGN KEY (desk_id)
REFERENCES desks (id)
ON DELETE CASCADE
ON UPDATE NO ACTION
points BLOB
);`).run();
db.query(`CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY,
type INTEGER,
desk_id INTEGER,
user_id INTEGER,
session_id INTEGER,
stroke_id INTEGER,
image_id INTEGER,
x INTEGER,
@ -80,71 +56,52 @@ export function startup() { @@ -80,71 +56,52 @@ export function startup() {
ON DELETE CASCADE
ON UPDATE NO ACTION
FOREIGN KEY (user_id)
REFERENCES users (id)
ON DELETE CASCADE
FOREIGN KEY (session_id)
REFERENCES sessions (id)
ON DELETE NO ACTION
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();
// INSERT
queries.insert_desk = db.query('INSERT INTO desks (id, title, sn) VALUES ($id, $title, 0)');
queries.insert_stroke = db.query('INSERT INTO strokes (id, width, color, points) VALUES ($id, $width, $color, $points)');
queries.insert_session = db.query('INSERT INTO sessions (id, desk_id, lsn) VALUES ($id, $desk_id, 0)');
queries.insert_event = db.query('INSERT INTO events (type, desk_id, session_id, stroke_id, image_id, x, y) VALUES ($type, $desk_id, $session_id, $stroke_id, $image_id, $x, $y)');
db.query(`CREATE INDEX IF NOT EXISTS idx_strokes_desk_id
ON strokes (desk_id);
`).run();
// UPDATE
queries.update_desk_sn = db.query('UPDATE desks SET sn = $sn WHERE id = $id');
queries.update_session_lsn = db.query('UPDATE sessions SET lsn = $lsn WHERE id = $id');
queries.update_session_color = db.query('UPDATE sessions SET color = $color WHERE id = $id');
queries.update_session_width = db.query('UPDATE sessions SET width = $width WHERE id = $id');
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 * 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, width, color) VALUES (?1, ?2, ?3, ?4, ?5)');
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)');
const res4 = db.query('SELECT COUNT(id) as count FROM sessions').get();
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`
);
${res4.count} sessions
`);
const stored_desks = get_desks();
const stored_events = get_events();
const stored_strokes = get_strokes();
const stored_sessions = get_sessions();
// Init in-memory view: merge strokes into events, set all sessions to closed
const stored_desks = db.query('SELECT * FROM desks').all();
const stored_events = db.query('SELECT * FROM events').all();
const stored_strokes = db.query('SELECT * FROM strokes').all();
const stored_sessions = db.query('SELECT * FROM sessions').all();
const stroke_dict = {};
for (const stroke of stored_strokes) {
stroke.points = new Uint16Array(stroke.points.buffer);
stroke.points = new Float32Array(stroke.points.buffer);
stroke_dict[stroke.id] = stroke;
}
@ -161,7 +118,6 @@ export function startup() { @@ -161,7 +118,6 @@ export function startup() {
event.width = stroke.width;
}
desks[event.desk_id].events.push(event);
}
@ -170,60 +126,4 @@ export function startup() { @@ -170,60 +126,4 @@ export function startup() {
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, width, color) {
return queries.put_desk_stroke.get(stroke_id, desk_id, new Uint8Array(points.buffer, points.byteOffset, points.byteLength), width, color);
}
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);
}
Loading…
Cancel
Save