feat: add cronjob for send https certificate expired notification
This commit is contained in:
parent
18f3073e94
commit
7b95c55a70
@ -1,7 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { TipIcon } from '../TipIcon';
|
||||||
|
|
||||||
interface MonitorStatsBlockProps {
|
interface MonitorStatsBlockProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
tooltip?: string;
|
||||||
desc: string;
|
desc: string;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
@ -9,7 +11,12 @@ export const MonitorStatsBlock: React.FC<MonitorStatsBlockProps> = React.memo(
|
|||||||
(props) => {
|
(props) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-0.5 font-bold">{props.title}</div>
|
<div className="mb-0.5 font-bold">
|
||||||
|
{props.title}
|
||||||
|
{props.tooltip && (
|
||||||
|
<TipIcon className="ml-1" content={props.tooltip} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="text-gray-500">{props.desc}</div>
|
<div className="text-gray-500">{props.desc}</div>
|
||||||
<div>{props.text}</div>
|
<div>{props.text}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,9 +30,13 @@ const MonitorHttp: React.FC = React.memo(() => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
tooltip={t(
|
||||||
|
'For HTTPS monitoring, if any notification method is assigned, notifications will be sent at 1, 3, 7 and 14 days before expiration.'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Input placeholder="https://example.com" />
|
<Input placeholder="https://example.com" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Method"
|
label="Method"
|
||||||
name={['payload', 'method']}
|
name={['payload', 'method']}
|
||||||
@ -161,6 +165,9 @@ export const MonitorHttpOverview: MonitorOverviewComponent = React.memo(
|
|||||||
return (
|
return (
|
||||||
<MonitorStatsBlock
|
<MonitorStatsBlock
|
||||||
title={t('Cert Exp.')}
|
title={t('Cert Exp.')}
|
||||||
|
tooltip={t(
|
||||||
|
'For HTTPS monitoring, if any notification method is assigned, notifications will be sent at 1, 3, 7 and 14 days before expiration.'
|
||||||
|
)}
|
||||||
desc={dayjs(payload.certInfo?.validTo).format('YYYY-MM-DD')}
|
desc={dayjs(payload.certInfo?.validTo).format('YYYY-MM-DD')}
|
||||||
text={t('{{num}} days', {
|
text={t('{{num}} days', {
|
||||||
num: payload.certInfo?.daysRemaining,
|
num: payload.certInfo?.daysRemaining,
|
||||||
|
@ -5,7 +5,9 @@ const config = {
|
|||||||
namespaces: ['translation'],
|
namespaces: ['translation'],
|
||||||
translator: {
|
translator: {
|
||||||
type: 'openai',
|
type: 'openai',
|
||||||
model: 'gpt-4',
|
openai: {
|
||||||
|
modelName: 'gpt-4',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
scanner: {
|
scanner: {
|
||||||
autoImport: false,
|
autoImport: false,
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
"k593cf342": "Sind Sie sicher, diesen Monitor zu löschen?",
|
"k593cf342": "Sind Sie sicher, diesen Monitor zu löschen?",
|
||||||
"k5a839f71": "Betriebszeit",
|
"k5a839f71": "Betriebszeit",
|
||||||
"k5eb87a8b": "Start",
|
"k5eb87a8b": "Start",
|
||||||
|
"k5ec0de4": "Für die HTTPS-Überwachung werden bei Zuweisung einer Benachrichtigungsmethode Benachrichtigungen 1, 3, 7 und 14 Tage vor Ablauf gesendet.",
|
||||||
"k5ecf04b0": "Ansicht",
|
"k5ecf04b0": "Ansicht",
|
||||||
"k6067f0ff": "TLS/SSL-Fehler ignorieren",
|
"k6067f0ff": "TLS/SSL-Fehler ignorieren",
|
||||||
"k621317b5": "Neue Seite",
|
"k621317b5": "Neue Seite",
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
"k593cf342": "Did you sure delete this monitor?",
|
"k593cf342": "Did you sure delete this monitor?",
|
||||||
"k5a839f71": "Uptime",
|
"k5a839f71": "Uptime",
|
||||||
"k5eb87a8b": "Start",
|
"k5eb87a8b": "Start",
|
||||||
|
"k5ec0de4": "For HTTPS monitoring, if any notification method is assigned, notifications will be sent at 1, 3, 7 and 14 days before expiration.",
|
||||||
"k5ecf04b0": "View",
|
"k5ecf04b0": "View",
|
||||||
"k6067f0ff": "Ignore TLS/SSL error",
|
"k6067f0ff": "Ignore TLS/SSL error",
|
||||||
"k621317b5": "New page",
|
"k621317b5": "New page",
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
"k593cf342": "Êtes-vous sûr de vouloir supprimer ce moniteur ?",
|
"k593cf342": "Êtes-vous sûr de vouloir supprimer ce moniteur ?",
|
||||||
"k5a839f71": "Disponibilité",
|
"k5a839f71": "Disponibilité",
|
||||||
"k5eb87a8b": "Démarrer",
|
"k5eb87a8b": "Démarrer",
|
||||||
|
"k5ec0de4": "Pour la surveillance HTTPS, si une méthode de notification est assignée, des notifications seront envoyées à 1, 3, 7 et 14 jours avant l'expiration.",
|
||||||
"k5ecf04b0": "Vue",
|
"k5ecf04b0": "Vue",
|
||||||
"k6067f0ff": "Ignorer l'erreur TLS/SSL",
|
"k6067f0ff": "Ignorer l'erreur TLS/SSL",
|
||||||
"k621317b5": "Nouvelle page",
|
"k621317b5": "Nouvelle page",
|
||||||
|
@ -87,12 +87,13 @@
|
|||||||
"k593cf342": "このモニターを削除してもよろしいですか?",
|
"k593cf342": "このモニターを削除してもよろしいですか?",
|
||||||
"k5a839f71": "アップタイム",
|
"k5a839f71": "アップタイム",
|
||||||
"k5eb87a8b": "開始",
|
"k5eb87a8b": "開始",
|
||||||
|
"k5ec0de4": "HTTPSモニタリングの場合、通知方法が割り当てられている場合、有効期限の1、3、7、14日前に通知が送信されます。",
|
||||||
"k5ecf04b0": "ビュー",
|
"k5ecf04b0": "ビュー",
|
||||||
"k6067f0ff": "TLS/SSLエラーを無視",
|
"k6067f0ff": "TLS/SSLエラーを無視",
|
||||||
"k621317b5": "新しいページ",
|
"k621317b5": "新しいページ",
|
||||||
"k62e19375": "最終更新:{{date}}",
|
"k62e19375": "最終更新:{{date}}",
|
||||||
"k646a3a80": "{{monitorName}}のメトリック",
|
"k646a3a80": "{{monitorName}}のメトリック",
|
||||||
"k659b065": "For example: https://open.feishu.cn/open-apis/bot/v2/hook/00000000-0000-0000-0000-000000000000",
|
"k659b065": "例:https://open.feishu.cn/open-apis/bot/v2/hook/00000000-0000-0000-0000-000000000000",
|
||||||
"k67c5a895": "昨日",
|
"k67c5a895": "昨日",
|
||||||
"k683be220": "実行",
|
"k683be220": "実行",
|
||||||
"k691b7170": "停止済み",
|
"k691b7170": "停止済み",
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
"k593cf342": "Czy na pewno chcesz usunąć ten monitor?",
|
"k593cf342": "Czy na pewno chcesz usunąć ten monitor?",
|
||||||
"k5a839f71": "Czas działania",
|
"k5a839f71": "Czas działania",
|
||||||
"k5eb87a8b": "Wznów",
|
"k5eb87a8b": "Wznów",
|
||||||
|
"k5ec0de4": "Dla monitorowania HTTPS, jeśli przypisana jest jakakolwiek metoda powiadamiania, powiadomienia zostaną wysłane 1, 3, 7 i 14 dni przed wygaśnięciem.",
|
||||||
"k5ecf04b0": "Widok",
|
"k5ecf04b0": "Widok",
|
||||||
"k6067f0ff": "Ignoruj błąd TLS/SSL",
|
"k6067f0ff": "Ignoruj błąd TLS/SSL",
|
||||||
"k621317b5": "Nowa strona",
|
"k621317b5": "Nowa strona",
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
"k593cf342": "De certeza que eliminou este monitor?",
|
"k593cf342": "De certeza que eliminou este monitor?",
|
||||||
"k5a839f71": "Tempo de atividade",
|
"k5a839f71": "Tempo de atividade",
|
||||||
"k5eb87a8b": "Início",
|
"k5eb87a8b": "Início",
|
||||||
|
"k5ec0de4": "Para monitoramento HTTPS, se algum método de notificação estiver atribuído, notificações serão enviadas com 1, 3, 7 e 14 dias antes do vencimento.",
|
||||||
"k5ecf04b0": "Ver",
|
"k5ecf04b0": "Ver",
|
||||||
"k6067f0ff": "Ignorar erro TLS/SSL",
|
"k6067f0ff": "Ignorar erro TLS/SSL",
|
||||||
"k621317b5": "Nova página",
|
"k621317b5": "Nova página",
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
"k593cf342": "Вы уверены, что хотите удалить этот монитор?",
|
"k593cf342": "Вы уверены, что хотите удалить этот монитор?",
|
||||||
"k5a839f71": "Время работы",
|
"k5a839f71": "Время работы",
|
||||||
"k5eb87a8b": "Старт",
|
"k5eb87a8b": "Старт",
|
||||||
|
"k5ec0de4": "Для мониторинга HTTPS, если назначен любой метод уведомления, уведомления будут отправлены за 1, 3, 7 и 14 дней до истечения срока действия.",
|
||||||
"k5ecf04b0": "Просмотр",
|
"k5ecf04b0": "Просмотр",
|
||||||
"k6067f0ff": "Игнорировать ошибку TLS/SSL",
|
"k6067f0ff": "Игнорировать ошибку TLS/SSL",
|
||||||
"k621317b5": "Новая страница",
|
"k621317b5": "Новая страница",
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
"k593cf342": "您确定要删除这个监控器吗?",
|
"k593cf342": "您确定要删除这个监控器吗?",
|
||||||
"k5a839f71": "正常运行时间",
|
"k5a839f71": "正常运行时间",
|
||||||
"k5eb87a8b": "开始",
|
"k5eb87a8b": "开始",
|
||||||
|
"k5ec0de4": "对于 HTTPS 监控,如果分配了任何通知方法,则将在到期前 1、3、7 和 14 天发送通知。",
|
||||||
"k5ecf04b0": "查看",
|
"k5ecf04b0": "查看",
|
||||||
"k6067f0ff": "忽略 TLS/SSL 错误",
|
"k6067f0ff": "忽略 TLS/SSL 错误",
|
||||||
"k621317b5": "新页面",
|
"k621317b5": "新页面",
|
||||||
|
@ -4,6 +4,9 @@ import { prisma } from '../model/_client';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { Prisma } from '@prisma/client';
|
import { Prisma } from '@prisma/client';
|
||||||
import { env } from '../utils/env';
|
import { env } from '../utils/env';
|
||||||
|
import { sendNotification } from '../model/notification';
|
||||||
|
import { token } from '../model/notification/token';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
type WebsiteEventCountSqlReturn = {
|
type WebsiteEventCountSqlReturn = {
|
||||||
workspace_id: string;
|
workspace_id: string;
|
||||||
@ -15,8 +18,11 @@ export function initCronjob() {
|
|||||||
logger.info('Start daily cronjob');
|
logger.info('Start daily cronjob');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await statDailyUsage();
|
await Promise.all([
|
||||||
await clearMonitorDataDaily();
|
statDailyUsage().catch(logger.error),
|
||||||
|
clearMonitorDataDaily().catch(logger.error),
|
||||||
|
dailyHTTPCertCheckNotify().catch(logger.error),
|
||||||
|
]);
|
||||||
|
|
||||||
logger.info('Daily cronjob completed');
|
logger.info('Daily cronjob completed');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -30,7 +36,7 @@ export function initCronjob() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function statDailyUsage() {
|
async function statDailyUsage() {
|
||||||
logger.info('Statistics Workspace Daily Usage Start');
|
logger.info('[statDailyUsage] Statistics Workspace Daily Usage Start');
|
||||||
const start = dayjs().subtract(1, 'day').startOf('day').toDate();
|
const start = dayjs().subtract(1, 'day').startOf('day').toDate();
|
||||||
const end = dayjs().startOf('day').toDate();
|
const end = dayjs().startOf('day').toDate();
|
||||||
const date = dayjs().subtract(1, 'day').toDate();
|
const date = dayjs().subtract(1, 'day').toDate();
|
||||||
@ -133,7 +139,7 @@ async function statDailyUsage() {
|
|||||||
skipDuplicates: true,
|
skipDuplicates: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info('Statistics Workspace Daily Usage Completed');
|
logger.info('[statDailyUsage] Statistics Workspace Daily Usage Completed');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,7 +151,10 @@ async function clearMonitorDataDaily() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const date = dayjs().subtract(2, 'weeks').toDate();
|
const date = dayjs().subtract(2, 'weeks').toDate();
|
||||||
logger.info('Start clear monitor data before:', date.toISOString());
|
logger.info(
|
||||||
|
'[clearMonitorDataDaily] Start clear monitor data before:',
|
||||||
|
date.toISOString()
|
||||||
|
);
|
||||||
const res = await prisma.monitorData.deleteMany({
|
const res = await prisma.monitorData.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
createdAt: {
|
createdAt: {
|
||||||
@ -154,5 +163,72 @@ async function clearMonitorDataDaily() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info('Clear monitor completed, delete record:', res.count);
|
logger.info(
|
||||||
|
'[clearMonitorDataDaily] Clear monitor completed, delete record:',
|
||||||
|
res.count
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Https notify
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function dailyHTTPCertCheckNotify() {
|
||||||
|
logger.info('[dailyHTTPCertCheckNotify] Start run dailyHTTPCertCheckNotify');
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
const res = await prisma.$queryRaw<
|
||||||
|
{ monitorId: string; daysRemaining: number }[]
|
||||||
|
>`
|
||||||
|
SELECT "monitorId", (payload -> 'certInfo' ->> 'daysRemaining')::int as "daysRemaining"
|
||||||
|
FROM "MonitorStatus"
|
||||||
|
WHERE "statusName" = 'tls'
|
||||||
|
AND "updatedAt" >= now() - interval '1 day'
|
||||||
|
AND (payload -> 'certInfo' ->> 'daysRemaining')::int in (1, 3, 7, 14);
|
||||||
|
`;
|
||||||
|
|
||||||
|
logger.info(`[dailyHTTPCertCheckNotify] find ${res.length} records`);
|
||||||
|
|
||||||
|
const monitors = await prisma.monitor.findMany({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
in: res.map((r) => r.monitorId),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
notifications: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let sendCount = 0;
|
||||||
|
|
||||||
|
for (const m of monitors) {
|
||||||
|
if (m.active === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const n of m.notifications) {
|
||||||
|
const daysRemaining = res.find(
|
||||||
|
(item) => item.monitorId === m.id
|
||||||
|
)?.daysRemaining;
|
||||||
|
if (!daysRemaining) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = `[${m.name}][${_.get(m.payload, 'url')}] Certificate will be expired in ${daysRemaining} days`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendNotification(n, content, [token.text(content)]).catch(
|
||||||
|
logger.error
|
||||||
|
);
|
||||||
|
sendCount++;
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`[dailyHTTPCertCheckNotify] run completed, send ${sendCount} notifications, time usage: ${Date.now() - start}ms`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user