import assert from "node:assert/strict"; import { test } from "node:test"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { parse } from "yaml"; import { certificateFixture } from "../certificate-fixture"; test("certificate provisioning preserves credentials, binds domain/ports, supports retry and consumes capability", async () => { const dir = mkdtempSync(path.join(tmpdir(), "hy2-provision-test-")); process.env.DATABASE_PATH = path.join(dir, "test.db"); process.env.APP_URL = "https://panel.example.com"; process.env.POLLER_ENABLED = "false"; const { ensureDb } = await import("../../lib/db"); const { decryptText } = await import("../../lib/crypto"); const { saveCertificate, validateCertificate, deleteCertificate, listCertificates, } = await import("../../lib/certificates"); const { createInstallation, installationForDownload, provisionNode, revokeInstallation, } = await import("../../lib/provisioning"); const { buildInstallScript } = await import("../../lib/install-script"); const db = await ensureDb(); const fixture = certificateFixture(); try { assert.throws( () => validateCertificate( fixture.certificate, certificateFixture().privateKey, ), /匹配/, ); assert.throws( () => validateCertificate( fixture.certificate, fixture.privateKey, "a.b.p.xn--876a.net", ), /SNI/, ); assert.throws( () => validateCertificate("invalid", fixture.privateKey), /解析/, ); const certId = Number( await saveCertificate( "Wildcard", fixture.certificate, fixture.privateKey, ), ); assert.equal( JSON.stringify(await listCertificates()).includes("PRIVATE KEY"), false, ); const stored = db .prepare("SELECT private_key_encrypted FROM managed_certificates") .get() as { private_key_encrypted: string }; assert.notEqual(stored.private_key_encrypted, fixture.privateKey); assert.equal(decryptText(stored.private_key_encrypted), fixture.privateKey); const id = await createInstallation({ name: "New server", slug: "new-server", certificateId: certId, }); await assert.rejects( () => createInstallation({ name: "Duplicate", slug: "new-server", certificateId: certId, }), /存在/, ); await assert.rejects(() => deleteCertificate(certId), /关联/); const job = await installationForDownload(id); const script = buildInstallScript(job); assert.ok(script.includes("--domain")); assert.ok(!script.includes("PRIVATE KEY")); assert.ok(!script.includes("__TOKEN__")); const token = decryptText(job.token_encrypted)!; assert.equal(job.token_hash.includes(token), false); const ports = { domain: "hk.p.xn--876a.net", hy2Port: 23456, statsPort: 23457, hy2ListenPort: 443, statsListenPort: 9999, }; await assert.rejects( () => provisionNode("f".repeat(64), "prepare", ports), /无效/, ); await assert.rejects(() => provisionNode(token, "complete"), /先获取/); await assert.rejects( () => provisionNode(token, "prepare", { ...ports, domain: "evil.example" }), /域名/, ); await assert.rejects( () => provisionNode(token, "prepare", { ...ports, hy2Port: 65536 }), /端口/, ); const bundle = await provisionNode(token, "prepare", ports); assert.ok("config" in bundle); assert.equal(bundle.privateKey, fixture.privateKey); const config = parse(bundle.config); assert.equal(config.listen, ":443"); assert.equal(config.trafficStats.listen, ":9999"); assert.match( config.auth.http.url, /^https:\/\/panel.example.com\/api\/hy2\/nodes\/new-server\/auth\?token=/, ); assert.deepEqual(await provisionNode(token, "prepare", ports), bundle); await assert.rejects( () => provisionNode(token, "prepare", { ...ports, statsPort: 12345 }), /绑定/, ); const node = db .prepare("SELECT * FROM hy2_nodes WHERE slug='new-server'") .get() as Record; assert.equal(node.server_port, 23456); assert.equal(node.traffic_stats_url, "http://hk.p.xn--876a.net:23457"); assert.equal(node.sni, ports.domain); assert.equal(node.enabled, 0); await provisionNode(token, "complete"); await provisionNode(token, "complete"); assert.equal( ( db.prepare("SELECT enabled FROM hy2_nodes WHERE id=?").get(node.id) as { enabled: number; } ).enabled, 1, ); await assert.rejects(() => provisionNode(token, "prepare", ports), /完成/); await assert.rejects(() => installationForDownload(id), /完成/); assert.equal( (db.prepare("SELECT COUNT(*) AS n FROM hy2_nodes").get() as { n: number }) .n, 1, ); const retryId = await createInstallation({ name: "Retry", slug: "retry", certificateId: certId, }); const retryToken = decryptText( (await installationForDownload(retryId)).token_encrypted, )!; await revokeInstallation(retryId); await assert.rejects( () => provisionNode(retryToken, "prepare", ports), /无效/, ); const expiredId = await createInstallation({ name: "Expired", slug: "expired", certificateId: certId, }); const expiredToken = decryptText( (await installationForDownload(expiredId)).token_encrypted, )!; db.prepare( "UPDATE node_installations SET expires_at='2000-01-01' WHERE id=?", ).run(expiredId); await assert.rejects( () => provisionNode(expiredToken, "prepare", ports), /过期/, ); await revokeInstallation(id, true); assert.equal( (db.prepare("SELECT COUNT(*) AS n FROM hy2_nodes").get() as { n: number }) .n, 1, ); await revokeInstallation(retryId, true); await revokeInstallation(expiredId, true); await deleteCertificate(certId); assert.equal((await listCertificates()).length, 0); assert.deepEqual(db.pragma("foreign_key_check"), []); } finally { db.close(); rmSync(dir, { recursive: true, force: true }); } });