Skip to content

Commit

Permalink
feat: add cronjob for send https certificate expired notification
Browse files Browse the repository at this point in the history
  • Loading branch information
moonrailgun committed May 2, 2024
1 parent 18f3073 commit 7b95c55
Show file tree
Hide file tree
Showing 12 changed files with 109 additions and 9 deletions.
9 changes: 8 additions & 1 deletion src/client/components/monitor/MonitorStatsBlock.tsx
@@ -1,15 +1,22 @@
import React from 'react';
import { TipIcon } from '../TipIcon';

interface MonitorStatsBlockProps {
title: string;
tooltip?: string;
desc: string;
text: string;
}
export const MonitorStatsBlock: React.FC<MonitorStatsBlockProps> = React.memo(
(props) => {
return (
<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>{props.text}</div>
</div>
Expand Down
7 changes: 7 additions & 0 deletions src/client/components/monitor/provider/http.tsx
Expand Up @@ -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" />
</Form.Item>

<Form.Item
label="Method"
name={['payload', 'method']}
Expand Down Expand Up @@ -161,6 +165,9 @@ export const MonitorHttpOverview: MonitorOverviewComponent = React.memo(
return (
<MonitorStatsBlock
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')}
text={t('{{num}} days', {
num: payload.certInfo?.daysRemaining,
Expand Down
4 changes: 3 additions & 1 deletion src/client/i18next-toolkit.config.cjs
Expand Up @@ -5,7 +5,9 @@ const config = {
namespaces: ['translation'],
translator: {
type: 'openai',
model: 'gpt-4',
openai: {
modelName: 'gpt-4',
},
},
scanner: {
autoImport: false,
Expand Down
1 change: 1 addition & 0 deletions src/client/public/locales/de/translation.json
Expand Up @@ -87,6 +87,7 @@
"k593cf342": "Sind Sie sicher, diesen Monitor zu löschen?",
"k5a839f71": "Betriebszeit",
"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",
"k6067f0ff": "TLS/SSL-Fehler ignorieren",
"k621317b5": "Neue Seite",
Expand Down
1 change: 1 addition & 0 deletions src/client/public/locales/en/translation.json
Expand Up @@ -87,6 +87,7 @@
"k593cf342": "Did you sure delete this monitor?",
"k5a839f71": "Uptime",
"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",
"k6067f0ff": "Ignore TLS/SSL error",
"k621317b5": "New page",
Expand Down
1 change: 1 addition & 0 deletions src/client/public/locales/fr/translation.json
Expand Up @@ -87,6 +87,7 @@
"k593cf342": "Êtes-vous sûr de vouloir supprimer ce moniteur ?",
"k5a839f71": "Disponibilité",
"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",
"k6067f0ff": "Ignorer l'erreur TLS/SSL",
"k621317b5": "Nouvelle page",
Expand Down
3 changes: 2 additions & 1 deletion src/client/public/locales/jp/translation.json
Expand Up @@ -87,12 +87,13 @@
"k593cf342": "このモニターを削除してもよろしいですか?",
"k5a839f71": "アップタイム",
"k5eb87a8b": "開始",
"k5ec0de4": "HTTPSモニタリングの場合、通知方法が割り当てられている場合、有効期限の1、3、7、14日前に通知が送信されます。",
"k5ecf04b0": "ビュー",
"k6067f0ff": "TLS/SSLエラーを無視",
"k621317b5": "新しいページ",
"k62e19375": "最終更新:{{date}}",
"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": "昨日",
"k683be220": "実行",
"k691b7170": "停止済み",
Expand Down
1 change: 1 addition & 0 deletions src/client/public/locales/pl/translation.json
Expand Up @@ -87,6 +87,7 @@
"k593cf342": "Czy na pewno chcesz usunąć ten monitor?",
"k5a839f71": "Czas działania",
"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",
"k6067f0ff": "Ignoruj błąd TLS/SSL",
"k621317b5": "Nowa strona",
Expand Down
1 change: 1 addition & 0 deletions src/client/public/locales/pt/translation.json
Expand Up @@ -87,6 +87,7 @@
"k593cf342": "De certeza que eliminou este monitor?",
"k5a839f71": "Tempo de atividade",
"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",
"k6067f0ff": "Ignorar erro TLS/SSL",
"k621317b5": "Nova página",
Expand Down
1 change: 1 addition & 0 deletions src/client/public/locales/ru/translation.json
Expand Up @@ -87,6 +87,7 @@
"k593cf342": "Вы уверены, что хотите удалить этот монитор?",
"k5a839f71": "Время работы",
"k5eb87a8b": "Старт",
"k5ec0de4": "Для мониторинга HTTPS, если назначен любой метод уведомления, уведомления будут отправлены за 1, 3, 7 и 14 дней до истечения срока действия.",
"k5ecf04b0": "Просмотр",
"k6067f0ff": "Игнорировать ошибку TLS/SSL",
"k621317b5": "Новая страница",
Expand Down
1 change: 1 addition & 0 deletions src/client/public/locales/zh/translation.json
Expand Up @@ -87,6 +87,7 @@
"k593cf342": "您确定要删除这个监控器吗?",
"k5a839f71": "正常运行时间",
"k5eb87a8b": "开始",
"k5ec0de4": "对于 HTTPS 监控,如果分配了任何通知方法,则将在到期前 1、3、7 和 14 天发送通知。",
"k5ecf04b0": "查看",
"k6067f0ff": "忽略 TLS/SSL 错误",
"k621317b5": "新页面",
Expand Down
88 changes: 82 additions & 6 deletions src/server/cronjob/index.ts
Expand Up @@ -4,6 +4,9 @@ import { prisma } from '../model/_client';
import dayjs from 'dayjs';
import { Prisma } from '@prisma/client';
import { env } from '../utils/env';
import { sendNotification } from '../model/notification';
import { token } from '../model/notification/token';
import _ from 'lodash';

type WebsiteEventCountSqlReturn = {
workspace_id: string;
Expand All @@ -15,8 +18,11 @@ export function initCronjob() {
logger.info('Start daily cronjob');

try {
await statDailyUsage();
await clearMonitorDataDaily();
await Promise.all([
statDailyUsage().catch(logger.error),
clearMonitorDataDaily().catch(logger.error),
dailyHTTPCertCheckNotify().catch(logger.error),
]);

logger.info('Daily cronjob completed');
} catch (err) {
Expand All @@ -30,7 +36,7 @@ export function initCronjob() {
}

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 end = dayjs().startOf('day').toDate();
const date = dayjs().subtract(1, 'day').toDate();
Expand Down Expand Up @@ -133,7 +139,7 @@ async function statDailyUsage() {
skipDuplicates: true,
});

logger.info('Statistics Workspace Daily Usage Completed');
logger.info('[statDailyUsage] Statistics Workspace Daily Usage Completed');
}

/**
Expand All @@ -145,7 +151,10 @@ async function clearMonitorDataDaily() {
}

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({
where: {
createdAt: {
Expand All @@ -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`
);
}

0 comments on commit 7b95c55

Please sign in to comment.