diff --git a/application/index.html b/application/index.html index da62e94..200a3e1 100644 --- a/application/index.html +++ b/application/index.html @@ -20,7 +20,6 @@
- diff --git a/application/src/api/settings/actions/sendTestEmail.ts b/application/src/api/settings/actions/sendTestEmail.ts index 5617cea..10200df 100644 --- a/application/src/api/settings/actions/sendTestEmail.ts +++ b/application/src/api/settings/actions/sendTestEmail.ts @@ -3,7 +3,7 @@ import { getAuthHeaders, getBaseUrl, validateEmail } from '../utils'; import { SettingsApiResponse } from '../types'; const createEmailTemplate = (template: string, data: any): { subject: string; htmlBody: string } => { - let subject = 'Test Email from ReamStack'; + let subject = 'Test Email from CheckCle'; let htmlBody = ` @@ -13,7 +13,7 @@ const createEmailTemplate = (template: string, data: any): { subject: string; ht

If you received this email, your SMTP configuration is working correctly.


- Sent from ReamStack Monitoring System
+ Sent from CheckCle Monitoring System
Template: ${template}
${data.collection ? `Collection: ${data.collection}` : ''}

@@ -24,7 +24,7 @@ const createEmailTemplate = (template: string, data: any): { subject: string; ht switch (template) { case 'verification': - subject = 'Email Verification Test - ReamStack'; + subject = 'Email Verification Test - CheckCle'; htmlBody = ` @@ -37,14 +37,14 @@ const createEmailTemplate = (template: string, data: any): { subject: string; ht

Collection: ${data.collection || '_superusers'}


-

Sent from ReamStack Monitoring System

+

Sent from CheckCle Monitoring System

`; break; case 'password-reset': - subject = 'Password Reset Test - ReamStack'; + subject = 'Password Reset Test - CheckCle'; htmlBody = ` @@ -57,14 +57,14 @@ const createEmailTemplate = (template: string, data: any): { subject: string; ht

Collection: ${data.collection || '_superusers'}


-

Sent from ReamStack Monitoring System

+

Sent from CheckCle Monitoring System

`; break; case 'email-change': - subject = 'Email Change Confirmation Test - ReamStack'; + subject = 'Email Change Confirmation Test - CheckCle'; htmlBody = ` @@ -76,7 +76,7 @@ const createEmailTemplate = (template: string, data: any): { subject: string; ht

Template: Email Change Confirmation


-

Sent from ReamStack Monitoring System

+

Sent from CheckCle Monitoring System

@@ -178,10 +178,6 @@ export const sendTestEmail = async (data: any): Promise => smtpPort: smtpSettings.port || 587 }); - // For now, we'll simulate a successful email send - // In a real implementation, you would integrate with your email service here - // This could be nodemailer, SendGrid, or your PocketBase email system - // Simulate processing time console.log('Simulating email send...'); await new Promise(resolve => setTimeout(resolve, 1000)); diff --git a/application/src/api/settings/actions/testEmail.ts b/application/src/api/settings/actions/testEmail.ts index c3263aa..0557a86 100644 --- a/application/src/api/settings/actions/testEmail.ts +++ b/application/src/api/settings/actions/testEmail.ts @@ -2,7 +2,7 @@ import { getAuthHeaders, getBaseUrl, validateEmail } from '../utils'; import { SettingsApiResponse } from '../types'; const createEmailTemplate = (template: string, data: any): { subject: string; htmlBody: string } => { - let subject = 'Test Email from ReamStack'; + let subject = 'Test Email from CheckCle'; let htmlBody = ` @@ -12,7 +12,7 @@ const createEmailTemplate = (template: string, data: any): { subject: string; ht

If you received this email, your SMTP configuration is working correctly.


- Sent from ReamStack Monitoring System
+ Sent from CheckCle Monitoring System
Template: ${template}
${data.collection ? `Collection: ${data.collection}` : ''}

@@ -23,7 +23,7 @@ const createEmailTemplate = (template: string, data: any): { subject: string; ht switch (template) { case 'verification': - subject = 'Email Verification Test - ReamStack'; + subject = 'Email Verification Test - CheckCle'; htmlBody = ` @@ -36,14 +36,14 @@ const createEmailTemplate = (template: string, data: any): { subject: string; ht

Collection: ${data.collection || '_superusers'}


-

Sent from ReamStack Monitoring System

+

Sent from CheckCle Monitoring System

`; break; case 'password-reset': - subject = 'Password Reset Test - ReamStack'; + subject = 'Password Reset Test - CheckCle'; htmlBody = ` @@ -56,14 +56,14 @@ const createEmailTemplate = (template: string, data: any): { subject: string; ht

Collection: ${data.collection || '_superusers'}


-

Sent from ReamStack Monitoring System

+

Sent from CheckCle Monitoring System

`; break; case 'email-change': - subject = 'Email Change Confirmation Test - ReamStack'; + subject = 'Email Change Confirmation Test - CheckCle'; htmlBody = ` @@ -75,7 +75,7 @@ const createEmailTemplate = (template: string, data: any): { subject: string; ht

Template: Email Change Confirmation


-

Sent from ReamStack Monitoring System

+

Sent from CheckCle Monitoring System

diff --git a/application/src/components/servers/ServerTable.tsx b/application/src/components/servers/ServerTable.tsx index d906b44..c73172f 100644 --- a/application/src/components/servers/ServerTable.tsx +++ b/application/src/components/servers/ServerTable.tsx @@ -47,21 +47,21 @@ export const ServerTable = ({ servers, isLoading, onRefresh }: ServerTableProps) newSet.delete(serverId); return newSet; }); - console.log('Resume server monitoring:', serverId); + // console.log('Resume server monitoring:', serverId); } else { setPausedServers(prev => new Set(prev).add(serverId)); - console.log('Pause server monitoring:', serverId); + // console.log('Pause server monitoring:', serverId); } }; const handleEdit = (serverId: string) => { // TODO: Implement edit functionality - console.log('Edit server:', serverId); + // console.log('Edit server:', serverId); }; const handleDelete = (serverId: string) => { // TODO: Implement delete functionality - console.log('Delete server:', serverId); + // console.log('Delete server:', serverId); }; if (isLoading) { diff --git a/application/src/components/services/ServiceUptimeHistory.tsx b/application/src/components/services/ServiceUptimeHistory.tsx index 9dca3a6..fa75419 100644 --- a/application/src/components/services/ServiceUptimeHistory.tsx +++ b/application/src/components/services/ServiceUptimeHistory.tsx @@ -25,7 +25,7 @@ export function ServiceUptimeHistory({ const { data: uptimeHistory, isLoading, error } = useQuery({ queryKey: ['uptimeHistory', serviceId, serviceType, startDate?.toISOString(), endDate?.toISOString()], queryFn: () => { - console.log(`ServiceUptimeHistory: Fetching for service ${serviceId} of type ${serviceType}`); + // console.log(`ServiceUptimeHistory: Fetching for service ${serviceId} of type ${serviceType}`); return uptimeService.getUptimeHistory(serviceId, 200, startDate, endDate, serviceType); }, enabled: !!serviceId && !!serviceType, diff --git a/application/src/components/services/ServicesPagination.tsx b/application/src/components/services/ServicesPagination.tsx new file mode 100644 index 0000000..a9fab1a --- /dev/null +++ b/application/src/components/services/ServicesPagination.tsx @@ -0,0 +1,131 @@ + +import { + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious +} from "@/components/ui/pagination"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "@/components/ui/select"; +import { PageSize } from "@/hooks/useServicesPagination"; +import { useLanguage } from "@/contexts/LanguageContext"; + +interface ServicesPaginationProps { + currentPage: number; + totalPages: number; + pageSize: PageSize; + totalItems: number; + onPageChange: (page: number) => void; + onPageSizeChange: (size: PageSize) => void; +} + +export function ServicesPagination({ + currentPage, + totalPages, + pageSize, + totalItems, + onPageChange, + onPageSizeChange, +}: ServicesPaginationProps) { + const { t } = useLanguage(); + + // Generate page numbers to display + const getPageNumbers = () => { + const pages = []; + const maxVisiblePages = 5; + const halfVisible = Math.floor(maxVisiblePages / 2); + + let startPage = Math.max(1, currentPage - halfVisible); + let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1); + + // Adjust start page if we don't have enough pages at the end + if (endPage - startPage + 1 < maxVisiblePages) { + startPage = Math.max(1, endPage - maxVisiblePages + 1); + } + + for (let i = startPage; i <= endPage; i++) { + pages.push(i); + } + + return pages; + }; + + const startItem = Math.min((currentPage - 1) * pageSize + 1, totalItems); + const endItem = Math.min(currentPage * pageSize, totalItems); + + return ( +
+
+ + {t("rowsPerPage") ?? "Rows per page"}: + + + + {totalItems > 0 + ? `${startItem}-${endItem} of ${totalItems} ${t("services") || "services"}` + : `0 ${t("services") || "services"}` + } + +
+ + {totalPages > 1 && ( + + + + onPageChange(Math.max(1, currentPage - 1))} + className={ + currentPage === 1 + ? "pointer-events-none opacity-50" + : "cursor-pointer" + } + /> + + + {getPageNumbers().map((page) => ( + + onPageChange(page)} + className="cursor-pointer" + > + {page} + + + ))} + + + onPageChange(Math.min(totalPages, currentPage + 1))} + className={ + currentPage === totalPages + ? "pointer-events-none opacity-50" + : "cursor-pointer" + } + /> + + + + )} +
+ ); +} \ No newline at end of file diff --git a/application/src/components/services/ServicesTableContainer.tsx b/application/src/components/services/ServicesTableContainer.tsx index d8e65db..589f4ee 100644 --- a/application/src/components/services/ServicesTableContainer.tsx +++ b/application/src/components/services/ServicesTableContainer.tsx @@ -2,10 +2,12 @@ import { useEffect } from "react"; import { Service } from "@/types/service.types"; import { ServicesTableView } from "./ServicesTableView"; +import { ServicesPagination } from "./ServicesPagination"; import { ServiceDeleteDialog } from "./ServiceDeleteDialog"; import { ServiceHistoryDialog } from "./ServiceHistoryDialog"; import { ServiceEditDialog } from "./ServiceEditDialog"; import { useServiceActions, useDialogState } from "./hooks"; +import { useServicesPagination } from "@/hooks/useServicesPagination"; interface ServicesTableContainerProps { services: Service[]; @@ -36,6 +38,16 @@ export const ServicesTableContainer = ({ services }: ServicesTableContainerProps handleDeleteDialogChange } = useDialogState(); + const { + paginatedServices, + currentPage, + totalPages, + pageSize, + totalItems, + handlePageChange, + handlePageSizeChange, + } = useServicesPagination({ services: localServices }); + // Update local services state when props change useEffect(() => { updateServices(services); @@ -62,7 +74,7 @@ export const ServicesTableContainer = ({ services }: ServicesTableContainerProps return (
+ +
); -} +} \ No newline at end of file diff --git a/application/src/components/services/ServicesTableView.tsx b/application/src/components/services/ServicesTableView.tsx index 5cdb36c..cc545ca 100644 --- a/application/src/components/services/ServicesTableView.tsx +++ b/application/src/components/services/ServicesTableView.tsx @@ -26,8 +26,8 @@ export const ServicesTableView = ({ const { t } = useLanguage(); return ( -
-
+
+
@@ -65,4 +65,4 @@ export const ServicesTableView = ({ ); -}; +}; \ No newline at end of file diff --git a/application/src/components/services/add-service/ServiceNotificationFields.tsx b/application/src/components/services/add-service/ServiceNotificationFields.tsx index 1d8441a..a8824ff 100644 --- a/application/src/components/services/add-service/ServiceNotificationFields.tsx +++ b/application/src/components/services/add-service/ServiceNotificationFields.tsx @@ -22,11 +22,11 @@ export function ServiceNotificationFields({ form }: ServiceNotificationFieldsPro const notificationChannels = form.watch("notificationChannels") || []; const alertTemplate = form.watch("alertTemplate"); - console.log("Current notification values:", { - notificationStatus, - notificationChannels, - alertTemplate - }); + // console.log("Current notification values:", { + // notificationStatus, + // notificationChannels, + // alertTemplate + // }); // Fetch alert configurations for notification channels const { data: alertConfigsData } = useQuery({ @@ -42,16 +42,16 @@ export function ServiceNotificationFields({ form }: ServiceNotificationFieldsPro setAlertConfigs(enabledChannels); // Debug log to check what alert configs are loaded - console.log("Loaded alert configurations:", enabledChannels); + // console.log("Loaded alert configurations:", enabledChannels); } }, [alertConfigsData]); // Log when form values change to debug useEffect(() => { - console.log("Notification values changed:", { - notificationStatus: form.getValues("notificationStatus"), - notificationChannels: form.getValues("notificationChannels") - }); + // console.log("Notification values changed:", { + // notificationStatus: form.getValues("notificationStatus"), + // notificationChannels: form.getValues("notificationChannels") + // }); }, [form.watch("notificationStatus"), form.watch("notificationChannels")]); const handleChannelAdd = (channelId: string) => { @@ -161,7 +161,7 @@ export function ServiceNotificationFields({ form }: ServiceNotificationFieldsPro render={({ field }) => { // Don't convert existing values to "default" const displayValue = field.value || "default"; - console.log("Rendering alert template field with value:", displayValue); + // console.log("Rendering alert template field with value:", displayValue); return ( diff --git a/application/src/components/services/service-row/ServiceRowActions.tsx b/application/src/components/services/service-row/ServiceRowActions.tsx index d2cd039..7cdc1e0 100644 --- a/application/src/components/services/service-row/ServiceRowActions.tsx +++ b/application/src/components/services/service-row/ServiceRowActions.tsx @@ -39,7 +39,7 @@ export const ServiceRowActions = ({ try { if (service.status === "paused") { // Resume monitoring - console.log(`Resuming monitoring for service ${service.id} (${service.name}) from dropdown`); + // console.log(`Resuming monitoring for service ${service.id} (${service.name}) from dropdown`); // First ensure we update the status await serviceService.resumeMonitoring(service.id); @@ -53,7 +53,7 @@ export const ServiceRowActions = ({ }); } else { // Pause monitoring - console.log(`Pausing monitoring for service ${service.id} (${service.name}) from dropdown`); + // console.log(`Pausing monitoring for service ${service.id} (${service.name}) from dropdown`); await serviceService.pauseMonitoring(service.id); toast({ @@ -65,7 +65,7 @@ export const ServiceRowActions = ({ // Call the parent handler to refresh the UI onPauseResume(service); } catch (error) { - console.error("Error toggling monitoring:", error); + // console.error("Error toggling monitoring:", error); toast({ variant: "destructive", title: "Error", @@ -83,10 +83,10 @@ export const ServiceRowActions = ({ if (onMuteAlerts) { try { - console.log(`Attempting to ${alertsMuted ? 'unmute' : 'mute'} alerts for service ${service.id} (${service.name})`); + // console.log(`Attempting to ${alertsMuted ? 'unmute' : 'mute'} alerts for service ${service.id} (${service.name})`); await onMuteAlerts(service); } catch (error) { - console.error("Error toggling alerts:", error); + // console.error("Error toggling alerts:", error); toast({ variant: "destructive", title: "Error", diff --git a/application/src/components/settings/about-system/AboutSystem.tsx b/application/src/components/settings/about-system/AboutSystem.tsx index ae2ce8d..108cc32 100644 --- a/application/src/components/settings/about-system/AboutSystem.tsx +++ b/application/src/components/settings/about-system/AboutSystem.tsx @@ -83,7 +83,7 @@ export const AboutSystem: React.FC = () => { {t('links')} - {systemName || 'ReamStack'} {t('resources').toLowerCase()} + {systemName || 'CheckCle'} {t('resources').toLowerCase()} diff --git a/application/src/hooks/useServicesPagination.ts b/application/src/hooks/useServicesPagination.ts new file mode 100644 index 0000000..960aca5 --- /dev/null +++ b/application/src/hooks/useServicesPagination.ts @@ -0,0 +1,52 @@ + +import { useState, useMemo } from 'react'; +import { Service } from '@/types/service.types'; + +export type PageSize = 10 | 30 | 50; + +interface UseServicesPaginationProps { + services: Service[]; + initialPageSize?: PageSize; +} + +export const useServicesPagination = ({ + services, + initialPageSize = 10 +}: UseServicesPaginationProps) => { + const [currentPage, setCurrentPage] = useState(1); + const [pageSize, setPageSize] = useState(initialPageSize); + + const { paginatedServices, totalPages } = useMemo(() => { + const totalItems = services.length; + const pages = Math.ceil(totalItems / pageSize); + const startIndex = (currentPage - 1) * pageSize; + const endIndex = startIndex + pageSize; + + return { + paginatedServices: services.slice(startIndex, endIndex), + totalPages: Math.max(1, pages) + }; + }, [services, currentPage, pageSize]); + + // Reset to first page when page size changes or services change + const handlePageSizeChange = (newPageSize: PageSize) => { + setPageSize(newPageSize); + setCurrentPage(1); + }; + + // Reset to first page if current page exceeds total pages + const handlePageChange = (page: number) => { + const validPage = Math.min(Math.max(1, page), totalPages); + setCurrentPage(validPage); + }; + + return { + paginatedServices, + currentPage, + totalPages, + pageSize, + totalItems: services.length, + handlePageChange, + handlePageSizeChange, + }; +}; \ No newline at end of file diff --git a/application/src/hooks/useSystemSettings.tsx b/application/src/hooks/useSystemSettings.tsx index 31bdb82..195eee6 100644 --- a/application/src/hooks/useSystemSettings.tsx +++ b/application/src/hooks/useSystemSettings.tsx @@ -161,6 +161,6 @@ export function useSystemSettings() { isUpdating: updateSettingsMutation.isPending, testEmailConnection: testEmailConnectionMutation.mutate, isTestingConnection: testEmailConnectionMutation.isPending, - systemName: settings?.system_name || settings?.meta?.appName || 'ReamStack', + systemName: settings?.system_name || settings?.meta?.appName || 'CheckCle', }; } \ No newline at end of file diff --git a/application/src/services/incident/pdf/generator.ts b/application/src/services/incident/pdf/generator.ts index 6337757..5ce2ffd 100644 --- a/application/src/services/incident/pdf/generator.ts +++ b/application/src/services/incident/pdf/generator.ts @@ -38,8 +38,8 @@ export const generatePdf = async (incident: IncidentItem): Promise => { doc.setProperties({ title: title, subject: 'Incident Report', - author: 'ReamStack System', - creator: 'ReamStack', + author: 'CheckCle System', + creator: 'CheckCle', }); // Add header section diff --git a/application/src/services/incident/pdf/headerFooter.ts b/application/src/services/incident/pdf/headerFooter.ts index 9b3ff51..4e0c443 100644 --- a/application/src/services/incident/pdf/headerFooter.ts +++ b/application/src/services/incident/pdf/headerFooter.ts @@ -28,11 +28,11 @@ export const addHeader = (doc: jsPDF, incident: IncidentItem): number => { doc.setFontSize(10); doc.text(`Generated on: ${new Date().toLocaleDateString()}`, 105, yPos, { align: 'center' }); - // Add ReamStack logo or text + // Add CheckCle logo or text yPos += 8; doc.setFontSize(12); doc.setFont(fonts.italic); - doc.text('ReamStack Incident Management', 105, yPos, { align: 'center' }); + doc.text('CheckCle Incident Management', 105, yPos, { align: 'center' }); // Add horizontal line yPos += 5; @@ -53,7 +53,7 @@ export const addFooter = (doc: jsPDF): void => { doc.setFontSize(8); doc.setTextColor(100, 100, 100); doc.text( - `Page ${i} of ${pageCount} | Generated by ReamStack Incident Management System`, + `Page ${i} of ${pageCount} | Generated by CheckCle Incident Management System`, 105, 285, { align: 'center' } diff --git a/application/src/services/monitoring/service-status/pauseMonitoring.ts b/application/src/services/monitoring/service-status/pauseMonitoring.ts index 7f353a7..677b09a 100644 --- a/application/src/services/monitoring/service-status/pauseMonitoring.ts +++ b/application/src/services/monitoring/service-status/pauseMonitoring.ts @@ -31,8 +31,8 @@ export async function pauseMonitoring(serviceId: string): Promise { // We'll skip the notification here since it will be handled by the UI component // This prevents duplicate notifications for the paused status - console.log(`Service ${service.name} paused at ${now}, skipping notification to prevent duplication`); + // console.log(`Service ${service.name} paused at ${now}, skipping notification to prevent duplication`); } catch (error) { - console.error("Error pausing monitoring:", error); + // console.error("Error pausing monitoring:", error); } } diff --git a/application/src/services/monitoring/service-status/resumeMonitoring.ts b/application/src/services/monitoring/service-status/resumeMonitoring.ts index 47fdcc6..3bc16b1 100644 --- a/application/src/services/monitoring/service-status/resumeMonitoring.ts +++ b/application/src/services/monitoring/service-status/resumeMonitoring.ts @@ -16,7 +16,7 @@ export async function resumeMonitoring(serviceId: string): Promise { // Fetch the current service to get its name for better logging const service = await pb.collection('services').getOne(serviceId); - console.log(`Resuming service ${service.name} at ${now}`); + // console.log(`Resuming service ${service.name} at ${now}`); // First, clear any existing interval just to be safe const existingInterval = monitoringIntervals.get(serviceId); @@ -57,7 +57,7 @@ export async function resumeMonitoring(serviceId: string): Promise { const alertsMuted = service.alerts === "muted" || serviceForNotification.alerts === "muted"; if (!alertsMuted) { - console.log(`Alerts NOT muted for service ${service.name}, sending resume notification`); + // console.log(`Alerts NOT muted for service ${service.name}, sending resume notification`); // Send notification that service has been resumed await notificationService.sendNotification({ service: serviceForNotification, @@ -65,7 +65,7 @@ export async function resumeMonitoring(serviceId: string): Promise { timestamp: now }); } else { - console.log(`Alerts muted for service ${service.name}, skipping resume notification`); + // console.log(`Alerts muted for service ${service.name}, skipping resume notification`); } // IMPORTANT: Wait a brief moment to ensure the status update is processed @@ -76,6 +76,6 @@ export async function resumeMonitoring(serviceId: string): Promise { console.log(`Service ${service.name} resumed and ready for monitoring`); } catch (error) { - console.error("Error resuming service:", error); + // console.error("Error resuming service:", error); } } \ No newline at end of file diff --git a/application/src/services/notificationService.ts b/application/src/services/notificationService.ts index a35b556..84ac207 100644 --- a/application/src/services/notificationService.ts +++ b/application/src/services/notificationService.ts @@ -31,20 +31,20 @@ export const notificationService = { try { const { service, status, responseTime } = data; - console.log(`Preparing to send notification for service: ${service.name}, status: ${status}`); - console.log(`Service alerts status: ${service.alerts}`); + // console.log(`Preparing to send notification for service: ${service.name}, status: ${status}`); + // console.log(`Service alerts status: ${service.alerts}`); // First check if alerts are muted for this service // STRICT equality check against "muted" string value if (service.alerts === "muted") { - console.log(`NOTIFICATION BLOCKED: Alerts are muted for service: ${service.name}`); + // console.log(`NOTIFICATION BLOCKED: Alerts are muted for service: ${service.name}`); return true; // Return true as this is expected behavior } // For paused status, check if this is a duplicate notification from another source // This helps prevent the double-notification issue if (status === "paused" && data._notificationSource === "duplicate_check") { - console.log("NOTIFICATION BLOCKED: Duplicate pause notification detected"); + // console.log("NOTIFICATION BLOCKED: Duplicate pause notification detected"); return true; // Return true as this is expected behavior } @@ -62,20 +62,20 @@ export const notificationService = { if (timeSinceLastNotif < NOTIFICATION_COOLDOWN) { // Increment count only if we haven't reached max retries if (lastNotif.count < maxRetries) { - console.log(`DOWN notification for ${service.name}: ${lastNotif.count + 1}/${maxRetries}`); + // console.log(`DOWN notification for ${service.name}: ${lastNotif.count + 1}/${maxRetries}`); lastNotifications[serviceId].count += 1; } else { - console.log(`DOWN notification for ${service.name} skipped: Max retries (${maxRetries}) reached. Next notification after cooldown.`); + // console.log(`DOWN notification for ${service.name} skipped: Max retries (${maxRetries}) reached. Next notification after cooldown.`); return true; // Skip notification but return success } } else { // Reset count after cooldown period - console.log(`Cooldown period elapsed for ${service.name}. Resetting notification count.`); + // console.log(`Cooldown period elapsed for ${service.name}. Resetting notification count.`); lastNotifications[serviceId] = { timestamp: now, count: 1 }; } } else { // First notification for this service - console.log(`First DOWN notification for ${service.name}: 1/${maxRetries}`); + // console.log(`First DOWN notification for ${service.name}: 1/${maxRetries}`); lastNotifications[serviceId] = { timestamp: now, count: 1 }; } @@ -85,7 +85,7 @@ export const notificationService = { // Check if notification channel is set if (!service.notificationChannel) { - console.log(`No notification channel set for service: ${service.name}`); + // console.log(`No notification channel set for service: ${service.name}`); return false; } @@ -93,12 +93,12 @@ export const notificationService = { const alertConfigRecord = await pb.collection('alert_configurations').getOne(service.notificationChannel); if (!alertConfigRecord) { - console.error(`Alert configuration not found for ID: ${service.notificationChannel}`); + // console.error(`Alert configuration not found for ID: ${service.notificationChannel}`); return false; } if (!alertConfigRecord.enabled) { - console.log(`Alert configuration is disabled for service: ${service.name}`); + // console.log(`Alert configuration is disabled for service: ${service.name}`); return false; } @@ -127,7 +127,7 @@ export const notificationService = { try { template = await templateService.getTemplate(service.alertTemplate); } catch (error) { - console.error(`Error fetching template for ID: ${service.alertTemplate}`, error); + // console.error(`Error fetching template for ID: ${service.alertTemplate}`, error); } } @@ -146,7 +146,7 @@ export const notificationService = { message += `\n\nAlert ${retryInfo.count}/${maxRetries}`; } - console.log(`Prepared notification message: ${message}`); + // console.log(`Prepared notification message: ${message}`); // Send notification based on notification type const notificationType = alertConfig.notification_type; @@ -156,10 +156,10 @@ export const notificationService = { } // For other types like discord, slack, etc. (not implemented yet) - console.log(`Notification type ${notificationType} not implemented yet`); + // console.log(`Notification type ${notificationType} not implemented yet`); return false; } catch (error) { - console.error("Error sending notification:", error); + // console.error("Error sending notification:", error); return false; } }, @@ -173,10 +173,10 @@ export const notificationService = { ? `Service ${serviceName} is UP${responseTime ? ` (Response time: ${responseTime}ms)` : ''}` : `Service ${serviceName} is DOWN`; - console.log(`Test notification would have been sent: ${message}`); + // console.log(`Test notification would have been sent: ${message}`); return true; // Just log, don't actually send } catch (error) { - console.error("Error in test notification:", error); + // console.error("Error in test notification:", error); return false; } }, @@ -187,7 +187,7 @@ export const notificationService = { */ resetNotificationCount(serviceId: string): void { if (lastNotifications[serviceId]) { - console.log(`Resetting notification count for service ${serviceId}`); + // console.log(`Resetting notification count for service ${serviceId}`); delete lastNotifications[serviceId]; } } diff --git a/application/src/services/serviceService.ts b/application/src/services/serviceService.ts index 4421f5b..af22318 100644 --- a/application/src/services/serviceService.ts +++ b/application/src/services/serviceService.ts @@ -9,7 +9,15 @@ export type { Service, UptimeData, CreateServiceParams }; export const serviceService = { async getServices(): Promise { try { - const response = await pb.collection('services').getList(1, 50, { + + // First get the total count of records + const countResponse = await pb.collection('services').getList(1, 1, { + sort: 'name', + }); + const totalRecords = countResponse.totalItems; + + // Then fetch all records using the total count as the limit + const response = await pb.collection('services').getList(1, totalRecords, { sort: 'name', }); diff --git a/application/src/services/ssl/notification/sslNotificationSender.ts b/application/src/services/ssl/notification/sslNotificationSender.ts index 32c2f16..c104366 100644 --- a/application/src/services/ssl/notification/sslNotificationSender.ts +++ b/application/src/services/ssl/notification/sslNotificationSender.ts @@ -14,7 +14,7 @@ export async function sendSSLNotification( try { // Check if notification channel is set if (!certificate.notification_channel) { - console.log(`No notification channel set for certificate: ${certificate.domain}`); + // console.log(`No notification channel set for certificate: ${certificate.domain}`); return false; } @@ -22,12 +22,12 @@ export async function sendSSLNotification( const alertConfigRecord = await pb.collection('alert_configurations').getOne(certificate.notification_channel); if (!alertConfigRecord) { - console.error(`Alert configuration not found for ID: ${certificate.notification_channel}`); + // console.error(`Alert configuration not found for ID: ${certificate.notification_channel}`); return false; } if (!alertConfigRecord.enabled) { - console.log(`Alert configuration is disabled for certificate: ${certificate.domain}`); + // console.log(`Alert configuration is disabled for certificate: ${certificate.domain}`); return false; } @@ -63,7 +63,7 @@ export async function sendSSLNotification( return await sendNotificationByType(alertConfig, certificate, message, isCritical, sslNotification); } catch (error) { - console.error("Error sending SSL notification:", error); + // console.error("Error sending SSL notification:", error); return false; } } @@ -87,7 +87,7 @@ async function sendNotificationByType( // case 'slack': // return await sendSlackNotification(alertConfig, certificate, message, isCritical); default: - console.log(`Notification type ${alertConfig.notification_type} not implemented yet for SSL certificates`); + // console.log(`Notification type ${alertConfig.notification_type} not implemented yet for SSL certificates`); return false; } } @@ -102,7 +102,7 @@ async function sendTelegramNotification( isCritical: boolean ): Promise { if (!alertConfig.bot_token || !alertConfig.telegram_chat_id) { - console.error("Missing Telegram bot token or chat ID"); + // console.error("Missing Telegram bot token or chat ID"); return false; } diff --git a/application/src/services/ssl/sslCertificateOperations.ts b/application/src/services/ssl/sslCertificateOperations.ts index dc37b67..88dc49f 100644 --- a/application/src/services/ssl/sslCertificateOperations.ts +++ b/application/src/services/ssl/sslCertificateOperations.ts @@ -112,10 +112,10 @@ export const deleteSSLCertificate = async (id: string): Promise => { */ export const refreshAllCertificates = async (): Promise<{ success: number; failed: number }> => { try { - const response = await pb.collection("ssl_certificates").getList(1, 100); + const response = await pb.collection("ssl_certificates").getList(1, 200); const certificates = response.items as unknown as SSLCertificate[]; - console.log(`Refreshing ${certificates.length} certificates...`); + // console.log(`Refreshing ${certificates.length} certificates...`); let success = 0; let failed = 0; @@ -125,14 +125,14 @@ export const refreshAllCertificates = async (): Promise<{ success: number; faile await checkCertificateAndNotify(cert); success++; } catch (error) { - console.error(`Failed to refresh certificate ${cert.domain}:`, error); + // console.error(`Failed to refresh certificate ${cert.domain}:`, error); failed++; } } return { success, failed }; } catch (error) { - console.error("Error refreshing certificates:", error); + // console.error("Error refreshing certificates:", error); throw error; } }; \ No newline at end of file diff --git a/application/src/services/ssl/sslCheckerService.ts b/application/src/services/ssl/sslCheckerService.ts index d970e90..3e76871 100644 --- a/application/src/services/ssl/sslCheckerService.ts +++ b/application/src/services/ssl/sslCheckerService.ts @@ -1,7 +1,6 @@ import type { SSLCheckerResponse } from "./types"; import { toast } from "sonner"; -import { checkWithFetch } from "./sslPrimaryChecker"; import { normalizeDomain, createErrorResponse } from "./sslCheckerUtils"; /** @@ -10,7 +9,7 @@ import { normalizeDomain, createErrorResponse } from "./sslCheckerUtils"; */ export const checkSSLCertificate = async (domain: string): Promise => { try { - console.log(`Checking SSL certificate for domain: ${domain}`); + // console.log(`Checking SSL certificate for domain: ${domain}`); // Normalize domain (remove protocol if present) const normalizedDomain = normalizeDomain(domain); @@ -20,11 +19,11 @@ export const checkSSLCertificate = async (domain: string): Promise => { const endpoint = "/api/collections/ssl_certificates/records"; const params = { page: 1, - perPage: 50, + perPage: 200, sort: "-created", cache: Date.now() // Prevent caching by adding a timestamp }; diff --git a/application/src/services/ssl/sslPrimaryChecker.ts b/application/src/services/ssl/sslPrimaryChecker.ts deleted file mode 100644 index 59fbddc..0000000 --- a/application/src/services/ssl/sslPrimaryChecker.ts +++ /dev/null @@ -1,29 +0,0 @@ - -import { createErrorResponse } from "./sslCheckerUtils"; -import type { SSLCheckerResponse } from "./types"; - -/** - * Check SSL certificate using fetch with CORS proxy - * This is our primary method that's proven to work - */ -export const checkWithFetch = async (domain: string): Promise => { - console.log(`Checking SSL via CORS proxy for: ${domain}`); - - try { - // Use the working CORS proxy - const corsProxyUrl = `https://api.allorigins.win/raw?url=${encodeURIComponent(`https://ssl-checker.io/api/v1/check/${domain}`)}`; - console.log("Using CORS proxy for SSL check:", corsProxyUrl); - - const proxyResponse = await fetch(corsProxyUrl); - if (!proxyResponse.ok) { - throw new Error(`CORS proxy request failed with status: ${proxyResponse.status}`); - } - - const proxyData = await proxyResponse.json(); - console.log("CORS proxy returned SSL data:", proxyData); - return proxyData; - } catch (error) { - console.error("SSL check failed:", error); - return createErrorResponse(domain, error); - } -}; \ No newline at end of file diff --git a/application/src/translations/types/about.ts b/application/src/translations/types/about.ts index 244ec53..7197b39 100644 --- a/application/src/translations/types/about.ts +++ b/application/src/translations/types/about.ts @@ -1,6 +1,6 @@ export interface AboutTranslations { - aboutReamStack: string; + aboutCheckCle: string; systemDescription: string; systemVersion: string; license: string; diff --git a/server/pb_data/auxiliary.db-shm b/server/pb_data/auxiliary.db-shm index 18d61e4..9a867ac 100644 Binary files a/server/pb_data/auxiliary.db-shm and b/server/pb_data/auxiliary.db-shm differ diff --git a/server/pb_data/auxiliary.db-wal b/server/pb_data/auxiliary.db-wal index 464dc0c..fe1b1ff 100644 Binary files a/server/pb_data/auxiliary.db-wal and b/server/pb_data/auxiliary.db-wal differ diff --git a/server/pb_data/data.db b/server/pb_data/data.db index 1258910..8047a3c 100644 Binary files a/server/pb_data/data.db and b/server/pb_data/data.db differ diff --git a/server/pb_data/data.db-shm b/server/pb_data/data.db-shm new file mode 100644 index 0000000..fe9ac28 Binary files /dev/null and b/server/pb_data/data.db-shm differ diff --git a/server/pb_data/data.db-wal b/server/pb_data/data.db-wal new file mode 100644 index 0000000..e69de29 diff --git a/server/service-operation/pocketbase/services.go b/server/service-operation/pocketbase/services.go index 249f7a1..11ef4db 100644 --- a/server/service-operation/pocketbase/services.go +++ b/server/service-operation/pocketbase/services.go @@ -62,30 +62,47 @@ func (c *PocketBaseClient) GetService(serviceID string) (*Service, error) { } func (c *PocketBaseClient) GetActiveServices() ([]Service, error) { - // Only fetch services that are not paused - req, err := http.NewRequest("GET", - fmt.Sprintf("%s/api/collections/services/records?filter=(status!='paused')", c.baseURL), nil) - if err != nil { - return nil, err + var allServices []Service + page := 1 + perPage := 30 // Use default pagination size + + for { + // Fetch services page by page with filter for non-paused services + req, err := http.NewRequest("GET", + fmt.Sprintf("%s/api/collections/services/records?page=%d&perPage=%d&filter=(status!='paused')", + c.baseURL, page, perPage), nil) + if err != nil { + return nil, err + } + + // No authentication header needed for public access + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to fetch active services, status: %d", resp.StatusCode) + } + + var servicesResponse ServicesResponse + if err := json.NewDecoder(resp.Body).Decode(&servicesResponse); err != nil { + return nil, err + } + + // Add current page items to the result + allServices = append(allServices, servicesResponse.Items...) + + // Check if we've fetched all pages + if page >= servicesResponse.TotalPages || len(servicesResponse.Items) == 0 { + break + } + + page++ } - // No authentication header needed for public access - resp, err := c.httpClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to fetch active services, status: %d", resp.StatusCode) - } - - var servicesResponse ServicesResponse - if err := json.NewDecoder(resp.Body).Decode(&servicesResponse); err != nil { - return nil, err - } - - return servicesResponse.Items, nil + return allServices, nil } func (c *PocketBaseClient) UpdateServiceStatus(serviceID string, status string, responseTime int64, errorMessage string) error { diff --git a/server/service-operation/pocketbase/ssl.go b/server/service-operation/pocketbase/ssl.go index 79a4008..6e39bfd 100644 --- a/server/service-operation/pocketbase/ssl.go +++ b/server/service-operation/pocketbase/ssl.go @@ -1,4 +1,3 @@ - package pocketbase import ( @@ -11,37 +10,53 @@ import ( ) type SSLCertificatesResponse struct { - Page int `json:"page"` - PerPage int `json:"perPage"` - TotalItems int `json:"totalItems"` - TotalPages int `json:"totalPages"` - Items []types.SSLCertificate `json:"items"` + Page int `json:"page"` + PerPage int `json:"perPage"` + TotalItems int `json:"totalItems"` + TotalPages int `json:"totalPages"` + Items []types.SSLCertificate `json:"items"` } func (c *PocketBaseClient) GetSSLCertificates() ([]types.SSLCertificate, error) { - url := fmt.Sprintf("%s/api/collections/ssl_certificates/records", c.baseURL) - - resp, err := c.httpClient.Get(url) - if err != nil { - return nil, fmt.Errorf("failed to fetch SSL certificates: %v", err) - } - defer resp.Body.Close() + var allCertificates []types.SSLCertificate + page := 1 + perPage := 200 // You can increase up to 500 if needed - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("PocketBase returned status %d", resp.StatusCode) + for { + url := fmt.Sprintf( + "%s/api/collections/ssl_certificates/records?page=%d&perPage=%d", + c.baseURL, page, perPage, + ) + + resp, err := c.httpClient.Get(url) + if err != nil { + return nil, fmt.Errorf("failed to fetch SSL certificates: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("PocketBase returned status %d", resp.StatusCode) + } + + var response SSLCertificatesResponse + if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { + return nil, fmt.Errorf("failed to decode SSL certificates response: %v", err) + } + + allCertificates = append(allCertificates, response.Items...) + + if page >= response.TotalPages || len(response.Items) == 0 { + break + } + page++ } - var response SSLCertificatesResponse - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return nil, fmt.Errorf("failed to decode SSL certificates response: %v", err) - } - - return response.Items, nil + return allCertificates, nil } func (c *PocketBaseClient) UpdateSSLCertificate(id string, data map[string]interface{}) error { url := fmt.Sprintf("%s/api/collections/ssl_certificates/records/%s", c.baseURL, id) - + jsonData, err := json.Marshal(data) if err != nil { return fmt.Errorf("failed to marshal SSL certificate data: %v", err) @@ -53,7 +68,7 @@ func (c *PocketBaseClient) UpdateSSLCertificate(id string, data map[string]inter } req.Header.Set("Content-Type", "application/json") - + resp, err := c.httpClient.Do(req) if err != nil { return fmt.Errorf("failed to update SSL certificate: %v", err) @@ -69,7 +84,7 @@ func (c *PocketBaseClient) UpdateSSLCertificate(id string, data map[string]inter func (c *PocketBaseClient) GetSSLCertificateByID(id string) (*types.SSLCertificate, error) { url := fmt.Sprintf("%s/api/collections/ssl_certificates/records/%s", c.baseURL, id) - + resp, err := c.httpClient.Get(url) if err != nil { return nil, fmt.Errorf("failed to fetch SSL certificate: %v", err) @@ -86,4 +101,4 @@ func (c *PocketBaseClient) GetSSLCertificateByID(id string) (*types.SSLCertifica } return &cert, nil -} \ No newline at end of file +}