2025-04-14 23:31:59 +08:00
|
|
|
|
// 扫描目录,列出文件,然后加载为模块
|
|
|
|
|
|
|
2025-04-28 23:34:08 +08:00
|
|
|
|
import path, { join } from "path";
|
|
|
|
|
|
import fs from "fs";
|
2025-04-14 23:31:59 +08:00
|
|
|
|
import { pathToFileURL } from "node:url";
|
2025-04-15 00:16:57 +08:00
|
|
|
|
import * as yaml from "js-yaml";
|
2026-05-31 01:41:33 +08:00
|
|
|
|
import { AbstractTaskPlugin, BaseAccess, BaseNotification } from "@certd/pipeline";
|
|
|
|
|
|
import { BaseAddon } from "@certd/lib-server";
|
2026-01-08 00:39:31 +08:00
|
|
|
|
import { dnsProviderRegistry } from "@certd/plugin-cert";
|
2026-05-31 01:41:33 +08:00
|
|
|
|
import { pluginRegistry, accessRegistry, notificationRegistry, pluginGroups } from "@certd/pipeline";
|
2025-04-28 23:34:08 +08:00
|
|
|
|
|
2025-04-14 23:31:59 +08:00
|
|
|
|
function scanDir(dir) {
|
|
|
|
|
|
const files = fs.readdirSync(dir);
|
|
|
|
|
|
const result = [];
|
|
|
|
|
|
// 扫描目录及子目录
|
|
|
|
|
|
for (const file of files) {
|
|
|
|
|
|
const filePath = join(dir, file);
|
|
|
|
|
|
const stat = fs.statSync(filePath);
|
|
|
|
|
|
if (stat.isDirectory()) {
|
|
|
|
|
|
result.push(...scanDir(filePath));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!file.endsWith(".js")) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
result.push(filePath);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-31 01:41:33 +08:00
|
|
|
|
return result;
|
2025-04-14 23:31:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default async function loadModules(dir) {
|
|
|
|
|
|
const files = scanDir(dir);
|
2026-05-31 01:41:33 +08:00
|
|
|
|
const modules = {};
|
2025-04-14 23:31:59 +08:00
|
|
|
|
for (const file of files) {
|
2026-05-31 01:41:33 +08:00
|
|
|
|
if (file === "dist/plugins/index.js" || file === "dist\\plugins\\index.js") {
|
|
|
|
|
|
continue;
|
2025-04-27 15:11:50 +08:00
|
|
|
|
}
|
2026-05-31 01:41:33 +08:00
|
|
|
|
const content = fs.readFileSync(file, "utf8");
|
|
|
|
|
|
if (content.includes(" abstract ")) {
|
|
|
|
|
|
continue;
|
2025-12-31 18:36:24 +08:00
|
|
|
|
}
|
2026-05-31 01:41:33 +08:00
|
|
|
|
const lines = content.split("\n");
|
|
|
|
|
|
let allExport = true;
|
2025-12-30 11:26:59 +08:00
|
|
|
|
for (let line of lines) {
|
2026-05-31 01:41:33 +08:00
|
|
|
|
line = line.trim();
|
2025-12-30 11:26:59 +08:00
|
|
|
|
if (!line || line.startsWith("//")) {
|
2026-05-31 01:41:33 +08:00
|
|
|
|
continue;
|
2025-12-30 11:26:59 +08:00
|
|
|
|
}
|
2026-05-31 01:41:33 +08:00
|
|
|
|
if (!line.startsWith("export ")) {
|
|
|
|
|
|
allExport = false;
|
|
|
|
|
|
break;
|
2025-12-30 11:26:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (allExport) {
|
2026-05-31 01:41:33 +08:00
|
|
|
|
continue;
|
2025-12-30 11:26:59 +08:00
|
|
|
|
}
|
2025-04-14 23:31:59 +08:00
|
|
|
|
try {
|
|
|
|
|
|
// 转换为 file:// URL(Windows 必需)
|
2026-05-31 01:41:33 +08:00
|
|
|
|
const moduleUrl = pathToFileURL(file).href;
|
|
|
|
|
|
const module = await import(moduleUrl);
|
2025-04-14 23:31:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果模块有默认导出,优先使用
|
2026-05-31 01:41:33 +08:00
|
|
|
|
modules[file] = module.default || module;
|
2025-04-14 23:31:59 +08:00
|
|
|
|
} catch (err) {
|
2026-05-31 01:41:33 +08:00
|
|
|
|
console.error(`加载模块 ${file} 失败:`, err);
|
2025-04-14 23:31:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return modules;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 01:41:33 +08:00
|
|
|
|
function isPrototypeOf(value, cls) {
|
|
|
|
|
|
return cls.prototype.isPrototypeOf(value.prototype);
|
2025-04-27 15:11:50 +08:00
|
|
|
|
}
|
2026-05-31 01:41:33 +08:00
|
|
|
|
async function genMetadata() {
|
|
|
|
|
|
const modules = await loadModules("./dist/plugins");
|
2025-04-27 15:11:50 +08:00
|
|
|
|
|
2025-05-08 23:27:46 +08:00
|
|
|
|
fs.rmSync("./metadata", { recursive: true });
|
|
|
|
|
|
fs.mkdirSync("./metadata", { recursive: true });
|
|
|
|
|
|
for (const key in modules) {
|
2026-05-31 01:41:33 +08:00
|
|
|
|
const module = modules[key];
|
|
|
|
|
|
const entry = Object.entries(module);
|
|
|
|
|
|
if (entry.length > 1) {
|
|
|
|
|
|
console.log(`[warning] 文件 ${key} 导出了 ${entry.length} 个对象: ${entry.map(([name, value]) => name).join(", ")}`);
|
2026-01-14 02:05:31 +08:00
|
|
|
|
}
|
2025-05-08 23:27:46 +08:00
|
|
|
|
for (const [name, value] of entry) {
|
|
|
|
|
|
//如果有define属性
|
2026-05-31 01:41:33 +08:00
|
|
|
|
if (value.define) {
|
2025-05-08 23:27:46 +08:00
|
|
|
|
//那么就是插件
|
2026-05-31 01:41:33 +08:00
|
|
|
|
let location = key.substring(4);
|
|
|
|
|
|
location = location.substring(0, location.length - 3);
|
|
|
|
|
|
location = location.replaceAll("\\", "/");
|
|
|
|
|
|
location += ".js";
|
|
|
|
|
|
location = `${location}`; // 从modules/plugin/plugin-service 加载 ../../plugins目录下的文件
|
2025-04-14 23:31:59 +08:00
|
|
|
|
|
2025-05-08 23:27:46 +08:00
|
|
|
|
const pluginDefine = {
|
2026-05-31 01:41:33 +08:00
|
|
|
|
...value.define,
|
|
|
|
|
|
};
|
|
|
|
|
|
let subType = "";
|
|
|
|
|
|
if (pluginDefine.accessType) {
|
|
|
|
|
|
pluginDefine.pluginType = "dnsProvider";
|
|
|
|
|
|
} else if (isPrototypeOf(value, AbstractTaskPlugin)) {
|
|
|
|
|
|
pluginDefine.pluginType = "deploy";
|
|
|
|
|
|
} else if (isPrototypeOf(value, BaseNotification)) {
|
|
|
|
|
|
pluginDefine.pluginType = "notification";
|
|
|
|
|
|
} else if (isPrototypeOf(value, BaseAccess)) {
|
|
|
|
|
|
pluginDefine.pluginType = "access";
|
|
|
|
|
|
} else if (isPrototypeOf(value, BaseAddon)) {
|
|
|
|
|
|
pluginDefine.pluginType = "addon";
|
|
|
|
|
|
subType = "_" + pluginDefine.addonType;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log(`[warning] 未知的插件类型:${pluginDefine.name}`);
|
2025-05-08 23:27:46 +08:00
|
|
|
|
}
|
2026-05-31 01:41:33 +08:00
|
|
|
|
pluginDefine.type = "builtIn";
|
|
|
|
|
|
|
|
|
|
|
|
const filePath = path.join(`./metadata/${pluginDefine.pluginType}${subType}_${pluginDefine.name}.yaml`);
|
|
|
|
|
|
|
|
|
|
|
|
pluginDefine.scriptFilePath = location;
|
|
|
|
|
|
console.log(location);
|
|
|
|
|
|
const data = yaml.dump(pluginDefine);
|
|
|
|
|
|
fs.writeFileSync(filePath, data, "utf8");
|
2025-05-08 23:27:46 +08:00
|
|
|
|
}
|
2025-04-15 00:16:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-14 23:31:59 +08:00
|
|
|
|
}
|
2026-01-08 00:39:31 +08:00
|
|
|
|
|
|
|
|
|
|
async function genPluginMd() {
|
|
|
|
|
|
const plugins = {
|
|
|
|
|
|
access: [],
|
|
|
|
|
|
deploy: [],
|
|
|
|
|
|
dnsProvider: [],
|
2026-05-31 01:41:33 +08:00
|
|
|
|
notification: [],
|
2026-01-08 00:39:31 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
plugins.access = accessRegistry.getDefineList();
|
|
|
|
|
|
plugins.deploy = pluginRegistry.getDefineList();
|
|
|
|
|
|
plugins.dnsProvider = dnsProviderRegistry.getDefineList();
|
|
|
|
|
|
plugins.notification = notificationRegistry.getDefineList();
|
|
|
|
|
|
|
2026-05-31 01:41:33 +08:00
|
|
|
|
// function genMd(list) {
|
|
|
|
|
|
// let mdContent = `<table style='width:100%'>
|
|
|
|
|
|
// <thead style='width:100%'>
|
|
|
|
|
|
// <tr >
|
|
|
|
|
|
// <th width='70'>序号</th><th width='265'>名称</th><th>说明</th>
|
|
|
|
|
|
// </tr>
|
|
|
|
|
|
// </thead>
|
|
|
|
|
|
// <tbody>
|
|
|
|
|
|
// `;
|
|
|
|
|
|
// let i = 0;
|
|
|
|
|
|
// for (const x of list) {
|
|
|
|
|
|
// i++
|
|
|
|
|
|
// mdContent += `<tr> <td>${i}.</td> <td style='font-weight: bold'>${x.title}</td> <td>${x.desc||''}</td> </tr>`;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// mdContent += `</tbody></table>`;
|
|
|
|
|
|
// return mdContent;
|
|
|
|
|
|
// }
|
2026-01-08 00:39:31 +08:00
|
|
|
|
|
|
|
|
|
|
// function genMd(list) {
|
|
|
|
|
|
// let mdContent = ``;
|
|
|
|
|
|
// let i = 0;
|
|
|
|
|
|
// for (const x of list) {
|
|
|
|
|
|
// i++
|
|
|
|
|
|
// mdContent += `${i}. **${x.title}** \n${x.desc||''} \n\n\n`;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// return mdContent;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
function genMd(list) {
|
|
|
|
|
|
let mdContent = `
|
|
|
|
|
|
| 序号 | 名称 | 说明 |
|
|
|
|
|
|
|-----|-----|-----|
|
|
|
|
|
|
`;
|
|
|
|
|
|
let i = 0;
|
|
|
|
|
|
for (const x of list) {
|
2026-05-31 01:41:33 +08:00
|
|
|
|
i++;
|
|
|
|
|
|
const desc = x.desc || "";
|
|
|
|
|
|
mdContent += `| ${i}.| **${x.title}** | ${desc.replaceAll("\n", " ")} | \n`;
|
2026-01-08 00:39:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
return mdContent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 01:41:33 +08:00
|
|
|
|
function addTableStyle() {
|
2026-01-08 00:39:31 +08:00
|
|
|
|
return `
|
|
|
|
|
|
<style module>
|
|
|
|
|
|
table th:first-of-type {
|
|
|
|
|
|
width: 65px;
|
|
|
|
|
|
}
|
|
|
|
|
|
table th:nth-of-type(2) {
|
|
|
|
|
|
width: 240px;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
2026-05-31 01:41:33 +08:00
|
|
|
|
`;
|
2026-01-08 00:39:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mdContent = "";
|
|
|
|
|
|
mdContent = "# 授权列表\n";
|
|
|
|
|
|
mdContent += genMd(plugins.access);
|
2026-05-31 01:41:33 +08:00
|
|
|
|
mdContent += addTableStyle();
|
2026-01-08 00:39:31 +08:00
|
|
|
|
fs.writeFileSync("../../../docs/guide/plugins/access.md", mdContent);
|
|
|
|
|
|
|
|
|
|
|
|
mdContent = "# DNS提供商\n";
|
|
|
|
|
|
mdContent += genMd(plugins.dnsProvider);
|
2026-05-31 01:41:33 +08:00
|
|
|
|
mdContent += addTableStyle();
|
2026-01-08 00:39:31 +08:00
|
|
|
|
fs.writeFileSync("../../../docs/guide/plugins/dns-provider.md", mdContent);
|
|
|
|
|
|
|
|
|
|
|
|
mdContent = "# 通知插件\n";
|
|
|
|
|
|
mdContent += genMd(plugins.notification);
|
2026-05-31 01:41:33 +08:00
|
|
|
|
mdContent += addTableStyle();
|
2026-01-08 00:39:31 +08:00
|
|
|
|
fs.writeFileSync("../../../docs/guide/plugins/notification.md", mdContent);
|
|
|
|
|
|
|
|
|
|
|
|
mdContent = "# 任务插件\n";
|
2026-05-31 01:41:33 +08:00
|
|
|
|
mdContent += `共 \`${plugins.deploy.length}\` 款任务插件 \n`;
|
|
|
|
|
|
let index = 0;
|
2026-01-08 00:39:31 +08:00
|
|
|
|
for (const key in pluginGroups) {
|
2026-05-31 01:41:33 +08:00
|
|
|
|
index++;
|
2026-01-08 00:39:31 +08:00
|
|
|
|
const group = pluginGroups[key];
|
|
|
|
|
|
mdContent += `## ${index}. ${group.title}\n`;
|
|
|
|
|
|
mdContent += genMd(group.plugins);
|
|
|
|
|
|
}
|
2026-05-31 01:41:33 +08:00
|
|
|
|
mdContent += addTableStyle();
|
2026-01-08 00:39:31 +08:00
|
|
|
|
fs.writeFileSync("../../../docs/guide/plugins/deploy.md", mdContent);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-28 23:34:08 +08:00
|
|
|
|
// import why from 'why-is-node-running'
|
|
|
|
|
|
// setTimeout(() => why(), 100); // 延迟打印原因
|
2026-05-31 01:41:33 +08:00
|
|
|
|
async function main() {
|
|
|
|
|
|
await genMetadata();
|
|
|
|
|
|
console.log("genMetadata success");
|
2026-01-14 00:12:43 +08:00
|
|
|
|
// 获取args genmd
|
2026-05-31 01:41:33 +08:00
|
|
|
|
const args = process.argv.slice(2);
|
|
|
|
|
|
if (!args.includes("docoff")) {
|
|
|
|
|
|
await genPluginMd();
|
|
|
|
|
|
console.log("genPluginMd success");
|
2026-01-14 00:12:43 +08:00
|
|
|
|
}
|
2026-05-31 01:41:33 +08:00
|
|
|
|
process.exit();
|
2026-01-08 00:39:31 +08:00
|
|
|
|
}
|
2026-01-14 00:12:43 +08:00
|
|
|
|
|
2026-05-31 01:41:33 +08:00
|
|
|
|
main();
|