blocked: blocked numbers page and API; M6 milestone check

/api/blocked-numbers stores numbers normalized (any input format) and
mirrors them to AstDB SP/BLOCK/<number>, which every trunk entry checks
(Hangup 21); the AstDB resync on connect includes them. Blocked numbers
page: add with a note, unblock.

e2e m6-check runs the M6 milestone scenario with real calls into a trunk
entry: DID → office hours (closed → voicemail; *31N from an admin's phone
forces open) → menu (1 → ring group ringing two phones together, 2 → a
user), and a caller blocked in the UI is rejected while others get
through.

Task: M6-T8
This commit is contained in:
iamdoubz committed 2026-10-18 13:57:27 -05:00
1 parent 57e0e3dac2
commit 4fc0fb4084
12 files changed
+559 -3

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// Blocked callers (02 blocked_numbers): calls from these numbers are rejected at the trunk.
import { z } from 'zod';
import { normalizeNumber } from './phone.ts';
export const BlockedCreateSchema = z.object({
/** Any format; stored normalized like incoming caller IDs (00 §8). */
number: z
.string()
.trim()
.min(1, 'Required')
.transform((v, ctx) => {
try {
return normalizeNumber(v);
} catch {
ctx.addIssue({ code: 'custom', message: 'A phone number like (312) 555-1234' });
return z.NEVER;
}
}),
reason: z.string().trim().max(100).nullable().optional(),
});
export type BlockedCreate = z.input<typeof BlockedCreateSchema>;
export type BlockedDto = { id: string; number: string; reason: string | null; createdAt: string };
+1
View File
@@ -17,3 +17,4 @@ export * from './ivrs.ts';
export * from './timeconditions.ts';
export * from './announcements.ts';
export * from './me.ts';
export * from './blocked.ts';