2025-06-06 16:12:30 +08:00
|
|
|
|
import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core";
|
|
|
|
|
|
import {BaseService, NeedSuiteException, NeedVIPException, SysSettingsService} from "@certd/lib-server";
|
|
|
|
|
|
import {InjectEntityModel} from "@midwayjs/typeorm";
|
|
|
|
|
|
import {Repository} from "typeorm";
|
|
|
|
|
|
import {SiteInfoEntity} from "../entity/site-info.js";
|
|
|
|
|
|
import {siteTester} from "./site-tester.js";
|
2025-06-05 23:31:36 +08:00
|
|
|
|
import dayjs from "dayjs";
|
2025-06-06 16:12:30 +08:00
|
|
|
|
import {logger, utils} from "@certd/basic";
|
|
|
|
|
|
import {PeerCertificate} from "tls";
|
|
|
|
|
|
import {NotificationService} from "../../pipeline/service/notification-service.js";
|
|
|
|
|
|
import {isComm, isPlus} from "@certd/plus-core";
|
|
|
|
|
|
import {UserSuiteService} from "@certd/commercial-core";
|
|
|
|
|
|
import {UserSettingsService} from "../../mine/service/user-settings-service.js";
|
|
|
|
|
|
import {UserSiteMonitorSetting} from "../../mine/service/models.js";
|
|
|
|
|
|
import {SiteIpService} from "./site-ip-service.js";
|
|
|
|
|
|
import {SiteIpEntity} from "../entity/site-ip.js";
|
2025-06-06 18:20:30 +08:00
|
|
|
|
import {Cron} from "../../cron/cron.js";
|
2025-07-07 00:10:51 +08:00
|
|
|
|
import { dnsContainer } from "./dns-custom.js";
|
2024-12-22 14:01:10 +08:00
|
|
|
|
|
|
|
|
|
|
@Provide()
|
2025-06-06 16:12:30 +08:00
|
|
|
|
@Scope(ScopeEnum.Request, {allowDowngrade: true})
|
2024-12-22 14:01:10 +08:00
|
|
|
|
export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|
|
|
|
|
@InjectEntityModel(SiteInfoEntity)
|
|
|
|
|
|
repository: Repository<SiteInfoEntity>;
|
|
|
|
|
|
|
2024-12-23 18:11:06 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
notificationService: NotificationService;
|
|
|
|
|
|
|
2024-12-23 23:33:13 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
sysSettingsService: SysSettingsService;
|
|
|
|
|
|
|
|
|
|
|
|
@Inject()
|
|
|
|
|
|
userSuiteService: UserSuiteService;
|
|
|
|
|
|
|
2025-05-25 23:38:25 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
userSettingsService: UserSettingsService;
|
|
|
|
|
|
|
2025-05-28 13:57:31 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
siteIpService: SiteIpService;
|
2025-05-25 23:38:25 +08:00
|
|
|
|
|
2025-06-06 18:20:30 +08:00
|
|
|
|
|
|
|
|
|
|
@Inject()
|
|
|
|
|
|
cron: Cron;
|
|
|
|
|
|
|
2024-12-22 14:01:10 +08:00
|
|
|
|
//@ts-ignore
|
|
|
|
|
|
getRepository() {
|
|
|
|
|
|
return this.repository;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-23 23:33:13 +08:00
|
|
|
|
async add(data: SiteInfoEntity) {
|
|
|
|
|
|
if (!data.userId) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
throw new Error("userId is required");
|
2024-12-23 23:33:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isComm()) {
|
2024-12-26 09:02:04 +08:00
|
|
|
|
const suiteSetting = await this.userSuiteService.getSuiteSetting();
|
2024-12-23 23:33:13 +08:00
|
|
|
|
if (suiteSetting.enabled) {
|
|
|
|
|
|
const userSuite = await this.userSuiteService.getMySuiteDetail(data.userId);
|
|
|
|
|
|
if (userSuite.monitorCount.max != -1 && userSuite.monitorCount.max <= userSuite.monitorCount.used) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
throw new NeedSuiteException("站点监控数量已达上限,请购买或升级套餐");
|
2024-12-23 23:33:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-05 23:31:36 +08:00
|
|
|
|
} else if (!isPlus()) {
|
|
|
|
|
|
const count = await this.getUserMonitorCount(data.userId);
|
|
|
|
|
|
if (count >= 1) {
|
|
|
|
|
|
throw new NeedVIPException("站点监控数量已达上限,请升级专业版");
|
|
|
|
|
|
}
|
2024-12-23 23:33:13 +08:00
|
|
|
|
}
|
2025-06-05 23:31:36 +08:00
|
|
|
|
data.disabled = false;
|
2024-12-23 23:33:13 +08:00
|
|
|
|
|
2025-06-05 23:31:36 +08:00
|
|
|
|
const found = await this.repository.findOne({
|
|
|
|
|
|
where: {
|
|
|
|
|
|
domain: data.domain,
|
|
|
|
|
|
userId: data.userId,
|
|
|
|
|
|
httpsPort: data.httpsPort || 443
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
if (found) {
|
2025-06-06 16:12:30 +08:00
|
|
|
|
return {id: found.id};
|
2025-06-05 23:31:36 +08:00
|
|
|
|
}
|
2025-03-10 00:06:49 +08:00
|
|
|
|
|
2024-12-24 01:12:12 +08:00
|
|
|
|
return await super.add(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async update(data: any) {
|
|
|
|
|
|
if (!data.id) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
throw new Error("id is required");
|
2024-12-24 01:12:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
delete data.userId;
|
|
|
|
|
|
await super.update(data);
|
2024-12-23 23:33:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-22 14:01:10 +08:00
|
|
|
|
async getUserMonitorCount(userId: number) {
|
|
|
|
|
|
if (!userId) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
throw new Error("userId is required");
|
2024-12-22 14:01:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
return await this.repository.count({
|
2025-06-06 16:12:30 +08:00
|
|
|
|
where: {userId}
|
2024-12-22 14:01:10 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2024-12-23 18:11:06 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查站点证书过期时间
|
|
|
|
|
|
* @param site
|
|
|
|
|
|
* @param notify
|
2025-01-04 20:10:00 +08:00
|
|
|
|
* @param retryTimes
|
2024-12-23 18:11:06 +08:00
|
|
|
|
*/
|
2025-06-13 00:25:08 +08:00
|
|
|
|
async doCheck(site: SiteInfoEntity, notify = true, retryTimes = null) {
|
2024-12-23 18:11:06 +08:00
|
|
|
|
if (!site?.domain) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
throw new Error("站点域名不能为空");
|
2024-12-23 18:11:06 +08:00
|
|
|
|
}
|
2025-07-07 00:10:51 +08:00
|
|
|
|
|
|
|
|
|
|
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId, UserSiteMonitorSetting);
|
|
|
|
|
|
const dnsServer = setting.dnsServer
|
|
|
|
|
|
let resolver = null
|
|
|
|
|
|
if (dnsServer && dnsServer.length > 0) {
|
|
|
|
|
|
resolver = dnsContainer.getDns(dnsServer) as any
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-23 18:11:06 +08:00
|
|
|
|
try {
|
2025-01-04 20:10:00 +08:00
|
|
|
|
await this.update({
|
|
|
|
|
|
id: site.id,
|
2025-06-05 23:31:36 +08:00
|
|
|
|
checkStatus: "checking",
|
|
|
|
|
|
lastCheckTime: dayjs().valueOf()
|
2025-01-04 20:10:00 +08:00
|
|
|
|
});
|
2024-12-23 18:11:06 +08:00
|
|
|
|
const res = await siteTester.test({
|
|
|
|
|
|
host: site.domain,
|
|
|
|
|
|
port: site.httpsPort,
|
2025-07-07 00:10:51 +08:00
|
|
|
|
retryTimes,
|
|
|
|
|
|
resolver
|
2024-12-23 18:11:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const certi: PeerCertificate = res.certificate;
|
|
|
|
|
|
if (!certi) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
throw new Error("没有发现证书");
|
2024-12-23 18:11:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
const expires = certi.valid_to;
|
2025-06-05 23:31:36 +08:00
|
|
|
|
const allDomains = certi.subjectaltname?.replaceAll("DNS:", "").split(",") || [];
|
2025-01-04 20:10:00 +08:00
|
|
|
|
const mainDomain = certi.subject?.CN;
|
|
|
|
|
|
let domains = allDomains;
|
|
|
|
|
|
if (!allDomains.includes(mainDomain)) {
|
|
|
|
|
|
domains = [mainDomain, ...allDomains];
|
|
|
|
|
|
}
|
2024-12-23 18:11:06 +08:00
|
|
|
|
const issuer = `${certi.issuer.O}<${certi.issuer.CN}>`;
|
|
|
|
|
|
const isExpired = dayjs().valueOf() > dayjs(expires).valueOf();
|
2025-06-05 23:31:36 +08:00
|
|
|
|
const status = isExpired ? "expired" : "ok";
|
2024-12-23 18:11:06 +08:00
|
|
|
|
const updateData = {
|
|
|
|
|
|
id: site.id,
|
2025-06-05 23:31:36 +08:00
|
|
|
|
certDomains: domains.join(","),
|
2024-12-23 18:11:06 +08:00
|
|
|
|
certStatus: status,
|
|
|
|
|
|
certProvider: issuer,
|
|
|
|
|
|
certExpiresTime: dayjs(expires).valueOf(),
|
|
|
|
|
|
lastCheckTime: dayjs().valueOf(),
|
|
|
|
|
|
error: null,
|
2025-06-05 23:31:36 +08:00
|
|
|
|
checkStatus: "ok"
|
2024-12-23 18:11:06 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-06-05 23:31:36 +08:00
|
|
|
|
if (site.ipCheck) {
|
2025-06-06 16:12:30 +08:00
|
|
|
|
delete updateData.checkStatus
|
2025-06-05 23:31:36 +08:00
|
|
|
|
}
|
2024-12-23 18:11:06 +08:00
|
|
|
|
await this.update(updateData);
|
2025-05-28 13:57:31 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//检查ip
|
2025-06-13 00:25:08 +08:00
|
|
|
|
await this.checkAllIp(site,retryTimes);
|
2025-05-28 13:57:31 +08:00
|
|
|
|
|
2024-12-23 18:11:06 +08:00
|
|
|
|
if (!notify) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
await this.sendExpiresNotify(site);
|
|
|
|
|
|
} catch (e) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
logger.error("send notify error", e);
|
2024-12-23 18:11:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
logger.error("check site error", e);
|
2024-12-23 18:11:06 +08:00
|
|
|
|
await this.update({
|
|
|
|
|
|
id: site.id,
|
2025-06-05 23:31:36 +08:00
|
|
|
|
checkStatus: "error",
|
2024-12-23 18:11:06 +08:00
|
|
|
|
lastCheckTime: dayjs().valueOf(),
|
2025-06-05 23:31:36 +08:00
|
|
|
|
error: e.message
|
2024-12-23 18:11:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
if (!notify) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
await this.sendCheckErrorNotify(site);
|
|
|
|
|
|
} catch (e) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
logger.error("send notify error", e);
|
2024-12-23 18:11:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-13 00:25:08 +08:00
|
|
|
|
async checkAllIp(site: SiteInfoEntity,retryTimes = null) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
if (!site.ipCheck) {
|
2025-05-28 15:12:54 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const certExpiresTime = site.certExpiresTime;
|
2025-06-05 23:31:36 +08:00
|
|
|
|
const onFinished = async (list: SiteIpEntity[]) => {
|
|
|
|
|
|
let errorCount = 0;
|
|
|
|
|
|
let errorMessage = "";
|
2025-05-28 15:12:54 +08:00
|
|
|
|
for (const item of list) {
|
2025-05-28 23:01:55 +08:00
|
|
|
|
if (!item) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-06-05 23:31:36 +08:00
|
|
|
|
errorCount++;
|
|
|
|
|
|
if (item.error) {
|
|
|
|
|
|
errorMessage += `${item.ipAddress}:${item.error}; \n`;
|
|
|
|
|
|
} else if (item.certExpiresTime !== certExpiresTime) {
|
|
|
|
|
|
errorMessage += `${item.ipAddress}:与主站证书过期时间不一致; \n`;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
errorCount--;
|
2025-05-28 15:12:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-05 23:31:36 +08:00
|
|
|
|
if (errorCount <= 0) {
|
|
|
|
|
|
//检查正常
|
|
|
|
|
|
await this.update({
|
|
|
|
|
|
id: site.id,
|
|
|
|
|
|
checkStatus: "ok",
|
|
|
|
|
|
error: "",
|
|
|
|
|
|
ipErrorCount: 0
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
2025-05-28 15:12:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
await this.update({
|
|
|
|
|
|
id: site.id,
|
2025-06-05 23:31:36 +08:00
|
|
|
|
checkStatus: "error",
|
2025-05-28 15:12:54 +08:00
|
|
|
|
error: errorMessage,
|
2025-06-05 23:31:36 +08:00
|
|
|
|
ipErrorCount: errorCount
|
|
|
|
|
|
});
|
2025-05-28 15:12:54 +08:00
|
|
|
|
try {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
site = await this.info(site.id);
|
|
|
|
|
|
await this.sendCheckErrorNotify(site, true);
|
2025-05-28 15:12:54 +08:00
|
|
|
|
} catch (e) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
logger.error("send notify error", e);
|
2025-05-28 15:12:54 +08:00
|
|
|
|
}
|
2025-06-05 23:31:36 +08:00
|
|
|
|
};
|
2025-07-01 22:33:27 +08:00
|
|
|
|
await this.siteIpService.syncAndCheck(site, retryTimes,onFinished);
|
2025-05-28 15:12:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-23 18:11:06 +08:00
|
|
|
|
/**
|
2025-05-28 13:57:31 +08:00
|
|
|
|
* 检查
|
2024-12-23 18:11:06 +08:00
|
|
|
|
* @param id
|
|
|
|
|
|
* @param notify
|
2025-01-04 20:10:00 +08:00
|
|
|
|
* @param retryTimes
|
2024-12-23 18:11:06 +08:00
|
|
|
|
*/
|
2025-06-13 00:25:08 +08:00
|
|
|
|
async check(id: number, notify = false, retryTimes = null) {
|
2024-12-23 18:11:06 +08:00
|
|
|
|
const site = await this.info(id);
|
|
|
|
|
|
if (!site) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
throw new Error("站点不存在");
|
2024-12-23 18:11:06 +08:00
|
|
|
|
}
|
2025-01-04 20:10:00 +08:00
|
|
|
|
return await this.doCheck(site, notify, retryTimes);
|
2024-12-23 18:11:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-05 23:31:36 +08:00
|
|
|
|
async sendCheckErrorNotify(site: SiteInfoEntity, fromIpCheck = false) {
|
|
|
|
|
|
const url = await this.notificationService.getBindUrl("#/certd/monitor/site");
|
2025-06-06 16:12:30 +08:00
|
|
|
|
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId, UserSiteMonitorSetting)
|
2024-12-23 18:11:06 +08:00
|
|
|
|
// 发邮件
|
|
|
|
|
|
await this.notificationService.send(
|
|
|
|
|
|
{
|
2025-06-06 16:12:30 +08:00
|
|
|
|
id: setting?.notificationId,
|
2024-12-23 18:11:06 +08:00
|
|
|
|
useDefault: true,
|
|
|
|
|
|
logger: logger,
|
|
|
|
|
|
body: {
|
|
|
|
|
|
url,
|
2025-06-05 23:31:36 +08:00
|
|
|
|
title: `站点证书${fromIpCheck ? "(IP)" : ""}检查出错<${site.name}>`,
|
2025-03-17 18:20:15 +08:00
|
|
|
|
content: `站点名称: ${site.name} \n站点域名: ${site.domain} \n错误信息:${site.error}`,
|
2025-06-05 23:31:36 +08:00
|
|
|
|
errorMessage: site.error
|
|
|
|
|
|
}
|
2024-12-23 18:11:06 +08:00
|
|
|
|
},
|
|
|
|
|
|
site.userId
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-06-05 23:31:36 +08:00
|
|
|
|
|
2024-12-23 18:11:06 +08:00
|
|
|
|
async sendExpiresNotify(site: SiteInfoEntity) {
|
2025-03-17 00:19:01 +08:00
|
|
|
|
|
2025-06-05 23:31:36 +08:00
|
|
|
|
const tipDays = 10;
|
2025-03-17 00:19:01 +08:00
|
|
|
|
|
2024-12-23 18:11:06 +08:00
|
|
|
|
const expires = site.certExpiresTime;
|
2025-06-05 23:31:36 +08:00
|
|
|
|
const validDays = dayjs(expires).diff(dayjs(), "day");
|
|
|
|
|
|
const url = await this.notificationService.getBindUrl("#/certd/monitor/site");
|
2025-06-06 16:12:30 +08:00
|
|
|
|
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId, UserSiteMonitorSetting)
|
2025-06-05 23:31:36 +08:00
|
|
|
|
const content = `站点名称: ${site.name} \n站点域名: ${site.domain} \n证书域名: ${site.certDomains} \n颁发机构: ${site.certProvider} \n过期时间: ${dayjs(site.certExpiresTime).format("YYYY-MM-DD")} \n`;
|
2025-03-17 00:19:01 +08:00
|
|
|
|
if (validDays >= 0 && validDays < tipDays) {
|
2024-12-23 18:11:06 +08:00
|
|
|
|
// 发通知
|
|
|
|
|
|
await this.notificationService.send(
|
|
|
|
|
|
{
|
2025-06-06 16:12:30 +08:00
|
|
|
|
id: setting?.notificationId,
|
2024-12-23 18:11:06 +08:00
|
|
|
|
useDefault: true,
|
|
|
|
|
|
logger: logger,
|
|
|
|
|
|
body: {
|
|
|
|
|
|
title: `站点证书即将过期,剩余${validDays}天,<${site.name}>`,
|
|
|
|
|
|
content,
|
2025-07-10 17:02:48 +08:00
|
|
|
|
url,
|
|
|
|
|
|
errorMessage: "站点证书即将过期"
|
2025-06-05 23:31:36 +08:00
|
|
|
|
}
|
2024-12-23 18:11:06 +08:00
|
|
|
|
},
|
|
|
|
|
|
site.userId
|
|
|
|
|
|
);
|
|
|
|
|
|
} else if (validDays < 0) {
|
|
|
|
|
|
//发过期通知
|
|
|
|
|
|
await this.notificationService.send(
|
|
|
|
|
|
{
|
2025-06-06 16:12:30 +08:00
|
|
|
|
id: setting?.notificationId,
|
2024-12-23 18:11:06 +08:00
|
|
|
|
useDefault: true,
|
|
|
|
|
|
logger: logger,
|
|
|
|
|
|
body: {
|
|
|
|
|
|
title: `站点证书已过期${-validDays}天<${site.name}>`,
|
|
|
|
|
|
content,
|
|
|
|
|
|
url,
|
2025-05-28 15:12:54 +08:00
|
|
|
|
errorMessage: "站点证书已过期"
|
2025-06-05 23:31:36 +08:00
|
|
|
|
}
|
2024-12-23 18:11:06 +08:00
|
|
|
|
},
|
|
|
|
|
|
site.userId
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-01-04 20:10:00 +08:00
|
|
|
|
async checkAllByUsers(userId: any) {
|
2024-12-23 18:11:06 +08:00
|
|
|
|
if (!userId) {
|
2025-06-05 23:31:36 +08:00
|
|
|
|
throw new Error("userId is required");
|
2024-12-23 18:11:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
const sites = await this.repository.find({
|
2025-06-06 16:12:30 +08:00
|
|
|
|
where: {userId}
|
2024-12-23 18:11:06 +08:00
|
|
|
|
});
|
2025-06-06 18:20:30 +08:00
|
|
|
|
this.checkList(sites,false);
|
2025-01-04 20:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-06 18:20:30 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2024-12-23 18:11:06 +08:00
|
|
|
|
for (const site of sites) {
|
2025-06-06 18:20:30 +08:00
|
|
|
|
const setting = await getFromCache(site.userId)
|
|
|
|
|
|
if (isCommon) {
|
|
|
|
|
|
//公共的检查,排除有设置cron的用户
|
|
|
|
|
|
if (setting?.cron) {
|
|
|
|
|
|
//设置了cron,跳过公共检查
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-13 00:25:08 +08:00
|
|
|
|
let retryTimes = setting?.retryTimes
|
2025-06-06 18:20:30 +08:00
|
|
|
|
this.doCheck(site,true,retryTimes).catch(e => {
|
2025-01-04 20:10:00 +08:00
|
|
|
|
logger.error(`检查站点证书失败,${site.domain}`, e.message);
|
|
|
|
|
|
});
|
2025-06-06 18:20:30 +08:00
|
|
|
|
await utils.sleep(100);
|
2024-12-23 18:11:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-05-25 23:38:25 +08:00
|
|
|
|
|
2025-06-05 23:31:36 +08:00
|
|
|
|
async getSetting(userId: number) {
|
2025-05-25 23:38:25 +08:00
|
|
|
|
return await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId, UserSiteMonitorSetting);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async saveSetting(userId: number, bean: UserSiteMonitorSetting) {
|
|
|
|
|
|
await this.userSettingsService.saveSetting(userId, bean);
|
2025-06-06 18:20:30 +08:00
|
|
|
|
if(bean.cron){
|
|
|
|
|
|
//注册job
|
|
|
|
|
|
await this.registerSiteMonitorJob(userId);
|
|
|
|
|
|
}else{
|
|
|
|
|
|
this.clearSiteMonitorJob(userId);
|
|
|
|
|
|
}
|
2025-05-25 23:38:25 +08:00
|
|
|
|
}
|
2025-05-28 13:57:31 +08:00
|
|
|
|
|
2025-06-05 23:31:36 +08:00
|
|
|
|
async ipCheckChange(req: { id: any; ipCheck: any }) {
|
2025-05-28 13:57:31 +08:00
|
|
|
|
|
|
|
|
|
|
await this.update({
|
|
|
|
|
|
id: req.id,
|
2025-06-05 23:31:36 +08:00
|
|
|
|
ipCheck: req.ipCheck
|
2025-05-28 13:57:31 +08:00
|
|
|
|
});
|
2025-06-05 23:31:36 +08:00
|
|
|
|
if (req.ipCheck) {
|
2025-05-28 13:57:31 +08:00
|
|
|
|
const site = await this.info(req.id);
|
2025-06-05 23:31:36 +08:00
|
|
|
|
await this.siteIpService.sync(site);
|
2025-05-28 13:57:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-05-28 15:12:54 +08:00
|
|
|
|
|
2025-05-28 15:49:48 +08:00
|
|
|
|
async disabledChange(req: { disabled: any; id: any }) {
|
|
|
|
|
|
await this.update({
|
|
|
|
|
|
id: req.id,
|
2025-06-05 23:31:36 +08:00
|
|
|
|
disabled: req.disabled
|
2025-05-28 15:49:48 +08:00
|
|
|
|
});
|
2025-06-05 23:31:36 +08:00
|
|
|
|
if (!req.disabled) {
|
2025-05-28 15:49:48 +08:00
|
|
|
|
const site = await this.info(req.id);
|
2025-06-05 23:31:36 +08:00
|
|
|
|
await this.doCheck(site);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async doImport(req: { text: string; userId: number }) {
|
|
|
|
|
|
if (!req.text) {
|
|
|
|
|
|
throw new Error("text is required");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!req.userId) {
|
|
|
|
|
|
throw new Error("userId is required");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const rows = req.text.split("\n");
|
|
|
|
|
|
|
|
|
|
|
|
const list = [];
|
|
|
|
|
|
for (const item of rows) {
|
|
|
|
|
|
if (!item) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
const arr = item.trim().split(":");
|
|
|
|
|
|
if (arr.length === 0) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
const domain = arr[0];
|
|
|
|
|
|
let port = 443;
|
|
|
|
|
|
let name = domain;
|
|
|
|
|
|
if (arr.length > 1) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
port = parseInt(arr[1] || "443");
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
throw new Error(`${item}格式错误`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
if (arr.length > 2) {
|
|
|
|
|
|
name = arr[2] || domain;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
list.push({
|
|
|
|
|
|
domain,
|
|
|
|
|
|
name,
|
|
|
|
|
|
httpsPort: port,
|
|
|
|
|
|
userId: req.userId
|
|
|
|
|
|
});
|
2025-05-28 15:49:48 +08:00
|
|
|
|
}
|
2025-06-05 23:31:36 +08:00
|
|
|
|
|
|
|
|
|
|
const batchAdd = async (list: any[]) => {
|
|
|
|
|
|
for (const item of list) {
|
|
|
|
|
|
await this.add(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// await this.checkAllByUsers(req.userId);
|
|
|
|
|
|
};
|
|
|
|
|
|
await batchAdd(list);
|
2025-05-28 15:49:48 +08:00
|
|
|
|
}
|
2025-06-06 18:20:30 +08:00
|
|
|
|
|
|
|
|
|
|
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??'所有用户'}]`);
|
|
|
|
|
|
}
|
2024-12-22 14:01:10 +08:00
|
|
|
|
}
|