mirror of https://github.com/certd/certd
fix: 修复执行日志没有清理的bug
parent
86ebbcb9bb
commit
22a336370a
|
@ -54,7 +54,10 @@ export class FileStore {
|
|||
deleteByParent(scope: string, parent: string) {
|
||||
const dir = path.join(this.rootDir, scope, parent);
|
||||
if (fs.existsSync(dir)) {
|
||||
fs.unlinkSync(dir);
|
||||
fs.rmSync(dir, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,17 @@ import { createVerify } from "node:crypto";
|
|||
import { logger } from "../utils/index.js";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const SecreteKey =
|
||||
let SecreteKey =
|
||||
"LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQW9VWE1EWUhjdi82WFROWEZFSUI2RlpuR2FER0cwZnR5bTV1dVhPck9NaVl0UkxSb1lvSGMKNVZxenE0N00rdEFqRFBhaTBlOFhWS1c3aytUQUw3MUs0N2JCQVEyWTBxNU5Ya3lYcE5PTVdueVFMYXBwb0tWNgpPMkFJMnpFVURWMVJVa0ZtMFZTVjU0VXNzMDcrdjI2aW5aQU1CWitDMU42eWFDc2tZL3grNnVlNkVRNVcyZXdFCjZOWEhJcUU1bHdEUmU2SXJtdEpnU2doSnlHTS91azIyejN6NGEraFVPVUlWMy9DbEhYV0VhRHBBRFFsakt3NSsKeHR0dURiTHZyUmdzdWp6czB0dEI2OE1SbXE0R0FJL0JtNWVPWkhlNGxFQjBFVVhFUXdVWE1jV1N1VFZSMUE2cApUM21LRGo5MGcwVDFZUlNOdE5TMm9aRzgvRWIwOVlxK3Z3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";
|
||||
export const appKey = "kQth6FHM71IPV3qdWc";
|
||||
let appKey = "kQth6FHM71IPV3qdWc";
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
SecreteKey =
|
||||
"LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQXY3TGtMaUp1dGM0NzhTU3RaTExjajVGZXh1YjJwR2NLMGxwa0hwVnlZWjhMY29rRFhuUlAKUGQ5UlJSTVRTaGJsbFl2Mzd4QUhOV1ZIQ0ZsWHkrQklVU001bUlBU1NDQTV0azlJNmpZZ2F4bEFDQm1BY0lGMwozKzBjeGZIYVkrVW9YdVluMkZ6YUt2Ym5GdFZIZ0lkMDg4a3d4clZTZzlCT3BDRVZIR1pxR2I5TWN5MXVHVXhUClFTVENCbmpoTWZlZ0p6cXVPYWVOY0ZPSE5tbmtWRWpLTythbTBPeEhNS1lyS3ZnQnVEbzdoVnFENlBFMUd6V3AKZHdwZUV4QXZDSVJxL2pWTkdRK3FtMkRWOVNJZ3U5bmF4MktmSUtFeU50dUFFS1VpekdqL0VmRFhDM1cxMExhegpKaGNYNGw1SUFZU1o3L3JWVmpGbExWSVl0WDU1T054L1Z3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";
|
||||
appKey = "z4nXOeTeSnnpUpnmsV1";
|
||||
}
|
||||
|
||||
export const AppKey = appKey;
|
||||
|
||||
export type LicenseVerifyReq = {
|
||||
subjectId: string;
|
||||
license: string;
|
||||
|
|
|
@ -5,9 +5,20 @@ export type SysPublicSetting = {
|
|||
managerOtherUserPipeline: boolean;
|
||||
};
|
||||
|
||||
export type SysInstallInfo = {
|
||||
siteId: string;
|
||||
};
|
||||
|
||||
export async function getSysPublicSettings(): Promise<SysPublicSetting> {
|
||||
return await request({
|
||||
url: "/basic/settings/public",
|
||||
method: "get"
|
||||
});
|
||||
}
|
||||
|
||||
export async function getInstallInfo(): Promise<SysInstallInfo> {
|
||||
return await request({
|
||||
url: "/basic/settings/install",
|
||||
method: "get"
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
<template>
|
||||
<div class="layout-vip" :class="{ isPlus: userStore.plusInfo?.isPlus }">
|
||||
<div class="layout-vip isPlus">
|
||||
<contextHolder />
|
||||
<fs-icon icon="mingcute:vip-1-line"></fs-icon>
|
||||
<div class="text">
|
||||
<span v-if="userStore.plusInfo?.isPlus">
|
||||
<template v-if="userStore.isPlus">
|
||||
<a-tooltip>
|
||||
<template #title> 到期时间:{{ expireTime }} </template>
|
||||
<span @click="openUpgrade">{{ texts.plus }}</span>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<span v-else @click="openUpgrade"> {{ texts.free }} </span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-tooltip>
|
||||
<template #title> 升级专业版,享受更多VIP特权 </template>
|
||||
<span @click="openUpgrade"> {{ texts.free }} {{ expiredDays }} </span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -19,9 +24,10 @@ import { useUserStore } from "/src/store/modules/user";
|
|||
import dayjs from "dayjs";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import * as api from "./api";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
|
||||
const props = defineProps<{
|
||||
mode: "button" | "nav";
|
||||
mode?: "button" | "nav";
|
||||
}>();
|
||||
type Texts = {
|
||||
plus: string;
|
||||
|
@ -30,22 +36,33 @@ type Texts = {
|
|||
const texts = computed<Texts>(() => {
|
||||
if (props.mode === "button") {
|
||||
return {
|
||||
plus: "已开通",
|
||||
free: "专业版功能"
|
||||
plus: "专业版已开通",
|
||||
free: "此为专业版功能"
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
plus: "专业版",
|
||||
free: "免费版,立即升级"
|
||||
free: "免费版"
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const userStore = useUserStore();
|
||||
const expireTime = ref("");
|
||||
if (userStore.plusInfo?.isPlus) {
|
||||
expireTime.value = dayjs(userStore.plusInfo.expireTime).format("YYYY-MM-DD");
|
||||
}
|
||||
const expireTime = computed(() => {
|
||||
if (userStore.isPlus) {
|
||||
return dayjs(userStore.plusInfo.expireTime).format("YYYY-MM-DD");
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
const expiredDays = computed(() => {
|
||||
if (userStore.plusInfo?.isPlus && !userStore.isPlus) {
|
||||
//已过期多少天
|
||||
const days = dayjs().diff(dayjs(userStore.plusInfo.expireTime), "day");
|
||||
return `专业版已过期${days}天`;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
const formState = reactive({
|
||||
code: ""
|
||||
|
@ -65,6 +82,9 @@ async function doActive() {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
const computedSiteId = computed(() => settingStore.installInfo?.siteId);
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
function openUpgrade() {
|
||||
const placeholder = "请输入激活码";
|
||||
|
@ -89,7 +109,11 @@ function openUpgrade() {
|
|||
<div>
|
||||
<h3 class="block-header">立刻激活/续期</h3>
|
||||
<div class="mt-10">
|
||||
<a-input v-model:value={formState.code} placeholder={placeholder} />
|
||||
<div class="flex-o w-100">
|
||||
<span>站点ID:</span>
|
||||
<fs-copyable class="flex-1" v-model={computedSiteId.value}></fs-copyable>
|
||||
</div>
|
||||
<a-input class="mt-10" v-model:value={formState.code} placeholder={placeholder} />
|
||||
</div>
|
||||
|
||||
<div class="mt-10">
|
||||
|
|
|
@ -5,7 +5,7 @@ import _ from "lodash-es";
|
|||
import { LocalStorage } from "/src/utils/util.storage";
|
||||
|
||||
import * as basicApi from "/@/api/modules/api.basic";
|
||||
import { SysPublicSetting } from "/@/api/modules/api.basic";
|
||||
import { SysInstallInfo, SysPublicSetting } from "/@/api/modules/api.basic";
|
||||
|
||||
export type ThemeToken = {
|
||||
token: {
|
||||
|
@ -21,6 +21,9 @@ export interface SettingState {
|
|||
themeConfig?: ThemeConfig;
|
||||
themeToken: ThemeToken;
|
||||
sysPublic?: SysPublicSetting;
|
||||
installInfo?: {
|
||||
siteId: string;
|
||||
};
|
||||
}
|
||||
|
||||
const defaultThemeConfig = {
|
||||
|
@ -39,6 +42,9 @@ export const useSettingStore = defineStore({
|
|||
sysPublic: {
|
||||
registerEnabled: false,
|
||||
managerOtherUserPipeline: false
|
||||
},
|
||||
installInfo: {
|
||||
siteId: ""
|
||||
}
|
||||
}),
|
||||
getters: {
|
||||
|
@ -47,12 +53,18 @@ export const useSettingStore = defineStore({
|
|||
},
|
||||
getSysPublic(): SysPublicSetting {
|
||||
return this.sysPublic;
|
||||
},
|
||||
getInstallInfo(): SysInstallInfo {
|
||||
return this.installInfo;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async loadSysSettings() {
|
||||
const settings = await basicApi.getSysPublicSettings();
|
||||
_.merge(this.sysPublic, settings);
|
||||
|
||||
const installInfo = await basicApi.getInstallInfo();
|
||||
_.merge(this.installInfo, installInfo);
|
||||
},
|
||||
persistThemeConfig() {
|
||||
LocalStorage.set(SETTING_THEME_KEY, this.getThemeConfig);
|
||||
|
|
|
@ -20,7 +20,7 @@ interface UserState {
|
|||
}
|
||||
|
||||
interface PlusInfo {
|
||||
level: number;
|
||||
vipType: string;
|
||||
expireTime: number;
|
||||
isPlus: boolean;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ export const useUserStore = defineStore({
|
|||
return this.getUserInfo?.id === 1;
|
||||
},
|
||||
isPlus(): boolean {
|
||||
return this.plusInfo?.isPlus || false;
|
||||
return this.plusInfo?.isPlus && this.plusInfo?.expireTime > new Date().getTime();
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
|
|
@ -342,7 +342,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
|||
title: "历史记录保持数",
|
||||
type: "number",
|
||||
form: {
|
||||
value: 30,
|
||||
value: 10,
|
||||
helper: "历史记录保持条数,多余的会被删除"
|
||||
},
|
||||
column: {
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
<template>
|
||||
<fs-page class="page-setting-email">
|
||||
<template #header>
|
||||
<div class="title">邮件设置</div>
|
||||
<div class="title">
|
||||
邮件设置
|
||||
<span class="sub">设置邮件发送服务器</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="email-form">
|
||||
<a-form
|
||||
:model="formState"
|
||||
name="basic"
|
||||
:label-col="{ span: 8 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
autocomplete="off"
|
||||
@finish="onFinish"
|
||||
@finish-failed="onFinishFailed"
|
||||
>
|
||||
<a-form-item label="使用邮件代理" name="usePlus">
|
||||
<div class="flex-o">
|
||||
<a-switch v-model:checked="formState.usePlus" :disabled="!userStore.isPlus" />
|
||||
<vip-button class="ml-5" mode="button"></vip-button>
|
||||
</div>
|
||||
<div class="helper">专业版功能,免除繁琐的邮件配置,直接发邮件</div>
|
||||
</a-form-item>
|
||||
<template v-if="!formState.usePlus">
|
||||
|
||||
<div class="flex-o">
|
||||
<div v-if="!formState.usePlus" class="email-form">
|
||||
<a-form
|
||||
:model="formState"
|
||||
name="basic"
|
||||
:label-col="{ span: 8 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
autocomplete="off"
|
||||
@finish="onFinish"
|
||||
@finish-failed="onFinishFailed"
|
||||
>
|
||||
<a-form-item label="使用自定义邮件服务器"> </a-form-item>
|
||||
<a-form-item label="SMTP域名" name="host" :rules="[{ required: true, message: '请输入smtp域名或ip' }]">
|
||||
<a-input v-model:value="formState.host" />
|
||||
</a-form-item>
|
||||
|
@ -46,22 +44,34 @@
|
|||
<a-form-item label="忽略证书校验" name="tls.rejectUnauthorized">
|
||||
<a-switch v-model:checked="formState.tls.rejectUnauthorized" />
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||
<a-button type="primary" html-type="submit">保存</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div>
|
||||
<a-form :model="testFormState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" @finish="onTestSend">
|
||||
<a-form-item label="测试收件邮箱" name="receiver" :rules="[{ required: true, message: '请输入测试收件邮箱' }]">
|
||||
<a-input v-model:value="testFormState.receiver" />
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||
<a-button type="primary" :loading="testFormState.loading" html-type="submit">测试</a-button>
|
||||
<a-button type="primary" html-type="submit">保存</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="email-form">
|
||||
<a-form :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form-item label="使用官方邮件服务器">
|
||||
<div class="flex-o">
|
||||
<a-switch v-model:checked="formState.usePlus" :disabled="!userStore.isPlus" @change="onUsePlusChanged" />
|
||||
<vip-button class="ml-5" mode="button"></vip-button>
|
||||
</div>
|
||||
<div class="helper">使用官方邮箱服务器直接发邮件,免除繁琐的配置</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="email-form">
|
||||
<a-form :model="testFormState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" @finish="onTestSend">
|
||||
<a-form-item label="测试收件邮箱" name="receiver" :rules="[{ required: true, message: '请输入测试收件邮箱' }]">
|
||||
<a-input v-model:value="testFormState.receiver" />
|
||||
<div class="helper">发送失败???可以试试使用官方邮件服务器↗↗↗↗↗↗↗↗</div>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||
<a-button type="primary" :loading="testFormState.loading" html-type="submit">测试</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
@ -120,6 +130,10 @@ const onFinishFailed = (errorInfo: any) => {
|
|||
// console.log("Failed:", errorInfo);
|
||||
};
|
||||
|
||||
async function onUsePlusChanged() {
|
||||
await api.SettingsSave(SettingKeys.Email, formState);
|
||||
}
|
||||
|
||||
interface TestFormState {
|
||||
receiver: string;
|
||||
loading: boolean;
|
||||
|
|
|
@ -11,6 +11,10 @@ typeorm:
|
|||
password: root
|
||||
database: postgres
|
||||
|
||||
#plus:
|
||||
# server:
|
||||
# baseUrl: 'https://api.ai.handsfree.work'
|
||||
|
||||
plus:
|
||||
server:
|
||||
baseUrl: 'https://api.ai.handsfree.work'
|
||||
baseUrl: 'http://127.0.0.1:11007'
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Controller, Get, Inject, Provide } from '@midwayjs/core';
|
|||
import { BaseController } from '../../../basic/base-controller.js';
|
||||
import { Constants } from '../../../basic/constants.js';
|
||||
import { SysSettingsService } from '../../system/service/sys-settings-service.js';
|
||||
import { SysPublicSettings } from '../../system/service/models.js';
|
||||
import { SysInstallInfo, SysPublicSettings } from '../../system/service/models.js';
|
||||
|
||||
export class SmsCodeReq {
|
||||
@Rule(RuleType.number().required())
|
||||
|
@ -32,4 +32,10 @@ export class BasicSettingsController extends BaseController {
|
|||
const settings = await this.sysSettingsService.getSetting(SysPublicSettings);
|
||||
return this.ok(settings);
|
||||
}
|
||||
|
||||
@Get('/install', { summary: Constants.per.guest })
|
||||
public async getInstallInfo() {
|
||||
const settings = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
return this.ok(settings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { SysSettingsService } from '../../system/service/sys-settings-service.js';
|
||||
import { SysInstallInfo } from '../../system/service/models.js';
|
||||
import { appKey, getPlusInfo, isPlus } from '@certd/pipeline';
|
||||
import { AppKey, getPlusInfo, isPlus } from '@certd/pipeline';
|
||||
import * as crypto from 'crypto';
|
||||
import { request } from '../../../utils/http.js';
|
||||
import { logger } from '../../../utils/logger.js';
|
||||
|
@ -30,7 +30,7 @@ export class PlusService {
|
|||
|
||||
const requestHeader = {
|
||||
subjectId: installInfo.siteId,
|
||||
appKey: appKey,
|
||||
appKey: AppKey,
|
||||
sign: sign,
|
||||
timestamps: timestamps,
|
||||
};
|
||||
|
|
|
@ -63,7 +63,7 @@ export class HistoryService extends BaseService<HistoryEntity> {
|
|||
return id;
|
||||
}
|
||||
|
||||
private async clear(pipelineId: number, keepCount = 30) {
|
||||
private async clear(pipelineId: number, keepCount = 10) {
|
||||
const count = await this.repository.count({
|
||||
where: {
|
||||
pipelineId,
|
||||
|
@ -73,13 +73,14 @@ export class HistoryService extends BaseService<HistoryEntity> {
|
|||
return;
|
||||
}
|
||||
let shouldDeleteCount = count - keepCount;
|
||||
const deleteCountBatch = 100;
|
||||
const fileStore = new FileStore({
|
||||
rootDir: this.certdConfig.fileRootDir,
|
||||
scope: pipelineId + '',
|
||||
parent: '0',
|
||||
});
|
||||
const maxDeleteCountBatch = 100;
|
||||
// const fileStore = new FileStore({
|
||||
// rootDir: this.certdConfig.fileRootDir,
|
||||
// scope: pipelineId + '',
|
||||
// parent: '0',
|
||||
// });
|
||||
while (shouldDeleteCount > 0) {
|
||||
const deleteCountBatch = maxDeleteCountBatch > shouldDeleteCount ? shouldDeleteCount : maxDeleteCountBatch;
|
||||
const list = await this.repository.find({
|
||||
select: {
|
||||
id: true,
|
||||
|
@ -94,18 +95,16 @@ export class HistoryService extends BaseService<HistoryEntity> {
|
|||
take: deleteCountBatch,
|
||||
});
|
||||
|
||||
for (const historyEntity of list) {
|
||||
const id = historyEntity.id;
|
||||
try {
|
||||
fileStore.deleteByParent(pipelineId + '', id + '');
|
||||
} catch (e) {
|
||||
logger.error('删除文件失败', e);
|
||||
}
|
||||
}
|
||||
await this.repository.remove(list);
|
||||
|
||||
await this.logService.deleteByHistoryIds(list.map(item => item.id));
|
||||
|
||||
// for (const historyEntity of list) {
|
||||
// const id = historyEntity.id;
|
||||
// try {
|
||||
// fileStore.deleteByParent(pipelineId + '', id + '');
|
||||
// } catch (e) {
|
||||
// logger.error('删除文件失败', e);
|
||||
// }
|
||||
// }
|
||||
const ids = list.map(item => item.id);
|
||||
await this.deleteByIds(ids, null);
|
||||
shouldDeleteCount -= deleteCountBatch;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
|||
async save(bean: PipelineEntity) {
|
||||
if (!isPlus()) {
|
||||
const count = await this.repository.count();
|
||||
if (count >= 10) {
|
||||
if (count >= freeCount) {
|
||||
throw new NeedVIPException('免费版最多只能创建10个pipeline');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
|
||||
import { SysSettingsService } from '../service/sys-settings-service.js';
|
||||
import { BaseController } from '../../../basic/base-controller.js';
|
||||
import { appKey, verify } from '@certd/pipeline';
|
||||
import { AppKey, verify } from '@certd/pipeline';
|
||||
import { SysInstallInfo, SysLicenseInfo } from '../service/models.js';
|
||||
import { logger } from '../../../utils/logger.js';
|
||||
import { PlusService } from '../../basic/service/plus-service.js';
|
||||
|
@ -22,7 +22,7 @@ export class SysPlusController extends BaseController {
|
|||
const { code } = body;
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
const formData = {
|
||||
appKey: appKey,
|
||||
appKey: AppKey,
|
||||
code,
|
||||
subjectId: installInfo.siteId,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue