mirror of
https://github.com/certd/certd.git
synced 2026-04-16 05:50:50 +08:00
perf: cname记录支持批量导入和导出
This commit is contained in:
@@ -60,7 +60,7 @@ export async function DeleteBatch(ids: any[]) {
|
||||
|
||||
export async function SyncSubmit(body: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/sync/submit",
|
||||
url: apiPrefix + "/sync/import",
|
||||
method: "post",
|
||||
data: body,
|
||||
});
|
||||
|
||||
@@ -157,6 +157,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
expirationDate: {
|
||||
title: t("certd.domain.expirationDate"),
|
||||
type: "date",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
challengeType: {
|
||||
title: t("certd.domain.challengeType"),
|
||||
|
||||
@@ -77,3 +77,11 @@ export async function ResetStatus(id: number) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function Import(form: { domainList: string; cnameProviderId: any }) {
|
||||
return await request({
|
||||
url: apiPrefix + "/import",
|
||||
method: "post",
|
||||
data: form,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import { useUserStore } from "/@/store/user";
|
||||
import { useSettingStore } from "/@/store/settings";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import CnameTip from "/@/components/plugins/cert/domains-verify-plan-editor/cname-tip.vue";
|
||||
import { useCnameImport } from "./use";
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const crudBinding = crudExpose.crudBinding;
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
@@ -27,10 +29,13 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
return res;
|
||||
};
|
||||
|
||||
const openCnameImportDialog = useCnameImport();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const settingStore = useSettingStore();
|
||||
const selectedRowKeys: Ref<any[]> = ref([]);
|
||||
context.selectedRowKeys = selectedRowKeys;
|
||||
|
||||
const dictRef = dict({
|
||||
data: [
|
||||
{ label: t("certd.pending_cname_setup"), value: "cname", color: "warning" },
|
||||
@@ -64,6 +69,32 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
actionbar: {
|
||||
buttons: {
|
||||
import: {
|
||||
title: "导入CNAME记录",
|
||||
type: "primary",
|
||||
text: "批量导入",
|
||||
click: () => {
|
||||
openCnameImportDialog({
|
||||
afterSubmit: () => {
|
||||
setTimeout(() => {
|
||||
crudExpose?.doRefresh();
|
||||
}, 2000);
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
export: {
|
||||
title: "导出CNAME记录之后,可用于批量导入cname解析到域名注册商",
|
||||
type: "primary",
|
||||
text: "批量导出",
|
||||
click: () => {
|
||||
crudBinding.value.toolbar.buttons.export.click({});
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
tabs: {
|
||||
name: "status",
|
||||
show: true,
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
import { message } from "ant-design-vue";
|
||||
import * as api from "./api";
|
||||
import { useFormDialog } from "/@/use/use-dialog";
|
||||
|
||||
export const cnameProviderDict = dict({
|
||||
url: "/cname/provider/list",
|
||||
value: "id",
|
||||
label: "domain",
|
||||
});
|
||||
export function useCnameImport() {
|
||||
const { openFormDialog } = useFormDialog();
|
||||
|
||||
const columns = {
|
||||
domainList: {
|
||||
title: "域名列表",
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "a-textarea",
|
||||
rows: 5,
|
||||
},
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
required: true,
|
||||
helper: "每个域名一行,批量导入\n泛域名请去掉*.\n已经存在的会自动跳过",
|
||||
},
|
||||
},
|
||||
cnameProviderId: {
|
||||
title: "CNAME服务",
|
||||
type: "dict-select",
|
||||
dict: cnameProviderDict,
|
||||
form: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return function openCnameImportDialog(req: { afterSubmit?: () => void }) {
|
||||
openFormDialog({
|
||||
title: "导入CNAME记录",
|
||||
columns: columns,
|
||||
onSubmit: async (form: any) => {
|
||||
await api.Import({
|
||||
domainList: form.domainList,
|
||||
cnameProviderId: form.cnameProviderId,
|
||||
});
|
||||
message.success("导入任务已提交");
|
||||
if (req.afterSubmit) {
|
||||
req.afterSubmit();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
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 { checkPlus } from '@certd/plus-core';
|
||||
|
||||
/**
|
||||
* 授权
|
||||
@@ -79,12 +80,13 @@ export class DomainController extends CrudController<DomainService> {
|
||||
}
|
||||
|
||||
|
||||
@Post('/sync/submit', { summary: Constants.per.authOnly })
|
||||
async syncSubmit(@Body(ALL) body: any) {
|
||||
@Post('/sync/import', { summary: Constants.per.authOnly })
|
||||
async syncImport(@Body(ALL) body: any) {
|
||||
const { dnsProviderType, dnsProviderAccessId } = body;
|
||||
const req = {
|
||||
dnsProviderType, dnsProviderAccessId, userId: this.getUserId(),
|
||||
}
|
||||
checkPlus()
|
||||
await this.service.doSyncFromProvider(req);
|
||||
return this.ok();
|
||||
}
|
||||
|
||||
@@ -99,4 +99,15 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
|
||||
const res = await this.service.resetStatus(body.id);
|
||||
return this.ok(res);
|
||||
}
|
||||
@Post('/import', { summary: Constants.per.authOnly })
|
||||
async import(@Body(ALL) body: { domainList: string; cnameProviderId: any }) {
|
||||
const userId = this.getUserId();
|
||||
const res = await this.service.doImport({
|
||||
userId,
|
||||
domainList: body.domainList,
|
||||
cnameProviderId: body.cnameProviderId,
|
||||
});
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -350,9 +350,9 @@ export class DomainService extends BaseService<DomainEntity> {
|
||||
const getDomainPage = async (pager: Pager) => {
|
||||
const pageRes = await this.page({
|
||||
query: query,
|
||||
buildQuery(bq) {
|
||||
bq.andWhere(" (expiration_date is null or expiration_date < :now) ", { now: dayjs().add(1, 'month').valueOf() })
|
||||
},
|
||||
// buildQuery(bq) {
|
||||
// bq.andWhere(" (expiration_date is null or expiration_date < :now) ", { now: dayjs().add(1, 'month').valueOf() })
|
||||
// },
|
||||
page: {
|
||||
offset: pager.getOffset(),
|
||||
limit: pager.pageSize,
|
||||
|
||||
@@ -9,7 +9,7 @@ export class BackTaskExecutor {
|
||||
}
|
||||
const oldTask = this.tasks[type][task.key]
|
||||
if (oldTask && oldTask.status === "running") {
|
||||
throw new Error(`任务 ${task.key} 正在运行中`)
|
||||
throw new Error(`任务 ${type}—${task.key} 正在运行中`)
|
||||
}
|
||||
this.tasks[type][task.key] = task
|
||||
this.run(type, task);
|
||||
@@ -39,7 +39,7 @@ export class BackTaskExecutor {
|
||||
|
||||
private async run(type: string, task: any) {
|
||||
if (task.status === "running") {
|
||||
throw new Error(`任务 ${task.key} 正在运行中`)
|
||||
throw new Error(`任务 ${type}—${task.key} 正在运行中`)
|
||||
}
|
||||
task.startTime = Date.now();
|
||||
task.clearTimeout();
|
||||
@@ -47,7 +47,7 @@ export class BackTaskExecutor {
|
||||
task.status = "running";
|
||||
return await task.run(task);
|
||||
} catch (e) {
|
||||
logger.error(`任务 ${task.title}[${task.key}] 执行失败`, e.message);
|
||||
logger.error(`任务 ${task.title}[${type}-${task.key}] 执行失败`, e.message);
|
||||
task.status = "failed";
|
||||
task.error = e.message;
|
||||
} finally {
|
||||
|
||||
@@ -22,6 +22,7 @@ import punycode from "punycode.js";
|
||||
import { SubDomainService } from "../../pipeline/service/sub-domain-service.js";
|
||||
import { SubDomainsGetter } from "../../pipeline/service/getter/sub-domain-getter.js";
|
||||
import { TaskServiceBuilder } from "../../pipeline/service/getter/task-service-getter.js";
|
||||
import { BackTask, taskExecutor } from "../../cert/service/task-executor.js";
|
||||
|
||||
type CnameCheckCacheValue = {
|
||||
validating: boolean;
|
||||
@@ -487,4 +488,49 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
||||
}
|
||||
await this.getRepository().update(id, { status: "cname", mainDomain: "" });
|
||||
}
|
||||
|
||||
async doImport(req:{ userId: number; domainList: string; cnameProviderId: any }) {
|
||||
const {userId,cnameProviderId,domainList} = req;
|
||||
const domains = domainList.split("\n").map(item => item.trim()).filter(item => item.length > 0);
|
||||
if (domains.length === 0) {
|
||||
throw new ValidateException("域名列表不能为空");
|
||||
}
|
||||
if (!req.cnameProviderId) {
|
||||
throw new ValidateException("CNAME服务提供商不能为空");
|
||||
}
|
||||
|
||||
taskExecutor.start("cnameImport",new BackTask({
|
||||
key: "user_"+userId,
|
||||
title: "导入CNAME记录",
|
||||
run: async (task) => {
|
||||
await this._import({ userId, domains, cnameProviderId },task);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
async _import(req :{ userId: number; domains: string[]; cnameProviderId: any },task:BackTask) {
|
||||
const userId = req.userId;
|
||||
for (const domain of req.domains) {
|
||||
const old = await this.getRepository().findOne({
|
||||
where: {
|
||||
userId: req.userId,
|
||||
domain,
|
||||
},
|
||||
});
|
||||
if (old) {
|
||||
logger.warn(`域名${domain}已存在,跳过`);
|
||||
}
|
||||
//开始导入
|
||||
try{
|
||||
await this.add({
|
||||
userId,
|
||||
domain: domain,
|
||||
cnameProviderId: req.cnameProviderId,
|
||||
});
|
||||
}catch(e){
|
||||
logger.error(`导入域名${domain}失败:${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user