mirror of
https://github.com/certd/certd.git
synced 2026-04-28 07:57:25 +08:00
perf: 优化站点监控,支持设置忽略主站证书一致性,支持开启和关闭自动同步ip
This commit is contained in:
@@ -282,6 +282,9 @@ export default {
|
|||||||
disabled: "Enable/Disable",
|
disabled: "Enable/Disable",
|
||||||
ipCheck: "Enable IP Check",
|
ipCheck: "Enable IP Check",
|
||||||
ipSyncAuto: "Enable IP Sync Auto",
|
ipSyncAuto: "Enable IP Sync Auto",
|
||||||
|
ipSyncMode: "IP Sync Mode",
|
||||||
|
ipIgnoreCoherence: "Ignore Certificate Coherence",
|
||||||
|
ipIgnoreCoherenceHelper: "Enable to ignore certificate coherence check, only check certificate expiration time",
|
||||||
selectRequired: "Please select",
|
selectRequired: "Please select",
|
||||||
ipCheckConfirm: "Are you sure to {status} IP check?",
|
ipCheckConfirm: "Are you sure to {status} IP check?",
|
||||||
ipCount: "IP Count",
|
ipCount: "IP Count",
|
||||||
|
|||||||
@@ -286,9 +286,16 @@ export default {
|
|||||||
disabled: "禁用启用",
|
disabled: "禁用启用",
|
||||||
ipCheck: "开启IP检查",
|
ipCheck: "开启IP检查",
|
||||||
ipSyncAuto: "自动同步IP",
|
ipSyncAuto: "自动同步IP",
|
||||||
|
ipSyncMode: "IP同步模式",
|
||||||
|
ipSyncModeHelper: "选择仅检查IPv4或IPv6,或检查所有IP",
|
||||||
|
ipSyncModeAll: "检查所有IP",
|
||||||
|
ipSyncModeIPV4Only: "仅检查IPv4",
|
||||||
|
ipSyncModeIPV6Only: "仅检查IPv6",
|
||||||
selectRequired: "请选择",
|
selectRequired: "请选择",
|
||||||
ipCheckConfirm: "确定{status}IP检查?",
|
ipCheckConfirm: "确定{status}IP检查?",
|
||||||
ipCount: "IP数量",
|
ipCount: "IP数量",
|
||||||
|
ipIgnoreCoherence: "忽略证书一致性",
|
||||||
|
ipIgnoreCoherenceHelper: "开启后,即使IP上的证书与站点证书不一致,也会被认为是正常,仅校验证书过期时间",
|
||||||
checkStatus: "检查状态",
|
checkStatus: "检查状态",
|
||||||
pipelineId: "关联流水线ID",
|
pipelineId: "关联流水线ID",
|
||||||
certInfoId: "证书ID",
|
certInfoId: "证书ID",
|
||||||
|
|||||||
@@ -148,7 +148,6 @@ async function refreshTarget(value: any) {
|
|||||||
type: "",
|
type: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
debugger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@@ -610,6 +610,46 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
checkStatus: {
|
||||||
|
title: t("certd.monitor.checkStatus"),
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: "dict-select",
|
||||||
|
dict: checkStatusDict,
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 100,
|
||||||
|
align: "center",
|
||||||
|
sorter: true,
|
||||||
|
cellRender({ value, row }) {
|
||||||
|
return (
|
||||||
|
<a-tooltip title={row.error}>
|
||||||
|
<fs-values-format v-model={value} dict={checkStatusDict}></fs-values-format>
|
||||||
|
</a-tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// error: {
|
||||||
|
// title: "错误信息",
|
||||||
|
// search: {
|
||||||
|
// show: false
|
||||||
|
// },
|
||||||
|
// type: "text",
|
||||||
|
// form: {
|
||||||
|
// show: false
|
||||||
|
// },
|
||||||
|
// column: {
|
||||||
|
// width: 200,
|
||||||
|
// sorter: true,
|
||||||
|
// cellRender({ value }) {
|
||||||
|
// return <a-tooltip title={value}>{value}</a-tooltip>;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },
|
||||||
ipCheck: {
|
ipCheck: {
|
||||||
title: t("certd.monitor.ipCheck"),
|
title: t("certd.monitor.ipCheck"),
|
||||||
type: "dict-switch",
|
type: "dict-switch",
|
||||||
@@ -672,46 +712,51 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
align: "center",
|
align: "center",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
checkStatus: {
|
ipSyncMode: {
|
||||||
title: t("certd.monitor.checkStatus"),
|
title: t("certd.monitor.ipSyncMode"),
|
||||||
search: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
dict: checkStatusDict,
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: t("certd.monitor.ipSyncModeAll"), value: "all" },
|
||||||
|
{ label: t("certd.monitor.ipSyncModeIPV4Only"), value: "ipv4" },
|
||||||
|
{ label: t("certd.monitor.ipSyncModeIPV6Only"), value: "ipv6" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
value: "all",
|
||||||
|
show: compute(({ form }) => {
|
||||||
|
return form.ipSyncAuto;
|
||||||
|
}),
|
||||||
|
helper: t("certd.monitor.ipSyncModeHelper"),
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 100,
|
width: 100,
|
||||||
align: "center",
|
|
||||||
sorter: true,
|
sorter: true,
|
||||||
cellRender({ value, row }) {
|
align: "center",
|
||||||
return (
|
},
|
||||||
<a-tooltip title={row.error}>
|
},
|
||||||
<fs-values-format v-model={value} dict={checkStatusDict}></fs-values-format>
|
ipIgnoreCoherence: {
|
||||||
</a-tooltip>
|
title: t("certd.monitor.ipIgnoreCoherence"),
|
||||||
);
|
type: "dict-switch",
|
||||||
},
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: t("common.enabled"), value: true, color: "green" },
|
||||||
|
{ label: t("common.disabled"), value: false, color: "gray" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
value: false,
|
||||||
|
show: compute(({ form }) => {
|
||||||
|
return form.ipCheck;
|
||||||
|
}),
|
||||||
|
helper: t("certd.monitor.ipIgnoreCoherenceHelper"),
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 100,
|
||||||
|
sorter: true,
|
||||||
|
align: "center",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// error: {
|
|
||||||
// title: "错误信息",
|
|
||||||
// search: {
|
|
||||||
// show: false
|
|
||||||
// },
|
|
||||||
// type: "text",
|
|
||||||
// form: {
|
|
||||||
// show: false
|
|
||||||
// },
|
|
||||||
// column: {
|
|
||||||
// width: 200,
|
|
||||||
// sorter: true,
|
|
||||||
// cellRender({ value }) {
|
|
||||||
// return <a-tooltip title={value}>{value}</a-tooltip>;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
pipelineId: {
|
pipelineId: {
|
||||||
title: t("certd.monitor.pipelineId"),
|
title: t("certd.monitor.pipelineId"),
|
||||||
search: {
|
search: {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
ALTER TABLE cd_site_info ADD COLUMN ip_sync_auto boolean DEFAULT (1);
|
ALTER TABLE cd_site_info ADD COLUMN ip_sync_auto boolean;
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN ip_sync_mode varchar(20);
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN ip_ignore_coherence boolean;
|
||||||
|
|
||||||
ALTER TABLE pi_pipeline ADD COLUMN webhook_key varchar(100);
|
ALTER TABLE pi_pipeline ADD COLUMN webhook_key varchar(100);
|
||||||
ALTER TABLE pi_pipeline ADD COLUMN trigger_count integer DEFAULT (0);
|
ALTER TABLE pi_pipeline ADD COLUMN trigger_count integer DEFAULT (0);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { SiteInfoService } from "../../../modules/monitor/service/site-info-serv
|
|||||||
import { UserSiteMonitorSetting } from "../../../modules/mine/service/models.js";
|
import { UserSiteMonitorSetting } from "../../../modules/mine/service/models.js";
|
||||||
import { merge } from "lodash-es";
|
import { merge } from "lodash-es";
|
||||||
import {SiteIpService} from "../../../modules/monitor/service/site-ip-service.js";
|
import {SiteIpService} from "../../../modules/monitor/service/site-ip-service.js";
|
||||||
|
import { utils } from "@certd/basic";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
@@ -104,6 +105,7 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
|
|||||||
async check(@Body('id') id: number) {
|
async check(@Body('id') id: number) {
|
||||||
await this.service.checkUserId(id, this.getUserId());
|
await this.service.checkUserId(id, this.getUserId());
|
||||||
await this.service.check(id, true, 0);
|
await this.service.check(id, true, 0);
|
||||||
|
await utils.sleep(1000);
|
||||||
return this.ok();
|
return this.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ export class SiteInfoEntity {
|
|||||||
@Column({ name: 'ip_sync_auto', comment: '是否自动同步IP' })
|
@Column({ name: 'ip_sync_auto', comment: '是否自动同步IP' })
|
||||||
ipSyncAuto: boolean;
|
ipSyncAuto: boolean;
|
||||||
|
|
||||||
|
@Column({ name: 'ip_sync_mode', comment: 'IP同步模式' })
|
||||||
|
ipSyncMode: string;
|
||||||
|
|
||||||
|
@Column({ name: 'ip_ignore_coherence', comment: '忽略证书一致性' })
|
||||||
|
ipIgnoreCoherence: boolean;
|
||||||
|
|
||||||
@Column({ name: 'ip_count', comment: 'ip数量' })
|
@Column({ name: 'ip_count', comment: 'ip数量' })
|
||||||
ipCount: number
|
ipCount: number
|
||||||
|
|
||||||
|
|||||||
@@ -162,16 +162,16 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
}
|
}
|
||||||
await this.update(updateData);
|
await this.update(updateData);
|
||||||
|
|
||||||
|
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId, UserSiteMonitorSetting)
|
||||||
//检查ip
|
//检查ip
|
||||||
await this.checkAllIp(site,retryTimes);
|
await this.checkAllIp(site,retryTimes,setting);
|
||||||
|
|
||||||
if (!notify) {
|
if (!notify) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.sendExpiresNotify(site.id);
|
await this.sendExpiresNotify(site.id,setting);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error("send notify error", e);
|
logger.error("send notify error", e);
|
||||||
}
|
}
|
||||||
@@ -187,18 +187,19 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await this.sendCheckErrorNotify(site.id);
|
await this.sendCheckErrorNotify(site.id,false,setting);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error("send notify error", e);
|
logger.error("send notify error", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkAllIp(site: SiteInfoEntity,retryTimes = null) {
|
async checkAllIp(site: SiteInfoEntity,retryTimes = null,setting: UserSiteMonitorSetting) {
|
||||||
if (!site.ipCheck) {
|
if (!site.ipCheck) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const certExpiresTime = site.certExpiresTime;
|
const certExpiresTime = site.certExpiresTime;
|
||||||
|
const tipDays = setting?.certValidDays || 10;
|
||||||
const onFinished = async (list: SiteIpEntity[]) => {
|
const onFinished = async (list: SiteIpEntity[]) => {
|
||||||
let errorCount = 0;
|
let errorCount = 0;
|
||||||
let errorMessage = "";
|
let errorMessage = "";
|
||||||
@@ -207,11 +208,19 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
errorCount++;
|
errorCount++;
|
||||||
|
|
||||||
|
const isExpired = dayjs().valueOf() > dayjs(item.certExpiresTime).valueOf();
|
||||||
|
const isWillExpired = dayjs().valueOf() > dayjs(item.certExpiresTime).subtract(tipDays, "day").valueOf();
|
||||||
|
|
||||||
if (item.error) {
|
if (item.error) {
|
||||||
errorMessage += `${item.ipAddress}:${item.error}; \n`;
|
errorMessage += `${item.ipAddress}:${item.error}; \n`;
|
||||||
} else if (item.certExpiresTime !== certExpiresTime) {
|
} else if (item.certExpiresTime !== certExpiresTime && !site.ipIgnoreCoherence) {
|
||||||
errorMessage += `${item.ipAddress}:与主站证书过期时间不一致(主站:${dayjs(certExpiresTime).format("YYYY-MM-DD")},IP:${dayjs(item.certExpiresTime).format("YYYY-MM-DD")}); \n`;
|
errorMessage += `${item.ipAddress}:与主站证书过期时间不一致(主站:${dayjs(certExpiresTime).format("YYYY-MM-DD")},IP:${dayjs(item.certExpiresTime).format("YYYY-MM-DD")}); \n`;
|
||||||
} else {
|
} else if (isExpired){
|
||||||
|
errorMessage += `${item.ipAddress}:证书已过期(过期时间:${dayjs(item.certExpiresTime).format("YYYY-MM-DD")}); \n`;
|
||||||
|
}else if (isWillExpired){
|
||||||
|
errorMessage += `${item.ipAddress}:证书将过期(过期时间:${dayjs(item.certExpiresTime).format("YYYY-MM-DD")}); \n`;
|
||||||
|
}else {
|
||||||
errorCount--;
|
errorCount--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,12 +241,12 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
ipErrorCount: errorCount
|
ipErrorCount: errorCount
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await this.sendCheckErrorNotify(site.id, true);
|
await this.sendCheckErrorNotify(site.id, true,setting);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error("send notify error", e);
|
logger.error("send notify error", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (!site.ipSyncAuto) {
|
if (site.ipSyncAuto === false) {
|
||||||
await this.siteIpService.checkAll(site, retryTimes,onFinished);
|
await this.siteIpService.checkAll(site, retryTimes,onFinished);
|
||||||
}else{
|
}else{
|
||||||
await this.siteIpService.syncAndCheck(site, retryTimes,onFinished);
|
await this.siteIpService.syncAndCheck(site, retryTimes,onFinished);
|
||||||
@@ -246,7 +255,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查
|
* 检查,不等待返回
|
||||||
* @param id
|
* @param id
|
||||||
* @param notify
|
* @param notify
|
||||||
* @param retryTimes
|
* @param retryTimes
|
||||||
@@ -256,13 +265,16 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
if (!site) {
|
if (!site) {
|
||||||
throw new Error("站点不存在");
|
throw new Error("站点不存在");
|
||||||
}
|
}
|
||||||
return await this.doCheck(site, notify, retryTimes);
|
|
||||||
|
this.doCheck(site, notify, retryTimes).catch((err) => {
|
||||||
|
logger.error("check site error", err);
|
||||||
|
});
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendCheckErrorNotify(siteId: number, fromIpCheck = false) {
|
async sendCheckErrorNotify(siteId: number, fromIpCheck = false,setting: UserSiteMonitorSetting) {
|
||||||
const site = await this.info(siteId);
|
const site = await this.info(siteId);
|
||||||
const url = await this.notificationService.getBindUrl("#/certd/monitor/site");
|
const url = await this.notificationService.getBindUrl("#/certd/monitor/site");
|
||||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId, UserSiteMonitorSetting)
|
|
||||||
// 发邮件
|
// 发邮件
|
||||||
await this.notificationService.send(
|
await this.notificationService.send(
|
||||||
{
|
{
|
||||||
@@ -281,11 +293,9 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendExpiresNotify(siteId: number) {
|
async sendExpiresNotify(siteId: number,setting: UserSiteMonitorSetting) {
|
||||||
const site = await this.info(siteId);
|
|
||||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId, UserSiteMonitorSetting)
|
|
||||||
const tipDays = setting?.certValidDays || 10;
|
const tipDays = setting?.certValidDays || 10;
|
||||||
|
const site = await this.info(siteId);
|
||||||
const expires = site.certExpiresTime;
|
const expires = site.certExpiresTime;
|
||||||
const validDays = dayjs(expires).diff(dayjs(), "day");
|
const validDays = dayjs(expires).diff(dayjs(), "day");
|
||||||
const url = await this.notificationService.getBindUrl("#/certd/monitor/site");
|
const url = await this.notificationService.getBindUrl("#/certd/monitor/site");
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//从域名解析中获取所有ip
|
//从域名解析中获取所有ip
|
||||||
const ips = await this.getAllIpsFromDomain(domain,resolver);
|
const ips = await this.getAllIpsFromDomain(domain,resolver,entity.ipSyncMode);
|
||||||
if (ips.length === 0 ) {
|
if (ips.length === 0 ) {
|
||||||
logger.warn(`没有发现${domain}的IP`)
|
logger.warn(`没有发现${domain}的IP`)
|
||||||
return
|
return
|
||||||
@@ -126,7 +126,7 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async check(ipId: number, domain: string, port: number,retryTimes = null) {
|
async check(ipId: number, domain: string, port: number,retryTimes = null ,tipDays = 10) {
|
||||||
if(!ipId){
|
if(!ipId){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -231,7 +231,7 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllIpsFromDomain(domain: string,resolver:any = dns):Promise<string[]> {
|
async getAllIpsFromDomain(domain: string,resolver:any = dns,ipSyncMode:string = "all"):Promise<string[]> {
|
||||||
const getFromV4 = async ():Promise<string[]> => {
|
const getFromV4 = async ():Promise<string[]> => {
|
||||||
try{
|
try{
|
||||||
return await resolver.resolve4(domain);
|
return await resolver.resolve4(domain);
|
||||||
@@ -249,6 +249,12 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ipSyncMode === "ipv4") {
|
||||||
|
return await getFromV4();
|
||||||
|
}
|
||||||
|
if (ipSyncMode === "ipv6") {
|
||||||
|
return await getFromV6();
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.all([getFromV4(), getFromV6()]).then(res => {
|
return Promise.all([getFromV4(), getFromV6()]).then(res => {
|
||||||
return [...res[0], ...res[1]];
|
return [...res[0], ...res[1]];
|
||||||
|
|||||||
Reference in New Issue
Block a user