feat: 通过插件配置懒加载依赖,动态加载第三方依赖包,精简安装镜像大小

This commit is contained in:
xiaojunnuo
2026-06-19 17:44:57 +08:00
parent 0d97ad67c5
commit 01568ca148
50 changed files with 2009 additions and 211 deletions
@@ -1,20 +1,32 @@
import { IAccessService } from "@certd/pipeline";
export type AccessRuntimeDepsService = {
ensureRuntimeDependencies(pluginKeys: string | string[]): Promise<any>;
importRuntime(specifier: string): Promise<any>;
};
export class AccessGetter implements IAccessService {
userId: number;
projectId?: number;
getter: <T>(id: any, userId?: number, projectId?: number, ignorePermission?: boolean) => Promise<T>;
constructor(userId: number, projectId: number, getter: (id: any, userId: number, projectId?: number, ignorePermission?: boolean) => Promise<any>) {
runtimeDepsService?: AccessRuntimeDepsService;
getter: <T>(id: any, userId?: number, projectId?: number, ignorePermission?: boolean, runtimeDepsService?: AccessRuntimeDepsService) => Promise<T>;
constructor(
userId: number,
projectId: number,
getter: (id: any, userId: number, projectId?: number, ignorePermission?: boolean, runtimeDepsService?: AccessRuntimeDepsService) => Promise<any>,
runtimeDepsService?: AccessRuntimeDepsService
) {
this.userId = userId;
this.projectId = projectId;
this.getter = getter;
this.runtimeDepsService = runtimeDepsService;
}
async getById<T = any>(id: any) {
return await this.getter<T>(id, this.userId, this.projectId);
return await this.getter<T>(id, this.userId, this.projectId, false, this.runtimeDepsService);
}
async getCommonById<T = any>(id: any) {
return await this.getter<T>(id, 0, null);
return await this.getter<T>(id, 0, null, false, this.runtimeDepsService);
}
}
@@ -2,10 +2,11 @@ import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { In, Repository } from "typeorm";
import { AccessGetter, BaseService, PageReq, PermissionException, ValidateException } from "../../../index.js";
import type { AccessRuntimeDepsService } from "./access-getter.js";
import { AccessEntity } from "../entity/access.js";
import { AccessDefine, accessRegistry, newAccess } from "@certd/pipeline";
import { EncryptService } from "./encrypt-service.js";
import { logger, utils } from "@certd/basic";
import { http, logger, utils } from "@certd/basic";
/**
* 授权
@@ -160,7 +161,7 @@ export class AccessService extends BaseService<AccessEntity> {
};
}
async getAccessById(id: any, checkUserId: boolean, userId?: number, projectId?: number): Promise<any> {
async getAccessById(id: any, checkUserId: boolean, userId?: number, projectId?: number, runtimeDepsService?: AccessRuntimeDepsService): Promise<any> {
const entity = await this.info(id);
if (entity == null) {
throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`);
@@ -183,12 +184,20 @@ export class AccessService extends BaseService<AccessEntity> {
id: entity.id,
...setting,
};
const accessGetter = new AccessGetter(userId, projectId, this.getById.bind(this));
return await newAccess(entity.type, input, accessGetter);
const getAccessById = this.getById.bind(this);
const accessGetter = new AccessGetter(userId, projectId, getAccessById, runtimeDepsService);
const accessContext = {
logger,
http,
utils,
accessService: accessGetter,
} as any;
const access = await newAccess(entity.type, input, accessGetter, accessContext);
return access;
}
async getById(id: any, userId: number, projectId?: number): Promise<any> {
return await this.getAccessById(id, true, userId, projectId);
async getById(id: any, userId: number, projectId?: number, _ignorePermission?: boolean, runtimeDepsService?: AccessRuntimeDepsService): Promise<any> {
return await this.getAccessById(id, true, userId, projectId, runtimeDepsService);
}
decryptAccessEntity(entity: AccessEntity): any {
@@ -27,6 +27,8 @@ export type AddonInputDefine = FormItemProps & {
export type AddonDefine = Registrable & {
addonType: string;
needPlus?: boolean;
dependPlugins?: Record<string, string>;
dependPackages?: Record<string, string>;
input?: {
[key: string]: AddonInputDefine;
};
@@ -64,6 +66,17 @@ export abstract class BaseAddon implements IAddon {
ctx!: AddonContext;
http!: HttpClient;
logger!: ILogger;
runtimeDepsService?: {
ensureRuntimeDependencies(pluginKeys: string | string[]): Promise<any>;
importRuntime(specifier: string): Promise<any>;
};
async importRuntime(specifier: string) {
if (!this.runtimeDepsService) {
return await import(specifier);
}
return await this.runtimeDepsService.importRuntime(specifier);
}
title!: string;
@@ -107,10 +120,16 @@ export abstract class BaseAddon implements IAddon {
}
setCtx(ctx: AddonContext) {
async setCtx(ctx: AddonContext) {
this.ctx = ctx;
this.http = ctx.http;
this.logger = ctx.logger;
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
}
if (this.runtimeDepsService && this.define?.addonType && this.define?.name) {
await this.runtimeDepsService.ensureRuntimeDependencies(`addon:${this.define.addonType}:${this.define.name}`);
}
}
setDefine = (define:AddonDefine) => {
this.define = define;
@@ -63,9 +63,7 @@ export async function newAddon(addonType:string,type: string, input: any, ctx: A
throw new Error("ctx is required");
}
plugin.setDefine(register.define);
plugin.setCtx(ctx);
await plugin.setCtx(ctx);
await plugin.onInstance();
return plugin;
}