70 lines
2.9 KiB
JavaScript
70 lines
2.9 KiB
JavaScript
/**
|
||||
|
|
* 只重跑「部署」这一步 —— 复用 KV 里**已经签好的**证书,不碰 ACME。
|
|||
|
|
*
|
|||
|
|
* 为什么需要它(2026-10-06 加):
|
|||
|
|
* 签发的成败与部署的成败是**两件独立的事**。一旦部署侧出问题(1Panel 接口
|
|||
|
|
* 改结构、站点名对不上、面板限流…),重新走 `/renew` 会**再签一张新证书**
|
|||
|
|
* —— 白白消耗 CA 配额,而证书本身根本没出问题。
|
|||
|
|
* 这个脚本直接把 KV 里的证书 + 配置里的部署目标重跑一遍。
|
|||
|
|
*
|
|||
|
|
* 用法:
|
|||
|
|
* docker exec cn-certkeeper node src/redeploy.mjs t-t.live
|
|||
|
|
* docker exec cn-certkeeper node src/redeploy.mjs t-t.live --dry # 只看会绑哪些站点
|
|||
|
|
*/
|
|||
|
|
import path from 'node:path';
|
|||
|
|
import { createRequire } from 'node:module';
|
|||
|
|
import { fileURLToPath } from 'node:url';
|
|||
|
|
import { FileKV } from './kv-file.mjs';
|
|||
|
|
|
|||
|
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|||
|
|
const require = createRequire(import.meta.url);
|
|||
|
|
const lib = (n) => require(path.resolve(HERE, '../lib', `${n}.js`));
|
|||
|
|
|
|||
|
|
const argv = process.argv.slice(2);
|
|||
|
|
const NAME = argv.find((a) => !a.startsWith('--'));
|
|||
|
|
const DRY = argv.includes('--dry');
|
|||
|
|
if (!NAME) {
|
|||
|
|
console.error('用法:node src/redeploy.mjs <域名> [--dry]');
|
|||
|
|
process.exit(2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const kv = new FileKV(process.env.DATA_DIR || '/data');
|
|||
|
|
const env = { RSS_KV: kv, TOKEN_SECRET: String(process.env.TOKEN_SECRET || '').trim(), EDITOR_API_BASE: '', EDITOR_TOKEN: '' };
|
|||
|
|
|
|||
|
|
const { loadConfig, getCert, getAccess } = lib('certstore');
|
|||
|
|
const { makeDeployer } = lib('deployer');
|
|||
|
|
|
|||
|
|
const cfg = await loadConfig(env);
|
|||
|
|
const d = cfg.domains.find((x) => x.name === NAME);
|
|||
|
|
if (!d) {
|
|||
|
|
console.error(`配置里没有域名「${NAME}」(现有:${cfg.domains.map((x) => x.name).join(', ')})`);
|
|||
|
|
process.exit(2);
|
|||
|
|
}
|
|||
|
|
const rec = await getCert(env, NAME);
|
|||
|
|
if (!rec?.cert || !rec?.key) {
|
|||
|
|
console.error(`KV 里没有「${NAME}」的证书内容(先跑一次签发)`);
|
|||
|
|
process.exit(2);
|
|||
|
|
}
|
|||
|
|
console.log(`域名 ${NAME}:证书到期 ${new Date(rec.expireAt).toISOString()}(${Math.round((rec.expireAt - Date.now()) / 86400000)} 天)`);
|
|||
|
|
console.log(`部署目标 ${(d.deploy || []).join(', ')};1Panel 站点 ${(d.one_panel_sites || []).join(', ')}`);
|
|||
|
|
if (DRY) process.exit(0);
|
|||
|
|
|
|||
|
|
for (const target of d.deploy || []) {
|
|||
|
|
const credName = target === '1panel' ? '1panel-cn' : target;
|
|||
|
|
const cred = await getAccess(env, credName);
|
|||
|
|
if (!cred) {
|
|||
|
|
console.log(` ${target}: ★ 找不到凭据「${credName}」`);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
const dp = makeDeployer(cred);
|
|||
|
|
const res = await dp.deploy(
|
|||
|
|
{ domain: NAME, cert: rec.cert, key: rec.key, notAfter: rec.expireAt },
|
|||
|
|
{ dogecloudDomains: d.dogecloud_domains, onePanelSites: d.one_panel_sites, log: (m) => console.log(` [${target}] ${m}`) },
|
|||
|
|
);
|
|||
|
|
console.log(` ${target}: 完成 —— ${res.details.join(';')}`);
|
|||
|
|
} catch (e) {
|
|||
|
|
console.log(` ${target}: ★ 失败 —— ${e instanceof Error ? e.message : e}`);
|
|||
|
|
process.exitCode = 1;
|
|||
|
|
}
|
|||
|
|
}
|