wip(db): async call-site conversion in progress (Phase 3) — DO NOT MERGE yet
On the db-migration branch only; master stays clean + deployable. Foundation (adapter, pg schema, smoke harness) is already on master and safe. Done: - repos.js fully async (Phase 2, validated: node --check clean, no missed transforms). - session.js currentUser/apiKeyFromReq async. - Mechanical `await` prefix applied across routes/static/calls/signaling/reminders/ webhooks/push. Remaining (does NOT compile yet — deterministic to finish): 1. Async cascade: helper fns that now contain `await` must be marked async and their callers awaited. node --check points to each (namesFor, authAttachmentRaw/ authAttachment in static, the WS handlers in calls/signaling, reminders/webhooks loops). 2. DTO builders are the real work: namesFor, avatarsFor, buildPollDTO, buildMsgDTO, recDTO all became async — every `.map(x => buildMsgDTO(...))` etc. must become `await Promise.all(arr.map(async x => ...))`. 3. Chained calls `R.x.y(...).map/.length/.includes` → `(await R.x.y(...)).method`. 4. Then: node --check all green → node test/db-smoke.js green → e2e → merge to master. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+20
-20
@@ -31,10 +31,10 @@ function persistCallHistory(room) {
|
||||
roomStats.delete(room);
|
||||
if (!st || !st.teamId || st.peak < 1) return;
|
||||
try {
|
||||
if (R.scheduledMeetings.byCode(room)) return; // the scheduled meeting row already represents this
|
||||
if (await R.scheduledMeetings.byCode(room)) return; // the scheduled meeting row already represents this
|
||||
const gid = roomToGroupCall.get(room) || null;
|
||||
const isDm = roomToDmCall.has(room);
|
||||
R.callHistory.create({
|
||||
await R.callHistory.create({
|
||||
id: A.id(), teamId: st.teamId, room, groupId: gid,
|
||||
kind: isDm ? 'dm' : (gid ? 'group' : 'adhoc'),
|
||||
title: isDm ? 'Direct call' : (gid ? null : 'Meeting'),
|
||||
@@ -48,7 +48,7 @@ function persistCallHistory(room) {
|
||||
// organizer chose "join directly". Logged-in tenant users are never held — only guests.
|
||||
function meetingRoomRequiresApproval(room) {
|
||||
if (roomLobby.has(room)) return !!roomLobby.get(room);
|
||||
try { const s = R.scheduledMeetings.byCode(room); if (s) return s.lobby !== 0; } catch (_) {}
|
||||
try { const s = await R.scheduledMeetings.byCode(room); if (s) return s.lobby !== 0; } catch (_) {}
|
||||
return true;
|
||||
}
|
||||
// Actually add a newcomer to the room: tell them who's here, tell others they arrived, and (if they're
|
||||
@@ -86,7 +86,7 @@ function handle(ws, m, req) {
|
||||
switch (m.type) {
|
||||
// --- Logged-in user registers this socket for live chat delivery ---
|
||||
case 'chat-hello': {
|
||||
const u = currentUser(req); // identity from the cookie/Bearer on the WS upgrade
|
||||
const u = await currentUser(req); // identity from the cookie/Bearer on the WS upgrade
|
||||
if (!u) return ws.send(JSON.stringify({ type: 'error', message: 'unauthorized' }));
|
||||
ws._chatUserId = u.id; ws._chatTeamId = u.team_id;
|
||||
CHAT.register(u.id, ws);
|
||||
@@ -97,10 +97,10 @@ function handle(ws, m, req) {
|
||||
// Recipient's client acknowledges a DM was delivered → mark it + tell the sender.
|
||||
case 'chat-delivered': {
|
||||
if (!ws._chatUserId || !m.id) break;
|
||||
const msg = R.messages.byId(m.id);
|
||||
const msg = await R.messages.byId(m.id);
|
||||
if (!msg || msg.conversation_id || msg.team_id !== ws._chatTeamId) break; // DMs only
|
||||
if (msg.recipient_id !== ws._chatUserId) break; // only the recipient can ack
|
||||
if (!msg.delivered_at) { R.messages.markDelivered(m.id); try { CHAT.pushToUser(msg.sender_id, { type: 'chat-delivered', id: m.id, with: msg.recipient_id }); } catch (_) {} }
|
||||
if (!msg.delivered_at) { await R.messages.markDelivered(m.id); try { CHAT.pushToUser(msg.sender_id, { type: 'chat-delivered', id: m.id, with: msg.recipient_id }); } catch (_) {} }
|
||||
break;
|
||||
}
|
||||
// Live "is typing…" — ephemeral, never persisted. Relay to the DM peer, or fan out to
|
||||
@@ -108,9 +108,9 @@ function handle(ws, m, req) {
|
||||
case 'chat-typing': {
|
||||
const uid = ws._chatUserId; if (!uid) break;
|
||||
const on = !!m.on;
|
||||
let name = ''; try { const u = R.users.byId(uid); name = (u && u.name) || ''; } catch (_) {}
|
||||
let name = ''; try { const u = await R.users.byId(uid); name = (u && u.name) || ''; } catch (_) {}
|
||||
if (m.group) {
|
||||
let members; try { if (!R.conversations.isMember(m.group, uid)) break; members = R.conversations.members(m.group); } catch (_) { break; }
|
||||
let members; try { if (!await R.conversations.isMember(m.group, uid)) break; members = await R.conversations.members(m.group); } catch (_) { break; }
|
||||
for (const mid of members) { if (mid !== uid) { try { CHAT.pushToUser(mid, { type: 'chat-typing', group: m.group, from: uid, name, on }); } catch (_) {} } }
|
||||
} else if (m.to) {
|
||||
try { CHAT.pushToUser(m.to, { type: 'chat-typing', from: uid, name, on }); } catch (_) {}
|
||||
@@ -121,7 +121,7 @@ function handle(ws, m, req) {
|
||||
case 'meeting-create': {
|
||||
let code; do { code = A.numericCode(6); } while (meetingRooms.has(code));
|
||||
meetingRooms.set(code, new Map());
|
||||
const cu = currentUser(req); if (cu) roomHost.set(code, cu.id); // ad-hoc meeting: creator = host
|
||||
const cu = await currentUser(req); if (cu) roomHost.set(code, cu.id); // ad-hoc meeting: creator = host
|
||||
// Lobby preference for guests joining this ad-hoc room by link (default: require approval).
|
||||
roomLobby.set(code, m.lobby === false ? false : true);
|
||||
ws.send(JSON.stringify({ type: 'meeting-created', room: code }));
|
||||
@@ -132,7 +132,7 @@ function handle(ws, m, req) {
|
||||
let peers = meetingRooms.get(room);
|
||||
// A scheduled meeting's room is created lazily on first join (its code lives in the DB).
|
||||
if (!peers) {
|
||||
const sched = R.scheduledMeetings.byCode(room);
|
||||
const sched = await R.scheduledMeetings.byCode(room);
|
||||
if (sched && !sched.ended_at) { peers = new Map(); meetingRooms.set(room, peers); }
|
||||
}
|
||||
if (!peers) return ws.send(JSON.stringify({ type: 'error', message: 'Meeting not found' }));
|
||||
@@ -141,8 +141,8 @@ function handle(ws, m, req) {
|
||||
ws.kind = 'meeting'; ws._meetingRoom = room; ws._peerId = peerId; ws._peerName = name;
|
||||
// Host = the meeting's creator. roomHost is set on call/meeting creation; scheduled meetings fall back to created_by.
|
||||
let hostUserId = roomHost.get(room);
|
||||
if (hostUserId === undefined) { try { const s = R.scheduledMeetings.byCode(room); if (s) { hostUserId = s.created_by; roomHost.set(room, hostUserId); } } catch (_) {} }
|
||||
const ju = currentUser(req);
|
||||
if (hostUserId === undefined) { try { const s = await R.scheduledMeetings.byCode(room); if (s) { hostUserId = s.created_by; roomHost.set(room, hostUserId); } } catch (_) {} }
|
||||
const ju = await currentUser(req);
|
||||
// Identity used to map LiveKit media → this tile (peerIdForUid). Logged-in users use their user id
|
||||
// (their LiveKit token identity is the same). GUESTS have no session, so they pass a stable client
|
||||
// guest id here that ALSO becomes their LiveKit token identity — otherwise their media never maps
|
||||
@@ -264,20 +264,20 @@ function handle(ws, m, req) {
|
||||
}
|
||||
// --- Agent comes online ---
|
||||
case 'agent-hello': {
|
||||
const machine = R.machines.byEnrollToken(m.enrollToken);
|
||||
const machine = await R.machines.byEnrollToken(m.enrollToken);
|
||||
if (!machine) return ws.send(JSON.stringify({ type: 'error', message: 'invalid enroll token' }));
|
||||
ws.kind = 'agent'; ws.machineId = machine.id;
|
||||
onlineAgents.set(machine.id, { ws, machine });
|
||||
R.machines.touch(machine.id);
|
||||
await R.machines.touch(machine.id);
|
||||
ws.send(JSON.stringify({ type: 'agent-registered', machineId: machine.id, name: machine.name }));
|
||||
break;
|
||||
}
|
||||
// --- Technician requests control of a machine ---
|
||||
case 'viewer-connect': {
|
||||
const u = currentUser(req); // cookie sent on WS upgrade
|
||||
const u = await currentUser(req); // cookie sent on WS upgrade
|
||||
if (!u) return ws.send(JSON.stringify({ type: 'error', message: 'unauthorized' }));
|
||||
const agent = onlineAgents.get(m.machineId);
|
||||
const machine = R.machines.inTenant(m.machineId, u.team_id);
|
||||
const machine = await R.machines.inTenant(m.machineId, u.team_id);
|
||||
if (!machine) return ws.send(JSON.stringify({ type: 'error', message: 'no such machine' }));
|
||||
if (!agent) return ws.send(JSON.stringify({ type: 'error', message: 'machine offline' }));
|
||||
if (u.role === 'viewer' && false) {} // view-only still allowed to watch; control gated agent-side
|
||||
@@ -301,7 +301,7 @@ function handle(ws, m, req) {
|
||||
if (m.granted) {
|
||||
audit({ team_id: sess.machine.team_id, user_id: sess.user.id, user_email: sess.user.email, machine_id: sess.machine.id, machine_name: sess.machine.name, action: 'consent_granted', detail: sess.ticket ? 'Ticket ' + sess.ticket : (sess.machine.id ? null : 'Direct session') });
|
||||
try {
|
||||
R.sessionsLog.create({ id: m.sessionId, tenantId: sess.machine.team_id, agentEmail: sess.user.email, agentName: sess.agentName || sess.user.email, ticket: sess.ticket || null });
|
||||
await R.sessionsLog.create({ id: m.sessionId, tenantId: sess.machine.team_id, agentEmail: sess.user.email, agentName: sess.agentName || sess.user.email, ticket: sess.ticket || null });
|
||||
} catch (e) { /* duplicate consent */ }
|
||||
try { W.emit('session.started', sess.machine.team_id, { sessionId: m.sessionId, agent_email: sess.user.email, agent_name: sess.agentName || sess.user.email, ticket: sess.ticket || null, started_at: Date.now() }); } catch (_) {}
|
||||
sess.viewerWs.send(JSON.stringify({ type: 'session-ready', sessionId: m.sessionId }));
|
||||
@@ -325,7 +325,7 @@ function handle(ws, m, req) {
|
||||
}
|
||||
// --- Logged-in agent enters the code (+ ticket) to connect ---
|
||||
case 'code-connect': {
|
||||
const agent = currentUser(req); // identity from the agent's authenticated session
|
||||
const agent = await currentUser(req); // identity from the agent's authenticated session
|
||||
if (!agent) {
|
||||
return ws.send(JSON.stringify({ type: 'error', message: 'Please sign in as an agent first' }));
|
||||
}
|
||||
@@ -379,9 +379,9 @@ function handle(ws, m, req) {
|
||||
function endSession(sessionId, reason) {
|
||||
const sess = liveSessions.get(sessionId);
|
||||
if (!sess) return;
|
||||
try { R.sessionsLog.end(sessionId); } catch (e) {}
|
||||
try { await R.sessionsLog.end(sessionId); } catch (e) {}
|
||||
try {
|
||||
const row = R.sessionsLog.byId(sessionId);
|
||||
const row = await R.sessionsLog.byId(sessionId);
|
||||
if (row) W.emit('session.ended', sess.machine.team_id, { sessionId: row.id, agent_email: row.agent_email,
|
||||
agent_name: row.agent_name, ticket: row.ticket, started_at: row.started_at, ended_at: row.ended_at,
|
||||
duration_ms: row.ended_at ? row.ended_at - row.started_at : null });
|
||||
|
||||
Reference in New Issue
Block a user