feat: BizGaze Connect home, BizGaze login, modular backend, /api/v1
User-facing - New post-login home (/home): chat rail + Share/Connect (embedded) + Meeting; login lives here when logged out - Landing: "Log in with BizGaze" + no-login screen share - Console replaced by a role-scoped Dashboard (/dashboard): admins see all team sessions, others see only their own; stats + CSV/PDF export - Recordings saved as MP4 (H.264/AAC) with WebM fallback; old .webm still downloadable - Fix: duplicate "Sign in" on the login card Auth / integration - BizGaze as identity provider: /api/login validates against BIZGAZE_LOGIN_URL (env-gated) and provisions a local user - Phase 2 start: /api/v1 alias for all /api routes; Authorization: Bearer accepted across HTTP + WS; login returns a token (for native desktop/mobile clients) Backend refactor (Phase 1, behavior-preserving) - Split server.js into config/lib/session/presence/routes/static/signaling + repos (data-access) + bizgaze (service) - All SQL behind repos.js, tenant-scoped (tenantId == team_id for now) - e2e updated to current flow (21/21 pass before and after) Docs: ARCHITECTURE.md (target architecture + phased plan), CLAUDE.md repo layout, .env.example BIZGAZE_LOGIN_URL Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+19
-31
@@ -1,15 +1,20 @@
|
||||
// End-to-end test of the backend platform.
|
||||
// Exercises the full flow: register -> enable MFA -> login (2 steps) ->
|
||||
// enroll machine -> agent comes online -> technician requests session ->
|
||||
// consent -> signaling relay -> audit trail. No browser/Electron needed:
|
||||
// the "agent" and "viewer" are raw WebSocket clients.
|
||||
// Exercises the full flow: register -> login -> enroll machine -> agent online ->
|
||||
// technician requests session -> consent -> signaling relay -> audit trail.
|
||||
// No browser/Electron needed: the "agent" and "viewer" are raw WebSocket clients.
|
||||
// (Login currently marks the session MFA-passed directly, so there is no separate
|
||||
// TOTP step in the product flow; the MFA endpoints still exist but aren't exercised here.)
|
||||
|
||||
process.env.DB_PATH = '/tmp/ra-e2e.db';
|
||||
const fs = require('fs');
|
||||
for (const f of ['/tmp/ra-e2e.db', '/tmp/ra-e2e.db-wal', '/tmp/ra-e2e.db-shm']) { try { fs.unlinkSync(f); } catch {} }
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const DB = path.join(os.tmpdir(), 'ra-e2e.db');
|
||||
process.env.DB_PATH = DB;
|
||||
for (const f of [DB, DB + '-wal', DB + '-shm']) { try { fs.unlinkSync(f); } catch {} }
|
||||
|
||||
const PORT = 8099;
|
||||
process.env.PORT = PORT;
|
||||
process.env.HTTPS_PORT = 8444; // avoid clashing with a running dev server on 8443
|
||||
const { server } = require('../server');
|
||||
const A = require('../auth');
|
||||
const WebSocket = require('ws');
|
||||
@@ -59,38 +64,21 @@ function nextMsg(ws, type, timeout = 3000) {
|
||||
await wait(300); // let server bind
|
||||
console.log('E2E backend tests:');
|
||||
|
||||
// 1. Register
|
||||
// 1. Register (first user becomes admin)
|
||||
const email = 'tech@example.com';
|
||||
const reg = await call('/api/register', { email, password: 'supersecret', teamName: 'Acme IT' });
|
||||
check('register returns mfa setup', reg.status === 200 && reg.data.mfaSetup && reg.data.mfaSetup.secret);
|
||||
const secret = reg.data.mfaSetup.secret;
|
||||
check('register succeeds', reg.status === 200 && reg.data.ok === true);
|
||||
|
||||
// 2. Login before MFA enabled — allowed, mfaRequired=false
|
||||
let login = await call('/api/login', { email, password: 'supersecret' });
|
||||
// 2. Login -> session cookie (login marks the session MFA-passed)
|
||||
const login = await call('/api/login', { email, password: 'supersecret' });
|
||||
check('login sets session cookie', !!login.cookie);
|
||||
const cookie = login.cookie;
|
||||
|
||||
// 3. Enable MFA with a valid TOTP
|
||||
const enable = await call('/api/mfa/enable', { email, code: A.totp(secret) });
|
||||
check('mfa enable succeeds with valid code', enable.status === 200);
|
||||
const badEnable = await call('/api/mfa/enable', { email, code: '000000' });
|
||||
check('mfa enable rejects bad code', badEnable.status === 401);
|
||||
|
||||
// 4. Fresh login now requires MFA
|
||||
login = await call('/api/login', { email, password: 'supersecret' });
|
||||
check('login now flags mfaRequired', login.data.mfaRequired === true);
|
||||
let cookie = login.cookie;
|
||||
|
||||
// 5. Protected route blocked until MFA passed
|
||||
const meBlocked = await get('/api/me', cookie);
|
||||
check('me blocked before mfa', meBlocked.status === 401);
|
||||
|
||||
// 6. Pass MFA
|
||||
const mfa = await call('/api/login/mfa', { code: A.totp(secret) }, cookie);
|
||||
check('login mfa step succeeds', mfa.status === 200);
|
||||
// 3. Protected route works right after login, role=admin
|
||||
const me = await get('/api/me', cookie);
|
||||
check('me works after mfa, role=admin', me.status === 200 && me.data.role === 'admin');
|
||||
check('me works after login, role=admin', me.status === 200 && me.data.role === 'admin');
|
||||
|
||||
// 7. Wrong password rejected
|
||||
// 4. Wrong password rejected
|
||||
const badLogin = await call('/api/login', { email, password: 'wrong' });
|
||||
check('wrong password rejected', badLogin.status === 401);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user