mirror of https://github.com/certd/certd
perf: 用户创建证书流水线没有购买套餐或者超限时提前报错
parent
f5ec9870fd
commit
472f06c2d1
|
@ -34,6 +34,8 @@ services:
|
||||||
# #↓↓↓↓ ----------------------------- 如果忘记管理员密码,可以设置为true,重启之后,管理员密码将改成123456,然后请及时修改回false
|
# #↓↓↓↓ ----------------------------- 如果忘记管理员密码,可以设置为true,重启之后,管理员密码将改成123456,然后请及时修改回false
|
||||||
- certd_system_resetAdminPasswd=false
|
- certd_system_resetAdminPasswd=false
|
||||||
# 默认使用sqlite文件数据库,如果需要使用其他数据库,请设置以下环境变量
|
# 默认使用sqlite文件数据库,如果需要使用其他数据库,请设置以下环境变量
|
||||||
|
# 注意: 选定使用一种数据库之后,不支持更换数据库。
|
||||||
|
# 数据库迁移方法:1、使用新数据库重新部署一套,然后将旧数据同步过去,注意flyway_history表的数据不要同步
|
||||||
# #↓↓↓↓ ----------------------------- 使用postgresql数据库,需要提前创建数据库
|
# #↓↓↓↓ ----------------------------- 使用postgresql数据库,需要提前创建数据库
|
||||||
# - certd_flyway_scriptDir=./db/migration-pg # 升级脚本目录
|
# - certd_flyway_scriptDir=./db/migration-pg # 升级脚本目录
|
||||||
# - certd_typeorm_dataSource_default_type=postgres # 数据库类型
|
# - certd_typeorm_dataSource_default_type=postgres # 数据库类型
|
||||||
|
|
|
@ -146,6 +146,10 @@ export class RunnableCollection {
|
||||||
|
|
||||||
static initPipelineRunnableType(pipeline: Pipeline) {
|
static initPipelineRunnableType(pipeline: Pipeline) {
|
||||||
pipeline.runnableType = "pipeline";
|
pipeline.runnableType = "pipeline";
|
||||||
|
if (pipeline.stages === undefined) {
|
||||||
|
pipeline.stages = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
pipeline.stages.forEach((stage) => {
|
pipeline.stages.forEach((stage) => {
|
||||||
stage.runnableType = "stage";
|
stage.runnableType = "stage";
|
||||||
stage.tasks.forEach((task) => {
|
stage.tasks.forEach((task) => {
|
||||||
|
|
|
@ -4,10 +4,13 @@ import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, Edi
|
||||||
import { siteInfoApi } from "./api";
|
import { siteInfoApi } from "./api";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
|
import { useSettingStore } from "/@/store/modules/settings";
|
||||||
|
import { mySuiteApi } from "/@/views/certd/suite/mine/api";
|
||||||
|
|
||||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const api = siteInfoApi;
|
const api = siteInfoApi;
|
||||||
|
const { crudBinding } = crudExpose;
|
||||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
|
@ -28,6 +31,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const settingsStore = useSettingStore();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
|
@ -51,6 +56,37 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
width: 600
|
width: 600
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
actionbar: {
|
||||||
|
buttons: {
|
||||||
|
add: {
|
||||||
|
async click() {
|
||||||
|
if (!settingsStore.isPlus) {
|
||||||
|
//非plus
|
||||||
|
if (crudBinding.value.data.length >= 1) {
|
||||||
|
notification.error({
|
||||||
|
message: "基础版只能添加一个监控站点"
|
||||||
|
});
|
||||||
|
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: {
|
rowHandle: {
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
width: 240,
|
width: 240,
|
||||||
|
@ -111,6 +147,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
{ required: true, message: "请输入域名" },
|
{ required: true, message: "请输入域名" },
|
||||||
|
//@ts-ignore
|
||||||
{ type: "domains", message: "请输入正确的域名" }
|
{ type: "domains", message: "请输入正确的域名" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { useModal } from "/@/use/use-modal";
|
||||||
import CertView from "./cert-view.vue";
|
import CertView from "./cert-view.vue";
|
||||||
import { eachStages } from "./utils";
|
import { eachStages } from "./utils";
|
||||||
import { createNotificationApi as createNotificationApi } from "../notification/api";
|
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 {
|
export default function ({ crudExpose, context: { certdFormRef, groupDictRef, selectedRowKeys } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -94,7 +95,23 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
|
||||||
lastResRef.value = res;
|
lastResRef.value = res;
|
||||||
return 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) => {
|
certdFormRef.value.open(async ({ form }: any) => {
|
||||||
// 添加certd pipeline
|
// 添加certd pipeline
|
||||||
const triggers = [];
|
const triggers = [];
|
||||||
|
|
|
@ -16,7 +16,7 @@ export type SuiteDetail = {
|
||||||
monitorCount?: SuiteValue;
|
monitorCount?: SuiteValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export const mySuiteApi = {
|
||||||
async GetList(query: any) {
|
async GetList(query: any) {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/page",
|
url: apiPrefix + "/page",
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
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 { useRouter } from "vue-router";
|
||||||
import SuiteValueEdit from "/@/views/sys/suite/product/suite-value-edit.vue";
|
import SuiteValueEdit from "/@/views/sys/suite/product/suite-value-edit.vue";
|
||||||
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
|
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
|
||||||
import DurationValue from "/@/views/sys/suite/product/duration-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";
|
import UserSuiteStatus from "/@/views/certd/suite/mine/user-suite-status.vue";
|
||||||
|
|
||||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
import { computed, onActivated, onMounted, ref } from "vue";
|
import { computed, onActivated, onMounted, ref } from "vue";
|
||||||
import { useFs } from "@fast-crud/fast-crud";
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
import createCrudOptions from "./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";
|
import SuiteCard from "/@/views/framework/home/dashboard/suite-card.vue";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
|
@ -35,7 +35,7 @@ const currentSuite = computed(() => {
|
||||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { detail, currentSuite } });
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { detail, currentSuite } });
|
||||||
|
|
||||||
async function loadSuiteDetail() {
|
async function loadSuiteDetail() {
|
||||||
detail.value = await api.SuiteDetailGet();
|
detail.value = await mySuiteApi.SuiteDetailGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
|
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import ExpiresTimeText from "/@/components/expires-time-text.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 { FsIcon } from "@fast-crud/fast-crud";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ defineOptions({
|
||||||
const detail = ref<SuiteDetail>({});
|
const detail = ref<SuiteDetail>({});
|
||||||
|
|
||||||
async function loadSuiteDetail() {
|
async function loadSuiteDetail() {
|
||||||
detail.value = await api.SuiteDetailGet();
|
detail.value = await mySuiteApi.SuiteDetailGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSuiteDetail();
|
loadSuiteDetail();
|
||||||
|
|
|
@ -166,6 +166,11 @@ export default defineComponent({
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: "请输入图片验证码"
|
message: "请输入图片验证码"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
min: 4,
|
||||||
|
max: 4,
|
||||||
|
message: "请输入4位图片验证码"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
smsCode: [
|
smsCode: [
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<div style="height: 400px">
|
<div style="height: 400px">
|
||||||
<ProductManager @refreshed="onTableRefresh"></ProductManager>
|
<ProductManager @refreshed="onTableRefresh"></ProductManager>
|
||||||
</div>
|
</div>
|
||||||
<div class="helper">不建议设置免费套餐,可以在下方配置注册赠送套餐</div>
|
<div class="helper">不建议设置免费套餐,可以在下方配置注册赠送套餐,或者在用户套餐管理中手动赠送套餐</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="注册赠送套餐" name="registerGift">
|
<a-form-item label="注册赠送套餐" name="registerGift">
|
||||||
|
|
|
@ -139,17 +139,27 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
column: { show: false },
|
column: { show: false },
|
||||||
addForm: {
|
addForm: {
|
||||||
|
show: true,
|
||||||
component: {
|
component: {
|
||||||
name: SuiteDurationSelector,
|
name: SuiteDurationSelector,
|
||||||
vModel: "modelValue"
|
vModel: "modelValue"
|
||||||
},
|
},
|
||||||
rules: [{ required: true, message: "请选择套餐" }]
|
rules: [
|
||||||
|
{
|
||||||
|
validator: async (rule, value) => {
|
||||||
|
if (value && value.productId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw new Error("请选择套餐");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
valueResolve({ form, value }) {
|
valueResolve({ form, value }) {
|
||||||
if (value) {
|
debugger;
|
||||||
const arr = value.splict("_");
|
if (value && value.productId) {
|
||||||
form.productId = parseInt(arr[0]);
|
form.productId = value.productId;
|
||||||
form.duration = parseInt(arr[1]);
|
form.duration = value.duration;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
form: { show: false }
|
form: { show: false }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Provide } from '@midwayjs/core';
|
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 { logger } from '@certd/basic';
|
||||||
import { BaseException, Result } from '@certd/lib-server';
|
import { Result } from '@certd/lib-server';
|
||||||
|
|
||||||
@Provide()
|
@Provide()
|
||||||
export class GlobalExceptionMiddleware implements IWebMiddleware {
|
export class GlobalExceptionMiddleware implements IWebMiddleware {
|
||||||
|
@ -14,11 +14,7 @@ export class GlobalExceptionMiddleware implements IWebMiddleware {
|
||||||
await next();
|
await next();
|
||||||
logger.info('请求完成:', url, Date.now() - startTime + 'ms');
|
logger.info('请求完成:', url, Date.now() - startTime + 'ms');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err.message);
|
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err);
|
||||||
if (!(err instanceof BaseException)) {
|
|
||||||
logger.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.status = 200;
|
ctx.status = 200;
|
||||||
if (err.code == null || typeof err.code !== 'number') {
|
if (err.code == null || typeof err.code !== 'number') {
|
||||||
err.code = 1;
|
err.code = 1;
|
||||||
|
|
|
@ -30,6 +30,19 @@ export class QywxNotification extends BaseNotification {
|
||||||
})
|
})
|
||||||
mentionedList!: string[];
|
mentionedList!: string[];
|
||||||
|
|
||||||
|
@NotificationInput({
|
||||||
|
title: '提醒指定手机号成员',
|
||||||
|
component: {
|
||||||
|
name: 'a-select',
|
||||||
|
vModel: 'value',
|
||||||
|
mode: 'tags',
|
||||||
|
open: false,
|
||||||
|
},
|
||||||
|
required: false,
|
||||||
|
helper: '填写成员手机号,@all 为提醒所有人',
|
||||||
|
})
|
||||||
|
mentionedMobileList!: string[];
|
||||||
|
|
||||||
async send(body: NotificationBody) {
|
async send(body: NotificationBody) {
|
||||||
if (!this.webhook) {
|
if (!this.webhook) {
|
||||||
throw new Error('webhook地址不能为空');
|
throw new Error('webhook地址不能为空');
|
||||||
|
@ -47,10 +60,11 @@ export class QywxNotification extends BaseNotification {
|
||||||
url: this.webhook,
|
url: this.webhook,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
msgtype: 'markdown',
|
msgtype: 'text',
|
||||||
markdown: {
|
text: {
|
||||||
content: `# ${body.title}\n\n${body.content}\n\n[查看详情](${body.url})`,
|
content: `· ${body.title}\n· ${body.content}\n· 查看详情: ${body.url}`,
|
||||||
mentioned_list: this.mentionedList,
|
mentioned_list: this.mentionedList,
|
||||||
|
mentioned_mobile_list: this.mentionedMobileList,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue