mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-30 13:49:25 -06:00
Merge branch 'develop' of https://github.com/Shivansh-22866/Checkmate into fix/remove_future_features
This commit is contained in:
3
.github/pull_request_template.md
vendored
3
.github/pull_request_template.md
vendored
@@ -20,7 +20,8 @@ const { t } = useTranslation();
|
||||
```
|
||||
- [ ] I have **not** included any files that are not related to my pull request, including package-lock and package-json if dependencies have not changed
|
||||
- [ ] I didn't use any hardcoded values (otherwise it will not scale, and will make it difficult to maintain consistency across the application).
|
||||
- [ ] I made sure font sizes, color choices etc are all referenced from the theme. I have no hardcoded dimensions.
|
||||
- [ ] I made sure font sizes, color choices etc are all referenced from the theme. I don't have any hardcoded dimensions.
|
||||
- [ ] My PR is granular and targeted to one specific feature.
|
||||
- [ ] I ran `npm run format` in server and client directories, which automatically formats your code.
|
||||
- [ ] I took a screenshot or a video and attached to this PR if there is a UI change.
|
||||
|
||||
|
||||
@@ -200,13 +200,31 @@ const Settings = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-end"
|
||||
sx={{
|
||||
position: "sticky",
|
||||
bottom: 0,
|
||||
boxShadow: theme.shape.boxShadow,
|
||||
zIndex: 1000,
|
||||
mt: 3,
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
pb: theme.spacing(4),
|
||||
pr: theme.spacing(15),
|
||||
pl: theme.spacing(5),
|
||||
pt: theme.spacing(4),
|
||||
border: 1,
|
||||
borderStyle: "solid",
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
borderRadius: theme.spacing(2),
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
loading={isSaving || isDeletingMonitorStats || isSettingsLoading}
|
||||
disabled={Object.keys(errors).length > 0}
|
||||
variant="contained"
|
||||
color="accent"
|
||||
sx={{ px: theme.spacing(12), mt: theme.spacing(20) }}
|
||||
sx={{ px: theme.spacing(12), py: theme.spacing(8) }}
|
||||
onClick={handleSave}
|
||||
>
|
||||
{t("settingsSave")}
|
||||
|
||||
@@ -15,7 +15,7 @@ const useMonitorsFetch = () => {
|
||||
const response = await networkService.getMonitorsByTeamId({
|
||||
teamId: user.teamId,
|
||||
limit: null, // donot return any checks for the monitors
|
||||
types: ["http", "ping"], // status page is available for uptime and ping monitors
|
||||
types: ["http", "ping", "port"], // status page is available for uptime, ping, and port monitors
|
||||
});
|
||||
setMonitors(response.data.data.monitors);
|
||||
} catch (error) {
|
||||
|
||||
@@ -23,7 +23,7 @@ const UploadFile = ({ onFileSelect }) => {
|
||||
// Basic file validation
|
||||
if (!selectedFile) return;
|
||||
|
||||
if (!selectedFile.name.endsWith(".csv")) {
|
||||
if (!selectedFile.name.toLowerCase().endsWith(".csv")) {
|
||||
setError(t("bulkImport.invalidFileType"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -353,7 +353,9 @@ const CreateMonitor = () => {
|
||||
>
|
||||
{t("settingsGeneralSettings")}
|
||||
</Typography>
|
||||
<Typography component="p">{t(`uptimeGeneralInstructions.${monitor.type}`)}</Typography>
|
||||
<Typography component="p">
|
||||
{t(`uptimeGeneralInstructions.${monitor.type}`)}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(15)}>
|
||||
<TextInput
|
||||
|
||||
@@ -532,7 +532,6 @@
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
},
|
||||
"uptimeCreateSelectURL": "",
|
||||
"settingsEmailConnectionHost": "",
|
||||
"sendTestEmail": "",
|
||||
"emailSent": "",
|
||||
@@ -553,5 +552,11 @@
|
||||
"settingsURLDescription": "",
|
||||
"settingsURLSelectTitle": "",
|
||||
"settingsURLEnabled": "",
|
||||
"settingURLDisabled": ""
|
||||
"settingURLDisabled": "",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +532,6 @@
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
},
|
||||
"uptimeCreateSelectURL": "",
|
||||
"settingsEmailConnectionHost": "",
|
||||
"sendTestEmail": "",
|
||||
"emailSent": "",
|
||||
@@ -553,5 +552,11 @@
|
||||
"settingsURLDescription": "",
|
||||
"settingsURLSelectTitle": "",
|
||||
"settingsURLEnabled": "",
|
||||
"settingURLDisabled": ""
|
||||
"settingURLDisabled": "",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +532,6 @@
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
},
|
||||
"uptimeCreateSelectURL": "",
|
||||
"settingsEmailConnectionHost": "",
|
||||
"sendTestEmail": "",
|
||||
"emailSent": "",
|
||||
@@ -553,5 +552,11 @@
|
||||
"settingsURLDescription": "",
|
||||
"settingsURLSelectTitle": "",
|
||||
"settingsURLEnabled": "",
|
||||
"settingURLDisabled": ""
|
||||
"settingURLDisabled": "",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,12 +532,6 @@
|
||||
"passwordRequirements": "New password must contain at least 8 characters and must have at least one uppercase letter, one lowercase letter, one number and one special character.",
|
||||
"saving": "Saving..."
|
||||
},
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "Enter the URL or IP to monitor (e.g., https://example.com/ or 192.168.1.100) and add a clear display name that appears on the dashboard.",
|
||||
"ping": "Enter the URL or IP to ping (e.g., https://example.com/ or 192.168.1.100) and add a clear display name that appears on the dashboard.",
|
||||
"docker": "Enter the Docker ID of your container. Docker IDs must be the full 64 char Docker ID. You can run docker inpsect <short_id> to get the full container ID.",
|
||||
"port": "Enter the URL or IP of the server, the port number and a clear display name that appears on the dashboard."
|
||||
},
|
||||
"settingsEmailConnectionHost": "Email connection host - Hostname to use in the HELO/EHLO greeting",
|
||||
"sendTestEmail": "Send test email",
|
||||
"emailSent": "Email sent successfully",
|
||||
@@ -558,5 +552,11 @@
|
||||
"settingsURLDescription": "Display the IP address or URL of monitor on the public Status page. If it's disabled, only the monitor name will be shown to protect sensitive information.",
|
||||
"settingsURLSelectTitle": "Display IP/URL on status page",
|
||||
"settingsURLEnabled": "Enabled",
|
||||
"settingURLDisabled": "Disabled"
|
||||
"settingURLDisabled": "Disabled",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "Enter the URL or IP to monitor (e.g., https://example.com/ or 192.168.1.100) and add a clear display name that appears on the dashboard.",
|
||||
"ping": "Enter the IP address or hostname to ping (e.g., 192.168.1.100 or example.com) and add a clear display name that appears on the dashboard.",
|
||||
"docker": "Enter the Docker ID of your container. Docker IDs must be the full 64 char Docker ID. You can run docker inspect <short_id> to get the full container ID.",
|
||||
"port": "Enter the URL or IP of the server, the port number and a clear display name that appears on the dashboard."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +532,6 @@
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
},
|
||||
"uptimeCreateSelectURL": "",
|
||||
"settingsEmailConnectionHost": "",
|
||||
"sendTestEmail": "",
|
||||
"emailSent": "",
|
||||
@@ -553,5 +552,11 @@
|
||||
"settingsURLDescription": "",
|
||||
"settingsURLSelectTitle": "",
|
||||
"settingsURLEnabled": "",
|
||||
"settingURLDisabled": ""
|
||||
"settingURLDisabled": "",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@
|
||||
"infrastructureCustomizeAlerts": "",
|
||||
"infrastructureAlertNotificationDescription": "",
|
||||
"infrastructureCreateMonitor": "",
|
||||
"infrastructureProtocol": "",
|
||||
"infrastructureProtocol": "Protokolla",
|
||||
"infrastructureServerUrlLabel": "Palvelimen URL-osoite",
|
||||
"infrastructureDisplayNameLabel": "",
|
||||
"infrastructureAuthorizationSecretLabel": "",
|
||||
@@ -353,11 +353,11 @@
|
||||
"statusPageCreateTabsContent": "",
|
||||
"statusPageCreateTabsContentDescription": "",
|
||||
"statusPageCreateTabsContentFeaturesDescription": "",
|
||||
"showCharts": "",
|
||||
"showCharts": "Näytä kaaviot",
|
||||
"showUptimePercentage": "",
|
||||
"removeLogo": "Poista logo",
|
||||
"statusPageStatus": "",
|
||||
"statusPageStatusContactAdmin": "",
|
||||
"statusPageStatusContactAdmin": "Ota yhteyttä järjestelmänvalvojaan",
|
||||
"statusPageStatusNotPublic": "",
|
||||
"statusPageStatusNoPage": "",
|
||||
"statusPageStatusServiceStatus": "",
|
||||
@@ -397,7 +397,7 @@
|
||||
"authRegisterFirstName": "Nimi",
|
||||
"authRegisterLastName": "Sukunimi",
|
||||
"authRegisterEmail": "Sähköpostiosoite",
|
||||
"authRegisterEmailRequired": "",
|
||||
"authRegisterEmailRequired": "Syötä sähköpostiosoitteesi jatkaaksesi",
|
||||
"bulkImport": {
|
||||
"title": "",
|
||||
"selectFileTips": "",
|
||||
@@ -426,7 +426,7 @@
|
||||
"backendUnreachableMessage": "",
|
||||
"backendUnreachableError": "",
|
||||
"retryConnection": "",
|
||||
"retryingConnection": "",
|
||||
"retryingConnection": "Yhdistetään...",
|
||||
"backendReconnected": "",
|
||||
"backendStillUnreachable": "",
|
||||
"backendConnectionError": "",
|
||||
@@ -441,7 +441,7 @@
|
||||
"ignoreTLSErrorDescription": "",
|
||||
"createNew": "Luo uusi",
|
||||
"greeting": {
|
||||
"prepend": "",
|
||||
"prepend": "Hei",
|
||||
"append": "",
|
||||
"overview": ""
|
||||
},
|
||||
@@ -453,11 +453,11 @@
|
||||
"roles": {
|
||||
"superAdmin": "Pääylläpitäjä",
|
||||
"admin": "Ylläpitäjä",
|
||||
"teamMember": "",
|
||||
"teamMember": "Tiimin jäsen",
|
||||
"demoUser": "Demokäyttäjä"
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "",
|
||||
"teamMembers": "Tiimin jäsenet",
|
||||
"filter": {
|
||||
"all": "Kaikki",
|
||||
"member": "Jäsen"
|
||||
@@ -532,7 +532,6 @@
|
||||
"passwordRequirements": "Uuden salasanan on oltava vähintään 8 merkkiä pitkä ja sen on sisältävä vähintään yksi iso kirjain, yksi pieni kirjain, yksi numero sekä yksi erikoismerkki.",
|
||||
"saving": "Tallennetaan..."
|
||||
},
|
||||
"uptimeCreateSelectURL": "",
|
||||
"settingsEmailConnectionHost": "",
|
||||
"sendTestEmail": "Lähetä testi sähköposti",
|
||||
"emailSent": "Sähköpostin lähetys onnistui",
|
||||
@@ -553,5 +552,11 @@
|
||||
"settingsURLDescription": "",
|
||||
"settingsURLSelectTitle": "",
|
||||
"settingsURLEnabled": "",
|
||||
"settingURLDisabled": ""
|
||||
"settingURLDisabled": "",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
"authRegisterTitle": "Créer un compte",
|
||||
"authRegisterStepOneTitle": "Créez votre compte",
|
||||
"authRegisterStepOneDescription": "Entrez vos informations pour commencer",
|
||||
"authRegisterStepTwoTitle": "",
|
||||
"authRegisterStepTwoDescription": "",
|
||||
"authRegisterStepTwoTitle": "Créer votre profil",
|
||||
"authRegisterStepTwoDescription": "Parlez-nous un peu de vous",
|
||||
"authRegisterStepThreeTitle": "Presque fini !",
|
||||
"authRegisterStepThreeDescription": "",
|
||||
"authRegisterStepThreeDescription": "Vérifiez vos infos",
|
||||
"authForgotPasswordDescription": "Pas d'inquiétude, nous allons vous envoyer des instructions de réinitialisation.",
|
||||
"authForgotPasswordSendInstructions": "Envoyer les instructions",
|
||||
"authForgotPasswordBackTo": "Retourner à",
|
||||
@@ -50,7 +50,7 @@
|
||||
"authPasswordOneNumber": "un nombre",
|
||||
"authPasswordUpperCharacter": "une lettre majuscule",
|
||||
"authPasswordLowerCharacter": "une lettre minuscule",
|
||||
"authPasswordConfirmAndPassword": "",
|
||||
"authPasswordConfirmAndPassword": "Confirmer le mot de passe",
|
||||
"authPasswordMustMatch": "doit correspondre",
|
||||
"authRegisterCreateAccount": "Créez votre compte pour commencer",
|
||||
"authRegisterCreateSuperAdminAccount": "Créez votre compte Super admin pour commencer",
|
||||
@@ -72,12 +72,12 @@
|
||||
"settingsHistoryAndMonitoring": "",
|
||||
"settingsHistoryAndMonitoringDescription": "",
|
||||
"settingsTTLLabel": "",
|
||||
"settingsTTLOptionalLabel": "",
|
||||
"settingsClearAllStats": "",
|
||||
"settingsClearAllStatsButton": "",
|
||||
"settingsClearAllStatsDialogTitle": "",
|
||||
"settingsTTLOptionalLabel": "0 pour infini",
|
||||
"settingsClearAllStats": "Effacer les stats. C'est irréversible.",
|
||||
"settingsClearAllStatsButton": "Effacer les stats",
|
||||
"settingsClearAllStatsDialogTitle": "Voulez-vous effacer toutes les stats ?",
|
||||
"settingsClearAllStatsDialogDescription": "",
|
||||
"settingsClearAllStatsDialogConfirm": "",
|
||||
"settingsClearAllStatsDialogConfirm": "Oui, effacer les stats",
|
||||
"settingsDemoMonitors": "",
|
||||
"settingsDemoMonitorsDescription": "",
|
||||
"settingsAddDemoMonitors": "",
|
||||
@@ -104,37 +104,37 @@
|
||||
"https": "",
|
||||
"http": "",
|
||||
"monitor": "",
|
||||
"aboutus": "",
|
||||
"aboutus": "A propos de nous",
|
||||
"now": "",
|
||||
"delete": "",
|
||||
"delete": "Supprimer",
|
||||
"configure": "",
|
||||
"networkError": "",
|
||||
"responseTime": "",
|
||||
"ms": "",
|
||||
"bar": "",
|
||||
"area": "",
|
||||
"country": "",
|
||||
"city": "",
|
||||
"area": "Zone",
|
||||
"country": "PAYS",
|
||||
"city": "VILLE",
|
||||
"response": "",
|
||||
"checkConnection": "",
|
||||
"checkConnection": "Vérifiez votre connexion",
|
||||
"passwordreset": "",
|
||||
"authRegisterStepOnePersonalDetails": "",
|
||||
"authCheckEmailOpenEmailButton": "",
|
||||
"authNewPasswordConfirmed": "",
|
||||
"authRegisterStepOnePersonalDetails": "Saisissez vos infos",
|
||||
"authCheckEmailOpenEmailButton": "Ouvrir l'appli mail",
|
||||
"authNewPasswordConfirmed": "Votre mot de passe a été réinitialisé avec succès. Cliquez ci-dessous pour vous connecter.",
|
||||
"monitorStatusUp": "",
|
||||
"monitorStatusDown": "",
|
||||
"webhookSendSuccess": "",
|
||||
"webhookSendError": "",
|
||||
"webhookUnsupportedPlatform": "",
|
||||
"distributedRightCategoryTitle": "",
|
||||
"distributedRightCategoryTitle": "Moniteur",
|
||||
"distributedStatusServerMonitors": "",
|
||||
"distributedStatusServerMonitorsDescription": "",
|
||||
"distributedUptimeCreateSelectURL": "",
|
||||
"distributedUptimeCreateChecks": "",
|
||||
"distributedUptimeCreateChecksDescription": "",
|
||||
"distributedUptimeCreateIncidentNotification": "",
|
||||
"distributedUptimeCreateIncidentNotification": "Notifications d'incidents",
|
||||
"distributedUptimeCreateIncidentDescription": "",
|
||||
"distributedUptimeCreateAdvancedSettings": "",
|
||||
"distributedUptimeCreateAdvancedSettings": "Paramètres avancés",
|
||||
"distributedUptimeDetailsNoMonitorHistory": "",
|
||||
"distributedUptimeDetailsFooterHeading": "",
|
||||
"distributedUptimeDetailsFooterBuilt": "",
|
||||
@@ -144,19 +144,19 @@
|
||||
"distributedUptimeDetailsStatusHeaderLastUpdate": "",
|
||||
"notifications": {
|
||||
"enableNotifications": "",
|
||||
"testNotification": "",
|
||||
"addOrEditNotifications": "",
|
||||
"testNotification": "Notification de test",
|
||||
"addOrEditNotifications": "Ajouter ou éditer des notifications",
|
||||
"slack": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookLabel": "URL du webhook",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
"discord": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"webhookLabel": "",
|
||||
"webhookLabel": "URL du webhook Discord",
|
||||
"webhookPlaceholder": "",
|
||||
"webhookRequired": ""
|
||||
},
|
||||
@@ -165,71 +165,71 @@
|
||||
"description": "",
|
||||
"tokenLabel": "",
|
||||
"tokenPlaceholder": "",
|
||||
"chatIdLabel": "",
|
||||
"chatIdPlaceholder": "",
|
||||
"chatIdLabel": "Votre Chat ID",
|
||||
"chatIdPlaceholder": "-1001234567890",
|
||||
"fieldsRequired": ""
|
||||
},
|
||||
"webhook": {
|
||||
"label": "",
|
||||
"description": "",
|
||||
"urlLabel": "",
|
||||
"urlPlaceholder": "",
|
||||
"urlRequired": ""
|
||||
"urlLabel": "URL du webhook",
|
||||
"urlPlaceholder": "https://votre-serveur.fr/webhook",
|
||||
"urlRequired": "L'URL du webhook est nécessaire"
|
||||
},
|
||||
"testNotificationDevelop": "",
|
||||
"integrationButton": "",
|
||||
"testSuccess": "",
|
||||
"testFailed": "",
|
||||
"testNotificationDevelop": "Notification de test 2",
|
||||
"integrationButton": "Intégration de notification",
|
||||
"testSuccess": "La notification de test a été envoyée avec succès !",
|
||||
"testFailed": "Échec de l'envoi de la notification de test",
|
||||
"unsupportedType": "",
|
||||
"networkError": ""
|
||||
},
|
||||
"testLocale": "",
|
||||
"add": "",
|
||||
"add": "Ajouter",
|
||||
"monitors": "",
|
||||
"distributedUptimeStatusCreateStatusPage": "",
|
||||
"distributedUptimeStatusCreateStatusPageAccess": "",
|
||||
"distributedUptimeStatusCreateStatusPage": "page de statut",
|
||||
"distributedUptimeStatusCreateStatusPageAccess": "Accès",
|
||||
"distributedUptimeStatusCreateStatusPageReady": "",
|
||||
"distributedUptimeStatusBasicInfoHeader": "",
|
||||
"distributedUptimeStatusBasicInfoDescription": "",
|
||||
"distributedUptimeStatusLogoHeader": "",
|
||||
"distributedUptimeStatusLogoHeader": "Logo",
|
||||
"distributedUptimeStatusLogoDescription": "",
|
||||
"distributedUptimeStatusLogoUploadButton": "",
|
||||
"distributedUptimeStatusLogoUploadButton": "Téléverser un logo",
|
||||
"distributedUptimeStatusStandardMonitorsHeader": "",
|
||||
"distributedUptimeStatusStandardMonitorsDescription": "",
|
||||
"distributedUptimeStatusCreateYour": "",
|
||||
"distributedUptimeStatusEditYour": "",
|
||||
"distributedUptimeStatusPublishedLabel": "",
|
||||
"distributedUptimeStatusCompanyNameLabel": "",
|
||||
"distributedUptimeStatusPageAddressLabel": "",
|
||||
"distributedUptimeStatus30Days": "",
|
||||
"distributedUptimeStatus60Days": "",
|
||||
"distributedUptimeStatus90Days": "",
|
||||
"distributedUptimeStatusCompanyNameLabel": "Nom de l'entreprise",
|
||||
"distributedUptimeStatusPageAddressLabel": "Adresse de votre page de statut",
|
||||
"distributedUptimeStatus30Days": "30 jours",
|
||||
"distributedUptimeStatus60Days": "60 jours",
|
||||
"distributedUptimeStatus90Days": "90 jours",
|
||||
"distributedUptimeStatusPageNotSetUp": "",
|
||||
"distributedUptimeStatusContactAdmin": "",
|
||||
"distributedUptimeStatusPageNotPublic": "",
|
||||
"distributedUptimeStatusPageDeleteDialog": "",
|
||||
"distributedUptimeStatusPageDeleteConfirm": "",
|
||||
"distributedUptimeStatusPageDeleteDescription": "",
|
||||
"distributedUptimeStatusContactAdmin": "Veuillez contacter votre administrateur",
|
||||
"distributedUptimeStatusPageNotPublic": "Cette page de statut n'est pas publique.",
|
||||
"distributedUptimeStatusPageDeleteDialog": "Voulez-vous supprimer cette page de statut ?",
|
||||
"distributedUptimeStatusPageDeleteConfirm": "Oui, supprimer la page de statut",
|
||||
"distributedUptimeStatusPageDeleteDescription": "Supprimer votre page de statut est irréversible.",
|
||||
"distributedUptimeStatusDevices": "",
|
||||
"distributedUptimeStatusUpt": "",
|
||||
"distributedUptimeStatusUptBurned": "",
|
||||
"distributedUptimeStatusUptLogo": "",
|
||||
"incidentsTableNoIncidents": "",
|
||||
"incidentsTablePaginationLabel": "",
|
||||
"incidentsTablePaginationLabel": "incidents",
|
||||
"incidentsTableMonitorName": "",
|
||||
"incidentsTableStatus": "",
|
||||
"incidentsTableDateTime": "",
|
||||
"incidentsTableDateTime": "Date et heure",
|
||||
"incidentsTableStatusCode": "",
|
||||
"incidentsTableMessage": "",
|
||||
"incidentsTableMessage": "Message",
|
||||
"incidentsOptionsHeader": "",
|
||||
"incidentsOptionsHeaderFilterBy": "",
|
||||
"incidentsOptionsHeaderFilterAll": "",
|
||||
"incidentsOptionsHeaderFilterDown": "",
|
||||
"incidentsOptionsHeaderFilterCannotResolve": "",
|
||||
"incidentsOptionsHeaderShow": "",
|
||||
"incidentsOptionsHeaderLastHour": "",
|
||||
"incidentsOptionsHeaderLastDay": "",
|
||||
"incidentsOptionsHeaderLastWeek": "",
|
||||
"incidentsOptionsHeaderLastHour": "Dernière heure",
|
||||
"incidentsOptionsHeaderLastDay": "Dernier jour",
|
||||
"incidentsOptionsHeaderLastWeek": "Dernière semaine",
|
||||
"incidentsOptionsPlaceholderAllServers": "",
|
||||
"infrastructureCreateYour": "",
|
||||
"infrastructureCreateGeneralSettingsDescription": "",
|
||||
@@ -237,18 +237,18 @@
|
||||
"infrastructureCustomizeAlerts": "",
|
||||
"infrastructureAlertNotificationDescription": "",
|
||||
"infrastructureCreateMonitor": "",
|
||||
"infrastructureProtocol": "",
|
||||
"infrastructureServerUrlLabel": "",
|
||||
"infrastructureProtocol": "Protocole",
|
||||
"infrastructureServerUrlLabel": "URL du serveur",
|
||||
"infrastructureDisplayNameLabel": "",
|
||||
"infrastructureAuthorizationSecretLabel": "",
|
||||
"gb": "",
|
||||
"mb": "",
|
||||
"mem": "",
|
||||
"memoryUsage": "",
|
||||
"cpu": "",
|
||||
"cpu": "CPU",
|
||||
"cpuUsage": "",
|
||||
"cpuTemperature": "",
|
||||
"diskUsage": "",
|
||||
"diskUsage": "Utilisation du disque",
|
||||
"used": "",
|
||||
"total": "",
|
||||
"cores": "",
|
||||
@@ -256,36 +256,36 @@
|
||||
"status": "",
|
||||
"cpuPhysical": "",
|
||||
"cpuLogical": "",
|
||||
"cpuFrequency": "",
|
||||
"avgCpuTemperature": "",
|
||||
"cpuFrequency": "Fréquence CPU",
|
||||
"avgCpuTemperature": "Température moyenne du CPU",
|
||||
"memory": "",
|
||||
"disk": "",
|
||||
"disk": "Disque",
|
||||
"uptime": "",
|
||||
"os": "",
|
||||
"host": "",
|
||||
"actions": "",
|
||||
"integrations": "",
|
||||
"integrationsPrism": "",
|
||||
"integrationsSlack": "",
|
||||
"integrationsSlackInfo": "",
|
||||
"integrationsDiscord": "",
|
||||
"integrationsDiscordInfo": "",
|
||||
"integrationsZapier": "",
|
||||
"integrationsZapierInfo": "",
|
||||
"commonSave": "",
|
||||
"createYour": "",
|
||||
"actions": "Actions",
|
||||
"integrations": "Intégrations",
|
||||
"integrationsPrism": "Connecter Prism à votre service préféré.",
|
||||
"integrationsSlack": "Slack",
|
||||
"integrationsSlackInfo": "Connecter à Slack et voir les incidents dans un canal",
|
||||
"integrationsDiscord": "Discord",
|
||||
"integrationsDiscordInfo": "Connecter à Discord et voir les incidents dans un canal",
|
||||
"integrationsZapier": "Zapier",
|
||||
"integrationsZapierInfo": "Envoyer tous les incidents à Zapier",
|
||||
"commonSave": "Sauvegarder",
|
||||
"createYour": "Créez votre",
|
||||
"createMonitor": "",
|
||||
"pause": "",
|
||||
"resume": "",
|
||||
"editing": "",
|
||||
"url": "",
|
||||
"access": "",
|
||||
"timezone": "",
|
||||
"url": "URL",
|
||||
"access": "Accès",
|
||||
"timezone": "Fuseau horaire",
|
||||
"features": "",
|
||||
"administrator": "",
|
||||
"administrator": "Administrateur ?",
|
||||
"loginHere": "",
|
||||
"displayName": "",
|
||||
"urlMonitor": "",
|
||||
"displayName": "Nom d'affichage",
|
||||
"urlMonitor": "URL à suivre",
|
||||
"portToMonitor": "",
|
||||
"websiteMonitoring": "",
|
||||
"websiteMonitoringDescription": "",
|
||||
@@ -296,7 +296,7 @@
|
||||
"portMonitoring": "",
|
||||
"portMonitoringDescription": "",
|
||||
"createMaintenanceWindow": "",
|
||||
"createMaintenance": "",
|
||||
"createMaintenance": "Créer une maintenance",
|
||||
"editMaintenance": "",
|
||||
"maintenanceWindowName": "",
|
||||
"friendlyNameInput": "",
|
||||
@@ -304,20 +304,20 @@
|
||||
"maintenanceRepeat": "",
|
||||
"maintenance": "",
|
||||
"duration": "",
|
||||
"addMonitors": "",
|
||||
"addMonitors": "Ajouter des moniteurs",
|
||||
"window": "",
|
||||
"cancel": "",
|
||||
"cancel": "Annuler",
|
||||
"message": "",
|
||||
"low": "",
|
||||
"high": "",
|
||||
"statusCode": "",
|
||||
"date&Time": "",
|
||||
"date&Time": "Date et heure",
|
||||
"type": "",
|
||||
"statusPageName": "",
|
||||
"publicURL": "",
|
||||
"repeat": "",
|
||||
"edit": "",
|
||||
"createA": "",
|
||||
"createA": "Créer un",
|
||||
"remove": "",
|
||||
"maintenanceWindowDescription": "",
|
||||
"startTime": "",
|
||||
@@ -327,22 +327,22 @@
|
||||
"notFoundButton": "",
|
||||
"pageSpeedConfigureSettingsDescription": "",
|
||||
"monitorDisplayName": "",
|
||||
"whenNewIncident": "",
|
||||
"whenNewIncident": "Lors d'un nouvel incident,",
|
||||
"notifySMS": "",
|
||||
"notifyEmails": "",
|
||||
"seperateEmails": "",
|
||||
"checkFrequency": "",
|
||||
"checkFrequency": "Vérifier la fréquence",
|
||||
"matchMethod": "",
|
||||
"expectedValue": "",
|
||||
"deleteDialogTitle": "",
|
||||
"deleteDialogDescription": "",
|
||||
"pageSpeedMonitor": "",
|
||||
"shown": "",
|
||||
"ago": "",
|
||||
"ago": "depuis",
|
||||
"companyName": "",
|
||||
"pageSpeedDetailsPerformanceReport": "",
|
||||
"pageSpeedDetailsPerformanceReportCalculator": "",
|
||||
"checkingEvery": "",
|
||||
"checkingEvery": "Vérification tous les",
|
||||
"statusPageCreateSettings": "",
|
||||
"basicInformation": "",
|
||||
"statusPageCreateBasicInfoDescription": "",
|
||||
@@ -362,7 +362,7 @@
|
||||
"statusPageStatusNoPage": "",
|
||||
"statusPageStatusServiceStatus": "",
|
||||
"deleteStatusPage": "",
|
||||
"deleteStatusPageConfirm": "",
|
||||
"deleteStatusPageConfirm": "Oui, supprimer la page de statut",
|
||||
"deleteStatusPageDescription": "",
|
||||
"uptimeCreate": "",
|
||||
"uptimeCreateJsonPath": "",
|
||||
@@ -379,25 +379,25 @@
|
||||
"pageSpeedLearnMoreLink": "",
|
||||
"pageSpeedAddApiKey": "",
|
||||
"update": "",
|
||||
"invalidFileFormat": "",
|
||||
"invalidFileSize": "",
|
||||
"ClickUpload": "",
|
||||
"invalidFileFormat": "Format de fichier non supporté !",
|
||||
"invalidFileSize": "Le fichier est trop volumineux !",
|
||||
"ClickUpload": "Cliquez pour téléverser",
|
||||
"DragandDrop": "",
|
||||
"MaxSize": "",
|
||||
"SupportedFormats": "",
|
||||
"FirstName": "",
|
||||
"LastName": "",
|
||||
"EmailDescriptionText": "",
|
||||
"YourPhoto": "",
|
||||
"YourPhoto": "Photo de profil",
|
||||
"PhotoDescriptionText": "",
|
||||
"save": "",
|
||||
"DeleteDescriptionText": "",
|
||||
"DeleteAccountWarning": "",
|
||||
"DeleteWarningTitle": "",
|
||||
"authRegisterFirstName": "",
|
||||
"authRegisterLastName": "",
|
||||
"authRegisterEmail": "",
|
||||
"authRegisterEmailRequired": "",
|
||||
"DeleteWarningTitle": "Vous supprimez vraiment ce compte ?",
|
||||
"authRegisterFirstName": "Prénom",
|
||||
"authRegisterLastName": "Nom",
|
||||
"authRegisterEmail": "Email",
|
||||
"authRegisterEmailRequired": "Pour continuer, veuillez saisir votre adresse e-mail",
|
||||
"bulkImport": {
|
||||
"title": "",
|
||||
"selectFileTips": "",
|
||||
@@ -405,31 +405,31 @@
|
||||
"selectFile": "",
|
||||
"parsingFailed": "",
|
||||
"uploadSuccess": "",
|
||||
"validationFailed": "",
|
||||
"validationFailed": "Echec de la validation",
|
||||
"noFileSelected": "",
|
||||
"fallbackPage": ""
|
||||
},
|
||||
"welcomeBack": "",
|
||||
"authRegisterLoginLink": "",
|
||||
"validationNameRequired": "",
|
||||
"welcomeBack": "Bienvenue ! Vous êtes connecté avec succès.",
|
||||
"authRegisterLoginLink": "Se connecter",
|
||||
"validationNameRequired": "Veuillez saisir votre nom",
|
||||
"validationNameTooLong": "",
|
||||
"validationNameInvalidCharacters": "",
|
||||
"validationNameInvalidCharacters": "Veuillez utiliser uniquement des lettres, des espaces, des apostrophes ou des traits d'union",
|
||||
"settingsSystemReset": "",
|
||||
"settingsSystemResetDescription": "",
|
||||
"DeleteAccountTitle": "",
|
||||
"DeleteAccountButton": "",
|
||||
"authRegisterEmailInvalid": "",
|
||||
"DeleteAccountTitle": "Supprimer le compte",
|
||||
"DeleteAccountButton": "Supprimer le compte",
|
||||
"authRegisterEmailInvalid": "Veuillez saisir une adresse e-mail valide",
|
||||
"publicLink": "",
|
||||
"doNotHaveAccount": "",
|
||||
"registerHere": "",
|
||||
"backendUnreachable": "",
|
||||
"backendUnreachable": "Erreur de connexion au serveur",
|
||||
"backendUnreachableMessage": "",
|
||||
"backendUnreachableError": "",
|
||||
"backendUnreachableError": "Impossible de se connecter au serveur. Veuillez réessayer plus tard.",
|
||||
"retryConnection": "",
|
||||
"retryingConnection": "",
|
||||
"backendReconnected": "",
|
||||
"backendStillUnreachable": "",
|
||||
"backendConnectionError": "",
|
||||
"backendReconnected": "Reconnexion au serveur réussie.",
|
||||
"backendStillUnreachable": "Le serveur est toujours inaccessible. Veuillez réessayer plus tard.",
|
||||
"backendConnectionError": "Erreur de connexion au serveur. Veuillez vérifier votre connexion réseau.",
|
||||
"maskedPageSpeedKeyPlaceholder": "",
|
||||
"pageSpeedApiKeyFieldTitle": "",
|
||||
"pageSpeedApiKeyFieldLabel": "",
|
||||
@@ -439,10 +439,10 @@
|
||||
"ignoreTLSError": "",
|
||||
"tlsErrorIgnored": "",
|
||||
"ignoreTLSErrorDescription": "",
|
||||
"createNew": "",
|
||||
"createNew": "Créer un nouveau",
|
||||
"greeting": {
|
||||
"prepend": "",
|
||||
"append": "",
|
||||
"append": "L'après-midi est votre libre, rendons-le épique !",
|
||||
"overview": ""
|
||||
},
|
||||
"monitorStatus": {
|
||||
@@ -452,37 +452,37 @@
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "",
|
||||
"admin": "",
|
||||
"admin": "Admin",
|
||||
"teamMember": "",
|
||||
"demoUser": ""
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "",
|
||||
"teamMembers": "Membres de l'équipe",
|
||||
"filter": {
|
||||
"all": "",
|
||||
"all": "Tous",
|
||||
"member": ""
|
||||
},
|
||||
"inviteTeamMember": "",
|
||||
"inviteNewTeamMember": "",
|
||||
"inviteDescription": "",
|
||||
"email": "",
|
||||
"email": "Email",
|
||||
"selectRole": "",
|
||||
"inviteLink": "",
|
||||
"cancel": "",
|
||||
"inviteLink": "Lien d'invitation",
|
||||
"cancel": "Annuler",
|
||||
"noMembers": "",
|
||||
"getToken": "",
|
||||
"emailToken": "",
|
||||
"table": {
|
||||
"name": "",
|
||||
"email": "",
|
||||
"email": "Email",
|
||||
"role": "",
|
||||
"created": ""
|
||||
"created": "Créée"
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "",
|
||||
"resumed": "",
|
||||
"active": ""
|
||||
"active": "Actif"
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "",
|
||||
@@ -492,18 +492,18 @@
|
||||
"incidents": "",
|
||||
"statusPages": "",
|
||||
"maintenance": "",
|
||||
"integrations": "",
|
||||
"integrations": "Intégrations",
|
||||
"settings": "",
|
||||
"support": "",
|
||||
"discussions": "",
|
||||
"discussions": "Discussions",
|
||||
"docs": "",
|
||||
"changelog": "",
|
||||
"changelog": "Changelog",
|
||||
"profile": "",
|
||||
"password": "",
|
||||
"team": "",
|
||||
"logOut": ""
|
||||
"logOut": "Déconnexion"
|
||||
},
|
||||
"settingsEmail": "",
|
||||
"settingsEmail": "Email",
|
||||
"settingsEmailDescription": "",
|
||||
"settingsEmailHost": "",
|
||||
"settingsEmailPort": "",
|
||||
@@ -514,25 +514,24 @@
|
||||
"state": "",
|
||||
"statusBreadCrumbsStatusPages": "",
|
||||
"statusBreadCrumbsDetails": "",
|
||||
"authForgotPasswordInstructions": "",
|
||||
"settingsThemeModeLight": "",
|
||||
"settingsThemeModeDark": "",
|
||||
"settingsClearAllStatsDialogCancel": "",
|
||||
"commonSaving": "",
|
||||
"authForgotPasswordInstructions": "Nous vous enverrons des instructions de réinitialisation.",
|
||||
"settingsThemeModeLight": "Clair",
|
||||
"settingsThemeModeDark": "Sombre",
|
||||
"settingsClearAllStatsDialogCancel": "Annuler",
|
||||
"commonSaving": "Sauvegarde...",
|
||||
"navControls": "",
|
||||
"incidentsPageTitle": "",
|
||||
"incidentsPageTitle": "Incidents",
|
||||
"passwordPanel": {
|
||||
"passwordChangedSuccess": "",
|
||||
"passwordInputIncorrect": "",
|
||||
"currentPassword": "",
|
||||
"enterCurrentPassword": "",
|
||||
"newPassword": "",
|
||||
"enterNewPassword": "",
|
||||
"confirmNewPassword": "",
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
"passwordChangedSuccess": "Votre mot de passe a été modifié avec succès.",
|
||||
"passwordInputIncorrect": "La saisie de votre mot de passe est incorrecte.",
|
||||
"currentPassword": "Mot de passe actuel",
|
||||
"enterCurrentPassword": "Entrez votre mot de passe actuel",
|
||||
"newPassword": "Nouveau mot de passe",
|
||||
"enterNewPassword": "Entrez votre nouveau mot de passe",
|
||||
"confirmNewPassword": "Confirmer le nouveau mot de passe",
|
||||
"passwordRequirements": "Le nouveau mot de passe doit contenir au moins 8 caractères et au moins une lettre majuscule, une lettre minuscule, un chiffre et un caractère spécial.",
|
||||
"saving": "Sauvegarde..."
|
||||
},
|
||||
"uptimeCreateSelectURL": "",
|
||||
"settingsEmailConnectionHost": "",
|
||||
"sendTestEmail": "",
|
||||
"emailSent": "",
|
||||
@@ -541,7 +540,7 @@
|
||||
"settingsTestEmailSuccess": "",
|
||||
"settingsTestEmailFailed": "",
|
||||
"settingsTestEmailFailedWithReason": "",
|
||||
"settingsTestEmailUnknownError": "",
|
||||
"settingsTestEmailUnknownError": "Erreur inconnue",
|
||||
"settingsEmailRequiredFields": "",
|
||||
"statusMsg": {
|
||||
"paused": "",
|
||||
@@ -553,5 +552,11 @@
|
||||
"settingsURLDescription": "",
|
||||
"settingsURLSelectTitle": "",
|
||||
"settingsURLEnabled": "",
|
||||
"settingURLDisabled": ""
|
||||
"settingURLDisabled": "",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,12 +532,6 @@
|
||||
"passwordRequirements": "A nova senha deve conter pelo menos 8 caracteres e deve ter pelo menos uma letra maiúscula, uma letra minúscula, um número e um caractere especial.",
|
||||
"saving": "Salvando..."
|
||||
},
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "Insira a URL ou o IP a ser monitorado (ex.: https://example.com/ ou 192.168.1.100) e adicione um nome de exibição claro que será exibido no painel.",
|
||||
"ping": "Insira a URL ou o IP para executar o ping (ex.: https://example.com/ ou 192.168.1.100) e adicione um nome de exibição claro que será exibido no painel.",
|
||||
"docker": "Insira o ID do seu contêiner Docker. Os IDs do Docker devem conter todos os 64 caracteres. Você pode executar o comando docker inspect <short_id> para obter o ID completo do contêiner.",
|
||||
"port": "Insira a URL ou o IP do servidor, o número da porta e um nome de exibição claro que será exibido no painel."
|
||||
},
|
||||
"settingsEmailConnectionHost": "Host de conexão de e-mail - Nome do host a ser usado na saudação HELO/EHLO",
|
||||
"sendTestEmail": "Enviar e-mail de teste",
|
||||
"emailSent": "E-mail enviado com sucesso",
|
||||
@@ -558,5 +552,11 @@
|
||||
"settingsURLDescription": "",
|
||||
"settingsURLSelectTitle": "",
|
||||
"settingsURLEnabled": "",
|
||||
"settingURLDisabled": ""
|
||||
"settingURLDisabled": "",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,12 +532,6 @@
|
||||
"passwordRequirements": "Новый пароль должен содержать не менее 8 символов и содержать как минимум одну заглавную букву, одну строчную букву, одну цифру и один специальный символ.",
|
||||
"saving": "Сохранение..."
|
||||
},
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "Введите URL или IP-адрес для мониторинга (например, https://example.com/ или 192.168.1.100) и добавьте понятное имя, которое будет отображаться на панели.",
|
||||
"ping": "Введите URL или IP-адрес для пинга (например, https://example.com/ или 192.168.1.100) и добавьте понятное имя, которое будет отображаться на панели.",
|
||||
"docker": "Введите идентификатор контейнера Docker. Идентификатор должен содержать все 64 символа. Вы можете выполнить команду docker inspect <short_id>, чтобы получить полный идентификатор контейнера.",
|
||||
"port": "Введите URL или IP-адрес сервера, номер порта и понятное имя, которое будет отображаться на панели."
|
||||
},
|
||||
"settingsEmailConnectionHost": "",
|
||||
"sendTestEmail": "",
|
||||
"emailSent": "",
|
||||
@@ -558,5 +552,11 @@
|
||||
"settingsURLDescription": "",
|
||||
"settingsURLSelectTitle": "",
|
||||
"settingsURLEnabled": "",
|
||||
"settingURLDisabled": ""
|
||||
"settingURLDisabled": "",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,57 +501,62 @@
|
||||
"profile": "",
|
||||
"password": "",
|
||||
"team": "",
|
||||
"logOut": ""
|
||||
"logOut": "Çıkış yap"
|
||||
},
|
||||
"settingsEmail": "",
|
||||
"settingsEmailDescription": "",
|
||||
"settingsEmailHost": "",
|
||||
"settingsEmailPort": "",
|
||||
"settingsEmailAddress": "",
|
||||
"settingsEmailPassword": "",
|
||||
"settingsEmailUser": "",
|
||||
"settingsEmailFieldResetLabel": "",
|
||||
"state": "",
|
||||
"statusBreadCrumbsStatusPages": "",
|
||||
"statusBreadCrumbsDetails": "",
|
||||
"authForgotPasswordInstructions": "",
|
||||
"settingsThemeModeLight": "",
|
||||
"settingsThemeModeDark": "",
|
||||
"settingsClearAllStatsDialogCancel": "",
|
||||
"commonSaving": "",
|
||||
"navControls": "",
|
||||
"incidentsPageTitle": "",
|
||||
"settingsEmailPassword": "E-posta şifresi - Kimlik doğrulama şifresi.",
|
||||
"settingsEmailUser": "E-posta kullanıcısı – Kimlik doğrulama için kullanıcı adı; belirtilirse e-posta adresinin yerine geçer.",
|
||||
"settingsEmailFieldResetLabel": "Şifre ayarlandı. Değiştirmek için \"Sıfırla\"ya tıklayın.",
|
||||
"state": "Durum",
|
||||
"statusBreadCrumbsStatusPages": "Durum sayfası",
|
||||
"statusBreadCrumbsDetails": "Detaylar",
|
||||
"authForgotPasswordInstructions": "Endişelenmeyin, size sıfırlama talimatlarını göndereceğiz.",
|
||||
"settingsThemeModeLight": "Aydınlat",
|
||||
"settingsThemeModeDark": "Karart",
|
||||
"settingsClearAllStatsDialogCancel": "İptal",
|
||||
"commonSaving": "Kaydediliyor.",
|
||||
"navControls": "Kontroller",
|
||||
"incidentsPageTitle": "Olaylar",
|
||||
"passwordPanel": {
|
||||
"passwordChangedSuccess": "",
|
||||
"passwordInputIncorrect": "",
|
||||
"currentPassword": "",
|
||||
"enterCurrentPassword": "",
|
||||
"newPassword": "",
|
||||
"enterNewPassword": "",
|
||||
"confirmNewPassword": "",
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
"passwordChangedSuccess": "Şifreniz başarı ile değiştirildi.",
|
||||
"passwordInputIncorrect": "Şifreniz hatalı",
|
||||
"currentPassword": "Mevcut parola",
|
||||
"enterCurrentPassword": "Mevcut parolanızı girin",
|
||||
"newPassword": "Yeni parola",
|
||||
"enterNewPassword": "Yeni parolanızı girin",
|
||||
"confirmNewPassword": "Yeni şifrenizi onaylayın",
|
||||
"passwordRequirements": "Yeni şifreniz en az 8 karakterden oluşmalı ve en az bir büyük harf, bir küçük harf, bir rakam ve bir özel karakter içermelidir.",
|
||||
"saving": "Kaydediliyor..."
|
||||
},
|
||||
"uptimeCreateSelectURL": "",
|
||||
"settingsEmailConnectionHost": "",
|
||||
"sendTestEmail": "",
|
||||
"emailSent": "",
|
||||
"failedToSendEmail": "",
|
||||
"settingsTestEmail": "",
|
||||
"settingsTestEmailSuccess": "",
|
||||
"settingsTestEmailFailed": "",
|
||||
"settingsTestEmailFailedWithReason": "",
|
||||
"settingsTestEmailUnknownError": "",
|
||||
"settingsEmailRequiredFields": "",
|
||||
"settingsEmailConnectionHost": "E-posta bağlantı ana bilgisayarı - HELO/EHLO selamlamasında kullanılacak ana bilgisayar adı",
|
||||
"sendTestEmail": "Test maili gönder",
|
||||
"emailSent": "E-posta başarı ile gönderildi.",
|
||||
"failedToSendEmail": "E-posta gönderimi başarısız.",
|
||||
"settingsTestEmail": "Test E-postası gönder.",
|
||||
"settingsTestEmailSuccess": "Test E-postası başarı ile gönderildi.",
|
||||
"settingsTestEmailFailed": "Test E-postası gönderimi başarısız.",
|
||||
"settingsTestEmailFailedWithReason": "Test E-postası gönderimi başarısız: {{reason}}",
|
||||
"settingsTestEmailUnknownError": "Bilinmeyen hata.",
|
||||
"settingsEmailRequiredFields": "E-posta sunucusu ve portu gereklidir.",
|
||||
"statusMsg": {
|
||||
"paused": "",
|
||||
"up": "",
|
||||
"down": "",
|
||||
"pending": ""
|
||||
"paused": "İzleme duraklatıldı.",
|
||||
"up": "Siteniz yayında.",
|
||||
"down": "Siteniz kapalı.",
|
||||
"pending": "Sırada.."
|
||||
},
|
||||
"settingsURLTitle": "",
|
||||
"settingsURLDescription": "",
|
||||
"settingsURLSelectTitle": "",
|
||||
"settingsURLEnabled": "",
|
||||
"settingURLDisabled": ""
|
||||
"settingsURLTitle": "Durum Sayfasında IP/URL'yi İzle",
|
||||
"settingsURLDescription": "Genel Durum sayfasında monitörün IP adresini veya URL'sini görüntüleyin. Devre dışı bırakılırsa, hassas bilgileri korumak için yalnızca monitör adı gösterilecektir.",
|
||||
"settingsURLSelectTitle": "Durum sayfasında IP/URL'yi görüntüle",
|
||||
"settingsURLEnabled": "Etkinleştirilmiş",
|
||||
"settingURLDisabled": "Devre dışı bırakılmış",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +532,6 @@
|
||||
"passwordRequirements": "",
|
||||
"saving": ""
|
||||
},
|
||||
"uptimeCreateSelectURL": "",
|
||||
"settingsEmailConnectionHost": "",
|
||||
"sendTestEmail": "",
|
||||
"emailSent": "",
|
||||
@@ -553,5 +552,11 @@
|
||||
"settingsURLDescription": "",
|
||||
"settingsURLSelectTitle": "",
|
||||
"settingsURLEnabled": "",
|
||||
"settingURLDisabled": ""
|
||||
"settingURLDisabled": "",
|
||||
"uptimeGeneralInstructions": {
|
||||
"http": "",
|
||||
"ping": "",
|
||||
"docker": "",
|
||||
"port": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,10 +550,8 @@ class MonitorController {
|
||||
})
|
||||
);
|
||||
|
||||
// Delete the old job(editedMonitor has the same ID as the old monitor)
|
||||
await this.jobQueue.deleteJob(monitorBeforeEdit);
|
||||
// Add the new job back to the queue
|
||||
await this.jobQueue.addJob(editedMonitor._id, editedMonitor);
|
||||
await this.jobQueue.updateJob(editedMonitor);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.monitorEdit,
|
||||
data: editedMonitor,
|
||||
@@ -595,8 +593,8 @@ class MonitorController {
|
||||
{ new: true }
|
||||
);
|
||||
monitor.isActive === true
|
||||
? await this.jobQueue.addJob(monitor._id, monitor)
|
||||
: await this.jobQueue.deleteJob(monitor);
|
||||
? await this.jobQueue.resumeJob(monitor._id, monitor)
|
||||
: await this.jobQueue.pauseJob(monitor);
|
||||
|
||||
return res.success({
|
||||
msg: monitor.isActive
|
||||
|
||||
@@ -46,18 +46,6 @@ class JobQueueController {
|
||||
}
|
||||
};
|
||||
|
||||
obliterateQueue = async (req, res, next) => {
|
||||
try {
|
||||
await this.jobQueue.obliterate();
|
||||
return res.success({
|
||||
msg: this.stringService.queueObliterate,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "obliterateQueue"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
flushQueue = async (req, res, next) => {
|
||||
try {
|
||||
const result = await this.jobQueue.flushQueues();
|
||||
|
||||
@@ -50,6 +50,9 @@ import JobQueue from "./service/JobQueue/JobQueue.js";
|
||||
import JobQueueHelper from "./service/JobQueue/JobQueueHelper.js";
|
||||
import { Queue, Worker } from "bullmq";
|
||||
|
||||
import PulseQueue from "./service/PulseQueue/PulseQueue.js";
|
||||
import PulseQueueHelper from "./service/PulseQueue/PulseQueueHelper.js";
|
||||
|
||||
//Network service and dependencies
|
||||
import NetworkService from "./service/networkService.js";
|
||||
import axios from "axios";
|
||||
@@ -183,25 +186,40 @@ const startApp = async () => {
|
||||
|
||||
const redisService = new RedisService({ Redis: IORedis, logger });
|
||||
|
||||
const jobQueueHelper = new JobQueueHelper({
|
||||
redisService,
|
||||
Queue,
|
||||
Worker,
|
||||
logger,
|
||||
// const jobQueueHelper = new JobQueueHelper({
|
||||
// redisService,
|
||||
// Queue,
|
||||
// Worker,
|
||||
// logger,
|
||||
// db,
|
||||
// networkService,
|
||||
// statusService,
|
||||
// notificationService,
|
||||
// });
|
||||
// const jobQueue = await JobQueue.create({
|
||||
// db,
|
||||
// jobQueueHelper,
|
||||
// logger,
|
||||
// stringService,
|
||||
// });
|
||||
|
||||
const pulseQueueHelper = new PulseQueueHelper({
|
||||
db,
|
||||
logger,
|
||||
networkService,
|
||||
statusService,
|
||||
notificationService,
|
||||
});
|
||||
const jobQueue = await JobQueue.create({
|
||||
const pulseQueue = await PulseQueue.create({
|
||||
appSettings,
|
||||
db,
|
||||
jobQueueHelper,
|
||||
pulseQueueHelper,
|
||||
logger,
|
||||
stringService,
|
||||
});
|
||||
|
||||
// Register services
|
||||
ServiceRegistry.register(JobQueue.SERVICE_NAME, jobQueue);
|
||||
// ServiceRegistry.register(JobQueue.SERVICE_NAME, jobQueue);
|
||||
ServiceRegistry.register(JobQueue.SERVICE_NAME, pulseQueue);
|
||||
ServiceRegistry.register(MongoDB.SERVICE_NAME, db);
|
||||
ServiceRegistry.register(SettingsService.SERVICE_NAME, settingsService);
|
||||
ServiceRegistry.register(EmailService.SERVICE_NAME, emailService);
|
||||
|
||||
98
server/package-lock.json
generated
98
server/package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@pulsecron/pulse": "1.6.8",
|
||||
"axios": "^1.7.2",
|
||||
"bcryptjs": "3.0.2",
|
||||
"bullmq": "5.41.2",
|
||||
@@ -1012,6 +1013,37 @@
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@pulsecron/pulse": {
|
||||
"version": "1.6.8",
|
||||
"resolved": "https://registry.npmjs.org/@pulsecron/pulse/-/pulse-1.6.8.tgz",
|
||||
"integrity": "sha512-fg9sT/pfpZTlUYr/ktDu6om6XEm2zmulcZ8TP4JZa+Dmol7/35mMKzDwT1H5a0AUcxiFIRfB4KOpkJ+BI2fgmg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron-parser": "^4.9.0",
|
||||
"date.js": "~0.3.3",
|
||||
"debug": "~4.3.4",
|
||||
"human-interval": "~2.0.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"mongodb": "^6.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@pulsecron/pulse/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@scarf/scarf": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
|
||||
@@ -2394,6 +2426,30 @@
|
||||
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/date.js": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/date.js/-/date.js-0.3.3.tgz",
|
||||
"integrity": "sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "~3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/date.js/node_modules/debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/date.js/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
@@ -3664,6 +3720,15 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/human-interval": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/human-interval/-/human-interval-2.0.1.tgz",
|
||||
"integrity": "sha512-r4Aotzf+OtKIGQCB3odUowy4GfUDTy3aTWTfLd7ZF2gBCy3XW3v/dJLRefZnOFFnjqs5B1TypvS8WarpBkYUNQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"numbered": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
@@ -4986,6 +5051,27 @@
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/moment-timezone": {
|
||||
"version": "0.5.48",
|
||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz",
|
||||
"integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"moment": "^2.29.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.13.0.tgz",
|
||||
@@ -5385,6 +5471,12 @@
|
||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/numbered": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/numbered/-/numbered-1.1.0.tgz",
|
||||
"integrity": "sha512-pv/ue2Odr7IfYOO0byC1KgBI10wo5YDauLhxY6/saNzAdAs0r1SotGCPzzCLNPL0xtrAwWRialLu23AAu9xO1g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -7099,9 +7191,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tar-fs": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz",
|
||||
"integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==",
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz",
|
||||
"integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chownr": "^1.1.1",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@pulsecron/pulse": "1.6.8",
|
||||
"axios": "^1.7.2",
|
||||
"bcryptjs": "3.0.2",
|
||||
"bullmq": "5.41.2",
|
||||
|
||||
@@ -25,12 +25,6 @@ class QueueRoutes {
|
||||
this.queueController.addJob
|
||||
);
|
||||
|
||||
this.router.post(
|
||||
"/obliterate",
|
||||
isAllowed(["admin", "superadmin"]),
|
||||
this.queueController.obliterateQueue
|
||||
);
|
||||
|
||||
this.router.post(
|
||||
"/flush",
|
||||
isAllowed(["admin", "superadmin"]),
|
||||
|
||||
@@ -51,7 +51,6 @@ class JobQueue {
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.healthCheckInterval = setInterval(async () => {
|
||||
try {
|
||||
const queueHealthChecks = await this.checkQueueHealth();
|
||||
@@ -116,6 +115,14 @@ class JobQueue {
|
||||
});
|
||||
}
|
||||
|
||||
pauseJob = async (monitor) => {
|
||||
this.deleteJob(monitor);
|
||||
};
|
||||
|
||||
resumeJob = async (monitor) => {
|
||||
this.addJob(monitor._id, monitor);
|
||||
};
|
||||
|
||||
async addJob(jobName, monitor) {
|
||||
this.logger.info({
|
||||
message: `Adding job ${monitor?.url ?? "No URL"}`,
|
||||
@@ -181,6 +188,11 @@ class JobQueue {
|
||||
}
|
||||
}
|
||||
|
||||
async updateJob(monitor) {
|
||||
await this.deleteJob(monitor);
|
||||
await this.addJob(monitor._id, monitor);
|
||||
}
|
||||
|
||||
async getJobs() {
|
||||
try {
|
||||
let stats = {};
|
||||
|
||||
@@ -254,7 +254,8 @@ class JobQueueHelper {
|
||||
|
||||
// Get the current status
|
||||
await job.updateProgress(30);
|
||||
const networkResponse = await this.networkService.getStatus(job);
|
||||
const monitor = job.data;
|
||||
const networkResponse = await this.networkService.getStatus(monitor);
|
||||
if (
|
||||
job.data.type === "distributed_http" ||
|
||||
job.data.type === "distributed_test"
|
||||
@@ -271,14 +272,17 @@ class JobQueueHelper {
|
||||
|
||||
// Handle status change
|
||||
await job.updateProgress(60);
|
||||
const { monitor, statusChanged, prevStatus } =
|
||||
await this.statusService.updateStatus(networkResponse);
|
||||
const {
|
||||
monitor: updatedMonitor,
|
||||
statusChanged,
|
||||
prevStatus,
|
||||
} = await this.statusService.updateStatus(networkResponse);
|
||||
// Handle notifications
|
||||
await job.updateProgress(80);
|
||||
this.notificationService
|
||||
.handleNotifications({
|
||||
...networkResponse,
|
||||
monitor,
|
||||
monitor: updatedMonitor,
|
||||
prevStatus,
|
||||
statusChanged,
|
||||
})
|
||||
|
||||
191
server/service/PulseQueue/PulseQueue.js
Normal file
191
server/service/PulseQueue/PulseQueue.js
Normal file
@@ -0,0 +1,191 @@
|
||||
import { Pulse } from "@pulsecron/pulse";
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
const SERVICE_NAME = "PulseQueue";
|
||||
class PulseQueue {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
constructor({ appSettings, db, pulseQueueHelper, logger }) {
|
||||
this.db = db;
|
||||
this.appSettings = appSettings;
|
||||
this.pulseQueueHelper = pulseQueueHelper;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
static async create({ appSettings, db, pulseQueueHelper, logger }) {
|
||||
const instance = new PulseQueue({ appSettings, db, pulseQueueHelper, logger });
|
||||
await instance.init();
|
||||
return instance;
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// Core methods
|
||||
// ****************************************
|
||||
init = async () => {
|
||||
try {
|
||||
const mongoConnectionString =
|
||||
this.appSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db";
|
||||
this.pulse = new Pulse({ db: { address: mongoConnectionString } });
|
||||
await this.pulse.start();
|
||||
this.pulse.define("monitor-job", this.pulseQueueHelper.getMonitorJob(), {});
|
||||
|
||||
const monitors = await this.db.getAllMonitors();
|
||||
for (const monitor of monitors) {
|
||||
await this.addJob(monitor._id, monitor);
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: "Failed to initialize PulseQueue",
|
||||
service: SERVICE_NAME,
|
||||
method: "init",
|
||||
details: error,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
addJob = async (monitorId, monitor) => {
|
||||
this.logger.debug({
|
||||
message: `Adding job ${monitor?.url ?? "No URL"}`,
|
||||
service: SERVICE_NAME,
|
||||
method: "addJob",
|
||||
});
|
||||
const intervalInSeconds = monitor.interval / 1000;
|
||||
const job = this.pulse.create("monitor-job", {
|
||||
monitor,
|
||||
});
|
||||
job.unique({ "data.monitor._id": monitor._id });
|
||||
job.attrs.jobId = monitorId.toString();
|
||||
job.repeatEvery(`${intervalInSeconds} seconds`);
|
||||
await job.save();
|
||||
};
|
||||
|
||||
deleteJob = async (monitor) => {
|
||||
this.logger.debug({
|
||||
message: `Deleting job ${monitor?.url ?? "No URL"}`,
|
||||
service: SERVICE_NAME,
|
||||
method: "deleteJob",
|
||||
});
|
||||
await this.pulse.cancel({
|
||||
"data.monitor._id": monitor._id,
|
||||
});
|
||||
};
|
||||
|
||||
pauseJob = async (monitor) => {
|
||||
const result = await this.pulse.disable({
|
||||
"data.monitor._id": monitor._id,
|
||||
});
|
||||
|
||||
if (result.length < 1) {
|
||||
throw new Error("Failed to pause monitor");
|
||||
}
|
||||
|
||||
this.logger.debug({
|
||||
message: `Paused monitor ${monitor._id}`,
|
||||
service: SERVICE_NAME,
|
||||
method: "pauseJob",
|
||||
});
|
||||
};
|
||||
|
||||
resumeJob = async (monitor) => {
|
||||
const result = await this.pulse.enable({
|
||||
"data.monitor._id": monitor._id,
|
||||
});
|
||||
|
||||
if (result.length < 1) {
|
||||
throw new Error("Failed to resume monitor");
|
||||
}
|
||||
|
||||
this.logger.debug({
|
||||
message: `Resumed monitor ${monitor._id}`,
|
||||
service: SERVICE_NAME,
|
||||
method: "resumeJob",
|
||||
});
|
||||
};
|
||||
|
||||
updateJob = async (monitor) => {
|
||||
const jobs = await this.pulse.jobs({
|
||||
"data.monitor._id": monitor._id,
|
||||
});
|
||||
|
||||
const job = jobs[0];
|
||||
if (!job) {
|
||||
throw new Error("Job not found");
|
||||
}
|
||||
|
||||
const intervalInSeconds = monitor.interval / 1000;
|
||||
job.repeatEvery(`${intervalInSeconds} seconds`);
|
||||
job.attrs.data.monitor = monitor;
|
||||
await job.save();
|
||||
};
|
||||
|
||||
shutdown = async () => {
|
||||
this.logger.info({
|
||||
message: "Shutting down JobQueue",
|
||||
service: SERVICE_NAME,
|
||||
method: "shutdown",
|
||||
});
|
||||
await this.pulse.stop();
|
||||
};
|
||||
|
||||
// ****************************************
|
||||
// Diagnostic methods
|
||||
// ****************************************
|
||||
|
||||
getMetrics = async () => {
|
||||
const jobs = await this.pulse.jobs();
|
||||
const metrics = jobs.reduce(
|
||||
(acc, job) => {
|
||||
acc.jobs++;
|
||||
if (job.attrs.failCount > 0 && job.attrs.failedAt >= job.attrs.lastFinishedAt) {
|
||||
acc.failingJobs++;
|
||||
}
|
||||
if (job.attrs.lockedAt) {
|
||||
acc.activeJobs++;
|
||||
}
|
||||
if (job.attrs.failCount > 0) {
|
||||
acc.jobsWithFailures.push({
|
||||
monitorId: job.attrs.data.monitor._id,
|
||||
monitorUrl: job.attrs.data.monitor.url,
|
||||
failCount: job.attrs.failCount,
|
||||
failReason: job.attrs.failReason,
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ jobs: 0, activeJobs: 0, failingJobs: 0, jobsWithFailures: [] }
|
||||
);
|
||||
return metrics;
|
||||
};
|
||||
|
||||
getJobs = async () => {
|
||||
const jobs = await this.pulse.jobs();
|
||||
return jobs.map((job) => {
|
||||
return {
|
||||
monitorId: job.attrs.data.monitor._id,
|
||||
monitorUrl: job.attrs.data.monitor.url,
|
||||
lockedAt: job.attrs.lockedAt,
|
||||
runCount: job.attrs.runCount || 0,
|
||||
failCount: job.attrs.failCount || 0,
|
||||
failReason: job.attrs.failReason,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
flushQueues = async () => {
|
||||
const cancelRes = await this.pulse.cancel();
|
||||
await this.pulse.stop();
|
||||
const initRes = await this.init();
|
||||
return {
|
||||
flushedJobs: cancelRes,
|
||||
initSuccess: initRes,
|
||||
};
|
||||
};
|
||||
|
||||
obliterate = async () => {
|
||||
await this.flushQueues();
|
||||
};
|
||||
}
|
||||
|
||||
export default PulseQueue;
|
||||
106
server/service/PulseQueue/PulseQueueHelper.js
Normal file
106
server/service/PulseQueue/PulseQueueHelper.js
Normal file
@@ -0,0 +1,106 @@
|
||||
const SERVICE_NAME = "PulseQueueHelper";
|
||||
|
||||
class PulseQueueHelper {
|
||||
constructor({ db, logger, networkService, statusService, notificationService }) {
|
||||
this.db = db;
|
||||
this.logger = logger;
|
||||
this.networkService = networkService;
|
||||
this.statusService = statusService;
|
||||
this.notificationService = notificationService;
|
||||
}
|
||||
|
||||
getMonitorJob = () => {
|
||||
return async (job) => {
|
||||
try {
|
||||
const monitor = job.attrs.data.monitor;
|
||||
const monitorId = job.attrs.data.monitor._id;
|
||||
if (!monitorId) {
|
||||
throw new Error("No monitor id");
|
||||
}
|
||||
|
||||
const maintenanceWindowActive = await this.isInMaintenanceWindow(monitorId);
|
||||
if (maintenanceWindowActive) {
|
||||
this.logger.info({
|
||||
message: `Monitor ${monitorId} is in maintenance window`,
|
||||
service: SERVICE_NAME,
|
||||
method: "getMonitorJob",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const networkResponse = await this.networkService.getStatus(monitor);
|
||||
if (monitor.type === "distributed_http" || monitor.type === "distributed_test") {
|
||||
await job.updateProgress(100);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!networkResponse) {
|
||||
throw new Error("No network response");
|
||||
}
|
||||
|
||||
const {
|
||||
monitor: updatedMonitor,
|
||||
statusChanged,
|
||||
prevStatus,
|
||||
} = await this.statusService.updateStatus(networkResponse);
|
||||
|
||||
this.notificationService
|
||||
.handleNotifications({
|
||||
...networkResponse,
|
||||
monitor: updatedMonitor,
|
||||
prevStatus,
|
||||
statusChanged,
|
||||
})
|
||||
.catch((error) => {
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: SERVICE_NAME,
|
||||
method: "getMonitorJob",
|
||||
details: `Error sending notifications for job ${job.id}: ${error.message}`,
|
||||
stack: error.stack,
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.warn({
|
||||
message: error.message,
|
||||
service: error.service || SERVICE_NAME,
|
||||
method: error.method || "getMonitorJob",
|
||||
stack: error.stack,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
async isInMaintenanceWindow(monitorId) {
|
||||
const maintenanceWindows = await this.db.getMaintenanceWindowsByMonitorId(monitorId);
|
||||
// Check for active maintenance window:
|
||||
const maintenanceWindowIsActive = maintenanceWindows.reduce((acc, window) => {
|
||||
if (window.active) {
|
||||
const start = new Date(window.start);
|
||||
const end = new Date(window.end);
|
||||
const now = new Date();
|
||||
const repeatInterval = window.repeat || 0;
|
||||
|
||||
// If start is < now and end > now, we're in maintenance
|
||||
if (start <= now && end >= now) return true;
|
||||
|
||||
// If maintenance window was set in the past with a repeat,
|
||||
// we need to advance start and end to see if we are in range
|
||||
|
||||
while (start < now && repeatInterval !== 0) {
|
||||
start.setTime(start.getTime() + repeatInterval);
|
||||
end.setTime(end.getTime() + repeatInterval);
|
||||
if (start <= now && end >= now) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return acc;
|
||||
}, false);
|
||||
return maintenanceWindowIsActive;
|
||||
}
|
||||
}
|
||||
|
||||
export default PulseQueueHelper;
|
||||
@@ -125,7 +125,7 @@ class NetworkService {
|
||||
* @property {number} code - The response code (200 if successful, error code otherwise).
|
||||
* @property {string} message - The message indicating the result of the HTTP request.
|
||||
*/
|
||||
async requestHttp(job) {
|
||||
async requestHttp(monitor) {
|
||||
try {
|
||||
const {
|
||||
url,
|
||||
@@ -138,7 +138,7 @@ class NetworkService {
|
||||
jsonPath,
|
||||
matchMethod,
|
||||
expectedValue,
|
||||
} = job.data;
|
||||
} = monitor;
|
||||
const config = {};
|
||||
|
||||
secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
|
||||
@@ -248,10 +248,10 @@ class NetworkService {
|
||||
* @property {number} code - The response code (200 if successful, error code otherwise).
|
||||
* @property {string} message - The message indicating the result of the PageSpeed request.
|
||||
*/
|
||||
async requestPagespeed(job) {
|
||||
async requestPagespeed(monitor) {
|
||||
try {
|
||||
const url = job.data.url;
|
||||
const updatedJob = { ...job };
|
||||
const url = monitor.url;
|
||||
const updatedMonitor = { ...monitor };
|
||||
let pagespeedUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance`;
|
||||
|
||||
const dbSettings = await this.settingsService.getDBSettings();
|
||||
@@ -266,8 +266,8 @@ class NetworkService {
|
||||
});
|
||||
return;
|
||||
}
|
||||
updatedJob.data.url = pagespeedUrl;
|
||||
return await this.requestHttp(updatedJob);
|
||||
updatedMonitor.url = pagespeedUrl;
|
||||
return await this.requestHttp(updatedMonitor);
|
||||
} catch (error) {
|
||||
error.service = this.SERVICE_NAME;
|
||||
error.method = "requestPagespeed";
|
||||
@@ -292,9 +292,9 @@ class NetworkService {
|
||||
* @property {number} code - The response code (200 if successful, error code otherwise).
|
||||
* @property {string} message - The message indicating the result of the hardware status request.
|
||||
*/
|
||||
async requestHardware(job) {
|
||||
async requestHardware(monitor) {
|
||||
try {
|
||||
return await this.requestHttp(job);
|
||||
return await this.requestHttp(monitor);
|
||||
} catch (error) {
|
||||
error.service = this.SERVICE_NAME;
|
||||
error.method = "requestHardware";
|
||||
@@ -318,7 +318,7 @@ class NetworkService {
|
||||
* @property {number} code - The response code (200 if successful, error code otherwise).
|
||||
* @property {string} message - The message indicating the result of the Docker inspection.
|
||||
*/
|
||||
async requestDocker(job) {
|
||||
async requestDocker(monitor) {
|
||||
try {
|
||||
const docker = new this.Docker({
|
||||
socketPath: "/var/run/docker.sock",
|
||||
@@ -326,19 +326,19 @@ class NetworkService {
|
||||
});
|
||||
|
||||
const containers = await docker.listContainers({ all: true });
|
||||
const containerExists = containers.some((c) => c.Id.startsWith(job.data.url));
|
||||
const containerExists = containers.some((c) => c.Id.startsWith(monitor.url));
|
||||
if (!containerExists) {
|
||||
throw new Error(this.stringService.dockerNotFound);
|
||||
}
|
||||
const container = docker.getContainer(job.data.url);
|
||||
const container = docker.getContainer(monitor.url);
|
||||
|
||||
const { response, responseTime, error } = await this.timeRequest(() =>
|
||||
container.inspect()
|
||||
);
|
||||
|
||||
const dockerResponse = {
|
||||
monitorId: job.data._id,
|
||||
type: job.data.type,
|
||||
monitorId: monitor._id,
|
||||
type: monitor.type,
|
||||
responseTime,
|
||||
};
|
||||
|
||||
@@ -360,9 +360,9 @@ class NetworkService {
|
||||
}
|
||||
}
|
||||
|
||||
async requestPort(job) {
|
||||
async requestPort(monitor) {
|
||||
try {
|
||||
const { url, port } = job.data;
|
||||
const { url, port } = monitor;
|
||||
const { response, responseTime, error } = await this.timeRequest(async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const socket = this.net.createConnection(
|
||||
@@ -391,8 +391,8 @@ class NetworkService {
|
||||
});
|
||||
|
||||
const portResponse = {
|
||||
monitorId: job.data._id,
|
||||
type: job.data.type,
|
||||
monitorId: monitor._id,
|
||||
type: monitor.type,
|
||||
responseTime,
|
||||
};
|
||||
|
||||
@@ -414,9 +414,8 @@ class NetworkService {
|
||||
}
|
||||
}
|
||||
|
||||
async requestDistributedHttp(job) {
|
||||
async requestDistributedHttp(monitor) {
|
||||
try {
|
||||
const monitor = job.data;
|
||||
const CALLBACK_URL = process.env.CALLBACK_URL;
|
||||
|
||||
const response = await this.axios.post(
|
||||
@@ -503,23 +502,23 @@ class NetworkService {
|
||||
* @returns {Promise<Object>} The response object from the appropriate request method.
|
||||
* @throws {Error} Throws an error if the job type is unsupported.
|
||||
*/
|
||||
async getStatus(job) {
|
||||
const type = job?.data?.type ?? "unknown";
|
||||
async getStatus(monitor) {
|
||||
const type = monitor.type ?? "unknown";
|
||||
switch (type) {
|
||||
case this.TYPE_PING:
|
||||
return await this.requestPing(job);
|
||||
return await this.requestPing(monitor);
|
||||
case this.TYPE_HTTP:
|
||||
return await this.requestHttp(job);
|
||||
return await this.requestHttp(monitor);
|
||||
case this.TYPE_PAGESPEED:
|
||||
return await this.requestPagespeed(job);
|
||||
return await this.requestPagespeed(monitor);
|
||||
case this.TYPE_HARDWARE:
|
||||
return await this.requestHardware(job);
|
||||
return await this.requestHardware(monitor);
|
||||
case this.TYPE_DOCKER:
|
||||
return await this.requestDocker(job);
|
||||
return await this.requestDocker(monitor);
|
||||
case this.TYPE_PORT:
|
||||
return await this.requestPort(job);
|
||||
return await this.requestPort(monitor);
|
||||
case this.TYPE_DISTRIBUTED_HTTP:
|
||||
return await this.requestDistributedHttp(job);
|
||||
return await this.requestDistributedHttp(monitor);
|
||||
case this.TYPE_DISTRIBUTED_TEST:
|
||||
return;
|
||||
default:
|
||||
|
||||
@@ -228,6 +228,12 @@ class StatusService {
|
||||
|
||||
if (type === "pagespeed") {
|
||||
if (typeof payload === "undefined") {
|
||||
this.logger.warn({
|
||||
message: "Failed to build check",
|
||||
service: this.SERVICE_NAME,
|
||||
method: "buildCheck",
|
||||
details: "empty payload",
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
const categories = payload?.lighthouseResult?.categories ?? {};
|
||||
@@ -287,9 +293,11 @@ class StatusService {
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: this.SERVICE_NAME,
|
||||
method: "insertCheck",
|
||||
details: `Error inserting check for monitor: ${networkResponse?.monitorId}`,
|
||||
service: error.service || this.SERVICE_NAME,
|
||||
method: error.method || "insertCheck",
|
||||
details:
|
||||
error.details ||
|
||||
`Error inserting check for monitor: ${networkResponse?.monitorId}`,
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user