mirror of https://github.com/certd/certd
perf: 站点证书监控支持定时设置,重试次数设置
parent
a00453c83a
commit
d3c2f8eb43
|
@ -1,17 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="cron-editor">
|
<div class="cron-editor">
|
||||||
<div class="flex-o">
|
<div class="flex-o">
|
||||||
<cron-light
|
<cron-light :disabled="disabled" :readonly="readonly" :period="period" class="flex-o cron-ant" locale="zh-CN" format="quartz" :model-value="modelValue" @update:model-value="onUpdate" @error="onError" />
|
||||||
:disabled="disabled"
|
|
||||||
:readonly="readonly"
|
|
||||||
:period="period"
|
|
||||||
class="flex-o cron-ant"
|
|
||||||
locale="zh-CN"
|
|
||||||
format="quartz"
|
|
||||||
:model-value="modelValue"
|
|
||||||
@update:model-value="onUpdate"
|
|
||||||
@error="onError"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-5 flex">
|
<div class="mt-5 flex">
|
||||||
<a-input :disabled="true" :readonly="readonly" :value="modelValue" @change="onChange"></a-input>
|
<a-input :disabled="true" :readonly="readonly" :value="modelValue" @change="onChange"></a-input>
|
||||||
|
@ -27,12 +17,13 @@ import parser from "cron-parser";
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "CronEditor"
|
name: "CronEditor",
|
||||||
});
|
});
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue?: string;
|
modelValue?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
|
allowEveryMin?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const period = ref<string>("");
|
const period = ref<string>("");
|
||||||
|
@ -58,9 +49,12 @@ const onUpdate = (value: string) => {
|
||||||
if (arr[0] === "*") {
|
if (arr[0] === "*") {
|
||||||
arr[0] = "0";
|
arr[0] = "0";
|
||||||
}
|
}
|
||||||
if (arr[1] === "*") {
|
if (!props.allowEveryMin) {
|
||||||
arr[1] = "0";
|
if (arr[1] === "*") {
|
||||||
|
arr[1] = "0";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value = arr.join(" ");
|
value = arr.join(" ");
|
||||||
|
|
||||||
emit("update:modelValue", value);
|
emit("update:modelValue", value);
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { request } from "/src/api/service";
|
||||||
const apiPrefix = "/monitor/site/setting";
|
const apiPrefix = "/monitor/site/setting";
|
||||||
export type UserSiteMonitorSetting = {
|
export type UserSiteMonitorSetting = {
|
||||||
notificationId?: number;
|
notificationId?: number;
|
||||||
|
retryTimes?: number;
|
||||||
|
cron?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function SiteMonitorSettingsGet() {
|
export async function SiteMonitorSettingsGet() {
|
||||||
|
|
|
@ -11,6 +11,19 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="helper">设置通知渠道</div>
|
<div class="helper">设置通知渠道</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="重试次数" :name="['retryTimes']">
|
||||||
|
<div class="flex">
|
||||||
|
<a-input-number v-model:value="formState.retryTimes" />
|
||||||
|
</div>
|
||||||
|
<div class="helper">监控请求重试次数</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="监控定时设置" :name="['cron']">
|
||||||
|
<div class="flex">
|
||||||
|
<cron-editor v-model="formState.cron" :disabled="!settingsStore.isPlus" :allow-every-min="userStore.isAdmin" />
|
||||||
|
<vip-button class="ml-5" mode="button"></vip-button>
|
||||||
|
</div>
|
||||||
|
<div class="helper">定时触发监控</div>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
||||||
<loading-button type="primary" html-type="button" :click="doSave">保存</loading-button>
|
<loading-button type="primary" html-type="button" :click="doSave">保存</loading-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -27,8 +40,10 @@ import { notification } from "ant-design-vue";
|
||||||
import { merge } from "lodash-es";
|
import { merge } from "lodash-es";
|
||||||
import { useSettingStore } from "/src/store/settings";
|
import { useSettingStore } from "/src/store/settings";
|
||||||
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
||||||
|
import { useUserStore } from "/@/store/user";
|
||||||
|
|
||||||
const settingsStore = useSettingStore();
|
const settingsStore = useSettingStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "UserSecurity",
|
name: "UserSecurity",
|
||||||
});
|
});
|
||||||
|
|
|
@ -154,4 +154,5 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
|
||||||
await this.service.saveSetting(userId, setting);
|
await this.service.saveSetting(userId, setting);
|
||||||
return this.ok({});
|
return this.ok({});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import { logger } from '@certd/basic';
|
||||||
import { SysSettingsService } from '@certd/lib-server';
|
import { SysSettingsService } from '@certd/lib-server';
|
||||||
import { SiteInfoService } from '../monitor/index.js';
|
import { SiteInfoService } from '../monitor/index.js';
|
||||||
import { Cron } from '../cron/cron.js';
|
import { Cron } from '../cron/cron.js';
|
||||||
|
import {UserSettingsService} from "../mine/service/user-settings-service.js";
|
||||||
|
import {UserSiteMonitorSetting} from "../mine/service/models.js";
|
||||||
|
|
||||||
@Autoload()
|
@Autoload()
|
||||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||||
|
@ -22,6 +24,8 @@ export class AutoCRegisterCron {
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
sysSettingsService: SysSettingsService;
|
sysSettingsService: SysSettingsService;
|
||||||
|
@Inject()
|
||||||
|
userSettingsService: UserSettingsService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
siteInfoService: SiteInfoService;
|
siteInfoService: SiteInfoService;
|
||||||
|
@ -39,39 +43,30 @@ export class AutoCRegisterCron {
|
||||||
// console.log('meta', meta);
|
// console.log('meta', meta);
|
||||||
// const metas = listPropertyDataFromClass(CLASS_KEY, this.echoPlugin);
|
// const metas = listPropertyDataFromClass(CLASS_KEY, this.echoPlugin);
|
||||||
// console.log('metas', metas);
|
// console.log('metas', metas);
|
||||||
this.registerSiteMonitorCron();
|
await this.registerSiteMonitorCron();
|
||||||
}
|
}
|
||||||
|
|
||||||
registerSiteMonitorCron() {
|
async registerSiteMonitorCron() {
|
||||||
const job = async () => {
|
//先注册公共job
|
||||||
logger.info('站点证书检查开始执行');
|
await this.siteInfoService.registerSiteMonitorJob()
|
||||||
|
|
||||||
let offset = 0;
|
//注册用户独立的检查时间
|
||||||
const limit = 50;
|
const monitorSettingList = await this.userSettingsService.list({
|
||||||
while (true) {
|
query:{
|
||||||
const res = await this.siteInfoService.page({
|
key: UserSiteMonitorSetting.__key__,
|
||||||
query: { disabled: false },
|
|
||||||
page: { offset, limit },
|
|
||||||
});
|
|
||||||
const { records } = res;
|
|
||||||
|
|
||||||
if (records.length === 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
offset += records.length;
|
|
||||||
await this.siteInfoService.checkList(records);
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
for (const item of monitorSettingList) {
|
||||||
|
const setting = item.setting ?? JSON.parse(item.setting)
|
||||||
|
if(!setting?.cron){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
await this.siteInfoService.registerSiteMonitorJob(item.userId)
|
||||||
|
}
|
||||||
|
|
||||||
logger.info('站点证书检查完成');
|
|
||||||
};
|
|
||||||
|
|
||||||
this.cron.register({
|
|
||||||
name: 'siteMonitor',
|
|
||||||
cron: '0 0 0 * * *',
|
|
||||||
job,
|
|
||||||
});
|
|
||||||
if (this.immediateTriggerSiteMonitor) {
|
if (this.immediateTriggerSiteMonitor) {
|
||||||
job();
|
logger.info(`立即触发一次站点证书检查任务`)
|
||||||
|
await this.siteInfoService.triggerJobOnce()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ export class UserSiteMonitorSetting extends BaseSettings {
|
||||||
static __key__ = "user.site.monitor";
|
static __key__ = "user.site.monitor";
|
||||||
|
|
||||||
notificationId?:number= 0;
|
notificationId?:number= 0;
|
||||||
|
cron?:string = undefined;
|
||||||
|
retryTimes?:number = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserEmailSetting extends BaseSettings {
|
export class UserEmailSetting extends BaseSettings {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {UserSettingsService} from "../../mine/service/user-settings-service.js";
|
||||||
import {UserSiteMonitorSetting} from "../../mine/service/models.js";
|
import {UserSiteMonitorSetting} from "../../mine/service/models.js";
|
||||||
import {SiteIpService} from "./site-ip-service.js";
|
import {SiteIpService} from "./site-ip-service.js";
|
||||||
import {SiteIpEntity} from "../entity/site-ip.js";
|
import {SiteIpEntity} from "../entity/site-ip.js";
|
||||||
|
import {Cron} from "../../cron/cron.js";
|
||||||
|
|
||||||
@Provide()
|
@Provide()
|
||||||
@Scope(ScopeEnum.Request, {allowDowngrade: true})
|
@Scope(ScopeEnum.Request, {allowDowngrade: true})
|
||||||
|
@ -36,6 +37,10 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||||
@Inject()
|
@Inject()
|
||||||
siteIpService: SiteIpService;
|
siteIpService: SiteIpService;
|
||||||
|
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
cron: Cron;
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
getRepository() {
|
getRepository() {
|
||||||
return this.repository;
|
return this.repository;
|
||||||
|
@ -307,15 +312,34 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||||
const sites = await this.repository.find({
|
const sites = await this.repository.find({
|
||||||
where: {userId}
|
where: {userId}
|
||||||
});
|
});
|
||||||
this.checkList(sites);
|
this.checkList(sites,false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkList(sites: SiteInfoEntity[]) {
|
async checkList(sites: SiteInfoEntity[],isCommon: boolean) {
|
||||||
|
const cache = {}
|
||||||
|
const getFromCache = async (userId: number) =>{
|
||||||
|
if (cache[userId]) {
|
||||||
|
return cache[userId];
|
||||||
|
}
|
||||||
|
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId, UserSiteMonitorSetting)
|
||||||
|
cache[userId] = setting
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
for (const site of sites) {
|
for (const site of sites) {
|
||||||
this.doCheck(site).catch(e => {
|
let retryTimes = 3;
|
||||||
|
const setting = await getFromCache(site.userId)
|
||||||
|
if (isCommon) {
|
||||||
|
//公共的检查,排除有设置cron的用户
|
||||||
|
if (setting?.cron) {
|
||||||
|
//设置了cron,跳过公共检查
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
retryTimes = setting.retryTimes
|
||||||
|
}
|
||||||
|
this.doCheck(site,true,retryTimes).catch(e => {
|
||||||
logger.error(`检查站点证书失败,${site.domain}`, e.message);
|
logger.error(`检查站点证书失败,${site.domain}`, e.message);
|
||||||
});
|
});
|
||||||
await utils.sleep(200);
|
await utils.sleep(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,6 +349,12 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||||
|
|
||||||
async saveSetting(userId: number, bean: UserSiteMonitorSetting) {
|
async saveSetting(userId: number, bean: UserSiteMonitorSetting) {
|
||||||
await this.userSettingsService.saveSetting(userId, bean);
|
await this.userSettingsService.saveSetting(userId, bean);
|
||||||
|
if(bean.cron){
|
||||||
|
//注册job
|
||||||
|
await this.registerSiteMonitorJob(userId);
|
||||||
|
}else{
|
||||||
|
this.clearSiteMonitorJob(userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async ipCheckChange(req: { id: any; ipCheck: any }) {
|
async ipCheckChange(req: { id: any; ipCheck: any }) {
|
||||||
|
@ -401,4 +431,67 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||||
};
|
};
|
||||||
await batchAdd(list);
|
await batchAdd(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearSiteMonitorJob(userId: number) {
|
||||||
|
this.cron.remove(`siteMonitor-${userId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async registerSiteMonitorJob(userId?: number) {
|
||||||
|
|
||||||
|
if(!userId){
|
||||||
|
//注册公共job
|
||||||
|
logger.info(`注册站点证书检查定时任务`)
|
||||||
|
this.cron.register({
|
||||||
|
name: 'siteMonitor',
|
||||||
|
cron: '0 0 0 * * *',
|
||||||
|
job:async ()=>{
|
||||||
|
await this.triggerJobOnce()
|
||||||
|
},
|
||||||
|
});
|
||||||
|
logger.info(`注册站点证书检查定时任务完成`)
|
||||||
|
}else{
|
||||||
|
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId, UserSiteMonitorSetting);
|
||||||
|
if (!setting.cron) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//注册个人的
|
||||||
|
this.cron.register({
|
||||||
|
name: `siteMonitor-${userId}`,
|
||||||
|
cron: setting.cron,
|
||||||
|
job: () => this.triggerJobOnce(userId),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async triggerJobOnce(userId?:number) {
|
||||||
|
logger.info(`站点证书检查开始执行[${userId??'所有用户'}]`);
|
||||||
|
const query:any = { disabled: false };
|
||||||
|
if(userId){
|
||||||
|
query.userId = userId;
|
||||||
|
//判断是否已关闭
|
||||||
|
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId, UserSiteMonitorSetting);
|
||||||
|
if (!setting.cron) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let offset = 0;
|
||||||
|
const limit = 50;
|
||||||
|
while (true) {
|
||||||
|
const res = await this.page({
|
||||||
|
query: query,
|
||||||
|
page: { offset, limit },
|
||||||
|
});
|
||||||
|
const { records } = res;
|
||||||
|
|
||||||
|
if (records.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offset += records.length;
|
||||||
|
const isCommon = !userId;
|
||||||
|
await this.checkList(records,isCommon);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`站点证书检查完成[${userId??'所有用户'}]`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue