feat(ql3): add bounded panel bootstrap

This commit is contained in:
whyour
2026-09-02 11:30:42 +08:00
parent 83966a1287
commit 882ce07d7e
20 changed files with 1095 additions and 134 deletions
@@ -314,6 +314,17 @@ function fixture(overrides = {}) {
};
},
},
panelBootstrapRoute: {
async handle(value) {
events.push(
`panel-bootstrap:${value.operationId}:${value.principal.subject.id}`,
);
return {
statusCode: 200,
body: { code: 200, data: { username: value.principal.subject.id } },
};
},
},
now: () => 10_000,
randomUuid: () => '019f70c0-0000-4000-8000-000000000002',
...overrides,
@@ -548,6 +559,27 @@ test('uses task.read and the exact panel Cron audit identity for the compatibili
]);
});
test('uses the same authenticated task.read chain for the panel bootstrap identity', async () => {
for (const operationId of ['panel.user.get', 'panel.system.config.get']) {
const { admission, events } = fixture();
const result = await execute(
admission,
request({
operation: Object.freeze({ operationId, projectId: 'default' }),
}),
);
assert.equal(result.statusCode, 200);
assert.equal(result.body.data.username, 'usr_local');
assert.deepEqual(events, [
'authenticate',
'authorize:task.read:default',
`audit:allowed:${operationId}`,
'confirm',
`panel-bootstrap:${operationId}:usr_local`,
]);
}
});
test('authorizes and audits run.stop before exposing the cancellation body handler', async () => {
const { admission, events } = fixture();
const prepared = await admission.prepare(
@@ -277,6 +277,14 @@ test('serves only the fixed canonical loopback Run route and drains idempotently
});
assert.deepEqual(observed[6].operation, panelCrons.body.input);
const finalEdgePage = await request(
port,
'/api/crons?searchValue=&page=4&size=20&filters=%7B%7D',
);
assert.equal(finalEdgePage.statusCode, 200);
assert.equal(finalEdgePage.body.input.maximumRows, 64);
assert.equal(finalEdgePage.body.input.page, 4);
const unsupportedPanelQuery = await request(
port,
'/api/crons?searchValue=private&page=1&size=20&filters=%7B%7D',
@@ -285,7 +293,7 @@ test('serves only the fixed canonical loopback Run route and drains idempotently
code: 'invalid_panel_cron_list_query',
});
assert.equal(unsupportedPanelQuery.statusCode, 400);
assert.equal(observed.length, 7);
assert.equal(observed.length, 8);
const cancellationBody = JSON.stringify({
schema: 'qinglong/run-cancellation@v1',
@@ -306,7 +314,7 @@ test('serves only the fixed canonical loopback Run route and drains idempotently
);
assert.equal(cancellation.statusCode, 202);
assert.deepEqual(cancellation.body.accepted, JSON.parse(cancellationBody));
assert.deepEqual(observed[7].operation, {
assert.deepEqual(observed[8].operation, {
operationId: 'run.cancel',
projectId: 'prj_default',
runId: 'run_123',
@@ -333,7 +341,7 @@ test('serves only the fixed canonical loopback Run route and drains idempotently
);
assert.equal(taskStart.statusCode, 202);
assert.deepEqual(taskStart.body.accepted, JSON.parse(taskStartBody));
assert.deepEqual(observed[8].operation, {
assert.deepEqual(observed[9].operation, {
operationId: 'task.start',
projectId: 'prj_default',
taskId: 'task_1',
@@ -357,20 +365,20 @@ test('serves only the fixed canonical loopback Run route and drains idempotently
);
assert.equal(taskPut.statusCode, 202);
assert.deepEqual(taskPut.body.accepted, JSON.parse(taskPutBody));
assert.deepEqual(observed[9].operation, {
assert.deepEqual(observed[10].operation, {
operationId: 'task.put',
projectId: 'prj_default',
taskId: 'task_1',
});
assert.equal(observed[9].localPresence, 'ql3p_request_bound_proof');
assert.equal(observed[9].taskAuthoringLease, 'ql3a_exact_snapshot_lease');
assert.equal(observed[10].localPresence, 'ql3p_request_bound_proof');
assert.equal(observed[10].taskAuthoringLease, 'ql3a_exact_snapshot_lease');
const log = await request(
port,
'/api/v3/projects/prj_default/runs/run_123/attempts/attempt_1/log?offset=4&length=32',
);
assert.deepEqual(log.body, { range: { offset: 4, length: 32 } });
assert.deepEqual(observed[10].operation, {
assert.deepEqual(observed[11].operation, {
operationId: 'run.log.read',
projectId: 'prj_default',
runId: 'run_123',
@@ -399,19 +407,19 @@ test('serves only the fixed canonical loopback Run route and drains idempotently
},
);
assert.equal(authoring.statusCode, 200);
assert.deepEqual(observed[12].operation, {
assert.deepEqual(observed[13].operation, {
operationId: 'task.authoring',
projectId: 'prj_default',
taskId: 'task_1',
});
assert.equal(observed[12].localPresence, 'ql3p_authoring_read_proof');
assert.equal(observed[13].localPresence, 'ql3p_authoring_read_proof');
const secrets = await request(
port,
'/api/v3/projects/prj_default/secrets?limit=8&after=YWxwaGE',
);
assert.deepEqual(secrets.body, { secrets: [], truncated: false });
assert.deepEqual(observed[13].operation, {
assert.deepEqual(observed[14].operation, {
operationId: 'secret.list',
projectId: 'prj_default',
limit: 8,
@@ -435,11 +443,11 @@ test('serves only the fixed canonical loopback Run route and drains idempotently
);
assert.equal(secretPut.statusCode, 202);
assert.deepEqual(secretPut.body.accepted, JSON.parse(secretPutBody));
assert.deepEqual(observed[14].operation, {
assert.deepEqual(observed[15].operation, {
operationId: 'secret.put',
projectId: 'prj_default',
});
assert.equal(observed[14].localPresence, 'ql3p_secret_bound_proof');
assert.equal(observed[15].localPresence, 'ql3p_secret_bound_proof');
for (const invalidPath of [
'/api/v3/projects/prj_default/runs/run_123?expanded=true',
@@ -521,13 +529,86 @@ test('serves only the fixed canonical loopback Run route and drains idempotently
assert.equal(invalid.statusCode, 400);
assert.deepEqual(invalid.body, { code: 'invalid_run_step_list_query' });
}
assert.equal(observed.length, 15);
assert.equal(observed.length, 16);
assert.deepEqual(
await Promise.all([surface.stopAndDrain(), surface.stopAndDrain()]),
['stopped', 'stopped'],
);
});
test('serves the public capability shell and authenticates private panel bootstrap reads', async (t) => {
const port = await reservePort();
const observed = [];
const surface = await startLocalApiHttpSurface({
profile: 'edge',
host: '127.0.0.1',
port,
admission: preparedAdmission(async (value) => {
observed.push(value);
return {
statusCode: 200,
body: {
code: 200,
data: { operationId: value.operation.operationId },
},
};
}),
randomUuid: () => '019f70c0-0000-4000-8000-000000000013',
});
t.after(() => surface.stopAndDrain());
const health = await request(port, '/api/health?t=100', { headers: {} });
assert.equal(health.statusCode, 200);
assert.equal(health.body.data.status, 'ok');
assert.equal(health.body.data.ql3.capabilitiesPath, '/api/v3/capabilities');
const system = await request(port, '/api/system', { headers: {} });
assert.equal(system.statusCode, 200);
assert.equal(system.body.data.isInitialized, true);
assert.equal(system.body.data.ql3.profile, 'edge');
const capabilities = await request(port, '/api/v3/capabilities?t=101', {
headers: {},
});
assert.equal(capabilities.statusCode, 200);
assert.equal(
capabilities.body.capabilities.authentication.loginEndpoint,
null,
);
assert.equal(capabilities.body.capabilities.panel.legacyLogin, false);
assert.equal(capabilities.body.capabilities.limits.cronRows, 64);
assert.equal(observed.length, 0);
const user = await request(port, '/api/user?t=102');
assert.deepEqual(user.body, {
code: 200,
data: { operationId: 'panel.user.get' },
});
assert.deepEqual(observed[0].operation, {
operationId: 'panel.user.get',
projectId: 'default',
});
const config = await request(port, '/api/system/config?t=103');
assert.deepEqual(config.body, {
code: 200,
data: { operationId: 'panel.system.config.get' },
});
assert.deepEqual(observed[1].operation, {
operationId: 'panel.system.config.get',
projectId: 'default',
});
const invalid = await request(port, '/api/system?search=wide', {
headers: {},
});
assert.equal(invalid.statusCode, 400);
assert.deepEqual(invalid.body, { code: 'invalid_panel_bootstrap_query' });
const encoded = await request(port, '/api/%73ystem', { headers: {} });
assert.equal(encoded.statusCode, 404);
assert.equal(observed.length, 2);
});
test('rejects GET bodies without invoking the prepared route handler', async (t) => {
const port = await reservePort();
let handlers = 0;
@@ -0,0 +1,150 @@
const assert = require('node:assert/strict');
const test = require('node:test');
const {
createPanelBootstrapRoute,
panelCapabilities,
panelPublicResponse,
} = require('../dist/panel-compatibility/panelBootstrapRoute.js');
const PRINCIPAL = Object.freeze({
subject: Object.freeze({ type: 'user', id: 'owner' }),
authenticationId: 'auth:owner',
authenticatedAtMs: 1_787_200_000_000,
expiresAtMs: 1_787_200_060_000,
assurance: 'local_console',
});
test('publishes an exact profile-aware capability contract', () => {
const edge = panelCapabilities('edge');
const standalone = panelCapabilities('standalone');
assert.deepEqual(edge, {
schemaVersion: 1,
product: 'qinglong3',
version: '3.0.0-alpha.2',
deployment: { mode: 'local', profile: 'edge' },
authentication: {
kind: 'api_credential',
transport: 'bearer',
persistence: 'memory_only',
loginEndpoint: null,
},
project: { selection: 'explicit', defaultId: 'default' },
panel: {
bootstrap: true,
cronList: 'bounded_read_only',
taskRead: true,
triggerRead: true,
runRead: true,
runLogRead: true,
legacyMutations: false,
legacyLogin: false,
subscriptions: false,
scripts: false,
environmentVariables: false,
webSocket: false,
},
limits: {
cronRows: 64,
cronPageSize: 64,
logChunkBytes: 16 * 1_024,
},
});
assert.deepEqual(standalone.limits, {
cronRows: 256,
cronPageSize: 64,
logChunkBytes: 32 * 1_024,
});
assert.throws(() => panelCapabilities('cluster'));
});
test('serves public health, system and native capability envelopes', () => {
assert.deepEqual(panelPublicResponse('health', 'edge'), {
statusCode: 200,
body: {
code: 200,
data: {
status: 'ok',
ql3: {
schemaVersion: 1,
apiVersion: 'v3',
capabilitiesPath: '/api/v3/capabilities',
},
},
},
});
const system = panelPublicResponse('system', 'standalone');
assert.equal(system.body.code, 200);
assert.equal(system.body.data.isInitialized, true);
assert.equal(system.body.data.version, '3.0.0-alpha.2');
assert.equal(system.body.data.ql3.profile, 'standalone');
const capabilities = panelPublicResponse('capabilities', 'edge');
assert.equal(capabilities.body.capabilities.limits.cronRows, 64);
});
test('projects the authenticated principal and bounded panel configuration', async () => {
const route = createPanelBootstrapRoute('edge');
const user = await route.handle({
operationId: 'panel.user.get',
principal: PRINCIPAL,
});
assert.deepEqual(user, {
statusCode: 200,
body: {
code: 200,
data: {
username: 'owner',
ql3: {
schemaVersion: 1,
subjectType: 'user',
assurance: 'local_console',
expiresAtMs: 1_787_200_060_000,
credentialPersistence: 'memory_only',
panelHome: '/crontab',
},
},
},
});
const config = await route.handle({
operationId: 'panel.system.config.get',
principal: PRINCIPAL,
});
assert.equal(config.statusCode, 200);
assert.deepEqual(config.body.data.info, {
panelTitle: 'QingLong 3.0',
lang: 'zh-cn',
});
assert.equal(config.body.data.ql3.limits.cronRows, 64);
});
test('fails closed for invalid profile, principal and operation', async () => {
assert.throws(() => createPanelBootstrapRoute('cluster'));
const route = createPanelBootstrapRoute('standalone');
assert.equal(
(await route.handle({ operationId: 'panel.user.get', principal: null }))
.statusCode,
503,
);
assert.equal(
(
await route.handle({
operationId: 'panel.user.get',
principal: {
...PRINCIPAL,
subject: { type: 'api_app', id: 'service' },
assurance: 'service',
},
})
).statusCode,
503,
);
assert.equal(
(
await route.handle({
operationId: 'panel.unknown',
principal: PRINCIPAL,
})
).statusCode,
503,
);
});
@@ -230,6 +230,18 @@ test('fails closed for detached pins, unsupported triggers, invalid budgets and
},
},
});
assert.deepEqual(
await route.handle({
projectId: 'default',
page: 4,
size: 20,
maximumRows: 64,
}),
{
statusCode: 200,
body: { code: 200, data: { data: [], total: 0 } },
},
);
assert.equal(
(
await route.handle({
@@ -462,6 +462,34 @@ test('serves an authenticated Run through one real SQLite authority and durable
});
t.after(() => active.stopAndDrain());
const health = await request(
port,
'Bearer ignored-public-credential',
'/api/health?t=100',
);
assert.equal(health.statusCode, 200);
assert.equal(health.body.data.status, 'ok');
const capabilities = await request(
port,
'Bearer ignored-public-credential',
'/api/v3/capabilities',
);
assert.equal(capabilities.statusCode, 200);
assert.equal(capabilities.body.capabilities.deployment.profile, 'edge');
assert.equal(capabilities.body.capabilities.panel.legacyMutations, false);
const panelUser = await request(port, `Bearer ${TOKEN}`, '/api/user?t=101');
assert.equal(panelUser.statusCode, 200);
assert.equal(panelUser.body.data.username, 'local-api-user');
assert.equal(panelUser.body.data.ql3.credentialPersistence, 'memory_only');
const panelConfig = await request(
port,
`Bearer ${TOKEN}`,
'/api/system/config?t=102',
);
assert.equal(panelConfig.statusCode, 200);
assert.equal(panelConfig.body.data.info.panelTitle, 'QingLong 3.0');
assert.equal(panelConfig.body.data.ql3.limits.cronRows, 64);
const accepted = await request(port, `Bearer ${TOKEN}`);
assert.equal(accepted.statusCode, 200);
assert.equal(accepted.body.run.id, RUN_ID);
@@ -1132,6 +1160,7 @@ test('serves an authenticated Run through one real SQLite authority and durable
'run.get', 'run.list', 'run.events.list', 'run.steps.list',
'run.cancel', 'task.authoring.read', 'task.create', 'task.get',
'task.list', 'task.start', 'task.update', 'run.log.read',
'panel.system.config.get', 'panel.user.get',
'trigger.create', 'trigger.get', 'trigger.list', 'trigger.update',
'secret.create', 'secret.list'
)
@@ -1140,6 +1169,8 @@ test('serves an authenticated Run through one real SQLite authority and durable
.all()
.map(({ operation_id, outcome }) => `${operation_id}:${outcome}`),
[
'panel.system.config.get:allowed',
'panel.user.get:allowed',
'run.cancel:allowed',
'run.cancel:allowed',
'run.events.list:allowed',