chore: 完善第三方依赖动态加载

This commit is contained in:
xiaojunnuo
2026-06-20 00:35:13 +08:00
parent 01568ca148
commit 42fcb91f2e
70 changed files with 528 additions and 503 deletions
@@ -31,7 +31,6 @@ process.on("uncaughtException", error => {
logger.error("您的服务器不支持监听IPV6格式的地址(::),请配置环境变量: certd_koa_hostname=0.0.0.0");
}
});
// function startHeapLog() {
// function format(bytes: any) {
// return (bytes / 1024 / 1024).toFixed(2) + ' MB';
@@ -7,6 +7,7 @@ import { getEmailSettings } from "../../../modules/sys/settings/fix.js";
import { http, logger, utils } from "@certd/basic";
import { CodeService } from "../../../modules/basic/service/code-service.js";
import { SmsServiceFactory } from "../../../modules/basic/sms/factory.js";
import { RuntimeDepsService } from "../../../modules/runtime-deps/runtime-deps-service.js";
/**
*/
@@ -23,6 +24,8 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
codeService: CodeService;
@Inject()
addonService: AddonService;
@Inject()
runtimeDepsService: RuntimeDepsService;
getService() {
return this.service;
@@ -216,4 +219,10 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
const list = await addonRegistry.getDefineList("oauth");
return this.ok(list);
}
@Post("/clearRuntimeDeps", { description: "sys:settings:edit" })
async clearRuntimeDeps() {
await this.runtimeDepsService.clearRuntimeDeps();
return this.ok(true);
}
}
@@ -72,7 +72,9 @@ export class TencentSmsService implements ISmsService {
this.ctx = ctx;
if (this.ctx.runtimeDepsService) {
await this.ctx.runtimeDepsService.ensureDependencies({
"tencentcloud-sdk-nodejs": "^4.1.112",
dependencies: {
"tencentcloud-sdk-nodejs": "^4.1.112",
},
});
}
}
@@ -332,7 +332,7 @@ export class PluginService extends BaseService<PluginEntity> {
if (!isBareModuleSpecifier(modulePath)) {
return await importLocalModule(modulePath);
}
return await this.runtimeDepsService.importRuntime(modulePath);
return await this.runtimeDepsService.importRuntime(modulePath, logger);
}
private async getPluginClassFromFile(item: any) {
@@ -38,9 +38,7 @@ export class NpmRegistryResolver {
const candidates = (config?.candidates || []).filter(Boolean);
const probes = await Promise.allSettled(candidates.map(registryUrl => this.probe(registryUrl)));
const okList = probes
.map(item => (item.status === "fulfilled" ? item.value : null))
.filter((item): item is RegistryProbeResult => !!item && item.ok);
const okList = probes.map(item => (item.status === "fulfilled" ? item.value : null)).filter((item): item is RegistryProbeResult => !!item && item.ok);
if (okList.length > 0) {
okList.sort((a, b) => a.elapsedMs - b.elapsedMs);
@@ -46,6 +46,7 @@ describe("RuntimeDepsService", () => {
}
assert.equal(args[0], "install");
assert.ok(args.includes("--ignore-workspace"));
assert.ok(args.includes("--no-frozen-lockfile"));
return { stdout: "", stderr: "", code: 0 };
},
} as any;
@@ -295,7 +296,7 @@ describe("RuntimeDepsService", () => {
target: async () => ({} as any),
});
try {
await service.ensureRuntimeDependencies("plugin:runtimeDepsKey");
await service.ensureRuntimeDependencies({ pluginKeys: "plugin:runtimeDepsKey" });
const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8"));
assert.deepEqual(manifest.dependencies, { keyed: "^1.0.0" });
@@ -332,7 +333,7 @@ describe("RuntimeDepsService", () => {
target: async () => ({} as any),
});
try {
await service.ensureRuntimeDependencies(["access:runtimeDepsArrayAccess", "addon:captcha:runtimeDepsArrayAddon"]);
await service.ensureRuntimeDependencies({ pluginKeys: ["access:runtimeDepsArrayAccess", "addon:captcha:runtimeDepsArrayAddon"] });
const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8"));
assert.deepEqual(manifest.dependencies, {
@@ -375,10 +376,7 @@ describe("RuntimeDepsService", () => {
it("reports bare dependent plugin names as invalid format", () => {
const service = new RuntimeDepsService();
assert.throws(
() => service.resolvePluginDependencies({ name: "deploy", pluginType: "deploy", dependPlugins: { runtimeDepsBareName: "*" } }),
/插件依赖格式错误/
);
assert.throws(() => service.resolvePluginDependencies({ name: "deploy", pluginType: "deploy", dependPlugins: { runtimeDepsBareName: "*" } }), /插件依赖格式错误/);
});
it("records runtime install environment state", async () => {
@@ -438,10 +436,7 @@ describe("RuntimeDepsService", () => {
serviceA.commandRunner = commandRunner as any;
serviceB.commandRunner = commandRunner as any;
await Promise.all([
serviceA.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }]),
serviceB.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }]),
]);
await Promise.all([serviceA.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }]), serviceB.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }])]);
assert.equal(installCount, 1);
});
@@ -487,4 +482,27 @@ describe("RuntimeDepsService", () => {
}
}
});
it("clears runtime dependency directory", async () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-clear-"));
const runtimeRootDir = path.join(rootDir, ".runtime-deps");
fs.mkdirSync(path.join(runtimeRootDir, "node_modules", "foo"), { recursive: true });
fs.writeFileSync(path.join(runtimeRootDir, "package.json"), "{}", "utf8");
const service = new RuntimeDepsService();
service.runtimeDepsRootDir = runtimeRootDir;
service.installTimeoutMs = 1000;
await service.clearRuntimeDeps();
assert.equal(fs.existsSync(runtimeRootDir), true);
assert.equal(fs.readdirSync(runtimeRootDir).length, 0);
});
it("rejects clearing unexpected runtime dependency path", async () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-clear-invalid-"));
const service = new RuntimeDepsService();
service.runtimeDepsRootDir = rootDir;
await assert.rejects(() => service.clearRuntimeDeps(), /动态依赖目录配置异常/);
});
});
@@ -9,6 +9,7 @@ import { NpmRegistryResolver } from "./npm-registry-resolver.js";
import { Registry, accessRegistry, notificationRegistry, pluginRegistry } from "@certd/pipeline";
import { dnsProviderRegistry } from "@certd/plugin-lib";
import { addonRegistry } from "@certd/lib-server";
import { logger, ILogger } from "@certd/basic";
export type RuntimeDependencyPluginDefine = {
name: string;
@@ -72,12 +73,14 @@ type CommandRunnerResult = {
};
type CommandRunner = {
// @ts-ignore
run(command: string, args: string[], options: { cwd: string; timeoutMs: number; env?: NodeJS.ProcessEnv }): Promise<CommandRunnerResult>;
};
const PROCESS_LOCKS = new Map<string, Promise<unknown>>();
class DefaultCommandRunner implements CommandRunner {
// @ts-ignore
async run(command: string, args: string[], options: { cwd: string; timeoutMs: number; env?: NodeJS.ProcessEnv }): Promise<CommandRunnerResult> {
return await new Promise<CommandRunnerResult>(resolve => {
let stdout = "";
@@ -87,6 +90,7 @@ class DefaultCommandRunner implements CommandRunner {
cwd: options.cwd,
env: options.env,
windowsHide: true,
// @ts-ignore
shell: process.platform === "win32",
});
@@ -182,18 +186,18 @@ export class RuntimeDepsService {
return { dependencies: merged, conflicts };
}
async ensureInstalled(plugins: RuntimeDependencyPluginDefine[]): Promise<InstallResult> {
async ensureInstalled(options: { plugins: RuntimeDependencyPluginDefine[]; logger?: ILogger }): Promise<InstallResult> {
const { plugins, logger: log } = options;
const { dependencies, conflicts } = this.resolveDependenciesFromPlugins(plugins);
if (conflicts.length > 0) {
const conflict = conflicts[0];
throw new Error(
`动态依赖版本冲突: ${conflict.packageName} => ${conflict.ranges.map(item => `${item.pluginName}:${item.range}`).join(", ")}`
);
throw new Error(`动态依赖版本冲突: ${conflict.packageName} => ${conflict.ranges.map(item => `${item.pluginName}:${item.range}`).join(", ")}`);
}
return await this.ensureDependencies(dependencies);
return await this.ensureDependencies({ dependencies, logger: log });
}
async ensureDependencies(dependencies: Record<string, string>): Promise<InstallResult> {
async ensureDependencies(options: { dependencies: Record<string, string>; logger?: ILogger }): Promise<InstallResult> {
const { dependencies, logger: log } = options;
if (!this.enabled) {
return {
registryUrl: "",
@@ -209,7 +213,7 @@ export class RuntimeDepsService {
const dependenciesHash = this.createDependenciesHash(dependencies);
let installPromise = this.installPromises.get(dependenciesHash);
if (!installPromise) {
installPromise = this.doEnsureInstalled(dependencies).catch(error => {
installPromise = this.doEnsureInstalled({ dependencies, logger: log }).catch(error => {
this.installPromises.delete(dependenciesHash);
throw error;
});
@@ -223,7 +227,8 @@ export class RuntimeDepsService {
return this.collectDependencies(expandedPlugins);
}
async ensureRuntimeDependencies(pluginKeys: string | string[]): Promise<InstallResult> {
async ensureRuntimeDependencies(options: { pluginKeys: string | string[]; logger?: ILogger }): Promise<InstallResult> {
const { pluginKeys, logger: log } = options;
const keys = Array.isArray(pluginKeys) ? pluginKeys : [pluginKeys];
const pluginDefines = keys.map(pluginKey => this.getDefineByPluginKey(pluginKey));
if (pluginDefines.every(pluginDefine => !pluginDefine.dependPackages && !pluginDefine.dependPlugins)) {
@@ -233,19 +238,23 @@ export class RuntimeDepsService {
};
}
const expandedPluginDefines = pluginDefines.flatMap(pluginDefine => this.resolvePluginDependencies(pluginDefine));
return await this.ensureInstalled(expandedPluginDefines);
return await this.ensureInstalled({ plugins: expandedPluginDefines, logger: log });
}
private async doEnsureInstalled(dependencies: Record<string, string>): Promise<InstallResult> {
private async doEnsureInstalled(options: { dependencies: Record<string, string>; logger?: ILogger }): Promise<InstallResult> {
let { dependencies } = options;
const log = options.logger || logger;
return await this.withInstallLock(async () => {
const rootDir = this.getRuntimeDepsRootDir();
const packageJsonPath = path.join(rootDir, "package.json");
const lockPath = path.join(rootDir, "pnpm-lock.yaml");
log.info(`第三方依赖安装: ${JSON.stringify(dependencies)}`);
dependencies = this.mergeInstalledDependencies(this.readManifestDependencies(packageJsonPath), dependencies);
const dependenciesHash = this.createDependenciesHash(dependencies);
const statePath = path.join(rootDir, "install-state.json");
const currentState = this.readInstallState(statePath);
if (currentState?.dependenciesHash === dependenciesHash && fs.existsSync(path.join(rootDir, "node_modules"))) {
log.info("第三方依赖已安装");
return { registryUrl: currentState.registryUrl || "", packageJsonPath };
}
const manifest = {
@@ -260,10 +269,12 @@ export class RuntimeDepsService {
const env = this.buildChildEnv(registryUrl);
const command = this.getPnpmCommand();
const pnpmVersion = await this.getPnpmVersion(command, env);
const args = ["install", "--prod", "--ignore-scripts", "--ignore-workspace", "--reporter=append-only"];
const args = ["install", "--prod", "--ignore-scripts", "--ignore-workspace", "--no-frozen-lockfile", "--reporter=append-only"];
if (registryUrl) {
args.push(`--registry=${registryUrl}`);
}
log.info(`开始安装第三方依赖: ${Object.keys(dependencies).join(", ")}`);
const result = await this.commandRunner.run(command, args, {
cwd: rootDir,
timeoutMs: this.installTimeoutMs,
@@ -277,6 +288,7 @@ export class RuntimeDepsService {
failedAt: new Date().toISOString(),
registryUrl,
dependenciesHash,
// @ts-ignore
nodeVersion: process.version,
pnpmVersion,
lockFileExists: fs.existsSync(lockPath),
@@ -288,35 +300,37 @@ export class RuntimeDepsService {
installedAt: new Date().toISOString(),
registryUrl,
dependenciesHash,
// @ts-ignore
nodeVersion: process.version,
pnpmVersion,
lockFileExists: fs.existsSync(lockPath),
});
log.info("第三方依赖安装完成");
return { registryUrl, packageJsonPath };
});
}
async importRuntime(specifier: string) {
async importRuntime(specifier: string,logger?:ILogger) {
if (this.isNativeImportSpecifier(specifier)) {
return await import(specifier);
}
const resolved = await this.resolveImportSpecifier(specifier);
const resolved = await this.resolveImportSpecifier(specifier,logger);
return await import(pathToFileURL(resolved).href);
}
private async resolveImportSpecifier(specifier: string) {
private async resolveImportSpecifier(specifier: string,logger?:ILogger) {
try {
return this.resolveRuntimeSpecifier(specifier).resolved;
} catch (runtimeError: any) {
if (!this.isModuleNotFoundError(runtimeError)) {
throw runtimeError;
}
return await this.resolveMissingRuntimeSpecifier(specifier, runtimeError);
return await this.resolveMissingRuntimeSpecifier(specifier, runtimeError,logger);
}
}
private async resolveMissingRuntimeSpecifier(specifier: string, runtimeError: any) {
private async resolveMissingRuntimeSpecifier(specifier: string, runtimeError: any,logger?:ILogger) {
const packageName = this.parsePackageName(specifier);
const lazyRange = this.lazyDependencies?.[packageName];
if (!lazyRange) {
@@ -327,7 +341,7 @@ export class RuntimeDepsService {
}
}
try {
await this.ensureLazyDependency(packageName);
await this.ensureLazyDependency(packageName,logger);
return this.resolveRuntimeSpecifier(specifier).resolved;
} catch (lazyError: any) {
return this.resolveProjectSpecifier(specifier, lazyError).resolved;
@@ -378,7 +392,7 @@ export class RuntimeDepsService {
return parts[0];
}
private async ensureLazyDependency(packageName: string) {
private async ensureLazyDependency(packageName: string,logger?:ILogger) {
const range = this.lazyDependencies?.[packageName];
if (!range) {
throw new Error(`动态依赖未安装且未配置懒加载版本: ${packageName}`);
@@ -386,7 +400,7 @@ export class RuntimeDepsService {
const dependencies = {
[packageName]: range,
};
await this.ensureDependencies(dependencies);
await this.ensureDependencies({ dependencies,logger });
}
private isModuleNotFoundError(error: any) {
@@ -485,6 +499,7 @@ export class RuntimeDepsService {
while (true) {
try {
const fd = fs.openSync(lockFile, "wx");
// @ts-ignore
fs.writeFileSync(fd, JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }), "utf8");
return fd;
} catch (error: any) {
@@ -508,6 +523,28 @@ export class RuntimeDepsService {
}
}
async clearRuntimeDeps() {
const rootDir = this.getRuntimeDepsRootDir();
const normalizedRootDir = path.normalize(rootDir);
if (!normalizedRootDir.endsWith(path.normalize(".runtime-deps"))) {
throw new Error(`动态依赖目录配置异常,拒绝清理: ${rootDir}`);
}
await this.withInstallLock(async () => {
if (fs.existsSync(rootDir)) {
const entries = fs.readdirSync(rootDir);
for (const entry of entries) {
if (entry === ".install.lock") {
continue;
}
const entryPath = path.join(rootDir, entry);
fs.rmSync(entryPath, { recursive: true, force: true });
}
}
this.installPromises.clear();
return undefined;
});
}
private readInstallState(statePath: string): any {
if (!fs.existsSync(statePath)) {
return null;
@@ -547,6 +584,7 @@ export class RuntimeDepsService {
return dependencies;
}
// @ts-ignore
private async getPnpmVersion(command: string, env: NodeJS.ProcessEnv) {
const rootDir = this.getRuntimeDepsRootDir();
const result = await this.commandRunner.run(command, ["--version"], {
@@ -568,6 +606,7 @@ export class RuntimeDepsService {
}
private buildChildEnv(registryUrl: string) {
// @ts-ignore
const env = { ...process.env };
for (const key of ["NODE_OPTIONS", "VSCODE_INSPECTOR_OPTIONS", "NODE_INSPECTOR_PORT", "NODE_DEBUG"]) {
if (!env[key]) {
@@ -41,10 +41,7 @@ export class NetTestService {
// 判断测试是否成功
const normalizedOutput = output.toLowerCase();
const success = this.isWindows()
? normalizedOutput.includes("端口连接成功")
: normalizedOutput.includes("succeeded!") || normalizedOutput.includes("connected to") || normalizedOutput.includes(" open");
const success = this.isWindows() ? normalizedOutput.includes("端口连接成功") : normalizedOutput.includes("succeeded!") || normalizedOutput.includes("connected to") || normalizedOutput.includes(" open");
// 处理结果
return {
@@ -16,7 +16,7 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
async onInstance() {
const access: AliyunAccess = this.ctx.access as AliyunAccess;
this.client = new AliyunClient({ logger: this.logger });
this.client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) });
await this.client.init({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
@@ -241,7 +241,7 @@ export class DeployCertToAliyunAckPlugin extends AbstractTaskPlugin {
}
async getClient(aliyunProvider: any, regionId: string) {
const client = new AliyunClient({ logger: this.logger, useROAClient: true });
const client = new AliyunClient({ logger: this.logger, useROAClient: true, importRuntime: aliyunProvider.importRuntime.bind(aliyunProvider) });
await client.init({
accessKeyId: aliyunProvider.accessKeyId,
accessKeySecret: aliyunProvider.accessKeySecret,
@@ -128,7 +128,7 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin {
async onInstance() {}
async getLBClient(access: AliyunAccess, region: string) {
const client = new AliyunClient({ logger: this.logger });
const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) });
const version = "2020-06-16";
await client.init({
@@ -231,7 +231,7 @@ export class DeployCertToAliyunApig extends AbstractTaskPlugin {
return {
list: records,
total: res?.data?.totalSize || 0,
}
};
}
async onGetRegionList(data: any) {
@@ -134,7 +134,7 @@ export class DeployCertToAliyunApiGateway extends AbstractTaskPlugin {
const access = await this.getAccess<AliyunAccess>(this.accessId);
const client = access.getClient(this.regionEndpoint);
const pager = new Pager(data)
const pager = new Pager(data);
const res = await client.doRequest({
// 接口名称
action: "DescribeApiGroups",
@@ -162,7 +162,7 @@ export class DeployCertToAliyunApiGateway extends AbstractTaskPlugin {
return {
list: records,
total: res?.TotalCount || 0,
}
};
}
async onGetDomainList(data: any) {
@@ -209,7 +209,7 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
}
async getClient(access: AliyunAccess) {
const client = new AliyunClient({ logger: this.logger });
const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) });
await client.init({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
@@ -143,7 +143,7 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
}
async getClient(access: AliyunAccess) {
const client = new AliyunClient({ logger: this.logger });
const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) });
await client.init({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
@@ -137,8 +137,8 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin {
const client = await this.getClient(access);
const $Util = await import("@alicloud/tea-util");
const $OpenApi = await import("@alicloud/openapi-client");
const $Util = await access.importRuntime("@alicloud/tea-util");
const $OpenApi = await access.importRuntime("@alicloud/openapi-client");
let privateKey = this.cert.key;
try {
@@ -204,7 +204,7 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin {
}
async getClient(access: AliyunAccess) {
const $OpenApi = await import("@alicloud/openapi-client");
const $OpenApi = await access.importRuntime("@alicloud/openapi-client");
const config = new $OpenApi.Config({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
@@ -221,8 +221,8 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin {
const access = await this.getAccess<AliyunAccess>(this.accessId);
const client = await this.getClient(access);
const $OpenApi = await import("@alicloud/openapi-client");
const $Util = await import("@alicloud/tea-util");
const $OpenApi = await access.importRuntime("@alicloud/openapi-client");
const $Util = await access.importRuntime("@alicloud/tea-util");
const params = new $OpenApi.Params({
// 接口名称
action: "ListCustomDomains",
@@ -117,7 +117,7 @@ export class AliyunDeployCertToNLB extends AbstractTaskPlugin {
async onInstance() {}
async getLBClient(access: AliyunAccess, region: string) {
const client = new AliyunClient({ logger: this.logger });
const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) });
const version = "2022-04-30";
await client.init({
@@ -196,7 +196,7 @@ export class DeployCertToAliyunOSS extends AbstractTaskPlugin {
async getClient(access: AliyunAccess) {
// @ts-ignore
const OSS = await import("ali-oss");
const OSS = await access.importRuntime("ali-oss");
return new OSS.default({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
@@ -130,7 +130,7 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin {
async onInstance() {}
async getLBClient(access: AliyunAccess, region: string) {
const client = new AliyunClient({ logger: this.logger });
const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) });
const version = "2014-05-15";
await client.init({
accessKeyId: access.accessKeyId,
@@ -103,7 +103,7 @@ export class AliyunDeployCertToWafCloud extends AbstractTaskPlugin {
async onInstance() {}
async getWafClient(access: AliyunAccess) {
const client = new AliyunClient({ logger: this.logger });
const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) });
await client.init({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
@@ -115,7 +115,7 @@ export class AliyunDeployCertToWaf extends AbstractTaskPlugin {
async onInstance() {}
async getWafClient(access: AliyunAccess) {
const client = new AliyunClient({ logger: this.logger });
const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) });
await client.init({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
@@ -15,7 +15,7 @@ export class AwsIAMClient {
}
async importCertificate(certInfo: CertInfo, certName: string) {
// 创建 IAM 客户端
const { IAMClient, UploadServerCertificateCommand } = await import("@aws-sdk/client-iam");
const { IAMClient, UploadServerCertificateCommand } = await this.access.importRuntime("@aws-sdk/client-iam");
const iamClient = new IAMClient({
region: this.region, // 替换为您的 AWS 区域
credentials: {
@@ -84,7 +84,7 @@ export class AwsCNDeployToCloudFront extends AbstractTaskPlugin {
}
//部署到CloudFront
const { CloudFrontClient, UpdateDistributionCommand, GetDistributionConfigCommand } = await import("@aws-sdk/client-cloudfront");
const { CloudFrontClient, UpdateDistributionCommand, GetDistributionConfigCommand } = await this.importRuntime("@aws-sdk/client-cloudfront");
const cloudFrontClient = new CloudFrontClient({
region: this.region,
credentials: {
@@ -135,7 +135,7 @@ export class AwsCNDeployToCloudFront extends AbstractTaskPlugin {
}
const access = await this.getAccess<AwsCNAccess>(this.accessId);
const { CloudFrontClient, ListDistributionsCommand } = await import("@aws-sdk/client-cloudfront");
const { CloudFrontClient, ListDistributionsCommand } = await this.importRuntime("@aws-sdk/client-cloudfront");
const cloudFrontClient = new CloudFrontClient({
region: this.region,
credentials: {
@@ -21,7 +21,7 @@ export class AwsClient {
}
async importCertificate(certInfo: CertInfo) {
// 创建 ACM 客户端
const { ACMClient, ImportCertificateCommand } = await import("@aws-sdk/client-acm");
const { ACMClient, ImportCertificateCommand } = await this.access.importRuntime("@aws-sdk/client-acm");
const acmClient = new ACMClient({
region: this.region, // 替换为您的 AWS 区域
credentials: {
@@ -49,7 +49,7 @@ export class AwsClient {
}
async getCallerIdentity() {
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
const { STSClient, GetCallerIdentityCommand } = await this.access.importRuntime("@aws-sdk/client-sts");
const client = new STSClient({
region: this.access.region || "us-east-1",
@@ -68,7 +68,7 @@ export class AwsClient {
}
async route53ClientGet() {
const { Route53Client } = await import("@aws-sdk/client-route-53");
const { Route53Client } = await this.access.importRuntime("@aws-sdk/client-route-53");
return new Route53Client({
region: this.region,
credentials: {
@@ -88,7 +88,7 @@ export class AwsClient {
};
}
async route53ListHostedZones(name: string): Promise<{ Id: string; Name: string }[]> {
const { ListHostedZonesByNameCommand } = await import("@aws-sdk/client-route-53"); // ES Modules import
const { ListHostedZonesByNameCommand } = await this.access.importRuntime("@aws-sdk/client-route-53"); // ES Modules import
const client = await this.route53ClientGet();
const input = {
@@ -96,7 +96,7 @@ export class AwsClient {
DNSName: name,
};
const command = new ListHostedZonesByNameCommand(input);
const response = await this.doRequest(() => client.send(command));
const response: any = await this.doRequest(() => client.send(command));
if (response.HostedZones.length === 0) {
throw new Error(`找不到 HostedZone ${name}`);
}
@@ -105,7 +105,7 @@ export class AwsClient {
}
async route53ListHostedZonesPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
const { ListHostedZonesByNameCommand } = await import("@aws-sdk/client-route-53"); // ES Modules import
const { ListHostedZonesByNameCommand } = await this.access.importRuntime("@aws-sdk/client-route-53"); // ES Modules import
const client = await this.route53ClientGet();
const input: any = {
@@ -116,7 +116,7 @@ export class AwsClient {
input.DNSName = req.searchKey;
}
const command = new ListHostedZonesByNameCommand(input);
const response = await this.doRequest(() => client.send(command));
const response: any = await this.doRequest(() => client.send(command));
let list: any[] = response.HostedZones || [];
list = list.map((item: any) => ({
id: item.Id.replace("/hostedzone/", ""),
@@ -129,7 +129,7 @@ export class AwsClient {
}
async route53ChangeRecord(req: { hostedZoneId: string; fullRecord: string; type: string; value: string; action: "UPSERT" | "DELETE" }) {
const { ChangeResourceRecordSetsCommand } = await import("@aws-sdk/client-route-53"); // ES Modules import
const { ChangeResourceRecordSetsCommand } = await this.access.importRuntime("@aws-sdk/client-route-53"); // ES Modules import
// const { Route53Client, ChangeResourceRecordSetsCommand } = require("@aws-sdk/client-route-53"); // CommonJS import
// import type { Route53ClientConfig } from "@aws-sdk/client-route-53";
const client = await this.route53ClientGet();
@@ -79,7 +79,7 @@ export class AwsDeployToCloudFront extends AbstractTaskPlugin {
}
//部署到CloudFront
const { CloudFrontClient, UpdateDistributionCommand, GetDistributionConfigCommand } = await import("@aws-sdk/client-cloudfront");
const { CloudFrontClient, UpdateDistributionCommand, GetDistributionConfigCommand } = await this.importRuntime("@aws-sdk/client-cloudfront");
const cloudFrontClient = new CloudFrontClient({
region: this.region,
credentials: {
@@ -133,7 +133,7 @@ export class AwsDeployToCloudFront extends AbstractTaskPlugin {
}
const access = await this.getAccess<AwsAccess>(this.accessId);
const { CloudFrontClient, ListDistributionsCommand } = await import("@aws-sdk/client-cloudfront");
const { CloudFrontClient, ListDistributionsCommand } = await this.importRuntime("@aws-sdk/client-cloudfront");
const cloudFrontClient = new CloudFrontClient({
region: this.region,
credentials: {
@@ -122,7 +122,7 @@ export class AzureAccess extends BaseAccess {
this.ctx.logger.info(`找到 DNS 区域: ${matchingZone.name}, ID: ${matchingZone.id}`);
return {
id: matchingZone.id.split("/").pop()!,
id: matchingZone.id.split("/").pop() || "",
name: matchingZone.name,
};
}
@@ -136,7 +136,7 @@ export class AzureAccess extends BaseAccess {
}
list = list.map((item: any) => ({
id: item.id.split("/").pop()!,
id: item.id.split("/").pop() || "",
domain: item.name,
}));
@@ -96,7 +96,7 @@ export class CmccClient {
async getToken(): Promise<string> {
// 检查是否有有效的token
if (this.isTokenValid()) {
return this.token!;
return this.token;
}
const datetime = this.getCurrentIsoTime();
@@ -36,10 +36,10 @@ export class DynadotDnsProvider extends AbstractDnsProvider<DynadotRecord> {
record_type: type.toLowerCase(),
record_value1: value,
record_value2: "",
}
]
},
];
await this.postRecords(domain, {subRecords, mainRecords: [], addToCurrent: true});
await this.postRecords(domain, { subRecords, mainRecords: [], addToCurrent: true });
this.logger.info("添加域名解析成功:", fullRecord, value);
return {
@@ -79,8 +79,8 @@ export class DynadotDnsProvider extends AbstractDnsProvider<DynadotRecord> {
record_type: "txt",
record_value1: "init_txt_by_certd",
record_value2: "",
}
]
},
];
}
await this.postRecords(domain, {
@@ -132,7 +132,7 @@ export class DynadotDnsProvider extends AbstractDnsProvider<DynadotRecord> {
return { mainRecords, subRecords };
}
private async postRecords(domain: string, records: { mainRecords: MainRecordItem[]; subRecords: SubRecordItem[] ,addToCurrent: boolean}): Promise<void> {
private async postRecords(domain: string, records: { mainRecords: MainRecordItem[]; subRecords: SubRecordItem[]; addToCurrent: boolean }): Promise<void> {
await this.access.doRequest({
method: "POST",
path: `/restful/v2/domains/${domain}/records`,
@@ -101,7 +101,7 @@ export class AliossAccess extends BaseAccess {
async getClient(access: AliyunAccess) {
// @ts-ignore
const OSS = await import("ali-oss");
const OSS = await access.importRuntime("ali-oss");
return new OSS.default({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
@@ -45,7 +45,7 @@ export class AliyunAccess extends BaseAccess {
}
async getStsClient() {
const StsClient = await import("@alicloud/sts-sdk");
const StsClient = await this.importRuntime("@alicloud/sts-sdk");
// 配置凭证
const sts = new StsClient.default({
@@ -30,7 +30,7 @@ export class AliyunClientV2 {
if (this.client) {
return this.client;
}
const $OpenApi = await import("@alicloud/openapi-client");
const $OpenApi = await this.access.importRuntime("@alicloud/openapi-client");
// const Credential = await import("@alicloud/credentials");
// //@ts-ignore
// const credential = new Credential.default.default({
@@ -52,9 +52,9 @@ export class AliyunClientV2 {
async doRequest(req: AliyunClientV2Req) {
const client = await this.getClient();
const $OpenApi = await import("@alicloud/openapi-client");
const $Util = await import("@alicloud/tea-util");
const OpenApiUtil = await import("@alicloud/openapi-util");
const $OpenApi = await this.access.importRuntime("@alicloud/openapi-client");
const $Util = await this.access.importRuntime("@alicloud/tea-util");
const OpenApiUtil = await this.access.importRuntime("@alicloud/openapi-util");
const params = new $OpenApi.Params({
// 接口名称
action: req.action,
@@ -1,14 +1,17 @@
import { getGlobalAgents, ILogger } from "@certd/basic";
import { ImportRuntime } from "@certd/pipeline";
export class AliyunClient {
client: any;
logger: ILogger;
agent: any;
useROAClient: boolean;
importRuntime: ImportRuntime;
constructor(opts: { logger: ILogger; useROAClient?: boolean }) {
constructor(opts: { logger: ILogger; useROAClient?: boolean; importRuntime?: ImportRuntime }) {
this.logger = opts.logger;
this.useROAClient = opts.useROAClient || false;
this.importRuntime = opts.importRuntime || (async (specifier: string) => await import(specifier));
const agents = getGlobalAgents();
this.agent = agents.httpsAgent;
}
@@ -17,13 +20,12 @@ export class AliyunClient {
if (this.useROAClient) {
return await this.getROAClient();
}
const Core = await import("@alicloud/pop-core");
const Core = await this.importRuntime("@alicloud/pop-core");
return Core.default;
}
async getROAClient() {
const Core = await import("@alicloud/pop-core");
console.log("aliyun sdk", Core);
const Core = await this.importRuntime("@alicloud/pop-core");
// @ts-ignore
return Core.ROAClient;
}
@@ -17,7 +17,7 @@ export class AliossClient {
return;
}
// @ts-ignore
const OSS = await import("ali-oss");
const OSS = await this.access.importRuntime("ali-oss");
const ossClient = new OSS.default({
accessKeyId: this.access.accessKeyId,
accessKeySecret: this.access.accessKeySecret,
@@ -55,7 +55,7 @@ export class AliyunSslClient {
async getClient() {
const access = this.opts.access;
const client = new AliyunClient({ logger: this.opts.logger });
const client = new AliyunClient({ logger: this.opts.logger, importRuntime: access.importRuntime.bind(access) });
let endpoint = this.opts.endpoint || "cas.aliyuncs.com";
if (this.opts.endpoint == null && this.opts.region) {
@@ -16,7 +16,7 @@ export default class S3OssClientImpl extends BaseOssClient<S3Access> {
async init() {
// import { S3Client } from "@aws-sdk/client-s3";
//@ts-ignore
const { S3Client } = await import("@aws-sdk/client-s3");
const { S3Client } = await this.access.importRuntime("@aws-sdk/client-s3");
this.client = new S3Client({
forcePathStyle: true,
//@ts-ignore
@@ -32,7 +32,7 @@ export default class S3OssClientImpl extends BaseOssClient<S3Access> {
async download(filePath: string, savePath: string): Promise<void> {
// @ts-ignore
const { GetObjectCommand } = await import("@aws-sdk/client-s3");
const { GetObjectCommand } = await this.access.importRuntime("@aws-sdk/client-s3");
const key = path.join(this.rootDir, filePath);
const params = {
Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'.
@@ -47,7 +47,7 @@ export default class S3OssClientImpl extends BaseOssClient<S3Access> {
async listDir(dir: string): Promise<OssFileItem[]> {
// @ts-ignore
const { ListObjectsCommand } = await import("@aws-sdk/client-s3");
const { ListObjectsCommand } = await this.access.importRuntime("@aws-sdk/client-s3");
const dirKey = this.join(this.rootDir, dir);
const params = {
Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'.
@@ -67,7 +67,7 @@ export default class S3OssClientImpl extends BaseOssClient<S3Access> {
}
async upload(filePath: string, fileContent: Buffer | string) {
// @ts-ignore
const { PutObjectCommand } = await import("@aws-sdk/client-s3");
const { PutObjectCommand } = await this.access.importRuntime("@aws-sdk/client-s3");
const key = path.join(this.rootDir, filePath);
this.logger.info(`开始上传文件: ${key}`);
const params = {
@@ -88,7 +88,7 @@ export default class S3OssClientImpl extends BaseOssClient<S3Access> {
}
const key = filePath;
// @ts-ignore
const { DeleteObjectCommand } = await import("@aws-sdk/client-s3");
const { DeleteObjectCommand } = await this.access.importRuntime("@aws-sdk/client-s3");
await this.client.send(
new DeleteObjectCommand({
Bucket: this.access.bucket,
@@ -1,5 +1,6 @@
import { TencentAccess } from "../access.js";
import { ILogger, safePromise } from "@certd/basic";
import { ImportRuntime } from "@certd/pipeline";
import fs from "fs";
export class TencentCosClient {
@@ -7,16 +8,18 @@ export class TencentCosClient {
logger: ILogger;
region: string;
bucket: string;
importRuntime: ImportRuntime;
constructor(opts: { access: TencentAccess; logger: ILogger; region: string; bucket: string }) {
constructor(opts: { access: TencentAccess; logger: ILogger; region: string; bucket: string; importRuntime?: ImportRuntime }) {
this.access = opts.access;
this.logger = opts.logger;
this.bucket = opts.bucket;
this.region = opts.region;
this.importRuntime = opts.importRuntime || (async (specifier: string) => await import(specifier));
}
async getCosClient() {
const sdk = await import("cos-nodejs-sdk-v5");
const sdk = await this.importRuntime("cos-nodejs-sdk-v5");
const clientConfig = {
SecretId: this.access.secretId,
SecretKey: this.access.secretKey,