perf,feat(events),ops: third optimization pass
Overview cold start: - InstanceResourceService persists every live fetch into status_snapshots (category 'resources') and serves a snapshot younger than 2 minutes before calling upstream, so the first fleet overview after a restart costs zero device requests; instance deletion cleans up via FK cascade Bounded retention for the four fastest-growing tables (15-minute sweep): - connection_logs: one row per probe per beat had no automatic cleanup - audit_events: new pruneBefore (90d) - operation_preparations: every prepare/retry attempt inserted a row, terminal rows now expire after 7 days - notification_queue: terminal rows pruned after 30 days SSE events now cover instance lifecycle: - create/update emit instance envelopes from the routes; the two-phase delete emits a job envelope on every terminal transition plus an instance envelope when the node actually disappears, so fleet and instance views invalidate in real time Linux ops: - 'install.sh unit' writes systemd user units for API and gateway with Restart=on-failure, 0600 secret injection, and MULTI_SIMADMIN_SYSTEMD_UNIT wired so console self-update restarts via systemctl -- closing the self-update loop on Linux
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { EventEnvelope } from '../../application/events/event-journal.js';
|
||||
import { buildApp } from '../../app.js';
|
||||
import {
|
||||
InstanceSessionStore,
|
||||
@@ -78,13 +79,36 @@ const fixture = () => {
|
||||
idFactory: () => `operation-${++id}`,
|
||||
tokenFactory: () => Buffer.alloc(32, 11).toString('base64url'),
|
||||
});
|
||||
const events: Array<Record<string, unknown>> = [];
|
||||
const onEvent = (envelope: EventEnvelope): void => {
|
||||
events.push(envelope as unknown as Record<string, unknown>);
|
||||
};
|
||||
const app = buildApp({
|
||||
registerRoutes: (scope) =>
|
||||
registerInstanceRoutes(scope, { instances, connections, login, deletion }),
|
||||
registerInstanceRoutes(scope, { instances, connections, login, deletion, onEvent }),
|
||||
});
|
||||
return { app, instances };
|
||||
return { app, instances, events };
|
||||
};
|
||||
|
||||
describe('instance route SSE events', () => {
|
||||
it('emits an instance envelope when a node is created', async () => {
|
||||
const { app, events } = fixture();
|
||||
const created = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/v1/instances',
|
||||
payload: {
|
||||
name: 'Alpha',
|
||||
origin: 'http://192.168.1.10:8080',
|
||||
password: { action: 'set', password: 'never-return-this' },
|
||||
},
|
||||
});
|
||||
expect(created.statusCode).toBe(201);
|
||||
expect(events).toHaveLength(1);
|
||||
expect(events[0]).toMatchObject({ kind: 'instance', requestId: expect.any(String) });
|
||||
expect(String(events[0]?.instanceId)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('instance HTTP routes', () => {
|
||||
it('creates, lists, gets, updates and deletes redacted instance resources', async () => {
|
||||
const { app } = fixture();
|
||||
|
||||
Reference in New Issue
Block a user