Merge remote-tracking branch 'origin/v2-dev' into v2-dev

This commit is contained in:
xiaojunnuo
2024-12-26 01:47:50 +08:00
87 changed files with 469 additions and 200 deletions

View File

@@ -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.29.1](https://github.com/certd/certd/compare/v1.29.0...v1.29.1) (2024-12-25)
### Bug Fixes
* 免费套餐支持购买 ([f5ec987](https://github.com/certd/certd/commit/f5ec9870fd6af1f0c9099852bbdb4d07813ccce8))
* 修复某处金额转换丢失精度的bug ([d2d6f12](https://github.com/certd/certd/commit/d2d6f12218cbe7bd55f4ae082b93084be85f0a7b))
* 修复新版本小红点显示错误问题 ([fe4786e](https://github.com/certd/certd/commit/fe4786e168afe03a5243dd67971476c348339809))
### Performance Improvements
* 用户创建证书流水线没有购买套餐或者超限时提前报错 ([472f06c](https://github.com/certd/certd/commit/472f06c2d190d0ae48e8b53c18bc278437656a1c))
* 优化插件名称显示 ([26adf7d](https://github.com/certd/certd/commit/26adf7d437e674385f26a8f92fded6521a620671))
# [1.29.0](https://github.com/certd/certd/compare/v1.28.4...v1.29.0) (2024-12-24)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.29.0",
"version": "1.29.1",
"private": true,
"scripts": {
"dev": "vite --open",
@@ -66,8 +66,8 @@
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@certd/lib-iframe": "^1.29.0",
"@certd/pipeline": "^1.29.0",
"@certd/lib-iframe": "^1.29.1",
"@certd/pipeline": "^1.29.1",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12",

View File

@@ -122,12 +122,11 @@ export const useSettingStore = defineStore({
};
return vipLabelMap[this.plusInfo?.vipType || "free"];
},
// @ts-ignore
getHeaderMenus() {
getHeaderMenus(): { menus: any[] } {
// @ts-ignore
return this.headerMenus?.menus || { menus: [] };
},
isSuiteEnabled() {
isSuiteEnabled(): boolean {
// @ts-ignore
return this.suiteSetting?.enabled === true;
}

View File

@@ -6,6 +6,7 @@ import * as mitt from "./util.mitt";
import { routerUtils } from "./util.router";
import { treeUtils } from "./util.tree";
import { hashUtils } from "./util.hash";
import { amountUtils } from "./util.amount";
export const util = {
...envs,
...sites,
@@ -14,5 +15,7 @@ export const util = {
...mitt,
router: routerUtils,
tree: treeUtils,
hash: hashUtils
hash: hashUtils,
amount: amountUtils
};
export const utils = util;

View File

@@ -0,0 +1,9 @@
export const amountUtils = {
toCent(amount: number): number {
return parseInt((amount * 100).toFixed(0));
},
toYuan(amount: number): number {
return parseFloat((amount / 100).toFixed(2));
}
};

View File

@@ -4,10 +4,14 @@ import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, Edi
import { siteInfoApi } from "./api";
import dayjs from "dayjs";
import { notification } from "ant-design-vue";
import { useSettingStore } from "/@/store/modules/settings";
import { mySuiteApi } from "/@/views/certd/suite/mine/api";
import { mitter } from "/@/utils/util.mitt";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { t } = useI18n();
const api = siteInfoApi;
const { crudBinding } = crudExpose;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
@@ -28,6 +32,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
return res;
};
const settingsStore = useSettingStore();
return {
crudOptions: {
request: {
@@ -51,6 +57,38 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
width: 600
}
},
actionbar: {
buttons: {
add: {
async click() {
if (!settingsStore.isPlus) {
//非plus
if (crudBinding.value.data.length >= 1) {
notification.error({
message: "基础版只能添加一个监控站点,请赞助升级专业版"
});
mitter.emit("openVipModal");
return;
}
}
//检查是否监控站点数量超出限制
if (settingsStore.isComm && settingsStore.suiteSetting.enabled) {
//检查数量是否超限
const suiteDetail = await mySuiteApi.SuiteDetailGet();
const max = suiteDetail.monitorCount.max;
if (max != -1 && max <= suiteDetail.monitorCount.used) {
notification.error({
message: `对不起,您最多只能创建条${max}监控记录,请购买或升级套餐`
});
return;
}
}
await crudExpose.openAdd({});
}
}
}
},
rowHandle: {
fixed: "right",
width: 240,
@@ -111,6 +149,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
form: {
rules: [
{ required: true, message: "请输入域名" },
//@ts-ignore
{ type: "domains", message: "请输入正确的域名" }
]
},

View File

@@ -15,6 +15,7 @@ import { useModal } from "/@/use/use-modal";
import CertView from "./cert-view.vue";
import { eachStages } from "./utils";
import { createNotificationApi as createNotificationApi } from "../notification/api";
import { mySuiteApi } from "/@/views/certd/suite/mine/api";
export default function ({ crudExpose, context: { certdFormRef, groupDictRef, selectedRowKeys } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const router = useRouter();
const { t } = useI18n();
@@ -94,7 +95,23 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
lastResRef.value = res;
return res;
};
function addCertdPipeline() {
const settingsStore = useSettingStore();
async function addCertdPipeline() {
//检查是否流水线数量超出限制
if (settingsStore.isComm && settingsStore.suiteSetting.enabled) {
//检查数量是否超限
const suiteDetail = await mySuiteApi.SuiteDetailGet();
const max = suiteDetail.pipelineCount.max;
if (max != -1 && max <= suiteDetail.pipelineCount.used) {
notification.error({
message: `对不起,您最多只能创建${max}条流水线,请购买或升级套餐`
});
return;
}
}
certdFormRef.value.open(async ({ form }: any) => {
// 添加certd pipeline
const triggers = [];

View File

@@ -1,5 +1,5 @@
<template>
<a-drawer v-model:open="stepDrawerVisible" placement="right" :closable="true" width="700px" class="step-form-drawer" :class="{ fullscreen }">
<a-drawer v-model:open="stepDrawerVisible" placement="right" :closable="true" width="760px" class="step-form-drawer" :class="{ fullscreen }">
<template #title>
<div>
编辑步骤
@@ -48,7 +48,7 @@
<a-card-meta>
<template #title>
<fs-icon class="plugin-icon" :icon="item.icon || 'clarity:plugin-line'"></fs-icon>
<span class="title">{{ item.title }}</span>
<span class="title" :title="item.title">{{ item.title }}</span>
<vip-button v-if="item.needPlus" mode="icon" />
</template>
<template #description>

View File

@@ -52,6 +52,14 @@ export async function TradeCreate(form: TradeCreateReq) {
});
}
export async function TradeCreateFree(form: TradeCreateReq) {
return await request({
url: "/suite/trade/createFree",
method: "POST",
data: form
});
}
export async function GetPaymentTypes() {
return await request({
url: "/suite/trade/payments",

View File

@@ -35,6 +35,7 @@ import { ref } from "vue";
import * as api from "./api";
import ProductInfo from "/@/views/certd/suite/product-info.vue";
import OrderModal from "/@/views/certd/suite/order-modal.vue";
import { notification } from "ant-design-vue";
const suites = ref([]);
const addons = ref([]);
@@ -48,6 +49,20 @@ async function loadProducts() {
loadProducts();
const orderModalRef = ref<any>(null);
async function doOrder(req: any) {
if (req.price === 0) {
//如果是0直接请求创建订单
await api.TradeCreateFree({
productId: req.productId,
duration: req.duration,
num: 1,
payType: "free"
});
notification.success({
message: "套餐购买成功"
});
return;
}
await orderModalRef.value.open({
...req
});

View File

@@ -16,7 +16,7 @@ export type SuiteDetail = {
monitorCount?: SuiteValue;
};
export default {
export const mySuiteApi = {
async GetList(query: any) {
return await request({
url: apiPrefix + "/page",

View File

@@ -1,10 +1,9 @@
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import api from "./api";
import { mySuiteApi as api } from "./api";
import { useRouter } from "vue-router";
import SuiteValueEdit from "/@/views/sys/suite/product/suite-value-edit.vue";
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
import DurationValue from "/@/views/sys/suite/product/duration-value.vue";
import dayjs from "dayjs";
import UserSuiteStatus from "/@/views/certd/suite/mine/user-suite-status.vue";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
@@ -263,7 +262,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
userSuite: compute(({ row }) => {
return row;
}),
currentSuite: context.detail
currentSuite: context.currentSuite
},
conditionalRender: {
match() {

View File

@@ -15,20 +15,27 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted, ref } from "vue";
import { computed, onActivated, onMounted, ref } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import api, { SuiteDetail } from "/@/views/certd/suite/mine/api";
import { mySuiteApi, SuiteDetail } from "/@/views/certd/suite/mine/api";
import SuiteCard from "/@/views/framework/home/dashboard/suite-card.vue";
defineOptions({
name: "MySuites"
});
const detail = ref<SuiteDetail>({});
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { detail } });
const currentSuite = computed(() => {
if (detail.value?.suites && detail.value.suites.length > 0) {
return detail.value.suites[0];
}
return null;
});
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { detail, currentSuite } });
async function loadSuiteDetail() {
detail.value = await api.SuiteDetailGet();
detail.value = await mySuiteApi.SuiteDetailGet();
}
// 页面打开后获取列表数据

View File

@@ -80,7 +80,7 @@ const productTypeDictRef = dict({
const emit = defineEmits(["order"]);
async function doOrder() {
emit("order", { product: props.product, productId: props.product.id, duration: selected.value.duration });
emit("order", { product: props.product, productId: props.product.id, duration: selected.value.duration, price: selected.value.price });
}
</script>

View File

@@ -162,7 +162,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
]
}),
column: {
width: 100
width: 100,
align: "center"
}
},
payType: {
@@ -173,14 +174,16 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
data: [
{ label: "聚合支付", value: "yizhifu" },
{ label: "支付宝", value: "alipay" },
{ label: "微信", value: "wxpay" }
{ label: "微信", value: "wxpay" },
{ label: "免费", value: "free" }
]
}),
column: {
width: 100,
component: {
color: "auto"
}
},
align: "center"
}
},
payTime: {

View File

@@ -36,8 +36,8 @@
</span>
<span>(<expires-time-text :value="item.expiresTime" />)</span>
</a-tag>
<div class="flex-o ml-5">
暂无套餐 <a-button v-if="detail.suites?.length === 0" class="ml-5" type="primary" size="small" @click="goBuy">去购买</a-button>
<div v-if="detail.suites?.length === 0" class="flex-o ml-5">
暂无套餐 <a-button class="ml-5" type="primary" size="small" @click="goBuy">去购买</a-button>
</div>
</div>
</a-popover>
@@ -49,7 +49,7 @@
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
import { ref } from "vue";
import ExpiresTimeText from "/@/components/expires-time-text.vue";
import api, { SuiteDetail } from "/@/views/certd/suite/mine/api";
import { mySuiteApi, SuiteDetail } from "/@/views/certd/suite/mine/api";
import { FsIcon } from "@fast-crud/fast-crud";
import { useRouter } from "vue-router";
@@ -60,7 +60,7 @@ defineOptions({
const detail = ref<SuiteDetail>({});
async function loadSuiteDetail() {
detail.value = await api.SuiteDetailGet();
detail.value = await mySuiteApi.SuiteDetailGet();
}
loadSuiteDetail();

View File

@@ -166,6 +166,11 @@ export default defineComponent({
{
required: true,
message: "请输入图片验证码"
},
{
min: 4,
max: 4,
message: "请输入4位图片验证码"
}
],
smsCode: [

View File

@@ -7,7 +7,7 @@
<script lang="ts" setup>
import { computed } from "vue";
import { utils } from "/@/utils";
const props = withDefaults(
defineProps<{
modelValue?: number;
@@ -34,11 +34,11 @@ const priceValue = computed(() => {
if (props.modelValue == null) {
return 0;
}
return (props.modelValue / 100.0).toFixed(2);
return utils.amount.toYuan(props.modelValue);
});
const priceLabel = computed(() => {
if (priceValue.value === 0 || priceValue.value === "0.00") {
if (priceValue.value === 0) {
return "免费";
}
return `¥${priceValue.value}`;
@@ -47,7 +47,7 @@ const priceLabel = computed(() => {
const emit = defineEmits(["update:modelValue"]);
const onPriceChange = (price: number) => {
emit("update:modelValue", price * 100);
emit("update:modelValue", utils.amount.toCent(price));
};
</script>

View File

@@ -17,6 +17,7 @@
<div style="height: 400px">
<ProductManager @refreshed="onTableRefresh"></ProductManager>
</div>
<div class="helper">不建议设置免费套餐可以在下方配置注册赠送套餐或者在用户套餐管理中手动赠送套餐</div>
</a-form-item>
<a-form-item label="注册赠送套餐" name="registerGift">

View File

@@ -7,6 +7,10 @@ import { ref, watch } from "vue";
import { dict } from "@fast-crud/fast-crud";
import { request } from "/@/api/service";
defineOptions({
name: "SuiteDurationSelector"
});
const props = defineProps<{
modelValue?: {
productId?: number;

View File

@@ -155,7 +155,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
component: {
name: DurationValue,
vModel: "modelValue"
}
},
align: "center"
}
},
amount: {
@@ -182,7 +183,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
]
}),
column: {
width: 100
width: 100,
align: "center"
}
},
payType: {
@@ -193,14 +195,16 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
data: [
{ label: "聚合支付", value: "yizhifu" },
{ label: "支付宝", value: "alipay" },
{ label: "微信", value: "wxpay" }
{ label: "微信", value: "wxpay" },
{ label: "免费", value: "free" }
]
}),
column: {
width: 100,
component: {
color: "auto"
}
},
align: "center"
}
},
payTime: {

View File

@@ -1,62 +1,65 @@
import { request } from "/src/api/service";
export function createApi() {
const apiPrefix = "/sys/suite/user-suite";
return {
async GetList(query: any) {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query
});
},
const apiPrefix = "/sys/suite/user-suite";
export const sysUserSuiteApi = {
async GetList(query: any) {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query
});
},
async AddObj(obj: any) {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj
});
},
async AddObj(obj: any) {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj
});
},
async UpdateObj(obj: any) {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj
});
},
async UpdateObj(obj: any) {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj
});
},
async DelObj(id: number) {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
});
},
async DelObj(id: number) {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
});
},
async GetObj(id: number) {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id }
});
},
async ListAll() {
return await request({
url: apiPrefix + "/all",
method: "post"
});
},
async GetObj(id: number) {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id }
});
},
async ListAll() {
return await request({
url: apiPrefix + "/all",
method: "post"
});
},
async GetSimpleUserByIds(ids: number[]) {
return await request({
url: "/sys/authority/user/getSimpleUserByIds",
method: "post",
data: { ids }
});
}
};
}
export const pipelineGroupApi = createApi();
async GetSimpleUserByIds(ids: number[]) {
return await request({
url: "/sys/authority/user/getSimpleUserByIds",
method: "post",
data: { ids }
});
},
async PresentSuite(form: any) {
return await request({
url: apiPrefix + "/presentSuite",
method: "post",
data: form
});
}
};

View File

@@ -1,15 +1,14 @@
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { pipelineGroupApi } from "./api";
import { sysUserSuiteApi } from "./api";
import { useRouter } from "vue-router";
import SuiteValueEdit from "/@/views/sys/suite/product/suite-value-edit.vue";
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
import DurationValue from "/@/views/sys/suite/product/duration-value.vue";
import dayjs from "dayjs";
import createCrudOptionsUser from "/@/views/sys/authority/user/crud";
import UserSuiteStatus from "/@/views/certd/suite/mine/user-suite-status.vue";
import SuiteDurationSelector from "../setting/suite-duration-selector.vue";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const api = pipelineGroupApi;
const api = sysUserSuiteApi;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
@@ -26,7 +25,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const addRequest = async (req: AddReq) => {
const { form } = req;
const res = await api.AddObj(form);
const res = await api.PresentSuite(form);
return res;
};
@@ -57,16 +56,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
},
actionbar: {
buttons: {
add: { show: false }
// buy: {
// text: "购买",
// type: "primary",
// click() {
// router.push({
// path: "/certd/suite/buy"
// });
// }
// }
add: { text: "赠送套餐" }
}
},
toolbar: { show: false },
@@ -112,7 +102,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
show: true
},
form: {
rules: [{ required: true, message: "此项必填" }]
show: false
},
column: {
width: 200
@@ -143,6 +133,37 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}
}
},
//赠送
presentSuiteId: {
title: "赠送套餐",
type: "dict-select",
column: { show: false },
addForm: {
show: true,
component: {
name: SuiteDurationSelector,
vModel: "modelValue"
},
rules: [
{
validator: async (rule, value) => {
if (value && value.productId) {
return true;
}
throw new Error("请选择套餐");
}
}
]
},
valueResolve({ form, value }) {
debugger;
if (value && value.productId) {
form.productId = value.productId;
form.duration = value.duration;
}
},
form: { show: false }
},
productType: {
title: "类型",
type: "dict-select",
@@ -158,7 +179,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
]
}),
form: {
rules: [{ required: true, message: "此项必填" }]
show: false
},
column: {
width: 80,
@@ -179,6 +200,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
title: "域名数量",
type: "text",
form: {
show: false,
key: ["content", "maxDomainCount"],
component: {
name: SuiteValueEdit,
@@ -201,6 +223,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
title: "流水线数量",
type: "text",
form: {
show: false,
key: ["content", "maxPipelineCount"],
component: {
name: SuiteValueEdit,
@@ -223,6 +246,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
title: "部署次数",
type: "text",
form: {
show: false,
key: ["content", "maxDeployCount"],
component: {
name: SuiteValueEdit,
@@ -248,6 +272,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
title: "证书监控数量",
type: "text",
form: {
show: false,
key: ["content", "maxMonitorCount"],
component: {
name: SuiteValueEdit,
@@ -269,7 +294,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
duration: {
title: "时长",
type: "text",
form: {},
form: { show: false },
column: {
component: {
name: DurationValue,
@@ -304,11 +329,17 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
type: "date",
column: {
width: 150
},
form: {
show: false
}
},
expiresTime: {
title: "过期时间",
type: "date",
form: {
show: false
},
column: {
width: 150,
component: {
@@ -328,7 +359,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
]
}),
form: {
value: true
value: true,
show: false
},
column: {
width: 100,

View File

@@ -11,10 +11,10 @@
</template>
<script lang="ts" setup>
import { defineComponent, onActivated, onMounted } from "vue";
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { createApi } from "./api";
defineOptions({
name: "UserSuites"
});

View File

@@ -3,6 +3,13 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.29.1](https://github.com/certd/certd/compare/v1.29.0...v1.29.1) (2024-12-25)
### Performance Improvements
* 用户创建证书流水线没有购买套餐或者超限时提前报错 ([472f06c](https://github.com/certd/certd/commit/472f06c2d190d0ae48e8b53c18bc278437656a1c))
* 优化插件名称显示 ([26adf7d](https://github.com/certd/certd/commit/26adf7d437e674385f26a8f92fded6521a620671))
# [1.29.0](https://github.com/certd/certd/compare/v1.28.4...v1.29.0) (2024-12-24)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.29.0",
"version": "1.29.1",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -34,18 +34,18 @@
"@aws-sdk/client-acm": "^3.699.0",
"@aws-sdk/client-cloudfront": "^3.699.0",
"@aws-sdk/client-s3": "^3.705.0",
"@certd/acme-client": "^1.29.0",
"@certd/basic": "^1.29.0",
"@certd/commercial-core": "^1.29.0",
"@certd/lib-huawei": "^1.29.0",
"@certd/lib-k8s": "^1.29.0",
"@certd/lib-server": "^1.29.0",
"@certd/midway-flyway-js": "^1.29.0",
"@certd/pipeline": "^1.29.0",
"@certd/plugin-cert": "^1.29.0",
"@certd/plugin-lib": "^1.29.0",
"@certd/plugin-plus": "^1.29.0",
"@certd/plus-core": "^1.29.0",
"@certd/acme-client": "^1.29.1",
"@certd/basic": "^1.29.1",
"@certd/commercial-core": "^1.29.1",
"@certd/lib-huawei": "^1.29.1",
"@certd/lib-k8s": "^1.29.1",
"@certd/lib-server": "^1.29.1",
"@certd/midway-flyway-js": "^1.29.1",
"@certd/pipeline": "^1.29.1",
"@certd/plugin-cert": "^1.29.1",
"@certd/plugin-lib": "^1.29.1",
"@certd/plugin-plus": "^1.29.1",
"@certd/plus-core": "^1.29.1",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
"@koa/cors": "^5.0.0",

View File

@@ -1,7 +1,7 @@
import { Provide } from '@midwayjs/core';
import { IWebMiddleware, IMidwayKoaContext, NextFunction } from '@midwayjs/koa';
import { IMidwayKoaContext, IWebMiddleware, NextFunction } from '@midwayjs/koa';
import { logger } from '@certd/basic';
import { BaseException, Result } from '@certd/lib-server';
import { Result } from '@certd/lib-server';
@Provide()
export class GlobalExceptionMiddleware implements IWebMiddleware {
@@ -14,11 +14,7 @@ export class GlobalExceptionMiddleware implements IWebMiddleware {
await next();
logger.info('请求完成:', url, Date.now() - startTime + 'ms');
} catch (err) {
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err.message);
if (!(err instanceof BaseException)) {
logger.error(err);
}
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err);
ctx.status = 200;
if (err.code == null || typeof err.code !== 'number') {
err.code = 1;

View File

@@ -4,7 +4,7 @@ import { optionsUtils } from '@certd/basic/dist/utils/util.options.js';
@IsTaskPlugin({
name: 'DeployCertToAliyunCDN',
title: '部署证书至阿里云CDN',
title: '阿里云-部署证书至CDN',
icon: 'ant-design:aliyun-outlined',
group: pluginGroups.aliyun.key,
desc: '自动部署域名证书至阿里云CDN',

View File

@@ -5,7 +5,7 @@ import { CertInfo } from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToAliyunDCDN',
title: '部署证书至阿里云DCDN',
title: '阿里云-部署证书至DCDN',
icon: 'ant-design:aliyun-outlined',
group: pluginGroups.aliyun.key,
desc: '依赖证书申请前置任务自动部署域名证书至阿里云DCDN',

View File

@@ -3,7 +3,7 @@ import { AliyunAccess } from '@certd/plugin-lib';
import { CertInfo } from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToAliyunOSS',
title: '部署证书至阿里云OSS',
title: '阿里云-部署证书至OSS',
icon: 'ant-design:aliyun-outlined',
group: pluginGroups.aliyun.key,
desc: '自动部署域名证书至阿里云OSS',

View File

@@ -5,7 +5,7 @@ import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
@IsTaskPlugin({
name: 'AliyunDeployCertToWaf',
title: '部署至阿里云WAF',
title: '阿里云-部署至阿里云WAF',
icon: 'ant-design:aliyun-outlined',
group: pluginGroups.aliyun.key,
desc: '部署证书到阿里云WAF',

View File

@@ -23,7 +23,7 @@ const regionDict = [
@IsTaskPlugin({
name: 'uploadCertToAliyun',
title: '上传证书到阿里云',
title: '阿里云-上传证书到阿里云',
icon: 'ant-design:aliyun-outlined',
group: pluginGroups.aliyun.key,
desc: '如果不想在阿里云上同一份证书上传多次,可以把此任务作为前置任务,其他阿里云任务证书那一项选择此任务的输出',

View File

@@ -8,7 +8,7 @@ import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
@IsTaskPlugin({
name: 'AwsDeployToCloudFront',
title: '部署证书到 AWS CloudFront',
title: 'AWS-部署证书到CloudFront',
desc: '部署证书到 AWS CloudFront',
icon: 'clarity:plugin-line',
group: pluginGroups.aws.key,

View File

@@ -5,7 +5,7 @@ import { AwsAcmClient } from '../libs/aws-acm-client.js';
@IsTaskPlugin({
name: 'AwsUploadToACM',
title: '上传证书 AWS ACM',
title: 'AWS-上传证书到ACM',
desc: '上传证书 AWS ACM',
icon: 'clarity:plugin-line',
group: pluginGroups.aws.key,

View File

@@ -4,7 +4,7 @@ import { CacheflyAccess } from '../access.js';
@IsTaskPlugin({
name: 'CacheFly',
title: '部署证书到 CacheFly',
title: 'CacheFly-部署证书到CacheFly',
desc: '部署证书到 CacheFly',
icon: 'clarity:plugin-line',
group: pluginGroups.cdn.key,

View File

@@ -6,7 +6,7 @@ import { optionsUtils } from '@certd/basic/dist/utils/util.options.js';
@IsTaskPlugin({
//命名规范,插件名称+功能就是目录plugin-demo中的demo大写字母开头驼峰命名
name: 'DemoTest',
title: 'Demo测试插件',
title: 'Demo-测试插件',
icon: 'clarity:plugin-line',
//插件分组
group: pluginGroups.other.key,

View File

@@ -5,7 +5,7 @@ import dayjs from 'dayjs';
@IsTaskPlugin({
name: 'DogeCloudDeployToCDN',
title: '部署证书到多吉云CDN',
title: '多吉云-部署到多吉云CDN',
icon: 'svg:icon-dogecloud',
group: pluginGroups.cdn.key,
default: {

View File

@@ -4,7 +4,7 @@ import { GcoreAccess } from '../access.js';
@IsTaskPlugin({
name: 'Gcoreflush',
title: '刷新证书 Gcore',
title: 'Gcore-刷新Gcore证书',
desc: '刷新现有的证书',
icon: 'clarity:plugin-line',
group: pluginGroups.cdn.key,

View File

@@ -4,7 +4,7 @@ import { GcoreAccess } from '../access.js';
@IsTaskPlugin({
name: 'Gcoreupload',
title: '部署证书到 Gcore',
title: 'Gcore-部署证书到Gcore',
desc: '仅上传 并不会部署到cdn',
icon: 'clarity:plugin-line',
group: pluginGroups.cdn.key,

View File

@@ -6,7 +6,7 @@ import path from 'path';
@IsTaskPlugin({
name: 'CopyToLocal',
title: '复制到本机',
title: '主机-复制到本机',
icon: 'solar:copy-bold-duotone',
desc: '【仅管理员使用】实际上是复制证书到docker容器内的某个路径需要做目录映射到宿主机',
group: pluginGroups.host.key,

View File

@@ -3,7 +3,7 @@ import { SshClient } from '@certd/plugin-lib';
@IsTaskPlugin({
name: 'hostShellExecute',
title: '执行远程主机脚本命令',
title: '主机-执行远程主机脚本命令',
icon: 'tabler:brand-powershell',
group: pluginGroups.host.key,
desc: '可以执行重启nginx等操作让证书生效',

View File

@@ -6,7 +6,7 @@ import { SshAccess, SshClient } from '@certd/plugin-lib';
@IsTaskPlugin({
name: 'uploadCertToHost',
title: '部署证书到主机',
title: '主机-部署证书到主机',
icon: 'line-md:uploading-loop',
group: pluginGroups.host.key,
desc: '上传证书到主机,然后执行部署脚本命令',

View File

@@ -6,7 +6,7 @@ import { resetLogConfigure } from '@certd/basic';
@IsTaskPlugin({
name: 'HauweiDeployCertToCDN',
title: '部署证书至华为云CDN',
title: '华为云-部署证书至CDN',
icon: 'svg:icon-huawei',
group: pluginGroups.huawei.key,
desc: '',

View File

@@ -30,6 +30,19 @@ export class QywxNotification extends BaseNotification {
})
mentionedList!: string[];
@NotificationInput({
title: '提醒指定手机号成员',
component: {
name: 'a-select',
vModel: 'value',
mode: 'tags',
open: false,
},
required: false,
helper: '填写成员手机号,@all 为提醒所有人',
})
mentionedMobileList!: string[];
async send(body: NotificationBody) {
if (!this.webhook) {
throw new Error('webhook地址不能为空');
@@ -47,10 +60,11 @@ export class QywxNotification extends BaseNotification {
url: this.webhook,
method: 'POST',
data: {
msgtype: 'markdown',
markdown: {
content: `# ${body.title}\n\n${body.content}\n\n[查看详情](${body.url})`,
msgtype: 'text',
text: {
content: `· ${body.title}\n· ${body.content}\n· 查看详情: ${body.url}`,
mentioned_list: this.mentionedList,
mentioned_mobile_list: this.mentionedMobileList,
},
},
});

View File

@@ -6,7 +6,7 @@ import { QiniuAccess, QiniuClient } from '@certd/plugin-plus';
@IsTaskPlugin({
name: 'QiniuDeployCertToCDN',
title: '部署证书至七牛云CDN',
title: '七牛云-部署证书至CDN',
icon: 'svg:icon-qiniuyun',
group: pluginGroups.qiniu.key,
desc: '自动部署域名证书至七牛云CDN',

View File

@@ -4,7 +4,7 @@ import { CertInfo } from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'QiniuCertUpload',
title: '上传到七牛云',
title: '七牛云-上传证书到七牛云',
icon: 'svg:icon-qiniuyun',
group: pluginGroups.qiniu.key,
desc: '上传到七牛云',

View File

@@ -7,7 +7,7 @@ import { SshAccess, SshClient } from '@certd/plugin-lib';
@IsTaskPlugin({
name: 'QnapDeploy',
title: '部署证书到威联通',
title: '威联通-部署证书到威联通',
icon: 'simple-icons:qnap',
group: pluginGroups.panel.key,
desc: '部署证书到qnap',

View File

@@ -6,7 +6,7 @@ import { remove } from 'lodash-es';
@IsTaskPlugin({
name: 'TencentDeleteExpiringCert',
title: '删除腾讯云即将过期证书',
title: '腾讯云-删除即将过期证书',
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,
desc: '仅删除未使用的证书',

View File

@@ -3,7 +3,7 @@ import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
@IsTaskPlugin({
name: 'DeployCertToTencentAll',
title: '部署证书到腾讯云任意云资源',
title: '腾讯云-部署证书到任意云资源',
needPlus: true,
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,

View File

@@ -6,7 +6,7 @@ import { createRemoteSelectInputDefine } from '@certd/plugin-lib';
@IsTaskPlugin({
name: 'TencentDeployCertToCDNv2',
title: '部署到腾讯云CDN-v2',
title: '腾讯云-部署到CDN-v2',
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,
desc: '推荐使用',

View File

@@ -4,7 +4,7 @@ import { CertInfo } from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToTencentCDN',
title: '部署到腾讯云CDN',
title: '腾讯云-部署到CDN废弃',
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,
desc: '已废弃请使用v2版',

View File

@@ -4,7 +4,7 @@ import dayjs from 'dayjs';
@IsTaskPlugin({
name: 'DeployCertToTencentCLB',
title: '部署到腾讯云CLB',
title: '腾讯云-部署到CLB',
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,
desc: '暂时只支持单向认证证书暂时只支持通用负载均衡必须开启sni',

View File

@@ -5,7 +5,7 @@ import { TencentSslClient } from '../../lib/index.js';
@IsTaskPlugin({
name: 'DeployCertToTencentCosPlugin',
title: '部署证书到腾讯云COS',
title: '腾讯云-部署证书到COS',
needPlus: false,
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,

View File

@@ -3,7 +3,7 @@ import { TencentAccess } from '@certd/plugin-plus';
@IsTaskPlugin({
name: 'DeployCertToTencentEO',
title: '部署到腾讯云EO',
title: '腾讯云-部署到腾讯云EO',
icon: 'svg:icon-tencentcloud',
desc: '腾讯云边缘安全加速平台EO必须配置上传证书到腾讯云任务',
group: pluginGroups.tencent.key,

View File

@@ -6,7 +6,7 @@ import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
@IsTaskPlugin({
name: 'DeployCertToTencentTKEIngress',
title: '部署到腾讯云TKE-ingress',
title: '腾讯云-部署到TKE-ingress',
needPlus: true,
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,

View File

@@ -5,7 +5,7 @@ import { createRemoteSelectInputDefine } from '@certd/plugin-lib';
@IsTaskPlugin({
name: 'TencentActionInstancesPlugin',
title: '腾讯云实例开关机',
title: '腾讯云-实例开关机',
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,
desc: '腾讯云实例开关机',

View File

@@ -3,7 +3,7 @@ import dayjs from 'dayjs';
@IsTaskPlugin({
name: 'UploadCertToTencent',
title: '上传证书到腾讯云',
title: '腾讯云-上传证书到腾讯云',
icon: 'svg:icon-tencentcloud',
desc: '上传成功后输出tencentCertId',
group: pluginGroups.tencent.key,

View File

@@ -4,7 +4,7 @@ import { WoaiAccess } from '../access.js';
@IsTaskPlugin({
name: 'WoaiCDN',
title: '部署证书到我爱云 CDN',
title: '我爱云-部署证书到我爱云CDN',
desc: '部署证书到我爱云CDN',
icon: 'clarity:plugin-line',
group: pluginGroups.cdn.key,