system: information panel and event log; about: download this build's source
ci / check (push) Successful in 1m20s
ci / images (push) Successful in 20s

System page laid out after 3CX's admin dashboard: a System information panel (version,
host name, company, public IP, LAN IPs, ports, telephony, restart needed, copy buttons,
how to restart) and an Event log panel (system_events, 90 days: trunk down/up, Asterisk
down/up, restart needed, config failed, 911, public IP changed, backups, disk) with Load
more and a CSV download.

About (/about, no sign-in, linked from the login page, My phone and System): AGPL-3.0
notice, credits, the repository link, and "Code v<version>", which downloads
selfphone.tar.gz: the api image now tars its own build context (no .env, data,
node_modules, build output or .git) before installing anything.

Task: M11-T7
This commit is contained in:
iamdoubz committed 2026-10-20 11:26:34 -05:00
1 parent a6ba61756c
commit 0ff0f9292b
29 files changed
+5035 -79

No files matched your search

+14 -1
View File
@@ -5,12 +5,18 @@ import type { LightMyRequestResponse } from 'fastify';
import { eq } from 'drizzle-orm';
import type { TrunkLive } from '@selfphoneng/shared';
import { createJobs, retentionCleanup, type JobDeps } from '../src/jobs.ts';
import { extensions, sessions, voicemailMessages } from '../src/db/schema.ts';
import { extensions, sessions, systemEvents, voicemailMessages } from '../src/db/schema.ts';
import { getSetting, setSetting } from '../src/services/settings.ts';
import { hasTestDb } from './db.ts';
import { ADMIN, H, createTestApp } from './app.ts';
describe.skipIf(!hasTestDb)('background jobs', () => {
const events = (code: string) =>
t.db
.select({ severity: systemEvents.severity, message: systemEvents.message })
.from(systemEvents)
.where(eq(systemEvents.code, code))
.orderBy(systemEvents.id);
let t: Awaited<ReturnType<typeof createTestApp>>;
let req: (m: string, url: string, payload?: unknown) => Promise<LightMyRequestResponse>;
let trunkId: string;
@@ -159,6 +165,9 @@ describe.skipIf(!hasTestDb)('background jobs', () => {
expect(await getSetting(t.db, 'network.publicIp')).toBe('203.0.113.9');
expect(await getSetting(t.db, 'system.restartRequired')).toBe(true);
expect(t.mails.map((m) => m.subject)).toEqual(['Public IP changed to 203.0.113.9']);
expect(await events('network.publicIpChanged')).toEqual([
{ severity: 'warning', message: 'Public IP changed from 203.0.113.5 to 203.0.113.9' },
]);
await j.publicIpWatch(); // unchanged: nothing
vi.stubGlobal('fetch', async () => new Response('<html>rate limited</html>'));
@@ -187,6 +196,10 @@ describe.skipIf(!hasTestDb)('background jobs', () => {
clock += 86_400_000; // next day: again
await j.diskWatch();
expect(t.mails).toHaveLength(2);
expect(await events('disk.low')).toEqual([
{ severity: 'warning', message: 'Disk almost full: 6% free' },
{ severity: 'warning', message: 'Disk almost full: 6% free' },
]);
});
it('a mail that fails to send is logged, not thrown', async () => {