conferences: rooms with PIN and admin PIN, per-room limits, recording
/api/conferences CRUD: PINs stored encrypted, shown to admins (they hand
them out), never in the audit log; an admin PIN needs a different room
PIN (checked against the stored values on PATCH). The generator renders
sp-conf-{id} per 03 §6.11 (PIN prompt, 3 tries, admin PIN joins with
sp_admin_user), the number in sp-local-numbers and destApp conference.
Two changes from the spec's snippet, both found with real calls and
written back into 03:
- Set(CONFBRIDGE(bridge,max_members)) is ignored once ConfBridge()
names a bridge profile: a 4th caller got into a room of 3. Each room
now has its own [sp_room_<n>](default_bridge) profile in
confbridge_selfphoneng.conf.
- A recorded room runs sp-sub-record on each caller's leg (just before
ConfBridge) instead of record_conference, so the files are ordinary
call recordings: call history, Recordings page, retention.
Web: Conference rooms page (list, drawer form), "Conference room" in
the DestinationPicker. Golden case conferences. e2e m8-t3: room built
in the UI, wrong PIN refused and asked again, admin PIN joins with
flags AM, a 4th caller turned away, recordings listed, and a no-PIN
room with two desk phones.
Task: M8-T3
This commit is contained in:
1 parent
964a3c0b25
commit
a6c423de97
45 files changed
+1456
-14
No files matched your search
@@ -5,6 +5,7 @@ import { alias } from 'drizzle-orm/pg-core';
|
||||
import type { Db } from '../db/client.ts';
|
||||
import {
|
||||
announcements,
|
||||
conferences,
|
||||
dids,
|
||||
extensions,
|
||||
holidays,
|
||||
@@ -115,6 +116,16 @@ export type SnapQueue = {
|
||||
timeoutDestination: Destination;
|
||||
};
|
||||
|
||||
export type SnapConference = {
|
||||
id: string;
|
||||
number: string;
|
||||
name: string;
|
||||
pin: string | null; // decrypted
|
||||
adminPin: string | null; // decrypted
|
||||
maxMembers: number;
|
||||
record: boolean;
|
||||
};
|
||||
|
||||
export type SnapIvr = {
|
||||
id: string;
|
||||
number: string;
|
||||
@@ -167,6 +178,7 @@ export type Snapshot = {
|
||||
routes: SnapRoute[];
|
||||
ringGroups: SnapRingGroup[];
|
||||
queues: SnapQueue[];
|
||||
conferences: SnapConference[];
|
||||
ivrs: SnapIvr[];
|
||||
timeConditions: SnapTimeCondition[];
|
||||
holidays: SnapHoliday[];
|
||||
@@ -220,6 +232,7 @@ export async function loadSnapshot(db: Db, now = new Date()): Promise<Snapshot>
|
||||
})
|
||||
.from(queueMembers)
|
||||
.innerJoin(extensions, eq(extensions.id, queueMembers.extensionId));
|
||||
const confRows = await db.select().from(conferences);
|
||||
const ivrRows = await db.select().from(ivrs);
|
||||
const tcRows = await db.select().from(timeConditions);
|
||||
const holidayRows = await db.select().from(holidays);
|
||||
@@ -313,6 +326,15 @@ export async function loadSnapshot(db: Db, now = new Date()): Promise<Snapshot>
|
||||
.map(({ number, name, penalty }) => ({ number, name, penalty })),
|
||||
timeoutDestination: q.timeoutDestination as Destination,
|
||||
})),
|
||||
conferences: confRows.map((c) => ({
|
||||
id: c.id,
|
||||
number: c.number,
|
||||
name: c.name,
|
||||
pin: c.pinEnc ? decrypt(c.pinEnc) : null,
|
||||
adminPin: c.adminPinEnc ? decrypt(c.adminPinEnc) : null,
|
||||
maxMembers: c.maxMembers,
|
||||
record: c.record,
|
||||
})),
|
||||
routes: routeRows.map((r) => ({
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
|
||||
Reference in new issue
Block a user