e67a783bdc
- db/pg.js: the pg backend (prepare/exec/tx/init) — ?→$N translation, BIGINT parsed as Number (matches sqlite; else expires_at<Date.now() compares string<number), transactions on one pooled client, init() applies schema.pg.sql. Same interface as db/sqlite.js, so repos are unchanged. - repos.js: the ~7 SQLite-only queries rewritten to run on BOTH engines — audit.add @named→positional; email lookups COLLATE NOCASE→LOWER()=LOWER(); INSERT OR IGNORE→ON CONFLICT DO NOTHING (addMember/poll vote/favorite); mergeInto's UPDATE OR IGNORE→UPDATE…WHERE NOT EXISTS/NOT IN and INSERT OR REPLACE→ON CONFLICT DO UPDATE. Re-validated on sqlite: db-smoke still 22/22. - server.js: boot now `await db.init()` before listening (pg creates tables; sqlite no-op), so the first request can't hit a missing table. - db/migrate-sqlite-to-pg.js: one-shot row copy in FK order (bulk insert, TRUNCATE first so re-runnable). audit_log id left to PG's identity. - package.json: add pg ^8.13.1. Next: validate DB_BACKEND=pg smoke against a real Postgres on the server, then merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
78 lines
3.7 KiB
JavaScript
78 lines
3.7 KiB
JavaScript
// BizGaze Connect — backend entry point.
|
|
// Thin wiring layer: HTTP request dispatch + WebSocket attach + listeners.
|
|
// All logic lives in focused modules:
|
|
// repos.js data-access (all SQL)
|
|
// bizgaze.js BizGaze identity provider
|
|
// lib.js HTTP helpers (json/readBody/parseCookies/now)
|
|
// session.js currentUser / audit
|
|
// presence.js shared in-memory live state (agents/sessions/shares)
|
|
// routes.js HTTP JSON API (/api/*, /sso)
|
|
// static.js static files + authenticated downloads (GET fallback)
|
|
// signaling.js WebSocket signaling (consent + SDP/ICE relay)
|
|
const http = require('http');
|
|
const https = require('https');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { WebSocketServer } = require('ws');
|
|
const { PORT, HTTPS_PORT } = require('./config');
|
|
const { json } = require('./lib');
|
|
const routes = require('./routes');
|
|
const { handleGet } = require('./static');
|
|
const { onConnection } = require('./signaling');
|
|
|
|
// ---------- HTTP request dispatch ----------
|
|
const server = http.createServer((req, res) => {
|
|
const key = `${req.method} ${req.url.split('?')[0]}`;
|
|
// Route/static handlers are async now (DB adapter). Catch any rejection so a handler error becomes a
|
|
// 500 instead of a hung request + unhandled promise rejection.
|
|
let p;
|
|
if (routes[key]) p = routes[key](req, res);
|
|
else if (req.method === 'GET') p = handleGet(req, res); // downloads + static
|
|
else return json(res, 404, { error: 'not found' });
|
|
if (p && typeof p.catch === 'function') p.catch((e) => { try { console.error('handler error', key, e && e.message); json(res, 500, { error: 'server error' }); } catch (_) {} });
|
|
});
|
|
|
|
// ---------- WebSocket signaling ----------
|
|
const wss = new WebSocketServer({ server, path: '/ws' });
|
|
wss.on('connection', onConnection);
|
|
|
|
// Apply the DB schema BEFORE serving. For Postgres this creates the tables (async); for SQLite it's a
|
|
// no-op (the schema is already applied synchronously when db.js is required). Serving only starts once the
|
|
// store is ready, so the first request can never hit a missing table.
|
|
const db = require('./dbx');
|
|
|
|
function startListening() {
|
|
server.listen(PORT, () => {
|
|
console.log(`HTTP on http://localhost:${PORT} (db=${db.name})`);
|
|
try { require('./media').backfill(); } catch (e) {} // streaming renditions for older video uploads
|
|
});
|
|
|
|
// HTTPS — required so other devices can share their screen (browsers block
|
|
// screen capture on non-secure origins). Uses cert.pem/key.pem if present.
|
|
try {
|
|
const certPath = path.join(__dirname, 'cert.pem');
|
|
const keyPath = path.join(__dirname, 'key.pem');
|
|
if (fs.existsSync(certPath) && fs.existsSync(keyPath)) {
|
|
const httpsServer = https.createServer(
|
|
{ cert: fs.readFileSync(certPath), key: fs.readFileSync(keyPath) },
|
|
(req, res) => server.emit('request', req, res)
|
|
);
|
|
const wssSecure = new WebSocketServer({ server: httpsServer, path: '/ws' });
|
|
wssSecure.on('connection', onConnection);
|
|
httpsServer.listen(HTTPS_PORT, () => {
|
|
console.log(`HTTPS on https://localhost:${HTTPS_PORT} (use this address from other devices)`);
|
|
console.log(` End user shares screen: https://<this-pc-ip>:${HTTPS_PORT}/share`);
|
|
console.log(` Technician connects: https://<this-pc-ip>:${HTTPS_PORT}/connect`);
|
|
});
|
|
} else {
|
|
console.log('(No cert.pem/key.pem found — HTTPS disabled. Other devices can view but not share their screen.)');
|
|
}
|
|
} catch (e) {
|
|
console.log('HTTPS failed to start:', e.message);
|
|
}
|
|
}
|
|
|
|
db.init().then(startListening).catch((e) => { console.error('DB init failed:', (e && e.message) || e); process.exit(1); });
|
|
|
|
module.exports = { server };
|