mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-07 18:29:41 -06:00
Merge pull request #3069 from bluewave-labs/feat/JSON-export
implement JSON export
This commit is contained in:
@@ -538,6 +538,23 @@ const useExportMonitors = () => {
|
||||
return [exportMonitors, isLoading];
|
||||
};
|
||||
|
||||
const useFetchJson = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const fetchJson = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const res = await networkService.fetchJson();
|
||||
createToast({ body: "JSON fetched successfully" });
|
||||
return res?.data?.data ?? [];
|
||||
} catch (error) {
|
||||
createToast({ body: "Failed to create monitor." });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
return [fetchJson, isLoading];
|
||||
};
|
||||
|
||||
export {
|
||||
useFetchMonitorsWithSummary,
|
||||
useFetchMonitorsWithChecks,
|
||||
@@ -557,4 +574,5 @@ export {
|
||||
useCreateBulkMonitors,
|
||||
useExportMonitors,
|
||||
useFetchMonitorGames,
|
||||
useFetchJson,
|
||||
};
|
||||
|
||||
65
client/src/Pages/v1/Settings/SettingsExport.jsx
Normal file
65
client/src/Pages/v1/Settings/SettingsExport.jsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
|
||||
// Utils
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { PropTypes } from "prop-types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Dialog from "@/Components/v1/Dialog/index.jsx";
|
||||
import { useState } from "react";
|
||||
|
||||
const SettingsDemoMonitors = ({ isAdmin, HEADER_SX, handleChange, isLoading }) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
// Local state
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
if (!isAdmin) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h2"
|
||||
>
|
||||
Export monitors to JSON
|
||||
</Typography>
|
||||
<Typography sx={HEADER_SX}>
|
||||
Export your monitors data as a JSON file for backup or transfer.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
loading={isLoading}
|
||||
onClick={() => {
|
||||
const syntheticEvent = {
|
||||
target: {
|
||||
name: "export",
|
||||
},
|
||||
};
|
||||
handleChange(syntheticEvent);
|
||||
}}
|
||||
sx={{ mt: theme.spacing(4) }}
|
||||
>
|
||||
Export Monitors to JSON
|
||||
</Button>
|
||||
</Box>
|
||||
</ConfigBox>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
SettingsDemoMonitors.propTypes = {
|
||||
isAdmin: PropTypes.bool,
|
||||
handleChange: PropTypes.func,
|
||||
HEADER_SX: PropTypes.object,
|
||||
};
|
||||
|
||||
export default SettingsDemoMonitors;
|
||||
@@ -9,6 +9,7 @@ import SettingsDemoMonitors from "./SettingsDemoMonitors.jsx";
|
||||
import SettingsAbout from "./SettingsAbout.jsx";
|
||||
import SettingsEmail from "./SettingsEmail.jsx";
|
||||
import SettingsGlobalThresholds from "./SettingsGlobalThresholds.jsx";
|
||||
import SettingsExport from "./SettingsExport.jsx";
|
||||
import Button from "@mui/material/Button";
|
||||
// Utils
|
||||
import { settingsValidation } from "../../../Validation/validation.js";
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
useAddDemoMonitors,
|
||||
useDeleteAllMonitors,
|
||||
useDeleteMonitorStats,
|
||||
useFetchJson,
|
||||
} from "../../../Hooks/v1/monitorHooks.js";
|
||||
// Constants
|
||||
const BREADCRUMBS = [{ name: `Settings`, path: "/settings" }];
|
||||
@@ -66,6 +68,7 @@ const Settings = () => {
|
||||
});
|
||||
const [deleteAllMonitors, isDeletingMonitors] = useDeleteAllMonitors();
|
||||
const [deleteMonitorStats, isDeletingMonitorStats] = useDeleteMonitorStats();
|
||||
const [fetchJson, isFetchingJson] = useFetchJson();
|
||||
|
||||
// Setup
|
||||
const isAdmin = useIsAdmin();
|
||||
@@ -128,6 +131,27 @@ const Settings = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (name === "export") {
|
||||
const json = await fetchJson();
|
||||
if (!json || json.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = new Blob([JSON.stringify(json, null, 2)], {
|
||||
type: "application/json",
|
||||
});
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = "monitors.json";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate
|
||||
const { error } = settingsValidation.validate(newSettingsData.settings, {
|
||||
abortEarly: false,
|
||||
@@ -224,6 +248,12 @@ const Settings = () => {
|
||||
setEmailPasswordHasBeenReset={setEmailPasswordHasBeenReset}
|
||||
/>
|
||||
|
||||
<SettingsExport
|
||||
isAdmin={isAdmin}
|
||||
HEADER_SX={HEADING_SX}
|
||||
handleChange={handleChange}
|
||||
isLoading={isSettingsLoading || isSaving || isFetchingJson}
|
||||
/>
|
||||
<SettingsAbout />
|
||||
<Stack
|
||||
direction="row"
|
||||
|
||||
@@ -1145,6 +1145,10 @@ class NetworkService {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchJson() {
|
||||
return this.axiosInstance.get("/monitors/export/json");
|
||||
}
|
||||
}
|
||||
|
||||
export default NetworkService;
|
||||
|
||||
@@ -442,6 +442,24 @@ class MonitorController extends BaseController {
|
||||
"exportMonitorsToCSV"
|
||||
);
|
||||
|
||||
exportMonitorsToJSON = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const json = await this.monitorService.exportMonitorsToJSON({ teamId });
|
||||
|
||||
return res.success({
|
||||
msg: "OK",
|
||||
data: json,
|
||||
});
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"exportMonitorsToJSON"
|
||||
);
|
||||
|
||||
getAllGames = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
return res.success({
|
||||
|
||||
@@ -43,6 +43,7 @@ class MonitorRoutes {
|
||||
// Other static routes
|
||||
this.router.post("/demo", isAllowed(["admin", "superadmin"]), this.monitorController.addDemoMonitors);
|
||||
this.router.get("/export", isAllowed(["admin", "superadmin"]), this.monitorController.exportMonitorsToCSV);
|
||||
this.router.get("/export/json", isAllowed(["admin", "superadmin"]), this.monitorController.exportMonitorsToJSON);
|
||||
this.router.post("/bulk", isAllowed(["admin", "superadmin"]), upload.single("csvFile"), this.monitorController.createBulkMonitors);
|
||||
this.router.post("/test-email", isAllowed(["admin", "superadmin"]), this.monitorController.sendTestEmail);
|
||||
this.router.get("/games", this.monitorController.getAllGames);
|
||||
|
||||
@@ -263,6 +263,46 @@ class MonitorService {
|
||||
const csv = this.papaparse.unparse(csvData);
|
||||
return csv;
|
||||
};
|
||||
exportMonitorsToJSON = async ({ teamId }) => {
|
||||
const monitors = await this.db.monitorModule.getMonitorsByTeamId({ teamId });
|
||||
|
||||
if (!monitors || monitors.length === 0) {
|
||||
throw this.errorService.createNotFoundError("No monitors to export");
|
||||
}
|
||||
|
||||
const json = monitors?.filteredMonitors
|
||||
?.map((monitor) => {
|
||||
const initialType = monitor.type;
|
||||
let parsedType;
|
||||
|
||||
if (initialType === "hardware") {
|
||||
parsedType = "infrastructure";
|
||||
} else if (initialType === "http") {
|
||||
if (monitor.url.startsWith("https://")) {
|
||||
parsedType = "https";
|
||||
} else {
|
||||
parsedType = "http";
|
||||
}
|
||||
} else if (initialType === "pagespeed") {
|
||||
parsedType = initialType;
|
||||
} else {
|
||||
// Skip unsupported types
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
name: monitor.name,
|
||||
url: monitor.url,
|
||||
type: parsedType,
|
||||
interval: monitor.interval,
|
||||
n: monitor.statusWindowSize,
|
||||
secret: monitor.secret,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
getAllGames = () => {
|
||||
return this.games;
|
||||
|
||||
Reference in New Issue
Block a user