fix: 修复执行日志没有清理的bug

pull/148/head
xiaojunnuo 2024-08-25 01:55:34 +08:00
parent 86ebbcb9bb
commit 22a336370a
14 changed files with 157 additions and 76 deletions

View File

@ -54,7 +54,10 @@ export class FileStore {
deleteByParent(scope: string, parent: string) { deleteByParent(scope: string, parent: string) {
const dir = path.join(this.rootDir, scope, parent); const dir = path.join(this.rootDir, scope, parent);
if (fs.existsSync(dir)) { if (fs.existsSync(dir)) {
fs.unlinkSync(dir); fs.rmSync(dir, {
recursive: true,
force: true,
});
} }
} }
} }

View File

@ -2,9 +2,17 @@ import { createVerify } from "node:crypto";
import { logger } from "../utils/index.js"; import { logger } from "../utils/index.js";
import dayjs from "dayjs"; import dayjs from "dayjs";
const SecreteKey = let SecreteKey =
"LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQW9VWE1EWUhjdi82WFROWEZFSUI2RlpuR2FER0cwZnR5bTV1dVhPck9NaVl0UkxSb1lvSGMKNVZxenE0N00rdEFqRFBhaTBlOFhWS1c3aytUQUw3MUs0N2JCQVEyWTBxNU5Ya3lYcE5PTVdueVFMYXBwb0tWNgpPMkFJMnpFVURWMVJVa0ZtMFZTVjU0VXNzMDcrdjI2aW5aQU1CWitDMU42eWFDc2tZL3grNnVlNkVRNVcyZXdFCjZOWEhJcUU1bHdEUmU2SXJtdEpnU2doSnlHTS91azIyejN6NGEraFVPVUlWMy9DbEhYV0VhRHBBRFFsakt3NSsKeHR0dURiTHZyUmdzdWp6czB0dEI2OE1SbXE0R0FJL0JtNWVPWkhlNGxFQjBFVVhFUXdVWE1jV1N1VFZSMUE2cApUM21LRGo5MGcwVDFZUlNOdE5TMm9aRzgvRWIwOVlxK3Z3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K"; "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 = { export type LicenseVerifyReq = {
subjectId: string; subjectId: string;
license: string; license: string;

View File

@ -5,9 +5,20 @@ export type SysPublicSetting = {
managerOtherUserPipeline: boolean; managerOtherUserPipeline: boolean;
}; };
export type SysInstallInfo = {
siteId: string;
};
export async function getSysPublicSettings(): Promise<SysPublicSetting> { export async function getSysPublicSettings(): Promise<SysPublicSetting> {
return await request({ return await request({
url: "/basic/settings/public", url: "/basic/settings/public",
method: "get" method: "get"
}); });
} }
export async function getInstallInfo(): Promise<SysInstallInfo> {
return await request({
url: "/basic/settings/install",
method: "get"
});
}

View File

