refactor(core): 重构访问控制和插件实例化逻辑

- 修改访问控制和插件注册方式,使用异步函数统一实例化逻辑
- 更新相关组件和控制器以适应新的异步实例化方式
- 优化 DNS 提供商选择器,增加访问类型支持
pull/370/head
xiaojunnuo 2025-04-12 01:21:50 +08:00
parent c4fb138ae8
commit 3d8a5196a0
15 changed files with 79 additions and 39 deletions

View File

@ -26,7 +26,9 @@ export function IsAccess(define: AccessDefine): ClassDecorator {
target.define = define; target.define = define;
accessRegistry.register(define.name, { accessRegistry.register(define.name, {
define, define,
target, target: async () => {
return target;
},
}); });
}; };
} }
@ -39,13 +41,15 @@ export function AccessInput(input?: AccessInputDefine): PropertyDecorator {
}; };
} }
export function newAccess(type: string, input: any, ctx?: AccessContext) { export async function newAccess(type: string, input: any, ctx?: AccessContext) {
const register = accessRegistry.get(type); const register = accessRegistry.get(type);
if (register == null) { if (register == null) {
throw new Error(`access ${type} not found`); throw new Error(`access ${type} not found`);
} }
// @ts-ignore // @ts-ignore
const access = new register.target(); const accessCls = await register.target();
// @ts-ignore
const access = new accessCls();
for (const key in input) { for (const key in input) {
access[key] = input[key]; access[key] = input[key];
} }

View File

@ -281,13 +281,9 @@ export class Executor {
let instance: ITaskPlugin = null; let instance: ITaskPlugin = null;
try { try {
//@ts-ignore //@ts-ignore
if (plugin.target.define) { const pluginCls = await plugin.target();
//@ts-ignore //@ts-ignore
instance = new plugin.target(); instance = new pluginCls();
} else {
//@ts-ignore
instance = await plugin.target();
}
} catch (e: any) { } catch (e: any) {
currentLogger.error(`实例化插件失败:${e.message}`); currentLogger.error(`实例化插件失败:${e.message}`);
throw new Error(`实例化插件失败`, e); throw new Error(`实例化插件失败`, e);

View File

@ -26,7 +26,9 @@ export function IsNotification(define: NotificationDefine): ClassDecorator {
target.define = define; target.define = define;
notificationRegistry.register(define.name, { notificationRegistry.register(define.name, {
define, define,
target, target: async () => {
return target;
},
}); });
}; };
} }
@ -44,9 +46,10 @@ export async function newNotification(type: string, input: any, ctx: Notificatio
if (register == null) { if (register == null) {
throw new Error(`notification ${type} not found`); throw new Error(`notification ${type} not found`);
} }
// @ts-ignore // @ts-ignore
const plugin = new register.target(); const pluginCls = await register.target();
// @ts-ignore
const plugin = new pluginCls();
merge(plugin, input); merge(plugin, input);
if (!ctx) { if (!ctx) {
throw new Error("ctx is required"); throw new Error("ctx is required");

View File

@ -65,7 +65,9 @@ export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
pluginRegistry.register(define.name, { pluginRegistry.register(define.name, {
define, define,
target, target: async () => {
return target;
},
}); });
}; };
} }

View File

@ -11,7 +11,7 @@ export type Registrable = {
export type TargetGetter<T> = () => Promise<T>; export type TargetGetter<T> = () => Promise<T>;
export type RegistryItem<T> = { export type RegistryItem<T> = {
define: Registrable; define: Registrable;
target: T | TargetGetter<T>; target: TargetGetter<T>;
}; };
export type OnRegisterContext<T> = { export type OnRegisterContext<T> = {

View File

@ -140,7 +140,7 @@ export class AccessService extends BaseService<AccessEntity> {
id: entity.id, id: entity.id,
...setting, ...setting,
}; };
return newAccess(entity.type, input); return await newAccess(entity.type, input);
} }
async getById(id: any, userId: number): Promise<any> { async getById(id: any, userId: number): Promise<any> {

View File

@ -29,6 +29,7 @@ export type DomainVerifyPlanInput = {
domain: string; domain: string;
type: "cname" | "dns" | "http"; type: "cname" | "dns" | "http";
dnsProviderType?: string; dnsProviderType?: string;
dnsProviderAccessType?: string;
dnsProviderAccessId?: number; dnsProviderAccessId?: number;
cnameVerifyPlan?: Record<string, CnameRecordInput>; cnameVerifyPlan?: Record<string, CnameRecordInput>;
httpVerifyPlan?: Record<string, HttpRecordInput>; httpVerifyPlan?: Record<string, HttpRecordInput>;
@ -99,7 +100,14 @@ HTTP文件验证不支持泛域名需要配置网站文件上传`,
return { return {
show: ctx.compute(({form})=>{ show: ctx.compute(({form})=>{
return form.challengeType === 'dns' return form.challengeType === 'dns'
}) }),
component:{
on:{
selectedChange({form,$event}){
form.dnsProviderAccessType = $event.accessType
}
}
}
} }
`, `,
required: true, required: true,
@ -107,6 +115,8 @@ HTTP文件验证不支持泛域名需要配置网站文件上传`,
}) })
dnsProviderType!: string; dnsProviderType!: string;
dnsProviderAccessType!: string;
@TaskInput({ @TaskInput({
title: "DNS解析授权", title: "DNS解析授权",
component: { component: {
@ -117,7 +127,7 @@ HTTP文件验证不支持泛域名需要配置网站文件上传`,
mergeScript: `return { mergeScript: `return {
component:{ component:{
type: ctx.compute(({form})=>{ type: ctx.compute(({form})=>{
return form.dnsProviderType return form.dnsProviderAccessType || form.dnsProviderType
}) })
}, },
show: ctx.compute(({form})=>{ show: ctx.compute(({form})=>{

View File

@ -5,6 +5,6 @@ const apiPrefix = "/pi/dnsProvider";
export async function GetList() { export async function GetList() {
return await request({ return await request({
url: apiPrefix + "/list", url: apiPrefix + "/list",
method: "post" method: "post",
}); });
} }

View File

@ -11,10 +11,10 @@ export default {
props: { props: {
modelValue: { modelValue: {
type: String, type: String,
default: undefined default: undefined,
} },
}, },
emits: ["update:modelValue"], emits: ["update:modelValue", "selected-change"],
setup(props: any, ctx: any) { setup(props: any, ctx: any) {
const options = ref<any[]>([]); const options = ref<any[]>([]);
@ -25,24 +25,36 @@ export default {
array.push({ array.push({
value: item.name, value: item.name,
label: item.title, label: item.title,
icon: item.icon icon: item.icon,
accessType: item.accessType,
}); });
} }
options.value = array; options.value = array;
// if (props.modelValue == null && options.value.length > 0) { // if (props.modelValue == null && options.value.length > 0) {
// ctx.emit("update:modelValue", options.value[0].value); // ctx.emit("update:modelValue", options.value[0].value);
// } // }
onSelectedChange(props.modelValue);
} }
onCreate(); onCreate();
function onChanged(value: any) { function onChanged(value: any) {
ctx.emit("update:modelValue", value); ctx.emit("update:modelValue", value);
onSelectedChange(value);
}
function onSelectedChange(value: any) {
if (value) {
const option = options.value.find(item => item.value == value);
if (option) {
ctx.emit("selected-change", option);
return;
}
}
} }
return { return {
options, options,
onChanged onChanged,
}; };
} },
}; };
</script> </script>

View File

@ -35,7 +35,7 @@ import { HttpRecord } from "/@/components/plugins/cert/domains-verify-plan-edito
import { dict } from "@fast-crud/fast-crud"; import { dict } from "@fast-crud/fast-crud";
defineOptions({ defineOptions({
name: "HttpVerifyPlan" name: "HttpVerifyPlan",
}); });
const emit = defineEmits(["update:modelValue", "change"]); const emit = defineEmits(["update:modelValue", "change"]);
@ -53,12 +53,12 @@ watch(
(value: any) => { (value: any) => {
if (value) { if (value) {
records.value = { records.value = {
...value ...value,
}; };
} }
}, },
{ {
immediate: true immediate: true,
} }
); );
@ -75,8 +75,8 @@ const uploaderTypeDict = dict({
{ label: "阿里云OSS", value: "alioss" }, { label: "阿里云OSS", value: "alioss" },
{ label: "腾讯云COS", value: "tencentcos" }, { label: "腾讯云COS", value: "tencentcos" },
{ label: "七牛OSS", value: "qiniuoss" }, { label: "七牛OSS", value: "qiniuoss" },
{ label: "SSH(已废弃请选择SFTP方式)", value: "ssh", disabled: true } { label: "SSH(已废弃请选择SFTP方式)", value: "ssh", disabled: true },
] ],
}); });
</script> </script>

View File

@ -32,14 +32,21 @@
<div class="form-item"> <div class="form-item">
<span class="label">DNS类型</span> <span class="label">DNS类型</span>
<span class="input"> <span class="input">
<fs-dict-select v-model:value="item.dnsProviderType" size="small" :dict="dnsProviderTypeDict" placeholder="DNS提供商" @change="onPlanChanged"></fs-dict-select> <fs-dict-select
v-model:value="item.dnsProviderType"
size="small"
:dict="dnsProviderTypeDict"
placeholder="DNS提供商"
@change="onPlanChanged"
@selected-change="onDnsProviderChange(item, $event)"
></fs-dict-select>
</span> </span>
</div> </div>
<a-divider type="vertical" /> <a-divider type="vertical" />
<div class="form-item"> <div class="form-item">
<span class="label">DNS授权</span> <span class="label">DNS授权</span>
<span class="input"> <span class="input">
<access-selector v-model="item.dnsProviderAccessId" size="small" :type="item.dnsProviderType" placeholder="请选择" @change="onPlanChanged"></access-selector> <access-selector v-model="item.dnsProviderAccessId" size="small" :type="item.dnsProviderAccessType || item.dnsProviderType" placeholder="请选择" @change="onPlanChanged"></access-selector>
</span> </span>
</div> </div>
</div> </div>
@ -101,6 +108,10 @@ const emit = defineEmits<{
"update:modelValue": any; "update:modelValue": any;
}>(); }>();
function onDnsProviderChange(item: any, option: any) {
item.dnsProviderAccessType = option.accessType;
}
const fullscreen = ref(false); const fullscreen = ref(false);
function fullscreenExit() { function fullscreenExit() {
if (fullscreen.value) { if (fullscreen.value) {

View File

@ -12,6 +12,7 @@ export type DomainVerifyPlanInput = {
domains: string[]; domains: string[];
type: "cname" | "dns" | "http"; type: "cname" | "dns" | "http";
dnsProviderType?: string; dnsProviderType?: string;
dnsProviderAccessType?: string;
dnsProviderAccessId?: number; dnsProviderAccessId?: number;
cnameVerifyPlan?: Record<string, CnameRecord>; cnameVerifyPlan?: Record<string, CnameRecord>;
httpVerifyPlan?: Record<string, HttpRecord>; httpVerifyPlan?: Record<string, HttpRecord>;

View File

@ -27,6 +27,8 @@ export class DnsProviderController extends BaseController {
dict.push({ dict.push({
value: item.name, value: item.name,
label: item.title, label: item.title,
//@ts-ignore
accessType: item.accessType,
}); });
} }
return this.ok(dict); return this.ok(dict);

View File

@ -49,7 +49,7 @@ export class HandleController extends BaseController {
} }
} }
const access = newAccess(body.typeName, inputAccess); const access = await newAccess(body.typeName, inputAccess);
const res = await access.onRequest(body); const res = await access.onRequest(body);
@ -76,7 +76,7 @@ export class HandleController extends BaseController {
async pluginRequest(@Body(ALL) body: PluginRequestHandleReq) { async pluginRequest(@Body(ALL) body: PluginRequestHandleReq) {
const userId = this.getUserId(); const userId = this.getUserId();
const pluginDefine = pluginRegistry.get(body.typeName); const pluginDefine = pluginRegistry.get(body.typeName);
const pluginCls = pluginDefine.target; const pluginCls = await pluginDefine.target();
if (pluginCls == null) { if (pluginCls == null) {
throw new Error(`plugin ${body.typeName} not found`); throw new Error(`plugin ${body.typeName} not found`);
} }

View File

@ -213,10 +213,9 @@ export class PluginService extends BaseService<PluginEntity> {
// const script = await this.compile(plugin.content); // const script = await this.compile(plugin.content);
const script = plugin.content const script = plugin.content
const getPluginClass = new AsyncFunction(script); const getPluginClass = new AsyncFunction(script);
const pluginClass = await getPluginClass({ logger: logger }); return await getPluginClass({ logger: logger });
return new pluginClass();
}catch (e) { }catch (e) {
logger.error("实例化插件失败:",e) logger.error("编译插件失败:",e)
throw e throw e
} }
@ -266,8 +265,8 @@ export class PluginService extends BaseService<PluginEntity> {
registry.register(item.name, { registry.register(item.name, {
define: item, define: item,
target: () => { target: async () => {
return this.getPluginTarget(item.name); return await this.getPluginTarget(item.name);
} }
}); });
} }