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) {
const dir = path.join(this.rootDir, scope, parent);
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 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;

View File

@ -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"
});
}

View File

@ -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">

View File

@ -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);

View File

@ -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: {

View File

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

View File

@ -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;

View File

@ -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'

View File

@ -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);
}
}

View File

@ -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,
};

View File

@ -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;
}
}

View File

@ -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');
}
}

View File

@ -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,
};