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:
+16
-16
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user