mirror of
https://github.com/certd/certd.git
synced 2026-04-23 19:57:27 +08:00
Merge branch 'v2-dev' into v2
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
link-workspace-packages=true
|
||||
link-workspace-packages=deep
|
||||
prefer-workspace-packages=true
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复1Panel面板本身证书更新导致判定执行失败的问题 ([2689e6d](https://github.com/certd/certd/commit/2689e6d6c03aba21da90d5d45232c6ba08696be1))
|
||||
* 修复Cname情况下,无法使用DNS类型的bug ([26dad39](https://github.com/certd/certd/commit/26dad399d5768b3205da099ddc11809aef7d6224))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 日志查看自动滚动到底部 ([4a2f7eb](https://github.com/certd/certd/commit/4a2f7ebf87b7c027cebff7cb763f8f35f6d2aa36))
|
||||
* 系统设置中的代理设置优化为可全局生效,环境变量中的https_proxy设置将无效 ([381a37f](https://github.com/certd/certd/commit/381a37fbaa6b61c887eda743897ae00afb825bdf))
|
||||
* 新手导航在非编辑模式下不显示 ([18bfcc2](https://github.com/certd/certd/commit/18bfcc24ad0bde57bb04db8a4209861ec6b8ff1d))
|
||||
* 专业版试用,无需绑定账号 ([c7c4318](https://github.com/certd/certd/commit/c7c4318c11b65a76089787aa58939832d338a232))
|
||||
|
||||
## [1.27.4](https://github.com/certd/certd/compare/v1.27.3...v1.27.4) (2024-11-14)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.27.4",
|
||||
"version": "1.27.5",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -65,8 +65,8 @@
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.27.4",
|
||||
"@certd/pipeline": "^1.27.4",
|
||||
"@certd/lib-iframe": "^1.27.5",
|
||||
"@certd/pipeline": "^1.27.5",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
|
||||
@@ -40,6 +40,7 @@ if (props.modelValue == null) {
|
||||
}
|
||||
const emit = defineEmits<{
|
||||
"update:modelValue": any;
|
||||
change: any;
|
||||
}>();
|
||||
|
||||
const errorMessage = ref<string | null>(null);
|
||||
|
||||
+2
-1
@@ -33,7 +33,7 @@
|
||||
<span class="label">DNS类型:</span>
|
||||
<span class="input">
|
||||
<fs-dict-select
|
||||
v-model="item.dnsProviderType"
|
||||
v-model:value="item.dnsProviderType"
|
||||
size="small"
|
||||
:dict="dnsProviderTypeDict"
|
||||
placeholder="DNS提供商"
|
||||
@@ -79,6 +79,7 @@ import CnameVerifyPlan from "./cname-verify-plan.vue";
|
||||
import psl from "psl";
|
||||
import { Form } from "ant-design-vue";
|
||||
import { DomainsVerifyPlanInput } from "./type";
|
||||
import { CnameRecord } from "./api";
|
||||
defineOptions({
|
||||
name: "DomainsVerifyPlanEditor"
|
||||
});
|
||||
|
||||
@@ -158,7 +158,7 @@ const steps = ref<Step[]>([
|
||||
title: "设置定时执行",
|
||||
descriptions: [
|
||||
"流水线测试成功,接下来配置定时触发,以后每天定时执行就不用管了",
|
||||
"推荐配置每天运行一次,在到期前20天才会重新申请新证书并部署,没到期前会自动跳过,不会重复申请。"
|
||||
"推荐配置每天运行一次,在到期前35天才会重新申请新证书并部署,没到期前会自动跳过,不会重复申请。"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,3 +7,11 @@ export async function doActive(form: any) {
|
||||
data: form
|
||||
});
|
||||
}
|
||||
|
||||
export async function getVipTrial() {
|
||||
return await request({
|
||||
url: "/sys/plus/getVipTrial",
|
||||
method: "post",
|
||||
data: {}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="tsx" setup>
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import { computed, reactive } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import * as api from "./api";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -129,21 +130,27 @@ function goAccount() {
|
||||
router.push("/sys/account");
|
||||
}
|
||||
|
||||
async function getVipTrial() {
|
||||
const res = await api.getVipTrial();
|
||||
message.success(`恭喜,您已获得专业版${res.duration}天试用`);
|
||||
await settingStore.init();
|
||||
}
|
||||
|
||||
function openTrialModal() {
|
||||
Modal.destroyAll();
|
||||
|
||||
modal.confirm({
|
||||
title: "7天专业版试用获取",
|
||||
okText: "立即去绑定账号",
|
||||
okText: "立即获取",
|
||||
onOk() {
|
||||
goAccount();
|
||||
getVipTrial();
|
||||
},
|
||||
width: 600,
|
||||
content: () => {
|
||||
return (
|
||||
<div class="flex-col mt-10 mb-10">
|
||||
<div>感谢您对开源项目的支持</div>
|
||||
<div>绑定袖手账号后,即可获取7天专业版试用</div>
|
||||
<div>点击确认,即可获取7天专业版试用</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ export default defineComponent({
|
||||
chooseForm.show = false;
|
||||
console.log("choose ok:", selectedId.value);
|
||||
refreshTarget(selectedId.value);
|
||||
ctx.emit("change", selectedId.value);
|
||||
ctx.emit("update:modelValue", selectedId.value);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -140,7 +140,7 @@ async function onLoaded(pipeline: PipelineDetail) {
|
||||
}
|
||||
const res = await api.GetCount();
|
||||
LocalStorage.set("pipeline-count", res.count);
|
||||
if (res.count <= 1) {
|
||||
if (res.count <= 1 && editMode.value === true) {
|
||||
await nextTick();
|
||||
tourHandleOpen(true);
|
||||
}
|
||||
|
||||
+17
-3
@@ -56,7 +56,7 @@
|
||||
<a key="edit" @click="stepEdit(currentTask, element, index)">编辑</a>
|
||||
<a key="edit" @click="stepCopy(currentTask, element, index)">复制</a>
|
||||
<a key="remove" @click="stepDelete(currentTask, index)">删除</a>
|
||||
<a key="disabled" @click="element.disabled = !!!element.disabled">{{ element.disabled ? "启用" : "禁用" }}</a>
|
||||
<a key="disabled" @click="toggleDisabled(currentTask, element)">{{ element.disabled ? "启用" : "禁用" }}</a>
|
||||
<fs-icon v-plus class="icon-button handle cursor-move" title="拖动排序" icon="ion:move-outline"></fs-icon>
|
||||
</div>
|
||||
</div>
|
||||
@@ -88,6 +88,7 @@ import { CopyOutlined } from "@ant-design/icons-vue";
|
||||
import VDraggable from "vuedraggable";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { filter } from "lodash-es";
|
||||
export default {
|
||||
name: "PiTaskForm",
|
||||
components: { CopyOutlined, PiStepForm, VDraggable },
|
||||
@@ -152,7 +153,11 @@ export default {
|
||||
});
|
||||
};
|
||||
|
||||
return { stepAdd, stepEdit, stepCopy, stepDelete, stepFormRef };
|
||||
const toggleDisabled = (task: any, step: any) => {
|
||||
step.disabled = !!!step.disabled;
|
||||
};
|
||||
|
||||
return { stepAdd, stepEdit, stepCopy, stepDelete, toggleDisabled, stepFormRef };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,7 +167,7 @@ export default {
|
||||
function useTaskForm() {
|
||||
const mode = ref("add");
|
||||
const callback = ref();
|
||||
const currentTask = ref({ title: undefined, steps: [] });
|
||||
const currentTask = ref({ title: undefined, steps: [], disabled: false });
|
||||
provide("currentTask", currentTask);
|
||||
const taskFormRef: Ref<any> = ref(null);
|
||||
const taskDrawerVisible = ref(false);
|
||||
@@ -219,6 +224,15 @@ export default {
|
||||
console.error("表单验证失败:", e);
|
||||
return;
|
||||
}
|
||||
const task: any = currentTask.value;
|
||||
const allDisabled = filter(task.steps, (item: any) => {
|
||||
return item.disabled;
|
||||
});
|
||||
if (task.steps.length > 0 && task.steps.length === allDisabled.length) {
|
||||
task.disabled = true;
|
||||
} else {
|
||||
task.disabled = false;
|
||||
}
|
||||
|
||||
callback.value("save", currentTask.value);
|
||||
taskDrawerClose();
|
||||
|
||||
-2
@@ -61,7 +61,6 @@ export default {
|
||||
if (currentHistory?.value?.logs != null) {
|
||||
node.logs = computed(() => {
|
||||
if (currentHistory?.value?.logs && currentHistory.value?.logs[node.node.id] != null) {
|
||||
console.log("log changed", node.node.id);
|
||||
const logs = currentHistory.value?.logs[node.node.id];
|
||||
const list = [];
|
||||
for (let log of logs) {
|
||||
@@ -86,7 +85,6 @@ export default {
|
||||
},
|
||||
async () => {
|
||||
let el = document.querySelector(`.pi-task-view-logs.${node.node.id}`);
|
||||
console.log("el", el);
|
||||
//判断当前是否在底部
|
||||
const isBottom = el ? el.scrollHeight - el.scrollTop === el.clientHeight : true;
|
||||
await nextTick();
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<span class="flex-o w-100">
|
||||
<span class="ellipsis flex-1 task-title" :class="{ 'in-edit': editMode }">{{ task.title }}</span>
|
||||
<span class="ellipsis flex-1 task-title" :class="{ 'in-edit': editMode, deleted: task.disabled }">{{ task.title }}</span>
|
||||
<pi-status-show :status="task.status?.result"></pi-status-show>
|
||||
</span>
|
||||
</a-popover>
|
||||
@@ -304,7 +304,6 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const loadCurrentHistoryDetail = async () => {
|
||||
console.log("load history logs");
|
||||
const detail: RunHistory = await props.options?.getHistoryDetail({ historyId: currentHistory.value.id });
|
||||
currentHistory.value.logs = detail.logs;
|
||||
_.merge(currentHistory.value.pipeline, detail.pipeline);
|
||||
@@ -329,7 +328,6 @@ export default defineComponent({
|
||||
if (reload) {
|
||||
histories.value = [];
|
||||
}
|
||||
console.log("load history list");
|
||||
const historyList = await props.options.getHistoryList({ pipelineId: pipeline.value.id });
|
||||
if (!historyList) {
|
||||
return;
|
||||
@@ -885,6 +883,9 @@ export default defineComponent({
|
||||
&.in-edit {
|
||||
margin-right: 28px;
|
||||
}
|
||||
&.disabled{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
link-workspace-packages=true
|
||||
link-workspace-packages=deep
|
||||
prefer-workspace-packages=true
|
||||
|
||||
@@ -3,6 +3,19 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复角色无法删除的bug ([66629a5](https://github.com/certd/certd/commit/66629a591aecc2d8364ea415c7afc3f9d0406562))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 新手导航在非编辑模式下不显示 ([18bfcc2](https://github.com/certd/certd/commit/18bfcc24ad0bde57bb04db8a4209861ec6b8ff1d))
|
||||
* 优化腾讯云 cloudflare 重复解析记录时的返回值 ([90d1b68](https://github.com/certd/certd/commit/90d1b68bd6cf232fbe085234efe07d29b7690044))
|
||||
* 支持namesilo ([80159ec](https://github.com/certd/certd/commit/80159ecca895103d0495f3217311199e66056572))
|
||||
* 专业版试用,无需绑定账号 ([c7c4318](https://github.com/certd/certd/commit/c7c4318c11b65a76089787aa58939832d338a232))
|
||||
|
||||
## [1.27.4](https://github.com/certd/certd/compare/v1.27.3...v1.27.4) (2024-11-14)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-server",
|
||||
"version": "1.27.4",
|
||||
"version": "1.27.5",
|
||||
"description": "fast-server base midway",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -29,17 +29,17 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@certd/acme-client": "^1.27.4",
|
||||
"@certd/basic": "^1.27.4",
|
||||
"@certd/commercial-core": "^1.27.4",
|
||||
"@certd/lib-huawei": "^1.27.4",
|
||||
"@certd/lib-k8s": "^1.27.4",
|
||||
"@certd/lib-server": "^1.27.4",
|
||||
"@certd/midway-flyway-js": "^1.27.4",
|
||||
"@certd/pipeline": "^1.27.4",
|
||||
"@certd/plugin-cert": "^1.27.4",
|
||||
"@certd/plugin-plus": "^1.27.4",
|
||||
"@certd/plus-core": "^1.27.4",
|
||||
"@certd/acme-client": "^1.27.5",
|
||||
"@certd/basic": "^1.27.5",
|
||||
"@certd/commercial-core": "^1.27.5",
|
||||
"@certd/lib-huawei": "^1.27.5",
|
||||
"@certd/lib-k8s": "^1.27.5",
|
||||
"@certd/lib-server": "^1.27.5",
|
||||
"@certd/midway-flyway-js": "^1.27.5",
|
||||
"@certd/pipeline": "^1.27.5",
|
||||
"@certd/plugin-cert": "^1.27.5",
|
||||
"@certd/plugin-plus": "^1.27.5",
|
||||
"@certd/plus-core": "^1.27.5",
|
||||
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
|
||||
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
|
||||
"@koa/cors": "^5.0.0",
|
||||
@@ -84,6 +84,7 @@
|
||||
"pg": "^8.12.0",
|
||||
"psl": "^1.9.0",
|
||||
"qiniu": "^7.12.0",
|
||||
"qs": "^6.13.1",
|
||||
"querystring": "^0.2.1",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rimraf": "^5.0.5",
|
||||
|
||||
@@ -35,6 +35,12 @@ export class SysPlusController extends BaseController {
|
||||
|
||||
return this.ok(true);
|
||||
}
|
||||
|
||||
@Post('/getVipTrial', { summary: 'sys:settings:edit' })
|
||||
async getVipTrial(@Body(ALL) body) {
|
||||
const res = await this.plusService.getVipTrial();
|
||||
return this.ok(res);
|
||||
}
|
||||
//
|
||||
// @Get('/test', { summary: Constants.per.guest })
|
||||
// async test() {
|
||||
|
||||
@@ -49,19 +49,31 @@ export class CloudflareDnsProvider extends AbstractDnsProvider<CloudflareRecord>
|
||||
}
|
||||
|
||||
private async doRequestApi(url: string, data: any = null, method = 'post') {
|
||||
const res = await this.http.request<any, any>({
|
||||
url,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${this.access.apiToken}`,
|
||||
},
|
||||
data,
|
||||
});
|
||||
if (!res.success) {
|
||||
throw new Error(`${JSON.stringify(res.errors)}`);
|
||||
try {
|
||||
const res = await this.http.request<any, any>({
|
||||
url,
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${this.access.apiToken}`,
|
||||
},
|
||||
data,
|
||||
});
|
||||
|
||||
if (!res.success) {
|
||||
throw new Error(`${JSON.stringify(res.errors)}`);
|
||||
}
|
||||
return res;
|
||||
} catch (e: any) {
|
||||
const data = e.response?.data;
|
||||
if (data && data.success === false && data.errors && data.errors.length > 0) {
|
||||
if (data.errors[0].code === 81058) {
|
||||
this.logger.info('dns解析记录重复');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,14 +100,30 @@ export class CloudflareDnsProvider extends AbstractDnsProvider<CloudflareRecord>
|
||||
type: type,
|
||||
ttl: 60,
|
||||
});
|
||||
const record = res.result as CloudflareRecord;
|
||||
this.logger.info(`添加域名解析成功:fullRecord=${fullRecord},value=${value}`);
|
||||
this.logger.info(`dns解析记录:${JSON.stringify(record)}`);
|
||||
|
||||
let record: any = null;
|
||||
if (res == null) {
|
||||
//重复的记录
|
||||
this.logger.info(`dns解析记录重复,无需重复添加`);
|
||||
record = await this.findRecord(zoneId, options);
|
||||
} else {
|
||||
record = res.result as CloudflareRecord;
|
||||
this.logger.info(`添加域名解析成功:fullRecord=${fullRecord},value=${value}`);
|
||||
this.logger.info(`dns解析记录:${JSON.stringify(record)}`);
|
||||
}
|
||||
//本接口需要返回本次创建的dns解析记录,这个记录会在删除的时候用到
|
||||
return record;
|
||||
}
|
||||
|
||||
async findRecord(zoneId: string, options: CreateRecordOptions): Promise<CloudflareRecord | null> {
|
||||
const { fullRecord, value } = options;
|
||||
const url = `https://api.cloudflare.com/client/v4/zones/${zoneId}/dns_records?type=TXT&name=${fullRecord}&content=${value}`;
|
||||
const res = await this.doRequestApi(url, null, 'get');
|
||||
if (res.result.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return res.result[0] as CloudflareRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除dns解析记录,清理申请痕迹
|
||||
* @param options
|
||||
@@ -105,7 +133,7 @@ export class CloudflareDnsProvider extends AbstractDnsProvider<CloudflareRecord>
|
||||
const record = options.recordRes;
|
||||
this.logger.info('删除域名解析:', fullRecord, value);
|
||||
if (!record) {
|
||||
this.logger.info('record不存在');
|
||||
this.logger.info('record为空,不执行删除');
|
||||
return;
|
||||
}
|
||||
//这里调用删除txt dns解析记录接口
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
|
||||
import { CertInfo, CertReader } from '@certd/plugin-cert';
|
||||
import { isDev } from '@certd/basic';
|
||||
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-plus';
|
||||
import { optionsUtils } from '@certd/basic/dist/utils/util.options.js';
|
||||
|
||||
@IsTaskPlugin({
|
||||
name: 'demoTest',
|
||||
@@ -13,8 +14,6 @@ import { isDev } from '@certd/basic';
|
||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||
},
|
||||
},
|
||||
// 你开发的插件要删除此项,否则不会在生产环墋中显示
|
||||
deprecated: isDev() ? '测试插件,生产环境不显示' : undefined,
|
||||
})
|
||||
export class DemoTestPlugin extends AbstractTaskPlugin {
|
||||
//测试参数
|
||||
@@ -65,6 +64,10 @@ export class DemoTestPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
cert!: CertInfo;
|
||||
|
||||
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
|
||||
//前端可以展示,当前申请的证书域名列表
|
||||
certDomains!: string[];
|
||||
|
||||
//授权选择框
|
||||
@TaskInput({
|
||||
title: 'demo授权',
|
||||
@@ -78,7 +81,23 @@ export class DemoTestPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
accessId!: string;
|
||||
|
||||
@TaskInput(
|
||||
createRemoteSelectInputDefine({
|
||||
title: '从后端获取选项',
|
||||
helper: '选择时可以从后端获取选项',
|
||||
typeName: 'demoTest',
|
||||
action: DemoTestPlugin.prototype.onGetSiteList.name,
|
||||
//当以下参数变化时,触发获取选项
|
||||
watches: ['certDomains', 'accessId'],
|
||||
required: true,
|
||||
})
|
||||
)
|
||||
siteName!: string | string[];
|
||||
|
||||
//插件实例化时执行的方法
|
||||
async onInstance() {}
|
||||
|
||||
//插件执行方法
|
||||
async execute(): Promise<void> {
|
||||
const { select, text, cert, accessId } = this;
|
||||
|
||||
@@ -102,6 +121,40 @@ export class DemoTestPlugin extends AbstractTaskPlugin {
|
||||
this.logger.info('switch:', this.switch);
|
||||
this.logger.info('授权id:', accessId);
|
||||
}
|
||||
|
||||
//此方法演示,如何让前端在添加插件时可以从后端获取选项,这里是后端返回选项的方法
|
||||
async onGetSiteList() {
|
||||
if (!this.accessId) {
|
||||
throw new Error('请选择Access授权');
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const access = await this.accessService.getById(this.accessId);
|
||||
|
||||
// const siteRes = await this.ctx.http.request({
|
||||
// url: '你的服务端获取选项的请求地址',
|
||||
// method: 'GET',
|
||||
// data: {
|
||||
// token:access.xxxx
|
||||
// }, //请求参数
|
||||
// });
|
||||
//以下是模拟数据
|
||||
const siteRes = [
|
||||
{ id: 1, siteName: 'site1.com' },
|
||||
{ id: 2, siteName: 'site2.com' },
|
||||
{ id: 3, siteName: 'site2.com' },
|
||||
];
|
||||
//转换为前端所需要的格式
|
||||
const options = siteRes.map((item: any) => {
|
||||
return {
|
||||
value: item.siteName,
|
||||
label: item.siteName,
|
||||
domain: item.siteName,
|
||||
};
|
||||
});
|
||||
//将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择
|
||||
return optionsUtils.buildGroupOptions(options, this.certDomains);
|
||||
}
|
||||
}
|
||||
//实例化一下,注册插件
|
||||
new DemoTestPlugin();
|
||||
|
||||
@@ -105,6 +105,10 @@ export class HuaweiDnsProvider extends AbstractDnsProvider {
|
||||
async removeRecord(options: RemoveRecordOptions<any>): Promise<any> {
|
||||
const { fullRecord, value } = options.recordReq;
|
||||
const record = options.recordRes;
|
||||
if (!record) {
|
||||
this.logger.info('解析记录recordId为空,不执行删除', fullRecord, value);
|
||||
return;
|
||||
}
|
||||
const req: ApiRequestOptions = {
|
||||
url: `${this.dnsEndpoint}/v2/zones/${record.zone_id}/recordsets/${record.id}`,
|
||||
method: 'DELETE',
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { IsAccess, AccessInput, BaseAccess } from '@certd/pipeline';
|
||||
|
||||
/**
|
||||
* 这个注解将注册一个授权配置
|
||||
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
|
||||
*/
|
||||
@IsAccess({
|
||||
name: 'namesilo',
|
||||
title: 'namesilo授权',
|
||||
desc: '',
|
||||
})
|
||||
export class NamesiloAccess extends BaseAccess {
|
||||
/**
|
||||
* 授权属性配置
|
||||
*/
|
||||
@AccessInput({
|
||||
title: 'API Key',
|
||||
component: {
|
||||
placeholder: 'api key',
|
||||
},
|
||||
helper: '前往 [获取API Key](https://www.namesilo.com/account/api-manager)',
|
||||
required: true,
|
||||
encrypt: true,
|
||||
})
|
||||
apiKey = '';
|
||||
}
|
||||
|
||||
new NamesiloAccess();
|
||||
@@ -0,0 +1,109 @@
|
||||
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
|
||||
import { Autowire } from '@certd/pipeline';
|
||||
import { HttpClient, ILogger } from '@certd/basic';
|
||||
import qs from 'qs';
|
||||
import { NamesiloAccess } from './access.js';
|
||||
import { merge } from 'lodash-es';
|
||||
|
||||
export type NamesiloRecord = {
|
||||
record_id: string;
|
||||
};
|
||||
|
||||
// 这里通过IsDnsProvider注册一个dnsProvider
|
||||
@IsDnsProvider({
|
||||
name: 'namesilo',
|
||||
title: 'namesilo',
|
||||
desc: 'namesilo dns provider',
|
||||
// 这里是对应的 cloudflare的access类型名称
|
||||
accessType: 'namesilo',
|
||||
})
|
||||
export class NamesiloDnsProvider extends AbstractDnsProvider<NamesiloRecord> {
|
||||
// 通过Autowire传递context
|
||||
@Autowire()
|
||||
logger!: ILogger;
|
||||
access!: NamesiloAccess;
|
||||
http!: HttpClient;
|
||||
async onInstance() {
|
||||
//一些初始化的操作
|
||||
// 也可以通过ctx成员变量传递context, 与Autowire效果一样
|
||||
this.access = this.ctx.access as NamesiloAccess;
|
||||
this.http = this.ctx.http;
|
||||
}
|
||||
|
||||
private async doRequest(url: string, params: any = null) {
|
||||
params = merge(
|
||||
{
|
||||
version: 1,
|
||||
type: 'json',
|
||||
key: this.access.apiKey,
|
||||
},
|
||||
params
|
||||
);
|
||||
const qsString = qs.stringify(params);
|
||||
url = `${url}?${qsString}`;
|
||||
const res = await this.http.request<any, any>({
|
||||
url,
|
||||
baseURL: 'https://www.namesilo.com',
|
||||
method: 'get',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (res.reply?.code !== '300') {
|
||||
throw new Error(`${JSON.stringify(res.reply.detail)}`);
|
||||
}
|
||||
return res.reply;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建dns解析记录,用于验证域名所有权
|
||||
*/
|
||||
async createRecord(options: CreateRecordOptions): Promise<NamesiloRecord> {
|
||||
/**
|
||||
* fullRecord: '_acme-challenge.test.example.com',
|
||||
* value: 一串uuid
|
||||
* type: 'TXT',
|
||||
* domain: 'example.com'
|
||||
*/
|
||||
const { fullRecord, hostRecord, value, type, domain } = options;
|
||||
this.logger.info('添加域名解析:', fullRecord, value, type, domain);
|
||||
|
||||
//domain=namesilo.com&rrtype=A&rrhost=test&rrvalue=55.55.55.55&rrttl=7207
|
||||
const record: any = await this.doRequest('/api/dnsAddRecord', {
|
||||
domain,
|
||||
rrtype: type,
|
||||
rrhost: hostRecord,
|
||||
rrvalue: value,
|
||||
rrttl: 10,
|
||||
});
|
||||
|
||||
//本接口需要返回本次创建的dns解析记录,这个记录会在删除的时候用到
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除dns解析记录,清理申请痕迹
|
||||
* @param options
|
||||
*/
|
||||
async removeRecord(options: RemoveRecordOptions<NamesiloRecord>): Promise<void> {
|
||||
const { fullRecord, value } = options.recordReq;
|
||||
const record = options.recordRes;
|
||||
this.logger.info('删除域名解析:', fullRecord, value);
|
||||
if (!record && !record.record_id) {
|
||||
this.logger.info('record为空,不执行删除');
|
||||
return;
|
||||
}
|
||||
//这里调用删除txt dns解析记录接口
|
||||
|
||||
const recordId = record.record_id;
|
||||
await this.doRequest('/api/dnsDeleteRecord', {
|
||||
domain: options.recordReq.domain,
|
||||
record_id: recordId,
|
||||
});
|
||||
this.logger.info(`删除域名解析成功:fullRecord=${fullRecord},value=${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
//实例化这个provider,将其自动注册到系统中
|
||||
new NamesiloDnsProvider();
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './dns-provider.js';
|
||||
export * from './access.js';
|
||||
+19
-1
@@ -65,15 +65,33 @@ export class TencentDnsProvider extends AbstractDnsProvider {
|
||||
} catch (e: any) {
|
||||
if (e?.code === 'InvalidParameter.DomainRecordExist') {
|
||||
this.logger.info('域名解析已存在,无需重复添加:', fullRecord, value);
|
||||
return {};
|
||||
return await this.findRecord(options);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async findRecord(options: CreateRecordOptions): Promise<any> {
|
||||
const params = {
|
||||
Domain: options.domain,
|
||||
RecordType: [options.type],
|
||||
Keyword: options.hostRecord,
|
||||
RecordValue: options.value,
|
||||
};
|
||||
const ret = await this.client.DescribeRecordFilterList(params);
|
||||
if (ret.RecordList && ret.RecordList.length > 0) {
|
||||
this.logger.info('已存在解析记录:', ret.RecordList);
|
||||
return ret.RecordList[0];
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
async removeRecord(options: RemoveRecordOptions<any>) {
|
||||
const { fullRecord, value, domain } = options.recordReq;
|
||||
const record = options.recordRes;
|
||||
if (!record) {
|
||||
this.logger.info('解析记录recordId为空,不执行删除', fullRecord, value);
|
||||
}
|
||||
const params = {
|
||||
Domain: domain,
|
||||
RecordId: record.RecordId,
|
||||
|
||||
Reference in New Issue
Block a user