generator: PJSIP transports, extension endpoints and rtp config
Golden cases empty, one-user, webrtc-disabled and hostile-names; each also renders identically from shuffled input. pnpm test:asterisk boots the asterisk image with every golden case and fails on a missing endpoint or context or any ERROR log line (checked against a deliberately broken case). On 24.0.0-rc2: 101 and 101-web load with every set_var. Task: M2-T3
This commit is contained in:
1 parent
a5de247948
commit
06ac99b9f4
44 files changed
+1040
-12
No files matched your search
@@ -1,12 +1,36 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Everything render() needs, loaded in one go (03 §1). render() itself does no I/O.
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { alias } from 'drizzle-orm/pg-core';
|
||||
import type { Db } from '../db/client.ts';
|
||||
import { dids, extensions, users } from '../db/schema.ts';
|
||||
import { decrypt } from '../crypto/secrets.ts';
|
||||
import { getAllSettings } from '../services/settings.ts';
|
||||
|
||||
export type SnapExtension = {
|
||||
id: string;
|
||||
number: string;
|
||||
name: string;
|
||||
sipPassword: string; // decrypted
|
||||
webrtcEnabled: boolean;
|
||||
maxContacts: number;
|
||||
ringSeconds: number;
|
||||
voicemailEnabled: boolean;
|
||||
outboundClass: 'internal' | 'local' | 'national' | 'international';
|
||||
outboundCallerId: string | null; // DID number
|
||||
emergencyCallerId: string | null; // DID number
|
||||
recordMode: 'never' | 'inbound' | 'outbound' | 'all';
|
||||
pickupGroup: string | null;
|
||||
language: string;
|
||||
mohClass: string;
|
||||
isAdmin: boolean; // the extension's user is an admin (time-condition override codes)
|
||||
};
|
||||
|
||||
export type Snapshot = {
|
||||
/** "YYYY-MM-DD" in general.timezone; render() never reads the clock. */
|
||||
today: string;
|
||||
settings: Awaited<ReturnType<typeof getAllSettings>>;
|
||||
extensions: SnapExtension[];
|
||||
};
|
||||
|
||||
export function todayIn(timezone: string, now = new Date()): string {
|
||||
@@ -16,5 +40,44 @@ export function todayIn(timezone: string, now = new Date()): string {
|
||||
|
||||
export async function loadSnapshot(db: Db, now = new Date()): Promise<Snapshot> {
|
||||
const settings = await getAllSettings(db);
|
||||
return { today: todayIn(settings['general.timezone'], now), settings };
|
||||
const outDid = alias(dids, 'out_did');
|
||||
const e911Did = alias(dids, 'e911_did');
|
||||
const rows = await db
|
||||
.select({
|
||||
ext: extensions,
|
||||
role: users.role,
|
||||
outCid: outDid.number,
|
||||
e911Cid: e911Did.number,
|
||||
})
|
||||
.from(extensions)
|
||||
.leftJoin(users, eq(users.id, extensions.userId))
|
||||
.leftJoin(outDid, eq(outDid.id, extensions.outboundCallerIdDidId))
|
||||
.leftJoin(e911Did, eq(e911Did.id, extensions.emergencyCallerIdDidId));
|
||||
|
||||
return {
|
||||
today: todayIn(settings['general.timezone'], now),
|
||||
settings,
|
||||
extensions: rows.map(({ ext, role, outCid, e911Cid }) => ({
|
||||
id: ext.id,
|
||||
number: ext.number,
|
||||
name: ext.name,
|
||||
sipPassword: decrypt(ext.sipPasswordEnc),
|
||||
webrtcEnabled: ext.webrtcEnabled,
|
||||
maxContacts: ext.maxContacts,
|
||||
ringSeconds: ext.ringSeconds,
|
||||
voicemailEnabled: ext.voicemailEnabled,
|
||||
outboundClass: ext.outboundClass,
|
||||
outboundCallerId: outCid,
|
||||
emergencyCallerId: e911Cid,
|
||||
recordMode: ext.recordMode,
|
||||
pickupGroup: ext.pickupGroup,
|
||||
language: ext.language,
|
||||
mohClass: ext.mohClass,
|
||||
isAdmin: role === 'admin',
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
/** Numeric order for internal numbers ("99" < "100"), then text as a tiebreak. */
|
||||
export const byNumber = (a: { number: string }, b: { number: string }) =>
|
||||
Number(a.number) - Number(b.number) || a.number.localeCompare(b.number);
|
||||
Reference in new issue
Block a user