diff --git a/server/db.js b/server/db.js index 3ee6c67..904ce49 100644 --- a/server/db.js +++ b/server/db.js @@ -236,12 +236,6 @@ try { SELECT MAX(created_at) FROM messages WHERE messages.sender_id = users.id ) WHERE last_seen IS NULL AND EXISTS (SELECT 1 FROM messages WHERE messages.sender_id = users.id)`); } catch (e) { /* messages table may not exist yet on a fresh db */ } -// External (non-Connect) invitee emails on a scheduled meeting (#4) — JSON array. They get an emailed -// guest join link instead of an in-app invite. -try { db.exec('ALTER TABLE scheduled_meetings ADD COLUMN guest_emails TEXT'); } catch (e) { /* exists */ } -// Lobby (#4): 1 = guests joining by link must be admitted by the host; 0 = they join directly. NULL is -// treated as "require approval" (safe default) by the signaling layer. -try { db.exec('ALTER TABLE scheduled_meetings ADD COLUMN lobby INTEGER'); } catch (e) { /* exists */ } try { db.exec('CREATE INDEX IF NOT EXISTS idx_users_bizgaze_user_id ON users(bizgaze_user_id)'); } catch (e) { /* exists */ } // When two accounts merge (#2), the merged-away row is deleted. This records old_id -> survivor so // any lingering reference to the old id (a cached contact, an in-flight DM) resolves to the survivor @@ -320,6 +314,13 @@ try { db.exec('ALTER TABLE scheduled_meetings ADD COLUMN cancelled INTEGER NOT N try { db.exec('ALTER TABLE scheduled_meetings ADD COLUMN duration_mins INTEGER'); } catch (e) { /* exists */ } // Weekly recurrence: JSON array of weekdays (0=Sun..6=Sat), or null for a one-off. try { db.exec('ALTER TABLE scheduled_meetings ADD COLUMN recurrence TEXT'); } catch (e) { /* exists */ } +// External (non-Connect) invitee emails on a scheduled meeting (#4) — JSON array. They get an emailed +// guest join link instead of an in-app invite. (Must come AFTER the CREATE above — on a fresh DB these +// ALTERs previously ran before the table existed and were silently lost.) +try { db.exec('ALTER TABLE scheduled_meetings ADD COLUMN guest_emails TEXT'); } catch (e) { /* exists */ } +// Lobby (#4): 1 = guests joining by link must be admitted by the host; 0 = they join directly. NULL is +// treated as "require approval" (safe default) by the signaling layer. +try { db.exec('ALTER TABLE scheduled_meetings ADD COLUMN lobby INTEGER'); } catch (e) { /* exists */ } // Meeting recordings & transcripts. Video bytes live in recordings/m_.webm, transcript text // in transcripts/m_.txt. Tied to a room (and group/scheduled meeting when applicable) so they diff --git a/server/test/e2e.js b/server/test/e2e.js index 29b8ade..5ba36eb 100644 --- a/server/test/e2e.js +++ b/server/test/e2e.js @@ -289,21 +289,21 @@ function nextMsg(ws, type, timeout = 3000) { const schP = await call('/api/v1/meetings/schedule', { title: 'Synced', scheduledAt: Date.now() + 3600000, participants: [bobId] }, cookie); check('meeting scheduled with participants', schP.status === 200 && Array.isArray(schP.data.participants) && schP.data.participants.includes(bobId)); const bobMeetings = await get('/api/v1/meetings', bobCookie); - const bm = bobMeetings.data.find((m) => m.id === schP.data.id); + const bm = bobMeetings.data.list.find((m) => m.id === schP.data.id); check('invited participant sees the meeting', !!bm && bm.isHost === false); const schPush = await nextMsg(bobWs, 'chat-message'); check('schedule announced in the group chat', schPush.message && /Scheduled a call/.test(schPush.message.body)); const pastM = await call('/api/v1/meetings/schedule', { group: gid, title: 'Old Standup', scheduledAt: Date.now() - 2 * 60 * 60 * 1000 }, cookie); check('past meeting scheduling is rejected', pastM.status === 400); // can't schedule in the past (#1) const mlist = await get('/api/v1/meetings', cookie); - const schUp = mlist.data.find((m) => m.id === sched.data.id); + const schUp = mlist.data.list.find((m) => m.id === sched.data.id); check('meetings list buckets upcoming', mlist.status === 200 && schUp && schUp.status === 'upcoming'); const sm = wsClient(); await new Promise((r) => sm.on('open', r)); sm.send(JSON.stringify({ type: 'meeting-join', room: sched.data.roomCode, name: 'Admin' })); const smJoined = await nextMsg(sm, 'meeting-joined'); check('scheduled meeting joinable by code (room created lazily)', !!smJoined.peerId && smJoined.room === sched.data.roomCode); const mlist2 = await get('/api/v1/meetings', cookie); - const upRun = mlist2.data.find((m) => m.id === sched.data.id); + const upRun = mlist2.data.list.find((m) => m.id === sched.data.id); check('scheduled meeting shows running while a peer is connected', !!upRun && upRun.status === 'running' && upRun.inCall >= 1); sm.close(); const cancelBob = await call('/api/v1/meetings/cancel', { id: sched.data.id }, bobCookie);