From 257718e9d6068cc75c319209b968ea7a7b021980 Mon Sep 17 00:00:00 2001 From: Tola Leng Date: Sat, 13 Sep 2025 21:38:38 +0700 Subject: [PATCH] feat: Add notification, integrate data retention service, and improve i18n support (#164) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(ssl): Ensure edit form saves notification_id and template_id in DB. - The SSL edit form was not saving the `notification_id` and `template_id` fields to the PB database when re-assigning the Notification Channel and Alert Template. * chore(partners): add Cloudflare to Ecosystem & Community Partner section - Cloudflare has been added to the Ecosystem & Community Partner list to acknowledge their support and contribution. * ⏳ chore(project): update development status and progress - fix(ssl): ensure edit form saves notification_id and template_id in DB - feat(notifications): add Notifiarr notification channel (planned) * feat: Add Notifiarr to notification channels (UI) - Add Notifiarr as a new channel type in the notification settings. The Notifiarr channel will use the `api_token` field for API key storage in PB. * feat: Add channel_id field for Notifiarr - Add channel_id field for Notifiarr to store record of the discord channel_id into the PB. * feat: Implement Notifiarr notification service - The Notifiarr notification functionality has been implemented. The alert configuration channel will now use the `api_token` and `channel_id` field for send to the Notifiarr API endpoint. * ⏳ chore(project): update development status and progress - feat: Add channel_id field for Notifiarr - feat: Implement Notifiarr notification service Closes: #133 * feat: add SSL history and Notifiarr notification channel schema * Fix: Remove duplicate close button - Removed the duplicate close button from the test email dialog. * feat: Add Gotify to notification channels - Add Gotify as a new channel type in the notification settings. The Gotify channel will use the `api_token` and `server_url` field for API key storage in PB. * feat: Implement Gotify notification service - The Gotify notification functionality has been implemented. The alert configuration channel will now use the `api_token` and `server_url` field for send to the Gotify API endpoint. * ⏳ chore(project): update development status and progress - feat: Add server_url field for Gotify - feat: Implement Gotify notification service * refactory(i18n): Improve internationalization configuration and add new translations - Added multiple new translation items - Updated the two language files: en and zhcn - Added type definitions for new translation items in types/services.ts - Modified some components to use the new translation items * refactory(i18n): Added architecture import function translation for the "About" page - Added translation items related to schema import in English, Chinese, and type definition files - Replaced hard-coded text with new translation items in the AboutSystem component * refactory(i18n): Added architecture import function translation for the "About" page - Added translation items related to schema import in English, Chinese, and type definition files - Replaced hard-coded text with new translation items in the AboutSystem component * refactory(i18n): Add internationalization support for the service statistics card * feat: Add NTFY API token field for Token-based authentication to ntfy server - Add the api_token field for NTFY notifications in both the dialog and service to save it to PB. NTFY schema to include api_token field. * feat(auth): add token support for ntfy - Updated the NTFY service to support API token authentication by adding Authorization Bearer header when api_token is provided. Closes: #89 * refactor(i18n): Added Chinese translations for Create Event and Maintenance - Supplemented missing Chinese translations - Added translation entries related to Create Event and Maintenance in both English and Chinese translation files * chore: deactivate GitHub donation * refactor(i18n): Optimize internationalization configuration and add new translation items - Add internationalization support to the LoadingState component - Implement internationalization translations in the Profile page - Use internationalized text in the TestEmailDialog component - Update English and Chinese translation files by adding new translation entries * docs(readme): deactivate donations, accept only infra support (cloud, domain, hosting) * feat(i18n): Enhanced internationalization support with variable interpolation - Refactored the `t` function in LanguageContext to support multiple parameter combinations - Added variable interpolation feature, allowing variables in translated texts - Optimized translation logic to improve accuracy and performance - Enhanced type safety by using TypeScript generics to ensure correct typing * refactor(i18n): Optimize service pagination internationalization copy - Added service pagination related copy in English and Chinese translation files - Updated type definitions to include new translation fields - Modified the ServicesPagination component to use the newly added translation fields * docs(README_zhcn): Update documentation content and sponsorship policy - Update image links in the documentation - Revise the sponsorship policy to no longer accept individual sponsorships, only enterprise-level collaborations - Update the display method for supporters and partners - Add Cloudflare and DigitalOcean as new partners * feat: Integrate data retention service - Implemented the data retention service in Go (service-operation) that manages cleanup of old records based on configured retention periods. The service runs once per day to ensure outdated data is removed automatically. * refactor: Remove manual cleanup buttons from Data Retention Settings - The three manual cleanup buttons have been removed from the dashboard. - The data retention service now automatically runs once per day, cleaning up older data based on the configured retention settings. --------- Co-authored-by: YiZixuan Co-authored-by: YiZixuan --- .github/FUNDING.yml | 2 +- README.md | 27 +- .../public/upload/data/pb_schema_latest.json | 168 ++++++- .../public/upload/notification/gotify.png | Bin 0 -> 81715 bytes .../public/upload/notification/notifiarr.png | Bin 0 -> 85134 bytes .../components/services/LastCheckedTime.tsx | 10 +- .../src/components/services/LoadingState.tsx | 6 +- .../components/services/ServiceEditDialog.tsx | 6 +- .../components/services/ServiceStatsCards.tsx | 30 +- .../services/ServicesPagination.tsx | 6 +- .../src/components/services/UptimeBar.tsx | 4 +- .../add-service/ServiceFormActions.tsx | 7 +- .../incident-history/IncidentTable.tsx | 4 +- .../incident-history/LatestChecksTable.tsx | 4 +- .../incident-history/TablePagination.tsx | 4 +- .../service-row/ServiceRowActions.tsx | 16 +- .../services/uptime/UptimeSummary.tsx | 4 +- .../settings/about-system/AboutSystem.tsx | 24 +- .../alerts-templates/TemplateDialog.tsx | 2 + .../data-retention/DataRetentionSettings.tsx | 103 ++--- .../settings/general/TestEmailDialog.tsx | 15 +- .../NotificationChannelDialog.tsx | 162 ++++++- .../NotificationChannelList.tsx | 2 + .../ssl-domain/SSLCertificatesTable.tsx | 2 + application/src/contexts/LanguageContext.tsx | 95 +++- application/src/pages/Profile.tsx | 13 +- .../src/services/alertConfigService.ts | 12 +- application/src/translations/en/about.ts | 15 + application/src/translations/en/incident.ts | 1 + .../src/translations/en/maintenance.ts | 8 + application/src/translations/en/services.ts | 43 +- application/src/translations/en/settings.ts | 6 +- application/src/translations/types/about.ts | 15 + .../src/translations/types/incident.ts | 1 + .../src/translations/types/maintenance.ts | 8 + .../src/translations/types/services.ts | 44 +- .../src/translations/types/settings.ts | 8 +- application/src/translations/zhcn/about.ts | 15 + application/src/translations/zhcn/incident.ts | 9 + .../src/translations/zhcn/maintenance.ts | 21 + application/src/translations/zhcn/services.ts | 42 +- application/src/translations/zhcn/settings.ts | 6 +- docs/DEVELOPMENT_ROADMAP.md | 11 +- docs/README_zhcn.md | 38 +- .../data-retention/handler.go | 59 +++ .../data-retention/scheduler.go | 63 +++ .../data-retention/service.go | 398 +++++++++++++++++ .../service-operation/data-retention/types.go | 42 ++ server/service-operation/main.go | 16 + .../service-operation/notification/gotify.go | 357 +++++++++++++++ .../service-operation/notification/manager.go | 2 + .../notification/notifiarr.go | 416 ++++++++++++++++++ server/service-operation/notification/ntfy.go | 10 + .../service-operation/notification/types.go | 3 +- 54 files changed, 2148 insertions(+), 237 deletions(-) create mode 100644 application/public/upload/notification/gotify.png create mode 100644 application/public/upload/notification/notifiarr.png create mode 100644 server/service-operation/data-retention/handler.go create mode 100644 server/service-operation/data-retention/scheduler.go create mode 100644 server/service-operation/data-retention/service.go create mode 100644 server/service-operation/data-retention/types.go create mode 100644 server/service-operation/notification/gotify.go create mode 100644 server/service-operation/notification/notifiarr.go diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index e4f6115..2647363 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: [tolaleng] +github: #[tolaleng] patreon: # Replace with a single Patreon username open_collective: #checkcle ko_fi: # Replace with a single Ko-fi username diff --git a/README.md b/README.md index 372a237..0a74b58 100644 --- a/README.md +++ b/README.md @@ -128,19 +128,9 @@ docker run -d \ --- ## Sponsors -🙏 We're incredibly thankful to our sponsors. Your contributions enable us to maintain infrastructure (hosting, domains), run tests, and continue developing valuable features. +Sponsorships will no longer be accepted. From now on, support will only be accepted in the form of ecosystem and community partnerships that provide infrastructure such as cloud servers, domains, or hosting credits. -### 🥈 Silver Uptime - - - - - -### 🧡 Ping Supporter - - - - +If you’re a tech company interested in supporting CheckCle, please contact the author directly at tolaleng@checkcle.io ### 🤝 Ecosystem & Community Partner
@@ -151,19 +141,18 @@ docker run -d \ alt="GitBook Logo" /> - + + Cloudflare Logo + + DigitalOcean Logo
- - - - - - --- diff --git a/application/public/upload/data/pb_schema_latest.json b/application/public/upload/data/pb_schema_latest.json index 79237a3..6db52b0 100644 --- a/application/public/upload/data/pb_schema_latest.json +++ b/application/public/upload/data/pb_schema_latest.json @@ -153,7 +153,8 @@ "google_chat", "email", "ntfy", - "pushover" + "pushover", + "notifiarr" ] }, { @@ -4823,6 +4824,169 @@ "indexes": [], "system": false }, + { + "id": "pbc_1753185970", + "listRule": "", + "viewRule": "", + "createRule": "", + "updateRule": "", + "deleteRule": "", + "name": "ssl_history", + "type": "base", + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text3208210256", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text2812878347", + "max": 0, + "min": 0, + "name": "cert_id", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text1881225410", + "max": 0, + "min": 0, + "name": "days_before_expiry", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text3062427817", + "max": 0, + "min": 0, + "name": "days_before_warning", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text98176952", + "max": 0, + "min": 0, + "name": "template_id", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text4011498884", + "max": 0, + "min": 0, + "name": "notification_id", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "select3839288252", + "maxSelect": 1, + "name": "warning_status", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "sent", + "pending", + "failed" + ] + }, + { + "hidden": false, + "id": "select2670434693", + "maxSelect": 1, + "name": "expiry_status", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "sent", + "pending", + "failed" + ] + }, + { + "hidden": false, + "id": "select3824245652", + "maxSelect": 1, + "name": "critical_status", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "sent", + "pending", + "failed" + ] + }, + { + "hidden": false, + "id": "autodate2990389176", + "name": "created", + "onCreate": true, + "onUpdate": false, + "presentable": false, + "system": false, + "type": "autodate" + }, + { + "hidden": false, + "id": "autodate3332085495", + "name": "updated", + "onCreate": true, + "onUpdate": true, + "presentable": false, + "system": false, + "type": "autodate" + } + ], + "indexes": [], + "system": false + }, { "id": "pbc_351174173", "listRule": "", @@ -5479,4 +5643,4 @@ "indexes": [], "system": false } -] \ No newline at end of file +] diff --git a/application/public/upload/notification/gotify.png b/application/public/upload/notification/gotify.png new file mode 100644 index 0000000000000000000000000000000000000000..105fc6cf18f88f10c370f135b00a41ab84528299 GIT binary patch literal 81715 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 literal 0 HcmV?d00001 diff --git a/application/public/upload/notification/notifiarr.png b/application/public/upload/notification/notifiarr.png new file mode 100644 index 0000000000000000000000000000000000000000..ae4d12ebc75b41943c8bba2485c0eae19caa8af2 GIT binary patch literal 85134 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 Z|Jk3MkJ=>N>>bVk1fH&bF6*2UngI5sRGk0- literal 0 HcmV?d00001 diff --git a/application/src/components/services/LastCheckedTime.tsx b/application/src/components/services/LastCheckedTime.tsx index 3757f12..85756a3 100644 --- a/application/src/components/services/LastCheckedTime.tsx +++ b/application/src/components/services/LastCheckedTime.tsx @@ -2,6 +2,7 @@ import React from "react"; import { Clock, TimerOff } from "lucide-react"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; +import { useLanguage } from "@/contexts/LanguageContext"; interface LastCheckedTimeProps { lastCheckedTime: string; @@ -10,6 +11,7 @@ interface LastCheckedTimeProps { } export const LastCheckedTime = ({ lastCheckedTime, status, interval }: LastCheckedTimeProps) => { + const { t } = useLanguage(); // Format the time without seconds to display a static time const formatTimeWithoutSeconds = (timeString: string) => { try { @@ -55,7 +57,7 @@ export const LastCheckedTime = ({ lastCheckedTime, status, interval }: LastCheck )} - {isPaused ? "Paused at " : ""} + {isPaused ? t("pausedAt") : ""} {formattedTime} @@ -66,14 +68,14 @@ export const LastCheckedTime = ({ lastCheckedTime, status, interval }: LastCheck >

- {isPaused ? "Monitoring Paused" : "Last Check Details"} + {isPaused ? t("monitoringPaused") : t("lastCheckDetails")}
- {isPaused ? "No automatic checks" : `Checked at ${formattedTime}`} + {isPaused ? t("noAutomaticChecks") : t("checkedAt") + `${formattedTime}`}
{interval && !isPaused && (
- Check interval: {formatInterval(interval)} + {t("checkInterval")}: {formatInterval(interval)}
)}
diff --git a/application/src/components/services/LoadingState.tsx b/application/src/components/services/LoadingState.tsx index a96a562..ebf909c 100644 --- a/application/src/components/services/LoadingState.tsx +++ b/application/src/components/services/LoadingState.tsx @@ -1,5 +1,7 @@ +import {useLanguage} from "@/contexts/LanguageContext.tsx"; export function LoadingState() { + const { t } = useLanguage(); return (
@@ -7,8 +9,8 @@ export function LoadingState() {
-

Loading server data

-

Please wait while we retrieve your information...

+

{t("loadingServerData")}

+

{t("retrievingYourInformation")}

); diff --git a/application/src/components/services/ServiceEditDialog.tsx b/application/src/components/services/ServiceEditDialog.tsx index 8603398..d020d56 100644 --- a/application/src/components/services/ServiceEditDialog.tsx +++ b/application/src/components/services/ServiceEditDialog.tsx @@ -11,6 +11,7 @@ import { Service } from "@/types/service.types"; import { useQueryClient } from "@tanstack/react-query"; import { useState, useEffect } from "react"; import { ScrollArea } from "@/components/ui/scroll-area"; +import { useLanguage } from "@/contexts/LanguageContext"; interface ServiceEditDialogProps { open: boolean; @@ -19,6 +20,7 @@ interface ServiceEditDialogProps { } export function ServiceEditDialog({ open, onOpenChange, service }: ServiceEditDialogProps) { + const { t } = useLanguage(); const queryClient = useQueryClient(); const [isSubmitting, setIsSubmitting] = useState(false); @@ -53,9 +55,9 @@ export function ServiceEditDialog({ open, onOpenChange, service }: ServiceEditDi }}> - Edit Service + {t("editService")} - Update the details of your monitored service. + {t("editServiceDesc")} {open && service && ( diff --git a/application/src/components/services/ServiceStatsCards.tsx b/application/src/components/services/ServiceStatsCards.tsx index db469e1..4d84708 100644 --- a/application/src/components/services/ServiceStatsCards.tsx +++ b/application/src/components/services/ServiceStatsCards.tsx @@ -4,6 +4,7 @@ import { Service, UptimeData } from "@/types/service.types"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { useMemo } from "react"; import { formatDistanceStrict } from "date-fns"; +import {useLanguage} from "@/contexts/LanguageContext.tsx"; interface ServiceStatsCardsProps { service: Service; @@ -11,6 +12,8 @@ interface ServiceStatsCardsProps { } export function ServiceStatsCards({ service, uptimeData }: ServiceStatsCardsProps) { + + const { t } = useLanguage(); // Calculate uptime percentage const calculateUptimePercentage = () => { if (uptimeData.length === 0) return 100; @@ -109,41 +112,46 @@ export function ServiceStatsCards({ service, uptimeData }: ServiceStatsCardsProp
- Response Time + {t('responseTime')}
{service.responseTime}ms
-

Last checked at {service.lastChecked}

+

{t('lastCheckedAt').replace('{datetime}', service.lastChecked)}

{avgResponseTime > 0 && avgResponseTime !== service.responseTime && ( -

Avg: {avgResponseTime}ms (last {upChecks.length} up checks)

+

{t('avg')}: {avgResponseTime}ms ({t('lastUpChecksCount').replace('{count}', String(upChecks.length))})

)}
- Uptime + {t('uptime')}
{uptimePercentage}%
-

Based on last {uptimeData.length} checks

+

{t('basedOnlastChecksCount').replace('{count}', String(uptimeData.length))}

- Total uptime: {uptimeStats.totalUptimeFormatted} + {t("totalUptime")}: {uptimeStats.totalUptimeFormatted}
- Total downtime: {uptimeStats.totalDowntimeFormatted} + {t("totalDowntime")}: {uptimeStats.totalDowntimeFormatted}
{service.status !== "paused" && (
- {service.status === "up" ? "Up" : "Down"} for {uptimeStats.currentStatusDuration} + { + service.status === "up" ? + t('upStatusDuration').replace("{duration}", uptimeStats.currentStatusDuration) + : + t('downStatusDuration').replace("{duration}", uptimeStats.currentStatusDuration) + }
)} @@ -153,16 +161,16 @@ export function ServiceStatsCards({ service, uptimeData }: ServiceStatsCardsProp - Monitoring Settings + {t('monitoringSettings')}
- Checked every {service.interval} seconds + {t('monitoringSettingsInterval').replace('{interval}', String(service.interval))}
- {service.type} monitoring + {service.type} {t('monitoringSettingsType')}
diff --git a/application/src/components/services/ServicesPagination.tsx b/application/src/components/services/ServicesPagination.tsx index a9fab1a..2fc48c4 100644 --- a/application/src/components/services/ServicesPagination.tsx +++ b/application/src/components/services/ServicesPagination.tsx @@ -80,9 +80,9 @@ export function ServicesPagination({ - {totalItems > 0 - ? `${startItem}-${endItem} of ${totalItems} ${t("services") || "services"}` - : `0 ${t("services") || "services"}` + {totalItems > 0 + ? t("servicesPagination", {"startItem": startItem, "endItem": endItem, "totalItems": totalItems}) + : t("servicesPaginationNoService") }
diff --git a/application/src/components/services/UptimeBar.tsx b/application/src/components/services/UptimeBar.tsx index 642e35e..7a85f05 100644 --- a/application/src/components/services/UptimeBar.tsx +++ b/application/src/components/services/UptimeBar.tsx @@ -5,6 +5,7 @@ import { TooltipProvider } from '@/components/ui/tooltip'; import { UptimeStatusItem } from './uptime/UptimeStatusItem'; import { uptimeService } from '@/services/uptimeService'; import { UptimeData } from '@/types/service.types'; +import {useLanguage} from "@/contexts/LanguageContext.tsx"; interface UptimeBarProps { uptime: number; @@ -15,6 +16,7 @@ interface UptimeBarProps { } const UptimeBarComponent = ({ uptime, status, serviceId, interval, serviceType = "HTTP" }: UptimeBarProps) => { + const { t } = useLanguage(); // Calculate date range for last 20 checks with much more aggressive caching const endDate = useMemo(() => new Date(), []); const startDate = useMemo(() => { @@ -84,7 +86,7 @@ const UptimeBarComponent = ({ uptime, status, serviceId, interval, serviceType =
- Last 20 checks + {t('last20Checks')} ); diff --git a/application/src/components/services/add-service/ServiceFormActions.tsx b/application/src/components/services/add-service/ServiceFormActions.tsx index 4f6ad59..e9a5552 100644 --- a/application/src/components/services/add-service/ServiceFormActions.tsx +++ b/application/src/components/services/add-service/ServiceFormActions.tsx @@ -2,6 +2,7 @@ import { Button } from "@/components/ui/button"; import { Loader2 } from "lucide-react"; import { MouseEvent } from "react"; +import {useLanguage} from "@/contexts/LanguageContext.tsx"; interface ServiceFormActionsProps { isSubmitting: boolean; @@ -14,6 +15,8 @@ export function ServiceFormActions({ onCancel, submitLabel = "Create Service" }: ServiceFormActionsProps) { + + const {t} = useLanguage(); const handleCancel = (e: MouseEvent) => { e.preventDefault(); if (!isSubmitting) { @@ -29,7 +32,7 @@ export function ServiceFormActions({ variant="outline" disabled={isSubmitting} > - Cancel + {t("cancel")}
@@ -110,7 +113,7 @@ const Profile = () => { {loading ? (
- Loading user data... + {t("loadingUserData")}
) : error ? (
@@ -121,7 +124,7 @@ const Profile = () => { onClick={() => fetchUserData()} className="px-4 py-2 bg-primary text-primary-foreground rounded-md" > - Retry + {t("retry")}
) : ( @@ -136,4 +139,4 @@ const Profile = () => { ); }; -export default Profile; \ No newline at end of file +export default Profile; diff --git a/application/src/services/alertConfigService.ts b/application/src/services/alertConfigService.ts index 83a702c..d6f0c30 100644 --- a/application/src/services/alertConfigService.ts +++ b/application/src/services/alertConfigService.ts @@ -7,7 +7,7 @@ export interface AlertConfiguration { collectionId?: string; collectionName?: string; service_id: string; - notification_type: "telegram" | "discord" | "slack" | "signal" | "google_chat" | "email" | "ntfy" | "pushover" | "webhook"; + notification_type: "telegram" | "discord" | "slack" | "signal" | "google_chat" | "email" | "ntfy" | "pushover" | "notifiarr" | "gotify" | "webhook"; telegram_chat_id?: string; discord_webhook_url?: string; signal_number?: string; @@ -31,6 +31,7 @@ export interface AlertConfiguration { ntfy_endpoint?: string; api_token?: string; user_key?: string; + server_url?: string; webhook_url?: string; webhook_payload_template?: string; } @@ -87,10 +88,19 @@ export const alertConfigService = { } else if (config.notification_type === "ntfy") { cleanConfig.ntfy_endpoint = config.ntfy_endpoint || ""; + cleanConfig.api_token = config.api_token || ""; } else if (config.notification_type === "pushover") { cleanConfig.api_token = config.api_token || ""; cleanConfig.user_key = config.user_key || ""; + + } else if (config.notification_type === "notifiarr") { + cleanConfig.api_token = config.api_token || ""; + cleanConfig.channel_id = config.channel_id || ""; + + } else if (config.notification_type === "gotify") { + cleanConfig.api_token = config.api_token || ""; + cleanConfig.server_url = config.server_url || ""; } else if (config.notification_type === "webhook") { cleanConfig.webhook_url = config.webhook_url || ""; diff --git a/application/src/translations/en/about.ts b/application/src/translations/en/about.ts index ef6ddcc..3e399ac 100644 --- a/application/src/translations/en/about.ts +++ b/application/src/translations/en/about.ts @@ -16,4 +16,19 @@ export const aboutTranslations: AboutTranslations = { quickActionsDescription: "Access common monitoring operations and features quickly. Select an action below to get started.", quickTips: "Quick Tips", releasedOn: "Released On", + updateSchema: "Update Schema", + updateSchemaDesc: "Automatic import collections schema", + mergeFieldsLabel: "Merge fields with existing collections (safe - preserves data)", + importing: "Importing...", + clickToUpdateSchema: "Click to update Schema", + importSuccessful: "Import Successful", + importFailed: "Import Failed", + instructions: "Instructions", + mergeFields: "Merge fields", + instructionsMergeFields: "Safely add new fields to existing collections, preserves all data", + instructionsCollections: "System collections (starting with _) and users collection will be skipped automatically", + instructionsImportAuth: "Only authenticated admins can perform schema imports", + collectionsUpdatedCount: "{count} collections updated", + collectionsCreatedCount: "{count} collections created", + collectionsSkippedCount: "{count} collections created", }; diff --git a/application/src/translations/en/incident.ts b/application/src/translations/en/incident.ts index 7a7943e..3e30c90 100644 --- a/application/src/translations/en/incident.ts +++ b/application/src/translations/en/incident.ts @@ -61,4 +61,5 @@ export const incidentTranslations: IncidentTranslations = { affectedSystems: 'Affected Systems', enterAffectedSystems: 'Enter Affected Systems', separateSystemsWithComma: 'Enter affected systems seperated by comma', + createIncidentDesc: 'Create a new incident', }; diff --git a/application/src/translations/en/maintenance.ts b/application/src/translations/en/maintenance.ts index 4d8563f..a4a4413 100644 --- a/application/src/translations/en/maintenance.ts +++ b/application/src/translations/en/maintenance.ts @@ -78,4 +78,12 @@ export const maintenanceTranslations: MaintenanceTranslations = { selectNotificationChannel: 'Add a notification channel', enableNotificationsFirst: 'Enable notifications first to select channel', updateMaintenance: 'Update Maintenance', + loginToViewProfile: 'Please login to view your profile', + goToLogin: 'Go to Login', + loadingUserData: 'Loading user data...', + retry: 'Retry', + loadingServerData: 'Loading server data...', + retrievingYourInformation: 'Please wait while we retrieve your information...', + servicesPagination: '{startItem}-{endItem} of {totalItems} services', + servicesPaginationNoService: '0 services', }; diff --git a/application/src/translations/en/services.ts b/application/src/translations/en/services.ts index 3058a85..9204fe2 100644 --- a/application/src/translations/en/services.ts +++ b/application/src/translations/en/services.ts @@ -3,7 +3,6 @@ import { ServicesTranslations } from '../types/services'; export const servicesTranslations: ServicesTranslations = { serviceStatus: "Service Status", - responseTime: "Response Time", uptime: "Uptime", lastChecked: "Last Checked", noServices: "No services match your filter criteria.", @@ -95,4 +94,46 @@ export const servicesTranslations: ServicesTranslations = { downServices: "DOWN SERVICES", pausedServices: "PAUSED SERVICES", warningServices: "WARNING SERVICES", + + // ServiceRowActions.tsx + viewDetail: "View Detail", + resumeMonitoring: "Resume Monitoring", + pauseMonitoring: "Pause Monitoring", + + + //IncidentTable.tsx + responseTime: "Response Time", + errorMessage: "Error Message", + details: "Details", + unmuteAlerts: "Unmute Alerts", + muteAlerts: "Mute Alerts", + + //LastCheckedTime.tsx + pausedAt: "Paused at ", + lastCheckDetails: "Last Check Details", + checkedAt: "Checked at ", + monitoringPaused: "Monitoring Paused", + noAutomaticChecks: "No automatic checks", + + //ServiveEditDialog.tsx + editService: "Edit Service", + editServiceDesc: "Update the details of your monitored service.", + + //ServiceStatsCards.tsx + lastCheckedAt: "Last checked at {datetime}", + avg: "Avg", + lastUpChecksCount: "last {count} up checks", + basedOnlastChecksCount: "Based on last {count} checks", + totalUptime: "Total Uptime", + totalDowntime: "Total Downtime", + monitoringSettings: "Monitoring Settings", + monitoringSettingsInterval: "Checked every {interval} seconds", + monitoringSettingsType: "monitoring", + upStatusDuration: "Up for {duration}", + downStatusDuration: "Down for {duration}", + + incidentHistory: "Incident History", + processing: "Processing", + back: "Back", + last20Checks: "Last 20 checks", }; \ No newline at end of file diff --git a/application/src/translations/en/settings.ts b/application/src/translations/en/settings.ts index 16077c5..2c4474d 100644 --- a/application/src/translations/en/settings.ts +++ b/application/src/translations/en/settings.ts @@ -37,8 +37,12 @@ export const settingsTranslations: SettingsTranslations = { selectCollection: "Select collection", toEmailAddress: "To email address", enterEmailAddress: "Enter email address", + send: "Send", sending: "Sending...", - + testEmailSettings: "Test Email Settings", + testEmailDescription: "Test whether the current Email Settings are available", + testEmailAlert: "This will send a test email using your configured SMTP settings. Make sure SMTP is properly configured first.", + // General Settings - Actions and status save: "Save Changes", saving: "Saving...", diff --git a/application/src/translations/types/about.ts b/application/src/translations/types/about.ts index 9d8a1f6..e2972c1 100644 --- a/application/src/translations/types/about.ts +++ b/application/src/translations/types/about.ts @@ -14,4 +14,19 @@ export interface AboutTranslations { quickActionsDescription: string; quickTips: string; releasedOn: string; + updateSchema: string; + updateSchemaDesc: string; + mergeFieldsLabel: string; + importing: string; + clickToUpdateSchema: string; + importSuccessful: string; + importFailed: string; + instructions: string; + mergeFields: string; + instructionsMergeFields: string; + instructionsCollections: string; + instructionsImportAuth: string; + collectionsUpdatedCount: string; + collectionsCreatedCount: string; + collectionsSkippedCount: string; } diff --git a/application/src/translations/types/incident.ts b/application/src/translations/types/incident.ts index 4ad5ccf..42c983d 100644 --- a/application/src/translations/types/incident.ts +++ b/application/src/translations/types/incident.ts @@ -59,4 +59,5 @@ export interface IncidentTranslations { affectedSystems: string; enterAffectedSystems: string; separateSystemsWithComma: string; + createIncidentDesc: string; } diff --git a/application/src/translations/types/maintenance.ts b/application/src/translations/types/maintenance.ts index 4189301..8e75bae 100644 --- a/application/src/translations/types/maintenance.ts +++ b/application/src/translations/types/maintenance.ts @@ -76,4 +76,12 @@ export interface MaintenanceTranslations { selectNotificationChannel: string; enableNotificationsFirst: string; updateMaintenance: string; + loginToViewProfile: string; + goToLogin: string; + loadingUserData: string; + retry: string; + loadingServerData: string; + retrievingYourInformation: string; + servicesPagination: string; + servicesPaginationNoService: string; } diff --git a/application/src/translations/types/services.ts b/application/src/translations/types/services.ts index bd9e7a9..cda603a 100644 --- a/application/src/translations/types/services.ts +++ b/application/src/translations/types/services.ts @@ -2,7 +2,6 @@ export interface ServicesTranslations { serviceStatus: string; - responseTime: string; uptime: string; lastChecked: string; noServices: string; @@ -93,5 +92,46 @@ export interface ServicesTranslations { upServices: string; downServices: string; pausedServices: string; - warningServices: string; + warningServices: string; + + // ServiceRowActions.tsx + viewDetail: string; + resumeMonitoring: string; + pauseMonitoring: string; + unmuteAlerts: string; + muteAlerts: string; + + //IncidentTable.tsx + responseTime: string; + errorMessage: string; + details: string; + + //LastCheckedTime.tsx + pausedAt: string; + lastCheckDetails: string; + checkedAt: string; + monitoringPaused: string; + noAutomaticChecks: string; + + //ServiveEditDialog.tsx + editService: string; + editServiceDesc: string; + + //ServiceStatsCards.tsx + lastCheckedAt: string; + avg: string; + lastUpChecksCount: string; + basedOnlastChecksCount: string; + totalUptime: string; + totalDowntime: string; + monitoringSettings: string; + monitoringSettingsInterval: string; + monitoringSettingsType: string; + upStatusDuration: string; + downStatusDuration: string; + + incidentHistory: string; + processing: string; + back: string; + last20Checks: string; } diff --git a/application/src/translations/types/settings.ts b/application/src/translations/types/settings.ts index cf4de01..1569f51 100644 --- a/application/src/translations/types/settings.ts +++ b/application/src/translations/types/settings.ts @@ -21,7 +21,7 @@ export interface SettingsTranslations { smtpAuthMethod: string; enableTLS: string; localName: string; - + // General Settings - Test Email testEmail: string; sendTestEmail: string; @@ -35,8 +35,12 @@ export interface SettingsTranslations { selectCollection: string; toEmailAddress: string; enterEmailAddress: string; + send: string; sending: string; - + testEmailSettings: string; + testEmailDescription: string; + testEmailAlert: string; + // General Settings - Actions and status save: string; saving: string; diff --git a/application/src/translations/zhcn/about.ts b/application/src/translations/zhcn/about.ts index f8eb611..5e0d3de 100644 --- a/application/src/translations/zhcn/about.ts +++ b/application/src/translations/zhcn/about.ts @@ -16,4 +16,19 @@ export const aboutTranslations: AboutTranslations = { quickActionsDescription: "快速访问常用的监控操作和功能。选择下面的操作开始。", quickTips: "快速提示", releasedOn: "发布于", + updateSchema: "更新架构", + updateSchemaDesc: "自动导入集合架构", + mergeFieldsLabel: "合并字段到现有集合(安全 - 保留数据)", + importing: "导入中...", + clickToUpdateSchema: "点击更新架构", + importSuccessful: "导入成功", + importFailed: "导入失败", + instructions: "说明", + mergeFields: "合并字段", + instructionsMergeFields: "安全地向现有集合添加新字段,保留所有数据", + instructionsCollections: "系统集合(以_开头的)和用户集合将被自动跳过", + instructionsImportAuth: "只有经过身份验证的管理员才能执行架构导入", + collectionsUpdatedCount: "已更新 {count} 个集合", + collectionsCreatedCount: "已创建 {count} 个集合", + collectionsSkippedCount: "已跳过 {count} 个集合", }; diff --git a/application/src/translations/zhcn/incident.ts b/application/src/translations/zhcn/incident.ts index 16c21c6..dc5a838 100644 --- a/application/src/translations/zhcn/incident.ts +++ b/application/src/translations/zhcn/incident.ts @@ -53,4 +53,13 @@ export const incidentTranslations: IncidentTranslations = { failedToUpdateStatus: '更新状态失败', inProgress: '进行中', enterRootCause: '输入故障根源', + enterIncidentTitle: '输入事件标题', + enterIncidentDescription: '输入事件描述', + enterServiceId: '输入以逗号分隔的服务ID', + selectAssignedUser: '选择分配用户', + noAssignedUser: '该事件当前未分配给任何用户', + affectedSystems: '受影响系统', + enterAffectedSystems: '受影响系统', + separateSystemsWithComma: '输入受影响的系统,以逗号分隔', + createIncidentDesc: '创建一个新的事件', }; diff --git a/application/src/translations/zhcn/maintenance.ts b/application/src/translations/zhcn/maintenance.ts index fa8f7c3..3568711 100644 --- a/application/src/translations/zhcn/maintenance.ts +++ b/application/src/translations/zhcn/maintenance.ts @@ -65,4 +65,25 @@ export const maintenanceTranslations: MaintenanceTranslations = { noScheduledMaintenance: '没有计划的维护', noMaintenanceWindows: '没有此时间段的维护窗口。通过点击“创建维护”按钮创建一个。', maintenanceCreatedSuccess: '维护窗口创建成功', + editMaintenanceWindow: '编辑维护', + editMaintenanceDesc: '编辑维护详情', + priorityDescription: '选择维护优先级', + statusDescription: '选择维护状态', + impactLevel: '影响级别', + impactLevelDescription: '选择影响级别', + selectAssignedUsers: '选择分配的用户', + assignedPersonnelDescription: '当前分配的用户', + mutedNotifications: '通知已禁用', + notificationsAreMuted: '此维护期间的通知当前已静音', + selectNotificationChannel: '添加通知渠道', + enableNotificationsFirst: '首先启用通知以选择频道', + updateMaintenance: '更新维护', + loginToViewProfile: '请登录查看您的个人资料', + goToLogin: '立即登录', + loadingUserData: '正在加载用户数据...', + retry: '重试', + loadingServerData: '正在加载服务器数据...', + retrievingYourInformation: '正在获取您的信息,请稍候...', + servicesPagination: '{startItem}-{endItem}, 共 {totalItems} 个服务', + servicesPaginationNoService: '0 服务', }; diff --git a/application/src/translations/zhcn/services.ts b/application/src/translations/zhcn/services.ts index 4e9cc95..f7ba25a 100644 --- a/application/src/translations/zhcn/services.ts +++ b/application/src/translations/zhcn/services.ts @@ -3,7 +3,6 @@ import { ServicesTranslations } from '../types/services'; export const servicesTranslations: ServicesTranslations = { serviceStatus: "服务状态", - responseTime: "响应时间", uptime: "在线时间", lastChecked: "最后检查时间", noServices: "没有符合您筛选条件的服务。", @@ -95,4 +94,45 @@ export const servicesTranslations: ServicesTranslations = { downServices: "下线服务", pausedServices: "暂停服务", warningServices: "告警服务", + + // ServiceRowActions.tsx + viewDetail: "查看详情", + resumeMonitoring: "恢复监控", + pauseMonitoring: "暂停监控", + unmuteAlerts: "取消静音警报", + muteAlerts: "静音警报", + + //IncidentTable.tsx + responseTime: "响应时间", + errorMessage: "错误信息", + details: "详情", + + //LastCheckedTime.tsx + pausedAt: "暂停于 ", + lastCheckDetails: "最近检查详情", + checkedAt: "检查于 ", + monitoringPaused: "监控已暂停", + noAutomaticChecks: "无自动检查", + + //ServiveEditDialog.tsx + editService: "编辑服务", + editServiceDesc: "更新您所监控服务的详细信息。", + + //ServiceStatsCards.tsx + lastCheckedAt: "最新检查时间 {datetime}", + avg: "平均", + lastUpChecksCount: "最近 {count} 次检查", + basedOnlastChecksCount: "基于最近的 {count} 次检查", + totalUptime: "总在线时间", + totalDowntime: "总宕机时间", + monitoringSettings: "监控设置", + monitoringSettingsInterval: "每 {interval} 秒检查一次", + monitoringSettingsType: "监控", + upStatusDuration: "已在线 {duration}", + downStatusDuration: "已宕机 {duration}", + + incidentHistory: "事件历史", + processing: "处理中", + back: "返回", + last20Checks: "最近 20 次检查", }; \ No newline at end of file diff --git a/application/src/translations/zhcn/settings.ts b/application/src/translations/zhcn/settings.ts index a57757e..f5544aa 100644 --- a/application/src/translations/zhcn/settings.ts +++ b/application/src/translations/zhcn/settings.ts @@ -37,8 +37,12 @@ export const settingsTranslations: SettingsTranslations = { selectCollection: "选择集合", toEmailAddress: "收件人邮箱地址", enterEmailAddress: "输入收件人邮箱地址", + send: "发送", sending: "发送中...", - + testEmailSettings: "测试邮箱配置", + testEmailDescription: "测试当前的邮箱设置是否可用", + testEmailAlert: "这将使用您配置的SMTP设置发送一封测试邮件。请确保SMTP已正确配置。", + // General Settings - Actions and status save: "保存变更", saving: "保存中...", diff --git a/docs/DEVELOPMENT_ROADMAP.md b/docs/DEVELOPMENT_ROADMAP.md index be995f4..1a87bf4 100644 --- a/docs/DEVELOPMENT_ROADMAP.md +++ b/docs/DEVELOPMENT_ROADMAP.md @@ -86,6 +86,9 @@ The roadmap is divided into the following stages: - [ ] ✅ sync German translations across incident, maintenance, services & settings. By @ghotso [#141](https://github.com/operacle/checkcle/pull/141) - [ ] ✅ fix(services-form): max retry attempts. By @ghotso [#147](https://github.com/operacle/checkcle/pull/147) - [ ] ✅ Add Pushover notifications +- [ ] ✅ fix(ssl): Ensure edit form saves notification_id and template_id in DB. +- [ ] ✅ Add Notifiarr notifications +- [ ] ✅ Add Gotify notifications - [ ] 🔧 Enhance the Data retention feature - [ ] 🔧 Improve docker agent for server monitoring - [ ] 🔧 Improve the Server Agent on Unraid @@ -96,6 +99,7 @@ The roadmap is divided into the following stages: - [ ] 🔧 Server support with Multiple Network Interfaces - [ ] 🔧 Improve the Operational status page - [ ] 🔧 Server network bandwidth monitoring +- [ ] 🔧 Grouping uptime services - [ ] 🎯 Improve the Schedule and Incident for automation - [ ] 🎯 Uptime Monitoring option for choose: HTTP/HTTPS. Add Option to Bypass SSL Verification in Uptime - [ ] 🎯 Add 2FA support @@ -106,7 +110,6 @@ The roadmap is divided into the following stages: These are community-suggested or experimental features under review: -- [ ] Grouping uptime services - [ ] OIDC Connect | OAuth2 integration - [ ] PWA support - [ ] Server outbound and inbound traffic usage @@ -125,12 +128,6 @@ To get involved: - Join us in shaping the roadmap via [Discussions](https://github.com/operacle/checkcle/discussions) --- - -## 📅 Last Updated -_This roadmap was last updated on **July 26, 2025**._ - ---- - With ❤️ from the CheckCle [Website](https://checkcle.io) | [GitHub](https://github.com/operacle/checkcle) diff --git a/docs/README_zhcn.md b/docs/README_zhcn.md index e72b236..7bdd5af 100644 --- a/docs/README_zhcn.md +++ b/docs/README_zhcn.md @@ -117,7 +117,7 @@ docker run -d \ ![Service Detail Page](https://cdn.checkcle.io/images/uptime/uptime-regional-detail.png) ![checkcle-server-instance](https://cdn.checkcle.io/images/server/server-list.png) ![SSL Monitoring](https://cdn.checkcle.io/images/ssl-domain/ssl-list.png) -![Schedule Maintenance](https://pub-4a4062303020445f8f289a2fee84f9e8.r2.dev/images/checkcle-schedule-maintenance.png) +![Notification System](https://cdn.checkcle.io/general/powerfull_notification.png) ## 🌟 面向社区的 CheckCle @@ -129,26 +129,30 @@ docker run -d \ --- ## 赞助商 -🙏 我们非常感谢我们的赞助商。你们的贡献使我们能够维护基础设施(托管、域名)、运行测试,并继续开发有价值的功能。 +我们将不再接受赞助形式。从今以后,仅接受以生态系统和社区合作形式提供的支持,例如云服务器、域名或托管资源等基础设施支持。 -### 🥈 白银在线保障合作伙伴 - -
- - +如果您是科技公司且有意支持 CheckCle ,请直接通过邮箱 tolaleng@checkcle.io 联系作者。 ### 🧡 支持者 +
+ + GitBook Logo + - - - - -### 🤝 生态系统与社区合作伙伴 - - - + + Cloudflare Logo + + + DigitalOcean Logo + +
--- diff --git a/server/service-operation/data-retention/handler.go b/server/service-operation/data-retention/handler.go new file mode 100644 index 0000000..514db3d --- /dev/null +++ b/server/service-operation/data-retention/handler.go @@ -0,0 +1,59 @@ +package dataretention + +import ( + "encoding/json" + "net/http" + "service-operation/pocketbase" +) + +// Handler handles HTTP requests for data retention operations +type Handler struct { + service *Service +} + +// NewHandler creates a new data retention handler +func NewHandler(pbClient *pocketbase.PocketBaseClient) *Handler { + return &Handler{ + service: NewService(pbClient), + } +} + +// GetSettings handles GET requests for data retention settings +func (h *Handler) GetSettings(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + settings, err := h.service.GetDataSettings() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(settings) +} + +// TriggerCleanup handles POST requests to trigger manual cleanup +func (h *Handler) TriggerCleanup(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + summary, err := h.service.CleanupOldRecords() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(summary) +} + +// SetupRoutes sets up the HTTP routes for data retention +func (h *Handler) SetupRoutes(mux *http.ServeMux) { + mux.HandleFunc("/data-retention/settings", h.GetSettings) + mux.HandleFunc("/data-retention/cleanup", h.TriggerCleanup) +} \ No newline at end of file diff --git a/server/service-operation/data-retention/scheduler.go b/server/service-operation/data-retention/scheduler.go new file mode 100644 index 0000000..a064cee --- /dev/null +++ b/server/service-operation/data-retention/scheduler.go @@ -0,0 +1,63 @@ +package dataretention + +import ( + "service-operation/pocketbase" + "time" +) + +// Scheduler handles periodic data retention cleanup +type Scheduler struct { + service *Service + interval time.Duration + stopChan chan bool +} + +// NewScheduler creates a new cleanup scheduler +func NewScheduler(pbClient *pocketbase.PocketBaseClient, interval time.Duration) *Scheduler { + return &Scheduler{ + service: NewService(pbClient), + interval: interval, + stopChan: make(chan bool), + } +} + +// Start begins the periodic cleanup process +func (s *Scheduler) Start() { + // log.Printf("Starting data retention scheduler with interval: %v", s.interval) + + ticker := time.NewTicker(s.interval) + defer ticker.Stop() + + // Run initial cleanup + go func() { + if err := s.service.ScheduleCleanup(); err != nil { + // log.Printf("Initial cleanup failed: %v", err) + } + }() + + for { + select { + case <-ticker.C: + go func() { + if err := s.service.ScheduleCleanup(); err != nil { + // log.Printf("Scheduled cleanup failed: %v", err) + } + }() + case <-s.stopChan: + // log.Println("Data retention scheduler stopped") + return + } + } +} + +// Stop stops the scheduler +func (s *Scheduler) Stop() { + // log.Println("Stopping data retention scheduler...") + s.stopChan <- true +} + +// RunOnce executes cleanup immediately (for manual triggers) +func (s *Scheduler) RunOnce() (*CleanupSummary, error) { + // log.Println("Running manual data retention cleanup...") + return s.service.CleanupOldRecords() +} \ No newline at end of file diff --git a/server/service-operation/data-retention/service.go b/server/service-operation/data-retention/service.go new file mode 100644 index 0000000..75e224c --- /dev/null +++ b/server/service-operation/data-retention/service.go @@ -0,0 +1,398 @@ +package dataretention + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" + "service-operation/pocketbase" + "sync" + "time" +) + +// Service handles data retention operations +type Service struct { + pbClient *pocketbase.PocketBaseClient +} + +// NewService creates a new data retention service +func NewService(pbClient *pocketbase.PocketBaseClient) *Service { + return &Service{ + pbClient: pbClient, + } +} + +// GetDataSettings fetches the current data retention settings +func (s *Service) GetDataSettings() (*DataSettings, error) { + resp, err := s.pbClient.GetHTTPClient().Get( + fmt.Sprintf("%s/api/collections/data_settings/records", s.pbClient.GetBaseURL()), + ) + if err != nil { + return nil, fmt.Errorf("failed to fetch data settings: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to fetch data settings, status: %d", resp.StatusCode) + } + + var response DataSettingsResponse + if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { + return nil, fmt.Errorf("failed to decode data settings response: %w", err) + } + + if len(response.Items) == 0 { + return nil, fmt.Errorf("no data settings found") + } + + return &response.Items[0], nil +} + +// CleanupOldRecords performs cleanup of old records based on retention settings +func (s *Service) CleanupOldRecords() (*CleanupSummary, error) { + startTime := time.Now() + + settings, err := s.GetDataSettings() + if err != nil { + return nil, fmt.Errorf("failed to get data settings: %w", err) + } + + // Check if cleanup should run (only once per day) + if !s.shouldRunCleanup(settings) { + // log.Println("Skipping cleanup - less than 24 hours since last run") + return &CleanupSummary{ + StartTime: startTime, + EndTime: time.Now(), + Duration: 0, + Results: make([]CleanupResult, 0), + }, nil + } + + summary := &CleanupSummary{ + StartTime: startTime, + Results: make([]CleanupResult, 0), + } + + // Define collections and their corresponding retention days + uptimeCollections := []string{"dns_data", "ping_data", "tcp_data", "uptime_data", "services_metrics"} + serverCollections := []string{"server_metrics", "docker_metrics"} + + // Process collections in parallel for better performance + var wg sync.WaitGroup + resultsChan := make(chan CleanupResult, len(uptimeCollections)+len(serverCollections)) + + // Cleanup uptime-related collections in parallel + if settings.UptimeRetentionDays > 0 { + cutoffDate := time.Now().AddDate(0, 0, -settings.UptimeRetentionDays) + // log.Printf("Cleaning uptime collections with %d days retention (cutoff: %s)", + // settings.UptimeRetentionDays, cutoffDate.Format("2006-01-02 15:04:05")) + for _, collection := range uptimeCollections { + wg.Add(1) + go func(col string) { + defer wg.Done() + result := s.cleanupCollection(col, cutoffDate) + resultsChan <- result + }(collection) + } + } else { + // log.Println("Skipping uptime collections cleanup - retention days is 0") + } + + // Cleanup server-related collections in parallel + if settings.ServerRetentionDays > 0 { + cutoffDate := time.Now().AddDate(0, 0, -settings.ServerRetentionDays) + // log.Printf("Cleaning server collections with %d days retention (cutoff: %s)", + // settings.ServerRetentionDays, cutoffDate.Format("2006-01-02 15:04:05")) + for _, collection := range serverCollections { + wg.Add(1) + go func(col string) { + defer wg.Done() + result := s.cleanupCollection(col, cutoffDate) + resultsChan <- result + }(collection) + } + } else { + // log.Println("Skipping server collections cleanup - retention days is 0") + } + + // Wait for all collections to complete and collect results + go func() { + wg.Wait() + close(resultsChan) + }() + + // Collect all results + for result := range resultsChan { + summary.Results = append(summary.Results, result) + summary.TotalDeleted += result.RecordsDeleted + } + + summary.EndTime = time.Now() + summary.Duration = summary.EndTime.Sub(summary.StartTime) + + // Update last cleanup time after successful completion + if err := s.updateLastCleanupTime(); err != nil { + // log.Printf("Warning: Failed to update last cleanup time: %v", err) + } + + // log.Printf("Data retention cleanup completed. Total records deleted: %d, Duration: %v", + // summary.TotalDeleted, summary.Duration) + + return summary, nil +} + +// cleanupCollection deletes records older than the cutoff date from a specific collection +func (s *Service) cleanupCollection(collection string, cutoffDate time.Time) CleanupResult { + result := CleanupResult{ + Collection: collection, + } + + // Format cutoff date for PocketBase filter (RFC3339 format) + cutoffStr := cutoffDate.UTC().Format("2006-01-02T15:04:05.000Z") + + // Get records to delete (balanced batch size for performance vs rate limits) + batchSize := 50 + totalDeleted := 0 + + for { + // Always fetch page 1 since we're deleting records (pagination shifts after deletes) + filter := fmt.Sprintf("created < '%s'", cutoffStr) + encodedFilter := url.QueryEscape(filter) + requestURL := fmt.Sprintf("%s/api/collections/%s/records?page=1&perPage=%d&filter=%s", + s.pbClient.GetBaseURL(), collection, batchSize, encodedFilter) + + resp, err := s.pbClient.GetHTTPClient().Get(requestURL) + if err != nil { + result.Error = fmt.Sprintf("failed to fetch records: %v", err) + return result + } + defer resp.Body.Close() + + // Check if response is successful + if resp.StatusCode != http.StatusOK { + result.Error = fmt.Sprintf("API request failed with status %d for collection %s", resp.StatusCode, collection) + return result + } + + var response struct { + Items []struct { + ID string `json:"id"` + } `json:"items"` + TotalItems int `json:"totalItems"` + } + + if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { + result.Error = fmt.Sprintf("failed to decode response for collection %s: %v", collection, err) + return result + } + + if len(response.Items) == 0 { + // log.Printf("No more old records found in collection %s", collection) + break // No more records to delete + } + + // log.Printf("Found %d old records in collection %s (batch), total items in DB: %d", + // len(response.Items), collection, response.TotalItems) + + // Delete records with conservative rate limiting to avoid 429 errors + semaphore := make(chan struct{}, 3) // Further reduced to 3 concurrent deletes + var deleteWg sync.WaitGroup + var mu sync.Mutex + batchDeleted := 0 + rateLimitHit := false + + for _, record := range response.Items { + deleteWg.Add(1) + go func(recordID string) { + defer deleteWg.Done() + semaphore <- struct{}{} // Acquire + defer func() { <-semaphore }() // Release + + deleted := false + retryCount := 0 + maxRetries := 5 + + for !deleted && retryCount < maxRetries { + if err := s.deleteRecord(collection, recordID); err != nil { + if retryCount < maxRetries-1 { + // Progressive backoff with longer delays for rate limits + baseWait := time.Duration(1<= 2 { + // Extra penalty for persistent rate limits + baseWait += time.Duration(retryCount) * 2 * time.Second + } + + mu.Lock() + rateLimitHit = true + mu.Unlock() + + time.Sleep(baseWait) + retryCount++ + continue + } else { + // log.Printf("Failed to delete record %s from %s after %d retries: %v", + // recordID, collection, maxRetries, err) + break + } + } else { + deleted = true + mu.Lock() + batchDeleted++ + mu.Unlock() + } + } + + // Increased delay between deletions with adaptive timing + mu.Lock() + currentRateLimitHit := rateLimitHit + mu.Unlock() + + if currentRateLimitHit { + time.Sleep(150 * time.Millisecond) // Longer delay if rate limits detected + } else { + time.Sleep(75 * time.Millisecond) // Standard delay + } + }(record.ID) + } + + deleteWg.Wait() + totalDeleted += batchDeleted + + // If we got fewer records than batch size, we're done + if len(response.Items) < batchSize { + // log.Printf("Finished processing collection %s - no more old records", collection) + break + } + + // More conservative delay between batches with adaptive timing + if len(response.Items) > 0 { + mu.Lock() + currentRateLimitHit := rateLimitHit + mu.Unlock() + + baseDelay := 300 * time.Millisecond + if currentRateLimitHit { + baseDelay = 1 * time.Second // Much longer delay if rate limits were hit + // log.Printf("Processed batch for collection %s (%d deleted), rate limits detected - waiting longer before next batch...", collection, batchDeleted) + } else { + // log.Printf("Processed batch for collection %s (%d deleted), processing next batch...", collection, batchDeleted) + } + + time.Sleep(baseDelay) + } + } + + result.RecordsDeleted = totalDeleted + + if totalDeleted > 0 { + // log.Printf("Deleted %d records from collection %s", totalDeleted, collection) + } + + return result +} + +// deleteRecord deletes a single record from a collection +func (s *Service) deleteRecord(collection, recordID string) error { + requestURL := fmt.Sprintf("%s/api/collections/%s/records/%s", s.pbClient.GetBaseURL(), collection, recordID) + + req, err := http.NewRequest("DELETE", requestURL, nil) + if err != nil { + return err + } + + resp, err := s.pbClient.GetHTTPClient().Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK { + return fmt.Errorf("failed to delete record, status: %d", resp.StatusCode) + } + + return nil +} + +// ScheduleCleanup runs cleanup based on schedule (can be called periodically) +func (s *Service) ScheduleCleanup() error { + // log.Println("Starting scheduled data retention cleanup...") + + _, err := s.CleanupOldRecords() + if err != nil { + // log.Printf("Scheduled cleanup failed: %v", err) + return err + } + + // log.Printf("Scheduled cleanup completed successfully. Summary: %+v", summary) + return nil +} + +// shouldRunCleanup checks if cleanup should run based on last cleanup time (once per day) +func (s *Service) shouldRunCleanup(settings *DataSettings) bool { + if settings.LastCleanup == "" { + // log.Println("No previous cleanup found, proceeding with cleanup...") + return true + } + + lastCleanup, err := time.Parse(time.RFC3339, settings.LastCleanup) + if err != nil { + // log.Printf("Failed to parse last cleanup time '%s', proceeding with cleanup: %v", settings.LastCleanup, err) + return true + } + + timeSinceLastCleanup := time.Since(lastCleanup) + minInterval := 24 * time.Hour + + if timeSinceLastCleanup < minInterval { + // log.Printf("Last cleanup was %v ago (less than 24h), skipping cleanup", timeSinceLastCleanup) + return false + } + + // log.Printf("Last cleanup was %v ago, proceeding with cleanup", timeSinceLastCleanup) + return true +} + +// updateLastCleanupTime updates the last cleanup timestamp in data settings +func (s *Service) updateLastCleanupTime() error { + settings, err := s.GetDataSettings() + if err != nil { + return fmt.Errorf("failed to get data settings: %w", err) + } + + // Update the last cleanup time + now := time.Now().UTC().Format(time.RFC3339) + + // Create the update payload + updateData := map[string]interface{}{ + "last_cleanup": now, + } + + jsonData, err := json.Marshal(updateData) + if err != nil { + return fmt.Errorf("failed to marshal update data: %w", err) + } + + // Update the record via PocketBase API + requestURL := fmt.Sprintf("%s/api/collections/data_settings/records/%s", s.pbClient.GetBaseURL(), settings.ID) + + req, err := http.NewRequest("PATCH", requestURL, bytes.NewBuffer(jsonData)) + if err != nil { + return fmt.Errorf("failed to create update request: %w", err) + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := s.pbClient.GetHTTPClient().Do(req) + if err != nil { + return fmt.Errorf("failed to update last cleanup time: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("failed to update last cleanup time, status: %d", resp.StatusCode) + } + + // log.Printf("Updated last cleanup time to: %s", now) + return nil +} \ No newline at end of file diff --git a/server/service-operation/data-retention/types.go b/server/service-operation/data-retention/types.go new file mode 100644 index 0000000..7ebdb1c --- /dev/null +++ b/server/service-operation/data-retention/types.go @@ -0,0 +1,42 @@ +package dataretention + +import "time" + +// DataSettings represents the data retention configuration from PocketBase +type DataSettings struct { + ID string `json:"id"` + CollectionId string `json:"collectionId"` + CollectionName string `json:"collectionName"` + RetentionDays int `json:"retention_days"` + ServerRetentionDays int `json:"server_retention_days"` + UptimeRetentionDays int `json:"uptime_retention_days"` + LastCleanup string `json:"last_cleanup"` + Backup string `json:"backup"` + Created string `json:"created"` + Updated string `json:"updated"` +} + +// DataSettingsResponse represents the response from PocketBase +type DataSettingsResponse struct { + Items []DataSettings `json:"items"` + Page int `json:"page"` + PerPage int `json:"perPage"` + TotalItems int `json:"totalItems"` + TotalPages int `json:"totalPages"` +} + +// CleanupResult represents the result of a cleanup operation +type CleanupResult struct { + Collection string `json:"collection"` + RecordsDeleted int `json:"records_deleted"` + Error string `json:"error,omitempty"` +} + +// CleanupSummary represents the overall cleanup summary +type CleanupSummary struct { + TotalDeleted int `json:"total_deleted"` + Results []CleanupResult `json:"results"` + StartTime time.Time `json:"start_time"` + EndTime time.Time `json:"end_time"` + Duration time.Duration `json:"duration"` +} \ No newline at end of file diff --git a/server/service-operation/main.go b/server/service-operation/main.go index e337318..37999aa 100644 --- a/server/service-operation/main.go +++ b/server/service-operation/main.go @@ -6,6 +6,7 @@ import ( "os" "os/signal" "syscall" + "time" "github.com/gorilla/mux" "service-operation/config" @@ -15,6 +16,7 @@ import ( servermonitoring "service-operation/server-monitoring" sslmonitoring "service-operation/ssl-monitoring" uptimemonitoring "service-operation/uptime-monitoring" + dataretention "service-operation/data-retention" ) func main() { @@ -35,6 +37,7 @@ func main() { var sslNotificationService *sslmonitoring.SSLMonitor var serverMonitoringService *servermonitoring.ServerMonitoringService var uptimeMonitoringService *uptimemonitoring.UptimeMonitor + var dataRetentionScheduler *dataretention.Scheduler if cfg.PocketBaseEnabled { //log.Println("🔧 Initializing PocketBase client...") @@ -80,6 +83,12 @@ func main() { uptimeMonitoringService = uptimemonitoring.NewUptimeMonitor(pbClient) go uptimeMonitoringService.Start() //log.Println("✅ Uptime monitoring started with notification support") + + // Initialize and start data retention scheduler + //log.Println("🔧 Initializing data retention scheduler...") + dataRetentionScheduler = dataretention.NewScheduler(pbClient, 24*time.Hour) // Run daily + go dataRetentionScheduler.Start() + //log.Println("✅ Data retention scheduler started (daily cleanup)") } } } @@ -123,6 +132,9 @@ func main() { log.Printf("✓Uptime monitoring enabled with notification support") } log.Printf("✓Supported operations: ping, dns, tcp, http, ssl") + if dataRetentionScheduler != nil { + log.Printf("✓Data retention scheduler enabled (daily cleanup)") + } // Setup graceful shutdown @@ -154,6 +166,10 @@ func main() { log.Println("🛑 Stopping uptime monitoring...") uptimeMonitoringService.Stop() } + if dataRetentionScheduler != nil { + log.Println("🛑 Stopping data retention scheduler...") + dataRetentionScheduler.Stop() + } log.Println("✅ All services stopped gracefully") log.Println("🛑 === SERVICE OPERATION SERVER STOPPED ===") diff --git a/server/service-operation/notification/gotify.go b/server/service-operation/notification/gotify.go new file mode 100644 index 0000000..d63a395 --- /dev/null +++ b/server/service-operation/notification/gotify.go @@ -0,0 +1,357 @@ +package notification + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "strings" +) + +// GotifyService handles Gotify notifications +type GotifyService struct{} + +// NewGotifyService creates a new Gotify notification service +func NewGotifyService() *GotifyService { + return &GotifyService{} +} + +// GotifyPayload represents the payload for Gotify API +type GotifyPayload struct { + Message string `json:"message"` + Title string `json:"title,omitempty"` + Priority int `json:"priority,omitempty"` +} + +// GotifyResponse represents the response from Gotify API +type GotifyResponse struct { + ID int `json:"id"` + AppID int `json:"appid"` + Message string `json:"message"` + Title string `json:"title"` + Priority int `json:"priority"` + Date string `json:"date"` +} + +// SendNotification sends a notification via Gotify +func (gs *GotifyService) SendNotification(config *AlertConfiguration, message string) error { + // fmt.Printf("📱 [GOTIFY] Attempting to send notification...\n") + // fmt.Printf("📱 [GOTIFY] Config - API Token: %s, Server URL: %s, Notify Name: %s\n", + // maskToken(config.APIToken), config.ServerURL, config.NotifyName) + // fmt.Printf("📱 [GOTIFY] Message: %s\n", message) + + if config.APIToken == "" || config.ServerURL == "" { + return fmt.Errorf("Gotify API token and server URL are required") + } + + // Prepare the payload + payload := GotifyPayload{ + Message: message, + Title: config.NotifyName, // Use notify_name as the title + Priority: 5, // Default priority + } + + jsonData, err := json.Marshal(payload) + if err != nil { + // fmt.Printf("❌ [GOTIFY] JSON marshal error: %v\n", err) + return err + } + + // fmt.Printf("📱 [GOTIFY] Payload: %s\n", string(jsonData)) + // fmt.Printf("📱 [GOTIFY] Sending POST request to Gotify API...\n") + + // Construct the Gotify API URL + apiURL := strings.TrimRight(config.ServerURL, "/") + "/message?token=" + config.APIToken + + // Send the request to Gotify API + resp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(jsonData)) + if err != nil { + // fmt.Printf("❌ [GOTIFY] HTTP error: %v\n", err) + return err + } + defer resp.Body.Close() + + // Check status code + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + // fmt.Printf("❌ [GOTIFY] HTTP status error: %d\n", resp.StatusCode) + return fmt.Errorf("Gotify API error: HTTP %d", resp.StatusCode) + } + + // Parse the response + var gotifyResp GotifyResponse + if err := json.NewDecoder(resp.Body).Decode(&gotifyResp); err != nil { + // fmt.Printf("❌ [GOTIFY] Response decode error: %v\n", err) + return err + } + + // fmt.Printf("📱 [GOTIFY] API response: %+v\n", gotifyResp) + + // Check if the message was created successfully + if gotifyResp.ID == 0 { + // fmt.Printf("❌ [GOTIFY] API error: No message ID returned\n") + return fmt.Errorf("Gotify API error: message not created") + } + + // fmt.Printf("✅ [GOTIFY] Message sent successfully! ID: %d\n", gotifyResp.ID) + return nil +} + +// SendServerNotification sends a server-specific notification via Gotify +func (gs *GotifyService) SendServerNotification(config *AlertConfiguration, payload *NotificationPayload, template *ServerNotificationTemplate, resourceType string) error { + message := gs.generateServerMessage(payload, template, resourceType) + return gs.SendNotification(config, message) +} + +// SendServiceNotification sends a service-specific notification via Gotify +func (gs *GotifyService) SendServiceNotification(config *AlertConfiguration, payload *NotificationPayload, template *ServiceNotificationTemplate) error { + message := gs.generateServiceMessage(payload, template) + return gs.SendNotification(config, message) +} + +// generateServerMessage creates a message for server notifications using server template +func (gs *GotifyService) generateServerMessage(payload *NotificationPayload, template *ServerNotificationTemplate, resourceType string) string { + var templateMessage string + + // Select appropriate template message based on status and resource type + switch strings.ToLower(payload.Status) { + case "down": + templateMessage = template.DownMessage + case "up": + templateMessage = template.UpMessage + case "warning": + templateMessage = template.WarningMessage + case "paused": + templateMessage = template.PausedMessage + default: + // Handle resource-specific messages + switch resourceType { + case "cpu": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreCPUMessage + } else { + templateMessage = template.CPUMessage + } + case "ram", "memory": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreRAMMessage + } else { + templateMessage = template.RAMMessage + } + case "disk": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreDiskMessage + } else { + templateMessage = template.DiskMessage + } + case "network": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreNetworkMessage + } else { + templateMessage = template.NetworkMessage + } + case "cpu_temp", "cpu_temperature": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreCPUTempMessage + } else { + templateMessage = template.CPUTempMessage + } + case "disk_io": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreDiskIOMessage + } else { + templateMessage = template.DiskIOMessage + } + default: + templateMessage = template.WarningMessage + } + } + + // If no template message found, use a default + if templateMessage == "" { + templateMessage = gs.generateDefaultServerMessage(payload, resourceType) + } + + return gs.replacePlaceholders(templateMessage, payload) +} + +// generateServiceMessage creates a message for service notifications using service template +func (gs *GotifyService) generateServiceMessage(payload *NotificationPayload, template *ServiceNotificationTemplate) string { + var templateMessage string + + // Select appropriate template message based on status + switch strings.ToLower(payload.Status) { + case "up": + templateMessage = template.UpMessage + case "down": + templateMessage = template.DownMessage + case "maintenance": + templateMessage = template.MaintenanceMessage + case "incident": + templateMessage = template.IncidentMessage + case "resolved": + templateMessage = template.ResolvedMessage + case "warning": + templateMessage = template.WarningMessage + default: + templateMessage = template.WarningMessage + } + + // If no template message found, use a default + if templateMessage == "" { + templateMessage = gs.generateDefaultUptimeMessage(payload) + } + + return gs.replacePlaceholders(templateMessage, payload) +} + +// replacePlaceholders replaces all placeholders in the message with actual values +func (gs *GotifyService) replacePlaceholders(message string, payload *NotificationPayload) string { + // Replace basic placeholders + message = strings.ReplaceAll(message, "${service_name}", payload.ServiceName) + message = strings.ReplaceAll(message, "${server_name}", payload.ServiceName) // server_name maps to service_name + message = strings.ReplaceAll(message, "${status}", strings.ToUpper(payload.Status)) + message = strings.ReplaceAll(message, "${host}", gs.safeString(payload.Host)) + message = strings.ReplaceAll(message, "${hostname}", gs.safeString(payload.Hostname)) + + // Replace URL with fallback to host + url := gs.safeString(payload.URL) + if url == "N/A" && payload.Host != "" { + url = payload.Host + } + message = strings.ReplaceAll(message, "${url}", url) + + // Replace domain + message = strings.ReplaceAll(message, "${domain}", gs.safeString(payload.Domain)) + + // Replace service type + if payload.ServiceType != "" { + message = strings.ReplaceAll(message, "${service_type}", strings.ToUpper(payload.ServiceType)) + } else { + message = strings.ReplaceAll(message, "${service_type}", "N/A") + } + + // Replace region and agent info + message = strings.ReplaceAll(message, "${region_name}", gs.safeString(payload.RegionName)) + message = strings.ReplaceAll(message, "${agent_id}", gs.safeString(payload.AgentID)) + + // Handle numeric fields safely + if payload.Port > 0 { + message = strings.ReplaceAll(message, "${port}", fmt.Sprintf("%d", payload.Port)) + } else { + message = strings.ReplaceAll(message, "${port}", "N/A") + } + + if payload.ResponseTime > 0 { + message = strings.ReplaceAll(message, "${response_time}", fmt.Sprintf("%dms", payload.ResponseTime)) + } else { + message = strings.ReplaceAll(message, "${response_time}", "N/A") + } + + if payload.Uptime > 0 { + message = strings.ReplaceAll(message, "${uptime}", fmt.Sprintf("%d%%", payload.Uptime)) + } else { + message = strings.ReplaceAll(message, "${uptime}", "N/A") + } + + // Replace server monitoring fields + message = strings.ReplaceAll(message, "${cpu_usage}", gs.safeString(payload.CPUUsage)) + message = strings.ReplaceAll(message, "${ram_usage}", gs.safeString(payload.RAMUsage)) + message = strings.ReplaceAll(message, "${disk_usage}", gs.safeString(payload.DiskUsage)) + message = strings.ReplaceAll(message, "${network_usage}", gs.safeString(payload.NetworkUsage)) + message = strings.ReplaceAll(message, "${cpu_temp}", gs.safeString(payload.CPUTemp)) + message = strings.ReplaceAll(message, "${disk_io}", gs.safeString(payload.DiskIO)) + message = strings.ReplaceAll(message, "${threshold}", gs.safeString(payload.Threshold)) + + // Replace SSL certificate fields + message = strings.ReplaceAll(message, "${certificate_name}", gs.safeString(payload.CertificateName)) + message = strings.ReplaceAll(message, "${expiry_date}", gs.safeString(payload.ExpiryDate)) + message = strings.ReplaceAll(message, "${days_left}", gs.safeString(payload.DaysLeft)) + message = strings.ReplaceAll(message, "${issuer_cn}", gs.safeString(payload.IssuerCN)) + message = strings.ReplaceAll(message, "${issuer}", gs.safeString(payload.IssuerCN)) // alias + + // Replace error message - important for uptime services + message = strings.ReplaceAll(message, "${error_message}", gs.safeString(payload.ErrorMessage)) + message = strings.ReplaceAll(message, "${error}", gs.safeString(payload.ErrorMessage)) + message = strings.ReplaceAll(message, "${message}", gs.safeString(payload.Message)) + + // Replace time placeholders + message = strings.ReplaceAll(message, "${time}", payload.Timestamp.Format("2006-01-02 15:04:05")) + message = strings.ReplaceAll(message, "${timestamp}", payload.Timestamp.Format("2006-01-02 15:04:05")) + + return message +} + +// safeString returns the string value or "N/A" if empty +func (gs *GotifyService) safeString(value string) string { + if value == "" { + return "N/A" + } + return value +} + +// generateDefaultUptimeMessage creates a default uptime message with proper formatting +func (gs *GotifyService) generateDefaultUptimeMessage(payload *NotificationPayload) string { + message := fmt.Sprintf("Service %s is %s.", payload.ServiceName, strings.ToUpper(payload.Status)) + + // Build formatted details + details := []string{} + + // Add URL or host + if payload.URL != "" { + details = append(details, fmt.Sprintf("Host URL: %s", payload.URL)) + } else if payload.Host != "" { + details = append(details, fmt.Sprintf("Host: %s", payload.Host)) + } + + // Add service type + if payload.ServiceType != "" { + details = append(details, fmt.Sprintf("Type: %s", strings.ToUpper(payload.ServiceType))) + } + + // Add port if available + if payload.Port > 0 { + details = append(details, fmt.Sprintf("Port: %d", payload.Port)) + } + + // Add domain if available + if payload.Domain != "" { + details = append(details, fmt.Sprintf("Domain: %s", payload.Domain)) + } + + // Add response time + if payload.ResponseTime > 0 { + details = append(details, fmt.Sprintf("Response time: %dms", payload.ResponseTime)) + } else { + details = append(details, "Response time: N/A") + } + + // Add region info + if payload.RegionName != "" { + details = append(details, fmt.Sprintf("Region: %s", payload.RegionName)) + } + + // Add agent info + if payload.AgentID != "" { + details = append(details, fmt.Sprintf("Agent: %s", payload.AgentID)) + } + + // Add uptime if available + if payload.Uptime > 0 { + details = append(details, fmt.Sprintf("Uptime: %d%%", payload.Uptime)) + } + + // Add timestamp + details = append(details, fmt.Sprintf("Time: %s", payload.Timestamp.Format("2006-01-02 15:04:05"))) + + // Combine message with details + if len(details) > 0 { + message += "\n" + strings.Join(details, "\n") + } + + return message +} + +// generateDefaultServerMessage creates a default server message +func (gs *GotifyService) generateDefaultServerMessage(payload *NotificationPayload, resourceType string) string { + return fmt.Sprintf("Server %s (%s) status: %s", payload.ServiceName, payload.Hostname, strings.ToUpper(payload.Status)) +} \ No newline at end of file diff --git a/server/service-operation/notification/manager.go b/server/service-operation/notification/manager.go index 39d5d69..eaed9d5 100644 --- a/server/service-operation/notification/manager.go +++ b/server/service-operation/notification/manager.go @@ -31,6 +31,8 @@ func NewNotificationManager(pbClient *pocketbase.PocketBaseClient) *Notification services["webhook"] = NewWebhookService() services["ntfy"] = NewNtfyService() services["pushover"] = NewPushoverService() + services["notifiarr"] = NewNotifiarrService() + services["gotify"] = NewGotifyService() // log.Printf("✅ Notification services initialized: %v", getKeys(services)) diff --git a/server/service-operation/notification/notifiarr.go b/server/service-operation/notification/notifiarr.go new file mode 100644 index 0000000..21a6538 --- /dev/null +++ b/server/service-operation/notification/notifiarr.go @@ -0,0 +1,416 @@ +package notification + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "strconv" + "strings" +) + +// NotifiarrService handles Notifiarr notifications +type NotifiarrService struct{} + +// NewNotifiarrService creates a new Notifiarr notification service +func NewNotifiarrService() *NotifiarrService { + return &NotifiarrService{} +} + +// NotifiarrPayload represents the payload for Notifiarr API +type NotifiarrPayload struct { + Notification NotifiarrNotification `json:"notification"` + Discord NotifiarrDiscord `json:"discord"` +} + +type NotifiarrNotification struct { + Update bool `json:"update"` + Name string `json:"name"` + Event string `json:"event,omitempty"` +} + +type NotifiarrDiscord struct { + Color string `json:"color,omitempty"` + Ping NotifiarrPing `json:"ping,omitempty"` + Images NotifiarrImages `json:"images,omitempty"` + Text NotifiarrText `json:"text"` + IDs NotifiarrIDs `json:"ids,omitempty"` +} + +type NotifiarrPing struct { + PingUser int64 `json:"pingUser,omitempty"` + PingRole int64 `json:"pingRole,omitempty"` +} + +type NotifiarrImages struct { + Thumbnail string `json:"thumbnail,omitempty"` + Image string `json:"image,omitempty"` +} + +type NotifiarrText struct { + Title string `json:"title"` + Icon string `json:"icon,omitempty"` + Content string `json:"content,omitempty"` + Description string `json:"description"` + Fields []any `json:"fields,omitempty"` + Footer string `json:"footer,omitempty"` +} + +type NotifiarrIDs struct { + Channel int64 `json:"channel,omitempty"` +} + +// SendNotification sends a notification via Notifiarr +func (ns *NotifiarrService) SendNotification(config *AlertConfiguration, message string) error { + // fmt.Printf("📡 [NOTIFIARR] Attempting to send notification...\n") + // fmt.Printf("📡 [NOTIFIARR] Config - API Token present: %v\n", config.APIToken != "") + // fmt.Printf("📡 [NOTIFIARR] Message: %s\n", message) + + if config.APIToken == "" { + return fmt.Errorf("notifiarr API token is required") + } + + // Parse channel ID if provided + var channelID int64 + if config.ChannelID != "" { + if parsed, err := strconv.ParseInt(config.ChannelID, 10, 64); err == nil { + channelID = parsed + } + } + + url := fmt.Sprintf("https://notifiarr.com/api/v1/notification/passthrough/%s", config.APIToken) + // fmt.Printf("📡 [NOTIFIARR] API URL: %s\n", strings.Replace(url, config.APIToken, "[REDACTED]", 1)) + + // Create status-based color + color := ns.getStatusColor(message) + + payload := NotifiarrPayload{ + Notification: NotifiarrNotification{ + Update: false, + Name: "This is an automated notification from CheckCle System", + Event: "", + }, + Discord: NotifiarrDiscord{ + Color: color, + Text: NotifiarrText{ + Title: "Service Alert", + Description: message, + Footer: "CheckCle Monitoring System", + }, + IDs: NotifiarrIDs{ + Channel: channelID, + }, + }, + } + + jsonData, err := json.Marshal(payload) + if err != nil { + return err + } + + // fmt.Printf("📡 [NOTIFIARR] Sending POST request...\n") + resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) + if err != nil { + // fmt.Printf("❌ [NOTIFIARR] HTTP error: %v\n", err) + return err + } + defer resp.Body.Close() + + // Check response status + if resp.StatusCode != http.StatusOK { + // fmt.Printf("❌ [NOTIFIARR] HTTP status error: %d\n", resp.StatusCode) + return fmt.Errorf("notifiarr API error: status code %d", resp.StatusCode) + } + + // fmt.Printf("✅ [NOTIFIARR] Message sent successfully!\n") + return nil +} + +// SendServerNotification sends a server-specific notification via Notifiarr +func (ns *NotifiarrService) SendServerNotification(config *AlertConfiguration, payload *NotificationPayload, template *ServerNotificationTemplate, resourceType string) error { + message := ns.generateServerMessage(payload, template, resourceType) + return ns.SendNotification(config, message) +} + +// SendServiceNotification sends a service-specific notification via Notifiarr +func (ns *NotifiarrService) SendServiceNotification(config *AlertConfiguration, payload *NotificationPayload, template *ServiceNotificationTemplate) error { + message := ns.generateServiceMessage(payload, template) + return ns.SendNotification(config, message) +} + +// generateServerMessage creates a message for server notifications using server template +func (ns *NotifiarrService) generateServerMessage(payload *NotificationPayload, template *ServerNotificationTemplate, resourceType string) string { + var templateMessage string + + // Select appropriate template message based on status and resource type + switch strings.ToLower(payload.Status) { + case "down": + templateMessage = template.DownMessage + case "up": + templateMessage = template.UpMessage + case "warning": + templateMessage = template.WarningMessage + case "paused": + templateMessage = template.PausedMessage + default: + // Handle resource-specific messages + switch resourceType { + case "cpu": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreCPUMessage + } else { + templateMessage = template.CPUMessage + } + case "ram", "memory": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreRAMMessage + } else { + templateMessage = template.RAMMessage + } + case "disk": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreDiskMessage + } else { + templateMessage = template.DiskMessage + } + case "network": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreNetworkMessage + } else { + templateMessage = template.NetworkMessage + } + case "cpu_temp", "cpu_temperature": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreCPUTempMessage + } else { + templateMessage = template.CPUTempMessage + } + case "disk_io": + if strings.Contains(strings.ToLower(payload.Status), "restore") { + templateMessage = template.RestoreDiskIOMessage + } else { + templateMessage = template.DiskIOMessage + } + default: + templateMessage = template.WarningMessage + } + } + + // If no template message found, use a default + if templateMessage == "" { + templateMessage = ns.generateDefaultServerMessage(payload, resourceType) + } + + return ns.replacePlaceholders(templateMessage, payload) +} + +// generateServiceMessage creates a message for service notifications using service template +func (ns *NotifiarrService) generateServiceMessage(payload *NotificationPayload, template *ServiceNotificationTemplate) string { + var templateMessage string + + // Select appropriate template message based on status + switch strings.ToLower(payload.Status) { + case "up": + templateMessage = template.UpMessage + case "down": + templateMessage = template.DownMessage + case "maintenance": + templateMessage = template.MaintenanceMessage + case "incident": + templateMessage = template.IncidentMessage + case "resolved": + templateMessage = template.ResolvedMessage + case "warning": + templateMessage = template.WarningMessage + default: + templateMessage = template.WarningMessage + } + + // If no template message found, use a default + if templateMessage == "" { + templateMessage = ns.generateDefaultUptimeMessage(payload) + } + + return ns.replacePlaceholders(templateMessage, payload) +} + +// replacePlaceholders replaces all placeholders in the message with actual values +func (ns *NotifiarrService) replacePlaceholders(message string, payload *NotificationPayload) string { + // Replace basic placeholders + message = strings.ReplaceAll(message, "${service_name}", payload.ServiceName) + message = strings.ReplaceAll(message, "${status}", strings.ToUpper(payload.Status)) + message = strings.ReplaceAll(message, "${host}", ns.safeString(payload.Host)) + message = strings.ReplaceAll(message, "${hostname}", ns.safeString(payload.Hostname)) + + // Replace URL with fallback to host + url := ns.safeString(payload.URL) + if url == "N/A" && payload.Host != "" { + url = payload.Host + } + message = strings.ReplaceAll(message, "${url}", url) + + // Replace domain + message = strings.ReplaceAll(message, "${domain}", ns.safeString(payload.Domain)) + + // Replace service type + if payload.ServiceType != "" { + message = strings.ReplaceAll(message, "${service_type}", strings.ToUpper(payload.ServiceType)) + } else { + message = strings.ReplaceAll(message, "${service_type}", "N/A") + } + + // Replace region and agent info + message = strings.ReplaceAll(message, "${region_name}", ns.safeString(payload.RegionName)) + message = strings.ReplaceAll(message, "${agent_id}", ns.safeString(payload.AgentID)) + + // Handle numeric fields safely + if payload.Port > 0 { + message = strings.ReplaceAll(message, "${port}", fmt.Sprintf("%d", payload.Port)) + } else { + message = strings.ReplaceAll(message, "${port}", "N/A") + } + + if payload.ResponseTime > 0 { + message = strings.ReplaceAll(message, "${response_time}", fmt.Sprintf("%dms", payload.ResponseTime)) + } else { + message = strings.ReplaceAll(message, "${response_time}", "N/A") + } + + if payload.Uptime > 0 { + message = strings.ReplaceAll(message, "${uptime}", fmt.Sprintf("%d%%", payload.Uptime)) + } else { + message = strings.ReplaceAll(message, "${uptime}", "N/A") + } + + // Replace server monitoring fields + message = strings.ReplaceAll(message, "${cpu_usage}", ns.safeString(payload.CPUUsage)) + message = strings.ReplaceAll(message, "${ram_usage}", ns.safeString(payload.RAMUsage)) + message = strings.ReplaceAll(message, "${disk_usage}", ns.safeString(payload.DiskUsage)) + message = strings.ReplaceAll(message, "${network_usage}", ns.safeString(payload.NetworkUsage)) + message = strings.ReplaceAll(message, "${cpu_temp}", ns.safeString(payload.CPUTemp)) + message = strings.ReplaceAll(message, "${disk_io}", ns.safeString(payload.DiskIO)) + message = strings.ReplaceAll(message, "${threshold}", ns.safeString(payload.Threshold)) + + // Replace error message - important for uptime services + message = strings.ReplaceAll(message, "${error_message}", ns.safeString(payload.ErrorMessage)) + message = strings.ReplaceAll(message, "${error}", ns.safeString(payload.ErrorMessage)) + + // Replace time placeholders + message = strings.ReplaceAll(message, "${time}", payload.Timestamp.Format("2006-01-02 15:04:05")) + message = strings.ReplaceAll(message, "${timestamp}", payload.Timestamp.Format("2006-01-02 15:04:05")) + + return message +} + +// safeString returns the string value or "N/A" if empty +func (ns *NotifiarrService) safeString(value string) string { + if value == "" { + return "N/A" + } + return value +} + +// getStatusColor returns appropriate color for status +func (ns *NotifiarrService) getStatusColor(message string) string { + messageLower := strings.ToLower(message) + + if strings.Contains(messageLower, "down") || strings.Contains(messageLower, "error") || strings.Contains(messageLower, "failed") { + return "FF0000" // Red + } else if strings.Contains(messageLower, "up") || strings.Contains(messageLower, "restored") || strings.Contains(messageLower, "resolved") { + return "00FF00" // Green + } else if strings.Contains(messageLower, "warning") || strings.Contains(messageLower, "alert") { + return "FFA500" // Orange + } + + return "0099FF" // Blue (default) +} + +// generateDefaultUptimeMessage creates a default uptime message with proper formatting +func (ns *NotifiarrService) generateDefaultUptimeMessage(payload *NotificationPayload) string { + // Status emoji mapping + statusEmoji := "🔵" + switch strings.ToLower(payload.Status) { + case "up": + statusEmoji = "🟢" + case "down": + statusEmoji = "🔴" + case "warning": + statusEmoji = "🟡" + case "maintenance", "paused": + statusEmoji = "🟠" + } + + message := fmt.Sprintf("%s Service %s is %s.", statusEmoji, payload.ServiceName, strings.ToUpper(payload.Status)) + + // Build formatted details + details := []string{} + + // Add URL or host + if payload.URL != "" { + details = append(details, fmt.Sprintf(" - Host URL: %s", payload.URL)) + } else if payload.Host != "" { + details = append(details, fmt.Sprintf(" - Host: %s", payload.Host)) + } + + // Add service type + if payload.ServiceType != "" { + details = append(details, fmt.Sprintf(" - Type: %s", strings.ToUpper(payload.ServiceType))) + } + + // Add port if available + if payload.Port > 0 { + details = append(details, fmt.Sprintf(" - Port: %d", payload.Port)) + } + + // Add domain if available + if payload.Domain != "" { + details = append(details, fmt.Sprintf(" - Domain: %s", payload.Domain)) + } + + // Add response time + if payload.ResponseTime > 0 { + details = append(details, fmt.Sprintf(" - Response time: %dms", payload.ResponseTime)) + } else { + details = append(details, " - Response time: N/A") + } + + // Add region info + if payload.RegionName != "" { + details = append(details, fmt.Sprintf(" - Region: %s", payload.RegionName)) + } + + // Add agent info + if payload.AgentID != "" { + details = append(details, fmt.Sprintf(" - Agent: %s", payload.AgentID)) + } + + // Add uptime if available + if payload.Uptime > 0 { + details = append(details, fmt.Sprintf(" - Uptime: %d%%", payload.Uptime)) + } + + // Add timestamp + details = append(details, fmt.Sprintf(" - Time: %s", payload.Timestamp.Format("2006-01-02 15:04:05"))) + + // Combine message with details + if len(details) > 0 { + message += "\n" + strings.Join(details, "\n") + } + + return message +} + +// generateDefaultServerMessage creates a default server message +func (ns *NotifiarrService) generateDefaultServerMessage(payload *NotificationPayload, resourceType string) string { + statusEmoji := "🔵" + switch strings.ToLower(payload.Status) { + case "up": + statusEmoji = "🟢" + case "down": + statusEmoji = "🔴" + case "warning": + statusEmoji = "🟡" + } + + return fmt.Sprintf("%s 🖥️ Server %s (%s) status: %s", statusEmoji, payload.ServiceName, payload.Hostname, strings.ToUpper(payload.Status)) +} \ No newline at end of file diff --git a/server/service-operation/notification/ntfy.go b/server/service-operation/notification/ntfy.go index 88ec7a5..9674445 100644 --- a/server/service-operation/notification/ntfy.go +++ b/server/service-operation/notification/ntfy.go @@ -32,6 +32,11 @@ func (ns *NtfyService) SendNotification(config *AlertConfiguration, message stri req.Header.Set("Title", "🔔 CheckCle Service Alert") req.Header.Set("Tags", "monitoring") req.Header.Set("Priority", "default") + + // Add API token authentication if provided + if config.APIToken != "" { + req.Header.Set("Authorization", "Bearer "+config.APIToken) + } // Send the request client := &http.Client{} @@ -111,6 +116,11 @@ func (ns *NtfyService) SendNotificationWithDetails(config *AlertConfiguration, m req.Header.Set("Tags", tags) req.Header.Set("Priority", priority) + + // Add API token authentication if provided + if config.APIToken != "" { + req.Header.Set("Authorization", "Bearer "+config.APIToken) + } // Send the request client := &http.Client{} diff --git a/server/service-operation/notification/types.go b/server/service-operation/notification/types.go index 3a8a5d2..125cbef 100644 --- a/server/service-operation/notification/types.go +++ b/server/service-operation/notification/types.go @@ -53,7 +53,7 @@ type AlertConfiguration struct { TemplateID string `json:"template_id"` SlackWebhookURL string `json:"slack_webhook_url"` GoogleChatWebhookURL string `json:"google_chat_webhook_url"` - Enabled string `json:"enabled"` // String because PocketBase returns it as string + Enabled string `json:"enabled"` EmailAddress string `json:"email_address"` EmailSenderName string `json:"email_sender_name"` SMTPServer string `json:"smtp_server"` @@ -66,6 +66,7 @@ type AlertConfiguration struct { NtfyEndpoint string `json:"ntfy_endpoint"` APIToken string `json:"api_token"` UserKey string `json:"user_key"` + ServerURL string `json:"server_url"` } // ServerNotificationTemplate represents a server notification template