Update Harbor client and gateway functionality
Build and Deploy Gateway / build-and-push (push) Successful in 35s
Build and Deploy Gateway / deploy (push) Successful in 14s

This commit is contained in:
2026-08-17 15:23:16 +03:00
parent 0b39211fbd
commit 7c255192b0
41 changed files with 1443 additions and 218 deletions
+45 -19
View File
@@ -90,6 +90,7 @@ test('state v1 projects legacy storage through the canonical profile snapshot',
appliedServerSnapshot: snapshot.profiles[0].servers[0],
});
assert.equal(snapshot.connection.process, 'running');
assert.equal(snapshot.route.rulesContractVersion, 2);
assert.equal(JSON.stringify(snapshot).includes(stored.subscriptionUrl), false);
assert.throws(
() => assertStateSnapshot({ ...snapshot, revision: -1 }),
@@ -107,7 +108,7 @@ test('startup discards a rejected cached subscription and returns to first-run',
server: '0.0.0.0',
server_port: 1,
};
const routeRules = [{ type: 'domain_suffix', value: 'example.org', enabled: true }];
const routeRules = [{ type: 'domain_suffix', value: 'example.org', enabled: true, outbound: 'direct' }];
fs.writeFileSync(path.join(dir, 'state.json'), JSON.stringify({
subscriptionUrl,
selectedTag: rejectedServer.tag,
@@ -296,14 +297,15 @@ setInterval(() => {}, 60_000);
assert.equal(initial.selection.appliedProfileId, 'profile_primary');
assert.equal(initial.selection.appliedServerSnapshot.id, testServerId);
assert.deepEqual(initial.route.localRules, [
{ type: 'domain_suffix', value: 'ru', enabled: true },
{ type: 'domain_suffix', value: 'ru', enabled: true, outbound: 'direct' },
]);
assert.equal(initial.route.rulesContractVersion, 2);
assert.deepEqual(initial.route.activeLocalRules, initial.route.localRules);
assert.equal(initial.route.localRulesRevision, 0);
assert.equal(initial.route.localRulesPendingRestart, false);
assert.equal(JSON.stringify(initial).includes(subscriptionUrl), false);
const migratedState = JSON.parse(fs.readFileSync(path.join(dir, 'state.json'), 'utf8'));
assert.equal(migratedState.schemaVersion, 5);
assert.equal(migratedState.schemaVersion, 6);
assert.equal(migratedState.profiles.length, 1);
assert.equal(migratedState.profiles[0].subscriptionUrl, subscriptionUrl);
assert.equal(migratedState.profiles[0].desiredServerId, testServerId);
@@ -521,19 +523,20 @@ setInterval(() => {}, 60_000);
assert.equal((await mutation('/api/singbox/stop')).state.connection.desired, 'stopped');
assert.equal((await mutation('/api/singbox/restart')).state.connection.desired, 'running');
let routed = await mutation('/api/route-rules', 'PUT', {
let routed = await mutation('/api/route-rules/v2', 'PUT', {
expectedRulesRevision: rulesRevision,
rulesContractVersion: 2,
rules: [
{ type: 'domain_suffix', value: 'ru', enabled: false },
{ type: 'domain', value: 'https://Example.com/private?q=1', enabled: true },
{ type: 'domain_suffix', value: '*.Example.org', enabled: true },
{ type: 'domain_suffix', value: 'ru', enabled: false, outbound: 'direct' },
{ type: 'domain', value: 'https://Example.com/private?q=1', enabled: true, outbound: 'vpn' },
{ type: 'domain_suffix', value: '*.Example.org', enabled: true, outbound: 'direct' },
],
});
rulesRevision = routed.state.route.localRulesRevision;
assert.deepEqual(routed.state.route.localRules, [
{ type: 'domain_suffix', value: 'ru', enabled: false },
{ type: 'domain', value: 'example.com', enabled: true },
{ type: 'domain_suffix', value: 'example.org', enabled: true },
{ type: 'domain_suffix', value: 'ru', enabled: false, outbound: 'direct' },
{ type: 'domain', value: 'example.com', enabled: true, outbound: 'vpn' },
{ type: 'domain_suffix', value: 'example.org', enabled: true, outbound: 'direct' },
]);
assert.equal(routed.state.route.localRulesPendingRestart, false);
assert.deepEqual(routed.state.route.activeLocalRules, routed.state.route.localRules);
@@ -545,16 +548,17 @@ setInterval(() => {}, 60_000);
timeout: '1s',
},
{ inbound: ['diagnostics-vpn-in'], outbound: testServerId },
{ domain: ['example.com'], outbound: 'direct' },
{ domain: ['example.com'], outbound: testServerId },
{ domain_suffix: ['example.org'], outbound: 'direct' },
]);
await mutation('/api/singbox/stop');
routed = await mutation('/api/route-rules', 'PUT', {
routed = await mutation('/api/route-rules/v2', 'PUT', {
expectedRulesRevision: rulesRevision,
rulesContractVersion: 2,
rules: [
...routed.state.route.localRules,
{ type: 'domain_keyword', value: 'media', enabled: true },
{ type: 'domain_keyword', value: 'media', enabled: true, outbound: 'vpn' },
],
});
rulesRevision = routed.state.route.localRulesRevision;
@@ -565,27 +569,48 @@ setInterval(() => {}, 60_000);
assert.equal(restartedRules.state.route.localRulesPendingRestart, false);
assert.deepEqual(restartedRules.state.route.activeLocalRules, routed.state.route.localRules);
const invalidRules = await rawRequest(port, '/api/route-rules', 'PUT', {
const invalidRules = await rawRequest(port, '/api/route-rules/v2', 'PUT', {
expectedRulesRevision: rulesRevision,
rules: [{ type: 'domain_regex', value: '.*' }],
rulesContractVersion: 2,
rules: [{ type: 'domain_regex', value: '.*', enabled: true, outbound: 'direct' }],
});
assert.equal(invalidRules.response.status, 400);
assert.equal(invalidRules.payload.error.code, 'REQUEST_INVALID');
assert.equal((await request(port, '/api/state')).revision, revision);
const staleRules = await rawRequest(port, '/api/route-rules', 'PUT', {
const staleRules = await rawRequest(port, '/api/route-rules/v2', 'PUT', {
expectedRulesRevision: 0,
rulesContractVersion: 2,
rules: [],
});
assert.equal(staleRules.response.status, 409);
assert.equal(staleRules.payload.error.code, 'STATE_CONFLICT');
assert.deepEqual((await request(port, '/api/state')).route.localRules, routed.state.route.localRules);
const skewConfig = fs.readFileSync(path.join(dir, 'sing-box-config.json'), 'utf8');
const legacyNoop = await rawRequest(port, '/api/route-rules', 'PUT', {
expectedRevision: revision,
rules: routed.state.route.localRules,
});
assert.equal(legacyNoop.response.status, 200);
assert.equal(legacyNoop.response.status, 400);
assert.equal(legacyNoop.payload.error.code, 'REQUEST_INVALID');
assert.equal((await request(port, '/api/state')).revision, revision);
assert.equal(fs.readFileSync(path.join(dir, 'sing-box-config.json'), 'utf8'), skewConfig);
for (const rules of [
[{ type: 'domain', value: 'missing.example', enabled: true }],
[{ type: 'domain', value: 'unknown.example', enabled: true, outbound: 'block' }],
]) {
const rejected = await rawRequest(port, '/api/route-rules/v2', 'PUT', {
expectedRulesRevision: rulesRevision,
rulesContractVersion: 2,
rules,
});
assert.equal(rejected.response.status, 400);
assert.equal(rejected.payload.error.code, 'REQUEST_INVALID');
assert.equal((await request(port, '/api/state')).revision, revision);
assert.equal(fs.readFileSync(path.join(dir, 'sing-box-config.json'), 'utf8'), skewConfig);
}
const workingConfig = fs.readFileSync(path.join(dir, 'sing-box-config.json'), 'utf8');
fs.writeFileSync(singboxPath, `#!/usr/bin/env node
@@ -599,9 +624,10 @@ process.on('SIGTERM', () => process.exit(0));
setInterval(() => {}, 60_000);
`);
fs.chmodSync(singboxPath, 0o755);
const failedRules = await rawRequest(port, '/api/route-rules', 'PUT', {
const failedRules = await rawRequest(port, '/api/route-rules/v2', 'PUT', {
expectedRulesRevision: rulesRevision,
rules: [{ type: 'domain', value: 'broken.example' }],
rulesContractVersion: 2,
rules: [{ type: 'domain', value: 'broken.example', enabled: true, outbound: 'vpn' }],
});
assert.equal(failedRules.response.status, 422);
assert.equal(failedRules.payload.error.code, 'CONFIG_INVALID');