diff --git a/packages/libs/lib-server/src/system/settings/service/models.ts b/packages/libs/lib-server/src/system/settings/service/models.ts index 8202d5d7b..3cb7c762e 100644 --- a/packages/libs/lib-server/src/system/settings/service/models.ts +++ b/packages/libs/lib-server/src/system/settings/service/models.ts @@ -255,3 +255,4 @@ export class SysSafeSetting extends BaseSettings { autoHiddenTimes: 5, }; } + diff --git a/packages/ui/certd-client/src/use/use-dialog.ts b/packages/ui/certd-client/src/use/use-dialog.ts index c77b78b70..fd06415eb 100644 --- a/packages/ui/certd-client/src/use/use-dialog.ts +++ b/packages/ui/certd-client/src/use/use-dialog.ts @@ -2,8 +2,9 @@ import { useFormWrapper } from "@fast-crud/fast-crud"; export type FormOptionReq = { title: string; - columns: any; + columns?: any; onSubmit?: any; + body?: any; }; export function useFormDialog() { @@ -18,6 +19,9 @@ export function useFormDialog() { wrapper: { title: req.title, saveRemind: false, + slots: { + "form-body-top": req.body, + }, }, async afterSubmit() {}, async doSubmit({ form }: any) { diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/api.ts b/packages/ui/certd-client/src/views/certd/cert/domain/api.ts index 23ba9736c..7e004b9a4 100644 --- a/packages/ui/certd-client/src/views/certd/cert/domain/api.ts +++ b/packages/ui/certd-client/src/views/certd/cert/domain/api.ts @@ -58,17 +58,44 @@ export async function DeleteBatch(ids: any[]) { }); } -export async function SyncSubmit(body: any) { +export async function ImportTaskAdd(body: any) { return await request({ - url: apiPrefix + "/sync/import", + url: apiPrefix + "/import/add", method: "post", data: body, }); } - -export async function SyncDomainsExpiration() { +export async function ImportTaskStatus() { return await request({ - url: apiPrefix + "/sync/expiration", + url: apiPrefix + "/import/status", + method: "post", + }); +} +export async function ImportTaskDelete(key: any) { + return await request({ + url: apiPrefix + "/import/delete", + method: "post", + params: { key }, + }); +} +export async function ImportTaskStart(key: any) { + return await request({ + url: apiPrefix + "/import/start", + method: "post", + params: { key }, + }); +} + +export async function SyncExpirationStart() { + return await request({ + url: apiPrefix + "/sync/expiration/start", + method: "post", + }); +} + +export async function SyncExpirationStatus() { + return await request({ + url: apiPrefix + "/sync/expiration/status", method: "post", }); } diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx b/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx index a64e6a824..baa841656 100644 --- a/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx @@ -1,15 +1,15 @@ -import * as api from "./api"; -import { useI18n } from "/src/locales"; +import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; +import { Modal, notification } from "ant-design-vue"; import { Ref, ref } from "vue"; import { useRouter } from "vue-router"; -import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; -import { useUserStore } from "/@/store/user"; -import { useSettingStore } from "/@/store/settings"; +import * as api from "./api"; +import DomainImportTaskStatus from "./domain-import-task-status.vue"; import { Dicts } from "/@/components/plugins/lib/dicts"; +import { useSettingStore } from "/@/store/settings"; +import { useUserStore } from "/@/store/user"; +import { useFormDialog } from "/@/use/use-dialog"; import { createAccessApi } from "/@/views/certd/access/api"; -import { Modal, notification } from "ant-design-vue"; -import { useDomainImport } from "./use"; - +import { useI18n } from "/src/locales"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const router = useRouter(); const { t } = useI18n(); @@ -51,7 +51,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat url: "pi/dnsProvider/dnsProviderTypeDict", }); - const openDomainImportDialog = useDomainImport(); + const { openFormDialog } = useFormDialog(); return { crudOptions: { settings: { @@ -103,13 +103,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat needPlus: true, color: "gold", icon: "mingcute:vip-1-line", - click: () => { + click: async () => { settingStore.checkPlus(); - openDomainImportDialog({ - afterSubmit: () => { - setTimeout(() => { - crudExpose.doRefresh(); - }, 2000); + await openFormDialog({ + title: "从域名提供商导入域名", + body: () => { + return ; + }, + onSubmit: async (form: any) => { + crudExpose.doRefresh(); }, }); }, @@ -120,7 +122,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat icon: "ion:refresh-outline", text: "同步域名过期时间", click: async () => { - await api.SyncDomainsExpiration(); + await api.SyncExpirationStart(); notification.success({ message: "同步任务已提交", }); diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/import.vue b/packages/ui/certd-client/src/views/certd/cert/domain/import.vue new file mode 100644 index 000000000..81653ab6a --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/cert/domain/import.vue @@ -0,0 +1,76 @@ + + + diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/use.tsx b/packages/ui/certd-client/src/views/certd/cert/domain/use.tsx index 4d77e4e3b..5fd6ef6ff 100644 --- a/packages/ui/certd-client/src/views/certd/cert/domain/use.tsx +++ b/packages/ui/certd-client/src/views/certd/cert/domain/use.tsx @@ -53,11 +53,10 @@ export function useDomainImport() { title: "从域名提供商导入域名", columns: columns, onSubmit: async (form: any) => { - await api.SyncSubmit({ + await api.ImportTaskAdd({ dnsProviderType: form.dnsProviderType, dnsProviderAccessId: form.dnsProviderAccessId, }); - message.success("导入任务已提交"); if (req.afterSubmit) { req.afterSubmit(); } diff --git a/packages/ui/certd-server/src/controller/user/cert/domain-controller.ts b/packages/ui/certd-server/src/controller/user/cert/domain-controller.ts index 78d476fd1..68cde5bf8 100644 --- a/packages/ui/certd-server/src/controller/user/cert/domain-controller.ts +++ b/packages/ui/certd-server/src/controller/user/cert/domain-controller.ts @@ -1,6 +1,6 @@ import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core'; import { Constants, CrudController } from '@certd/lib-server'; -import {DomainService} from "../../../modules/cert/service/domain-service.js"; +import { DomainService } from "../../../modules/cert/service/domain-service.js"; import { checkPlus } from '@certd/plus-core'; /** @@ -80,23 +80,63 @@ export class DomainController extends CrudController { } - @Post('/sync/import', { summary: Constants.per.authOnly }) - async syncImport(@Body(ALL) body: any) { - const { dnsProviderType, dnsProviderAccessId } = body; + @Post('/import/start', { summary: Constants.per.authOnly }) + async importStart(@Body(ALL) body: any) { + const { key } = body; const req = { - dnsProviderType, dnsProviderAccessId, userId: this.getUserId(), + key, userId: this.getUserId(), } checkPlus() - await this.service.doSyncFromProvider(req); + await this.service.startDomainImportTask(req); return this.ok(); } - @Post('/sync/expiration', { summary: Constants.per.authOnly }) - async syncExpiration(@Body(ALL) body: any) { - await this.service.doSyncDomainsExpirationDate({ + @Post('/import/status', { summary: Constants.per.authOnly }) + async importStatus() { + const req = { + userId: this.getUserId(), + } + const task = await this.service.getDomainImportTaskStatus(req); + return this.ok(task); + } + + @Post('/import/add', { summary: Constants.per.authOnly }) + async importAdd(@Body(ALL) body: any) { + const { dnsProviderType, dnsProviderAccessId, title } = body; + const req = { + userId: this.getUserId(), + dnsProviderType, dnsProviderAccessId, title, + } + const item = await this.service.addDomainImportTask(req); + return this.ok(item); + } + + @Post('/import/delete', { summary: Constants.per.authOnly }) + async importDelete(@Body(ALL) body: any) { + const { key } = body; + const req = { + userId: this.getUserId(), + key, + } + await this.service.deleteDomainImportTask(req); + return this.ok(); + } + + + @Post('/sync/expiration/start', { summary: Constants.per.authOnly }) + async syncExpirationStart(@Body(ALL) body: any) { + await this.service.startSyncExpirationTask({ userId: this.getUserId(), }) return this.ok(); } + @Post('/sync/expiration/status', { summary: Constants.per.authOnly }) + async syncExpirationStatus(@Body(ALL) body: any) { + const status = await this.service.getSyncExpirationTaskStatus({ + userId: this.getUserId(), + }) + return this.ok(status); + } + } diff --git a/packages/ui/certd-server/src/modules/basic/service/task-executor.ts b/packages/ui/certd-server/src/modules/basic/service/task-executor.ts index a1899dfcc..72a25e40b 100644 --- a/packages/ui/certd-server/src/modules/basic/service/task-executor.ts +++ b/packages/ui/certd-server/src/modules/basic/service/task-executor.ts @@ -3,16 +3,17 @@ import { logger } from "@certd/basic" export class BackTaskExecutor { tasks: Record> = {} - start(type: string, task: BackTask) { + start(task: BackTask) { + const type = task.type || 'default' if (!this.tasks[type]) { this.tasks[type] = {} } const oldTask = this.tasks[type][task.key] if (oldTask && oldTask.status === "running") { - throw new Error(`任务 ${type}—${task.key} 正在运行中`) + throw new Error(`任务 ${task.title} 正在运行中`) } this.tasks[type][task.key] = task - this.run(type, task); + this.run(task); } get(type: string, key: string) { @@ -37,7 +38,8 @@ export class BackTaskExecutor { } } - private async run(type: string, task: any) { + private async run(task: BackTask) { + const type = task.type || 'default' if (task.status === "running") { throw new Error(`任务 ${type}—${task.key} 正在运行中`) } @@ -49,7 +51,7 @@ export class BackTaskExecutor { } catch (e) { logger.error(`任务 ${task.title}[${type}-${task.key}] 执行失败`, e.message); task.status = "failed"; - task.error = e.message; + task.addError(e.message) } finally { task.endTime = Date.now(); task.status = "done"; @@ -61,9 +63,10 @@ export class BackTaskExecutor { } - + } export class BackTask { + type: string; key: string; title: string; total: number = 0; @@ -79,10 +82,12 @@ export class BackTask { run: (task: BackTask) => Promise; - constructor(opts:{ + constructor(opts: { + type: string, key: string, title: string, run: (task: BackTask) => Promise }) { - const {key, title, run} = opts + const { key, title, run, type } = opts + this.type = type this.key = key; this.title = title; Object.defineProperty(this, 'run', { diff --git a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts index 97aa28529..a7538aff7 100644 --- a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts +++ b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts @@ -14,6 +14,8 @@ import { TaskServiceBuilder } from '../../pipeline/service/getter/task-service-g import { SubDomainService } from "../../pipeline/service/sub-domain-service.js"; import { DomainEntity } from '../entity/domain.js'; import { BackTask, taskExecutor } from '../../basic/service/task-executor.js'; +import { UserSettingsService } from '../../mine/service/user-settings-service.js'; +import { UserDomainImportSetting } from '../../mine/service/models.js'; export interface SyncFromProviderReq { userId: number; @@ -22,6 +24,9 @@ export interface SyncFromProviderReq { } +const DOMAIN_IMPORT_TASK_TYPE = 'domainImportTask' +const DOMAIN_EXPIRE_TASK_TYPE = 'domainExpirationSyncTask' + /** * @@ -43,6 +48,9 @@ export class DomainService extends BaseService { @Inject() taskServiceBuilder: TaskServiceBuilder; + @Inject() + userSettingService: UserSettingsService; + //@ts-ignore getRepository() { return this.repository; @@ -207,13 +215,26 @@ export class DomainService extends BaseService { } - async doSyncFromProvider(req: SyncFromProviderReq) { - const key = `user_${req.userId || 0}` - taskExecutor.start('syncFromProviderTask', new BackTask({ + async startDomainImportTask(req: {userId:number,key:string}) { + const key = req.key + const setting = await this.userSettingService.getSetting(req.userId, UserDomainImportSetting) + + const item = setting.domainImportList.find(item => item.key === key) + if (!item) { + throw new Error(`域名导入任务配置(${key})还未注册`) + } + const { dnsProviderType, dnsProviderAccessId,title } = item + + taskExecutor.start(new BackTask({ + type: DOMAIN_IMPORT_TASK_TYPE, key, - title: `从域名提供商导入域名(${key})`, + title: title, run: async (task: BackTask) => { - await this._syncFromProvider(req, task) + await this._syncFromProvider({ + userId: req.userId, + dnsProviderType, + dnsProviderAccessId, + }, task) }, })) } @@ -288,10 +309,85 @@ export class DomainService extends BaseService { logger.info(`从域名提供商${dnsProviderType}导入域名完成(${key}),共导入${task.current}个域名,跳过${task.getSkipCount()}个域名`) } - async doSyncDomainsExpirationDate(req: { userId?: number }) { + async getDomainImportTaskStatus(req:{userId?:number}) { + const userId = req.userId || 0 + + const setting = await this.userSettingService.getSetting(userId, UserDomainImportSetting) + const list= setting?.domainImportList || [] + + const taskList:any = [] + + for (const item of list) { + const { key } = item + + const task = taskExecutor.get(DOMAIN_IMPORT_TASK_TYPE,key) + + taskList.push({ + ...item, + task, + }) + } + return taskList + } + + async addDomainImportTask(req:{userId?:number,dnsProviderType:string,dnsProviderAccessId:string,title:string}) { + const userId = req.userId || 0 + const { dnsProviderType, dnsProviderAccessId,title } = req + const key = `user_${userId}_${dnsProviderType}_${dnsProviderAccessId}` + + const setting = await this.userSettingService.getSetting(userId, UserDomainImportSetting) + setting.domainImportList = setting.domainImportList || [] + if (setting.domainImportList.find(item => item.key === key)) { + throw new Error(`该域名导入任务${key}已存在`) + } + + const access = await this.accessService.getAccessById(dnsProviderAccessId, true, userId) + if (!access) { + throw new Error(`该授权(${dnsProviderAccessId})不存在,请检查是否已被删除`) + } + + const item = { + dnsProviderType, + dnsProviderAccessId, + key, + title, + } + setting.domainImportList.push(item) + await this.userSettingService.saveSetting(userId, setting) + + return item + } + + async deleteDomainImportTask(req:{userId?:number,key:string}) { + const userId = req.userId || 0 + const { key } = req + + const setting = await this.userSettingService.getSetting(userId, UserDomainImportSetting) + setting.domainImportList = setting.domainImportList || [] + const index = setting.domainImportList.findIndex(item => item.key === key) + if (index === -1) { + throw new Error(`该域名导入任务${key}不存在`) + } + setting.domainImportList.splice(index, 1) + taskExecutor.clear(DOMAIN_IMPORT_TASK_TYPE,key) + await this.userSettingService.saveSetting(userId, setting) + } + + + + + async getSyncExpirationTaskStatus(req:{userId?:number}) { + const userId = req.userId || 0 + const key = `user_${userId}` + const task = taskExecutor.get(DOMAIN_EXPIRE_TASK_TYPE,key) + return task + } + + async startSyncExpirationTask(req: { userId?: number }) { const userId = req.userId const key = `user_${userId || 0}` - taskExecutor.start('syncDomainsExpirationDateTask', new BackTask({ + taskExecutor.start(new BackTask({ + type: DOMAIN_EXPIRE_TASK_TYPE, key, title: `同步注册域名过期时间(${key}))`, run: async (task: BackTask) => { @@ -407,4 +503,6 @@ export class DomainService extends BaseService { const key = `user_${req.userId || 'all'}` logger.info(`同步用户(${key})注册域名过期时间完成(${req.task.getSuccessCount()}个成功,${req.task.errors.length}个失败)` ) } + + } diff --git a/packages/ui/certd-server/src/modules/mine/service/models.ts b/packages/ui/certd-server/src/modules/mine/service/models.ts index 5b49ade16..6fe936701 100644 --- a/packages/ui/certd-server/src/modules/mine/service/models.ts +++ b/packages/ui/certd-server/src/modules/mine/service/models.ts @@ -46,3 +46,11 @@ export class UserGrantSetting extends BaseSettings { allowAdminViewCerts:boolean = false; } + + +export class UserDomainImportSetting extends BaseSettings { + static __title__ = "用户域名导入设置"; + static __key__ = "user.domain.import"; + + domainImportList:{dnsProviderType:string,dnsProviderAccessId:string,key:string,title:string}[]; +}