mirror of
https://github.com/certd/certd.git
synced 2026-04-24 12:27:25 +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) {
|
export async function SyncSubmit(body: any) {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/sync/submit",
|
url: apiPrefix + "/sync/import",
|
||||||
method: "post",
|
method: "post",
|
||||||
data: body,
|
data: body,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -157,6 +157,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
expirationDate: {
|
expirationDate: {
|
||||||
title: t("certd.domain.expirationDate"),
|
title: t("certd.domain.expirationDate"),
|
||||||
type: "date",
|
type: "date",
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
challengeType: {
|
challengeType: {
|
||||||
title: t("certd.domain.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 { useSettingStore } from "/@/store/settings";
|
||||||
import { message, Modal } from "ant-design-vue";
|
import { message, Modal } from "ant-design-vue";
|
||||||
import CnameTip from "/@/components/plugins/cert/domains-verify-plan-editor/cname-tip.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 {
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
|
const crudBinding = crudExpose.crudBinding;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
@@ -27,10 +29,13 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openCnameImportDialog = useCnameImport();
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
const selectedRowKeys: Ref<any[]> = ref([]);
|
const selectedRowKeys: Ref<any[]> = ref([]);
|
||||||
context.selectedRowKeys = selectedRowKeys;
|
context.selectedRowKeys = selectedRowKeys;
|
||||||
|
|
||||||
const dictRef = dict({
|
const dictRef = dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: t("certd.pending_cname_setup"), value: "cname", color: "warning" },
|
{ label: t("certd.pending_cname_setup"), value: "cname", color: "warning" },
|
||||||
@@ -64,6 +69,32 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
editRequest,
|
editRequest,
|
||||||
delRequest,
|
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: {
|
tabs: {
|
||||||
name: "status",
|
name: "status",
|
||||||
show: true,
|
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 { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
|
||||||
import { Constants, CrudController } from '@certd/lib-server';
|
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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 授权
|
* 授权
|
||||||
@@ -79,12 +80,13 @@ export class DomainController extends CrudController<DomainService> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Post('/sync/submit', { summary: Constants.per.authOnly })
|
@Post('/sync/import', { summary: Constants.per.authOnly })
|
||||||
async syncSubmit(@Body(ALL) body: any) {
|
async syncImport(@Body(ALL) body: any) {
|
||||||
const { dnsProviderType, dnsProviderAccessId } = body;
|
const { dnsProviderType, dnsProviderAccessId } = body;
|
||||||
const req = {
|
const req = {
|
||||||
dnsProviderType, dnsProviderAccessId, userId: this.getUserId(),
|
dnsProviderType, dnsProviderAccessId, userId: this.getUserId(),
|
||||||
}
|
}
|
||||||
|
checkPlus()
|
||||||
await this.service.doSyncFromProvider(req);
|
await this.service.doSyncFromProvider(req);
|
||||||
return this.ok();
|
return this.ok();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,4 +99,15 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
|
|||||||
const res = await this.service.resetStatus(body.id);
|
const res = await this.service.resetStatus(body.id);
|
||||||
return this.ok(res);
|
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 getDomainPage = async (pager: Pager) => {
|
||||||
const pageRes = await this.page({
|
const pageRes = await this.page({
|
||||||
query: query,
|
query: query,
|
||||||
buildQuery(bq) {
|
// buildQuery(bq) {
|
||||||
bq.andWhere(" (expiration_date is null or expiration_date < :now) ", { now: dayjs().add(1, 'month').valueOf() })
|
// bq.andWhere(" (expiration_date is null or expiration_date < :now) ", { now: dayjs().add(1, 'month').valueOf() })
|
||||||
},
|
// },
|
||||||
page: {
|
page: {
|
||||||
offset: pager.getOffset(),
|
offset: pager.getOffset(),
|
||||||
limit: pager.pageSize,
|
limit: pager.pageSize,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class BackTaskExecutor {
|
|||||||
}
|
}
|
||||||
const oldTask = this.tasks[type][task.key]
|
const oldTask = this.tasks[type][task.key]
|
||||||
if (oldTask && oldTask.status === "running") {
|
if (oldTask && oldTask.status === "running") {
|
||||||
throw new Error(`任务 ${task.key} 正在运行中`)
|
throw new Error(`任务 ${type}—${task.key} 正在运行中`)
|
||||||
}
|
}
|
||||||
this.tasks[type][task.key] = task
|
this.tasks[type][task.key] = task
|
||||||
this.run(type, task);
|
this.run(type, task);
|
||||||
@@ -39,7 +39,7 @@ export class BackTaskExecutor {
|
|||||||
|
|
||||||
private async run(type: string, task: any) {
|
private async run(type: string, task: any) {
|
||||||
if (task.status === "running") {
|
if (task.status === "running") {
|
||||||
throw new Error(`任务 ${task.key} 正在运行中`)
|
throw new Error(`任务 ${type}—${task.key} 正在运行中`)
|
||||||
}
|
}
|
||||||
task.startTime = Date.now();
|
task.startTime = Date.now();
|
||||||
task.clearTimeout();
|
task.clearTimeout();
|
||||||
@@ -47,7 +47,7 @@ export class BackTaskExecutor {
|
|||||||
task.status = "running";
|
task.status = "running";
|
||||||
return await task.run(task);
|
return await task.run(task);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(`任务 ${task.title}[${task.key}] 执行失败`, e.message);
|
logger.error(`任务 ${task.title}[${type}-${task.key}] 执行失败`, e.message);
|
||||||
task.status = "failed";
|
task.status = "failed";
|
||||||
task.error = e.message;
|
task.error = e.message;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import punycode from "punycode.js";
|
|||||||
import { SubDomainService } from "../../pipeline/service/sub-domain-service.js";
|
import { SubDomainService } from "../../pipeline/service/sub-domain-service.js";
|
||||||
import { SubDomainsGetter } from "../../pipeline/service/getter/sub-domain-getter.js";
|
import { SubDomainsGetter } from "../../pipeline/service/getter/sub-domain-getter.js";
|
||||||
import { TaskServiceBuilder } from "../../pipeline/service/getter/task-service-getter.js";
|
import { TaskServiceBuilder } from "../../pipeline/service/getter/task-service-getter.js";
|
||||||
|
import { BackTask, taskExecutor } from "../../cert/service/task-executor.js";
|
||||||
|
|
||||||
type CnameCheckCacheValue = {
|
type CnameCheckCacheValue = {
|
||||||
validating: boolean;
|
validating: boolean;
|
||||||
@@ -487,4 +488,49 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||||||
}
|
}
|
||||||
await this.getRepository().update(id, { status: "cname", mainDomain: "" });
|
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