extensions: numbering service, CRUD API, credentials, Users page tabs
One namespace for internal numbers (range, reserved codes, parking, every object table) with a next-free lookup. Creating an extension generates its SIP password and voicemail PIN and a runtime-state row; every change and password regeneration triggers a config apply. Credentials are revealed only on request and each reveal is audited. Users get an Ext. column and a drawer with General (number + next free), Phone and Credentials tabs. scripts/e2e/m2-t5.mjs checks the flow in a browser and that the generated PJSIP file follows; the files it produced load cleanly in Asterisk. Task: M2-T5
This commit is contained in:
1 parent
a4da81cf8f
commit
1cfc13ead3
12 files changed
+1167
-141
No files matched your search
@@ -1,6 +1,17 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Typed fetch wrappers (05 §1). Every request sends X-Requested-With (CSRF, 04 §2.2).
|
||||
import type { AuthUser, ListResponse, UserCreate, UserDto, UserUpdate } from '@selfphoneng/shared';
|
||||
import type {
|
||||
AuthUser,
|
||||
ExtensionCreate,
|
||||
ExtensionDto,
|
||||
ExtensionUpdate,
|
||||
ListResponse,
|
||||
NumberKind,
|
||||
SipCredentials,
|
||||
UserCreate,
|
||||
UserDto,
|
||||
UserUpdate,
|
||||
} from '@selfphoneng/shared';
|
||||
|
||||
export class ApiError extends Error {
|
||||
readonly status: number;
|
||||
@@ -50,3 +61,16 @@ export const usersApi = {
|
||||
resetPassword: (id: string) =>
|
||||
api<{ temporaryPassword: string }>('POST', `/api/users/${id}/reset-password`),
|
||||
};
|
||||
|
||||
export const extensionsApi = {
|
||||
get: (id: string) => api<ExtensionDto>('GET', `/api/extensions/${id}`),
|
||||
create: (e: ExtensionCreate) => api<ExtensionDto>('POST', '/api/extensions', e),
|
||||
update: (id: string, e: ExtensionUpdate) =>
|
||||
api<ExtensionDto>('PATCH', `/api/extensions/${id}`, e),
|
||||
remove: (id: string) => api<{ ok: true }>('DELETE', `/api/extensions/${id}`),
|
||||
credentials: (id: string) => api<SipCredentials>('GET', `/api/extensions/${id}/sip-credentials`),
|
||||
regenerateSipPassword: (id: string) =>
|
||||
api<{ ok: true }>('POST', `/api/extensions/${id}/regenerate-sip-password`),
|
||||
nextNumber: (kind: NumberKind) =>
|
||||
api<{ number: string | null }>('GET', `/api/numbering/next?kind=${kind}`).then((r) => r.number),
|
||||
};
|
||||
Reference in new issue
Block a user