Merge pull request #2537 from bluewave-labs/master
Master -> Demo for demo deploy
This commit is contained in:
39 files changed
+974
-893
No files matched your search
@@ -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"
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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("");
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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() }))}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -24,6 +24,7 @@ i18n.use(initReactI18next).init({
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
returnEmptyString: false,
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
@@ -23,9 +23,7 @@
|
||||
"settingsClearAllStatsDialogConfirm": "",
|
||||
"settingsDemoMonitors": "",
|
||||
"settingsDemoMonitorsDescription": "",
|
||||
"settingsAddDemoMonitors": "",
|
||||
"settingsAddDemoMonitorsButton": "",
|
||||
"settingsRemoveAllMonitors": "",
|
||||
"settingsRemoveAllMonitorsButton": "",
|
||||
"settingsRemoveAllMonitorsDialogTitle": "",
|
||||
"settingsRemoveAllMonitorsDialogConfirm": "",
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
"settingsClearAllStatsDialogConfirm": "",
|
||||
"settingsDemoMonitors": "",
|
||||
"settingsDemoMonitorsDescription": "",
|
||||
"settingsAddDemoMonitors": "",
|
||||
"settingsAddDemoMonitorsButton": "",
|
||||
"settingsRemoveAllMonitors": "",
|
||||
"settingsRemoveAllMonitorsButton": "",
|
||||
"settingsRemoveAllMonitorsDialogTitle": "",
|
||||
"settingsRemoveAllMonitorsDialogConfirm": "",
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
"settingsClearAllStatsDialogConfirm": "",
|
||||
"settingsDemoMonitors": "",
|
||||
"settingsDemoMonitorsDescription": "",
|
||||
"settingsAddDemoMonitors": "",
|
||||
"settingsAddDemoMonitorsButton": "",
|
||||
"settingsRemoveAllMonitors": "",
|
||||
"settingsRemoveAllMonitorsButton": "",
|
||||
"settingsRemoveAllMonitorsDialogTitle": "",
|
||||
"settingsRemoveAllMonitorsDialogConfirm": "",
|
||||
|
||||
+765
-748
File diff suppressed because it is too large.
Load diff
@@ -23,9 +23,7 @@
|
||||
"settingsClearAllStatsDialogConfirm": "",
|
||||
"settingsDemoMonitors": "",
|
||||
"settingsDemoMonitorsDescription": "",
|
||||
"settingsAddDemoMonitors": "",
|
||||
"settingsAddDemoMonitorsButton": "",
|
||||
"settingsRemoveAllMonitors": "",
|
||||
"settingsRemoveAllMonitorsButton": "",
|
||||
"settingsRemoveAllMonitorsDialogTitle": "",
|
||||
"settingsRemoveAllMonitorsDialogConfirm": "",
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
"settingsClearAllStatsDialogConfirm": "",
|
||||
"settingsDemoMonitors": "",
|
||||
"settingsDemoMonitorsDescription": "",
|
||||
"settingsAddDemoMonitors": "",
|
||||
"settingsAddDemoMonitorsButton": "",
|
||||
"settingsRemoveAllMonitors": "",
|
||||
"settingsRemoveAllMonitorsButton": "",
|
||||
"settingsRemoveAllMonitorsDialogTitle": "",
|
||||
"settingsRemoveAllMonitorsDialogConfirm": "",
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
"settingsClearAllStatsDialogConfirm": "Oui, effacer les stats",
|
||||
"settingsDemoMonitors": "",
|
||||
"settingsDemoMonitorsDescription": "",
|
||||
"settingsAddDemoMonitors": "",
|
||||
"settingsAddDemoMonitorsButton": "",
|
||||
"settingsRemoveAllMonitors": "",
|
||||
"settingsRemoveAllMonitorsButton": "",
|
||||
"settingsRemoveAllMonitorsDialogTitle": "",
|
||||
"settingsRemoveAllMonitorsDialogConfirm": "",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
"settingsClearAllStatsDialogConfirm": "Да, очистить всю статистику",
|
||||
"settingsDemoMonitors": "Демо мониторы",
|
||||
"settingsDemoMonitorsDescription": "Здесь вы можете добавлять и удалять демонстрационные мониторы.",
|
||||
"settingsAddDemoMonitors": "Добавьте демонстрационные мониторы",
|
||||
"settingsAddDemoMonitorsButton": "Добавьте демонстрационные мониторы",
|
||||
"settingsRemoveAllMonitors": "Удалить все демонстрационные мониторы",
|
||||
"settingsRemoveAllMonitorsButton": "Удалить все демонстрационные мониторы",
|
||||
"settingsRemoveAllMonitorsDialogTitle": "Хотите удалить все мониторы?",
|
||||
"settingsRemoveAllMonitorsDialogConfirm": "Да, очистить все мониторы",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
"settingsClearAllStatsDialogConfirm": "",
|
||||
"settingsDemoMonitors": "",
|
||||
"settingsDemoMonitorsDescription": "",
|
||||
"settingsAddDemoMonitors": "",
|
||||
"settingsAddDemoMonitorsButton": "",
|
||||
"settingsRemoveAllMonitors": "",
|
||||
"settingsRemoveAllMonitorsButton": "",
|
||||
"settingsRemoveAllMonitorsDialogTitle": "",
|
||||
"settingsRemoveAllMonitorsDialogConfirm": "",
|
||||
|
||||
+23
-8
@@ -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),
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -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,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,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
|
||||
|
||||
Vendored
-12
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user