@ -1,15 +1,20 @@
<template> <template>
<div class="layout-vip" :class="{ isPlus: userStore.plusInfo?.isPlus }"> <div class="layout-vip isPlus">
<contextHolder /> <contextHolder />
<fs-icon icon="mingcute:vip-1-line"></fs-icon> <fs-icon icon="mingcute:vip-1-line"></fs-icon>
<div class="text"> <div class="text">
<span v-if="userStore.plusInfo?.isPlus"> <template v-if="userStore.isPlus">
<a-tooltip> <a-tooltip>
<template #title> 到期时间{{ expireTime }} </template> <template #title> 到期时间{{ expireTime }} </template>
<span @click="openUpgrade">{{ texts.plus }}</span> <span @click="openUpgrade">{{ texts.plus }}</span>
</a-tooltip> </a-tooltip>
</span> </template>
<span v-else @click="openUpgrade"> {{ texts.free }} </span> <template v-else>
<a-tooltip>
<template #title> 升级专业版享受更多VIP特权 </template>
<span @click="openUpgrade"> {{ texts.free }} {{ expiredDays }} </span>
</a-tooltip>
</template>
</div> </div>
</div> </div>
</template> </template>
@ -19,9 +24,10 @@ import { useUserStore } from "/src/store/modules/user";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import * as api from "./api"; import * as api from "./api";
import { useSettingStore } from "/@/store/modules/settings";
const props = defineProps<{ const props = defineProps<{
mode: "button" | "nav"; mode?: "button" | "nav";
}>(); }>();
type Texts = { type Texts = {
plus: string; plus: string;
@ -30,22 +36,33 @@ type Texts = {
const texts = computed<Texts>(() => { const texts = computed<Texts>(() => {
if (props.mode === "button") { if (props.mode === "button") {
return { return {
plus: "已开通", plus: "专业版已开通",
free: "专业版功能" free: "此为专业版功能"
}; };
} else { } else {
return { return {
plus: "专业版", plus: "专业版",
free: "免费版,立即升级" free: "免费版"
}; };
} }
}); });
const userStore = useUserStore(); const userStore = useUserStore();
const expireTime = ref(""); const expireTime = computed(() => {
if (userStore.plusInfo?.isPlus) { if (userStore.isPlus) {
expireTime.value = dayjs(userStore.plusInfo.expireTime).format("YYYY-MM-DD"); 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({ const formState = reactive({
code: "" code: ""
@ -65,6 +82,9 @@ async function doActive() {
}); });
} }
} }
const settingStore = useSettingStore();
const computedSiteId = computed(() => settingStore.installInfo?.siteId);
const [modal, contextHolder] = Modal.useModal(); const [modal, contextHolder] = Modal.useModal();
function openUpgrade() { function openUpgrade() {
const placeholder = "请输入激活码"; const placeholder = "请输入激活码";
@ -89,7 +109,11 @@ function openUpgrade() {
<div> <div>
<h3 class="block-header">立刻激活/续期</h3> <h3 class="block-header">立刻激活/续期</h3>
<div class="mt-10"> <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>
<div class="mt-10"> <div class="mt-10">

View File

@ -5,7 +5,7 @@ import _ from "lodash-es";
import { LocalStorage } from "/src/utils/util.storage"; import { LocalStorage } from "/src/utils/util.storage";
import * as basicApi from "/@/api/modules/api.basic"; 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 = { export type ThemeToken = {
token: { token: {
@ -21,6 +21,9 @@ export interface SettingState {
themeConfig?: ThemeConfig; themeConfig?: ThemeConfig;
themeToken: ThemeToken; themeToken: ThemeToken;
sysPublic?: SysPublicSetting; sysPublic?: SysPublicSetting;
installInfo?: {
siteId: string;
};
} }
const defaultThemeConfig = { const defaultThemeConfig = {
@ -39,6 +42,9 @@ export const useSettingStore = defineStore({
sysPublic: { sysPublic: {
registerEnabled: false, registerEnabled: false,
managerOtherUserPipeline: false managerOtherUserPipeline: false
},
installInfo: {
siteId: ""
} }
}), }),
getters: { getters: {
@ -47,12 +53,18 @@ export const useSettingStore = defineStore({
}, },
getSysPublic(): SysPublicSetting { getSysPublic(): SysPublicSetting {
return this.sysPublic; return this.sysPublic;
},
getInstallInfo(): SysInstallInfo {
return this.installInfo;
} }
}, },
actions: { actions: {
async loadSysSettings() { async loadSysSettings() {
const settings = await basicApi.getSysPublicSettings(); const settings = await basicApi.getSysPublicSettings();
_.merge(this.sysPublic, settings); _.merge(this.sysPublic, settings);
const installInfo = await basicApi.getInstallInfo();
_.merge(this.installInfo, installInfo);
}, },
persistThemeConfig() { persistThemeConfig() {
LocalStorage.set(SETTING_THEME_KEY, this.getThemeConfig); LocalStorage.set(SETTING_THEME_KEY, this.getThemeConfig);

View File

@ -20,7 +20,7 @@ interface UserState {
} }
interface PlusInfo { interface PlusInfo {
level: number; vipType: string;
expireTime: number; expireTime: number;
isPlus: boolean; isPlus: boolean;
} }
@ -49,7 +49,7 @@ export const useUserStore = defineStore({
return this.getUserInfo?.id === 1; return this.getUserInfo?.id === 1;
}, },
isPlus(): boolean { isPlus(): boolean {
return this.plusInfo?.isPlus || false; return this.plusInfo?.isPlus && this.plusInfo?.expireTime > new Date().getTime();
} }
}, },
actions: { actions: {

View File

@ -342,7 +342,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
title: "历史记录保持数", title: "历史记录保持数",
type: "number", type: "number",
form: { form: {
value: 30, value: 10,
helper: "历史记录保持条数,多余的会被删除" helper: "历史记录保持条数,多余的会被删除"
}, },
column: { column: {

View File

@ -1,9 +1,14 @@
<template> <template>
<fs-page class="page-setting-email"> <fs-page class="page-setting-email">
<template #header> <template #header>
<div class="title">邮件设置</div> <div class="title">
邮件设置
<span class="sub">设置邮件发送服务器</span>
</div>
</template> </template>
<div class="email-form">
<div class="flex-o">
<div v-if="!formState.usePlus" class="email-form">
<a-form <a-form
:model="formState" :model="formState"
name="basic" name="basic"
@ -13,14 +18,7 @@
@finish="onFinish" @finish="onFinish"
@finish-failed="onFinishFailed" @finish-failed="onFinishFailed"
> >
<a-form-item label="使用邮件代理" name="usePlus"> <a-form-item label="使用自定义邮件服务器"> </a-form-item>
<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">
<a-form-item label="SMTP域名" name="host" :rules="[{ required: true, message: '请输入smtp域名或ip' }]"> <a-form-item label="SMTP域名" name="host" :rules="[{ required: true, message: '请输入smtp域名或ip' }]">
<a-input v-model:value="formState.host" /> <a-input v-model:value="formState.host" />
</a-form-item> </a-form-item>
@ -46,23 +44,35 @@
<a-form-item label="忽略证书校验" name="tls.rejectUnauthorized"> <a-form-item label="忽略证书校验" name="tls.rejectUnauthorized">
<a-switch v-model:checked="formState.tls.rejectUnauthorized" /> <a-switch v-model:checked="formState.tls.rejectUnauthorized" />
</a-form-item> </a-form-item>
</template>
<a-form-item :wrapper-col="{ offset: 8, span: 16 }"> <a-form-item :wrapper-col="{ offset: 8, span: 16 }">
<a-button type="primary" html-type="submit">保存</a-button> <a-button type="primary" html-type="submit">保存</a-button>
</a-form-item> </a-form-item>
</a-form> </a-form>
<div> </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 :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-form-item label="测试收件邮箱" name="receiver" :rules="[{ required: true, message: '请输入测试收件邮箱' }]">
<a-input v-model:value="testFormState.receiver" /> <a-input v-model:value="testFormState.receiver" />
<div class="helper">发送失败可以试试使用官方邮件服务器</div>
</a-form-item> </a-form-item>
<a-form-item :wrapper-col="{ offset: 8, span: 16 }"> <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" :loading="testFormState.loading" html-type="submit">测试</a-button>
</a-form-item> </a-form-item>
</a-form> </a-form>
</div> </div>
</div>
</fs-page> </fs-page>
</template> </template>
@ -120,6 +130,10 @@ const onFinishFailed = (errorInfo: any) => {
// console.log("Failed:", errorInfo); // console.log("Failed:", errorInfo);
}; };
async function onUsePlusChanged() {
await api.SettingsSave(SettingKeys.Email, formState);
}
interface TestFormState { interface TestFormState {
receiver: string; receiver: string;
loading: boolean; loading: boolean;

View File

@ -11,6 +11,10 @@ typeorm:
password: root password: root
database: postgres database: postgres
#plus:
# server:
# baseUrl: 'https://api.ai.handsfree.work'
plus: plus:
server: server:
baseUrl: 'https://api.ai.handsfree.work' baseUrl: 'http://127.0.0.1:11007'

View File

@ -3,7 +3,7 @@ import { Controller, Get, Inject, Provide } from '@midwayjs/core';
import { BaseController } from '../../../basic/base-controller.js'; import { BaseController } from '../../../basic/base-controller.js';
import { Constants } from '../../../basic/constants.js'; import { Constants } from '../../../basic/constants.js';
import { SysSettingsService } from '../../system/service/sys-settings-service.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 { export class SmsCodeReq {
@Rule(RuleType.number().required()) @Rule(RuleType.number().required())
@ -32,4 +32,10 @@ export class BasicSettingsController extends BaseController {
const settings = await this.sysSettingsService.getSetting(SysPublicSettings); const settings = await this.sysSettingsService.getSetting(SysPublicSettings);
return this.ok(settings); return this.ok(settings);
} }
@Get('/install', { summary: Constants.per.guest })
public async getInstallInfo() {
const settings = await this.sysSettingsService.getSetting(SysInstallInfo);
return this.ok(settings);
}
} }

View File

@ -1,7 +1,7 @@
import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core'; import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { SysSettingsService } from '../../system/service/sys-settings-service.js'; import { SysSettingsService } from '../../system/service/sys-settings-service.js';
import { SysInstallInfo } from '../../system/service/models.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 * as crypto from 'crypto';
import { request } from '../../../utils/http.js'; import { request } from '../../../utils/http.js';
import { logger } from '../../../utils/logger.js'; import { logger } from '../../../utils/logger.js';
@ -30,7 +30,7 @@ export class PlusService {
const requestHeader = { const requestHeader = {
subjectId: installInfo.siteId, subjectId: installInfo.siteId,
appKey: appKey, appKey: AppKey,
sign: sign, sign: sign,
timestamps: timestamps, timestamps: timestamps,
}; };

View File

@ -63,7 +63,7 @@ export class HistoryService extends BaseService<HistoryEntity> {
return id; return id;
} }
private async clear(pipelineId: number, keepCount = 30) { private async clear(pipelineId: number, keepCount = 10) {
const count = await this.repository.count({ const count = await this.repository.count({
where: { where: {
pipelineId, pipelineId,
@ -73,13 +73,14 @@ export class HistoryService extends BaseService<HistoryEntity> {
return; return;
} }
let shouldDeleteCount = count - keepCount; let shouldDeleteCount = count - keepCount;
const deleteCountBatch = 100; const maxDeleteCountBatch = 100;
const fileStore = new FileStore({ // const fileStore = new FileStore({
rootDir: this.certdConfig.fileRootDir, // rootDir: this.certdConfig.fileRootDir,
scope: pipelineId + '', // scope: pipelineId + '',
parent: '0', // parent: '0',
}); // });
while (shouldDeleteCount > 0) { while (shouldDeleteCount > 0) {
const deleteCountBatch = maxDeleteCountBatch > shouldDeleteCount ? shouldDeleteCount : maxDeleteCountBatch;
const list = await this.repository.find({ const list = await this.repository.find({
select: { select: {
id: true, id: true,
@ -94,18 +95,16 @@ export class HistoryService extends BaseService<HistoryEntity> {
take: deleteCountBatch, take: deleteCountBatch,
}); });
for (const historyEntity of list) { // for (const historyEntity of list) {
const id = historyEntity.id; // const id = historyEntity.id;
try { // try {
fileStore.deleteByParent(pipelineId + '', id + ''); // fileStore.deleteByParent(pipelineId + '', id + '');
} catch (e) { // } catch (e) {
logger.error('删除文件失败', e); // logger.error('删除文件失败', e);
} // }
} // }
await this.repository.remove(list); const ids = list.map(item => item.id);
await this.deleteByIds(ids, null);
await this.logService.deleteByHistoryIds(list.map(item => item.id));
shouldDeleteCount -= deleteCountBatch; shouldDeleteCount -= deleteCountBatch;
} }
} }

View File

@ -107,7 +107,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
async save(bean: PipelineEntity) { async save(bean: PipelineEntity) {
if (!isPlus()) { if (!isPlus()) {
const count = await this.repository.count(); const count = await this.repository.count();
if (count >= 10) { if (count >= freeCount) {
throw new NeedVIPException('免费版最多只能创建10个pipeline'); throw new NeedVIPException('免费版最多只能创建10个pipeline');
} }
} }

View File

@ -1,7 +1,7 @@
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core'; import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
import { SysSettingsService } from '../service/sys-settings-service.js'; import { SysSettingsService } from '../service/sys-settings-service.js';
import { BaseController } from '../../../basic/base-controller.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 { SysInstallInfo, SysLicenseInfo } from '../service/models.js';
import { logger } from '../../../utils/logger.js'; import { logger } from '../../../utils/logger.js';
import { PlusService } from '../../basic/service/plus-service.js'; import { PlusService } from '../../basic/service/plus-service.js';
@ -22,7 +22,7 @@ export class SysPlusController extends BaseController {
const { code } = body; const { code } = body;
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo); const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
const formData = { const formData = {
appKey: appKey, appKey: AppKey,
code, code,
subjectId: installInfo.siteId, subjectId: installInfo.siteId,
}; };