feat: rebuild warm operations workbench

This commit is contained in:
Codex
2026-07-30 14:49:33 +08:00
parent 25c853dea8
commit 570edf6bb2
88 changed files with 13413 additions and 2250 deletions
+242 -11
View File
@@ -1,6 +1,7 @@
import Database from 'better-sqlite3';
import { afterEach, describe, expect, it } from 'vitest';
import { buildControlPlaneApp } from './control-plane.js';
import { ScheduledTaskRepository } from './application/automation/scheduled-task-repository.js';
import { migrateDatabase } from './infrastructure/database/migrations.js';
import { UpstreamError } from './infrastructure/transport/upstream-error.js';
import { SafeInstanceTransport } from './infrastructure/transport/safe-instance-transport.js';
@@ -12,17 +13,83 @@ afterEach(async () => {
for (const db of dbs.splice(0)) db.close();
});
class Store implements SecretStore {
async set() {
return '';
private readonly values = new Map<string, string>();
async set(key: { instanceId: string; purpose: string; slot?: string }, value: string) {
const account = Buffer.from(
JSON.stringify(
key.slot === undefined
? [key.instanceId, key.purpose]
: [key.instanceId, key.purpose, key.slot],
),
'utf8',
).toString('base64url');
const reference = `keychain://multi-simadmin/${account}`;
this.values.set(reference, value);
return reference;
}
async get() {
return '[REDACTED]';
async get(reference: string) {
return this.values.get(reference);
}
async delete() {
return false;
async delete(reference: string) {
return this.values.delete(reference);
}
}
describe('buildControlPlaneApp', () => {
it('reconciles interrupted scheduled runs before starting the scheduler', async () => {
const db = new Database(':memory:');
db.pragma('foreign_keys=ON');
migrateDatabase(db);
dbs.push(db);
const repository = new ScheduledTaskRepository(db);
const task = {
name: 'Interrupted restart',
operationType: 'restart-service' as const,
cronExpression: '0 9 * * *',
timezone: 'Asia/Shanghai' as const,
targetSelector: { mode: 'fixed' as const, instanceIds: ['instance-a'] },
misfirePolicy: 'skip' as const,
overlapPolicy: 'skip' as const,
retryPolicy: { maxRetries: 0, intervalSeconds: 60 },
enabled: false,
};
repository.create({
id: 'task-1',
task,
createdBy: 'operator',
now: '2026-07-30T00:00:00.000Z',
});
repository.claimOccurrence({
id: 'run-1',
scheduledTaskId: 'task-1',
scheduleVersion: 1,
dueAt: '2026-07-30T01:00:00.000Z',
claimedAt: '2026-07-30T01:00:00.000Z',
triggerSource: 'scheduled',
targetSnapshot: [],
taskSnapshot: task,
});
repository.startRun('run-1', '2026-07-30T01:00:01.000Z');
const app = buildControlPlaneApp({
db,
store: new Store(),
upstream: {
get: async () => ({ status: 200, headers: {}, body: '' }),
request: async () => ({ status: 200, headers: {}, body: '' }),
postNetworkRegisterAuto: async () => ({ status: 200 }),
postServiceRestart: async () => ({ status: 200 }),
postSystemReboot: async () => ({ status: 200 }),
},
now: () => new Date('2026-07-30T02:00:00.000Z'),
});
expect(repository.getRun('run-1')).toMatchObject({
outcome: 'needs-attention',
finishedAt: '2026-07-30T02:00:00.000Z',
});
await app.close();
});
it('registers the durable event route fail-closed when no authentication dependency is supplied', async () => {
const db = new Database(':memory:');
db.pragma('foreign_keys=ON');
@@ -127,14 +194,178 @@ describe('buildControlPlaneApp', () => {
url: '/api/v1/instances/' + created.json().id + '/login',
payload: { password: 'never-leak-this' },
});
expect(insecureLogin.statusCode).toBe(400);
// Literal private HTTP origins are allowed for LAN instances; secrets still never leak.
expect(insecureLogin.statusCode).toBe(200);
expect(insecureLogin.json()).toMatchObject({
title: 'Bad Request',
status: 400,
code: 'UPSTREAM_INSECURE_AUTH',
authenticated: true,
instanceId: created.json().id,
});
expect(insecureLogin.body).not.toContain('never-leak-this');
expect(insecureLogin.body).not.toContain('192.168.1.10');
expect(insecureLogin.body).not.toContain('simadmin_session');
await app.close();
});
it('automatically logs in with saved credentials before aggregate messages and resource reads', async () => {
const db = new Database(':memory:');
db.pragma('foreign_keys=ON');
migrateDatabase(db);
dbs.push(db);
const calls: Array<{ url: string; cookie?: string }> = [];
const app = buildControlPlaneApp({
db,
store: new Store(),
upstream: {
get: async () => ({ status: 200, headers: {}, body: '' }),
request: async (request) => {
calls.push({
url: request.url,
...(request.headers.cookie ? { cookie: request.headers.cookie } : {}),
});
if (request.url.endsWith('/api/auth/login'))
return {
status: 200,
headers: { 'set-cookie': 'simadmin_session=aggregate-session' },
body: '{"status":"success"}',
};
if (request.url.includes('/api/sms/list'))
return {
status: 200,
headers: {},
body: JSON.stringify({
status: 'success',
data: {
messages: [
{
id: 7,
direction: 'incoming',
phone_number: '10086',
content: 'hi',
timestamp: '2026-07-29 12:00:00',
status: 'received',
},
],
},
}),
};
if (request.url.endsWith('/api/health'))
return {
status: 200,
headers: {},
body: '{"status":"ok","version":"2.3.4","platform":"linux"}',
};
return { status: 200, headers: {}, body: '{"status":"success","data":{}}' };
},
postNetworkRegisterAuto: async () => ({ status: 200 }),
postServiceRestart: async () => ({ status: 200 }),
postSystemReboot: async () => ({ status: 200 }),
},
});
const created = await app.inject({
method: 'POST',
url: '/api/v1/instances',
payload: {
name: 'Protected',
origin: 'http://192.168.1.20:3000',
password: { action: 'set', password: '[REDACTED]' },
},
});
const id = created.json().id as string;
const messages = await app.inject({
method: 'GET',
url: `/api/v1/instances/${id}/messages?limit=1&offset=0`,
});
expect(messages.statusCode).toBe(200);
expect(messages.json().messages).toEqual([
expect.objectContaining({ id: '7', phoneNumber: '10086', content: 'hi' }),
]);
const resources = await app.inject({ method: 'GET', url: `/api/v1/instances/${id}/resources` });
expect(resources.statusCode).toBe(200);
expect(resources.json()).toMatchObject({ version: '2.3.4', platform: 'linux' });
expect(calls.filter((call) => call.url.endsWith('/api/auth/login'))).toHaveLength(1);
for (const call of calls.filter((item) => !item.url.endsWith('/api/auth/login')))
expect(call.cookie).toBe('simadmin_session=aggregate-session');
await app.close();
});
it('attaches an origin-bound session cookie when executing restart operations', async () => {
const db = new Database(':memory:');
db.pragma('foreign_keys=ON');
migrateDatabase(db);
dbs.push(db);
const calls: unknown[] = [];
const app = buildControlPlaneApp({
db,
store: new Store(),
upstream: {
get: async () => ({ status: 200, headers: {}, body: '' }),
request: async (request) => {
calls.push({ kind: 'auth', request });
if (request.url.endsWith('/api/auth/login'))
return {
status: 200,
headers: { 'set-cookie': 'simadmin_session=opaque-token' },
body: '',
};
return { status: 200, headers: {}, body: '' };
},
postNetworkRegisterAuto: async () => ({ status: 200 }),
postServiceRestart: async (origin, cookie) => {
calls.push({ kind: 'restart', origin, cookie });
return { status: 200 };
},
postSystemReboot: async (origin, delaySeconds, cookie) => {
calls.push({ kind: 'reboot', origin, delaySeconds, cookie });
return { status: 200 };
},
},
now: () => new Date('2026-07-16T12:00:00.000Z'),
});
const created = await app.inject({
method: 'POST',
url: '/api/v1/instances',
payload: {
name: 'Restart Target',
origin: 'http://192.168.1.10:8080',
password: { action: 'set', password: '[REDACTED]' },
},
});
expect(created.statusCode).toBe(201);
const instanceId = created.json().id as string;
const revision = created.json().revision as number;
const prepared = await app.inject({
method: 'POST',
url: '/api/v1/operations/prepare',
payload: {
operationId: 'postServiceRestart',
targets: [{ instanceId, revision }],
parameters: {
parameterSchemaId: 'simadmin.58e2204.postServiceRestart.parameters.v1',
fields: [],
},
},
});
expect(prepared.statusCode).toBe(200);
const executed = await app.inject({
method: 'POST',
url: '/api/v1/operations/execute',
payload: {
preparationId: prepared.json().id,
confirmationToken: prepared.json().confirmationToken,
},
});
expect(executed.statusCode).toBe(202);
expect(executed.json()).toMatchObject({
operationId: 'postServiceRestart',
status: 'succeeded',
});
expect(calls).toContainEqual({
kind: 'restart',
origin: 'http://192.168.1.10:8080',
cookie: 'simadmin_session=opaque-token',
});
// Auth request body uses the redacted marker; the actual secret must never appear.
expect(JSON.stringify(calls)).toContain('"[REDACTED]"');
expect(JSON.stringify(calls)).not.toMatch(/never-leak|password":"[^[]/);
await app.close();
});