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:
2026-07-24 21:26:10 +05:30
parent dbd209ac2b
commit 2460c0f9eb
9 changed files with 450 additions and 447 deletions
+16 -16
View File
@@ -13,14 +13,14 @@ const MIME = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css
// Authorize an attachment id: the uploader, a member of the group using it as an avatar, or a participant of
// ANY message carrying it (the "any" covers forwarded attachments, which reuse the same id). Returns the row.
function authAttachmentRaw(id, u) {
const a = R.attachments.byId(id);
const a = await R.attachments.byId(id);
if (!a || a.team_id !== u.team_id) return null;
const avatarGroup = R.conversations.byAvatar(id);
const carriers = R.messages.allByAttachment(id);
const avatarGroup = await R.conversations.byAvatar(id);
const carriers = await R.messages.allByAttachment(id);
const ok = a.uploader_id === u.id
|| (avatarGroup && R.conversations.isMember(avatarGroup.id, u.id))
|| (avatarGroup && await R.conversations.isMember(avatarGroup.id, u.id))
|| carriers.some((msg) => msg.conversation_id
? R.conversations.isMember(msg.conversation_id, u.id)
? await R.conversations.isMember(msg.conversation_id, u.id)
: (msg.sender_id === u.id || msg.recipient_id === u.id));
return ok ? a : null;
}
@@ -141,11 +141,11 @@ function handleGet(req, res) {
});
}
if (pathOnly.startsWith('/transcripts/')) {
const u = currentUser(req);
const u = await currentUser(req);
if (!u) return json(res, 401, { error: 'unauthorized' });
const name = path.basename(decodeURIComponent(pathOnly));
const sid = name.replace(/\.txt$/i, '');
const row = R.sessionsLog.byIdInTenant(sid, u.team_id);
const row = await R.sessionsLog.byIdInTenant(sid, u.team_id);
if (!row || !row.transcript) return json(res, 404, { error: 'not found' });
const fp = path.join(TRANS_DIR, row.transcript);
if (!fp.startsWith(TRANS_DIR)) return json(res, 403, { error: 'forbidden' });
@@ -158,11 +158,11 @@ function handleGet(req, res) {
});
}
if (pathOnly.startsWith('/recordings/')) {
const u = currentUser(req);
const u = await currentUser(req);
if (!u) return json(res, 401, { error: 'unauthorized' });
const name = path.basename(decodeURIComponent(pathOnly));
const sid = name.replace(/\.(webm|mp4)$/i, '');
const row = R.sessionsLog.byIdInTenant(sid, u.team_id);
const row = await R.sessionsLog.byIdInTenant(sid, u.team_id);
if (!row || !row.recording) return json(res, 404, { error: 'not found' });
const fp = path.join(REC_DIR, row.recording);
if (!fp.startsWith(REC_DIR)) return json(res, 403, { error: 'forbidden' });
@@ -179,16 +179,16 @@ function handleGet(req, res) {
// Meeting recordings & transcripts (/mrec/<id>). Visible to the creator, group members, or those
// who can see the scheduled meeting it belongs to.
if (pathOnly.startsWith('/mrec/')) {
const u = currentUser(req);
const u = await currentUser(req);
if (!u) return json(res, 401, { error: 'unauthorized' });
const id = path.basename(decodeURIComponent(pathOnly));
const r = R.recordings.byId(id);
const r = await R.recordings.byId(id);
if (!r || r.team_id !== u.team_id || !r.file) return json(res, 404, { error: 'not found' });
let allowed = r.created_by === u.id;
if (r.kind === 'transcript') allowed = r.created_by === u.id; // transcripts are private to their owner
else {
if (!allowed && r.group_id) allowed = R.conversations.isMember(r.group_id, u.id);
if (!allowed && r.meeting_id) { const s = R.scheduledMeetings.byId(r.meeting_id); if (s) allowed = s.created_by === u.id || (s.participants && s.participants.includes('"' + u.id + '"')); }
if (!allowed && r.group_id) allowed = await R.conversations.isMember(r.group_id, u.id);
if (!allowed && r.meeting_id) { const s = await R.scheduledMeetings.byId(r.meeting_id); if (s) allowed = s.created_by === u.id || (s.participants && s.participants.includes('"' + u.id + '"')); }
}
if (!allowed) return json(res, 403, { error: 'forbidden' });
const isVideo = r.kind === 'video';
@@ -208,7 +208,7 @@ function handleGet(req, res) {
// Video POSTER thumbnail — first frame extracted with ffmpeg, cached next to the file. Cosmetic: if ffmpeg
// is missing or fails we 404 and the <video> just falls back to its own (black) poster.
if (pathOnly.startsWith('/thumbs/')) {
const u = currentUser(req);
const u = await currentUser(req);
if (!u) return json(res, 401, { error: 'unauthorized' });
const id = path.basename(decodeURIComponent(pathOnly));
const a = authAttachment(id, u);
@@ -236,7 +236,7 @@ function handleGet(req, res) {
// back to the original bytes while that is still transcoding, so a video is never unplayable. The
// download button keeps pointing at /files, which always serves the untouched original.
if (pathOnly.startsWith('/stream/')) {
const u = currentUser(req);
const u = await currentUser(req);
if (!u) return json(res, 401, { error: 'unauthorized' });
const id = path.basename(decodeURIComponent(pathOnly));
const a = authAttachment(id, u);
@@ -253,7 +253,7 @@ function handleGet(req, res) {
});
}
if (pathOnly.startsWith('/files/')) {
const u = currentUser(req);
const u = await currentUser(req);
if (!u) return json(res, 401, { error: 'unauthorized' });
const id = path.basename(decodeURIComponent(pathOnly));
const a = authAttachment(id, u);