Merge pull request #2537 from bluewave-labs/master

Master -> Demo for demo deploy
This commit is contained in:
Alexander Holliday authored and GitHub committed 2025-06-25 09:40:06 +08:00
commit bad7a3c7ea
39 files changed
+974 -893

No files matched your search

+4
View File
@@ -16,4 +16,8 @@ module.exports = {
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
"react/no-unescaped-entities": "off",
},
globals: {
__APP_VERSION__: "readonly",
process: "readonly",
},
};
@@ -29,7 +29,7 @@ const CreateMonitorHeader = ({ isAdmin, label, isLoading = true, path, bulkPath
>
{label || t("createNew")}
</Button>
{bulkPath && <MonitorActions isLoading={isLoading} />}
{/* {bulkPath && <MonitorActions isLoading={isLoading} />} */}
</Stack>
);
};
@@ -168,7 +168,7 @@ const TeamPanel = () => {
});
} catch (error) {
createToast({
body: error.message || "Unknown error.",
body: error?.response?.data?.msg || error.message || "Unknown error.",
});
} finally {
setIsSendingInvite(false);
@@ -268,7 +268,7 @@ const TeamPanel = () => {
value={toInvite.email}
onChange={handleChange}
error={errors.email ? true : false}
helperText={errors.email}
helperText={t(errors.email)}
/>
<Select
id="team-member-role"
+3 -3
View File
@@ -16,15 +16,15 @@ const SettingsAbout = () => {
component="h1"
variant="h2"
>
{t("settingsAbout")}
{t("settingsPage.aboutSettings.title")}
</Typography>
</Box>
<Box>
<Typography component="h2">
{t("common.appName")} {2.1}
{t("common.appName")} {__APP_VERSION__}
</Typography>
<Typography sx={{ mt: theme.spacing(2), mb: theme.spacing(6), opacity: 0.6 }}>
{t("settingsDevelopedBy")}
{t("settingsPage.aboutSettings.labelDevelopedBy")}
</Typography>
<Link
level="secondary"
@@ -27,12 +27,13 @@ const SettingsDemoMonitors = ({ isAdmin, HEADER_SX, handleChange, isLoading }) =
component="h1"
variant="h2"
>
{t("settingsDemoMonitors")}
{t("settingsPage.demoMonitorsSettings.title")}
</Typography>
<Typography sx={HEADER_SX}>
{t("settingsPage.demoMonitorsSettings.description")}
</Typography>
<Typography sx={HEADER_SX}>{t("settingsDemoMonitorsDescription")}</Typography>
</Box>
<Box>
<Typography>{t("settingsAddDemoMonitors")}</Typography>
<Button
variant="contained"
color="accent"
@@ -47,7 +48,7 @@ const SettingsDemoMonitors = ({ isAdmin, HEADER_SX, handleChange, isLoading }) =
}}
sx={{ mt: theme.spacing(4) }}
>
{t("settingsAddDemoMonitorsButton")}
{t("settingsPage.demoMonitorsSettings.buttonAddMonitors")}
</Button>
</Box>
</ConfigBox>
@@ -57,14 +58,13 @@ const SettingsDemoMonitors = ({ isAdmin, HEADER_SX, handleChange, isLoading }) =
component="h1"
variant="h2"
>
{t("settingsSystemReset")}
{t("settingsPage.systemResetSettings.title")}
</Typography>
<Typography sx={{ mt: theme.spacing(2) }}>
{t("settingsSystemResetDescription")}
{t("settingsPage.systemResetSettings.description")}
</Typography>
</Box>
<Box>
<Typography>{t("settingsRemoveAllMonitors")}</Typography>
<Button
variant="contained"
color="error"
@@ -72,15 +72,15 @@ const SettingsDemoMonitors = ({ isAdmin, HEADER_SX, handleChange, isLoading }) =
onClick={() => setIsOpen(true)}
sx={{ mt: theme.spacing(4) }}
>
{t("settingsRemoveAllMonitorsButton")}
{t("settingsPage.systemResetSettings.buttonRemoveAllMonitors")}
</Button>
</Box>
<Dialog
open={isOpen}
theme={theme}
title={t("settingsRemoveAllMonitorsDialogTitle")}
title={t("settingsPage.systemResetSettings.dialogTitle")}
onCancel={() => setIsOpen(false)}
confirmationButtonLabel={t("settingsRemoveAllMonitorsDialogConfirm")}
confirmationButtonLabel={t("settingsPage.systemResetSettings.dialogConfirm")}
onConfirm={() => {
const syntheticEvent = {
target: {
+38 -20
View File
@@ -9,7 +9,7 @@ import TextLink from "../../Components/TextLink";
// Utils
import { useTheme } from "@emotion/react";
import { PropTypes } from "prop-types";
import { useState, useEffect } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { PasswordEndAdornment } from "../../Components/Inputs/TextInput/Adornments";
import { useSendTestEmail } from "../../Hooks/useSendTestEmail";
@@ -111,14 +111,16 @@ const SettingsEmail = ({
component="h1"
variant="h2"
>
{t("settingsEmail")}
{t("settingsPage.emailSettings.title")}
</Typography>
<Typography sx={HEADER_SX}>
{t("settingsPage.emailSettings.description")}
</Typography>
<Typography sx={HEADER_SX}>{t("settingsEmailDescription")}</Typography>
</Box>
<Box>
<Stack gap={theme.spacing(10)}>
<Box>
<Typography>{t("settingsEmailHost")}</Typography>
<Typography>{t("settingsPage.emailSettings.labelHost")}</Typography>
<TextInput
name="systemEmailHost"
placeholder="smtp.gmail.com"
@@ -127,7 +129,7 @@ const SettingsEmail = ({
/>
</Box>
<Box>
<Typography>{t("settingsEmailPort")}</Typography>
<Typography>{t("settingsPage.emailSettings.labelPort")}</Typography>
<TextInput
name="systemEmailPort"
placeholder="425"
@@ -137,16 +139,16 @@ const SettingsEmail = ({
/>
</Box>
<Box>
<Typography>{t("settingsEmailUser")}</Typography>
<Typography>{t("settingsPage.emailSettings.labelUser")}</Typography>
<TextInput
name="systemEmailUser"
placeholder="Leave empty if not required"
placeholder={t("settingsPage.emailSettings.placeholderUser")}
value={systemEmailUser}
onChange={handleChange}
/>
</Box>
<Box>
<Typography>{t("settingsEmailAddress")}</Typography>
<Typography>{t("settingsPage.emailSettings.labelAddress")}</Typography>
<TextInput
name="systemEmailAddress"
placeholder="uptime@bluewavelabs.ca"
@@ -156,7 +158,7 @@ const SettingsEmail = ({
</Box>
{(isEmailPasswordSet === false || emailPasswordHasBeenReset === true) && (
<Box>
<Typography>{t("settingsEmailPassword")}</Typography>
<Typography>{t("settingsPage.emailSettings.labelPassword")}</Typography>
<TextInput
name="systemEmailPassword"
type="password"
@@ -170,7 +172,7 @@ const SettingsEmail = ({
{isEmailPasswordSet === true && emailPasswordHasBeenReset === false && (
<Box>
<Typography>{t("settingsEmailFieldResetLabel")}</Typography>
<Typography>{t("settingsPage.emailSettings.labelPasswordSet")}</Typography>
<Button
onClick={() => {
setPassword("");
@@ -189,7 +191,7 @@ const SettingsEmail = ({
</Box>
)}
<Box>
<Typography>{t("settingsEmailTLSServername")}</Typography>
<Typography>{t("settingsPage.emailSettings.labelTLSServername")}</Typography>
<TextInput
name="systemEmailTLSServername"
placeholder="bluewavelabs.ca"
@@ -198,7 +200,7 @@ const SettingsEmail = ({
/>
</Box>
<Box>
<Typography>{t("settingsEmailConnectionHost")}</Typography>
<Typography>{t("settingsPage.emailSettings.labelConnectionHost")}</Typography>
<TextInput
name="systemEmailConnectionHost"
placeholder="bluewavelabs.ca"
@@ -214,12 +216,28 @@ const SettingsEmail = ({
}}
>
{[
["settingsEmailSecure", "systemEmailSecure", systemEmailSecure],
["settingsEmailPool", "systemEmailPool", systemEmailPool],
["settingsEmailIgnoreTLS", "systemEmailIgnoreTLS", systemEmailIgnoreTLS],
["settingsEmailRequireTLS", "systemEmailRequireTLS", systemEmailRequireTLS],
[
"settingsEmailRejectUnauthorized",
"settingsPage.emailSettings.labelSecure",
"systemEmailSecure",
systemEmailSecure,
],
[
"settingsPage.emailSettings.labelPool",
"systemEmailPool",
systemEmailPool,
],
[
"settingsPage.emailSettings.labelIgnoreTLS",
"systemEmailIgnoreTLS",
systemEmailIgnoreTLS,
],
[
"settingsPage.emailSettings.labelRequireTLS",
"systemEmailRequireTLS",
systemEmailRequireTLS,
],
[
"settingsPage.emailSettings.labelRejectUnauthorized",
"systemEmailRejectUnauthorized",
systemEmailRejectUnauthorized,
],
@@ -242,8 +260,8 @@ const SettingsEmail = ({
))}
<TextLink
text={t("settingsEmailTransportLinkDescription")}
linkText={t("settingsEmailTransportLinkText")}
text={t("settingsPage.emailSettings.descriptionTransport")}
linkText={t("settingsPage.emailSettings.linkTransport")}
href="https://nodemailer.com/smtp"
target="_blank"
/>
@@ -293,7 +311,7 @@ const SettingsEmail = ({
loading={isSending}
onClick={handleSendTestEmail}
>
{t("settingsTestEmail", "Send test e-mail")}
{t("settingsPage.emailSettings.buttonSendTestEmail")}
</Button>
)}
</Box>
@@ -8,7 +8,7 @@ import { PasswordEndAdornment } from "../../Components/Inputs/TextInput/Adornmen
// Utils
import { useTheme } from "@emotion/react";
import { PropTypes } from "prop-types";
import { useState, useEffect } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
const SettingsPagespeed = ({
@@ -46,15 +46,17 @@ const SettingsPagespeed = ({
component="h1"
variant="h2"
>
{t("pageSpeedApiKeyFieldTitle")}
{t("settingsPage.pageSpeedSettings.title")}
</Typography>
<Typography sx={HEADING_SX}>
{t("settingsPage.pageSpeedSettings.description")}
</Typography>
<Typography sx={HEADING_SX}>{t("pageSpeedApiKeyFieldDescription")}</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
{(isApiKeySet === false || apiKeyHasBeenReset === true) && (
<TextInput
name="pagespeedApiKey"
label={t("pageSpeedApiKeyFieldLabel")}
label={t("settingsPage.pageSpeedSettings.labelApiKey")}
value={apiKey}
type={"password"}
onChange={handleChange}
@@ -65,7 +67,7 @@ const SettingsPagespeed = ({
{isApiKeySet === true && apiKeyHasBeenReset === false && (
<Box>
<Typography>{t("pageSpeedApiKeyFieldResetLabel")}</Typography>
<Typography>{t("settingsPage.pageSpeedSettings.labelApiKeySet")}</Typography>
<Button
onClick={() => {
setApiKey("");
+13 -9
View File
@@ -29,17 +29,17 @@ const SettingsStats = ({ isAdmin, HEADING_SX, handleChange, settingsData, errors
variant="h2"
sx={HEADING_SX}
>
{t("settingsHistoryAndMonitoring")}
{t("settingsPage.statsSettings.title")}
</Typography>
<Typography sx={{ mt: theme.spacing(2) }}>
{t("settingsHistoryAndMonitoringDescription")}
{t("settingsPage.statsSettings.description")}
</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<TextInput
name="checkTTL"
label={t("settingsTTLLabel")}
optionalLabel={t("settingsTTLOptionalLabel")}
label={t("settingsPage.statsSettings.labelTTL")}
optionalLabel={t("settingsPage.statsSettings.labelTTLOptional")}
value={settingsData?.settings?.checkTTL ?? ""}
onChange={handleChange}
type="number"
@@ -47,24 +47,28 @@ const SettingsStats = ({ isAdmin, HEADING_SX, handleChange, settingsData, errors
helperText={errors.checkTTL}
/>
<Box>
<Typography>{t("settingsClearAllStats")}</Typography>
<Typography>
{t("settingsPage.statsSettings.clearAllStatsDescription")}
</Typography>
<Button
variant="contained"
color="error"
onClick={() => setIsOpen(true)}
sx={{ mt: theme.spacing(4) }}
>
{t("settingsClearAllStatsButton")}
{t("settingsPage.statsSettings.clearAllStatsButton")}
</Button>
</Box>
</Stack>
<Dialog
open={isOpen}
theme={theme}
title={t("settingsClearAllStatsDialogTitle")}
description={t("settingsClearAllStatsDialogDescription")}
title={t("settingsPage.statsSettings.clearAllStatsDialogTitle")}
description={t("settingsPage.statsSettings.clearAllStatsDialogDescription")}
onCancel={() => setIsOpen(false)}
confirmationButtonLabel={t("settingsClearAllStatsDialogConfirm")}
confirmationButtonLabel={t(
"settingsPage.statsSettings.clearAllStatsDialogConfirm"
)}
onConfirm={() => {
const syntheticEvent = {
target: {
@@ -19,16 +19,17 @@ const SettingsTimeZone = ({ HEADING_SX, handleChange, timezone }) => {
component="h1"
variant="h2"
>
{t("settingsGeneralSettings")}
{t("settingsPage.timezoneSettings.title")}
</Typography>
<Typography sx={HEADING_SX}>
<Typography component="span">{t("settingsDisplayTimezone")}</Typography>-{" "}
{t("settingsDisplayTimezoneDescription")}
<Typography component="span">
{t("settingsPage.timezoneSettings.description")}
</Typography>
</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<Select
label={t("settingsDisplayTimezone")}
label={t("settingsPage.timezoneSettings.label")}
name="timezone"
value={timezone}
onChange={handleChange}
+6 -4
View File
@@ -20,14 +20,16 @@ const SettingsUI = ({ HEADING_SX, handleChange, mode, language }) => {
component="h1"
variant="h2"
>
{t("settingsAppearance")}
{t("settingsPage.uiSettings.title")}
</Typography>
<Typography sx={HEADING_SX}>
{t("settingsPage.uiSettings.description")}
</Typography>
<Typography sx={HEADING_SX}>{t("settingsAppearanceDescription")}</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<Select
name="mode"
label={t("settingsThemeMode")}
label={t("settingsPage.uiSettings.labelTheme")}
value={mode}
onChange={handleChange}
items={[
@@ -37,7 +39,7 @@ const SettingsUI = ({ HEADING_SX, handleChange, mode, language }) => {
></Select>
<Select
name="language"
label={t("settingsLanguage")}
label={t("settingsPage.uiSettings.labelLanguage")}
value={language}
onChange={handleChange}
items={languages.map((lang) => ({ _id: lang, name: lang.toUpperCase() }))}
+7 -5
View File
@@ -15,18 +15,20 @@ const SettingsURL = ({ HEADING_SX, handleChange, showURL = false }) => {
return (
<ConfigBox>
<Box>
<Typography component="h1">{t("settingsURLTitle")}</Typography>
<Typography sx={HEADING_SX}>{t("settingsURLDescription")}</Typography>
<Typography component="h1">{t("settingsPage.urlSettings.title")}</Typography>
<Typography sx={HEADING_SX}>
{t("settingsPage.urlSettings.description")}
</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<Select
name="showURL"
label={t("settingsURLSelectTitle")}
label={t("settingsPage.urlSettings.label")}
value={showURL || ""}
onChange={handleChange}
items={[
{ _id: true, name: t("settingsURLEnabled") },
{ _id: false, name: t("settingURLDisabled") },
{ _id: true, name: t("settingsPage.urlSettings.selectEnabled") },
{ _id: false, name: t("settingsPage.urlSettings.selectDisabled") },
]}
></Select>
</Stack>
+2 -2
View File
@@ -157,7 +157,7 @@ const Settings = () => {
return (
<Stack gap={theme.spacing(10)}>
<Breadcrumbs list={BREADCRUMBS} />
<Typography variant="h1">Settings</Typography>
<Typography variant="h1">{t("settingsPage.title")}</Typography>
<SettingsTimeZone
HEADING_SX={HEADING_SX}
handleChange={handleChange}
@@ -242,7 +242,7 @@ const Settings = () => {
sx={{ px: theme.spacing(12), py: theme.spacing(8) }}
onClick={handleSave}
>
{t("settingsSave")}
{t("settingsPage.saveButtonLabel")}
</Button>
</Stack>
</Stack>
+14
View File
@@ -111,6 +111,7 @@ const UptimeDetails = () => {
return (
<Stack gap={theme.spacing(10)}>
<Breadcrumbs list={BREADCRUMBS} />
<MonitorDetailsControlHeader
path={"uptime"}
isAdmin={isAdmin}
@@ -118,6 +119,19 @@ const UptimeDetails = () => {
monitor={monitor}
triggerUpdate={triggerUpdate}
/>
<UptimeStatusBoxes
isLoading={monitorIsLoading}
monitor={monitor}
monitorStats={monitorStats}
certificateExpiry={certificateExpiry}
/>
<MonitorTimeFrameHeader
isLoading={monitorIsLoading}
hasDateRange={true}
dateRange={dateRange}
setDateRange={setDateRange}
/>
<GenericFallback>
<Typography>{t("distributedUptimeDetailsNoMonitorHistory")}</Typography>
</GenericFallback>
+2 -1
View File
@@ -85,6 +85,7 @@ const semanticColors = {
const newColors = {
offWhite: "#FEFEFE",
offBlack: "#131315",
gray0: "#FDFDFD",
gray10: "#F4F4FF",
gray100: "#F3F3F3",
gray200: "#EFEFEF",
@@ -178,7 +179,7 @@ const newSemanticColors = {
},
primaryBackground: {
main: {
light: newColors.gray10,
light: newColors.gray0,
dark: "#000000",
},
},
+5
View File
@@ -36,6 +36,11 @@ const baseTheme = (palette) => ({
color: palette.primary.contrastTextTertiary,
fontWeight: 400,
},
label: {
fontSize: "var(--env-var-font-size-medium)",
color: palette.primary.contrastTextSecondary,
fontWeight: 500,
},
},
/* TODO change to 4 */
spacing: 2,
+1
View File
@@ -24,6 +24,7 @@ i18n.use(initReactI18next).init({
interpolation: {
escapeValue: false,
},
returnEmptyString: false,
});
export default i18n;
-2
View File
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
"settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
"settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
-2
View File
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
"settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
"settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
-2
View File
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
"settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
"settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
+765 -748
View File
File diff suppressed because it is too large. Load diff
-2
View File
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
"settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
"settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
-2
View File
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
"settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
"settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
-2
View File
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "Oui, effacer les stats",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
"settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
"settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
-2
View File
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "Sim, limpar todas as estatísticas",
"settingsDemoMonitors": "Monitores de demonstração",
"settingsDemoMonitorsDescription": "Adicione monitores de genéricos para fins de demonstração.",
"settingsAddDemoMonitors": "Adicionando monitores de demonstração",
"settingsAddDemoMonitorsButton": "Adicionar monitores de demonstração",
"settingsRemoveAllMonitors": "Removendo todos os monitores",
"settingsRemoveAllMonitorsButton": "Remover todos os monitores",
"settingsRemoveAllMonitorsDialogTitle": "Você deseja remover todos os monitores?",
"settingsRemoveAllMonitorsDialogConfirm": "Sim, remova todos os monitores",
-2
View File
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "Да, очистить всю статистику",
"settingsDemoMonitors": "Демо мониторы",
"settingsDemoMonitorsDescription": "Здесь вы можете добавлять и удалять демонстрационные мониторы.",
"settingsAddDemoMonitors": "Добавьте демонстрационные мониторы",
"settingsAddDemoMonitorsButton": "Добавьте демонстрационные мониторы",
"settingsRemoveAllMonitors": "Удалить все демонстрационные мониторы",
"settingsRemoveAllMonitorsButton": "Удалить все демонстрационные мониторы",
"settingsRemoveAllMonitorsDialogTitle": "Хотите удалить все мониторы?",
"settingsRemoveAllMonitorsDialogConfirm": "Да, очистить все мониторы",
-2
View File
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "Evet, tüm istatistikleri temizle",
"settingsDemoMonitors": "Demo monitörler",
"settingsDemoMonitorsDescription": "Burada demo monitörler ekleyebilir ve kaldırabilirsiniz.",
"settingsAddDemoMonitors": "Demo monitörler ekle",
"settingsAddDemoMonitorsButton": "Demo monitörler ekle",
"settingsRemoveAllMonitors": "Tüm monitörleri kaldır",
"settingsRemoveAllMonitorsButton": "Tüm monitörleri kaldır",
"settingsRemoveAllMonitorsDialogTitle": "Tüm monitörleri kaldırmak istiyor musunuz?",
"settingsRemoveAllMonitorsDialogConfirm": "Evet, tüm monitörleri temizle",
-2
View File
@@ -23,9 +23,7 @@
"settingsClearAllStatsDialogConfirm": "",
"settingsDemoMonitors": "",
"settingsDemoMonitorsDescription": "",
"settingsAddDemoMonitors": "",
"settingsAddDemoMonitorsButton": "",
"settingsRemoveAllMonitors": "",
"settingsRemoveAllMonitorsButton": "",
"settingsRemoveAllMonitorsDialogTitle": "",
"settingsRemoveAllMonitorsDialogConfirm": "",
+23 -8
View File
@@ -1,12 +1,27 @@
import { defineConfig } from "vite";
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react";
import svgr from "vite-plugin-svgr";
import { execSync } from "child_process";
// https://vitejs.dev/config/
export default defineConfig({
base: "/",
plugins: [svgr(), react()],
optimizeDeps: {
include: ["@mui/material/Tooltip", "@emotion/styled"],
},
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
let version;
try {
version =
env.VITE_APP_VERSION ||
execSync("git describe --tags --abbrev=0").toString().trim();
} catch (error) {
version = "unknown";
}
return {
base: "/",
plugins: [svgr(), react()],
optimizeDeps: {
include: ["@mui/material/Tooltip", "@emotion/styled"],
},
define: {
__APP_VERSION__: JSON.stringify(version),
},
};
});
+1
View File
@@ -10,4 +10,5 @@ prod/redis/data/*
dist/docker-compose-test.yaml
/dist-mono/mongo/data/*
/dist-mono/redis/data/*
/dist-arm/mongo/data/*
*.env
+1 -10
View File
@@ -1,6 +1,6 @@
services:
server:
image: ghcr.io/bluewave-labs/checkmate:backend-dist-mono-multiarch
image: ghcr.io/bluewave-labs/checkmate-backend-mono-multiarch:latest
restart: always
ports:
- "52345:52345"
@@ -12,16 +12,7 @@ services:
- CLIENT_HOST=http://localhost
- JWT_SECRET=my_secret
depends_on:
- redis
- mongodb
redis:
image: redis:7
container_name: checkmate-redis
ports:
- "6379:6379"
volumes:
- ./redis/data:/data
restart: unless-stopped
mongodb:
image: mongo:4.4.18
container_name: checkmate-mongodb
-12
View File
@@ -12,19 +12,7 @@ services:
- CLIENT_HOST=http://localhost
- JWT_SECRET=my_secret
depends_on:
- redis
- mongodb
redis:
image: ghcr.io/bluewave-labs/checkmate-redis:latest
restart: always
volumes:
- ./redis/data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 5
start_period: 5s
mongodb:
image: ghcr.io/bluewave-labs/checkmate-mongo:latest
restart: always
-12
View File
@@ -16,7 +16,6 @@ services:
ports:
- "52345:52345"
depends_on:
- redis
- mongodb
environment:
- DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db?replicaSet=rs0
@@ -25,17 +24,6 @@ services:
- JWT_SECRET=my_secret
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock:ro
redis:
image: ghcr.io/bluewave-labs/checkmate-redis:latest
restart: always
volumes:
- ./redis/data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 5
start_period: 5s
mongodb:
image: ghcr.io/bluewave-labs/checkmate-mongo:latest
restart: always
+3 -11
View File
@@ -82,7 +82,7 @@ class AuthController {
}
const newUser = await this.db.insertUser({ ...req.body }, req.file);
logger.info({
this.logger.info({
message: this.stringService.authCreateUser,
service: SERVICE_NAME,
details: newUser._id,
@@ -111,7 +111,7 @@ class AuthController {
});
});
} catch (error) {
logger.warn({
this.logger.warn({
message: error.message,
service: SERVICE_NAME,
method: "registerUser",
@@ -412,8 +412,7 @@ class AuthController {
// 1. Find all the monitors associated with the team ID if superadmin
const result = await this.db.getMonitorsByTeamId({
query: {},
params: { teamId: user.teamId.toString() },
teamId: user.teamId.toString(),
});
if (user.role.includes("superadmin")) {
@@ -424,13 +423,6 @@ class AuthController {
await this.jobQueue.deleteJob(monitor);
})
));
// 3. Delete team
await this.db.deleteTeam(user.teamId);
// 4. Delete all other team members
await this.db.deleteAllOtherUsers();
// 5. Delete each monitor
await this.db.deleteMonitorsByUserId(user._id);
}
// 6. Delete the user by id
await this.db.deleteUser(user._id);
+6 -1
View File
@@ -77,11 +77,16 @@ class InviteController {
name: firstname,
link: `${clientHost}/register/${inviteToken.token}`,
});
await this.emailService.sendEmail(
const result = await this.emailService.sendEmail(
req.body.email,
"Welcome to Uptime Monitor",
html
);
if (!result) {
return res.error({
msg: "Failed to send invite e-mail... Please verify your settings.",
});
}
} catch (error) {
logger.warn({
message: error.message,
+22
View File
@@ -1,6 +1,9 @@
import mongoose from "mongoose";
import bcrypt from "bcryptjs";
import logger from "../../utils/logger.js";
import Monitor from "./Monitor.js";
import Team from "./Team.js";
import Notification from "./Notification.js";
const UserSchema = mongoose.Schema(
{
@@ -74,6 +77,25 @@ UserSchema.pre("findOneAndUpdate", function (next) {
next();
});
UserSchema.pre("findOneAndDelete", async function (next) {
try {
const userToDelete = await this.model.findOne(this.getFilter());
if (!userToDelete) return next();
if (userToDelete.role.includes("superadmin")) {
await Team.deleteOne({ _id: userToDelete.teamId });
await Monitor.deleteMany({ userId: userToDelete._id });
await this.model.deleteMany({
teamId: userToDelete.teamId,
_id: { $ne: userToDelete._id },
});
await Notification.deleteMany({ teamId: userToDelete.teamId });
}
next();
} catch (error) {
next(error);
}
});
UserSchema.methods.comparePassword = async function (submittedPassword) {
const res = await bcrypt.compare(submittedPassword, this.password);
return res;
+6 -1
View File
@@ -1,15 +1,20 @@
import logger from "../utils/logger.js";
import ServiceRegistry from "../service/serviceRegistry.js";
import StringService from "../service/stringService.js";
import { ObjectId } from "mongodb";
const SERVICE_NAME = "verifyOwnership";
const verifyOwnership = (Model, paramName) => {
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
return async (req, res, next) => {
const userId = req.user._id;
const documentId = req.params[paramName];
let documentId = req.params[paramName];
try {
if (typeof documentId === "string") {
documentId = ObjectId.createFromHexString(documentId);
}
const doc = await Model.findById(documentId);
//If the document is not found, return a 404 error
if (!doc) {
+11
View File
@@ -137,6 +137,17 @@ class EmailService {
};
this.transporter = this.nodemailer.createTransport(emailConfig);
try {
await this.transporter.verify();
} catch (error) {
this.logger.warn({
message: "Email transporter verification failed",
service: SERVICE_NAME,
method: "verifyTransporter",
});
return false;
}
try {
const info = await this.transporter.sendMail({
to: to,
+12
View File
@@ -436,6 +436,18 @@ class StringService {
get testEmailSubject() {
return this.translationService.getTranslation("testEmailSubject");
}
get verifyOwnerNotFound() {
return this.translationService.getTranslation("verifyOwnerNotFound");
}
get verifyOwnerUnauthorized() {
return this.translationService.getTranslation("verifyOwnerUnauthorized");
}
get dbUserNotFound() {
return this.translationService.getTranslation("dbUserNotFound");
}
}
export default StringService;
+4 -4
View File
@@ -76,7 +76,7 @@ class Logger {
info(config) {
const logEntry = this.buildLogEntry("info", config);
this.cacheLog(logEntry);
this.logger.info(config.message, logEntry);
this.logger.info(logEntry);
}
/**
@@ -90,7 +90,7 @@ class Logger {
warn(config) {
const logEntry = this.buildLogEntry("warn", config);
this.cacheLog(logEntry);
this.logger.warn(config.message, logEntry);
this.logger.warn(logEntry);
}
/**
@@ -104,7 +104,7 @@ class Logger {
error(config) {
const logEntry = this.buildLogEntry("error", config);
this.cacheLog(logEntry);
this.logger.error(config.message, logEntry);
this.logger.error(logEntry);
}
/**
* Logs a debug message.
@@ -117,7 +117,7 @@ class Logger {
debug(config) {
const logEntry = this.buildLogEntry("debug", config);
this.cacheLog(logEntry);
this.logger.debug(config.message, logEntry);
this.logger.debug(logEntry);
}
cacheLog(entry) {