From eb53e56dc7f3e5f7db59d312c4ad4e33f0aa9560 Mon Sep 17 00:00:00 2001 From: Dunemask Date: Mon, 22 Jan 2024 11:04:19 -0700 Subject: [PATCH 01/38] [FEATURE] Very basic server updates --- lib/controllers/lifecycle-controller.js | 32 ++++- lib/database/queries/server-queries.js | 46 ++++++- lib/routes/server-route.js | 4 + src/components/server-options/CpuOption.jsx | 2 +- .../server-options/ExtraPortsOption.jsx | 3 +- src/components/server-options/HostOption.jsx | 3 +- .../server-options/MemoryOption.jsx | 2 +- src/components/server-options/NameOption.jsx | 3 +- .../server-options/ServerTypeOption.jsx | 2 +- .../server-options/VersionOption.jsx | 2 +- src/components/servers/ServerCard.jsx | 1 + src/nav/MCLPages.jsx | 10 +- src/pages/CreateCoreOptions.jsx | 8 +- src/pages/Edit.jsx | 14 ++ src/pages/EditCoreOptions.jsx | 129 ++++++++++++++++++ src/util/queries.js | 8 ++ 16 files changed, 255 insertions(+), 14 deletions(-) create mode 100644 src/pages/Edit.jsx create mode 100644 src/pages/EditCoreOptions.jsx diff --git a/lib/controllers/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js index 57b8385..b259d5d 100644 --- a/lib/controllers/lifecycle-controller.js +++ b/lib/controllers/lifecycle-controller.js @@ -4,8 +4,9 @@ import { createServerEntry, deleteServerEntry, getServerEntry, + modifyServerEntry, } from "../database/queries/server-queries.js"; -import { sendError } from "../util/ExpressClientError.js"; +import ExpressClientError, { sendError } from "../util/ExpressClientError.js"; import { toggleServer } from "../k8s/k8s-server-control.js"; const dnsRegex = new RegExp( @@ -18,6 +19,8 @@ function payloadFilter(req, res) { const { name, host, version, serverType, memory, extraPorts } = serverSpec; const { backupHost, backupBucket, backupId, backupKey, backupInterval } = serverSpec; + console.log("GOT VVV"); + console.log(serverSpec); if (!name) return res.status(400).send("Server name is required!"); if (!host) return res.status(400).send("Server host is required!"); if (!dnsRegex.test(host)) return res.status(400).send("Hostname invalid!"); @@ -117,3 +120,30 @@ export async function stopServer(req, res) { .then(() => res.sendStatus(200)) .catch(sendError(res)); } + +export async function getServer(req, res) { + // Ensure spec is safe + const serverSpec = req.body; + try { + checkServerId(serverSpec); + } catch (e) { + return sendError(res)(e); + } + const { id } = serverSpec; + getServerEntry(id).then((s) => res.json(s)); +} + +export async function modifyServer(req, res) { + if (payloadFilter(req, res) !== "filtered") return; + const serverSpec = req.body; + try { + checkServerId(serverSpec); + const serverEntry = await modifyServerEntry(serverSpec); + console.log("NEW ENTRY"); + console.log(serverEntry); + // await createServerResources(serverEntry); + res.sendStatus(200); + } catch (e) { + sendError(res)(e); + } +} diff --git a/lib/database/queries/server-queries.js b/lib/database/queries/server-queries.js index 93e84bd..cce9b55 100644 --- a/lib/database/queries/server-queries.js +++ b/lib/database/queries/server-queries.js @@ -1,5 +1,10 @@ import pg from "../postgres.js"; -import { deleteQuery, insertQuery, selectWhereQuery } from "../pg-query.js"; +import { + deleteQuery, + insertQuery, + selectWhereQuery, + updateWhereAllQuery, +} from "../pg-query.js"; import ExpressClientError from "../../util/ExpressClientError.js"; const table = "servers"; @@ -128,6 +133,45 @@ export async function getServerEntry(serverId) { } } +export async function modifyServerEntry(serverSpec) { + const { + id, + name, + host, + version, + serverType: server_type, + memory, + extraPorts: extra_ports, + backupEnabled: backup_enabled, + backupHost: backup_host, + backupBucket: backup_bucket_path, + backupId: backup_id, + backupKey: backup_key, + backupInterval: backup_interval, + } = serverSpec; + + const q = updateWhereAllQuery( + table, + { + name, + host, + version, + server_type, + memory, + extra_ports, + backup_enabled, + backup_host, + backup_bucket_path, + backup_id, + backup_key, + backup_interval, + }, + { id }, + ); + + return pg.query(q); +} + export async function getServerEntries() { const q = `SELECT * FROM ${table}`; return pg.query(q); diff --git a/lib/routes/server-route.js b/lib/routes/server-route.js index 7361abd..d6eb922 100644 --- a/lib/routes/server-route.js +++ b/lib/routes/server-route.js @@ -4,6 +4,8 @@ import { deleteServer, startServer, stopServer, + getServer, + modifyServer, } from "../controllers/lifecycle-controller.js"; import { serverInstances, @@ -18,4 +20,6 @@ router.post("/start", startServer); router.post("/stop", stopServer); router.get("/list", serverList); router.get("/instances", serverInstances); +router.post("/blueprint", getServer); +router.post("/modify", modifyServer); export default router; diff --git a/src/components/server-options/CpuOption.jsx b/src/components/server-options/CpuOption.jsx index 8eb8a83..ebc812c 100644 --- a/src/components/server-options/CpuOption.jsx +++ b/src/components/server-options/CpuOption.jsx @@ -12,7 +12,7 @@ export default function CpuOption(props) { p !== "25565" && p !== "25575" && p.length < 6; export default function ExtraPortsOption(props) { - const [extraPorts, setExtraPorts] = useState([]); + const { extraPorts: initExtraPorts } = props; + const [extraPorts, setExtraPorts] = useState(initExtraPorts ?? []); const { onChange } = props; function portChange(e, val, optionType, changedValue) { diff --git a/src/components/server-options/HostOption.jsx b/src/components/server-options/HostOption.jsx index 7efe6a9..d03d1db 100644 --- a/src/components/server-options/HostOption.jsx +++ b/src/components/server-options/HostOption.jsx @@ -1,10 +1,11 @@ import TextField from "@mui/material/TextField"; export default function HostOption(props) { - const { onChange } = props; + const { value, onChange } = props; return ( diff --git a/src/nav/MCLPages.jsx b/src/nav/MCLPages.jsx index 56ab52e..c2518c7 100644 --- a/src/nav/MCLPages.jsx +++ b/src/nav/MCLPages.jsx @@ -1,6 +1,7 @@ import Home from "@mcl/pages/Home.jsx"; import Create from "@mcl/pages/Create.jsx"; import Files from "@mcl/pages/Files.jsx"; +import Edit from "@mcl/pages/Edit.jsx"; // Go To https://mui.com/material-ui/material-icons/ for more! import HomeIcon from "@mui/icons-material/Home"; import AddIcon from "@mui/icons-material/Add"; @@ -21,10 +22,17 @@ export default [ visible: true, }, { - name: "Edit", + name: "Files", path: "/mcl/files", icon: , component: , visible: false, }, + { + name: "Edit", + path: "/mcl/edit", + icon: , + component: , + visible: false, + }, ]; diff --git a/src/pages/CreateCoreOptions.jsx b/src/pages/CreateCoreOptions.jsx index afe2f0d..4d68121 100644 --- a/src/pages/CreateCoreOptions.jsx +++ b/src/pages/CreateCoreOptions.jsx @@ -55,8 +55,8 @@ export default function CreateCoreOptions() { async function upsertSpec() { if (validateSpec() !== "validated") return; - createServer(spec) - // .then(() => nav("/")) + createServer() + .then(() => nav("/")) .catch(alert); } @@ -90,8 +90,8 @@ export default function CreateCoreOptions() { sx={{ width: "100%", maxWidth: "600px", margin: "auto" }} > - - + + + + + + + ); +} diff --git a/src/pages/EditCoreOptions.jsx b/src/pages/EditCoreOptions.jsx new file mode 100644 index 0000000..c5fbd64 --- /dev/null +++ b/src/pages/EditCoreOptions.jsx @@ -0,0 +1,129 @@ +import { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import FormControl from "@mui/material/FormControl"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import Switch from "@mui/material/Switch"; +import Typography from "@mui/material/Typography"; +import { useGetServer, useModifyServer } from "@mcl/queries"; + +// Core Options +import NameOption from "@mcl/components/server-options/NameOption.jsx"; +import HostOption from "@mcl/components/server-options/HostOption.jsx"; +import VersionOption from "@mcl/components/server-options/VersionOption.jsx"; +import ServerTypeOption, { + serverTypeOptions, +} from "@mcl/components/server-options/ServerTypeOption.jsx"; +import CpuOption, { + cpuOptions, +} from "@mcl/components/server-options/CpuOption.jsx"; +import MemoryOption, { + memoryOptions, +} from "@mcl/components/server-options/MemoryOption.jsx"; +import ExtraPortsOption from "@mcl/components/server-options/ExtraPortsOption.jsx"; + +import BackupHostOption from "@mcl/components/server-options/BackupHostOption.jsx"; +import BackupBucketOption from "@mcl/components/server-options/BackupBucketOption.jsx"; +import BackupIdOption from "@mcl/components/server-options/BackupIdOption.jsx"; +import BackupKeyOption from "@mcl/components/server-options/BackupKeyOption.jsx"; +import BackupIntervalOption, { + backupIntervalDefault, +} from "@mcl/components/server-options/BackupIntervalOption.jsx"; + +export default function EditCoreOptions(props) { + const { serverId } = props; + const [backupEnabled, setBackupEnabled] = useState(false); + const [spec, setSpec] = useState(); + const modifyServer = useModifyServer(spec); + const { isLoading, data: serverBlueprint } = useGetServer(serverId); + + useEffect(() => setSpec(serverBlueprint), [serverBlueprint]); + + const updateSpec = (attr, val) => { + const s = { ...spec }; + s[attr] = val; + setSpec(s); + }; + + const coreUpdate = (attr) => (e) => updateSpec(attr, e.target.value); + + const upsertSpec = () => { + console.log(spec); + modifyServer(spec); + }; + + function validateSpec() { + console.log("TODO CREATE VALIDATION"); + if (!spec.host) return alertValidationError("Host cannot be blank"); + if (!spec.name) return alertValidationError("Name not included"); + if (!spec.version) return alertValidationError("Version cannot be blank"); + return "validated"; + } + + function alertValidationError(reason) { + alert(`Could not validate spec because: ${reason}`); + } + + if (!spec) return; // TODO: Add loading for spec + return ( + + + + + + + + + + + + } + label="Enable Backups?" + labelPlacement="start" + sx={{ mr: "auto" }} + /> + {backupEnabled && ( + + Backups + + + + + + + )} + + + + + ); +} diff --git a/src/util/queries.js b/src/util/queries.js index 2e02366..c80be01 100644 --- a/src/util/queries.js +++ b/src/util/queries.js @@ -39,6 +39,14 @@ export const useDeleteServer = (serverId) => postJsonApi("/server/delete", { id: serverId }, "server-instances", "DELETE"); export const useCreateServer = (spec) => postJsonApi("/server/create", spec, "server-list"); +export const useModifyServer = (spec) => + postJsonApi("/server/modify", spec, "server-list"); + +export const useGetServer = (serverId) => + useQuery({ + queryKey: [`server-blueprint-${serverId}`], + queryFn: fetchApiPost("/server/blueprint", { id: serverId }), + }); export const getServerFiles = async (serverId, path) => fetchApiCore("/files/list", { id: serverId, path }, "POST", true); From ea54ee6239889b89078b728c6e0e166031d0a8f8 Mon Sep 17 00:00:00 2001 From: Dunemask Date: Tue, 23 Jan 2024 13:02:16 -0700 Subject: [PATCH 02/38] [FEATURE] Initial Mutability Features --- lib/controllers/lifecycle-controller.js | 51 +++++++++++-------- lib/database/queries/server-queries.js | 2 +- .../server-options/BackupBucketOption.jsx | 2 +- .../server-options/BackupHostOption.jsx | 3 +- .../server-options/BackupIdOption.jsx | 3 +- src/pages/EditCoreOptions.jsx | 43 +++++++++------- 6 files changed, 61 insertions(+), 43 deletions(-) diff --git a/lib/controllers/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js index b259d5d..cab471c 100644 --- a/lib/controllers/lifecycle-controller.js +++ b/lib/controllers/lifecycle-controller.js @@ -13,28 +13,10 @@ const dnsRegex = new RegExp( `^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`, ); -function payloadFilter(req, res) { +function backupPayloadFilter(req, res) { const serverSpec = req.body; - if (!serverSpec) return res.sendStatus(400); - const { name, host, version, serverType, memory, extraPorts } = serverSpec; const { backupHost, backupBucket, backupId, backupKey, backupInterval } = serverSpec; - console.log("GOT VVV"); - console.log(serverSpec); - if (!name) return res.status(400).send("Server name is required!"); - if (!host) return res.status(400).send("Server host is required!"); - if (!dnsRegex.test(host)) return res.status(400).send("Hostname invalid!"); - if (!version) return res.status(400).send("Server version is required!"); - if (!serverType) return res.status(400).send("Server type is required!"); - if (!memory) return res.status(400).send("Memory is required!"); - if ( - !!extraPorts && - (!Array.isArray(extraPorts) || - extraPorts.find((e) => typeof e !== "string" || e.length > 5)) - ) - return res - .status(400) - .send("Extra ports must be a list of strings with length of 5!"); // TODO: Impliment non creation time backups if ( !!backupHost || @@ -60,6 +42,27 @@ function payloadFilter(req, res) { return "filtered"; } +function payloadFilter(req, res) { + const serverSpec = req.body; + if (!serverSpec) return res.sendStatus(400); + const { name, host, version, serverType, memory, extraPorts } = serverSpec; + if (!name) return res.status(400).send("Server name is required!"); + if (!host) return res.status(400).send("Server host is required!"); + if (!dnsRegex.test(host)) return res.status(400).send("Hostname invalid!"); + if (!version) return res.status(400).send("Server version is required!"); + if (!serverType) return res.status(400).send("Server type is required!"); + if (!memory) return res.status(400).send("Memory is required!"); + if ( + !!extraPorts && + (!Array.isArray(extraPorts) || + extraPorts.find((e) => typeof e !== "string" || e.length > 5)) + ) + return res + .status(400) + .send("Extra ports must be a list of strings with length of 5!"); + return "filtered"; +} + function checkServerId(serverSpec) { if (!serverSpec) throw new ExpressClientError({ c: 400 }); if (!serverSpec.id) @@ -68,6 +71,7 @@ function checkServerId(serverSpec) { export async function createServer(req, res) { if (payloadFilter(req, res) !== "filtered") return; + if (backupPayloadFilter(req, res) !== "filtered") return; const serverSpec = req.body; try { const serverEntry = await createServerEntry(serverSpec); @@ -130,7 +134,12 @@ export async function getServer(req, res) { return sendError(res)(e); } const { id } = serverSpec; - getServerEntry(id).then((s) => res.json(s)); + getServerEntry(id).then((s) => { + delete s.backupKey; + s.backupBucket = s.backupPath; + delete s.backupPath; + res.json(s); + }); } export async function modifyServer(req, res) { @@ -139,8 +148,6 @@ export async function modifyServer(req, res) { try { checkServerId(serverSpec); const serverEntry = await modifyServerEntry(serverSpec); - console.log("NEW ENTRY"); - console.log(serverEntry); // await createServerResources(serverEntry); res.sendStatus(200); } catch (e) { diff --git a/lib/database/queries/server-queries.js b/lib/database/queries/server-queries.js index cce9b55..66300bc 100644 --- a/lib/database/queries/server-queries.js +++ b/lib/database/queries/server-queries.js @@ -35,7 +35,7 @@ export async function createServerEntry(serverSpec) { server_type, memory, extra_ports, - backup_enabled: !!backup_interval, // We already verified the payload, so any backup key will work + backup_enabled: !!backup_interval ? true : null, // We already verified the payload, so any backup key will work backup_host, backup_bucket_path, backup_id, diff --git a/src/components/server-options/BackupBucketOption.jsx b/src/components/server-options/BackupBucketOption.jsx index bc7139d..7650187 100644 --- a/src/components/server-options/BackupBucketOption.jsx +++ b/src/components/server-options/BackupBucketOption.jsx @@ -6,7 +6,7 @@ export default function BackupBucketOption(props) { setSpec(serverBlueprint), [serverBlueprint]); + useEffect(() => { + setSpec(serverBlueprint); + console.log("PUTTING BLUEPRINT:"); + console.log(serverBlueprint); + }, [serverBlueprint]); const updateSpec = (attr, val) => { const s = { ...spec }; @@ -49,10 +52,12 @@ export default function EditCoreOptions(props) { const coreUpdate = (attr) => (e) => updateSpec(attr, e.target.value); const upsertSpec = () => { - console.log(spec); modifyServer(spec); }; + const toggleBackupEnabled = () => + updateSpec("backupEnabled", !spec.backupEnabled); + function validateSpec() { console.log("TODO CREATE VALIDATION"); if (!spec.host) return alertValidationError("Host cannot be blank"); @@ -83,18 +88,22 @@ export default function EditCoreOptions(props) { - - } - label="Enable Backups?" - labelPlacement="start" - sx={{ mr: "auto" }} - /> - {backupEnabled && ( + {spec.backupEnabled !== null && ( + + } + label="Enable Backups?" + labelPlacement="start" + sx={{ mr: "auto" }} + /> + )} + + {/*spec.backupEnabled && ( // TODO: Disabled while secrets are insecure - )} + )*/} From cbadc54d861b438f10afda3debd4a56c96d35cf7 Mon Sep 17 00:00:00 2001 From: Dunemask Date: Tue, 23 Jan 2024 13:03:44 -0700 Subject: [PATCH 03/38] [FIX] Removed S3 Key & ID from returning to an API client --- lib/controllers/lifecycle-controller.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/controllers/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js index cab471c..5aa271d 100644 --- a/lib/controllers/lifecycle-controller.js +++ b/lib/controllers/lifecycle-controller.js @@ -135,9 +135,10 @@ export async function getServer(req, res) { } const { id } = serverSpec; getServerEntry(id).then((s) => { - delete s.backupKey; + delete s.backupKey; // Do not let this ever get to an API client s.backupBucket = s.backupPath; delete s.backupPath; + delete s.backupId; // Do not let this ever get to an API client res.json(s); }); } From 246a790bc25d34669fec5ad62a8c484b6428364c Mon Sep 17 00:00:00 2001 From: Dunemask Date: Tue, 23 Jan 2024 13:07:56 -0700 Subject: [PATCH 04/38] [FIX] Removed extra log statements --- src/pages/EditCoreOptions.jsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/pages/EditCoreOptions.jsx b/src/pages/EditCoreOptions.jsx index 68930b3..163a6a1 100644 --- a/src/pages/EditCoreOptions.jsx +++ b/src/pages/EditCoreOptions.jsx @@ -37,11 +37,7 @@ export default function EditCoreOptions(props) { const modifyServer = useModifyServer(spec); const { isLoading, data: serverBlueprint } = useGetServer(serverId); - useEffect(() => { - setSpec(serverBlueprint); - console.log("PUTTING BLUEPRINT:"); - console.log(serverBlueprint); - }, [serverBlueprint]); + useEffect(() => setSpec(serverBlueprint), [serverBlueprint]); const updateSpec = (attr, val) => { const s = { ...spec }; From 3d73f69678a3a984d1de73950ff243f5d0a4c921 Mon Sep 17 00:00:00 2001 From: dunemask Date: Tue, 23 Jan 2024 20:10:00 +0000 Subject: [PATCH 05/38] [FEATURE] Mutable Servers & Edit UI (#8) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/8 --- lib/controllers/lifecycle-controller.js | 74 +++++++--- lib/database/queries/server-queries.js | 48 ++++++- lib/routes/server-route.js | 4 + .../server-options/BackupBucketOption.jsx | 2 +- .../server-options/BackupHostOption.jsx | 3 +- .../server-options/BackupIdOption.jsx | 3 +- src/components/server-options/CpuOption.jsx | 2 +- .../server-options/ExtraPortsOption.jsx | 3 +- src/components/server-options/HostOption.jsx | 3 +- .../server-options/MemoryOption.jsx | 2 +- src/components/server-options/NameOption.jsx | 3 +- .../server-options/ServerTypeOption.jsx | 2 +- .../server-options/VersionOption.jsx | 2 +- src/components/servers/ServerCard.jsx | 1 + src/nav/MCLPages.jsx | 10 +- src/pages/CreateCoreOptions.jsx | 8 +- src/pages/Edit.jsx | 14 ++ src/pages/EditCoreOptions.jsx | 134 ++++++++++++++++++ src/util/queries.js | 8 ++ 19 files changed, 291 insertions(+), 35 deletions(-) create mode 100644 src/pages/Edit.jsx create mode 100644 src/pages/EditCoreOptions.jsx diff --git a/lib/controllers/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js index 57b8385..5aa271d 100644 --- a/lib/controllers/lifecycle-controller.js +++ b/lib/controllers/lifecycle-controller.js @@ -4,34 +4,19 @@ import { createServerEntry, deleteServerEntry, getServerEntry, + modifyServerEntry, } from "../database/queries/server-queries.js"; -import { sendError } from "../util/ExpressClientError.js"; +import ExpressClientError, { sendError } from "../util/ExpressClientError.js"; import { toggleServer } from "../k8s/k8s-server-control.js"; const dnsRegex = new RegExp( `^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`, ); -function payloadFilter(req, res) { +function backupPayloadFilter(req, res) { const serverSpec = req.body; - if (!serverSpec) return res.sendStatus(400); - const { name, host, version, serverType, memory, extraPorts } = serverSpec; const { backupHost, backupBucket, backupId, backupKey, backupInterval } = serverSpec; - if (!name) return res.status(400).send("Server name is required!"); - if (!host) return res.status(400).send("Server host is required!"); - if (!dnsRegex.test(host)) return res.status(400).send("Hostname invalid!"); - if (!version) return res.status(400).send("Server version is required!"); - if (!serverType) return res.status(400).send("Server type is required!"); - if (!memory) return res.status(400).send("Memory is required!"); - if ( - !!extraPorts && - (!Array.isArray(extraPorts) || - extraPorts.find((e) => typeof e !== "string" || e.length > 5)) - ) - return res - .status(400) - .send("Extra ports must be a list of strings with length of 5!"); // TODO: Impliment non creation time backups if ( !!backupHost || @@ -57,6 +42,27 @@ function payloadFilter(req, res) { return "filtered"; } +function payloadFilter(req, res) { + const serverSpec = req.body; + if (!serverSpec) return res.sendStatus(400); + const { name, host, version, serverType, memory, extraPorts } = serverSpec; + if (!name) return res.status(400).send("Server name is required!"); + if (!host) return res.status(400).send("Server host is required!"); + if (!dnsRegex.test(host)) return res.status(400).send("Hostname invalid!"); + if (!version) return res.status(400).send("Server version is required!"); + if (!serverType) return res.status(400).send("Server type is required!"); + if (!memory) return res.status(400).send("Memory is required!"); + if ( + !!extraPorts && + (!Array.isArray(extraPorts) || + extraPorts.find((e) => typeof e !== "string" || e.length > 5)) + ) + return res + .status(400) + .send("Extra ports must be a list of strings with length of 5!"); + return "filtered"; +} + function checkServerId(serverSpec) { if (!serverSpec) throw new ExpressClientError({ c: 400 }); if (!serverSpec.id) @@ -65,6 +71,7 @@ function checkServerId(serverSpec) { export async function createServer(req, res) { if (payloadFilter(req, res) !== "filtered") return; + if (backupPayloadFilter(req, res) !== "filtered") return; const serverSpec = req.body; try { const serverEntry = await createServerEntry(serverSpec); @@ -117,3 +124,34 @@ export async function stopServer(req, res) { .then(() => res.sendStatus(200)) .catch(sendError(res)); } + +export async function getServer(req, res) { + // Ensure spec is safe + const serverSpec = req.body; + try { + checkServerId(serverSpec); + } catch (e) { + return sendError(res)(e); + } + const { id } = serverSpec; + getServerEntry(id).then((s) => { + delete s.backupKey; // Do not let this ever get to an API client + s.backupBucket = s.backupPath; + delete s.backupPath; + delete s.backupId; // Do not let this ever get to an API client + res.json(s); + }); +} + +export async function modifyServer(req, res) { + if (payloadFilter(req, res) !== "filtered") return; + const serverSpec = req.body; + try { + checkServerId(serverSpec); + const serverEntry = await modifyServerEntry(serverSpec); + // await createServerResources(serverEntry); + res.sendStatus(200); + } catch (e) { + sendError(res)(e); + } +} diff --git a/lib/database/queries/server-queries.js b/lib/database/queries/server-queries.js index 93e84bd..66300bc 100644 --- a/lib/database/queries/server-queries.js +++ b/lib/database/queries/server-queries.js @@ -1,5 +1,10 @@ import pg from "../postgres.js"; -import { deleteQuery, insertQuery, selectWhereQuery } from "../pg-query.js"; +import { + deleteQuery, + insertQuery, + selectWhereQuery, + updateWhereAllQuery, +} from "../pg-query.js"; import ExpressClientError from "../../util/ExpressClientError.js"; const table = "servers"; @@ -30,7 +35,7 @@ export async function createServerEntry(serverSpec) { server_type, memory, extra_ports, - backup_enabled: !!backup_interval, // We already verified the payload, so any backup key will work + backup_enabled: !!backup_interval ? true : null, // We already verified the payload, so any backup key will work backup_host, backup_bucket_path, backup_id, @@ -128,6 +133,45 @@ export async function getServerEntry(serverId) { } } +export async function modifyServerEntry(serverSpec) { + const { + id, + name, + host, + version, + serverType: server_type, + memory, + extraPorts: extra_ports, + backupEnabled: backup_enabled, + backupHost: backup_host, + backupBucket: backup_bucket_path, + backupId: backup_id, + backupKey: backup_key, + backupInterval: backup_interval, + } = serverSpec; + + const q = updateWhereAllQuery( + table, + { + name, + host, + version, + server_type, + memory, + extra_ports, + backup_enabled, + backup_host, + backup_bucket_path, + backup_id, + backup_key, + backup_interval, + }, + { id }, + ); + + return pg.query(q); +} + export async function getServerEntries() { const q = `SELECT * FROM ${table}`; return pg.query(q); diff --git a/lib/routes/server-route.js b/lib/routes/server-route.js index 7361abd..d6eb922 100644 --- a/lib/routes/server-route.js +++ b/lib/routes/server-route.js @@ -4,6 +4,8 @@ import { deleteServer, startServer, stopServer, + getServer, + modifyServer, } from "../controllers/lifecycle-controller.js"; import { serverInstances, @@ -18,4 +20,6 @@ router.post("/start", startServer); router.post("/stop", stopServer); router.get("/list", serverList); router.get("/instances", serverInstances); +router.post("/blueprint", getServer); +router.post("/modify", modifyServer); export default router; diff --git a/src/components/server-options/BackupBucketOption.jsx b/src/components/server-options/BackupBucketOption.jsx index bc7139d..7650187 100644 --- a/src/components/server-options/BackupBucketOption.jsx +++ b/src/components/server-options/BackupBucketOption.jsx @@ -6,7 +6,7 @@ export default function BackupBucketOption(props) { p !== "25565" && p !== "25575" && p.length < 6; export default function ExtraPortsOption(props) { - const [extraPorts, setExtraPorts] = useState([]); + const { extraPorts: initExtraPorts } = props; + const [extraPorts, setExtraPorts] = useState(initExtraPorts ?? []); const { onChange } = props; function portChange(e, val, optionType, changedValue) { diff --git a/src/components/server-options/HostOption.jsx b/src/components/server-options/HostOption.jsx index 7efe6a9..d03d1db 100644 --- a/src/components/server-options/HostOption.jsx +++ b/src/components/server-options/HostOption.jsx @@ -1,10 +1,11 @@ import TextField from "@mui/material/TextField"; export default function HostOption(props) { - const { onChange } = props; + const { value, onChange } = props; return ( diff --git a/src/nav/MCLPages.jsx b/src/nav/MCLPages.jsx index 56ab52e..c2518c7 100644 --- a/src/nav/MCLPages.jsx +++ b/src/nav/MCLPages.jsx @@ -1,6 +1,7 @@ import Home from "@mcl/pages/Home.jsx"; import Create from "@mcl/pages/Create.jsx"; import Files from "@mcl/pages/Files.jsx"; +import Edit from "@mcl/pages/Edit.jsx"; // Go To https://mui.com/material-ui/material-icons/ for more! import HomeIcon from "@mui/icons-material/Home"; import AddIcon from "@mui/icons-material/Add"; @@ -21,10 +22,17 @@ export default [ visible: true, }, { - name: "Edit", + name: "Files", path: "/mcl/files", icon: , component: , visible: false, }, + { + name: "Edit", + path: "/mcl/edit", + icon: , + component: , + visible: false, + }, ]; diff --git a/src/pages/CreateCoreOptions.jsx b/src/pages/CreateCoreOptions.jsx index afe2f0d..4d68121 100644 --- a/src/pages/CreateCoreOptions.jsx +++ b/src/pages/CreateCoreOptions.jsx @@ -55,8 +55,8 @@ export default function CreateCoreOptions() { async function upsertSpec() { if (validateSpec() !== "validated") return; - createServer(spec) - // .then(() => nav("/")) + createServer() + .then(() => nav("/")) .catch(alert); } @@ -90,8 +90,8 @@ export default function CreateCoreOptions() { sx={{ width: "100%", maxWidth: "600px", margin: "auto" }} > - - + + + + + + + ); +} diff --git a/src/pages/EditCoreOptions.jsx b/src/pages/EditCoreOptions.jsx new file mode 100644 index 0000000..163a6a1 --- /dev/null +++ b/src/pages/EditCoreOptions.jsx @@ -0,0 +1,134 @@ +import { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import FormControl from "@mui/material/FormControl"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import Switch from "@mui/material/Switch"; +import Typography from "@mui/material/Typography"; +import { useGetServer, useModifyServer } from "@mcl/queries"; + +// Core Options +import NameOption from "@mcl/components/server-options/NameOption.jsx"; +import HostOption from "@mcl/components/server-options/HostOption.jsx"; +import VersionOption from "@mcl/components/server-options/VersionOption.jsx"; +import ServerTypeOption, { + serverTypeOptions, +} from "@mcl/components/server-options/ServerTypeOption.jsx"; +import CpuOption, { + cpuOptions, +} from "@mcl/components/server-options/CpuOption.jsx"; +import MemoryOption, { + memoryOptions, +} from "@mcl/components/server-options/MemoryOption.jsx"; +import ExtraPortsOption from "@mcl/components/server-options/ExtraPortsOption.jsx"; + +import BackupHostOption from "@mcl/components/server-options/BackupHostOption.jsx"; +import BackupBucketOption from "@mcl/components/server-options/BackupBucketOption.jsx"; +import BackupIdOption from "@mcl/components/server-options/BackupIdOption.jsx"; +import BackupKeyOption from "@mcl/components/server-options/BackupKeyOption.jsx"; +import BackupIntervalOption, { + backupIntervalDefault, +} from "@mcl/components/server-options/BackupIntervalOption.jsx"; + +export default function EditCoreOptions(props) { + const { serverId } = props; + const [spec, setSpec] = useState(); + const modifyServer = useModifyServer(spec); + const { isLoading, data: serverBlueprint } = useGetServer(serverId); + + useEffect(() => setSpec(serverBlueprint), [serverBlueprint]); + + const updateSpec = (attr, val) => { + const s = { ...spec }; + s[attr] = val; + setSpec(s); + }; + + const coreUpdate = (attr) => (e) => updateSpec(attr, e.target.value); + + const upsertSpec = () => { + modifyServer(spec); + }; + + const toggleBackupEnabled = () => + updateSpec("backupEnabled", !spec.backupEnabled); + + function validateSpec() { + console.log("TODO CREATE VALIDATION"); + if (!spec.host) return alertValidationError("Host cannot be blank"); + if (!spec.name) return alertValidationError("Name not included"); + if (!spec.version) return alertValidationError("Version cannot be blank"); + return "validated"; + } + + function alertValidationError(reason) { + alert(`Could not validate spec because: ${reason}`); + } + + if (!spec) return; // TODO: Add loading for spec + return ( + + + + + + + + + + + {spec.backupEnabled !== null && ( + + } + label="Enable Backups?" + labelPlacement="start" + sx={{ mr: "auto" }} + /> + )} + + {/*spec.backupEnabled && ( // TODO: Disabled while secrets are insecure + + Backups + + + + + + + )*/} + + + + + ); +} diff --git a/src/util/queries.js b/src/util/queries.js index 2e02366..c80be01 100644 --- a/src/util/queries.js +++ b/src/util/queries.js @@ -39,6 +39,14 @@ export const useDeleteServer = (serverId) => postJsonApi("/server/delete", { id: serverId }, "server-instances", "DELETE"); export const useCreateServer = (spec) => postJsonApi("/server/create", spec, "server-list"); +export const useModifyServer = (spec) => + postJsonApi("/server/modify", spec, "server-list"); + +export const useGetServer = (serverId) => + useQuery({ + queryKey: [`server-blueprint-${serverId}`], + queryFn: fetchApiPost("/server/blueprint", { id: serverId }), + }); export const getServerFiles = async (serverId, path) => fetchApiCore("/files/list", { id: serverId, path }, "POST", true); From 43c440949816f848ec62ea92911d7b856150e30f Mon Sep 17 00:00:00 2001 From: dunemask Date: Wed, 24 Jan 2024 16:39:57 +0000 Subject: [PATCH 06/38] [FEATURE] Storage adjustments and minor tweaks (#9) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/9 --- lib/controllers/lifecycle-controller.js | 12 +++++-- .../migrations/1_create_servers_table.sql | 1 + lib/database/queries/server-queries.js | 17 ++++++++++ lib/k8s/configs/server-deployment.yml | 8 +++-- lib/k8s/server-containers.js | 22 ++++++++++--- lib/k8s/server-create.js | 29 +++++++++++------ .../server-options/BackupHostOption.jsx | 2 +- .../server-options/BackupIdOption.jsx | 2 +- .../server-options/BackupKeyOption.jsx | 3 +- .../server-options/StorageOption.jsx | 26 ++++++++++++++++ src/components/servers/RconSocket.js | 2 +- src/pages/CreateCoreOptions.jsx | 31 +++++++++++-------- src/pages/EditCoreOptions.jsx | 5 ++- 13 files changed, 121 insertions(+), 39 deletions(-) create mode 100644 src/components/server-options/StorageOption.jsx diff --git a/lib/controllers/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js index 5aa271d..ceede86 100644 --- a/lib/controllers/lifecycle-controller.js +++ b/lib/controllers/lifecycle-controller.js @@ -15,8 +15,14 @@ const dnsRegex = new RegExp( function backupPayloadFilter(req, res) { const serverSpec = req.body; - const { backupHost, backupBucket, backupId, backupKey, backupInterval } = - serverSpec; + const { + storage, + backupHost, + backupBucket, + backupId, + backupKey, + backupInterval, + } = serverSpec; // TODO: Impliment non creation time backups if ( !!backupHost || @@ -25,6 +31,8 @@ function backupPayloadFilter(req, res) { !!backupKey || !!backupInterval ) { + if (storage === 0) + return res.status(400).send("Backups cannot be used if storage is zero!"); // If any keys are required, all are required if ( !( diff --git a/lib/database/migrations/1_create_servers_table.sql b/lib/database/migrations/1_create_servers_table.sql index 3ef14ee..33ddc5a 100644 --- a/lib/database/migrations/1_create_servers_table.sql +++ b/lib/database/migrations/1_create_servers_table.sql @@ -7,6 +7,7 @@ CREATE TABLE servers ( server_type varchar(63) DEFAULT 'VANILLA', cpu varchar(63) DEFAULT '500', memory varchar(63) DEFAULT '512', + storage varchar(63) DEFAULT NULL, backup_enabled BOOLEAN DEFAULT FALSE, backup_host varchar(255) DEFAULT NULL, backup_bucket_path varchar(255) DEFAULT NULL, diff --git a/lib/database/queries/server-queries.js b/lib/database/queries/server-queries.js index 66300bc..c98902e 100644 --- a/lib/database/queries/server-queries.js +++ b/lib/database/queries/server-queries.js @@ -20,7 +20,9 @@ export async function createServerEntry(serverSpec) { host, version, serverType: server_type, + cpu, // TODO: Ignored for now by the K8S manifests memory, + storage: storage_val, extraPorts: extra_ports, backupHost: backup_host, backupBucket: backup_bucket_path, @@ -28,12 +30,15 @@ export async function createServerEntry(serverSpec) { backupKey: backup_key, backupInterval: backup_interval, } = serverSpec; + var q = insertQuery(table, { name, host, version, server_type, + cpu, // TODO: Ignored for now by the K8S manifests memory, + storage: !storage_val || storage_val === "0" ? null : storage_val, // 0, undefined, null, or "0" becomes null extra_ports, backup_enabled: !!backup_interval ? true : null, // We already verified the payload, so any backup key will work backup_host, @@ -51,7 +56,9 @@ export async function createServerEntry(serverSpec) { host, version, server_type: serverType, + cpu, // TODO: Ignored for now by the K8S manifests memory, + storage, extra_ports: extraPorts, backup_enabled: backupEnabled, backup_host: backupHost, @@ -68,7 +75,9 @@ export async function createServerEntry(serverSpec) { host, version, serverType, + cpu, // TODO: Ignored for now by the K8S manifests memory, + storage, extraPorts, backupEnabled, backupHost, @@ -102,7 +111,9 @@ export async function getServerEntry(serverId) { host, version, server_type: serverType, + cpu, // TODO: Ignored for now by the K8S manifests memory, + storage, extra_ports: extraPorts, backup_enabled: backupEnabled, backup_host: backupHost, @@ -119,7 +130,9 @@ export async function getServerEntry(serverId) { host, version, serverType, + cpu, // TODO: Ignored for now by the K8S manifests memory, + storage, extraPorts, backupEnabled, backupHost, @@ -140,7 +153,9 @@ export async function modifyServerEntry(serverSpec) { host, version, serverType: server_type, + cpu, // TODO: Ignored for now by the K8S manifests memory, + // storage, // DO NOT INCLUDE THIS KEY, Not all storage providers in kubernetes allow for dynamically resizable PVCs extraPorts: extra_ports, backupEnabled: backup_enabled, backupHost: backup_host, @@ -157,7 +172,9 @@ export async function modifyServerEntry(serverSpec) { host, version, server_type, + cpu, // TODO: Ignored for now by the K8S manifests memory, + // storage, // DO NOT INCLUDE THIS KEY, Not all storage providers in kubernetes allow for dynamically resizable PVCs extra_ports, backup_enabled, backup_host, diff --git a/lib/k8s/configs/server-deployment.yml b/lib/k8s/configs/server-deployment.yml index 275013a..0acd0cf 100644 --- a/lib/k8s/configs/server-deployment.yml +++ b/lib/k8s/configs/server-deployment.yml @@ -30,11 +30,13 @@ spec: # runAsUser: 1000 terminationGracePeriodSeconds: 30 volumes: - - name: datadir - persistentVolumeClaim: - claimName: changeme-pvc-name + - emptyDir: {} + name: datadir - emptyDir: {} name: backupdir + # - name: datadir + # persistentVolumeClaim: + # claimName: changeme-pvc-name # - name: rclone-config # secret: # defaultMode: 420 diff --git a/lib/k8s/server-containers.js b/lib/k8s/server-containers.js index 0192d2a..a969c69 100644 --- a/lib/k8s/server-containers.js +++ b/lib/k8s/server-containers.js @@ -4,7 +4,7 @@ import yaml from "js-yaml"; const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8")); export function getFtpContainer(serverSpec) { - const { mclName } = serverSpec; + const { mclName, storage } = serverSpec; const ftpContainer = loadYaml("lib/k8s/configs/containers/ftp-server.yml"); ftpContainer.name = `mcl-${mclName}-ftp`; const ftpPortList = [ @@ -18,11 +18,12 @@ export function getFtpContainer(serverSpec) { name, protocol: "TCP", })); + if (!storage) delete ftpContainer.volumeMounts; return ftpContainer; } export function getCoreServerContainer(serverSpec) { - const { mclName, version, serverType, memory } = serverSpec; + const { mclName, version, serverType, memory, storage } = serverSpec; const container = loadYaml("lib/k8s/configs/containers/minecraft-server.yml"); // Container Updates container.name = `mcl-${mclName}-server`; @@ -38,12 +39,21 @@ export function getCoreServerContainer(serverSpec) { // RCON const rs = `mcl-${mclName}-rcon-secret`; findEnv("RCON_PASSWORD").valueFrom.secretKeyRef.name = rs; + if (!storage) delete container.volumeMounts; return container; } export function getServerContainer(serverSpec) { - const { difficulty, gamemode, motd, maxPlayers, seed, ops, whitelist } = - serverSpec; + const { + difficulty, + gamemode, + motd, + maxPlayers, + seed, + ops, + whitelist, + storage, + } = serverSpec; const container = getCoreServerContainer(serverSpec); const findEnv = (k) => container.env.find(({ name: n }) => n === k); @@ -57,12 +67,13 @@ export function getServerContainer(serverSpec) { updateEnv("SEED", seed); updateEnv("OPS", ops); updateEnv("WHITELIST", whitelist); */ + if (!storage) delete container.volumeMounts; return container; } export function getBackupContainer(serverSpec) { - const { mclName, backupEnabled, backupPath } = serverSpec; + const { mclName, backupEnabled, backupPath, storage } = serverSpec; const container = loadYaml("lib/k8s/configs/containers/minecraft-backup.yml"); if (!backupEnabled) return; const findEnv = (k) => container.env.find(({ name: n }) => n === k); @@ -73,6 +84,7 @@ export function getBackupContainer(serverSpec) { // RCON const rs = `mcl-${mclName}-rcon-secret`; findEnv("RCON_PASSWORD").valueFrom.secretKeyRef.name = rs; + if (!storage) delete container.volumeMounts; return container; } diff --git a/lib/k8s/server-create.js b/lib/k8s/server-create.js index 7578838..421a244 100644 --- a/lib/k8s/server-create.js +++ b/lib/k8s/server-create.js @@ -86,18 +86,19 @@ function createRconSecret(serverSpec) { } function createServerVolume(serverSpec) { - const { mclName, id } = serverSpec; + const { mclName, id, storage } = serverSpec; + if (!storage) return; const volumeYaml = loadYaml("lib/k8s/configs/server-pvc.yml"); volumeYaml.metadata.labels.service = `mcl-${mclName}-server`; volumeYaml.metadata.name = `mcl-${mclName}-volume`; volumeYaml.metadata.namespace = namespace; volumeYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; - volumeYaml.spec.resources.requests.storage = "5Gi"; // TODO: Changeme + volumeYaml.spec.resources.requests.storage = `${storage}Gi`; return volumeYaml; } function createServerDeploy(serverSpec) { - const { mclName, id, backupEnabled } = serverSpec; + const { mclName, id, backupEnabled, storage } = serverSpec; const deployYaml = loadYaml("lib/k8s/configs/server-deployment.yml"); const { metadata } = deployYaml; const serverContainer = getServerContainer(serverSpec); @@ -119,9 +120,18 @@ function createServerDeploy(serverSpec) { id; // Volumes - deployYaml.spec.template.spec.volumes.find( - ({ name }) => name === "datadir", - ).persistentVolumeClaim.claimName = `mcl-${mclName}-volume`; + if (!!storage) { + const dvi = deployYaml.spec.template.spec.volumes.findIndex( + ({ name }) => name === "datadir", + ); + delete deployYaml.spec.template.spec.volumes[dvi].emptyDir; + deployYaml.spec.template.spec.volumes[dvi] = { + ...deployYaml.spec.template.spec.volumes[dvi], + persistentVolumeClaim: { + claimName: `mcl-${mclName}-volume`, + }, + }; + } // Backups if (backupEnabled) { @@ -194,9 +204,10 @@ export default async function createServerResources(createSpec) { const rconService = createRconService(createSpec); const extraService = createExtraService(createSpec); const serverResources = []; - serverResources.push( - k8sCore.createNamespacedPersistentVolumeClaim(namespace, serverVolume), - ); + if (!!serverVolume) + serverResources.push( + k8sCore.createNamespacedPersistentVolumeClaim(namespace, serverVolume), + ); if (!!extraService) serverResources.push( k8sCore.createNamespacedService(namespace, extraService), diff --git a/src/components/server-options/BackupHostOption.jsx b/src/components/server-options/BackupHostOption.jsx index 8f0798b..5d96002 100644 --- a/src/components/server-options/BackupHostOption.jsx +++ b/src/components/server-options/BackupHostOption.jsx @@ -6,7 +6,7 @@ export default function BackupHostOption(props) { (i + 1) * 0.5); + +export default function StorageOption(props) { + const { value, onChange } = props; + return ( + + No Storage + {storageOptions.map((o, i) => ( + {`${o} GB`} + ))} + + ); +} diff --git a/src/components/servers/RconSocket.js b/src/components/servers/RconSocket.js index 42a1226..f18c0ef 100644 --- a/src/components/servers/RconSocket.js +++ b/src/components/servers/RconSocket.js @@ -22,7 +22,7 @@ export default class RconSocket { onRconError(v) { this.rconLive = false; - console.log("Server sent" + v); + console.log("Server sent: ", v); } onConnect() { diff --git a/src/pages/CreateCoreOptions.jsx b/src/pages/CreateCoreOptions.jsx index 4d68121..ad983ae 100644 --- a/src/pages/CreateCoreOptions.jsx +++ b/src/pages/CreateCoreOptions.jsx @@ -22,6 +22,7 @@ import MemoryOption, { memoryOptions, } from "@mcl/components/server-options/MemoryOption.jsx"; import ExtraPortsOption from "@mcl/components/server-options/ExtraPortsOption.jsx"; +import StorageOption from "@mcl/components/server-options/StorageOption.jsx"; import BackupHostOption from "@mcl/components/server-options/BackupHostOption.jsx"; import BackupBucketOption from "@mcl/components/server-options/BackupBucketOption.jsx"; @@ -36,6 +37,7 @@ const defaultServer = { serverType: serverTypeOptions[0], cpu: cpuOptions[0], memory: memoryOptions[2], // 1.5GB + storage: 0, extraPorts: [], }; @@ -99,20 +101,23 @@ export default function CreateCoreOptions() { /> + - - } - label="Enable Backups?" - labelPlacement="start" - sx={{ mr: "auto" }} - /> - {backupEnabled && ( + {spec.storage !== 0 && ( + + } + label="Enable Backups?" + labelPlacement="start" + sx={{ mr: "auto" }} + /> + )} + {backupEnabled && spec.storage !== 0 && ( setSpec(serverBlueprint), [serverBlueprint]); @@ -47,9 +48,7 @@ export default function EditCoreOptions(props) { const coreUpdate = (attr) => (e) => updateSpec(attr, e.target.value); - const upsertSpec = () => { - modifyServer(spec); - }; + const upsertSpec = () => modifyServer().then(() => nav("/")); const toggleBackupEnabled = () => updateSpec("backupEnabled", !spec.backupEnabled); From 8a70fad76a490c5f477062da140b181d9930e172 Mon Sep 17 00:00:00 2001 From: dunemask Date: Wed, 24 Jan 2024 17:25:43 +0000 Subject: [PATCH 07/38] [FEATURE] Cluster Wide Helm Value Toggle (#10) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/10 --- templates/clusterrole-binding.yaml | 14 ++++++++++++++ templates/clusterrole.yaml | 27 +++++++++++++++++++++++++++ values.yaml | 1 + 3 files changed, 42 insertions(+) create mode 100644 templates/clusterrole-binding.yaml create mode 100644 templates/clusterrole.yaml diff --git a/templates/clusterrole-binding.yaml b/templates/clusterrole-binding.yaml new file mode 100644 index 0000000..0a5e7d4 --- /dev/null +++ b/templates/clusterrole-binding.yaml @@ -0,0 +1,14 @@ +{{- if and (.Values.serviceAccount.create) (.Values.serviceAccount.clusterWide) -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "minecluster.serviceAccountName" . }} +subjects: +- kind: ServiceAccount + name: {{ include "minecluster.serviceAccountName" . }} + namespace: {{ .Values.mcl.deploymentNamespace | default .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ include "minecluster.serviceAccountName" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/templates/clusterrole.yaml b/templates/clusterrole.yaml new file mode 100644 index 0000000..a0fb3e5 --- /dev/null +++ b/templates/clusterrole.yaml @@ -0,0 +1,27 @@ +{{- if and (.Values.serviceAccount.create) (.Values.serviceAccount.clusterWide) -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "minecluster.serviceAccountName" . }} +rules: +- apiGroups: ["apps"] + resources: + - deployments + verbs: ["get", "list", "watch", "create", "patch", "update", "delete"] +- apiGroups: [""] + resources: + - nodes + verbs: ["list"] +- apiGroups: [""] + resources: + - services + - pods + - pods/log + - containers + - persistentvolumeclaims + - secrets + verbs: ["get", "list", "watch", "create", "patch", "update", "delete"] +- apiGroups: ["metrics.k8s.io"] + resources: ["pods"] + verbs: ["list"] +{{- end }} diff --git a/values.yaml b/values.yaml index 2339df8..922fb54 100644 --- a/values.yaml +++ b/values.yaml @@ -15,6 +15,7 @@ nameOverride: "" fullnameOverride: "" serviceAccount: + clusterWide: false # Specifies whether a service account should be created create: true # Annotations to add to the service account From 4390f90b1c0cbd6e278c093274c294af264381ca Mon Sep 17 00:00:00 2001 From: Dunemask Date: Sat, 27 Jan 2024 22:42:32 -0700 Subject: [PATCH 08/38] [CHORE] Adjust repository backup path --- .gitea/workflows/s3-repo-backup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/s3-repo-backup.yml b/.gitea/workflows/s3-repo-backup.yml index 68c426c..77b0702 100644 --- a/.gitea/workflows/s3-repo-backup.yml +++ b/.gitea/workflows/s3-repo-backup.yml @@ -22,7 +22,7 @@ jobs: ACCESS_KEY_ID: ${{ env.S3_BACKUP_KEY_ID }} SECRET_ACCESS_KEY: ${{ env.S3_BACKUP_KEY }} MIRROR_SOURCE: ${{ env.REPO_DIR }} - MIRROR_TARGET: repository-backups/${{ gitea.repository }} + MIRROR_TARGET: backups/gitea-repositories/${{ gitea.repository }} STORAGE_SERVICE_URL: ${{env.S3_BACKUP_ENDPOINT}} with: args: --overwrite --remove From edbfc2348ae462ed6f8b7d4926940d9470087f25 Mon Sep 17 00:00:00 2001 From: dunemask Date: Sun, 4 Feb 2024 04:59:38 +0000 Subject: [PATCH 09/38] [FEATURE] Basic changes for basic testing in cubit (#11) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/11 --- lib/controllers/lifecycle-controller.js | 2 +- lib/k8s/server-status.js | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/controllers/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js index ceede86..47acb41 100644 --- a/lib/controllers/lifecycle-controller.js +++ b/lib/controllers/lifecycle-controller.js @@ -84,7 +84,7 @@ export async function createServer(req, res) { try { const serverEntry = await createServerEntry(serverSpec); await createServerResources(serverEntry); - res.sendStatus(200); + res.json(serverEntry); } catch (e) { sendError(res)(e); } diff --git a/lib/k8s/server-status.js b/lib/k8s/server-status.js index 6317d8e..43f00af 100644 --- a/lib/k8s/server-status.js +++ b/lib/k8s/server-status.js @@ -39,7 +39,7 @@ function getServerStatus(server) { ) !== undefined; const serverAvailable = services.includes(`server`) && deploymentAvailable; const ftpAvailable = services.includes("ftp"); // TODO this needs some handling for container creation - return { serverAvailable, ftpAvailable, services }; + return { serverAvailable, ftpAvailable, services, deploymentAvailable }; } export async function getInstances() { @@ -53,15 +53,18 @@ export async function getInstances() { const serverInstances = serverDeployments.map((s) => { serverId = s.metadata.annotations["minecluster.dunemask.net/id"]; const entry = entries.find((e) => e.id === serverId); - const { ftpAvailable, serverAvailable, services } = getServerStatus(s); + const { ftpAvailable, serverAvailable, services, deploymentAvailable } = + getServerStatus(s); metrics = getServerMetrics(podMetricsRes, serverId, serverAvailable); return { name: !!entry ? entry.name : "Unknown", + host: !!entry ? entry.host : "Unkonwn", id: serverId, metrics, services, serverAvailable, ftpAvailable, + deploymentAvailable, }; }); return serverInstances; From 78c5b7248215bcac15815626bca2ca163a8e3239 Mon Sep 17 00:00:00 2001 From: dunemask Date: Mon, 5 Feb 2024 02:13:32 +0000 Subject: [PATCH 10/38] [FEATURE] Integrated Minecluster with Cairo & Established Gitea workflows (#12) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/12 --- .gitea/workflows/deploy.edge.yml | 28 +++++++++ .gitea/workflows/qa-api-tests.yml | 36 +++++++++++ lib/controllers/file-controller.js | 11 ++++ lib/controllers/lifecycle-controller.js | 18 +++--- lib/controllers/status-controller.js | 6 +- .../sub-controllers/console-controller.js | 5 +- .../migrations/1_create_servers_table.sql | 1 + lib/database/queries/server-queries.js | 23 +++++-- lib/k8s/k8s-config.js | 12 ++++ lib/k8s/k8s-server-control.js | 18 +++++- lib/k8s/server-create.js | 3 +- lib/k8s/server-delete.js | 3 +- lib/k8s/server-status.js | 9 ++- lib/routes/auth-route.js | 16 +++++ lib/routes/files-route.js | 4 +- lib/routes/middlewares/auth-middleware.js | 33 ++++++++++ lib/routes/server-route.js | 5 +- lib/routes/system-route.js | 7 ++- lib/server/router.js | 2 + package-lock.json | 41 ++++++++++++ package.json | 27 ++++---- src/components/files/FilePreview.jsx | 3 +- src/components/files/MineclusterFiles.jsx | 2 + src/components/servers/RconSocket.js | 2 + src/components/servers/RconView.jsx | 8 ++- src/ctx/SettingsContext.jsx | 2 + src/nav/Viewport.jsx | 7 ++- src/pages/Auth.jsx | 63 +++++++++++++++++++ src/util/auth.js | 40 ++++++++++++ src/util/queries.js | 9 ++- 30 files changed, 391 insertions(+), 53 deletions(-) create mode 100644 .gitea/workflows/deploy.edge.yml create mode 100644 .gitea/workflows/qa-api-tests.yml create mode 100644 lib/k8s/k8s-config.js create mode 100644 lib/routes/auth-route.js create mode 100644 lib/routes/middlewares/auth-middleware.js create mode 100644 src/pages/Auth.jsx create mode 100644 src/util/auth.js diff --git a/.gitea/workflows/deploy.edge.yml b/.gitea/workflows/deploy.edge.yml new file mode 100644 index 0000000..bae115f --- /dev/null +++ b/.gitea/workflows/deploy.edge.yml @@ -0,0 +1,28 @@ +name: Deploy Edge +run-name: ${{ gitea.actor }} Deploy Edge +on: + push: + branches: [ master ] + +env: + GITEA_TOKEN: ${{ secrets.ELYSIUM_ORG_READ_TOKEN }} + KUBECONFIG_BASE64: ${{ secrets.KUBECONFIG_USW_EDGE }} + OASIS_PROD_CONFIG: ${{ secrets.OASIS_PROD_CONFIG }} + GARDEN_DEPLOY_ACTION: minecluster + # Additional Deploy Envars + POSTGRES_PROD_PASSWORD: ${{ secrets.POSTGRES_PROD_PASSWORD }} + MCL_KUBECONFIG: ${{ secrets.KUBECONFIG_USW_MC }} + + +jobs: + deploy-edge: + steps: + - name: Oasis Setup + uses: https://gitea.dunemask.dev/elysium/oasis-action@master + with: + gitea-token: ${{ env.GITEA_TOKEN }} + kubeconfig: ${{ env.KUBECONFIG_BASE64 }} + oasis-prod-config: ${{ env. OASIS_PROD_CONFIG }} + - name: Deploy to Edge env + run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-edge + working-directory: ${{ env.OASIS_WORKSPACE }} \ No newline at end of file diff --git a/.gitea/workflows/qa-api-tests.yml b/.gitea/workflows/qa-api-tests.yml new file mode 100644 index 0000000..4014415 --- /dev/null +++ b/.gitea/workflows/qa-api-tests.yml @@ -0,0 +1,36 @@ +name: QA API Tests +run-name: ${{ gitea.actor }} QA API Test +on: + pull_request: + branches: [ master ] + +env: + REPO_DIR: ${{ gitea.workspace }}/minecluster + KUBECONFIG_BASE64: ${{ secrets.KUBECONFIG_USW_DEV }} + GITEA_TOKEN: ${{ secrets.ELYSIUM_ORG_READ_TOKEN }} + GARDEN_LINK_ACTION: build.minecluster-image + +jobs: + qa-api-tests: + steps: + - name: Oasis Setup + uses: https://gitea.dunemask.dev/elysium/oasis-action@master + with: + gitea-token: ${{ env.GITEA_TOKEN }} + kubeconfig: ${{ env.KUBECONFIG_BASE64 }} + # Test Code + - name: Checkout repository + uses: actions/checkout@v3 + with: + path: ${{ env.REPO_DIR }} + # Garden tests + - name: Link Repo code to Garden + run: garden link action $GARDEN_LINK_ACTION $REPO_DIR --env usw-ci --var cubit-projects=cairo,minecluster + working-directory: ${{ env.OASIS_WORKSPACE }} + # Cubit CI Tests + - name: Run Cubit tests in CI env + run: garden workflow qa-api-tests --env usw-ci --var ci-ttl=25 + working-directory: ${{ env.OASIS_WORKSPACE }} + - name: Status Alert + if: always() + run: echo "The Job ended with status ${{ job.status }}." \ No newline at end of file diff --git a/lib/controllers/file-controller.js b/lib/controllers/file-controller.js index 1a92c17..83e1ab5 100644 --- a/lib/controllers/file-controller.js +++ b/lib/controllers/file-controller.js @@ -6,11 +6,14 @@ import { uploadServerItem, } from "../k8s/server-files.js"; import { sendError } from "../util/ExpressClientError.js"; +import { checkAuthorization } from "../database/queries/server-queries.js"; export async function listFiles(req, res) { const serverSpec = req.body; if (!serverSpec) return res.sendStatus(400); if (!serverSpec.id) return res.status(400).send("Server id missing!"); + const authorized = await checkAuthorization(serverSpec.id, req.cairoId); + if (!authorized) return res.sendStatus(403); listServerFiles(serverSpec) .then((f) => { const fileData = f.map((fi, i) => ({ @@ -31,6 +34,8 @@ export async function createFolder(req, res) { if (!serverSpec) return res.sendStatus(400); if (!serverSpec.id) return res.status(400).send("Server id missing!"); if (!serverSpec.path) return res.status(400).send("Path required!"); + const authorized = await checkAuthorization(serverSpec.id, req.cairoId); + if (!authorized) return res.sendStatus(403); createServerFolder(serverSpec) .then(() => res.sendStatus(200)) .catch(sendError(res)); @@ -43,6 +48,8 @@ export async function deleteItem(req, res) { if (!serverSpec.path) return res.status(400).send("Path required!"); if (serverSpec.isDir === undefined || serverSpec.isDir === null) return res.status(400).send("IsDIr required!"); + const authorized = await checkAuthorization(serverSpec.id, req.cairoId); + if (!authorized) return res.sendStatus(403); removeServerItem(serverSpec) .then(() => res.sendStatus(200)) .catch(sendError(res)); @@ -52,6 +59,8 @@ export async function uploadItem(req, res) { const serverSpec = req.body; if (!serverSpec.id) return res.status(400).send("Server id missing!"); if (!serverSpec.path) return res.status(400).send("Path required!"); + const authorized = await checkAuthorization(serverSpec.id, req.cairoId); + if (!authorized) return res.sendStatus(403); uploadServerItem(serverSpec, req.file) .then(() => res.sendStatus(200)) .catch(sendError(res)); @@ -61,6 +70,8 @@ export async function getItem(req, res) { const serverSpec = req.body; if (!serverSpec.id) return res.status(400).send("Server id missing!"); if (!serverSpec.path) return res.status(400).send("Path required!"); + const authorized = await checkAuthorization(serverSpec.id, req.cairoId); + if (!authorized) return res.sendStatus(403); getServerItem(serverSpec, res) .then(({ ds, ftpTransfer }) => { ds.pipe(res).on("error", sendError(res)); diff --git a/lib/controllers/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js index 47acb41..d5b9e3a 100644 --- a/lib/controllers/lifecycle-controller.js +++ b/lib/controllers/lifecycle-controller.js @@ -8,6 +8,7 @@ import { } from "../database/queries/server-queries.js"; import ExpressClientError, { sendError } from "../util/ExpressClientError.js"; import { toggleServer } from "../k8s/k8s-server-control.js"; +import { checkAuthorization } from "../database/queries/server-queries.js"; const dnsRegex = new RegExp( `^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`, @@ -71,10 +72,13 @@ function payloadFilter(req, res) { return "filtered"; } -function checkServerId(serverSpec) { +async function checkServerId(cairoId, serverSpec) { if (!serverSpec) throw new ExpressClientError({ c: 400 }); if (!serverSpec.id) throw new ExpressClientError({ c: 400, m: "Server id missing!" }); + const authorized = await checkAuthorization(serverSpec.id, cairoId); + if (!authorized) + throw new ExpressClientError({ c: 403, m: "Access forbidden!" }); } export async function createServer(req, res) { @@ -82,7 +86,7 @@ export async function createServer(req, res) { if (backupPayloadFilter(req, res) !== "filtered") return; const serverSpec = req.body; try { - const serverEntry = await createServerEntry(serverSpec); + const serverEntry = await createServerEntry(req.cairoId, serverSpec); await createServerResources(serverEntry); res.json(serverEntry); } catch (e) { @@ -94,7 +98,7 @@ export async function deleteServer(req, res) { // Ensure spec is safe const serverSpec = req.body; try { - checkServerId(serverSpec); + await checkServerId(req.cairoId, serverSpec); } catch (e) { return sendError(res)(e); } @@ -109,7 +113,7 @@ export async function startServer(req, res) { // Ensure spec is safe const serverSpec = req.body; try { - checkServerId(serverSpec); + await checkServerId(req.cairoId, serverSpec); } catch (e) { return sendError(res)(e); } @@ -123,7 +127,7 @@ export async function stopServer(req, res) { // Ensure spec is safe const serverSpec = req.body; try { - checkServerId(serverSpec); + await checkServerId(req.cairoId, serverSpec); } catch (e) { return sendError(res)(e); } @@ -137,7 +141,7 @@ export async function getServer(req, res) { // Ensure spec is safe const serverSpec = req.body; try { - checkServerId(serverSpec); + await checkServerId(req.cairoId, serverSpec); } catch (e) { return sendError(res)(e); } @@ -155,7 +159,7 @@ export async function modifyServer(req, res) { if (payloadFilter(req, res) !== "filtered") return; const serverSpec = req.body; try { - checkServerId(serverSpec); + await checkServerId(req.cairoId, serverSpec); const serverEntry = await modifyServerEntry(serverSpec); // await createServerResources(serverEntry); res.sendStatus(200); diff --git a/lib/controllers/status-controller.js b/lib/controllers/status-controller.js index cd5ff57..a36a53b 100644 --- a/lib/controllers/status-controller.js +++ b/lib/controllers/status-controller.js @@ -1,9 +1,9 @@ -import { getDeployments } from "../k8s/k8s-server-control.js"; +import { getUserDeployments } from "../k8s/k8s-server-control.js"; import { getInstances } from "../k8s/server-status.js"; import { sendError } from "../util/ExpressClientError.js"; export function serverList(req, res) { - getDeployments() + getUserDeployments(req.cairoId) .then((sd) => res.json(sd.map((s) => s.metadata.name.substring(4)))) .catch((e) => { ERR("STATUS CONTROLLER", e); @@ -12,7 +12,7 @@ export function serverList(req, res) { } export function serverInstances(req, res) { - getInstances() + getInstances(req.cairoId) .then((i) => res.json(i)) .catch(sendError(res)); } diff --git a/lib/controllers/sub-controllers/console-controller.js b/lib/controllers/sub-controllers/console-controller.js index 264146a..6713960 100644 --- a/lib/controllers/sub-controllers/console-controller.js +++ b/lib/controllers/sub-controllers/console-controller.js @@ -4,10 +4,9 @@ import { Rcon as RconClient } from "rcon-client"; import stream from "stream"; import { ERR, WARN } from "../../util/logging.js"; import { getServerEntry } from "../../database/queries/server-queries.js"; - +import kc from "../../k8s/k8s-config.js"; // Kubernetes Configuration -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); + const k8sCore = kc.makeApiClient(k8s.CoreV1Api); const namespace = process.env.MCL_SERVER_NAMESPACE; diff --git a/lib/database/migrations/1_create_servers_table.sql b/lib/database/migrations/1_create_servers_table.sql index 33ddc5a..fe3f357 100644 --- a/lib/database/migrations/1_create_servers_table.sql +++ b/lib/database/migrations/1_create_servers_table.sql @@ -1,6 +1,7 @@ CREATE SEQUENCE servers_id_seq; CREATE TABLE servers ( id bigint NOT NULL DEFAULT nextval('servers_id_seq') PRIMARY KEY, + owner_cairo_id bigint, host varchar(255) DEFAULT NULL, name varchar(255) DEFAULT NULL, version varchar(63) DEFAULT 'latest', diff --git a/lib/database/queries/server-queries.js b/lib/database/queries/server-queries.js index c98902e..326572b 100644 --- a/lib/database/queries/server-queries.js +++ b/lib/database/queries/server-queries.js @@ -2,7 +2,7 @@ import pg from "../postgres.js"; import { deleteQuery, insertQuery, - selectWhereQuery, + selectWhereAllQuery, updateWhereAllQuery, } from "../pg-query.js"; import ExpressClientError from "../../util/ExpressClientError.js"; @@ -12,9 +12,18 @@ const asExpressClientError = (e) => { throw new ExpressClientError({ m: e.message, c: 409 }); }; -const getMclName = (host, id) => `${host.replaceAll(".", "-")}-${id}`; +const getMclName = (host, id) => + `${host.toLowerCase().replaceAll(".", "-")}-${id}`; -export async function createServerEntry(serverSpec) { +export async function checkAuthorization(serverId, cairoId) { + const q = selectWhereAllQuery(table, { + id: serverId, + owner_cairo_id: cairoId, + }); + return (await pg.query(q)).length === 1; +} + +export async function createServerEntry(cairoId, serverSpec) { const { name, host, @@ -33,6 +42,7 @@ export async function createServerEntry(serverSpec) { var q = insertQuery(table, { name, + owner_cairo_id: cairoId, host, version, server_type, @@ -52,6 +62,7 @@ export async function createServerEntry(serverSpec) { const entries = await pg.query(q); const { id, + owner_cairo_id: ownerCairoId, name, host, version, @@ -72,6 +83,7 @@ export async function createServerEntry(serverSpec) { name, mclName, id, + ownerCairoId, host, version, serverType, @@ -99,7 +111,7 @@ export async function deleteServerEntry(serverId) { export async function getServerEntry(serverId) { if (!serverId) asExpressClientError({ message: "Server ID Required!" }); - const q = selectWhereQuery(table, { id: serverId }); + const q = selectWhereAllQuery(table, { id: serverId }); try { const serverSpecs = await pg.query(q); if (serverSpecs.length === 0) return []; @@ -107,6 +119,7 @@ export async function getServerEntry(serverId) { throw Error("Multiple servers found with the same name!"); const { id, + owner_cairo_id: ownerCairoId, name, host, version, @@ -127,6 +140,7 @@ export async function getServerEntry(serverId) { name, mclName, id, + ownerCairoId, host, version, serverType, @@ -149,6 +163,7 @@ export async function getServerEntry(serverId) { export async function modifyServerEntry(serverSpec) { const { id, + // ownerCairoId: owner_cairo_id, // DIsabled! If these becomes a reqest, please create a new function! name, host, version, diff --git a/lib/k8s/k8s-config.js b/lib/k8s/k8s-config.js new file mode 100644 index 0000000..4167552 --- /dev/null +++ b/lib/k8s/k8s-config.js @@ -0,0 +1,12 @@ +import k8s from "@kubernetes/client-node"; +const MCL_KUBECONFIG = process.env.MCL_KUBECONFIG; +const envConfig = MCL_KUBECONFIG ? MCL_KUBECONFIG : null; +const kc = new k8s.KubeConfig(); +try { + if (!!envConfig) + kc.loadFromString(Buffer.from(envConfig, "base64").toString("utf8")); + else kc.loadFromDefault(); +} catch (e) { + kc.loadFromDefault(); +} +export default kc; diff --git a/lib/k8s/k8s-server-control.js b/lib/k8s/k8s-server-control.js index f82187a..c708328 100644 --- a/lib/k8s/k8s-server-control.js +++ b/lib/k8s/k8s-server-control.js @@ -7,8 +7,8 @@ import { getCoreServerContainer, getBackupContainer, } from "./server-containers.js"; -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); +import { checkAuthorization } from "../database/queries/server-queries.js"; +import kc from "./k8s-config.js"; const k8sDeps = kc.makeApiClient(k8s.AppsV1Api); const k8sCore = kc.makeApiClient(k8s.CoreV1Api); @@ -25,6 +25,20 @@ const mineclusterManaged = (o) => export const serverMatch = (serverId) => (o) => o.metadata.annotations["minecluster.dunemask.net/id"] === serverId; +export const cairoMatch = (cairoId) => (o) => + checkAuthorization( + o.metadata.annotations["minecluster.dunemask.net/id"], + cairoId, + ); + +export async function getUserDeployments(cairoId) { + const authFIlter = cairoMatch(cairoId); + const allDeployments = await getDeployments(); + const authChecks = allDeployments.map(authFIlter); + const authorizations = await Promise.all(authChecks); + return allDeployments.filter((_d, i) => authorizations[i]); +} + export async function getDeployments() { const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); const serverDeployments = deploymentRes.body.items.filter(mineclusterManaged); diff --git a/lib/k8s/server-create.js b/lib/k8s/server-create.js index 421a244..3d4618a 100644 --- a/lib/k8s/server-create.js +++ b/lib/k8s/server-create.js @@ -11,8 +11,7 @@ import { getBackupContainer, } from "./server-containers.js"; -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); +import kc from "./k8s-config.js"; const k8sDeps = kc.makeApiClient(k8s.AppsV1Api); const k8sCore = kc.makeApiClient(k8s.CoreV1Api); const namespace = process.env.MCL_SERVER_NAMESPACE; diff --git a/lib/k8s/server-delete.js b/lib/k8s/server-delete.js index 4ced830..c0364a8 100644 --- a/lib/k8s/server-delete.js +++ b/lib/k8s/server-delete.js @@ -2,8 +2,7 @@ import k8s from "@kubernetes/client-node"; import { ERR } from "../util/logging.js"; import { getServerAssets } from "./k8s-server-control.js"; import ExpressClientError from "../util/ExpressClientError.js"; -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); +import kc from "./k8s-config.js"; const k8sDeps = kc.makeApiClient(k8s.AppsV1Api); const k8sCore = kc.makeApiClient(k8s.CoreV1Api); diff --git a/lib/k8s/server-status.js b/lib/k8s/server-status.js index 43f00af..0c421c1 100644 --- a/lib/k8s/server-status.js +++ b/lib/k8s/server-status.js @@ -1,8 +1,7 @@ import k8s from "@kubernetes/client-node"; -import { getDeployments } from "./k8s-server-control.js"; +import { getUserDeployments } from "./k8s-server-control.js"; import { getServerEntries } from "../database/queries/server-queries.js"; -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); +import kc from "./k8s-config.js"; const k8sMetrics = new k8s.Metrics(kc); const namespace = process.env.MCL_SERVER_NAMESPACE; @@ -42,9 +41,9 @@ function getServerStatus(server) { return { serverAvailable, ftpAvailable, services, deploymentAvailable }; } -export async function getInstances() { +export async function getInstances(cairoId) { const [serverDeployments, podMetricsRes, entries] = await Promise.all([ - getDeployments(), + getUserDeployments(cairoId), k8sMetrics.getPodMetrics(namespace), getServerEntries(), ]); diff --git a/lib/routes/auth-route.js b/lib/routes/auth-route.js new file mode 100644 index 0000000..8409975 --- /dev/null +++ b/lib/routes/auth-route.js @@ -0,0 +1,16 @@ +import { Router } from "express"; +import cairoAuthMiddleware from "./middlewares/auth-middleware.js"; +const router = Router(); + +const ok = (_r, res) => res.sendStatus(200); + +function cairoRedirect(req, res) { + res.redirect( + `${process.env.MCL_CAIRO_URL}/cairo/auth?redirectUri=${req.query.redirectUri}`, + ); +} + +router.get("/verify", cairoAuthMiddleware, ok); +router.get("/redirect", cairoRedirect); + +export default router; diff --git a/lib/routes/files-route.js b/lib/routes/files-route.js index c27c175..1092a81 100644 --- a/lib/routes/files-route.js +++ b/lib/routes/files-route.js @@ -8,8 +8,10 @@ import { getItem, } from "../controllers/file-controller.js"; +import cairoAuthMiddleware from "./middlewares/auth-middleware.js"; + const router = Router(); -router.use(jsonMiddleware()); +router.use([jsonMiddleware(), cairoAuthMiddleware]); const multerMiddleware = multer(); router.post("/list", listFiles); diff --git a/lib/routes/middlewares/auth-middleware.js b/lib/routes/middlewares/auth-middleware.js new file mode 100644 index 0000000..0a73234 --- /dev/null +++ b/lib/routes/middlewares/auth-middleware.js @@ -0,0 +1,33 @@ +// Imports +import { Router } from "express"; +import bearerTokenMiddleware from "express-bearer-token"; +import { ERR, VERB } from "../../util/logging.js"; + +// Constants +const { MCL_CAIRO_URL } = process.env; +const cairoAuthMiddleware = Router(); + +const cairoAuthenticate = async (token) => { + const config = { headers: { Authorization: `Bearer ${token}` } }; + return fetch(`${MCL_CAIRO_URL}/api/user/info`, config).then((res) => + res.json(), + ); +}; + +// Middleware +const cairoAuthHandler = (req, res, next) => { + if (!req.token) return res.status(401).send("Cairo auth required!"); + VERB("AUTH", `${MCL_CAIRO_URL}/api/user/info`); + cairoAuthenticate(req.token) + .then((authData) => (req.cairoId = authData.id)) + .then(() => next()) + .catch((err) => { + ERR("AUTH", err.response ? err.response.data : err.message); + if (!err.response) return res.status(500).send(`Auth failure ${err}`); + return res.status(err.response.status).send(err.response.data); + }); +}; + +cairoAuthMiddleware.use([bearerTokenMiddleware(), cairoAuthHandler]); + +export default cairoAuthMiddleware; diff --git a/lib/routes/server-route.js b/lib/routes/server-route.js index d6eb922..d8ae832 100644 --- a/lib/routes/server-route.js +++ b/lib/routes/server-route.js @@ -11,8 +11,11 @@ import { serverInstances, serverList, } from "../controllers/status-controller.js"; + +import cairoAuthMiddleware from "./middlewares/auth-middleware.js"; + const router = Router(); -router.use(jsonMiddleware()); +router.use([jsonMiddleware(), cairoAuthMiddleware]); // Routes router.post("/create", createServer); router.delete("/delete", deleteServer); diff --git a/lib/routes/system-route.js b/lib/routes/system-route.js index 66e1022..ef913ed 100644 --- a/lib/routes/system-route.js +++ b/lib/routes/system-route.js @@ -1,9 +1,12 @@ import { Router } from "express"; import k8s from "@kubernetes/client-node"; import { WARN } from "../util/logging.js"; +import kc from "../k8s/k8s-config.js"; const router = Router(); -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); + +import cairoAuthMiddleware from "./middlewares/auth-middleware.js"; +router.use(cairoAuthMiddleware); + const k8sApi = kc.makeApiClient(k8s.CoreV1Api); // Get Routes router.get("/available", (req, res) => { diff --git a/lib/server/router.js b/lib/server/router.js index ccedebb..b4eb444 100644 --- a/lib/server/router.js +++ b/lib/server/router.js @@ -3,6 +3,7 @@ import express from "express"; // Routes import vitals from "../routes/vitals-route.js"; +import authRoute from "../routes/auth-route.js"; import systemRoute from "../routes/system-route.js"; import serverRoute from "../routes/server-route.js"; import filesRoute from "../routes/files-route.js"; @@ -22,6 +23,7 @@ export default function buildRoutes(pg, skio) { // Middlewares // Routes + router.use("/api/auth", authRoute); router.use("/api/system", systemRoute); router.use("/api/server", serverRoute); router.use("/api/files", filesRoute); diff --git a/package-lock.json b/package-lock.json index a773352..f506448 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "bcrypt": "^5.1.1", "chalk": "^5.3.0", "express": "^4.18.2", + "express-bearer-token": "^2.4.0", "figlet": "^1.7.0", "js-yaml": "^4.1.0", "moment": "^2.29.4", @@ -4603,6 +4604,26 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -5073,6 +5094,26 @@ "node": ">= 0.10.0" } }, + "node_modules/express-bearer-token": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/express-bearer-token/-/express-bearer-token-2.4.0.tgz", + "integrity": "sha512-2+kRZT2xo+pmmvSY7Ma5FzxTJpO3kGaPCEXPbAm3GaoZ/z6FE4K6L7cvs1AUZwY2xkk15PcQw7t4dWjsl5rdJw==", + "dependencies": { + "cookie": "^0.3.1", + "cookie-parser": "^1.4.4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/express-bearer-token/node_modules/cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", diff --git a/package.json b/package.json index af82f14..353c74d 100644 --- a/package.json +++ b/package.json @@ -22,41 +22,42 @@ "author": "Dunemask", "license": "LGPL-2.1", "devDependencies": { - "@emotion/react": "^11.11.1", + "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", - "@mui/icons-material": "^5.14.19", - "@mui/material": "^5.14.20", - "@tanstack/react-query": "^5.12.2", + "@mui/icons-material": "^5.15.7", + "@mui/material": "^5.15.7", + "@tanstack/react-query": "^5.18.1", "@vitejs/plugin-react": "^4.2.1", "chonky": "^2.3.2", "chonky-icon-fontawesome": "^2.3.2", "concurrently": "^8.2.2", - "nodemon": "^3.0.2", - "prettier": "^3.1.0", + "nodemon": "^3.0.3", + "prettier": "^3.2.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-quill": "^2.0.0", - "react-router-dom": "^6.20.1", - "react-toastify": "^9.1.3", - "socket.io-client": "^4.7.2", - "vite": "^5.0.7" + "react-router-dom": "^6.22.0", + "react-toastify": "^10.0.4", + "socket.io-client": "^4.7.4", + "vite": "^5.0.12" }, "dependencies": { "@kubernetes/client-node": "^0.20.0", - "aws-sdk": "^2.1514.0", + "aws-sdk": "^2.1550.0", "basic-ftp": "^5.0.4", "bcrypt": "^5.1.1", "chalk": "^5.3.0", "express": "^4.18.2", + "express-bearer-token": "^2.4.0", "figlet": "^1.7.0", "js-yaml": "^4.1.0", - "moment": "^2.29.4", + "moment": "^2.30.1", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "pg-promise": "^11.5.4", "postgres-migrations": "^5.3.0", "rcon-client": "^4.2.4", - "socket.io": "^4.7.2", + "socket.io": "^4.7.4", "uuid": "^9.0.1" } } diff --git a/src/components/files/FilePreview.jsx b/src/components/files/FilePreview.jsx index 8a7d27c..0241cf5 100644 --- a/src/components/files/FilePreview.jsx +++ b/src/components/files/FilePreview.jsx @@ -7,8 +7,8 @@ import DialogContent from "@mui/material/DialogContent"; import DialogActions from "@mui/material/DialogActions"; import Dialog from "@mui/material/Dialog"; import Toolbar from "@mui/material/Toolbar"; - import TextEditor from "./TextEditor.jsx"; +import { cairoAuthHeader } from "@mcl/util/auth.js"; const textFileTypes = ["properties", "txt", "yaml", "yml", "json", "env"]; const imageFileTypes = ["png", "jpeg", "jpg"]; @@ -52,6 +52,7 @@ export default function FilePreview(props) { await fetch("/api/files/upload", { method: "POST", body: formData, + headers: cairoAuthHeader(), }); dialogToggle(); } diff --git a/src/components/files/MineclusterFiles.jsx b/src/components/files/MineclusterFiles.jsx index 3dbc6d4..e41652e 100644 --- a/src/components/files/MineclusterFiles.jsx +++ b/src/components/files/MineclusterFiles.jsx @@ -18,6 +18,7 @@ import { getServerItem, } from "@mcl/queries"; import { previewServerItem } from "../../util/queries"; +import { cairoAuthHeader } from "@mcl/util/auth.js"; import { supportedFileTypes } from "./FilePreview.jsx"; @@ -109,6 +110,7 @@ export default function MineclusterFiles(props) { await fetch("/api/files/upload", { method: "POST", body: formData, + headers: cairoAuthHeader(), }); } diff --git a/src/components/servers/RconSocket.js b/src/components/servers/RconSocket.js index f18c0ef..330b85d 100644 --- a/src/components/servers/RconSocket.js +++ b/src/components/servers/RconSocket.js @@ -8,6 +8,7 @@ export default class RconSocket { this.sk.on("rcon-error", this.onRconError.bind(this)); this.sk.on("error", () => console.log("WHOOSPSIE I GUESS?")); this.rconLive = false; + this.rconError = false; } onPush(p) { @@ -22,6 +23,7 @@ export default class RconSocket { onRconError(v) { this.rconLive = false; + this.rconError = true; console.log("Server sent: ", v); } diff --git a/src/components/servers/RconView.jsx b/src/components/servers/RconView.jsx index d4c99fd..24f87ee 100644 --- a/src/components/servers/RconView.jsx +++ b/src/components/servers/RconView.jsx @@ -55,10 +55,12 @@ export default function RconView(props) { variant="outlined" value={cmd} onChange={updateCmd} - disabled={!(rcon && rcon.rconLive)} + disabled={!(rcon && rcon.rconLive && !rcon.rconError)} /> - {rcon && rcon.rconLive && } - {!(rcon && rcon.rconLive) && ( + {rcon && rcon.rconLive && !rcon.rconError && ( + + )} + {!(rcon && rcon.rconLive && !rcon.rconError) && ( )} diff --git a/src/ctx/SettingsContext.jsx b/src/ctx/SettingsContext.jsx index e323177..9d2ef57 100644 --- a/src/ctx/SettingsContext.jsx +++ b/src/ctx/SettingsContext.jsx @@ -10,6 +10,7 @@ const defaultSettings = { simplifiedControls: false, logAppDetails: true, defaultPage: "home", + cairoAuth: null, }; const settings = localSettings ? JSON.parse(localSettings) : defaultSettings; @@ -27,6 +28,7 @@ const settingsUpdater = (oldState, settingsUpdate) => { if (settingsUpdate[k] === undefined) continue; settingsToUpdate[k] = settingsUpdate[k]; } + console.log("SAVING", settingsToUpdate); localStorage.setItem("settings", JSON.stringify(settingsToUpdate)); }; diff --git a/src/nav/Viewport.jsx b/src/nav/Viewport.jsx index a6b7622..fabe10c 100644 --- a/src/nav/Viewport.jsx +++ b/src/nav/Viewport.jsx @@ -1,13 +1,14 @@ -import Box from "@mui/material/Box"; import Toolbar from "@mui/material/Toolbar"; import MCLPortal from "./MCLPortal.jsx"; -import Button from "@mui/material/Button"; -import SpeedDialIcon from "@mui/material/SpeedDialIcon"; // Import Navbar /*import Navbar from "./Navbar.jsx";*/ +import { useCairoAuth } from "@mcl/util/auth.js"; import MCLMenu from "./MCLMenu.jsx"; +import Auth from "@mcl/pages/Auth.jsx"; export default function Views() { + const auth = useCairoAuth(); + if (!auth) return ; return (
diff --git a/src/pages/Auth.jsx b/src/pages/Auth.jsx new file mode 100644 index 0000000..e7278fa --- /dev/null +++ b/src/pages/Auth.jsx @@ -0,0 +1,63 @@ +import { useState, useEffect } from "react"; +import { useSearchParams, useNavigate } from "react-router-dom"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Typography from "@mui/material/Typography"; + +export default function Auth() { + const [searchParams] = useSearchParams(); + const currentServer = searchParams.get("token"); + + const nav = useNavigate(); + + const cairoLogin = () => + (window.location.href = `/api/auth/redirect?redirectUri=${window.location.href}`); + + return ( + theme.palette.primary.main, + }} + > + + + + + + + + + ); +} diff --git a/src/util/auth.js b/src/util/auth.js new file mode 100644 index 0000000..fe3138b --- /dev/null +++ b/src/util/auth.js @@ -0,0 +1,40 @@ +import { useState, useContext, useEffect } from "react"; +import { useSearchParams, useNavigate } from "react-router-dom"; +import SettingsContext from "@mcl/settings"; + +const verifyAuth = (authToken) => + fetch("/api/auth/verify", { + headers: { Authorization: `Bearer ${authToken}` }, + }) + .then((res) => res.status === 200) + .catch(() => false); + +export function useCairoAuth() { + const { state: settings, updateSettings } = useContext(SettingsContext); + const [auth, setAuth] = useState(!!settings.cairoAuth); + const [searchParams] = useSearchParams(); + const nav = useNavigate(); + + useEffect(() => { + const webToken = searchParams.get("cairoAuthToken"); + if (!webToken) return; + verifyAuth(webToken).then(setAuth); + updateSettings({ cairoAuth: webToken }); + nav("/"); + }, [searchParams]); + + useEffect(() => { + verifyAuth(settings.cairoAuth).then(setAuth); + nav("/"); + }, [settings.cairoAuth]); + + return auth; +} + +export function getAuthTokenFromStorage() { + return JSON.parse(localStorage.getItem("settings")).cairoAuth; +} + +export function cairoAuthHeader() { + return { Authorization: `Bearer ${getAuthTokenFromStorage()}` }; +} diff --git a/src/util/queries.js b/src/util/queries.js index c80be01..14a7035 100644 --- a/src/util/queries.js +++ b/src/util/queries.js @@ -1,12 +1,17 @@ import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { cairoAuthHeader } from "@mcl/util/auth.js"; + const fetchApi = (subPath) => async () => - fetch(`/api${subPath}`).then((res) => res.json()); + fetch(`/api${subPath}`, { headers: cairoAuthHeader() }).then((res) => + res.json(), + ); const fetchApiCore = async (subPath, json, method = "POST", jsonify = false) => fetch(`/api${subPath}`, { method, headers: { "Content-Type": "application/json", + ...cairoAuthHeader(), }, body: JSON.stringify(json), }).then((res) => (jsonify ? res.json() : res)); @@ -16,6 +21,7 @@ const fetchApiPost = (subPath, json) => async () => method: "POST", headers: { "Content-Type": "application/json", + ...cairoAuthHeader(), }, body: JSON.stringify(json), }).then((res) => res.json()); @@ -117,6 +123,7 @@ const postJsonApi = (subPath, body, invalidate, method = "POST") => { method, headers: { "Content-Type": "application/json", + ...cairoAuthHeader(), }, body: JSON.stringify(body), }); From 40b3a42c7346683cd6ee838501b999af102ef8ac Mon Sep 17 00:00:00 2001 From: Dunemask Date: Sun, 4 Feb 2024 19:33:40 -0700 Subject: [PATCH 11/38] [HOTFIX] Postgres Password Fix --- lib/database/postgres.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/database/postgres.js b/lib/database/postgres.js index eb2a037..452b53c 100644 --- a/lib/database/postgres.js +++ b/lib/database/postgres.js @@ -11,7 +11,7 @@ const { MCL_POSTGRES_DATABASE: database, MCL_POSTGRES_ENABLED: pgEnabled, MCL_POSTGRES_HOST: host, - MCL_POSTGRES_PASSWORD: password, + MCL_POSTGRES_PASS: password, MCL_POSTGRES_PORT: port, MCL_POSTGRES_USER: user, } = process.env; From 1eaa7ff5a5dd3c2f495ea89b46e44df9dd6baf9a Mon Sep 17 00:00:00 2001 From: dunemask Date: Mon, 5 Feb 2024 03:16:53 +0000 Subject: [PATCH 12/38] [FIX] Enable Correct Production Hooks (#13) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/13 --- src/util/auth.js | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/util/auth.js b/src/util/auth.js index fe3138b..f25a6bb 100644 --- a/src/util/auth.js +++ b/src/util/auth.js @@ -1,6 +1,8 @@ -import { useState, useContext, useEffect } from "react"; -import { useSearchParams, useNavigate } from "react-router-dom"; -import SettingsContext from "@mcl/settings"; +import { useState, useEffect } from "react"; +import { useSearchParams } from "react-router-dom"; + +const tokenStorageName = "cairoAuthToken"; +const tokenQuery = "cairoAuthToken"; const verifyAuth = (authToken) => fetch("/api/auth/verify", { @@ -10,29 +12,34 @@ const verifyAuth = (authToken) => .catch(() => false); export function useCairoAuth() { - const { state: settings, updateSettings } = useContext(SettingsContext); - const [auth, setAuth] = useState(!!settings.cairoAuth); - const [searchParams] = useSearchParams(); - const nav = useNavigate(); + const [authToken, setAuthToken] = useState( + localStorage.getItem(tokenStorageName), + ); + const [auth, setAuth] = useState(false); + const [searchParams, setSearchParams] = useSearchParams(); useEffect(() => { - const webToken = searchParams.get("cairoAuthToken"); + if (!authToken) return; + verifyAuth(authToken).then((authorized) => { + if (!authorized) localStorage.removeItem(tokenStorageName); + setAuth(authorized); + }); + }, [authToken]); + + useEffect(() => { + const webToken = searchParams.get(tokenQuery); if (!webToken) return; - verifyAuth(webToken).then(setAuth); - updateSettings({ cairoAuth: webToken }); - nav("/"); + localStorage.setItem(tokenStorageName, webToken); + searchParams.delete(tokenQuery); + setAuthToken(webToken); + setSearchParams(searchParams); }, [searchParams]); - useEffect(() => { - verifyAuth(settings.cairoAuth).then(setAuth); - nav("/"); - }, [settings.cairoAuth]); - return auth; } export function getAuthTokenFromStorage() { - return JSON.parse(localStorage.getItem("settings")).cairoAuth; + return localStorage.getItem(tokenStorageName); } export function cairoAuthHeader() { From bee4e61c87966a3d47f7d0595a0aa527d3a98ad4 Mon Sep 17 00:00:00 2001 From: dunemask Date: Mon, 5 Feb 2024 04:11:50 +0000 Subject: [PATCH 13/38] [FEATURE] Minecluster Icons (#14) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/14 --- index.html | 9 + public/icons/android-chrome-192x192.png | Bin 0 -> 8839 bytes public/icons/android-chrome-512x512.png | Bin 0 -> 60570 bytes .../apple-touch-icon-120x120-precomposed.png | Bin 0 -> 4387 bytes public/icons/apple-touch-icon-120x120.png | Bin 0 -> 4060 bytes .../apple-touch-icon-152x152-precomposed.png | Bin 0 -> 6335 bytes public/icons/apple-touch-icon-152x152.png | Bin 0 -> 6229 bytes .../apple-touch-icon-180x180-precomposed.png | Bin 0 -> 8553 bytes public/icons/apple-touch-icon-180x180.png | Bin 0 -> 8150 bytes .../apple-touch-icon-60x60-precomposed.png | Bin 0 -> 1826 bytes public/icons/apple-touch-icon-60x60.png | Bin 0 -> 1646 bytes .../apple-touch-icon-76x76-precomposed.png | Bin 0 -> 2494 bytes public/icons/apple-touch-icon-76x76.png | Bin 0 -> 2210 bytes public/icons/apple-touch-icon-precomposed.png | Bin 0 -> 8553 bytes public/icons/apple-touch-icon.png | Bin 0 -> 8150 bytes public/icons/browserconfig.xml | 9 + public/icons/favicon-16x16.png | Bin 0 -> 444 bytes public/icons/favicon-32x32.png | Bin 0 -> 854 bytes public/icons/favicon.ico | Bin 0 -> 7406 bytes public/icons/mstile-150x150.png | Bin 0 -> 5158 bytes public/icons/safari-pinned-tab.svg | 252 ++++++++++++++++++ public/icons/site.webmanifest | 19 ++ 22 files changed, 289 insertions(+) create mode 100644 public/icons/android-chrome-192x192.png create mode 100644 public/icons/android-chrome-512x512.png create mode 100644 public/icons/apple-touch-icon-120x120-precomposed.png create mode 100644 public/icons/apple-touch-icon-120x120.png create mode 100644 public/icons/apple-touch-icon-152x152-precomposed.png create mode 100644 public/icons/apple-touch-icon-152x152.png create mode 100644 public/icons/apple-touch-icon-180x180-precomposed.png create mode 100644 public/icons/apple-touch-icon-180x180.png create mode 100644 public/icons/apple-touch-icon-60x60-precomposed.png create mode 100644 public/icons/apple-touch-icon-60x60.png create mode 100644 public/icons/apple-touch-icon-76x76-precomposed.png create mode 100644 public/icons/apple-touch-icon-76x76.png create mode 100644 public/icons/apple-touch-icon-precomposed.png create mode 100644 public/icons/apple-touch-icon.png create mode 100644 public/icons/browserconfig.xml create mode 100644 public/icons/favicon-16x16.png create mode 100644 public/icons/favicon-32x32.png create mode 100644 public/icons/favicon.ico create mode 100644 public/icons/mstile-150x150.png create mode 100644 public/icons/safari-pinned-tab.svg create mode 100644 public/icons/site.webmanifest diff --git a/index.html b/index.html index ce38114..4fadd4e 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,15 @@ + + + + + + + + + Minecluster diff --git a/public/icons/android-chrome-192x192.png b/public/icons/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..5f91c4adc8e9f1712ce88345ec7b5ff7b45d4fc0 GIT binary patch literal 8839 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 literal 0 HcmV?d00001 diff --git a/public/icons/android-chrome-512x512.png b/public/icons/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..ffad351cf341c8034b869473d58a366a9262410f GIT binary patch literal 60570 zcmZ5nc{J2t*#FKx24ifIb?ho4OUN=~CqyXxQZtsK5T%7M^R=&KtrRhmP>NKN%8aeF zF)0Z zp9lxAh<{DTUG?sN4Kl>q%^Cn6?S4DM{MK2odpSB6jv8f#niZbbuqWx$0}nsRKha-d zb@$>C=JnH@8oNeXz`L^jFH=>l%sgLTj+o2z|8zBn-|qN6B4)ZFdFi?G88-&+omYQ? z6D#V{(lzqKvjklu^;CDmTSv+=wo?D?1$WN#fENscAj7jcqDpR(0}=KBKm?rati5A~ zX8n&`9CDS&*;X!zIeHj-ZneATZR@PXjLbX)^{cv3?&+!a%*eb3+b)DN!tUL_AGi2t zIP6vb|Efe(#7j+gnl$mtE%~sCC^P;)$=bNYYef|37i2r2EO@5$H$%CGext2dR zx#qmp=OeUq!M+WCE@^YUWB4;Sq_a1YId_;et9N%`CFbehuaS}OOCQYBuAQ?K{pP+j zCHPBdh=jHhUVU~K#LI0qqUc9|dYR_XeH{VWIRCwy9nLE3QvY>%`EuQ;Bg;19=2@{? zk0Mo8BV@!I`AR7OPbCH)$1BkSWOv?Go*7s*xY#hf?jzDU9|wd-MsE{vY`K${>Xp}S zA^mwKK;71_*8cKCM5)o0l_gq2IAmM}dhXo6V>1OvZ1W|(-ncmCu20NXWzvlv~!r(|(-mPu4Bqm!0-*e6^{T!Qs2Z21qr9w;-pZSVS zG3NN;iXF?5r#4eQQX+BHiB-I8qQ}l%)w0b?S`Kaej{xH}6mck}PDNU(3d(A7g02GZ z+D(iFhO_k8%R=z*Wejr*^Wl-y=7h+Ga)H3FNgq@640=MT-i=D0M7LsG$%G6c>ril< z<)OD*Y}uH{)Lumc!%d9&+-@j3K{64(E#aA>a8Xh~mJady*6 zSi<(lPKx}T=J&Gp=DV-0m&B3bqFGz;QOYv+8nVBY?o=ea6(s7UVE^002Pq!b|zPW z{fOjWiO2lFPh^sWxJ5b`oG6jRG(}oCBcwiGdIiN00oZ&if-AWC+r45!meDA28oIF` zwD$sb0BVR!K9K#YkD&glj0;JguEsOke7Uri;cnc&b^hi~HP^<~d3P^Ygf!yT3Ahiz z$Sp0=NLb{Rce1|_R_ud1`i<6c#?-7Y&g1mqiYp~IR4L9>3@Z)*1II%oE`1dEpFwU1 zx5|;=b?|5DTYAZ6oK{)d9l*k*X)3zC9wEdU+xzIQ<1N08mJ^oHpc6H*=@DC_Vq9a8 zXUcjDyhBvV!FP-LVhJd={C~kvx`Kyz!(m%4C;cn=)pdaTMyy0P@K61By+@SGyd|t} zF?1POjG8428dhfHQ`(eM4wXozb9@r+_6%?({|wi-r;AB%%hU13T^K#=^pxR-&>Dw+ zV`NF#E}~>mQPQZ8FYcao5Sl=!lX^KbF**mW=fkL1Jr~N%==ae31L2-1YFX8NC`i;a zdfD*-&jw9J4TkdEzJ?GQY*{Df-x7&O2EzE`XFN38g6+oUV2l-RUR|p)I){F_^*qww zxH@!9e8p|bl~1HGL~9HjQUT%`DhbS9hu`u zql%mO^1)&uIQnllk`93I)waALvZqQ;}j zdjJn`ka-tJIJCdFZj;`RP^dzdE{*FQe*Tvh2vr=WNdu}a|5+!lY(C;9C(u*bE?<=Q-KXk`kUKV0t7y24mN(&b9la%IW)=z}X^qcH6X#7kK| zNjVE*Z&sOzijZaahf7{5nk?EqiM|z?I(p~^WO85NdlNwproQlK>AQSV{KTn-H5zap z^Cnrn0f3*FPu3RHYWN-w7q<_Z7c&M-$vvBhJ#d1@4}%>nPjdNAKnx~LALkpL+!(M~ z?i#X1H=oF7`s>NWG>rH^B#LStLMSpA(juTl=!ZJ8aUFvw((mAk_$pOep-0{9$UZ5g z({&+re>=QQq9t33Cjn$FgU!op0%aKp6yqjBe;^FTg z(a=>RWSp?o1Adzl@BOc=Vxo4GA9a^aghKf(**fZZQJc@oO)IRytsY`88Vm3Ax;`aL zc&YrvCVjoep7%@2gCXg(x8vqFdLk4u3%}xw8GHhzwO)M9A@;aeK7brtbkTZRVY7@?&<(+DKskd$q ztWPT|(~1x1k5NtxBcxW#pRUWlss7>)CpggLz9jT4op0n2m_jts$7!4&;CAkQQuKLm z!dLJuoUInhmf0pJJGhMngBhi4_7x>Rc0;_Z`Vmf8%Rq=LtfmP-SP_3?ZKJ4iFYu1p zWkdV|+=a`Ez#4ef`R1I}wt)JFqKy<$CD)ZFZ|j}EMCRtcr>)dQWgfQ)PCT#+W^U!= zM|MPG?xVLn2suqA+WUd9#9*@iaZnBEp*t@HXiR|xA3+0{P7}t|=Rwv_dJ1TE2eP)> zOzu|};Xh%onguOaaj$m^^B;d&vddiv0lw>Ci{ZPQOfz1oP73e9IpIogz81mV`d(M_ z;NK#mH!rc=(o?*-MZk@V2T{BDVL{zIURrCVXQ;w^&FTojmc4_&R)givw7)5(j z2Hz3|aQ5Mw8XL|WHOm%NQjpm6muwjo-(AD)HQpTBGgV|mRsA+XZa-rtc%9h7~` zdz$}nj11-f$xmIom7+guQun+{cOUk*%7cW3EDEz{LgudhOM) zDT|ux$uU#J8jvTDhVD^U52_?5O{%heyZbTJB?=nMPGu=fl7iQV@!))lx53)L;ASpB zUao)3IYRTCPab?xm0$geiGhA5H!*G6Lg8Jk2e*l5(p~HIw@c@pA{wK&xR~O?$T zeST-IV-0Y=D_@GwqjF)?*Yp_No!~b>8+vtui3f)T<&lCK*`yn!n_HpnF!vi9HPVc$ zw6gWEDTQx_ef07B1;7b}vrxY}o0j+%VJu9x2P;)|;2yZLIxSO?PqH6cI%tPuF1)G; z6?%|#8~JdDny$d z)+pYf86ee8npmMs@Yx4UtFWOX-CED$Di?x&w;o9aPD7O|ncB_MmLFw0VK#^_hGcx+ z^w`6QN9ybwZBy73>qJ4VY|bl{aKe5i#-56Lt7xKN3_wT?z4(ZtbwX`D+4x8C@uqpmnhl0fS>EVG5U~6XHdEG zdIvNIOj?A$@BJ+(ae;~!Bb&#bf%N{~a&zZx{8Igj==P;f{`=YN!;e*c`vg{9lEb#`jGT1XE25D<$nN2z%m>G>c zy56_&>X=8}{YBxegj}PDxRweH!us+}b~el%Zet%)U>DXN65`b1d{!y%!wEZy?Nswl z)Mm$CROpcmi%~7rhUP-XK`5waY?@>5NNS(w6lc-BHjOdqw3iEp8g9y7TZ!K$wV|aB z^vj3PQ2GaqHpW<<`i6T?5pLnm0%9%{=sP0^mCkyyded_*EWP1Je!QfHc+yCJ2z!?` z!iqvNins!ENKb*UIL75b+g~(|G(D2Vc0@KEU%!j_CYA(Bmuj9lY81+bpvI?F(fE+G zB7d!t3o?Vd8^*_un<_}~qie5H#(b6rBO`*K%a7tO{|w4hPAp^bLKm78=}*Stlb+I1 zh15hc+96<%c23*Xx7q43F`2tP&~~m8-~JRUdsL>`)gn@Mni|V!hqAKwHuxxTAO2LX zf{ZHQ9+pqH{Dc1i*()B(P|dPSRU=(k2oQdML%>%)GfwH| zeP0h6c1$hzYl(}0densXcjIy`u?qW-eIc+u=*NSrZmlqVdtWD^2&c( z2V6(qw7QcKd?r^2GX>)bX|t9E0a$ho`upkA{DFgX7Hk`A;d3R)QE@}-=bZ3t-0NAr zi8=I(+=;o@?i+mA0NFL11KM=Dghup&k3`mYm{-=z9du+28E5(oFdY>+o}mBMqc zCs^FZr3Em`AHY)3Nkeqed}iDiFx(q@bXSOsh4i8YdXaQ%7s{1|xy;eA!Q4Hq+&>qo z>w#O_;#pZq3aGP$X2SlLm>;Z#ng*6isn|8$MpeV-Qo1l=PUhWEdy#xk++JU7e}hq| zdfSWZ`LSKttaW;i(C;Ehz8|`Gd_5hA(lDpI|1SQ%|JfUwiQUHFjI1$}PBSvIy66<7+C+!lb{;4^dn$()LJ; ztfG4f^e^+X;D62`j5=<7eVD zm=Mmgs%U=PNISaz?A_eCp5#&qK_TBfQV__Sp+d_(o9U9ZEL+=5Pi=fdxjJ6n(M}fb zLXaN1_wETt@mzc38AXt%|3<9%0$BDAQYv<6f&J(Ia02N8f|=9hd+3cngjw{Ns~6}& zkk4JgcJSFOlf=K6Bve+xlUgst_`fJ030;5dej!l`!H~Lhh5JFub5MJqM0AYdBM+q@ z8?VYSPvl`68NCPOp9{oM8LD)}v=o~&mk;+%mIe9McUPF7h?RAK{ZGhd>zM0Rs2}p1 zCH-C^#j-;}#|rpke<<~*_-k*jM}xa65sJ?NDTIh-*-t-<(4MHjAuyAMJID||Vk&75`VY*6#6uYyHXOZf zu@HLCCh6^RZtwYZC7R=I{dHFLRYbY1#(GCoU?P&Bgm3<-c|cb;pa*V$z-AiuTCCfm zEld4)td5jhs@#8y&tkcDxOpT49RgThh1{MX4m+;vodFm;+tKSi^aqgeYb*W5kl}JO z*f@)1C+G<$StP7I3I$fU)hO6@=`*^aGhMN{vjot_LwFqPk!BkelP2~N$z$xp!_{>n z!iKMC4)s1sT%)3#I81Zc_+j!bUUmfia$_XG+Z{D+xHOGN3(UE5q$GX=@+Gffbxslv z$ddHqh0s32D6#9$X;%ES|JsEI;MX7LV3d?t@sEOtCbDg?`(IUWW9I#90wWgwi%CLV zJ$b5EY-8Zx#UvYp4%>+|81^HjZ8MSxYzr6EH>^e%i1i>2sQgyO$9snDDYvu$yh(ynZksPiS0lR(MBp9$jR`7D_afHi+4 zla-9NFaRD`zl9GT*v9(K3%P%vWV(6O{w4kMdg9m9={)--r8wT$5_2SNj*W`nxjs8I z4!^E^Sco0(#0vN#U0f{o;QK^(${nc{_?2BcM}k^(JiIlaD&dS=meEaw1@t%vx<3oF z;mJwnXW&xNB>XsFuz_fLW=#+JN|~i>3U(b9bglkeqSH8^dWm=o66vwnlM$_4%wqBU z*)s@Ll5!x%G(51v?*~pVsH{P$cyNs1@Y}j{c%N?Dv=nfkc%ir^g#RW3W0A1=(Etz! z5YN{xt3e7+;U8|}D>n?CIPeLv$FErHZ&%;0Z(MIv{9y!Q$Exd*??A3pV23{@oUkox z{kD)aT-k|m$r=@%XSkaev_c12q@fkl zgH!2=?%8dS91Z0#> zXNt1teOgM1@QJWdiUV+!o-Pdz?E+^{RnZQ@x_5EkS54E>N|bM}S>L;|V`(nptD3?l zb%cYO5DSOUyXQ~WatF3n28=_6%~2R3DdKC{4c3^fG`w#2^Z%qSd=E5B6!M`3g-)8h z_E#f5ZriRsc%Rj*28zzmqv7+5(7mk|e;rLlh8<0lrjR-3^NIN%^H0+2xs4*VEv6-N z5p$G#_aDuKq``y0byaUM2ZZWhG-G?71K5><`qCx=jMOA zYiGFV?s$ZU6JQEo9#QNRukLtJVrBE#~ee-5`eT7GJfaM@s zrJO2*WYF4Z-yJb$)qaSGUV1pV{B=8kd?tCj@f#+ENWF9Um;?M{JhItKf-6sTi$eTk ztBxIjrr-=Ay4i2#n)q+yTJC(*x)6eV37YfX7&*<{u}F`%_)~7xOC;~kOW+R7JKnL- zcfFiyb*h2DN(w1b3cl-fQzU9fbJpGD-9oMsDZhmdjqS@5i}gc%kMoq(9yb^Py7B3| zh_x0s{9ta9+^+E-7&XXoY^!|x)=53_VZ=;p*cCqW=nwaPXlXrIk1Es5Zz6&nsy@O^ zg_7-sYrv>N=2g2C&E*z?NaRQ3MuX191A1O$NeLiH2>itA80L&EQ5GUXq~i9;j?l$& zdi^(VMZBMEUuC96KywAFoc+ta|EA_2ji=V3cZl=$Gd#{DUcn-^A$neV?|ji|AL0?C zI`fgFBkG~Z@DJG6mOfaznk4G+Lht}P`)8*!8CVl$TzvufpyV0v>o!`Cna$_!HgA5; zJWC7sG0^16yRkv9dN-$gHQ|IHw|areY;A2Vw*n4wr_s#FEHTwHKs=DdGLw@GQf$a= zX>;q8^6%rf0a91|8T~Uq2k~-GLza_o$bs{eI{0r-IC7E+M{#jjumOp4A?5#A@`W%j z#r)E%A4?;eoU?p(5F`rz zv`LTL>Dk>tNIW?zi|fIAnQ(h@u3qQwgtMflXf~+5l9IKs@3kA(xc~!t)D%QYT^14r zL-+wR7lllfeKH`}I7Sg#Une1hpuGTh7cGCLUgJ3Mq%!6azb8xiH=0@lL(@oL~6gk7gzx}cb)91(ZCJp|w# zZ*dH#?c{gyY-RI28QkQ#gT{m3@|}*Hz8Xx<6fxr3D~FHj9HGnoIVHr zgWzOJJO@W2$cLso!zhv{w=*_47$g?Nu1wvjq=b{8vC zhAgIoe<3ZyYZ_(<~I5~xktvVDWdz$owho~ zqfIN5T;;T`I1MTJJ4+Fgu$oCnjn4#}FgbJb;quBqr0%YhlmTxlsQ|-+@aqyj;3GlD z)81s=gx7T>P@unhF<1~z-_3jTDYp{JfIUx{M=9mKZ!6Kl@6gY=e@8c#tRyy(#%-?7 zE_x^K7qF8hc_*Gulh5zEs^$OpN}=VX7dTS&=Z!h{TxHybLbl75=i(6law#aJM)xPBuqR~6P*=w^47A{yd6GKtj=U6 zb0M$W`80}f_I6{%-R6aqx#%6_ULXY^E4r`_M+t{KzY3>D{Nn8Uk0)mciqf2abuC#f z4$8!go>}ZopgRZ_XQ1*&_y4%5xwP2Ccd*;3Pa6BWeb3k=C$?PgF@ zZ&>A#No?7T%X<$!J9gmKzLkSk?@*0lgZo(%FlR(>GihFAVxU_;6|A}~Wv z!^GbuFlK zGWokjB0XB{&?$#@T!HV!egX0KY2a0qtSq^zt;YQCw9B>v5D#sD>os!#K;n9iTyN8cM3 zG0|~AD(0shBg;B6U*{+MRN~azhP3Iwd7lXSyY&Sef_1Qim$fLyMMeJ$djA?lomzn` zH9tb_!8;v?G2V7NG2MkXnT%UvHTcKEQj7E;en4h-z@k!{`2oN zO#$T{VE<>sdQEWRXL2a%%8KxS*Hap5`uUy7@{gSH^4F+K?0{6^tJ>6ocjc60Uj*1w zmw)83zusuN1>n)~(mQ@5(|Y$W4**Uu-<9x05f2sv@j;4gCdgwe;{cZik`Gk%m$K^< z$JvmN`6D&~<$f%$E4M=IW?7SAwzX2^*Tr&#BG37gLAK<&cpVoznWS?U8|VhwbDW35 z?*bBir$xG|iL@G3kxjckYmQUL?cwNV)_-Bn(cdna>6@^Ac0r|GrZ*Iwps zy$a!r?HAN8p4b^h}+dNfDw zeR*>YXzw@*JWSNE%8bUzQJ6L4My&dMGKd&l;1r}mWgF3dCIM0zpJc`6mGTN#3NfW6 zOV9bgY556ciNp(uQ?M$~;+hv+mVJqQ1bbXI?8DE3@sY@VYWgq(cf?z%aiQM^;%!xkHTY8Z@LY?V+PWmi0tG|BnPmj zh4egxzi8sd_p$U)jBT^;+KQ3K@{Rv$Ce}(K9QXq$e~Zx5O|M^2@aBMjV*Kn8q&wkB ztRM&r1x*8zn~uZGHJ8>USTx6OQ(|dNldc-a6#89$LXf#ye~kGufrF7_syXGSbbwW& zi-PX#PjXi^KE>@v#+s&-1X(sT$l=#Uw)MTRQP;3YLDXH^!+kysKl}vg--esdI87S= zOZOX827u@C^c`FG+j7UYzCJZs`q&5A7SL~vTFF7)tx zKSU_qp7k5cZuuNwVPF3Z_Vq&;>hMFbC@NX-ryNCwOhDj6o{+~O9E=QOyb$7&;un%v z10(O}qbltZD=yY%Lk#SY}2A_>L)u=tYZIYJIdYJjVn6D!}^MF3o+>0!Ey#=8C2 z^0BS8B3jnM`L{)4UYpSqb2()q<^J3|hOlwi5!}lJU~k<;r>47VIV!-5#Po4Q*6y25 zfx;#jS3nf^iY1ck)^kI>#Xvov8&0|dO3#2S)tVvrSxr#wDGZlhE+J}g2u#~VtUgPD zUIp?7-c*JOyw?|I(!Z3C3TiC`k}d7ejZkdym!fSQJF)t^i%c?*+wlR^bPzb@%8|d# zKqDKZ?-(!|FQQ7HvvANO!6CKL614K#`@p z{f0A3>4KS5u0$s1i18x1doJ@5mWBOHJ15;x$h*vuYydikq;(k;B({f`dV4V zuZ*{Bk=<3QJ7689d!7MHH~Ik#y7)H8sYQFdZ>^EkJN73x7YfZIo#Z#He(oogH(q|c zQa|Rn{;_sTR|D}7Euq6b=$OC5riOhuN<|Y_bpH-x*J_rIxQ2GMhF#7DWDg2PD6qGx zQrmp-4DGvJ!6^j@xg=&m0r!SbwY1?$pE#E`m%#Ma`Hjf97N*5&?aESbGsnJ!nb@P} zAeiSSPyd{hV?NXp!tM}9>V*dG>{d*i5dEF-=~z@VpR!W-H&@+`+-9?8?{?4Kw_h@^ z_WP{>JWN&9oB+eZic7+%XR!rSbXiCRd1s(EUHRp-vNk`d^DzyQ+WPrVc~wEb;LXYd zd;+hHoqi-_{Rpp17nL%;&0n}wNG9r%O&R`+k(x#13NAAl*M?KM=j3|S&ALTDd1v6& zu9WSf+9&~4yrzobEkrfu2B3-CSzjT$EyIo66|17b_XqnEbVk+|%m46PzH#y}>NCeG zM+&E3ME(oq%k2b{7s2T04_4f-@SZ4cvq>iPJubQpvG=4^iru3kkX@+2s(a zi@&&8xDc5>Y(1GMIhI_2an+?-FvNR^Vp)kA4ZDK(u+sWq-6+k|D!_4u){cjL-1QY( z+ZZh5D`T_MepFTHJk|C)s;|9hG^=ONSkvt z>K|RTc=qfx%on{)n*6s2DB2Fa?dJ$rL7#Y^m!4&h$~4ex`MtmRTAxE^9Dj1gqqR8~ zpRMciEWTgh!p!gQhJ?P*T2X2!}X%*TV) zt8-qUIO6OakS{VDe8++H3ARlzVA(|%fsQTv6y+j$6on-dV+Uw)(Y%|S7Pi`|-fRo5PqUq<0Y1VGM zg?}FNV+e$z_CY`{8ql9B-^Tj5HSn1<4_e*29xlk)a$2mOuUDm?g~jm4`eZsLIVp4N z&Ibr#Hot{l9bA~y$`ri-V|+6?7`4|K-!VZ}75UpAAFKTVbA4^EQ0H`%2&~c*bJ)_R z;P+kRXImO{2ua;$YDhjI2Xj)Nf2$n^^sSgY72IzjX)LEdQ9c}--#^#>lr#KdFXUIp z6sEL2QTZ|VE}Dr7Cn*Utu%6lsT$sz%SI*c z1!nta;JC)JqA8qsF2Xj$(s$@87T~-vcDA>Pxt&0^$8i z8Hl8{%@_Q~%xJrUYk33RH+K_cpZale;(j`8`%JB7WW-5rlw%XF=F)PfD2b=%1&%|m z#$*-3DEy8JK?R6e%PoiGnrN|OCDk;!HJ>)kpIu=qzx#Iry)}_* zGW=Upt#c17_f?*Io%o@=&hLZvG4{6?Tm1sZbtSs;Pg)~XKd}53It}3eo7<0P^YiB( z%)P=WuNP@EOxRu``bBL&5Jz8I$yo1pK^unt5O{fu_#iB+Gw*k$D=e* zH}A77t|p1IM(o@`T^Lq_!T_jK901ywGxdWEe+ky8g8JP+eAhDFvh?)y%H*@}-^3M& zH(Iy0KPZC?&B$Sbb)nOF{OToeX8Mc3>=uS6X~9@Z+>2M2$@>qih5tgQR_i3`(;t>eMMb}tXh+|T&lp$6&mAgENStx+b}H z%+k6J&%?@e(qMQFl^?Fu++Wv`YG7w9a-7tZyZkUG)>oTP!)Rqa?y+ne1YnD zVbn6bcPu0eyOs$wcA^`Kp}ZQ7!-V{`gfQdPxAl_~7s@#r7Af~u;%hf2#N~DRg2O{9*QKTwe8W& za_#R+r9;^d`i4RUmVLZS5r3Fnb7~^2>r7c7VFC<^iA7p(a-x=|LTaSuPDyyt-(Thi zpH3Ot=@1l@d*Kh)R*I0R;2|${Gwl+%E#)`RyaTxHcv$rYy0b_FrM7|Lv#o!I+w6ov?fEuy3zj_$ z)D+Y64atvB3V|M|@L4&kAME%FJ$~cOO{N8uKDZQM10l@mI6>LiEIkinx>6m$%0AY_ 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 literal 0 HcmV?d00001 diff --git a/public/icons/apple-touch-icon-120x120-precomposed.png b/public/icons/apple-touch-icon-120x120-precomposed.png new file mode 100644 index 0000000000000000000000000000000000000000..1fae26b22d04fe1f9948801a3b57af42a515a74f GIT binary patch literal 4387 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 O00000{{R3FC5Sl00004XF*Lt006O% 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 literal 0 HcmV?d00001 diff --git a/public/icons/apple-touch-icon-60x60-precomposed.png b/public/icons/apple-touch-icon-60x60-precomposed.png new file mode 100644 index 0000000000000000000000000000000000000000..26178b4be0296a65400083b191a869ce635916d5 GIT binary patch literal 1826 zcmV+-2i^FIP)Px#mQYMoMNDaN9zIAscxg9oVJS~YHECWtb7dz>MI=K$ zE?ZJQeQh#hSub5wL4$EbiFPYgOD0J}95_Qqk9r|RO(afINtJypZeBEnXE=~+Xz+qN zoNyU1Jx-v8Oq+pIuZ=&ZcQ1QjWYeHVwt!K=iczSESh|#a?Xzv)rzTxeT*aAm=dLJc zS4hTfU*3OF(0E+Xltiv=jP$)>xGN)Uxv0`91QG!hpC6=f8d|NrOSt*UMyzD%E!8BO@Ox0Ws%%c3>;nr=3m z^?JQrE*A^WbA8`;oH;MY!57!_7K_DliA&a-&Gc(xnKdEugC%suOK{iq2wpObHP!fJ zGqAWlqf7>}m?v?gE!R&z*(J+DmB=s*cRT#UIMms?ajxm724=k3Ng%(M6p`pcUBWVI}z z&*!D8F3+2&haCA2dTI@(J&#!AsYmaB|K1rFI{2imQNY4#Sw?HZF9ZHTTmNH6=%sBh z{6Atpjp-il6V2@LK7S+j20b!O5hSY#Dx~)8%rk(i56DS zsx8minO2sai#i8GXHpZ<%nAz%nuiK_+ zUR{k2{xGycGugbz9UaX@xsb{H=(6Ex>r$_!0*-bbPRiG zOAF6e;o)=<6mfR1m&#XY@KELW+)?BO;nnT1BWdg~z4W|+z z-9ufb5$*NmBZ-@6<}+_qi%+z~tM} zu(;l7Sf$E$&U;}}xy!y)eu6*b>Qqy$L-LK3S*A00huft(!P&}j-Rrp{-}Dlr<&y(q3?CA(q`xWM73yhq4$u-T9u|^UG{1~FgsRw?6=RJ&UD=1|xtG>GK#ps8lL7 zaPSE~38p%$dD=1qB;~u5`5dy?;kTc#H-hiaGj(UW%9-KjE~wQ|eQxgJ@&TiPjqN)o z*O|gjIOM$F7z+CgXj$Fsf5H`JL0h0)DfVzvqLa|_B~6AdS7!{uTIS#wb~OCNn6B>? ziVgOa<^hJmZk1;|SI%vX8CaG#fmc6aJQI1&*7vq(2u_keSjbuTY8p7MTACDBncwqI z_?XhPA$RZQmE}3@LXN*_5NumwKBf8YHy9&3pzDESgjUU&8j2@CR zc=Vi>4x2OVZ)q9t96O;oqf3&t?a0v~_b`ni90`2eW?Z}{#4C+zSv!vBM%e5seV*{? z&w@W_QF~E!@KMXgBNzulJ%%`FhHc}0y_Opg8})4L$eB!!UWVt`KN{}6?u`14qU zMJAbz{aqh2!$umV8sP1QPkCfHiil-2wq4^|&us&H-%Llwx}5L$7q+0di#s|BTK#D? z2)%EnBU0LK>C0vlQR$5=zHGh0^+G>l4g4)I(ciS#fngc;LoG~C$=M*iS^|#rvcEMw zuz(|XI6Nz>0Q1sSj~#h2oaxuW{{ZR| z7GF+=Egt{?09SfcSaechcOY6Cgx@G{a;ABePT>%h=S&#LUDT#0SfONT5nC0O}VJbn-$q Ql>h($07*qoM6N<$f{#IH;{X5v literal 0 HcmV?d00001 diff --git a/public/icons/apple-touch-icon-60x60.png b/public/icons/apple-touch-icon-60x60.png new file mode 100644 index 0000000000000000000000000000000000000000..c23ddf35aff0f8b5bbcb40c4e83800895c2268df GIT binary patch literal 1646 zcmV-!29f!RP)Px#bx=%HMK*0=KYng2R!%NmQ!{2;LWOfhjCeV6WgR<4 zJa}nIm3=K@Rv<%5Crw5gGCob7gd|N;QmTqSr*k7hJv*9jD{NeN>#{V3XE%>(P{E00 z)1hwQs717XTECY{$Z%%yfLzU&Fn?lJwUI4$UR>LKYV3xJ^Si}D=&Aq!1vE)SK~zY` zeV2)HvpNig9m1Lr0xr}hp<(a)KjJ--Y#?p_tC>!-@Ntf1StzRbkhmd&AUL0|VW?D+ zWQY?*mL*A|R2W{b=QFOrbwU({`Y8Uv5*S4howg)R{btFrppRjA$bS(h>n>|dE1zg)*)q!#(o!()yzjIaXIK(_64@Q?INJ8{ z4V=Y!;n}uqb=wmiG0TphL)&SNy4KzB+`R9!hhbW6ETQZ2($>@zcC_8Htc-iwORG&d ziop($eGbd=dUf@f88ePqre6z!)UkA;H)*wHI=)np#lB*#FGP(^p%^v2qtrd|Eod?> zIa3GW=^-fAiOa_^qC;f}=9PDZKc|DV+O`KUe+ap{*j$t-2w)-AyopK!%{O!aXGT$#}#!?CeT-!BFSbng1Tf0;-U zZ#stKXouY%Bh>^WV!Nz;zrN;qws_I6+C=pEXsYKh$zG$x0f#Qv4mQ>#nO=pl^{b~B zgGpw50r+#6s;yAeR+-iGcviJB5pG)PZduob|KPw@u~H|x@kFrCcc``_L2!$8QIoEa zGSmjCE-yPb_e+D^%}ChiWAckup$ZT1XRGaq%TxvRre4{yX;gE$H(~e&v-AevslHn5 zvHBavrviWdhA$pA28O1nY35pN>#<=Nm0tgb6G=9~U%IwtR!vopdZA?XNZO+3OWZWZ zm>VZ_(m)0-Z@TE0?u~qgbj50cHu%&%ACoYnHwM`>M(&Bhpn6h90 z0}q-TKr~Z%uA26zTd7cSKdSof@e9s27@e!*WzQPtVQvs~Ff^J1PDa<_ME?_}(>`?C z&XyWLi?fGaXsH)yG^3VV#qc+bzDceAiAo{Xq8XrFo@VYOwH}~4wC$k_zQGe6ciev9 zHe?%#xn>)xA88k~CZKFHIClF)Xve?YJ032^*V z8NO9so(V@E)7)mMS#OR(UV}W%_UE00Wzi~4b9x4lxLdk+3|X*c?qC@bLgnTV@(tMI zK66|%cihy9|Hl%oU<^^w+2DCdb}>DGDXp0n@pMc|@faWW4cMa_mume8_1 z`6VA=NGEx|;{nNY06nv83icI%&n%OE6rB70)u99A;3p54&*`c(~l?Vow#p`B7LV zgKgZ+aN^O!)NUpiJE|UD9@8tVQj= z7p^>qZKJ2m_@&?I+?87HNkkYQerp5-AL&z+7tFXyp*j_qwk$orvp1Ubk96*n{IS6< z_jnuZzmR0!{y5OUJzRL@6Cgx@G{a;A sBePT>%h=S&#LUDT#0SfONT5nC0O}VJbn-$ql>h($07*qoM6N<$f;i_DZ~y=R literal 0 HcmV?d00001 diff --git a/public/icons/apple-touch-icon-76x76-precomposed.png b/public/icons/apple-touch-icon-76x76-precomposed.png new file mode 100644 index 0000000000000000000000000000000000000000..1e0a2f87f53d7d2f9efc093ecd5de4b339582a77 GIT binary patch literal 2494 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 IM6N<$g0TjJ{{R30 literal 0 HcmV?d00001 diff --git a/public/icons/apple-touch-icon-76x76.png b/public/icons/apple-touch-icon-76x76.png new file mode 100644 index 0000000000000000000000000000000000000000..954eb27934ff2ddbe078ad0d7c583d51ffde1fca GIT binary patch literal 2210 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 literal 0 HcmV?d00001 diff --git a/public/icons/apple-touch-icon-precomposed.png b/public/icons/apple-touch-icon-precomposed.png new file mode 100644 index 0000000000000000000000000000000000000000..b3fc31af1515b4464262dad02e53ec0b28a7c97e GIT binary patch literal 8553 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 literal 0 HcmV?d00001 diff --git a/public/icons/browserconfig.xml b/public/icons/browserconfig.xml new file mode 100644 index 0000000..0502cdd --- /dev/null +++ b/public/icons/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #00aba9 + + + diff --git a/public/icons/favicon-16x16.png b/public/icons/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..821bb9fab1c6d189d8c29ad22d71779e8c877f8c GIT binary patch literal 444 zcmV;t0YmPx#OHfQyML~seBS}vxR#8*1jWcIkJ$-CPr*=1PVj)6C zJ9TFrIzcU8R5XKUMvi)6&6z1qM_JT(O{RuazmH0pfIE|IMz(TO#fo0Sm~Y*s@*>k9 z00022NklwY~RLeHLB|Be#fvQW%`Rv7jqV)hLl9GsX z-rljMq(>|(S<@>=wV1?-)0q7aJNpSa>P>oU00012dQ@0+Qek%>aB^>EX>4U6ba`-P zAZc)PV*mhnoa6Eg2ys>@D9TUE%t_@^00ScnE@KN5BNI!L6ay0=M1VBIWCJ6!R3OXP m)X2ol#2my2%YaCrN-hBE7ZG&wLN%2D0000Px#Y*0*8MK)|;AVW+fO;RgaQ9E~N9Xm!ja%4Y#ZZT(C zE?-tAOhrweghGXKMT&N6+@@5ykv5EKM~`_LGd?R+Oi7h~BSJkqm~cR(b2EWuN3ee| zcVcAHpjgI}UfX|BtBX*~b$#fuNV;!j@PBOPoUxNqGXMYrB}qg(^E*sxm?cN+0V2^Kk7h#_Qz+ek|)@xeIy{TtN8i zbq2G)c5O^>>)i!$f*n>LLg}v^=d~{*h2Z-kWl*}~&@YB)jNyby%txZIB%lOP=DKAd zlD&ZdAQ}cnRT2Po1A#0e&hxcjI+_Q_iRc61?C_6h!WifN2l~w$T+`>d-!If}{lCDr z_Um$;UUgmY=e3|<_y!JGZvB3mU`#aUoBho31+*~+JiC7I9#8qb;Z9XNaLos>ZH=J_ ze(2W?FJW+@s%70;&DXpES%|ET#t(3DD9>b({RKK}Wl)y8 zB<2iBm;q4~LRM)o)4Nc5WQSrZnx@HE7edOKXFI(tWN5AtAg~0DU|AMYb3#RF6Cgx@G{a;ABePT>%h=S&#LUDT g#0SfONT5nC0O}VJbn-$ql>h($07*qoM6N<$g1DA>x&QzG literal 0 HcmV?d00001 diff --git a/public/icons/favicon.ico b/public/icons/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a19896dce3da7a6ce7d31812b23ae657831ddd6c GIT binary patch literal 7406 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 literal 0 HcmV?d00001 diff --git a/public/icons/safari-pinned-tab.svg b/public/icons/safari-pinned-tab.svg new file mode 100644 index 0000000..ad6d511 --- /dev/null +++ b/public/icons/safari-pinned-tab.svg @@ -0,0 +1,252 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + + + + + + + + + diff --git a/public/icons/site.webmanifest b/public/icons/site.webmanifest new file mode 100644 index 0000000..c008ad1 --- /dev/null +++ b/public/icons/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "Minecluster", + "short_name": "Minecluster", + "icons": [ + { + "src": "/icons/android-chrome-192x192.png?v=feb4-24-mineblock", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/icons/android-chrome-512x512.png?v=feb4-24-mineblock", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#249c6b", + "background_color": "#249c6b", + "display": "standalone" +} From 1741356e5fb4d8269b7e20cbbf1ce79b8f7815f2 Mon Sep 17 00:00:00 2001 From: dunemask Date: Fri, 9 Feb 2024 16:37:45 +0000 Subject: [PATCH 14/38] [HOTIX] Get Backups working (#15) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/15 --- lib/k8s/server-create.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/k8s/server-create.js b/lib/k8s/server-create.js index 3d4618a..004fd03 100644 --- a/lib/k8s/server-create.js +++ b/lib/k8s/server-create.js @@ -66,6 +66,8 @@ function createBackupSecret(serverSpec) { `secret_access_key = ${backupKey}`, `endpoint = ${backupHost}`, `acl = private`, + `no_check_bucket = true`, + `no_check_container = true` ].join("\n"); backupYaml.data["rclone.conf"] = Buffer.from(rcloneConfig).toString("base64"); return backupYaml; From 2fe79d0c576b6e3b96d67f60d811af68ed5c1f2b Mon Sep 17 00:00:00 2001 From: dunemask Date: Sat, 10 Feb 2024 04:55:12 +0000 Subject: [PATCH 15/38] [FEATURE] New Deploy Pattern (#16) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/16 --- .gitea/workflows/deploy-edge-proxy.yml | 23 +++++++++++++++++++ .../{deploy.edge.yml => deploy-edge.yml} | 10 ++++---- 2 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 .gitea/workflows/deploy-edge-proxy.yml rename .gitea/workflows/{deploy.edge.yml => deploy-edge.yml} (72%) diff --git a/.gitea/workflows/deploy-edge-proxy.yml b/.gitea/workflows/deploy-edge-proxy.yml new file mode 100644 index 0000000..3551003 --- /dev/null +++ b/.gitea/workflows/deploy-edge-proxy.yml @@ -0,0 +1,23 @@ +name: Deploy Edge Proxy +run-name: ${{ gitea.actor }} Deploy Edge Proxy +on: + push: + branches: [ master ] + +env: + GITEA_TOKEN: ${{ secrets.ELYSIUM_ORG_READ_TOKEN }} + KUBECONFIG_BASE64: ${{ secrets.KUBECONFIG_USW_EDGE }} + GARDEN_DEPLOY_ACTION: minecluster-proxy + + +jobs: + deploy-edge: + steps: + - name: Oasis Setup + uses: https://gitea.dunemask.dev/elysium/oasis-action@master + with: + gitea-token: ${{ env.GITEA_TOKEN }} + kubeconfig: ${{ env.KUBECONFIG_BASE64 }} + - name: Deploy to Edge env + run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-edge + working-directory: ${{ env.OASIS_WORKSPACE }} \ No newline at end of file diff --git a/.gitea/workflows/deploy.edge.yml b/.gitea/workflows/deploy-edge.yml similarity index 72% rename from .gitea/workflows/deploy.edge.yml rename to .gitea/workflows/deploy-edge.yml index bae115f..fc9ed65 100644 --- a/.gitea/workflows/deploy.edge.yml +++ b/.gitea/workflows/deploy-edge.yml @@ -1,13 +1,12 @@ -name: Deploy Edge -run-name: ${{ gitea.actor }} Deploy Edge +name: Deploy USW-MC +run-name: ${{ gitea.actor }} Deploy USW-MC on: push: branches: [ master ] env: GITEA_TOKEN: ${{ secrets.ELYSIUM_ORG_READ_TOKEN }} - KUBECONFIG_BASE64: ${{ secrets.KUBECONFIG_USW_EDGE }} - OASIS_PROD_CONFIG: ${{ secrets.OASIS_PROD_CONFIG }} + KUBECONFIG_BASE64: ${{ secrets.KUBECONFIG_USW_MC }} GARDEN_DEPLOY_ACTION: minecluster # Additional Deploy Envars POSTGRES_PROD_PASSWORD: ${{ secrets.POSTGRES_PROD_PASSWORD }} @@ -22,7 +21,6 @@ jobs: with: gitea-token: ${{ env.GITEA_TOKEN }} kubeconfig: ${{ env.KUBECONFIG_BASE64 }} - oasis-prod-config: ${{ env. OASIS_PROD_CONFIG }} - name: Deploy to Edge env - run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-edge + run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-mc working-directory: ${{ env.OASIS_WORKSPACE }} \ No newline at end of file From 4959d6c1feb704246f55ddb3ad1db8341717b0b7 Mon Sep 17 00:00:00 2001 From: dunemask Date: Sat, 10 Feb 2024 07:22:48 +0000 Subject: [PATCH 16/38] [FEATURE} Change extra ports from ClusterIp to LoadBalancer (#17) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/17 --- lib/k8s/configs/extra-svc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/k8s/configs/extra-svc.yml b/lib/k8s/configs/extra-svc.yml index 447a169..dee717f 100644 --- a/lib/k8s/configs/extra-svc.yml +++ b/lib/k8s/configs/extra-svc.yml @@ -20,4 +20,4 @@ spec: selector: app: changeme-app sessionAffinity: None - type: ClusterIP + type: LoadBalancer From 0a0f9c84636feee576ddb4a72d959b8adfeac0b7 Mon Sep 17 00:00:00 2001 From: dunemask Date: Sun, 11 Feb 2024 03:57:01 +0000 Subject: [PATCH 17/38] [FEATURE] Several QOL Updates (#18) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/18 --- lib/controllers/file-controller.js | 16 + .../sub-controllers/console-controller.js | 10 +- lib/k8s/server-create.js | 2 +- lib/k8s/server-files.js | 14 +- lib/routes/files-route.js | 2 + lib/routes/middlewares/auth-middleware.js | 1 - lib/storage/s3-integration.js | 34 - package-lock.json | 954 +++++------------- package.json | 15 +- .../files/ChonkyStyledFileBrowser.jsx | 42 - src/components/files/FilePreview.jsx | 17 +- src/components/files/MineclusterFiles.jsx | 74 +- src/components/servers/RconDialog.jsx | 17 +- src/components/servers/RconView.jsx | 52 +- src/css/rcon.css | 3 +- src/util/queries.js | 7 + 16 files changed, 432 insertions(+), 828 deletions(-) delete mode 100644 lib/storage/s3-integration.js delete mode 100644 src/components/files/ChonkyStyledFileBrowser.jsx diff --git a/lib/controllers/file-controller.js b/lib/controllers/file-controller.js index 83e1ab5..e5e7075 100644 --- a/lib/controllers/file-controller.js +++ b/lib/controllers/file-controller.js @@ -4,6 +4,7 @@ import { listServerFiles, removeServerItem, uploadServerItem, + moveServerItems, } from "../k8s/server-files.js"; import { sendError } from "../util/ExpressClientError.js"; import { checkAuthorization } from "../database/queries/server-queries.js"; @@ -79,3 +80,18 @@ export async function getItem(req, res) { }) .catch(sendError(res)); } + +export async function moveItems(req, res) { + const serverSpec = req.body; + if (!serverSpec.id) return res.status(400).send("Server id missing!"); + if (!serverSpec.destination) + return res.status(400).send("Destination required!"); + if (!serverSpec.origin) return res.status(400).send("Origin required!"); + if (!serverSpec.files || !Array.isArray(serverSpec.files)) + return res.status(400).send("Files required!"); + const authorized = await checkAuthorization(serverSpec.id, req.cairoId); + if (!authorized) return res.sendStatus(403); + moveServerItems(serverSpec) + .then(() => res.sendStatus(200)) + .catch(sendError(res)); +} diff --git a/lib/controllers/sub-controllers/console-controller.js b/lib/controllers/sub-controllers/console-controller.js index 6713960..b272f03 100644 --- a/lib/controllers/sub-controllers/console-controller.js +++ b/lib/controllers/sub-controllers/console-controller.js @@ -26,9 +26,13 @@ export async function webConsoleLogs(socket) { const log = new k8s.Log(kc); const logStream = new stream.PassThrough(); - logStream.on("data", (chunk) => - socket.emit("push", Buffer.from(chunk).toString()), - ); + var logstreamBuffer = ""; + logStream.on("data", (chunk) => { + const bufferString = Buffer.from(chunk).toString(); + if (!bufferString.includes("\n")) return (logstreamBuffer += bufferString); + const clientChunks = `${logstreamBuffer}${bufferString}`.split("\n"); + for (var c of clientChunks) socket.emit("push", c); + }); log .log(namespace, mcsPods[0], containerName, logStream, { follow: true, diff --git a/lib/k8s/server-create.js b/lib/k8s/server-create.js index 004fd03..30e21fa 100644 --- a/lib/k8s/server-create.js +++ b/lib/k8s/server-create.js @@ -67,7 +67,7 @@ function createBackupSecret(serverSpec) { `endpoint = ${backupHost}`, `acl = private`, `no_check_bucket = true`, - `no_check_container = true` + `no_check_container = true`, ].join("\n"); backupYaml.data["rclone.conf"] = Buffer.from(rcloneConfig).toString("base64"); return backupYaml; diff --git a/lib/k8s/server-files.js b/lib/k8s/server-files.js index ce29a02..efa32d8 100644 --- a/lib/k8s/server-files.js +++ b/lib/k8s/server-files.js @@ -86,12 +86,22 @@ export async function uploadServerItem(serverSpec, file) { }).catch(handleError); } -export async function getServerItem(serverSpec, writableStream) { +export async function getServerItem(serverSpec) { const { path } = serverSpec; - const ds = new Transform({ transform: (c, e, cb) => cb(null, c) }); + const ds = new Transform({ transform: (c, _e, cb) => cb(null, c) }); pathSecurityCheck(path); const ftpTransfer = useServerFtp(serverSpec, async (c) => { await c.downloadTo(ds, path); }).catch(handleError); return { ds, ftpTransfer }; } + +export async function moveServerItems(serverSpec) { + const { destination, origin, files } = serverSpec; + useServerFtp(serverSpec, async (c) => + Promise.all( + files.map((f) => c.rename(`${origin}/${f}`, `${destination}/${f}`)), + ), + ).catch(handleError); + return files; +} diff --git a/lib/routes/files-route.js b/lib/routes/files-route.js index 1092a81..d82f780 100644 --- a/lib/routes/files-route.js +++ b/lib/routes/files-route.js @@ -6,6 +6,7 @@ import { listFiles, uploadItem, getItem, + moveItems, } from "../controllers/file-controller.js"; import cairoAuthMiddleware from "./middlewares/auth-middleware.js"; @@ -18,6 +19,7 @@ router.post("/list", listFiles); router.post("/folder", createFolder); router.delete("/item", deleteItem); router.post("/item", getItem); +router.post("/move", moveItems); router.post("/upload", multerMiddleware.single("file"), uploadItem); export default router; diff --git a/lib/routes/middlewares/auth-middleware.js b/lib/routes/middlewares/auth-middleware.js index 0a73234..8f4318f 100644 --- a/lib/routes/middlewares/auth-middleware.js +++ b/lib/routes/middlewares/auth-middleware.js @@ -17,7 +17,6 @@ const cairoAuthenticate = async (token) => { // Middleware const cairoAuthHandler = (req, res, next) => { if (!req.token) return res.status(401).send("Cairo auth required!"); - VERB("AUTH", `${MCL_CAIRO_URL}/api/user/info`); cairoAuthenticate(req.token) .then((authData) => (req.cairoId = authData.id)) .then(() => next()) diff --git a/lib/storage/s3-integration.js b/lib/storage/s3-integration.js deleted file mode 100644 index 34ed5d5..0000000 --- a/lib/storage/s3-integration.js +++ /dev/null @@ -1,34 +0,0 @@ -import multer from "multer"; -import multerS3 from "multer-s3"; -import AWS from "aws-sdk"; - -// Environment Variables -const { - MCL_S3_ENDPOINT: s3Endpoint, - MCL_S3_ACCESS_KEY_ID: s3KeyId, - MCL_S3_ACCESS_KEY: s3Key, -} = process.env; - -export const mcl = "mcl"; - -export const s3 = new AWS.S3({ - endpoint: s3Endpoint, - accessKeyId: s3KeyId, - secretAccessKey: s3Key, - sslEnabled: true, - s3ForcePathStyle: true, -}); - -const storage = multerS3({ - s3, - bucket, - contentType: multerS3.AUTO_CONTENT_TYPE, - metadata: (req, file, cb) => { - cb(null, { fieldName: file.fieldname }); - }, - key: (req, file, cb) => { - cb(null, Date.now().toString()); - }, -}); - -export const upload = multer({ storage }); diff --git a/package-lock.json b/package-lock.json index f506448..af76135 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "minecluster", - "version": "0.0.1-alpha.0", + "version": "0.0.1-alpha.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "minecluster", - "version": "0.0.1-alpha.0", + "version": "0.0.1-alpha.1", "license": "LGPL-2.1", "dependencies": { "@kubernetes/client-node": "^0.20.0", - "aws-sdk": "^2.1514.0", + "aws-sdk": "^2.1555.0", "basic-ftp": "^5.0.4", "bcrypt": "^5.1.1", "chalk": "^5.3.0", @@ -18,34 +18,35 @@ "express-bearer-token": "^2.4.0", "figlet": "^1.7.0", "js-yaml": "^4.1.0", - "moment": "^2.29.4", + "moment": "^2.30.1", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "pg-promise": "^11.5.4", "postgres-migrations": "^5.3.0", "rcon-client": "^4.2.4", - "socket.io": "^4.7.2", + "react-dropzone": "^14.2.3", + "socket.io": "^4.7.4", "uuid": "^9.0.1" }, "devDependencies": { - "@emotion/react": "^11.11.1", + "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", - "@mui/icons-material": "^5.14.19", - "@mui/material": "^5.14.20", - "@tanstack/react-query": "^5.12.2", + "@mui/icons-material": "^5.15.9", + "@mui/material": "^5.15.9", + "@tanstack/react-query": "^5.20.1", "@vitejs/plugin-react": "^4.2.1", "chonky": "^2.3.2", "chonky-icon-fontawesome": "^2.3.2", "concurrently": "^8.2.2", - "nodemon": "^3.0.2", - "prettier": "^3.1.0", + "nodemon": "^3.0.3", + "prettier": "^3.2.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-quill": "^2.0.0", - "react-router-dom": "^6.20.1", - "react-toastify": "^9.1.3", - "socket.io-client": "^4.7.2", - "vite": "^5.0.7" + "react-router-dom": "^6.22.0", + "react-toastify": "^10.0.4", + "socket.io-client": "^4.7.4", + "vite": "^5.1.1" } }, "node_modules/@ampproject/remapping": { @@ -1293,10 +1294,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz", - "integrity": "sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==", - "dev": true, + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1407,15 +1407,15 @@ "dev": true }, "node_modules/@emotion/react": { - "version": "11.11.1", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", - "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "version": "11.11.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz", + "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==", "dev": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.11.0", "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", + "@emotion/serialize": "^1.1.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", "@emotion/utils": "^1.2.1", "@emotion/weak-memoize": "^0.3.1", @@ -1431,9 +1431,9 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", - "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", + "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", "dev": true, "dependencies": { "@emotion/hash": "^0.9.1", @@ -1505,246 +1505,6 @@ "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", "dev": true }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.8.tgz", - "integrity": "sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.8.tgz", - "integrity": "sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.8.tgz", - "integrity": "sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.8.tgz", - "integrity": "sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.8.tgz", - "integrity": "sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.8.tgz", - "integrity": "sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.8.tgz", - "integrity": "sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.8.tgz", - "integrity": "sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.8.tgz", - "integrity": "sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.8.tgz", - "integrity": "sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.8.tgz", - "integrity": "sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.8.tgz", - "integrity": "sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.8.tgz", - "integrity": "sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.8.tgz", - "integrity": "sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.8.tgz", - "integrity": "sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/linux-x64": { "version": "0.19.8", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.8.tgz", @@ -1761,128 +1521,32 @@ "node": ">=12" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.8.tgz", - "integrity": "sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.8.tgz", - "integrity": "sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.8.tgz", - "integrity": "sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.8.tgz", - "integrity": "sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.8.tgz", - "integrity": "sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.8.tgz", - "integrity": "sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@floating-ui/core": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.2.tgz", - "integrity": "sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", "dev": true, "dependencies": { - "@floating-ui/utils": "^0.1.3" + "@floating-ui/utils": "^0.2.1" } }, "node_modules/@floating-ui/dom": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", - "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.1.tgz", + "integrity": "sha512-iA8qE43/H5iGozC3W0YSnVSW42Vh522yyM1gj+BqRwVsTNOyr231PsXDaV04yT39PsO0QL2QpbI/M0ZaLUQgRQ==", "dev": true, "dependencies": { - "@floating-ui/core": "^1.4.2", - "@floating-ui/utils": "^0.1.3" + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.1" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz", - "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", "dev": true, "dependencies": { - "@floating-ui/dom": "^1.5.1" + "@floating-ui/dom": "^1.6.1" }, "peerDependencies": { "react": ">=16.8.0", @@ -1890,9 +1554,9 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", - "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==", "dev": true }, "node_modules/@formatjs/ecma402-abstract": { @@ -2200,17 +1864,17 @@ } }, "node_modules/@mui/base": { - "version": "5.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.26.tgz", - "integrity": "sha512-gPMRKC84VRw+tjqYoyBzyrBUqHQucMXdlBpYazHa5rCXrb91fYEQk5SqQ2U5kjxx9QxZxTBvWAmZ6DblIgaGhQ==", + "version": "5.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.36.tgz", + "integrity": "sha512-6A8fYiXgjqTO6pgj31Hc8wm1M3rFYCxDRh09dBVk0L0W4cb2lnurRJa3cAyic6hHY+we1S58OdGYRbKmOsDpGQ==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.4", - "@floating-ui/react-dom": "^2.0.4", - "@mui/types": "^7.2.10", - "@mui/utils": "^5.14.20", + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.13", + "@mui/utils": "^5.15.9", "@popperjs/core": "^2.11.8", - "clsx": "^2.0.0", + "clsx": "^2.1.0", "prop-types": "^15.8.1" }, "engines": { @@ -2232,9 +1896,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.14.20", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.20.tgz", - "integrity": "sha512-fXoGe8VOrIYajqALysFuyal1q1YmBARqJ3tmnWYDVl0scu8f6h6tZQbS2K8BY28QwkWNGyv4WRfuUkzN5HR3Ow==", + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.9.tgz", + "integrity": "sha512-CSDpVevGaxsvMkiYBZ8ztki1z/eT0mM2MqUT21eCRiMz3DU4zQw5rXG5ML/yTuJF9Z2Wv9SliIeaRAuSR/9Nig==", "dev": true, "funding": { "type": "opencollective", @@ -2242,12 +1906,12 @@ } }, "node_modules/@mui/icons-material": { - "version": "5.14.19", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.14.19.tgz", - "integrity": "sha512-yjP8nluXxZGe3Y7pS+yxBV+hWZSsSBampCxkZwaw+1l+feL+rfP74vbEFbMrX/Kil9I/Y1tWfy5bs/eNvwNpWw==", + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.9.tgz", + "integrity": "sha512-6tLQoM6RylQuDnHR6qQay0G0pJgKmrhn5MIm0IfrwtmSO8eV5iUFR+nNUTXsWa24gt7ZbIKnJ962UlYaeXa4bg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.4" + "@babel/runtime": "^7.23.9" }, "engines": { "node": ">=12.0.0" @@ -2268,20 +1932,20 @@ } }, "node_modules/@mui/material": { - "version": "5.14.20", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.20.tgz", - "integrity": "sha512-SUcPZnN6e0h1AtrDktEl76Dsyo/7pyEUQ+SAVe9XhHg/iliA0b4Vo+Eg4HbNkELsMbpDsUF4WHp7rgflPG7qYQ==", + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.9.tgz", + "integrity": "sha512-kbHTZDcFmN8GHKzRpImUEl9AJfFWI/0Kl+DsYVT3kHzQWUuHiKm3uHXR1RCOqr7H8IgHFPdbxItmCSQ/mj7zgg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.4", - "@mui/base": "5.0.0-beta.26", - "@mui/core-downloads-tracker": "^5.14.20", - "@mui/system": "^5.14.20", - "@mui/types": "^7.2.10", - "@mui/utils": "^5.14.20", - "@types/react-transition-group": "^4.4.9", - "clsx": "^2.0.0", - "csstype": "^3.1.2", + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.36", + "@mui/core-downloads-tracker": "^5.15.9", + "@mui/system": "^5.15.9", + "@mui/types": "^7.2.13", + "@mui/utils": "^5.15.9", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", "prop-types": "^15.8.1", "react-is": "^18.2.0", "react-transition-group": "^4.4.5" @@ -2313,13 +1977,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "5.14.20", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.20.tgz", - "integrity": "sha512-WV560e1vhs2IHCh0pgUaWHznrcrVoW9+cDCahU1VTkuwPokWVvb71ccWQ1f8Y3tRBPPcNkU2dChkkRJChLmQlQ==", + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.9.tgz", + "integrity": "sha512-/aMJlDOxOTAXyp4F2rIukW1O0anodAMCkv1DfBh/z9vaKHY3bd5fFf42wmP+0GRmwMinC5aWPpNfHXOED1fEtg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.4", - "@mui/utils": "^5.14.20", + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.9", "prop-types": "^15.8.1" }, "engines": { @@ -2340,14 +2004,14 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.14.20", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.20.tgz", - "integrity": "sha512-Vs4nGptd9wRslo9zeRkuWcZeIEp+oYbODy+fiZKqqr4CH1Gfi9fdP0Q1tGYk8OiJ2EPB/tZSAyOy62Hyp/iP7g==", + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.9.tgz", + "integrity": "sha512-NRKtYkL5PZDH7dEmaLEIiipd3mxNnQSO+Yo8rFNBNptY8wzQnQ+VjayTq39qH7Sast5cwHKYFusUrQyD+SS4Og==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.4", + "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", - "csstype": "^3.1.2", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { @@ -2372,18 +2036,18 @@ } }, "node_modules/@mui/system": { - "version": "5.14.20", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.20.tgz", - "integrity": "sha512-jKOGtK4VfYZG5kdaryUHss4X6hzcfh0AihT8gmnkfqRtWP7xjY+vPaUhhuSeibE5sqA5wCtdY75z6ep9pxFnIg==", + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.9.tgz", + "integrity": "sha512-SxkaaZ8jsnIJ77bBXttfG//LUf6nTfOcaOuIgItqfHv60ZCQy/Hu7moaob35kBb+guxVJnoSZ+7vQJrA/E7pKg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.4", - "@mui/private-theming": "^5.14.20", - "@mui/styled-engine": "^5.14.19", - "@mui/types": "^7.2.10", - "@mui/utils": "^5.14.20", - "clsx": "^2.0.0", - "csstype": "^3.1.2", + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.15.9", + "@mui/styled-engine": "^5.15.9", + "@mui/types": "^7.2.13", + "@mui/utils": "^5.15.9", + "clsx": "^2.1.0", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { @@ -2412,9 +2076,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.10", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.10.tgz", - "integrity": "sha512-wX1vbDC+lzF7FlhT6A3ffRZgEoKWPF8VqRoTu4lZwouFX2t90KyCMsgepMw5DxLak1BSp/KP86CmtZttikb/gQ==", + "version": "7.2.13", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz", + "integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==", "dev": true, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0" @@ -2426,12 +2090,12 @@ } }, "node_modules/@mui/utils": { - "version": "5.14.20", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.20.tgz", - "integrity": "sha512-Y6yL5MoFmtQml20DZnaaK1znrCEwG6/vRSzW8PKOTrzhyqKIql0FazZRUR7sA5EPASgiyKZfq0FPwISRXm5NdA==", + "version": "5.15.9", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.9.tgz", + "integrity": "sha512-yDYfr61bCYUz1QtwvpqYy/3687Z8/nS4zv7lv/ih/6ZFGMl1iolEvxRmR84v2lOYxlds+kq1IVYbXxDKh8Z9sg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.4", + "@babel/runtime": "^7.23.9", "@types/prop-types": "^15.7.11", "prop-types": "^15.8.1", "react-is": "^18.2.0" @@ -2476,10 +2140,9 @@ "dev": true }, "node_modules/@react-dnd/shallowequal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", - "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==", - "dev": true + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" }, "node_modules/@reduxjs/toolkit": { "version": "1.9.7", @@ -2506,118 +2169,14 @@ } }, "node_modules/@remix-run/router": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.13.1.tgz", - "integrity": "sha512-so+DHzZKsoOcoXrILB4rqDkMDy7NLMErRdOxvzvOKb507YINKUP4Di+shbTZDhSE/pBZ+vr7XGIpcOO0VLSA+Q==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.0.tgz", + "integrity": "sha512-HOil5aFtme37dVQTB6M34G95kPM3MMuqSmIRVCC52eKV+Y/tGSqw9P3rWhlAx6A+mz+MoX+XxsGsNJbaI5qCgQ==", "dev": true, "engines": { "node": ">=14.0.0" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.7.0.tgz", - "integrity": "sha512-rGku10pL1StFlFvXX5pEv88KdGW6DHUghsxyP/aRYb9eH+74jTGJ3U0S/rtlsQ4yYq1Hcc7AMkoJOb1xu29Fxw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.7.0.tgz", - "integrity": "sha512-/EBw0cuJ/KVHiU2qyVYUhogXz7W2vXxBzeE9xtVIMC+RyitlY2vvaoysMUqASpkUtoNIHlnKTu/l7mXOPgnKOA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.7.0.tgz", - "integrity": "sha512-4VXG1bgvClJdbEYYjQ85RkOtwN8sqI3uCxH0HC5w9fKdqzRzgG39K7GAehATGS8jghA7zNoS5CjSKkDEqWmNZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.7.0.tgz", - "integrity": "sha512-/ImhO+T/RWJ96hUbxiCn2yWI0/MeQZV/aeukQQfhxiSXuZJfyqtdHPUPrc84jxCfXTxbJLmg4q+GBETeb61aNw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.7.0.tgz", - "integrity": "sha512-zhye8POvTyUXlKbfPBVqoHy3t43gIgffY+7qBFqFxNqVtltQLtWeHNAbrMnXiLIfYmxcoL/feuLDote2tx+Qbg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.7.0.tgz", - "integrity": "sha512-RAdr3OJnUum6Vs83cQmKjxdTg31zJnLLTkjhcFt0auxM6jw00GD6IPFF42uasYPr/wGC6TRm7FsQiJyk0qIEfg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.7.0.tgz", - "integrity": "sha512-nhWwYsiJwZGq7SyR3afS3EekEOsEAlrNMpPC4ZDKn5ooYSEjDLe9W/xGvoIV8/F/+HNIY6jY8lIdXjjxfxopXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.7.0.tgz", - "integrity": "sha512-rlfy5RnQG1aop1BL/gjdH42M2geMUyVQqd52GJVirqYc787A/XVvl3kQ5NG/43KXgOgE9HXgCaEH05kzQ+hLoA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.7.0.tgz", @@ -2644,45 +2203,6 @@ "linux" ] }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.7.0.tgz", - "integrity": "sha512-CPtgaQL1aaPc80m8SCVEoxFGHxKYIt3zQYC3AccL/SqqiWXblo3pgToHuBwR8eCP2Toa+X1WmTR/QKFMykws7g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.7.0.tgz", - "integrity": "sha512-pmioUlttNh9GXF5x2CzNa7Z8kmRTyhEzzAC+2WOOapjewMbl+3tGuAnxbwc5JyG8Jsz2+hf/QD/n5VjimOZ63g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.7.0.tgz", - "integrity": "sha512-SeZzC2QhhdBQUm3U0c8+c/P6UlRyBcLL2Xp5KX7z46WXZxzR8RJSIWL9wSUeBTgxog5LTPJuPj0WOT9lvrtP7Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@smithy/abort-controller": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.11.tgz", @@ -3327,9 +2847,9 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, "node_modules/@tanstack/query-core": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.12.1.tgz", - "integrity": "sha512-WbZztNmKq0t6QjdNmHzezbi/uifYo9j6e2GLJkodsYaYUlzMbAp91RDyeHkIZrm7EfO4wa6Sm5sxJZm5SPlh6w==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.20.1.tgz", + "integrity": "sha512-OONHHYG5vzjob4An+EfzbW7TRyb+sCA0AEgHzUIMlV9NYlF7wIwbla3PUfB3ocnaK1gZyROf0Lux/CBSu0exBQ==", "dev": true, "funding": { "type": "github", @@ -3337,12 +2857,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.12.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.12.2.tgz", - "integrity": "sha512-BeWZu8zVFH20oRc+S/K9ADPgWjEzP/XQCGBNz5IbApUwPQAdwkQYbXODVL5AyAlWiSxhx+P2xlARPBApj2Yrog==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.20.1.tgz", + "integrity": "sha512-KRkOtJ47tv9B3EXfjHkbPkiFzOzYCOid8BrYBozk0rm9JpDB2xSf71q8w1PRudlQW6QUQIEDI9E6NIMh6AlLUw==", "dev": true, "dependencies": { - "@tanstack/query-core": "5.12.1" + "@tanstack/query-core": "5.20.1" }, "funding": { "type": "github", @@ -3431,7 +2951,7 @@ "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -3463,7 +2983,7 @@ "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "dev": true + "devOptional": true }, "node_modules/@types/quill": { "version": "1.3.10", @@ -3478,7 +2998,7 @@ "version": "17.0.73", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.73.tgz", "integrity": "sha512-6AcjgPIVsXTIsFDgsGW0iQhvg0xb2vt2qAWgXyncnVNRaW9ZXTTwAh7RQoh7PzK1AhjPoGDvUBkdAREih9n5oQ==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -3545,7 +3065,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true + "devOptional": true }, "node_modules/@types/shortid": { "version": "0.0.29", @@ -3725,6 +3245,14 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==", + "engines": { + "node": ">=4" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -3737,9 +3265,9 @@ } }, "node_modules/aws-sdk": { - "version": "2.1514.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1514.0.tgz", - "integrity": "sha512-ZQE5kHhJozwBB+Zaa21Gglm2pSQVU+8fFZNOn4pr+Kc1scYPlmVBPR3a0w19Vc4HNXPzjApAk2G4xMvzZDktAw==", + "version": "2.1555.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1555.0.tgz", + "integrity": "sha512-hjYs1MQkJxdHnoZm8hypqGy4PQKWVUs19McdXRXWNXr97V0il4xcUpIfvjHQ9x9EjP0p/jyIx9/BtyrR68jnUQ==", "dependencies": { "buffer": "4.9.2", "events": "1.1.1", @@ -3750,7 +3278,7 @@ "url": "0.10.3", "util": "^0.12.4", "uuid": "8.0.0", - "xml2js": "0.5.0" + "xml2js": "0.6.2" }, "engines": { "node": ">= 10.0.0" @@ -4311,6 +3839,12 @@ "react-dom": "^16.8.0 || ^17.0.0" } }, + "node_modules/chonky/node_modules/@react-dnd/shallowequal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", + "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==", + "dev": true + }, "node_modules/chonky/node_modules/clsx": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", @@ -4326,6 +3860,22 @@ "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==", "dev": true }, + "node_modules/chonky/node_modules/react-dnd": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz", + "integrity": "sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ==", + "dev": true, + "dependencies": { + "@react-dnd/shallowequal": "^2.0.0", + "@types/hoist-non-react-statics": "^3.3.1", + "dnd-core": "^11.1.3", + "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "react": ">= 16.9.0", + "react-dom": ">= 16.9.0" + } + }, "node_modules/chonky/node_modules/react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -4396,9 +3946,9 @@ } }, "node_modules/clsx": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", - "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", + "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", "dev": true, "engines": { "node": ">=6" @@ -4704,10 +4254,10 @@ } }, "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "devOptional": true }, "node_modules/dashdash": { "version": "1.14.1", @@ -5195,6 +4745,17 @@ "node": ">= 0.4.0" } }, + "node_modules/file-selector": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz", + "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/file-type": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", @@ -5337,20 +4898,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -5594,7 +5141,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dev": true, "dependencies": { "react-is": "^16.7.0" } @@ -5602,8 +5148,7 @@ "node_modules/hoist-non-react-statics/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/hotkeys-js": { "version": "3.13.2", @@ -5950,8 +5495,7 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -6218,7 +5762,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -6373,9 +5916,9 @@ } }, "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { "node": "*" } @@ -6487,9 +6030,9 @@ "dev": true }, "node_modules/nodemon": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.2.tgz", - "integrity": "sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz", + "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==", "dev": true, "dependencies": { "chokidar": "^3.5.2", @@ -6922,9 +6465,9 @@ "dev": true }, "node_modules/postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "funding": [ { @@ -7006,9 +6549,9 @@ } }, "node_modules/prettier": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", - "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -7029,7 +6572,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -7039,8 +6581,7 @@ "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -7177,7 +6718,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -7192,19 +6732,32 @@ "dev": true }, "node_modules/react-dnd": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz", - "integrity": "sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ==", - "dev": true, + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", "dependencies": { - "@react-dnd/shallowequal": "^2.0.0", - "@types/hoist-non-react-statics": "^3.3.1", - "dnd-core": "^11.1.3", - "hoist-non-react-statics": "^3.3.0" + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" }, "peerDependencies": { - "react": ">= 16.9.0", - "react-dom": ">= 16.9.0" + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } } }, "node_modules/react-dnd-html5-backend": { @@ -7216,6 +6769,26 @@ "dnd-core": "^11.1.3" } }, + "node_modules/react-dnd/node_modules/@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "node_modules/react-dnd/node_modules/@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "node_modules/react-dnd/node_modules/dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dependencies": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -7229,6 +6802,22 @@ "react": "^18.2.0" } }, + "node_modules/react-dropzone": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz", + "integrity": "sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==", + "dependencies": { + "attr-accept": "^2.2.2", + "file-selector": "^0.6.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, "node_modules/react-intl": { "version": "5.25.1", "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", @@ -7355,12 +6944,12 @@ } }, "node_modules/react-router": { - "version": "6.20.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.20.1.tgz", - "integrity": "sha512-ccvLrB4QeT5DlaxSFFYi/KR8UMQ4fcD8zBcR71Zp1kaYTC5oJKYAp1cbavzGrogwxca+ubjkd7XjFZKBW8CxPA==", + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.0.tgz", + "integrity": "sha512-q2yemJeg6gw/YixRlRnVx6IRJWZD6fonnfZhN1JIOhV2iJCPeRNSH3V1ISwHf+JWcESzLC3BOLD1T07tmO5dmg==", "dev": true, "dependencies": { - "@remix-run/router": "1.13.1" + "@remix-run/router": "1.15.0" }, "engines": { "node": ">=14.0.0" @@ -7370,13 +6959,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.20.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.20.1.tgz", - "integrity": "sha512-npzfPWcxfQN35psS7rJgi/EW0Gx6EsNjfdJSAk73U/HqMEJZ2k/8puxfwHFgDQhBGmS3+sjnGbMdMSV45axPQw==", + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.0.tgz", + "integrity": "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==", "dev": true, "dependencies": { - "@remix-run/router": "1.13.1", - "react-router": "6.20.1" + "@remix-run/router": "1.15.0", + "react-router": "6.22.0" }, "engines": { "node": ">=14.0.0" @@ -7387,27 +6976,18 @@ } }, "node_modules/react-toastify": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", - "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.4.tgz", + "integrity": "sha512-etR3RgueY8pe88SA67wLm8rJmL1h+CLqUGHuAoNsseW35oTGJEri6eBTyaXnFKNQ80v/eO10hBYLgz036XRGgA==", "dev": true, "dependencies": { - "clsx": "^1.1.1" + "clsx": "^2.1.0" }, "peerDependencies": { "react": ">=16", "react-dom": ">=16" } }, - "node_modules/react-toastify/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -7480,7 +7060,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "dev": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -7506,8 +7085,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", @@ -7933,9 +7511,9 @@ "dev": true }, "node_modules/socket.io": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", - "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.4.tgz", + "integrity": "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", @@ -7958,9 +7536,9 @@ } }, "node_modules/socket.io-client": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", - "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz", + "integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==", "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", @@ -8488,13 +8066,13 @@ } }, "node_modules/vite": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.7.tgz", - "integrity": "sha512-B4T4rJCDPihrQo2B+h1MbeGL/k/GMAHzhQ8S0LjQ142s6/+l3hHTT095ORvsshj4QCkoWu3Xtmob5mazvakaOw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", + "integrity": "sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==", "dev": true, "dependencies": { "esbuild": "^0.19.3", - "postcss": "^8.4.32", + "postcss": "^8.4.35", "rollup": "^4.2.0" }, "bin": { @@ -8625,9 +8203,9 @@ } }, "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" diff --git a/package.json b/package.json index 353c74d..74ce608 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minecluster", - "version": "0.0.1-alpha.0", + "version": "0.0.1-alpha.1", "description": "Minecraft Server management using Kubernetes", "type": "module", "scripts": { @@ -8,7 +8,7 @@ "start": "node dist/app.js", "dev:server": "nodemon dist/app.js", "dev:react": "vite", - "kub": "nodemon lib/k8s.js", + "lint": "npx prettier -w src lib vite.config.js", "start:dev": "concurrently -k \"MCL_DEV_PORT=52025 npm run dev:server\" \" MCL_VITE_DEV_PORT=52000 MCL_VITE_BACKEND_URL=http://localhost:52025 npm run dev:react\" -n s,v -p -c green,yellow", "start:dev:garden": "concurrently -k \"npm run dev:server\" \"npm run dev:react\" -n s,v -p -c green,yellow" }, @@ -24,9 +24,9 @@ "devDependencies": { "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", - "@mui/icons-material": "^5.15.7", - "@mui/material": "^5.15.7", - "@tanstack/react-query": "^5.18.1", + "@mui/icons-material": "^5.15.9", + "@mui/material": "^5.15.9", + "@tanstack/react-query": "^5.20.1", "@vitejs/plugin-react": "^4.2.1", "chonky": "^2.3.2", "chonky-icon-fontawesome": "^2.3.2", @@ -39,11 +39,11 @@ "react-router-dom": "^6.22.0", "react-toastify": "^10.0.4", "socket.io-client": "^4.7.4", - "vite": "^5.0.12" + "vite": "^5.1.1" }, "dependencies": { "@kubernetes/client-node": "^0.20.0", - "aws-sdk": "^2.1550.0", + "aws-sdk": "^2.1555.0", "basic-ftp": "^5.0.4", "bcrypt": "^5.1.1", "chalk": "^5.3.0", @@ -57,6 +57,7 @@ "pg-promise": "^11.5.4", "postgres-migrations": "^5.3.0", "rcon-client": "^4.2.4", + "react-dropzone": "^14.2.3", "socket.io": "^4.7.4", "uuid": "^9.0.1" } diff --git a/src/components/files/ChonkyStyledFileBrowser.jsx b/src/components/files/ChonkyStyledFileBrowser.jsx deleted file mode 100644 index 3273684..0000000 --- a/src/components/files/ChonkyStyledFileBrowser.jsx +++ /dev/null @@ -1,42 +0,0 @@ -// ChonkyFullFileBrowser.tsx -import { forwardRef, memo } from "react"; -import { - StylesProvider, - createGenerateClassName, -} from "@material-ui/core/styles"; - -import { - FileBrowser, - FileList, - FileContextMenu, - FileNavbar, - FileToolbar, - setChonkyDefaults, - FileBrowserHandle, - FileBrowserProps, -} from "chonky"; - -import { ChonkyIconFA } from "chonky-icon-fontawesome"; - -setChonkyDefaults({ iconComponent: ChonkyIconFA }); - -const muiJSSClassNameGenerator = createGenerateClassName({ - // Seed property is used to add a prefix classes generated by material ui. - seed: "chonky", -}); - -export default memo( - forwardRef((props, ref) => { - const { onScroll } = props; - return ( - - - - - - - - - ); - }), -); diff --git a/src/components/files/FilePreview.jsx b/src/components/files/FilePreview.jsx index 0241cf5..c6e1002 100644 --- a/src/components/files/FilePreview.jsx +++ b/src/components/files/FilePreview.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect, memo } from "react"; +import { useState, useEffect } from "react"; import useMediaQuery from "@mui/material/useMediaQuery"; import { useTheme } from "@mui/material/styles"; import Button from "@mui/material/Button"; @@ -10,7 +10,17 @@ import Toolbar from "@mui/material/Toolbar"; import TextEditor from "./TextEditor.jsx"; import { cairoAuthHeader } from "@mcl/util/auth.js"; -const textFileTypes = ["properties", "txt", "yaml", "yml", "json", "env"]; +const textFileTypes = [ + "properties", + "txt", + "yaml", + "yml", + "json", + "env", + "toml", + "tml", + "text", +]; const imageFileTypes = ["png", "jpeg", "jpg"]; export const supportedFileTypes = [...textFileTypes, ...imageFileTypes]; @@ -44,6 +54,7 @@ export default function FilePreview(props) { } async function onSave() { + if (!isTextFile) return; const formData = new FormData(); const blob = new Blob([modifiedText], { type: "plain/text" }); formData.append("file", blob, name); @@ -77,7 +88,7 @@ export default function FilePreview(props) { {name} - + {isTextFile && } + )} {!(rcon && rcon.rconLive && !rcon.rconError) && ( diff --git a/src/css/rcon.css b/src/css/rcon.css index a6093ad..c4fb9e3 100644 --- a/src/css/rcon.css +++ b/src/css/rcon.css @@ -1,8 +1,7 @@ .rconLogsWrapper { overflow-y: scroll; - max-height: 20rem; + max-height: calc(100% - 6rem); word-wrap: break-word; - margin-bottom: 10px; } .rconActions { display: inline-flex; diff --git a/src/util/queries.js b/src/util/queries.js index 14a7035..f2d65b1 100644 --- a/src/util/queries.js +++ b/src/util/queries.js @@ -64,6 +64,13 @@ export const createServerFolder = async (serverId, path) => export const deleteServerItem = async (serverId, path, isDir) => fetchApiCore("/files/item", { id: serverId, path, isDir }, "DELETE"); +export const moveServerItems = async (serverId, files, destination, origin) => + fetchApiCore( + "/files/move", + { id: serverId, files, destination, origin }, + "POST", + ); + export async function previewServerItem(serverId, path) { const resp = await fetchApiCore("/files/item", { id: serverId, path }); if (resp.status !== 200) return console.log("AHHHH"); From fc60df27acf36080f82a1fb0b837e5cb138a59ae Mon Sep 17 00:00:00 2001 From: dunemask Date: Tue, 13 Feb 2024 05:09:18 +0000 Subject: [PATCH 18/38] [FEATURE] Live Modifications, Host Safety, Minor Tweaks (#19) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/19 --- dist/app.js | 2 +- lib/controllers/lifecycle-controller.js | 12 ++- lib/database/queries/server-queries.js | 76 ++++++++++++++----- lib/k8s/configs/server-svc.yml | 2 - lib/k8s/server-create.js | 6 +- lib/k8s/server-modify.js | 59 ++++++++++++++ .../server-options/ExtraPortsOption.jsx | 12 ++- src/components/server-options/HostOption.jsx | 10 ++- src/pages/EditCoreOptions.jsx | 6 +- 9 files changed, 154 insertions(+), 31 deletions(-) create mode 100644 lib/k8s/server-modify.js diff --git a/dist/app.js b/dist/app.js index 2f46e88..d87897e 100644 --- a/dist/app.js +++ b/dist/app.js @@ -8,4 +8,4 @@ const kc = new k8s.KubeConfig(); kc.loadFromDefault(); } -main().catch((e)=>{console.log(e)}); +main().catch((e)=>{console.error(e)}); diff --git a/lib/controllers/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js index d5b9e3a..1916157 100644 --- a/lib/controllers/lifecycle-controller.js +++ b/lib/controllers/lifecycle-controller.js @@ -9,6 +9,8 @@ import { import ExpressClientError, { sendError } from "../util/ExpressClientError.js"; import { toggleServer } from "../k8s/k8s-server-control.js"; import { checkAuthorization } from "../database/queries/server-queries.js"; +import { WARN } from "../util/logging.js"; +import modifyServerResources from "../k8s/server-modify.js"; const dnsRegex = new RegExp( `^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`, @@ -69,6 +71,9 @@ function payloadFilter(req, res) { return res .status(400) .send("Extra ports must be a list of strings with length of 5!"); + if (host !== host.toLowerCase()) + WARN("CREATE", "Host automatically being lowercasified..."); + req.body.host = host.toLowerCase(); return "filtered"; } @@ -158,10 +163,15 @@ export async function getServer(req, res) { export async function modifyServer(req, res) { if (payloadFilter(req, res) !== "filtered") return; const serverSpec = req.body; + if (!!serverSpec.host) + WARN( + "MODIFY", + "Warning, hostname changing is not implimented yet! Please ask the developer if you'd like to see this added!", + ); try { await checkServerId(req.cairoId, serverSpec); const serverEntry = await modifyServerEntry(serverSpec); - // await createServerResources(serverEntry); + await modifyServerResources(serverEntry); res.sendStatus(200); } catch (e) { sendError(res)(e); diff --git a/lib/database/queries/server-queries.js b/lib/database/queries/server-queries.js index 326572b..a08d05b 100644 --- a/lib/database/queries/server-queries.js +++ b/lib/database/queries/server-queries.js @@ -165,7 +165,7 @@ export async function modifyServerEntry(serverSpec) { id, // ownerCairoId: owner_cairo_id, // DIsabled! If these becomes a reqest, please create a new function! name, - host, + // host, // TODO: Can only be updated if service name is generic and non descriptive version, serverType: server_type, cpu, // TODO: Ignored for now by the K8S manifests @@ -180,28 +180,66 @@ export async function modifyServerEntry(serverSpec) { backupInterval: backup_interval, } = serverSpec; - const q = updateWhereAllQuery( - table, - { + const q = + updateWhereAllQuery( + table, + { + name, + // host, // TODO: Can only be updated if service name is generic and non descriptive + version, + server_type, + cpu, // TODO: Ignored for now by the K8S manifests + memory, + // storage, // DO NOT INCLUDE THIS KEY, Not all storage providers in kubernetes allow for dynamically resizable PVCs + extra_ports, + backup_enabled, + backup_host, + backup_bucket_path, + backup_id, + backup_key, + backup_interval, + }, + { id }, + ) + ` RETURNING *;`; + try { + const entries = await pg.query(q); + const { name, - host, + host, // Should always read the database value + server_type: serverType, + storage, + extra_ports: extraPorts, + backup_enabled: backupEnabled, + backup_host: backupHost, + backup_bucket_path: backupPath, + backup_id: backupId, + backup_key: backupKey, + backup_interval: backupInterval, + } = entries[0]; + + const mclName = getMclName(host, id); + + return { + name, // Could change + mclName, // Shouldn't change + id, // Won't change + // host, // TODO: Can only be updated if service name is generic and non descriptive version, - server_type, + serverType, cpu, // TODO: Ignored for now by the K8S manifests memory, - // storage, // DO NOT INCLUDE THIS KEY, Not all storage providers in kubernetes allow for dynamically resizable PVCs - extra_ports, - backup_enabled, - backup_host, - backup_bucket_path, - backup_id, - backup_key, - backup_interval, - }, - { id }, - ); - - return pg.query(q); + storage, + extraPorts, + backupEnabled, + backupHost, + backupPath, + backupId, + backupKey, + backupInterval, + }; + } catch (e) { + asExpressClientError(e); + } } export async function getServerEntries() { diff --git a/lib/k8s/configs/server-svc.yml b/lib/k8s/configs/server-svc.yml index f21db9a..a6f520e 100644 --- a/lib/k8s/configs/server-svc.yml +++ b/lib/k8s/configs/server-svc.yml @@ -11,8 +11,6 @@ metadata: namespace: changeme-namespace spec: internalTrafficPolicy: Cluster - ipFamilies: - - IPv4 ipFamilyPolicy: SingleStack ports: # Programatically add all FTP ports. Port range includes 20, 21, 40000-40001 - name: minecraft diff --git a/lib/k8s/server-create.js b/lib/k8s/server-create.js index 30e21fa..7d09e61 100644 --- a/lib/k8s/server-create.js +++ b/lib/k8s/server-create.js @@ -18,7 +18,7 @@ const namespace = process.env.MCL_SERVER_NAMESPACE; const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8")); -function createExtraService(serverSpec) { +export function createExtraService(serverSpec) { const { mclName, id, extraPorts } = serverSpec; if (!extraPorts) return; const serviceYaml = loadYaml("lib/k8s/configs/extra-svc.yml"); @@ -49,7 +49,7 @@ function createExtraService(serverSpec) { return serviceYaml; } -function createBackupSecret(serverSpec) { +export function createBackupSecret(serverSpec) { if (!serverSpec.backupEnabled) return; // If backup not defined, don't create RCLONE secret const { mclName, id, backupId, backupKey, backupHost } = serverSpec; const backupYaml = loadYaml("lib/k8s/configs/backup-secret.yml"); @@ -153,7 +153,7 @@ function createServerDeploy(serverSpec) { return deployYaml; } -function createServerService(serverSpec) { +export function createServerService(serverSpec) { const { mclName, host, id } = serverSpec; const serviceYaml = loadYaml("lib/k8s/configs/server-svc.yml"); serviceYaml.metadata.annotations["ingress.qumine.io/hostname"] = host; diff --git a/lib/k8s/server-modify.js b/lib/k8s/server-modify.js new file mode 100644 index 0000000..74a1428 --- /dev/null +++ b/lib/k8s/server-modify.js @@ -0,0 +1,59 @@ +import k8s from "@kubernetes/client-node"; +import { + createExtraService, + createBackupSecret, + createServerService, +} from "./server-create.js"; +import kc from "./k8s-config.js"; +import { getServerAssets } from "./k8s-server-control.js"; +const k8sCore = kc.makeApiClient(k8s.CoreV1Api); +const namespace = process.env.MCL_SERVER_NAMESPACE; + +export default async function modifyServerResources(modifySpec) { + const { id: serverId } = modifySpec; + const serverAssets = await getServerAssets(serverId); + const serverService = createServerService(modifySpec); + const extraService = createExtraService(modifySpec); + const backupSecret = createBackupSecret(modifySpec); + const serverResources = []; + + if (!!serverService) + // Will Always Exist + serverResources.push( + k8sCore.replaceNamespacedService( + serverAssets.service.metadata.name, + namespace, + serverService, + ), + ); + + if (!!extraService && !!serverAssets.extraService) + // Might not exist + serverResources.push( + k8sCore.replaceNamespacedService( + serverAssets.extraService.metadata.name, + namespace, + extraService, + ), + ); + else if (!!extraService) + serverResources.push( + k8sCore.createNamespacedService(namespace, extraService), + ); + + if (!!backupSecret && !!serverAssets.backupSecret) + // Might not exist + serverResources.push( + k8sCore.replaceNamespacedSecret( + serverAssets.backupSecret.metadata.name, + namespace, + backupSecret, + ), + ); + else if (!!backupSecret) + serverResources.push( + k8sCore.createNamespacedSecret(namespace, backupSecret), + ); + + return await Promise.all(serverResources); +} diff --git a/src/components/server-options/ExtraPortsOption.jsx b/src/components/server-options/ExtraPortsOption.jsx index c97b40b..893a067 100644 --- a/src/components/server-options/ExtraPortsOption.jsx +++ b/src/components/server-options/ExtraPortsOption.jsx @@ -3,7 +3,8 @@ import TextField from "@mui/material/TextField"; import Autocomplete from "@mui/material/Autocomplete"; import Chip from "@mui/material/Chip"; -const validatePort = (p) => p !== "25565" && p !== "25575" && p.length < 6; +const validatePort = (p) => + p !== "25565" && p !== "25575" && p.length < 6 && parseInt(p) < 60_000; export default function ExtraPortsOption(props) { const { extraPorts: initExtraPorts } = props; @@ -30,7 +31,14 @@ export default function ExtraPortsOption(props) { value={extraPorts} onChange={portChange} freeSolo - renderInput={(p) => } + renderInput={(p) => ( + + )} renderTags={(value, getTagProps) => value.map((option, index) => { const defaultChipProps = getTagProps({ index }); diff --git a/src/components/server-options/HostOption.jsx b/src/components/server-options/HostOption.jsx index d03d1db..15d14e9 100644 --- a/src/components/server-options/HostOption.jsx +++ b/src/components/server-options/HostOption.jsx @@ -1,15 +1,21 @@ import TextField from "@mui/material/TextField"; export default function HostOption(props) { - const { value, onChange } = props; + const { value, onChange, disabled } = props; + + function onTextChange(e) { + e.target.value = e.target.value.toLowerCase(); + onChange(e); + } return ( ); } diff --git a/src/pages/EditCoreOptions.jsx b/src/pages/EditCoreOptions.jsx index a098cb6..bda2c34 100644 --- a/src/pages/EditCoreOptions.jsx +++ b/src/pages/EditCoreOptions.jsx @@ -73,7 +73,11 @@ export default function EditCoreOptions(props) { > - + Date: Tue, 13 Feb 2024 09:37:48 -0700 Subject: [PATCH 19/38] [HOTFIX] Host return from database --- lib/database/queries/server-queries.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/database/queries/server-queries.js b/lib/database/queries/server-queries.js index a08d05b..feafec2 100644 --- a/lib/database/queries/server-queries.js +++ b/lib/database/queries/server-queries.js @@ -223,7 +223,7 @@ export async function modifyServerEntry(serverSpec) { name, // Could change mclName, // Shouldn't change id, // Won't change - // host, // TODO: Can only be updated if service name is generic and non descriptive + host, // TODO: Can only be updated if service name is generic and non descriptive, this returns the host from the database version, serverType, cpu, // TODO: Ignored for now by the K8S manifests From ace95a20c66a0c696cae7a798d892b21ac33c4d4 Mon Sep 17 00:00:00 2001 From: dunemask Date: Tue, 20 Feb 2024 09:30:56 +0000 Subject: [PATCH 20/38] [FEATURE] Workflow Overhaul (#20) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/20 --- .gitea/workflows/deploy-edge-proxy.yml | 38 ++++++++++++++++++-------- .gitea/workflows/deploy-edge.yml | 35 ++++++++++++++---------- .gitea/workflows/qa-api-tests.yml | 24 ++++++++++------ 3 files changed, 63 insertions(+), 34 deletions(-) diff --git a/.gitea/workflows/deploy-edge-proxy.yml b/.gitea/workflows/deploy-edge-proxy.yml index 3551003..e4dc61d 100644 --- a/.gitea/workflows/deploy-edge-proxy.yml +++ b/.gitea/workflows/deploy-edge-proxy.yml @@ -5,19 +5,35 @@ on: branches: [ master ] env: - GITEA_TOKEN: ${{ secrets.ELYSIUM_ORG_READ_TOKEN }} - KUBECONFIG_BASE64: ${{ secrets.KUBECONFIG_USW_EDGE }} GARDEN_DEPLOY_ACTION: minecluster-proxy - jobs: deploy-edge: steps: - - name: Oasis Setup - uses: https://gitea.dunemask.dev/elysium/oasis-action@master - with: - gitea-token: ${{ env.GITEA_TOKEN }} - kubeconfig: ${{ env.KUBECONFIG_BASE64 }} - - name: Deploy to Edge env - run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-edge - working-directory: ${{ env.OASIS_WORKSPACE }} \ No newline at end of file + # Configure proper kubeconfig + - name: Get usw-mc deployment kubeconfig + uses: https://gitea.dunemask.dev/elysium/elysium-actions@infisical-env + with: + infisical-token: ${{ secrets.INFISICAL_ELYSIUM_EDGE_READ_TOKEN }} + secret-envs: edge + secret-paths: /kubernetes + # Setup Oasis + - name: Oasis Setup + uses: https://gitea.dunemask.dev/elysium/elysium-actions@oasis-setup-auto + with: + deploy-env: edge + infisical-token: ${{ secrets.INFISICAL_ELYSIUM_EDGE_READ_TOKEN }} + kubeconfig: ${{ env.KUBERNETES_CONFIG_USW_MC }} + # Deploy to Edge Cluster + - name: Deploy to Edge Cluster + run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-edge + working-directory: ${{ env.OASIS_WORKSPACE }} + # Alert via Discord + - name: Discord Alert + if: always() + uses: https://gitea.dunemask.dev/elysium/elysium-actions@discord-status + with: + status: ${{ job.status }} + channel: deployments + header: DEPLOY EDGE + additional-content: "Minecluster Proxy" \ No newline at end of file diff --git a/.gitea/workflows/deploy-edge.yml b/.gitea/workflows/deploy-edge.yml index fc9ed65..530be68 100644 --- a/.gitea/workflows/deploy-edge.yml +++ b/.gitea/workflows/deploy-edge.yml @@ -5,22 +5,29 @@ on: branches: [ master ] env: - GITEA_TOKEN: ${{ secrets.ELYSIUM_ORG_READ_TOKEN }} - KUBECONFIG_BASE64: ${{ secrets.KUBECONFIG_USW_MC }} GARDEN_DEPLOY_ACTION: minecluster - # Additional Deploy Envars - POSTGRES_PROD_PASSWORD: ${{ secrets.POSTGRES_PROD_PASSWORD }} - MCL_KUBECONFIG: ${{ secrets.KUBECONFIG_USW_MC }} - jobs: deploy-edge: steps: - - name: Oasis Setup - uses: https://gitea.dunemask.dev/elysium/oasis-action@master - with: - gitea-token: ${{ env.GITEA_TOKEN }} - kubeconfig: ${{ env.KUBECONFIG_BASE64 }} - - name: Deploy to Edge env - run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-mc - working-directory: ${{ env.OASIS_WORKSPACE }} \ No newline at end of file + # Setup Oasis + - name: Oasis Setup + uses: https://gitea.dunemask.dev/elysium/elysium-actions@oasis-setup-auto + with: + deploy-env: edge + infisical-token: ${{ secrets.INFISICAL_ELYSIUM_EDGE_READ_TOKEN }} + extra-secret-paths: /alexandria + extra-secret-envs: edge + # Deploy to Edge + - name: Deploy to Edge env + run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-edge + working-directory: ${{ env.OASIS_WORKSPACE }} + # Alert via Discord + - name: Discord Alert + if: always() + uses: https://gitea.dunemask.dev/elysium/elysium-actions@discord-status + with: + status: ${{ job.status }} + channel: deployments + header: DEPLOY MC + additional-content: "Minecluster Server Manager Deployment" \ No newline at end of file diff --git a/.gitea/workflows/qa-api-tests.yml b/.gitea/workflows/qa-api-tests.yml index 4014415..8222cdf 100644 --- a/.gitea/workflows/qa-api-tests.yml +++ b/.gitea/workflows/qa-api-tests.yml @@ -6,31 +6,37 @@ on: env: REPO_DIR: ${{ gitea.workspace }}/minecluster - KUBECONFIG_BASE64: ${{ secrets.KUBECONFIG_USW_DEV }} - GITEA_TOKEN: ${{ secrets.ELYSIUM_ORG_READ_TOKEN }} GARDEN_LINK_ACTION: build.minecluster-image jobs: qa-api-tests: steps: + # Setup Oasis - name: Oasis Setup - uses: https://gitea.dunemask.dev/elysium/oasis-action@master + uses: https://gitea.dunemask.dev/elysium/elysium-actions@oasis-setup-auto with: - gitea-token: ${{ env.GITEA_TOKEN }} - kubeconfig: ${{ env.KUBECONFIG_BASE64 }} + deploy-env: ci + infisical-token: ${{ secrets.INFISICAL_ELYSIUM_CI_READ_TOKEN }} # Test Code - name: Checkout repository uses: actions/checkout@v3 with: path: ${{ env.REPO_DIR }} - # Garden tests + # Garden link - name: Link Repo code to Garden run: garden link action $GARDEN_LINK_ACTION $REPO_DIR --env usw-ci --var cubit-projects=cairo,minecluster working-directory: ${{ env.OASIS_WORKSPACE }} # Cubit CI Tests - name: Run Cubit tests in CI env - run: garden workflow qa-api-tests --env usw-ci --var ci-ttl=25 + run: garden workflow qa-api-tests --env usw-ci --var ci-ttl=25m working-directory: ${{ env.OASIS_WORKSPACE }} - - name: Status Alert + # Discord Alert + - name: Discord Alert if: always() - run: echo "The Job ended with status ${{ job.status }}." \ No newline at end of file + uses: https://gitea.dunemask.dev/elysium/elysium-actions@discord-status + with: + status: ${{ job.status }} + channel: ci + header: QA API Tests + additional-content: "CI Namespace: `${{env.CI_NAMESPACE}}`" + \ No newline at end of file From c93c97b275bed21e96fdc04d6350878a5609142f Mon Sep 17 00:00:00 2001 From: Dunemask Date: Tue, 20 Feb 2024 02:35:16 -0700 Subject: [PATCH 21/38] [HOTFIX] Workflow Overhaul --- .gitea/workflows/deploy-edge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/deploy-edge.yml b/.gitea/workflows/deploy-edge.yml index 530be68..5ad9c1f 100644 --- a/.gitea/workflows/deploy-edge.yml +++ b/.gitea/workflows/deploy-edge.yml @@ -20,7 +20,7 @@ jobs: extra-secret-envs: edge # Deploy to Edge - name: Deploy to Edge env - run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-edge + run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-mc working-directory: ${{ env.OASIS_WORKSPACE }} # Alert via Discord - name: Discord Alert From e9bd043924ef8741c290bbef0d366fa009cd504d Mon Sep 17 00:00:00 2001 From: Dunemask Date: Tue, 20 Feb 2024 02:38:01 -0700 Subject: [PATCH 22/38] [HOTFIX] Workflow Overhaul --- .gitea/workflows/deploy-edge-proxy.yml | 8 -------- .gitea/workflows/deploy-edge.yml | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/deploy-edge-proxy.yml b/.gitea/workflows/deploy-edge-proxy.yml index e4dc61d..c961b21 100644 --- a/.gitea/workflows/deploy-edge-proxy.yml +++ b/.gitea/workflows/deploy-edge-proxy.yml @@ -10,20 +10,12 @@ env: jobs: deploy-edge: steps: - # Configure proper kubeconfig - - name: Get usw-mc deployment kubeconfig - uses: https://gitea.dunemask.dev/elysium/elysium-actions@infisical-env - with: - infisical-token: ${{ secrets.INFISICAL_ELYSIUM_EDGE_READ_TOKEN }} - secret-envs: edge - secret-paths: /kubernetes # Setup Oasis - name: Oasis Setup uses: https://gitea.dunemask.dev/elysium/elysium-actions@oasis-setup-auto with: deploy-env: edge infisical-token: ${{ secrets.INFISICAL_ELYSIUM_EDGE_READ_TOKEN }} - kubeconfig: ${{ env.KUBERNETES_CONFIG_USW_MC }} # Deploy to Edge Cluster - name: Deploy to Edge Cluster run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-edge diff --git a/.gitea/workflows/deploy-edge.yml b/.gitea/workflows/deploy-edge.yml index 5ad9c1f..a9386b4 100644 --- a/.gitea/workflows/deploy-edge.yml +++ b/.gitea/workflows/deploy-edge.yml @@ -10,6 +10,13 @@ env: jobs: deploy-edge: steps: + # Configure proper kubeconfig + - name: Get usw-mc deployment kubeconfig + uses: https://gitea.dunemask.dev/elysium/elysium-actions@infisical-env + with: + infisical-token: ${{ secrets.INFISICAL_ELYSIUM_EDGE_READ_TOKEN }} + secret-envs: edge + secret-paths: /kubernetes # Setup Oasis - name: Oasis Setup uses: https://gitea.dunemask.dev/elysium/elysium-actions@oasis-setup-auto @@ -18,6 +25,7 @@ jobs: infisical-token: ${{ secrets.INFISICAL_ELYSIUM_EDGE_READ_TOKEN }} extra-secret-paths: /alexandria extra-secret-envs: edge + kubeconfig: ${{ env.KUBERNETES_CONFIG_USW_MC }} # Deploy to Edge - name: Deploy to Edge env run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-mc From b15f616adbfd361721b188db1eb0f41d2afae737 Mon Sep 17 00:00:00 2001 From: Dunemask Date: Tue, 20 Feb 2024 02:44:52 -0700 Subject: [PATCH 23/38] [HOTFIX] Workflow Overhaul --- .gitea/workflows/deploy-edge.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/deploy-edge.yml b/.gitea/workflows/deploy-edge.yml index a9386b4..04be45d 100644 --- a/.gitea/workflows/deploy-edge.yml +++ b/.gitea/workflows/deploy-edge.yml @@ -30,6 +30,8 @@ jobs: - name: Deploy to Edge env run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-mc working-directory: ${{ env.OASIS_WORKSPACE }} + env: + MCL_KUBECONFIG: ${{ secrets.KUBECONFIG_USW_MC }} # Alert via Discord - name: Discord Alert if: always() From 332f84972c7418d79c395c8fb04b51000f09d69d Mon Sep 17 00:00:00 2001 From: dunemask Date: Mon, 11 Mar 2024 19:32:11 +0000 Subject: [PATCH 24/38] [FEATURE] Allow folder uploads (#21) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/21 --- lib/k8s/server-files.js | 6 ++++-- src/components/files/MineclusterFiles.jsx | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/k8s/server-files.js b/lib/k8s/server-files.js index efa32d8..36dcd4a 100644 --- a/lib/k8s/server-files.js +++ b/lib/k8s/server-files.js @@ -2,7 +2,8 @@ import ftp from "basic-ftp"; import { ERR } from "../util/logging.js"; import { getServerAssets } from "./k8s-server-control.js"; import ExpressClientError from "../util/ExpressClientError.js"; -import { Readable, Writable, Transform } from "node:stream"; +import { Readable, Transform } from "node:stream"; +import { dirname, basename } from "node:path"; const namespace = process.env.MCL_SERVER_NAMESPACE; @@ -82,7 +83,8 @@ export async function uploadServerItem(serverSpec, file) { const { path } = serverSpec; pathSecurityCheck(path); await useServerFtp(serverSpec, async (c) => { - await c.uploadFrom(fileStream, path); + await c.ensureDir(dirname(path)); + await c.uploadFrom(fileStream, basename(path)); }).catch(handleError); } diff --git a/src/components/files/MineclusterFiles.jsx b/src/components/files/MineclusterFiles.jsx index 656af40..eb48283 100644 --- a/src/components/files/MineclusterFiles.jsx +++ b/src/components/files/MineclusterFiles.jsx @@ -111,10 +111,12 @@ export default function MineclusterFiles(props) { } async function uploadFile(file) { + const filePath = file.path.startsWith("/") ? file.path : `/${file.path}`; const formData = new FormData(); formData.append("file", file); formData.append("id", serverId); - formData.append("path", [...dirStack, file.name].join("/")); + const path = `${[...dirStack].join("/")}${filePath}`; + formData.append("path", path); await fetch("/api/files/upload", { method: "POST", body: formData, From 6efa50e86b7dd543de2edbc24a720fdde81b8b2d Mon Sep 17 00:00:00 2001 From: dunemask Date: Tue, 12 Mar 2024 01:58:25 +0000 Subject: [PATCH 25/38] [FEATURE] Backups View & Style fix (#22) Co-authored-by: Dunemask Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/22 --- lib/controllers/s3-controller.js | 84 ++ lib/routes/s3-route.js | 11 + lib/server/router.js | 2 + package-lock.json | 1674 ++++++++++------------ package.json | 3 +- src/components/servers/BackupsDialog.jsx | 88 ++ src/components/servers/RconView.jsx | 1 + src/components/servers/ServerCard.jsx | 11 +- src/pages/Home.jsx | 21 +- src/util/queries.js | 5 + 10 files changed, 969 insertions(+), 931 deletions(-) create mode 100644 lib/controllers/s3-controller.js create mode 100644 lib/routes/s3-route.js create mode 100644 src/components/servers/BackupsDialog.jsx diff --git a/lib/controllers/s3-controller.js b/lib/controllers/s3-controller.js new file mode 100644 index 0000000..43c6f7d --- /dev/null +++ b/lib/controllers/s3-controller.js @@ -0,0 +1,84 @@ +import { S3, GetObjectCommand } from "@aws-sdk/client-s3"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; +import { basename } from "node:path"; +import { getServerEntry } from "../database/queries/server-queries.js"; +import { ERR } from "../util/logging.js"; +import { checkAuthorization } from "../database/queries/server-queries.js"; +const s3Region = "us-east-1"; + +async function getS3BackupData(serverId) { + const serverEntry = await getServerEntry(serverId); + if (!serverEntry?.backupHost) return undefined; + const s3Config = { + credentials: { + accessKeyId: serverEntry.backupId, + secretAccessKey: serverEntry.backupKey, + }, + endpoint: `https://${serverEntry.backupHost}`, + forcePathStyle: true, + region: s3Region, + }; + const pathParts = serverEntry.backupPath.split("/"); + if (pathParts[0] === "") pathParts.shift(); + const bucket = pathParts.shift(); + const backupPrefix = pathParts.join("/"); + return { s3Config, bucket, backupPrefix }; +} + +export async function listS3Backups(req, res) { + const serverSpec = req.body; + if (!serverSpec.id) return res.status(400).send("Server id missing!"); + const authorized = await checkAuthorization(serverSpec.id, req.cairoId); + if (!authorized) + return res + .status(403) + .send("You do not have permission to access that server!"); + const s3Data = await getS3BackupData(serverSpec.id); + if (!s3Data) return res.status(409).send("Backup not configured!"); + const { s3Config, bucket, backupPrefix } = s3Data; + const s3Client = new S3(s3Config); + try { + const listResponse = await s3Client.listObjectsV2({ + Bucket: bucket, + Prefix: backupPrefix, + }); + const files = + listResponse.Contents?.map((f) => ({ + name: basename(f.Key), + lastModified: f.LastModified, + path: f.Key, + size: f.Size, + })) ?? []; + res.json(files); + } catch (e) { + ERR("S3", e); + res.sendStatus(500); + } +} + +export async function getS3BackupUrl(req, res) { + const serverSpec = req.body; + if (!serverSpec.id) return res.status(400).send("Server id missing!"); + if (!serverSpec.backupPath) + return res.status(400).send("Backup path missing!"); + const authorized = await checkAuthorization(serverSpec.id, req.cairoId); + if (!authorized) + return res + .status(403) + .send("You do not have permission to access that server!"); + const s3Data = await getS3BackupData(serverSpec.id); + if (!s3Data) return res.status(409).send("Backup not configured!"); + const { s3Config, bucket } = s3Data; + const s3Client = new S3(s3Config); + try { + const command = new GetObjectCommand({ + Bucket: bucket, + Key: serverSpec.backupPath, + }); + const url = await getSignedUrl(s3Client, command, { expiresIn: 3600 }); + res.json({ url }); + } catch (e) { + ERR("S3", e); + res.sendStatus(500); + } +} diff --git a/lib/routes/s3-route.js b/lib/routes/s3-route.js new file mode 100644 index 0000000..c97afb0 --- /dev/null +++ b/lib/routes/s3-route.js @@ -0,0 +1,11 @@ +import { Router, json as jsonMiddleware } from "express"; +import { getS3BackupUrl, listS3Backups } from "../controllers/s3-controller.js"; +import cairoAuthMiddleware from "./middlewares/auth-middleware.js"; + +const router = Router(); +router.use([cairoAuthMiddleware, jsonMiddleware()]); + +router.post("/backups", listS3Backups); +router.post("/backup-url", getS3BackupUrl); + +export default router; diff --git a/lib/server/router.js b/lib/server/router.js index b4eb444..685dd1d 100644 --- a/lib/server/router.js +++ b/lib/server/router.js @@ -8,6 +8,7 @@ import systemRoute from "../routes/system-route.js"; import serverRoute from "../routes/server-route.js"; import filesRoute from "../routes/files-route.js"; import reactRoute from "../routes/react-route.js"; +import s3Route from "../routes/s3-route.js"; import { logErrors, clientErrorHandler, @@ -27,6 +28,7 @@ export default function buildRoutes(pg, skio) { router.use("/api/system", systemRoute); router.use("/api/server", serverRoute); router.use("/api/files", filesRoute); + router.use("/api/s3", s3Route); router.use(["/mcl", "/mcl/*"], reactRoute); // Static Build Route /*router.use(logErrors); router.use(clientErrorHandler); diff --git a/package-lock.json b/package-lock.json index af76135..cc9f68d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,9 @@ "version": "0.0.1-alpha.1", "license": "LGPL-2.1", "dependencies": { + "@aws-sdk/client-s3": "^3.529.1", + "@aws-sdk/s3-request-presigner": "^3.529.1", "@kubernetes/client-node": "^0.20.0", - "aws-sdk": "^2.1555.0", "basic-ftp": "^5.0.4", "bcrypt": "^5.1.1", "chalk": "^5.3.0", @@ -66,7 +67,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "peer": true, "dependencies": { "@aws-crypto/util": "^3.0.0", "@aws-sdk/types": "^3.222.0", @@ -76,14 +76,12 @@ "node_modules/@aws-crypto/crc32/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "peer": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/crc32c": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz", "integrity": "sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==", - "peer": true, "dependencies": { "@aws-crypto/util": "^3.0.0", "@aws-sdk/types": "^3.222.0", @@ -93,14 +91,12 @@ "node_modules/@aws-crypto/crc32c/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "peer": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/ie11-detection": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "peer": true, "dependencies": { "tslib": "^1.11.1" } @@ -108,14 +104,12 @@ "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "peer": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/sha1-browser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz", "integrity": "sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==", - "peer": true, "dependencies": { "@aws-crypto/ie11-detection": "^3.0.0", "@aws-crypto/supports-web-crypto": "^3.0.0", @@ -129,14 +123,12 @@ "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "peer": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/sha256-browser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "peer": true, "dependencies": { "@aws-crypto/ie11-detection": "^3.0.0", "@aws-crypto/sha256-js": "^3.0.0", @@ -151,14 +143,12 @@ "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "peer": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/sha256-js": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "peer": true, "dependencies": { "@aws-crypto/util": "^3.0.0", "@aws-sdk/types": "^3.222.0", @@ -168,14 +158,12 @@ "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "peer": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "peer": true, "dependencies": { "tslib": "^1.11.1" } @@ -183,14 +171,12 @@ "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "peer": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-crypto/util": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "peer": true, "dependencies": { "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", @@ -200,69 +186,69 @@ "node_modules/@aws-crypto/util/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "peer": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-sdk/client-s3": { - "version": "3.427.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.427.0.tgz", - "integrity": "sha512-YKjJ9zgn0oE393HURKgvjNoX6lxUjb+dkTBE1GymFnGCPl6VxQbKXajXWNqUyN+oPPlZ2osEiljPaN0RserUjA==", - "peer": true, + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.529.1.tgz", + "integrity": "sha512-ZpvyO4w3XWo/OjXLd3fm7CLcKUUYcyady9qzTnKKSnp8a2NqO7UvU/1zhYdm+yyy8TR/9t7sDy+q6AYd4Nsr8g==", "dependencies": { "@aws-crypto/sha1-browser": "3.0.0", "@aws-crypto/sha256-browser": "3.0.0", "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.427.0", - "@aws-sdk/credential-provider-node": "3.427.0", - "@aws-sdk/middleware-bucket-endpoint": "3.425.0", - "@aws-sdk/middleware-expect-continue": "3.425.0", - "@aws-sdk/middleware-flexible-checksums": "3.425.0", - "@aws-sdk/middleware-host-header": "3.425.0", - "@aws-sdk/middleware-location-constraint": "3.425.0", - "@aws-sdk/middleware-logger": "3.425.0", - "@aws-sdk/middleware-recursion-detection": "3.425.0", - "@aws-sdk/middleware-sdk-s3": "3.427.0", - "@aws-sdk/middleware-signing": "3.425.0", - "@aws-sdk/middleware-ssec": "3.425.0", - "@aws-sdk/middleware-user-agent": "3.427.0", - "@aws-sdk/region-config-resolver": "3.425.0", - "@aws-sdk/signature-v4-multi-region": "3.425.0", - "@aws-sdk/types": "3.425.0", - "@aws-sdk/util-endpoints": "3.427.0", - "@aws-sdk/util-user-agent-browser": "3.425.0", - "@aws-sdk/util-user-agent-node": "3.425.0", - "@aws-sdk/xml-builder": "3.310.0", - "@smithy/config-resolver": "^2.0.11", - "@smithy/eventstream-serde-browser": "^2.0.10", - "@smithy/eventstream-serde-config-resolver": "^2.0.10", - "@smithy/eventstream-serde-node": "^2.0.10", - "@smithy/fetch-http-handler": "^2.2.1", - "@smithy/hash-blob-browser": "^2.0.10", - "@smithy/hash-node": "^2.0.10", - "@smithy/hash-stream-node": "^2.0.10", - "@smithy/invalid-dependency": "^2.0.10", - "@smithy/md5-js": "^2.0.10", - "@smithy/middleware-content-length": "^2.0.12", - "@smithy/middleware-endpoint": "^2.0.10", - "@smithy/middleware-retry": "^2.0.13", - "@smithy/middleware-serde": "^2.0.10", - "@smithy/middleware-stack": "^2.0.4", - "@smithy/node-config-provider": "^2.0.13", - "@smithy/node-http-handler": "^2.1.6", - "@smithy/protocol-http": "^3.0.6", - "@smithy/smithy-client": "^2.1.9", - "@smithy/types": "^2.3.4", - "@smithy/url-parser": "^2.0.10", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.13", - "@smithy/util-defaults-mode-node": "^2.0.15", - "@smithy/util-retry": "^2.0.3", - "@smithy/util-stream": "^2.0.14", - "@smithy/util-utf8": "^2.0.0", - "@smithy/util-waiter": "^2.0.10", - "fast-xml-parser": "4.2.5", + "@aws-sdk/client-sts": "3.529.1", + "@aws-sdk/core": "3.529.1", + "@aws-sdk/credential-provider-node": "3.529.1", + "@aws-sdk/middleware-bucket-endpoint": "3.525.0", + "@aws-sdk/middleware-expect-continue": "3.523.0", + "@aws-sdk/middleware-flexible-checksums": "3.523.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-location-constraint": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-sdk-s3": "3.525.0", + "@aws-sdk/middleware-signing": "3.523.0", + "@aws-sdk/middleware-ssec": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/signature-v4-multi-region": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@aws-sdk/xml-builder": "3.523.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/eventstream-serde-browser": "^2.1.3", + "@smithy/eventstream-serde-config-resolver": "^2.1.3", + "@smithy/eventstream-serde-node": "^2.1.3", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-blob-browser": "^2.1.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/hash-stream-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/md5-js": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-stream": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "@smithy/util-waiter": "^2.1.3", "tslib": "^2.5.0" }, "engines": { @@ -270,92 +256,166 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.427.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.427.0.tgz", - "integrity": "sha512-sFVFEmsQ1rmgYO1SgrOTxE/MTKpeE4hpOkm1WqhLQK7Ij136vXpjCxjH1JYZiHiUzO1wr9t4ex4dlB5J3VS/Xg==", - "peer": true, + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.529.1.tgz", + "integrity": "sha512-KT1U/ZNjDhVv2ZgjzaeAn9VM7l667yeSguMrRYC8qk5h91/61MbjZypi6eOuKuVM+0fsQvzKScTQz0Lio0eYag==", "dependencies": { "@aws-crypto/sha256-browser": "3.0.0", "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.425.0", - "@aws-sdk/middleware-logger": "3.425.0", - "@aws-sdk/middleware-recursion-detection": "3.425.0", - "@aws-sdk/middleware-user-agent": "3.427.0", - "@aws-sdk/region-config-resolver": "3.425.0", - "@aws-sdk/types": "3.425.0", - "@aws-sdk/util-endpoints": "3.427.0", - "@aws-sdk/util-user-agent-browser": "3.425.0", - "@aws-sdk/util-user-agent-node": "3.425.0", - "@smithy/config-resolver": "^2.0.11", - "@smithy/fetch-http-handler": "^2.2.1", - "@smithy/hash-node": "^2.0.10", - "@smithy/invalid-dependency": "^2.0.10", - "@smithy/middleware-content-length": "^2.0.12", - "@smithy/middleware-endpoint": "^2.0.10", - "@smithy/middleware-retry": "^2.0.13", - "@smithy/middleware-serde": "^2.0.10", - "@smithy/middleware-stack": "^2.0.4", - "@smithy/node-config-provider": "^2.0.13", - "@smithy/node-http-handler": "^2.1.6", - "@smithy/protocol-http": "^3.0.6", - "@smithy/smithy-client": "^2.1.9", - "@smithy/types": "^2.3.4", - "@smithy/url-parser": "^2.0.10", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.13", - "@smithy/util-defaults-mode-node": "^2.0.15", - "@smithy/util-retry": "^2.0.3", - "@smithy/util-utf8": "^2.0.0", + "@aws-sdk/core": "3.529.1", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.427.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.427.0.tgz", - "integrity": "sha512-le2wLJKILyWuRfPz2HbyaNtu5kEki+ojUkTqCU6FPDRrqUvEkaaCBH9Awo/2AtrCfRkiobop8RuTTj6cAnpiJg==", - "peer": true, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.529.1.tgz", + "integrity": "sha512-bimxCWAvRnVcluWEQeadXvHyzWlBWsuGVligsaVZaGF0TLSn0eLpzpN9B1EhHzTf7m0Kh/wGtPSH1JxO6PpB+A==", "dependencies": { "@aws-crypto/sha256-browser": "3.0.0", "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.427.0", - "@aws-sdk/middleware-host-header": "3.425.0", - "@aws-sdk/middleware-logger": "3.425.0", - "@aws-sdk/middleware-recursion-detection": "3.425.0", - "@aws-sdk/middleware-sdk-sts": "3.425.0", - "@aws-sdk/middleware-signing": "3.425.0", - "@aws-sdk/middleware-user-agent": "3.427.0", - "@aws-sdk/region-config-resolver": "3.425.0", - "@aws-sdk/types": "3.425.0", - "@aws-sdk/util-endpoints": "3.427.0", - "@aws-sdk/util-user-agent-browser": "3.425.0", - "@aws-sdk/util-user-agent-node": "3.425.0", - "@smithy/config-resolver": "^2.0.11", - "@smithy/fetch-http-handler": "^2.2.1", - "@smithy/hash-node": "^2.0.10", - "@smithy/invalid-dependency": "^2.0.10", - "@smithy/middleware-content-length": "^2.0.12", - "@smithy/middleware-endpoint": "^2.0.10", - "@smithy/middleware-retry": "^2.0.13", - "@smithy/middleware-serde": "^2.0.10", - "@smithy/middleware-stack": "^2.0.4", - "@smithy/node-config-provider": "^2.0.13", - "@smithy/node-http-handler": "^2.1.6", - "@smithy/protocol-http": "^3.0.6", - "@smithy/smithy-client": "^2.1.9", - "@smithy/types": "^2.3.4", - "@smithy/url-parser": "^2.0.10", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.13", - "@smithy/util-defaults-mode-node": "^2.0.15", - "@smithy/util-retry": "^2.0.3", - "@smithy/util-utf8": "^2.0.0", + "@aws-sdk/client-sts": "3.529.1", + "@aws-sdk/core": "3.529.1", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.529.1" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.529.1.tgz", + "integrity": "sha512-Rvk2Sr3MACQTOtngUU+omlf4E17k47dRVXR7OFRD6Ow5iGgC9tkN2q/ExDPW/ktPOmM0lSgzWyQ6/PC/Zq3HUg==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.529.1", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.529.1" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.529.1.tgz", + "integrity": "sha512-Sj42sYPfaL9PHvvciMICxhyrDZjqnnvFbPKDmQL5aFKyXy122qx7RdVqUOQERDmMQfvJh6+0W1zQlLnre89q4Q==", + "dependencies": { + "@smithy/core": "^1.3.5", + "@smithy/protocol-http": "^3.2.1", + "@smithy/signature-v4": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", "fast-xml-parser": "4.2.5", "tslib": "^2.5.0" }, @@ -364,14 +424,32 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.425.0.tgz", - "integrity": "sha512-J20etnLvMKXRVi5FK4F8yOCNm2RTaQn5psQTGdDEPWJNGxohcSpzzls8U2KcMyUJ+vItlrThr4qwgpHG3i/N0w==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.523.0.tgz", + "integrity": "sha512-Y6DWdH6/OuMDoNKVzZlNeBc6f1Yjk1lYMjANKpIhMbkRCvLJw/PYZKOZa8WpXbTYdgg9XLjKybnLIb3ww3uuzA==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.525.0.tgz", + "integrity": "sha512-RNWQGuSBQZhl3iqklOslUEfQ4br1V3DCPboMpeqFtddUWJV3m2u2extFur9/4Uy+1EHVF120IwZUKtd8dF+ibw==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/property-provider": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-stream": "^2.1.3", "tslib": "^2.5.0" }, "engines": { @@ -379,20 +457,20 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.427.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.427.0.tgz", - "integrity": "sha512-NmH1cO/w98CKMltYec3IrJIIco19wRjATFNiw83c+FGXZ+InJwReqBnruxIOmKTx2KDzd6fwU1HOewS7UjaaaQ==", - "peer": true, + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.529.1.tgz", + "integrity": "sha512-RjHsuTvHIwXG7a/3ERexemiD3c9riKMCZQzY2/b0Gg0ButEVbBcMfERtUzWmQ0V4ufe/PEZjP68MH1gupcoF9A==", "dependencies": { - "@aws-sdk/credential-provider-env": "3.425.0", - "@aws-sdk/credential-provider-process": "3.425.0", - "@aws-sdk/credential-provider-sso": "3.427.0", - "@aws-sdk/credential-provider-web-identity": "3.425.0", - "@aws-sdk/types": "3.425.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.3.4", + "@aws-sdk/client-sts": "3.529.1", + "@aws-sdk/credential-provider-env": "3.523.0", + "@aws-sdk/credential-provider-process": "3.523.0", + "@aws-sdk/credential-provider-sso": "3.529.1", + "@aws-sdk/credential-provider-web-identity": "3.529.1", + "@aws-sdk/types": "3.523.0", + "@smithy/credential-provider-imds": "^2.2.3", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -400,21 +478,21 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.427.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.427.0.tgz", - "integrity": "sha512-wYYbQ57nKL8OfgRbl8k6uXcdnYml+p3LSSfDUAuUEp1HKlQ8lOXFJ3BdLr5qrk7LhpyppSRnWBmh2c3kWa7ANQ==", - "peer": true, + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.529.1.tgz", + "integrity": "sha512-mvY7F3dMmk/0dZOCfl5sUI1bG0osureBjxhELGCF0KkJqhWI0hIzh8UnPkYytSg3vdc97CMv7pTcozxrdA3b0g==", "dependencies": { - "@aws-sdk/credential-provider-env": "3.425.0", - "@aws-sdk/credential-provider-ini": "3.427.0", - "@aws-sdk/credential-provider-process": "3.425.0", - "@aws-sdk/credential-provider-sso": "3.427.0", - "@aws-sdk/credential-provider-web-identity": "3.425.0", - "@aws-sdk/types": "3.425.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.3.4", + "@aws-sdk/credential-provider-env": "3.523.0", + "@aws-sdk/credential-provider-http": "3.525.0", + "@aws-sdk/credential-provider-ini": "3.529.1", + "@aws-sdk/credential-provider-process": "3.523.0", + "@aws-sdk/credential-provider-sso": "3.529.1", + "@aws-sdk/credential-provider-web-identity": "3.529.1", + "@aws-sdk/types": "3.523.0", + "@smithy/credential-provider-imds": "^2.2.3", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -422,15 +500,14 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.425.0.tgz", - "integrity": "sha512-YY6tkLdvtb1Fgofp3b1UWO+5vwS14LJ/smGmuGpSba0V7gFJRdcrJ9bcb9vVgAGuMdjzRJ+bUKlLLtqXkaykEw==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.523.0.tgz", + "integrity": "sha512-f0LP9KlFmMvPWdKeUKYlZ6FkQAECUeZMmISsv6NKtvPCI9e4O4cLTeR09telwDK8P0HrgcRuZfXM7E30m8re0Q==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -438,17 +515,16 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.427.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.427.0.tgz", - "integrity": "sha512-c+tXyS/i49erHs4bAp6vKNYeYlyQ0VNMBgoco0LCn1rL0REtHbfhWMnqDLF6c2n3yIWDOTrQu0D73Idnpy16eA==", - "peer": true, + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.529.1.tgz", + "integrity": "sha512-KFMKkaoTGDgSJG+o9Ii7AglWG5JQeF6IFw9cXLMwDdIrp3KUmRcUIqe0cjOoCqeQEDGy0VHsimHmKKJ3894i/A==", "dependencies": { - "@aws-sdk/client-sso": "3.427.0", - "@aws-sdk/token-providers": "3.427.0", - "@aws-sdk/types": "3.425.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.3.4", + "@aws-sdk/client-sso": "3.529.1", + "@aws-sdk/token-providers": "3.529.1", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -456,14 +532,14 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.425.0.tgz", - "integrity": "sha512-/0R65TgRzL01JU3SzloivWNwdkbIhr06uY/F5pBHf/DynQqaspKNfdHn6AiozgSVDfwRHFjKBTUy6wvf3QFkuA==", - "peer": true, + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.529.1.tgz", + "integrity": "sha512-AGuZDOKN+AttjwTjrF47WLqzeEut2YynyxjkXZhxZF/xn8i5Y51kUAUdXsXw1bgR25pAeXQIdhsrQlRa1Pm5kw==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.3.4", + "@aws-sdk/client-sts": "3.529.1", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -508,17 +584,16 @@ } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.425.0.tgz", - "integrity": "sha512-7UTfA10fmDw9cgHLApxRUNPywZTG4S/1TNZgTxndO/1OM9ZHtIatw1iLbqJD35gHrpEYI8Vo14YvcnD2ITuiMw==", - "peer": true, + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.525.0.tgz", + "integrity": "sha512-nYfQ2Xspfef7j8mZO7varUWLPH6HQlXateH7tBVtBNUAazyQE4UJEvC0fbQ+Y01e+FKlirim/m2umkdMXqAlTg==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@aws-sdk/util-arn-parser": "3.310.0", - "@smithy/node-config-provider": "^2.0.13", - "@smithy/protocol-http": "^3.0.6", - "@smithy/types": "^2.3.4", - "@smithy/util-config-provider": "^2.0.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-arn-parser": "3.495.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "@smithy/util-config-provider": "^2.2.1", "tslib": "^2.5.0" }, "engines": { @@ -526,14 +601,13 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.425.0.tgz", - "integrity": "sha512-CqAmnDST2o7+sKKw2/ffHKiYKE+jZb/Ce9U0P//ZYzqp9R1Wb016ID+W6DoxufyPJAS9dpRMcUDnAssmMIC/EA==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.523.0.tgz", + "integrity": "sha512-E5DyRAHU39VHaAlQLqXYS/IKpgk3vsryuU6kkOcIIK8Dgw0a2tjoh5AOCaNa8pD+KgAGrFp35JIMSX1zui5diA==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/protocol-http": "^3.0.6", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -541,18 +615,17 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.425.0.tgz", - "integrity": "sha512-BDwn2vVVsC/AzmHXQlaZhEpKXL7GfKFpH7ZFccZuwEQBcyn8lVCcwtfaRe5P1mEe2wklVzOXd1dw8bt0+BOUPA==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.523.0.tgz", + "integrity": "sha512-lIa1TdWY9q4zsDFarfSnYcdrwPR+nypaU4n6hb95i620/1F5M5s6H8P0hYtwTNNvx+slrR8F3VBML9pjBtzAHw==", "dependencies": { "@aws-crypto/crc32": "3.0.0", "@aws-crypto/crc32c": "3.0.0", - "@aws-sdk/types": "3.425.0", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/protocol-http": "^3.0.6", - "@smithy/types": "^2.3.4", - "@smithy/util-utf8": "^2.0.0", + "@aws-sdk/types": "3.523.0", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "@smithy/util-utf8": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -560,14 +633,13 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.425.0.tgz", - "integrity": "sha512-E5Gt41LObQ+cr8QnLthwsH3MtVSNXy1AKJMowDr85h0vzqA/FHUkgHyOGntgozzjXT5M0MaSRYxS0xwTR5D4Ew==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.523.0.tgz", + "integrity": "sha512-4g3q7Ta9sdD9TMUuohBAkbx/e3I/juTqfKi7TPgP+8jxcYX72MOsgemAMHuP6CX27eyj4dpvjH+w4SIVDiDSmg==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/protocol-http": "^3.0.6", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -575,13 +647,12 @@ } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.425.0.tgz", - "integrity": "sha512-3rt0LpGmL1LCRFuEObS1yERd9OEV+AEIAvhY7b53M7u7SyrjWQtpntWkI365L/QljhgMXQBfps2qO4JtrhQnsA==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.523.0.tgz", + "integrity": "sha512-1QAUXX3U0jkARnU0yyjk81EO4Uw5dCeQOtvUY5s3bUOHatR3ThosQeIr6y9BCsbXHzNnDe1ytCjqAPyo8r/bYw==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -589,13 +660,12 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.425.0.tgz", - "integrity": "sha512-INE9XWRXx2f4a/r2vOU0tAmgctVp7nEaEasemNtVBYhqbKLZvr9ndLBSgKGgJ8LIcXAoISipaMuFiqIGkFsm7A==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.523.0.tgz", + "integrity": "sha512-PeDNJNhfiaZx54LBaLTXzUaJ9LXFwDFFIksipjqjvxMafnoVcQwKbkoPUWLe5ytT4nnL1LogD3s55mERFUsnwg==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -603,14 +673,13 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.425.0.tgz", - "integrity": "sha512-77gnzJ5b91bgD75L/ugpOyerx6lR3oyS4080X1YI58EzdyBMkDrHM4FbMcY2RynETi3lwXCFzLRyZjWXY1mRlw==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.523.0.tgz", + "integrity": "sha512-nZ3Vt7ehfSDYnrcg/aAfjjvpdE+61B3Zk68i6/hSUIegT3IH9H1vSW67NDKVp+50hcEfzWwM2HMPXxlzuyFyrw==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/protocol-http": "^3.0.6", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -618,31 +687,18 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.427.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.427.0.tgz", - "integrity": "sha512-virGCf9vsqYCLpmngLOZOVSYgVr2cCOCvTuRoT9vf5tD/63JwaC173jnbdoJO6CWI7ID5Iz0eNdgITXVQ2mpew==", - "peer": true, + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.525.0.tgz", + "integrity": "sha512-ewFyyFM6wdFTOqCiId5GQNi7owDdLEonQhB4h8tF6r3HV52bRlDvZA4aDos+ft6N/XY2J6L0qlFTFq+/oiurXw==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@aws-sdk/util-arn-parser": "3.310.0", - "@smithy/protocol-http": "^3.0.6", - "@smithy/smithy-client": "^2.1.9", - "@smithy/types": "^2.3.4", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.425.0.tgz", - "integrity": "sha512-JFojrg76oKAoBknnr9EL5N2aJ1mRCtBqXoZYST58GSx8uYdFQ89qS65VNQ8JviBXzsrCNAn4vDhZ5Ch5E6TxGQ==", - "peer": true, - "dependencies": { - "@aws-sdk/middleware-signing": "3.425.0", - "@aws-sdk/types": "3.425.0", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-arn-parser": "3.495.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/protocol-http": "^3.2.1", + "@smithy/signature-v4": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-config-provider": "^2.2.1", "tslib": "^2.5.0" }, "engines": { @@ -650,17 +706,16 @@ } }, "node_modules/@aws-sdk/middleware-signing": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.425.0.tgz", - "integrity": "sha512-ZpOfgJHk7ovQ0sSwg3tU4NxFOnz53lJlkJRf7S+wxQALHM0P2MJ6LYBrZaFMVsKiJxNIdZBXD6jclgHg72ZW6Q==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.523.0.tgz", + "integrity": "sha512-pFXV4don6qcmew/OvEjLUr2foVjzoJ8o5k57Oz9yAHz8INx3RHK8MP/K4mVhHo6n0SquRcWrm4kY/Tw+89gkEA==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.6", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.3.4", - "@smithy/util-middleware": "^2.0.3", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/signature-v4": "^2.1.3", + "@smithy/types": "^2.10.1", + "@smithy/util-middleware": "^2.1.3", "tslib": "^2.5.0" }, "engines": { @@ -668,13 +723,12 @@ } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.425.0.tgz", - "integrity": "sha512-9HTuXnHYAZWkwPC8x9tElsQjFPxDT//orbIFauS7VF5HkLCKn9J6O6lW1wKMxrEnDwfN/Vi3nw479MoPj5Ss0Q==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.523.0.tgz", + "integrity": "sha512-FaqAZQeF5cQzZLOIboIJRaWVOQ2F2pJZAXGF5D7nJsxYNFChotA0O0iWimBRxU35RNn7yirVxz35zQzs20ddIw==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -682,15 +736,14 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.427.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.427.0.tgz", - "integrity": "sha512-y9HxYsNvnA3KqDl8w1jHeCwz4P9CuBEtu/G+KYffLeAMBsMZmh4SIkFFCO9wE/dyYg6+yo07rYcnnIfy7WA0bw==", - "peer": true, + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.525.0.tgz", + "integrity": "sha512-4al/6uO+t/QIYXK2OgqzDKQzzLAYJza1vWFS+S0lJ3jLNGyLB5BMU5KqWjDzevYZ4eCnz2Nn7z0FveUTNz8YdQ==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@aws-sdk/util-endpoints": "3.427.0", - "@smithy/protocol-http": "^3.0.6", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -698,15 +751,33 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.425.0.tgz", - "integrity": "sha512-u7uv/iUOapIJdRgRkO3wnpYsUgV6ponsZJQgVg/8L+n+Vo5PQL5gAcIuAOwcYSKQPFaeK+KbmByI4SyOK203Vw==", - "peer": true, + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.525.0.tgz", + "integrity": "sha512-8kFqXk6UyKgTMi7N7QlhA6qM4pGPWbiUXqEY2RgUWngtxqNFGeM9JTexZeuavQI+qLLe09VPShPNX71fEDcM6w==", "dependencies": { - "@smithy/node-config-provider": "^2.0.13", - "@smithy/types": "^2.3.4", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.3", + "@aws-sdk/types": "3.523.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.529.1.tgz", + "integrity": "sha512-54nNN/LjqlyUDTLO3U9D7xkYK4/UttcqfKoHQuPI6QabqZGT1hMFs5SzsyihNchgxci6ZTo4pqQQ3lGfE/HHOA==", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-format-url": "3.523.0", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -714,15 +785,15 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.425.0.tgz", - "integrity": "sha512-7n2FRPE9rLaVa26xXQJ8TExrt53dWN824axQd1a0r5va0SmMQYG/iV5LBmwUlAntUSq46Lse4Q5YnbOVedGOmw==", - "peer": true, + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.525.0.tgz", + "integrity": "sha512-j8gkdfiokaherRgokfZBl2azYBMHlegT7pOnR/3Y79TSz6G+bJeIkuNk8aUbJArr6R8nvAM1j4dt1rBM+efolQ==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/protocol-http": "^3.0.6", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.3.4", + "@aws-sdk/middleware-sdk-s3": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/signature-v4": "^2.1.3", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -730,45 +801,15 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.427.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.427.0.tgz", - "integrity": "sha512-4E5E+4p8lJ69PBY400dJXF06LUHYx5lkKzBEsYqWWhoZcoftrvi24ltIhUDoGVLkrLcTHZIWSdFAWSos4hXqeg==", - "peer": true, + "version": "3.529.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.529.1.tgz", + "integrity": "sha512-NpgMjsfpqiugbxrYGXtta914N43Mx/H0niidqv8wKMTgWQEtsJvYtOni+kuLXB+LmpjaMFNlpadooFU/bK4buA==", "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.425.0", - "@aws-sdk/middleware-logger": "3.425.0", - "@aws-sdk/middleware-recursion-detection": "3.425.0", - "@aws-sdk/middleware-user-agent": "3.427.0", - "@aws-sdk/types": "3.425.0", - "@aws-sdk/util-endpoints": "3.427.0", - "@aws-sdk/util-user-agent-browser": "3.425.0", - "@aws-sdk/util-user-agent-node": "3.425.0", - "@smithy/config-resolver": "^2.0.11", - "@smithy/fetch-http-handler": "^2.2.1", - "@smithy/hash-node": "^2.0.10", - "@smithy/invalid-dependency": "^2.0.10", - "@smithy/middleware-content-length": "^2.0.12", - "@smithy/middleware-endpoint": "^2.0.10", - "@smithy/middleware-retry": "^2.0.13", - "@smithy/middleware-serde": "^2.0.10", - "@smithy/middleware-stack": "^2.0.4", - "@smithy/node-config-provider": "^2.0.13", - "@smithy/node-http-handler": "^2.1.6", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.6", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/smithy-client": "^2.1.9", - "@smithy/types": "^2.3.4", - "@smithy/url-parser": "^2.0.10", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.13", - "@smithy/util-defaults-mode-node": "^2.0.15", - "@smithy/util-retry": "^2.0.3", - "@smithy/util-utf8": "^2.0.0", + "@aws-sdk/client-sso-oidc": "3.529.1", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -776,12 +817,11 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.425.0.tgz", - "integrity": "sha512-6lqbmorwerN4v+J5dqbHPAsjynI0mkEF+blf+69QTaKKGaxBBVaXgqoqul9RXYcK5MMrrYRbQIMd0zYOoy90kA==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.523.0.tgz", + "integrity": "sha512-AqGIu4u+SxPiUuNBp2acCVcq80KDUFjxe6e3cMTvKWTzCbrVk1AXv0dAaJnCmdkWIha6zJDWxpIk/aL4EGhZ9A==", "dependencies": { - "@smithy/types": "^2.3.4", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -789,10 +829,9 @@ } }, "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.310.0.tgz", - "integrity": "sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==", - "peer": true, + "version": "3.495.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.495.0.tgz", + "integrity": "sha512-hwdA3XAippSEUxs7jpznwD63YYFR+LtQvlEcebPTgWR9oQgG9TfS+39PUfbnEeje1ICuOrN3lrFqFbmP9uzbMg==", "dependencies": { "tslib": "^2.5.0" }, @@ -801,13 +840,27 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.427.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.427.0.tgz", - "integrity": "sha512-rSyiAIFF/EVvity/+LWUqoTMJ0a25RAc9iqx0WZ4tf1UjuEXRRXxZEb+jEZg1bk+pY84gdLdx9z5E+MSJCZxNQ==", - "peer": true, + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.525.0.tgz", + "integrity": "sha512-DIW7WWU5tIGkeeKX6NJUyrEIdWMiqjLQG3XBzaUj+ufIENwNjdAHhlD8l2vX7Yr3JZRT6yN/84wBCj7Tw1xd1g==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/node-config-provider": "^2.0.13", + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", + "@smithy/util-endpoints": "^1.1.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.523.0.tgz", + "integrity": "sha512-OWi+8bsEfxG4DvHkWauxyWVZMbYrezC49DbGDEu1lJgk9eqQALlyGkZHt9O8KKfyT/mdqQbR8qbpkxqYcGuHVA==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/querystring-builder": "^2.1.3", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -818,7 +871,6 @@ "version": "3.310.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", - "peer": true, "dependencies": { "tslib": "^2.5.0" }, @@ -827,26 +879,24 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.425.0.tgz", - "integrity": "sha512-22Y9iMtjGcFjGILR6/xdp1qRezlHVLyXtnpEsbuPTiernRCPk6zfAnK/ATH77r02MUjU057tdxVkd5umUBTn9Q==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.523.0.tgz", + "integrity": "sha512-6ZRNdGHX6+HQFqTbIA5+i8RWzxFyxsZv8D3soRfpdyWIKkzhSz8IyRKXRciwKBJDaC7OX2jzGE90wxRQft27nA==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", "bowser": "^2.11.0", "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.425.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.425.0.tgz", - "integrity": "sha512-SIR4F5uQeeVAi8lv4OgRirtdtNi5zeyogTuQgGi9su8F/WP1N6JqxofcwpUY5f8/oJ2UlXr/tx1f09UHfJJzvA==", - "peer": true, + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.525.0.tgz", + "integrity": "sha512-88Wjt4efyUSBGcyIuh1dvoMqY1k15jpJc5A/3yi67clBQEFsu9QCodQCQPqmRjV3VRcMtBOk+jeCTiUzTY5dRQ==", "dependencies": { - "@aws-sdk/types": "3.425.0", - "@smithy/node-config-provider": "^2.0.13", - "@smithy/types": "^2.3.4", + "@aws-sdk/types": "3.523.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -865,17 +915,16 @@ "version": "3.259.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "peer": true, "dependencies": { "tslib": "^2.3.1" } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.310.0.tgz", - "integrity": "sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==", - "peer": true, + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.523.0.tgz", + "integrity": "sha512-wfvyVymj2TUw7SuDor9IuFcAzJZvWRBZotvY/wQJOlYa3UP3Oezzecy64N4FWfBJEsZdrTN+HOZFl+IzTWWnUA==", "dependencies": { + "@smithy/types": "^2.10.1", "tslib": "^2.5.0" }, "engines": { @@ -1297,6 +1346,7 @@ "version": "7.23.9", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2139,11 +2189,6 @@ "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==", "dev": true }, - "node_modules/@react-dnd/shallowequal": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", - "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" - }, "node_modules/@reduxjs/toolkit": { "version": "1.9.7", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", @@ -2204,11 +2249,11 @@ ] }, "node_modules/@smithy/abort-controller": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.11.tgz", - "integrity": "sha512-MSzE1qR2JNyb7ot3blIOT3O3H0Jn06iNDEgHRaqZUwBgx5EG+VIx24Y21tlKofzYryIOcWpIohLrIIyocD6LMA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.4.tgz", + "integrity": "sha512-66HO817oIZ2otLIqy06R5muapqZjkgF1jfU0wyNko8cuqZNu8nbS9ljlhcRYw/M/uWRJzB9ih81DLSHhYbBLlQ==", "dependencies": { - "@smithy/types": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2216,34 +2261,49 @@ } }, "node_modules/@smithy/chunked-blob-reader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-2.0.0.tgz", - "integrity": "sha512-k+J4GHJsMSAIQPChGBrjEmGS+WbPonCXesoqP9fynIqjn7rdOThdH8FAeCmokP9mxTYKQAKoHCLPzNlm6gh7Wg==", - "peer": true, + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-2.1.1.tgz", + "integrity": "sha512-NjNFCKxC4jVvn+lUr3Yo4/PmUJj3tbyqH6GNHueyTGS5Q27vlEJ1MkNhUDV8QGxJI7Bodnc2pD18lU2zRfhHlQ==", "dependencies": { "tslib": "^2.5.0" } }, "node_modules/@smithy/chunked-blob-reader-native": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-2.0.0.tgz", - "integrity": "sha512-HM8V2Rp1y8+1343tkZUKZllFhEQPNmpNdgFAncbTsxkZ18/gqjk23XXv3qGyXWp412f3o43ZZ1UZHVcHrpRnCQ==", - "peer": true, + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-2.1.2.tgz", + "integrity": "sha512-KwR9fFc/t5jH9RQFbrA9DHSmI+URTmB4v+i7H08UNET9AcN6GGBTBMiDKpA56Crw6CN7cSaSDXaRS/AsfOuupQ==", "dependencies": { - "@smithy/util-base64": "^2.0.0", + "@smithy/util-base64": "^2.2.0", "tslib": "^2.5.0" } }, "node_modules/@smithy/config-resolver": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.14.tgz", - "integrity": "sha512-K1K+FuWQoy8j/G7lAmK85o03O89s2Vvh6kMFmzEmiHUoQCRH1rzbDtMnGNiaMHeSeYJ6y79IyTusdRG+LuWwtg==", - "peer": true, + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.1.5.tgz", + "integrity": "sha512-LcBB5JQC3Tx2ZExIJzfvWaajhFIwHrUNQeqxhred2r5nnqrdly9uoCrvM1sxOOdghYuWWm2Kr8tBCDOmxsgeTA==", "dependencies": { - "@smithy/node-config-provider": "^2.1.1", - "@smithy/types": "^2.3.5", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.4", + "@smithy/node-config-provider": "^2.2.5", + "@smithy/types": "^2.11.0", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.7.tgz", + "integrity": "sha512-zHrrstOO78g+/rOJoHi4j3mGUBtsljRhcKNzloWPv1XIwgcFUi+F1YFKr2qPQ3z7Ls5dNc4L2SPrVarNFIQqog==", + "dependencies": { + "@smithy/middleware-endpoint": "^2.4.6", + "@smithy/middleware-retry": "^2.1.6", + "@smithy/middleware-serde": "^2.2.1", + "@smithy/protocol-http": "^3.2.2", + "@smithy/smithy-client": "^2.4.4", + "@smithy/types": "^2.11.0", + "@smithy/util-middleware": "^2.1.4", "tslib": "^2.5.0" }, "engines": { @@ -2251,15 +2311,14 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.16.tgz", - "integrity": "sha512-tKa2xF+69TvGxJT+lnJpGrKxUuAZDLYXFhqnPEgnHz+psTpkpcB4QRjHj63+uj83KaeFJdTfW201eLZeRn6FfA==", - "peer": true, + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.6.tgz", + "integrity": "sha512-+xQe4Pite0kdk9qn0Vyw5BRVh0iSlj+T4TEKRXr4E1wZKtVgIzGlkCrfICSjiPVFkPxk4jMpVboMYdEiiA88/w==", "dependencies": { - "@smithy/node-config-provider": "^2.1.1", - "@smithy/property-provider": "^2.0.12", - "@smithy/types": "^2.3.5", - "@smithy/url-parser": "^2.0.11", + "@smithy/node-config-provider": "^2.2.5", + "@smithy/property-provider": "^2.1.4", + "@smithy/types": "^2.11.0", + "@smithy/url-parser": "^2.1.4", "tslib": "^2.5.0" }, "engines": { @@ -2267,25 +2326,23 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.11.tgz", - "integrity": "sha512-BQCTjxhCYRZIfXapa2LmZSaH8QUBGwMZw7XRN83hrdixbLjIcj+o549zjkedFS07Ve2TlvWUI6BTzP+nv7snBA==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.1.4.tgz", + "integrity": "sha512-UkiieTztP7adg8EuqZvB0Y4LewdleZCJU7Kgt9RDutMsRYqO32fMpWeQHeTHaIMosmzcRZUykMRrhwGJe9mP3A==", "dependencies": { "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.3.5", - "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/types": "^2.11.0", + "@smithy/util-hex-encoding": "^2.1.1", "tslib": "^2.5.0" } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.0.11.tgz", - "integrity": "sha512-p9IK4uvwT6B3pT1VGlODvcVBfPVikjBFHAcKpvvNF+7lAEI+YiC6d0SROPkpjnvCgVBYyGXa3ciqrWnFze6mwQ==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.1.4.tgz", + "integrity": "sha512-K0SyvrUu/vARKzNW+Wp9HImiC/cJ6K88/n7FTH1slY+MErdKoiSbRLaXbJ9qD6x1Hu28cplHMlhADwZelUx/Ww==", "dependencies": { - "@smithy/eventstream-serde-universal": "^2.0.11", - "@smithy/types": "^2.3.5", + "@smithy/eventstream-serde-universal": "^2.1.4", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2293,12 +2350,11 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.0.11.tgz", - "integrity": "sha512-vN32E8yExo0Z8L7kXhlU9KRURrhqOpPdLxQMp3MwfMThrjiqbr1Sk5srUXc1ed2Ygl/l0TEN9vwNG0bQHg6AjQ==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.1.4.tgz", + "integrity": "sha512-FH+2AwOwZ0kHPB9sciWJtUqx81V4vizfT3P6T9eslmIC2hi8ch/KFvQlF7jDmwR1aLlPlq6qqLKLqzK/71Ki4A==", "dependencies": { - "@smithy/types": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2306,13 +2362,12 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.0.11.tgz", - "integrity": "sha512-Gjqbpg7UmD+YzkpgNShNcDNZcUpBWIkvX2XCGptz5PoxJU/UQbuF9eSc93ZlIb7j4aGjtFfqk23HUMW8Hopg2Q==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.1.4.tgz", + "integrity": "sha512-gsc5ZTvVcB9sleLQzsK/rOhgn52+AAsmhEr41WDwAcctccBjh429+b8gT9t+SU8QyajypfsLOZfJQu0+zE515Q==", "dependencies": { - "@smithy/eventstream-serde-universal": "^2.0.11", - "@smithy/types": "^2.3.5", + "@smithy/eventstream-serde-universal": "^2.1.4", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2320,13 +2375,12 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.0.11.tgz", - "integrity": "sha512-F8FsxLTbFN4+Esgpo+nNKcEajrgRZJ+pG9c8+MhLM4Odp5ejLHw2GMCXd81cGsgmfcbnzdDEXazPPVzOwj89MQ==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.1.4.tgz", + "integrity": "sha512-NKLAsYnZA5s+ntipJRKo1RrRbhYHrsEnmiUoz0EhVYrAih+UELY9sKR+A1ujGaFm3nKDs5fPfiozC2wpXq2zUA==", "dependencies": { - "@smithy/eventstream-codec": "^2.0.11", - "@smithy/types": "^2.3.5", + "@smithy/eventstream-codec": "^2.1.4", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2334,38 +2388,36 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.2.2.tgz", - "integrity": "sha512-K7aRtRuaBjzlk+jWWeyfDTLAmRRvmA4fU8eHUXtjsuEDgi3f356ZE32VD2ssxIH13RCLVZbXMt5h7wHzYiSuVA==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.4.tgz", + "integrity": "sha512-DSUtmsnIx26tPuyyrK49dk2DAhPgEw6xRW7V62nMHIB5dk3NqhGnwcKO2fMdt/l3NUVgia34ZsSJA8bD+3nh7g==", "dependencies": { - "@smithy/protocol-http": "^3.0.7", - "@smithy/querystring-builder": "^2.0.11", - "@smithy/types": "^2.3.5", - "@smithy/util-base64": "^2.0.0", + "@smithy/protocol-http": "^3.2.2", + "@smithy/querystring-builder": "^2.1.4", + "@smithy/types": "^2.11.0", + "@smithy/util-base64": "^2.2.0", "tslib": "^2.5.0" } }, "node_modules/@smithy/hash-blob-browser": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-2.0.11.tgz", - "integrity": "sha512-/6vq/NiH2EN3mWdwcLdjVohP+VCng+ZA1GnlUdx959egsfgIlLWQvCyjnB2ze9Hr6VHV5XEFLLpLQH2dHA6Sgw==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-2.1.4.tgz", + "integrity": "sha512-bDugS1DortnriGDdp0sqdq7dLI5if8CEOF9rKtpJa1ZYMq6fxOtTId//dlilS5QgUtUs6GHN5aMQVxEjhBzzQA==", "dependencies": { - "@smithy/chunked-blob-reader": "^2.0.0", - "@smithy/chunked-blob-reader-native": "^2.0.0", - "@smithy/types": "^2.3.5", + "@smithy/chunked-blob-reader": "^2.1.1", + "@smithy/chunked-blob-reader-native": "^2.1.2", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" } }, "node_modules/@smithy/hash-node": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.11.tgz", - "integrity": "sha512-PbleVugN2tbhl1ZoNWVrZ1oTFFas/Hq+s6zGO8B9bv4w/StTriTKA9W+xZJACOj9X7zwfoTLbscM+avCB1KqOQ==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.4.tgz", + "integrity": "sha512-uvCcpDLXaTTL0X/9ezF8T8sS77UglTfZVQaUOBiCvR0QydeSyio3t0Hj3QooVdyFsKTubR8gCk/ubLk3vAyDng==", "dependencies": { - "@smithy/types": "^2.3.5", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", + "@smithy/types": "^2.11.0", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.2.0", "tslib": "^2.5.0" }, "engines": { @@ -2373,13 +2425,12 @@ } }, "node_modules/@smithy/hash-stream-node": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-2.0.11.tgz", - "integrity": "sha512-Jn2yl+Dn0kvwKvSavvR1/BFVYa2wIkaJKWeTH48kno89gqHAJxMh1hrtBN6SJ7F8VhodNZTiNOlQVqCSfLheNQ==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-2.1.4.tgz", + "integrity": "sha512-HcDQRs/Fcx7lwAd+/vSW/e7ltdh148D2Pq7XI61CEWcOoQdQ0W8aYBHDRC4zjtXv6hySdmWE+vo3dvdTt7aj8A==", "dependencies": { - "@smithy/types": "^2.3.5", - "@smithy/util-utf8": "^2.0.0", + "@smithy/types": "^2.11.0", + "@smithy/util-utf8": "^2.2.0", "tslib": "^2.5.0" }, "engines": { @@ -2387,19 +2438,18 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.11.tgz", - "integrity": "sha512-zazq99ujxYv/NOf9zh7xXbNgzoVLsqE0wle8P/1zU/XdhPi/0zohTPKWUzIxjGdqb5hkkwfBkNkl5H+LE0mvgw==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.4.tgz", + "integrity": "sha512-QzlNBl6jt3nb9jNnE51wTegReVvUdozyMMrFEyb/rc6AzPID1O+qMJYjAAoNw098y0CZVfCpEnoK2+mfBOd8XA==", "dependencies": { - "@smithy/types": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" } }, "node_modules/@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz", + "integrity": "sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ==", "dependencies": { "tslib": "^2.5.0" }, @@ -2408,24 +2458,22 @@ } }, "node_modules/@smithy/md5-js": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-2.0.11.tgz", - "integrity": "sha512-YBIv+e95qeGvQA05ucwstmTeQ/bUzWgU+nO2Ffmif5awu6IzSR0Jfk3XLYh4mdy7f8DCgsn8qA63u7N9Lu0+5A==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-2.1.4.tgz", + "integrity": "sha512-WHTnnYJPKE7Sy49DogLuox42TnlwD3cQ6TObPD6WFWjKocWIdpEpIvdJHwWUfSFf0JIi8ON8z6ZEhsnyKVCcLQ==", "dependencies": { - "@smithy/types": "^2.3.5", - "@smithy/util-utf8": "^2.0.0", + "@smithy/types": "^2.11.0", + "@smithy/util-utf8": "^2.2.0", "tslib": "^2.5.0" } }, "node_modules/@smithy/middleware-content-length": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.13.tgz", - "integrity": "sha512-Md2kxWpaec3bXp1oERFPQPBhOXCkGSAF7uc1E+4rkwjgw3/tqAXRtbjbggu67HJdwaif76As8AV6XxbD1HzqTQ==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.1.4.tgz", + "integrity": "sha512-C6VRwfcr0w9qRFhDGCpWMVhlEIBFlmlPRP1aX9Cv9xDj9SUwlDrNvoV1oP1vjRYuLxCDgovBBynCwwcluS2wLw==", "dependencies": { - "@smithy/protocol-http": "^3.0.7", - "@smithy/types": "^2.3.5", + "@smithy/protocol-http": "^3.2.2", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2433,14 +2481,16 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.11.tgz", - "integrity": "sha512-mCugsvB15up6fqpzUEpMT4CuJmFkEI+KcozA7QMzYguXCaIilyMKsyxgamwmr+o7lo3QdjN0//XLQ9bWFL129g==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.6.tgz", + "integrity": "sha512-AsXtUXHPOAS0EGZUSFOsVJvc7p0KL29PGkLxLfycPOcFVLru/oinYB6yvyL73ZZPX2OB8sMYUMrj7eH2kI7V/w==", "dependencies": { - "@smithy/middleware-serde": "^2.0.11", - "@smithy/types": "^2.3.5", - "@smithy/url-parser": "^2.0.11", - "@smithy/util-middleware": "^2.0.4", + "@smithy/middleware-serde": "^2.2.1", + "@smithy/node-config-provider": "^2.2.5", + "@smithy/shared-ini-file-loader": "^2.3.5", + "@smithy/types": "^2.11.0", + "@smithy/url-parser": "^2.1.4", + "@smithy/util-middleware": "^2.1.4", "tslib": "^2.5.0" }, "engines": { @@ -2448,17 +2498,17 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.16.tgz", - "integrity": "sha512-Br5+0yoiMS0ugiOAfJxregzMMGIRCbX4PYo1kDHtLgvkA/d++aHbnHB819m5zOIAMPvPE7AThZgcsoK+WOsUTA==", - "peer": true, + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.1.6.tgz", + "integrity": "sha512-khpSV0NxqMHfa06kfG4WYv+978sVvfTFmn0hIFKKwOXtIxyYtPKiQWFT4nnwZD07fGdYGbtCBu3YALc8SsA5mA==", "dependencies": { - "@smithy/node-config-provider": "^2.1.1", - "@smithy/protocol-http": "^3.0.7", - "@smithy/service-error-classification": "^2.0.4", - "@smithy/types": "^2.3.5", - "@smithy/util-middleware": "^2.0.4", - "@smithy/util-retry": "^2.0.4", + "@smithy/node-config-provider": "^2.2.5", + "@smithy/protocol-http": "^3.2.2", + "@smithy/service-error-classification": "^2.1.4", + "@smithy/smithy-client": "^2.4.4", + "@smithy/types": "^2.11.0", + "@smithy/util-middleware": "^2.1.4", + "@smithy/util-retry": "^2.1.4", "tslib": "^2.5.0", "uuid": "^8.3.2" }, @@ -2470,17 +2520,16 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "peer": true, "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/@smithy/middleware-serde": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.11.tgz", - "integrity": "sha512-NuxnjMyf4zQqhwwdh0OTj5RqpnuT6HcH5Xg5GrPijPcKzc2REXVEVK4Yyk8ckj8ez1XSj/bCmJ+oNjmqB02GWA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.2.1.tgz", + "integrity": "sha512-VAWRWqnNjgccebndpyK94om4ZTYzXLQxUmNCXYzM/3O9MTfQjTNBgtFtQwyIIez6z7LWcCsXmnKVIOE9mLqAHQ==", "dependencies": { - "@smithy/types": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2488,11 +2537,11 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.5.tgz", - "integrity": "sha512-bVQU/rZzBY7CbSxIrDTGZYnBWKtIw+PL/cRc9B7etZk1IKSOe0NvKMJyWllfhfhrTeMF6eleCzOihIQympAvPw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.1.4.tgz", + "integrity": "sha512-Qqs2ba8Ax1rGKOSGJS2JN23fhhox2WMdRuzx0NYHtXzhxbJOIMmz9uQY6Hf4PY8FPteBPp1+h0j5Fmr+oW12sg==", "dependencies": { - "@smithy/types": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2500,14 +2549,13 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.1.tgz", - "integrity": "sha512-1lF6s1YWBi1LBu2O30tD3jyTgMtuvk/Z1twzXM4GPYe4dmZix4nNREPJIPOcfFikNU2o0eTYP80+izx5F2jIJA==", - "peer": true, + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.2.5.tgz", + "integrity": "sha512-CxPf2CXhjO79IypHJLBATB66Dw6suvr1Yc2ccY39hpR6wdse3pZ3E8RF83SODiNH0Wjmkd0ze4OF8exugEixgA==", "dependencies": { - "@smithy/property-provider": "^2.0.12", - "@smithy/shared-ini-file-loader": "^2.2.0", - "@smithy/types": "^2.3.5", + "@smithy/property-provider": "^2.1.4", + "@smithy/shared-ini-file-loader": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2515,14 +2563,14 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.7.tgz", - "integrity": "sha512-PQIKZXlp3awCDn/xNlCSTFE7aYG/5Tx33M05NfQmWYeB5yV1GZZOSz4dXpwiNJYTXb9jPqjl+ueXXkwtEluFFA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.4.2.tgz", + "integrity": "sha512-yrj3c1g145uiK5io+1UPbJAHo8BSGORkBzrmzvAsOmBKb+1p3jmM8ZwNLDH/HTTxVLm9iM5rMszx+iAh1HUC4Q==", "dependencies": { - "@smithy/abort-controller": "^2.0.11", - "@smithy/protocol-http": "^3.0.7", - "@smithy/querystring-builder": "^2.0.11", - "@smithy/types": "^2.3.5", + "@smithy/abort-controller": "^2.1.4", + "@smithy/protocol-http": "^3.2.2", + "@smithy/querystring-builder": "^2.1.4", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2530,12 +2578,11 @@ } }, "node_modules/@smithy/property-provider": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.12.tgz", - "integrity": "sha512-Un/OvvuQ1Kg8WYtoMCicfsFFuHb/TKL3pCA6ZIo/WvNTJTR94RtoRnL7mY4XkkUAoFMyf6KjcQJ76y1FX7S5rw==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.1.4.tgz", + "integrity": "sha512-nWaY/MImj1BiXZ9WY65h45dcxOx8pl06KYoHxwojDxDL+Q9yLU1YnZpgv8zsHhEftlj9KhePENjQTlNowWVyug==", "dependencies": { - "@smithy/types": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2543,11 +2590,11 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.7.tgz", - "integrity": "sha512-HnZW8y+r66ntYueCDbLqKwWcMNWW8o3eVpSrHNluwtBJ/EUWfQHRKSiu6vZZtc6PGfPQWgVfucoCE/C3QufMAA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.2.2.tgz", + "integrity": "sha512-xYBlllOQcOuLoxzhF2u8kRHhIFGQpDeTQj/dBSnw4kfI29WMKL5RnW1m9YjnJAJ49miuIvrkJR+gW5bCQ+Mchw==", "dependencies": { - "@smithy/types": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2555,12 +2602,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.11.tgz", - "integrity": "sha512-b4kEbVMxpmfv2VWUITn2otckTi7GlMteZQxi+jlwedoATOGEyrCJPfRcYQJjbCi3fZ2QTfh3PcORvB27+j38Yg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.4.tgz", + "integrity": "sha512-LXSL0J/nRWvGT+jIj+Fip3j0J1ZmHkUyBFRzg/4SmPNCLeDrtVu7ptKOnTboPsFZu5BxmpYok3kJuQzzRdrhbw==", "dependencies": { - "@smithy/types": "^2.3.5", - "@smithy/util-uri-escape": "^2.0.0", + "@smithy/types": "^2.11.0", + "@smithy/util-uri-escape": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2568,11 +2615,11 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.11.tgz", - "integrity": "sha512-YXe7jhi7s3dQ0Fu9dLoY/gLu6NCyy8tBWJL/v2c9i7/RLpHgKT+uT96/OqZkHizCJ4kr0ZD46tzMjql/o60KLg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.1.4.tgz", + "integrity": "sha512-U2b8olKXgZAs0eRo7Op11jTNmmcC/sqYmsA7vN6A+jkGnDvJlEl7AetUegbBzU8q3D6WzC5rhR/joIy8tXPzIg==", "dependencies": { - "@smithy/types": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2580,24 +2627,22 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.4.tgz", - "integrity": "sha512-77506l12I5gxTZqBkx3Wb0RqMG81bMYLaVQ+EqIWFwQDJRs5UFeXogKxSKojCmz1wLUziHZQXm03MBzPQiumQw==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.4.tgz", + "integrity": "sha512-JW2Hthy21evnvDmYYk1kItOmbp3X5XI5iqorXgFEunb6hQfSDZ7O1g0Clyxg7k/Pcr9pfLk5xDIR2To/IohlsQ==", "dependencies": { - "@smithy/types": "^2.3.5" + "@smithy/types": "^2.11.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.0.tgz", - "integrity": "sha512-xFXqs4vAb5BdkzHSRrTapFoaqS4/3m/CGZzdw46fBjYZ0paYuLAoMY60ICCn1FfGirG+PiJ3eWcqJNe4/SkfyA==", - "peer": true, + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.5.tgz", + "integrity": "sha512-oI99+hOvsM8oAJtxAGmoL/YCcGXtbP0fjPseYGaNmJ4X5xOFTer0KPk7AIH3AL6c5AlYErivEi1X/X78HgTVIw==", "dependencies": { - "@smithy/types": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2605,18 +2650,17 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.11.tgz", - "integrity": "sha512-EFVU1dT+2s8xi227l1A9O27edT/GNKvyAK6lZnIZ0zhIHq/jSLznvkk15aonGAM1kmhmZBVGpI7Tt0odueZK9A==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.4.tgz", + "integrity": "sha512-gnu9gCn0qQ8IdhNjs6o3QVCXzUs33znSDYwVMWo3nX4dM6j7z9u6FC302ShYyVWfO4MkVMuGCCJ6nl3PcH7V1Q==", "dependencies": { - "@smithy/eventstream-codec": "^2.0.11", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.3.5", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.4", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", + "@smithy/eventstream-codec": "^2.1.4", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/types": "^2.11.0", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-middleware": "^2.1.4", + "@smithy/util-uri-escape": "^2.1.1", + "@smithy/util-utf8": "^2.2.0", "tslib": "^2.5.0" }, "engines": { @@ -2624,13 +2668,15 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.10.tgz", - "integrity": "sha512-2OEmZDiW1Z196QHuQZ5M6cBE8FCSG0H2HADP1G+DY8P3agsvb0YJyfhyKuJbxIQy15tr3eDAK6FOrlbxgKOOew==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.4.4.tgz", + "integrity": "sha512-SNE17wjycPZIJ2P5sv6wMTteV/vQVPdaqQkoK1KeGoWHXx79t3iLhQXj1uqRdlkMUS9pXJrLOAS+VvUSOYwQKw==", "dependencies": { - "@smithy/middleware-stack": "^2.0.5", - "@smithy/types": "^2.3.5", - "@smithy/util-stream": "^2.0.15", + "@smithy/middleware-endpoint": "^2.4.6", + "@smithy/middleware-stack": "^2.1.4", + "@smithy/protocol-http": "^3.2.2", + "@smithy/types": "^2.11.0", + "@smithy/util-stream": "^2.1.4", "tslib": "^2.5.0" }, "engines": { @@ -2638,9 +2684,9 @@ } }, "node_modules/@smithy/types": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.3.5.tgz", - "integrity": "sha512-ehyDt8M9hehyxrLQGoA1BGPou8Js1Ocoh5M0ngDhJMqbFmNK5N6Xhr9/ZExWkyIW8XcGkiMPq3ZUEE0ScrhbuQ==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.11.0.tgz", + "integrity": "sha512-AR0SXO7FuAskfNhyGfSTThpLRntDI5bOrU0xrpVYU0rZyjl3LBXInZFMTP/NNSd7IS6Ksdtar0QvnrPRIhVrLQ==", "dependencies": { "tslib": "^2.5.0" }, @@ -2649,21 +2695,22 @@ } }, "node_modules/@smithy/url-parser": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.11.tgz", - "integrity": "sha512-h89yXMCCF+S5k9XIoKltMIWTYj+FcEkU/IIFZ6RtE222fskOTL4Iak6ZRG+ehSvZDt8yKEcxqheTDq7JvvtK3g==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.1.4.tgz", + "integrity": "sha512-1hTy6UYRYqOZlHKH2/2NzdNQ4NNmW2Lp0sYYvztKy+dEQuLvZL9w88zCzFQqqFer3DMcscYOshImxkJTGdV+rg==", "dependencies": { - "@smithy/querystring-parser": "^2.0.11", - "@smithy/types": "^2.3.5", + "@smithy/querystring-parser": "^2.1.4", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" } }, "node_modules/@smithy/util-base64": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", - "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.2.0.tgz", + "integrity": "sha512-RiQI/Txu0SxCR38Ky5BMEVaFfkNTBjpbxlr2UhhxggSmnsHDQPZJWMtPoXs7TWZaseslIlAWMiHmqRT3AV/P2w==", "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.2.0", "tslib": "^2.5.0" }, "engines": { @@ -2671,19 +2718,17 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", - "peer": true, + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.1.tgz", + "integrity": "sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag==", "dependencies": { "tslib": "^2.5.0" } }, "node_modules/@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", - "peer": true, + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.2.1.tgz", + "integrity": "sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg==", "dependencies": { "tslib": "^2.5.0" }, @@ -2692,11 +2737,11 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz", + "integrity": "sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg==", "dependencies": { - "@smithy/is-array-buffer": "^2.0.0", + "@smithy/is-array-buffer": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2704,10 +2749,9 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "peer": true, + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.2.1.tgz", + "integrity": "sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw==", "dependencies": { "tslib": "^2.5.0" }, @@ -2716,14 +2760,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.14.tgz", - "integrity": "sha512-NupG7SWUucm3vJrvlpt9jG1XeoPJphjcivgcUUXhDJbUPy4F04LhlTiAhWSzwlCNcF8OJsMvZ/DWbpYD3pselw==", - "peer": true, + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.6.tgz", + "integrity": "sha512-lM2JMYCilrejfGf8WWnVfrKly3vf+mc5x9TrTpT++qIKP452uWfLqlaUxbz1TkSfhqm8RjrlY22589B9aI8A9w==", "dependencies": { - "@smithy/property-provider": "^2.0.12", - "@smithy/smithy-client": "^2.1.10", - "@smithy/types": "^2.3.5", + "@smithy/property-provider": "^2.1.4", + "@smithy/smithy-client": "^2.4.4", + "@smithy/types": "^2.11.0", "bowser": "^2.11.0", "tslib": "^2.5.0" }, @@ -2732,27 +2775,39 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.18.tgz", - "integrity": "sha512-+3jMom/b/Cdp21tDnY4vKu249Al+G/P0HbRbct7/aSZDlROzv1tksaYukon6UUv7uoHn+/McqnsvqZHLlqvQ0g==", - "peer": true, + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.2.6.tgz", + "integrity": "sha512-UmUbPHbkBJCXRFbq+FPLpVwiFPHj1oPWXJS2f2sy23PtXM94c9X5EceI6JKuKdBty+tzhrAs5JbmPM/HvmDB8Q==", "dependencies": { - "@smithy/config-resolver": "^2.0.14", - "@smithy/credential-provider-imds": "^2.0.16", - "@smithy/node-config-provider": "^2.1.1", - "@smithy/property-provider": "^2.0.12", - "@smithy/smithy-client": "^2.1.10", - "@smithy/types": "^2.3.5", + "@smithy/config-resolver": "^2.1.5", + "@smithy/credential-provider-imds": "^2.2.6", + "@smithy/node-config-provider": "^2.2.5", + "@smithy/property-provider": "^2.1.4", + "@smithy/smithy-client": "^2.4.4", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { "node": ">= 10.0.0" } }, + "node_modules/@smithy/util-endpoints": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.1.5.tgz", + "integrity": "sha512-tgDpaUNsUtRvNiBulKU1VnpoXU1GINMfZZXunRhUXOTBEAufG1Wp79uDXLau2gg1RZ4dpAR6lXCkrmddihCGUg==", + "dependencies": { + "@smithy/node-config-provider": "^2.2.5", + "@smithy/types": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.1.tgz", + "integrity": "sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg==", "dependencies": { "tslib": "^2.5.0" }, @@ -2761,11 +2816,11 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.4.tgz", - "integrity": "sha512-Pbu6P4MBwRcjrLgdTR1O4Y3c0sTZn2JdOiJNcgL7EcIStcQodj+6ZTXtbyU/WTEU3MV2NMA10LxFc3AWHZ3+4A==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.1.4.tgz", + "integrity": "sha512-5yYNOgCN0DL0OplME0pthoUR/sCfipnROkbTO7m872o0GHCVNJj5xOFJ143rvHNA54+pIPMLum4z2DhPC2pVGA==", "dependencies": { - "@smithy/types": "^2.3.5", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2773,13 +2828,12 @@ } }, "node_modules/@smithy/util-retry": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.4.tgz", - "integrity": "sha512-b+n1jBBKc77C1E/zfBe1Zo7S9OXGBiGn55N0apfhZHxPUP/fMH5AhFUUcWaJh7NAnah284M5lGkBKuhnr3yK5w==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.1.4.tgz", + "integrity": "sha512-JRZwhA3fhkdenSEYIWatC8oLwt4Bdf2LhHbNQApqb7yFoIGMl4twcYI3BcJZ7YIBZrACA9jGveW6tuCd836XzQ==", "dependencies": { - "@smithy/service-error-classification": "^2.0.4", - "@smithy/types": "^2.3.5", + "@smithy/service-error-classification": "^2.1.4", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2787,17 +2841,17 @@ } }, "node_modules/@smithy/util-stream": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.15.tgz", - "integrity": "sha512-A/hkYJPH2N5MCWYvky4tTpQihpYAEzqnUfxDyG3L/yMndy/2sLvxnyQal9Opuj1e9FiKSTeMyjnU9xxZGs0mRw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.1.4.tgz", + "integrity": "sha512-CiWaFPXstoR7v/PGHddFckovkhJb28wgQR7LwIt6RsQCJeRIHvUTVWhXw/Pco6Jm6nz/vfzN9FFdj/JN7RTkxQ==", "dependencies": { - "@smithy/fetch-http-handler": "^2.2.2", - "@smithy/node-http-handler": "^2.1.7", - "@smithy/types": "^2.3.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", + "@smithy/fetch-http-handler": "^2.4.4", + "@smithy/node-http-handler": "^2.4.2", + "@smithy/types": "^2.11.0", + "@smithy/util-base64": "^2.2.0", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-utf8": "^2.2.0", "tslib": "^2.5.0" }, "engines": { @@ -2805,9 +2859,9 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", + "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", "dependencies": { "tslib": "^2.5.0" }, @@ -2816,11 +2870,11 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", - "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.2.0.tgz", + "integrity": "sha512-hBsKr5BqrDrKS8qy+YcV7/htmMGxriA1PREOf/8AGBhHIZnfilVv1Waf1OyKhSbFW15U/8+gcMUQ9/Kk5qwpHQ==", "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-buffer-from": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2828,13 +2882,12 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.0.11.tgz", - "integrity": "sha512-8SJWUl9O1YhjC77EccgltI3q4XZQp3vp9DGEW6o0OdkUcwqm/H4qOLnMkA2n+NDojuM5Iia2jWoCdbluIiG7TA==", - "peer": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.1.4.tgz", + "integrity": "sha512-AK17WaC0hx1wR9juAOsQkJ6DjDxBGEf5TrKhpXtNFEn+cVto9Li3MVsdpAO97AF7bhFXSyC8tJA3F4ThhqwCdg==", "dependencies": { - "@smithy/abort-controller": "^2.0.11", - "@smithy/types": "^2.3.5", + "@smithy/abort-controller": "^2.1.4", + "@smithy/types": "^2.11.0", "tslib": "^2.5.0" }, "engines": { @@ -2951,7 +3004,7 @@ "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", - "devOptional": true, + "dev": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -2983,7 +3036,7 @@ "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "devOptional": true + "dev": true }, "node_modules/@types/quill": { "version": "1.3.10", @@ -2998,7 +3051,7 @@ "version": "17.0.73", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.73.tgz", "integrity": "sha512-6AcjgPIVsXTIsFDgsGW0iQhvg0xb2vt2qAWgXyncnVNRaW9ZXTTwAh7RQoh7PzK1AhjPoGDvUBkdAREih9n5oQ==", - "devOptional": true, + "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -3065,7 +3118,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "devOptional": true + "dev": true }, "node_modules/@types/shortid": { "version": "0.0.29", @@ -3253,45 +3306,6 @@ "node": ">=4" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sdk": { - "version": "2.1555.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1555.0.tgz", - "integrity": "sha512-hjYs1MQkJxdHnoZm8hypqGy4PQKWVUs19McdXRXWNXr97V0il4xcUpIfvjHQ9x9EjP0p/jyIx9/BtyrR68jnUQ==", - "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aws-sdk/node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -3445,8 +3459,7 @@ "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "peer": true + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -3501,16 +3514,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -4257,7 +4260,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "dev": true }, "node_modules/dashdash": { "version": "1.14.1", @@ -4589,14 +4592,6 @@ "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==", "dev": true }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/exact-trie": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/exact-trie/-/exact-trie-1.0.13.tgz", @@ -4726,7 +4721,6 @@ "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "peer": true, "dependencies": { "strnum": "^1.0.5" }, @@ -4821,14 +4815,6 @@ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", "dev": true }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -5024,6 +5010,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -5111,6 +5098,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -5141,6 +5129,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, "dependencies": { "react-is": "^16.7.0" } @@ -5148,7 +5137,8 @@ "node_modules/hoist-non-react-statics/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true }, "node_modules/hotkeys-js": { "version": "3.13.2", @@ -5297,6 +5287,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -5326,17 +5317,6 @@ "node": ">=8" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-core-module": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", @@ -5381,20 +5361,6 @@ "node": ">=8" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -5438,20 +5404,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -5475,14 +5427,6 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/jose": { "version": "4.13.1", "resolved": "https://registry.npmjs.org/jose/-/jose-4.13.1.tgz", @@ -6628,15 +6572,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6731,35 +6666,6 @@ "integrity": "sha512-I+vcaK9t4+kypiSgaiVWAipqHRXYmZIuAiS8vzFvXHHXVigg/sMKwlRgLy6LH2i3rmP+0Vzfl5lFsFRwF1r3pg==", "dev": true }, - "node_modules/react-dnd": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", - "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", - "dependencies": { - "@react-dnd/invariant": "^4.0.1", - "@react-dnd/shallowequal": "^4.0.1", - "dnd-core": "^16.0.1", - "fast-deep-equal": "^3.1.3", - "hoist-non-react-statics": "^3.3.2" - }, - "peerDependencies": { - "@types/hoist-non-react-statics": ">= 3.3.1", - "@types/node": ">= 12", - "@types/react": ">= 16", - "react": ">= 16.14" - }, - "peerDependenciesMeta": { - "@types/hoist-non-react-statics": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, "node_modules/react-dnd-html5-backend": { "version": "11.1.3", "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz", @@ -6769,26 +6675,6 @@ "dnd-core": "^11.1.3" } }, - "node_modules/react-dnd/node_modules/@react-dnd/asap": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", - "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" - }, - "node_modules/react-dnd/node_modules/@react-dnd/invariant": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", - "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" - }, - "node_modules/react-dnd/node_modules/dnd-core": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", - "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", - "dependencies": { - "@react-dnd/asap": "^5.0.1", - "@react-dnd/invariant": "^4.0.1", - "redux": "^4.2.0" - } - }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -7060,6 +6946,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dev": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -7085,7 +6972,8 @@ "node_modules/regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "dev": true }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", @@ -7309,11 +7197,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" - }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -7702,8 +7585,7 @@ "node_modules/strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "peer": true + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, "node_modules/styled-components": { "version": "5.3.11", @@ -7993,32 +7875,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -8134,24 +7990,6 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -8202,26 +8040,6 @@ } } }, - "node_modules/xml2js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", - "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "engines": { - "node": ">=4.0" - } - }, "node_modules/xmlhttprequest-ssl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", diff --git a/package.json b/package.json index 74ce608..3332706 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,9 @@ "vite": "^5.1.1" }, "dependencies": { + "@aws-sdk/client-s3": "^3.529.1", + "@aws-sdk/s3-request-presigner": "^3.529.1", "@kubernetes/client-node": "^0.20.0", - "aws-sdk": "^2.1555.0", "basic-ftp": "^5.0.4", "bcrypt": "^5.1.1", "chalk": "^5.3.0", diff --git a/src/components/servers/BackupsDialog.jsx b/src/components/servers/BackupsDialog.jsx new file mode 100644 index 0000000..dff558b --- /dev/null +++ b/src/components/servers/BackupsDialog.jsx @@ -0,0 +1,88 @@ +import { useEffect, useState } from "react"; +import useMediaQuery from "@mui/material/useMediaQuery"; +import { useTheme } from "@mui/material/styles"; +import Button from "@mui/material/Button"; +import DialogTitle from "@mui/material/DialogTitle"; +import DialogContent from "@mui/material/DialogContent"; +import DialogActions from "@mui/material/DialogActions"; +import Dialog from "@mui/material/Dialog"; +import IconButton from "@mui/material/IconButton"; +import Toolbar from "@mui/material/Toolbar"; +import Typography from "@mui/material/Typography"; +import Stack from "@mui/material/Stack"; +import DownloadIcon from "@mui/icons-material/Download"; +import { getBackupUrl, getServerBackups } from "../../util/queries"; + +export function useBackupDialog(isOpen = false) { + const [open, setOpen] = useState(isOpen); + const dialogToggle = () => setOpen(!open); + return [open, dialogToggle]; +} + +export default function BackupDialog(props) { + const { serverId, open, dialogToggle } = props; + const theme = useTheme(); + const fullScreen = useMediaQuery(theme.breakpoints.down("md")); + const [backups, setBackups] = useState([]); + + function refreshUpdateList() { + getServerBackups(serverId).then(setBackups); + } + useEffect(() => { + if (!serverId) return; + refreshUpdateList(); + }, [serverId, open]); + + function normalizeLastModified(lastModified) { + const d = new Date(Date.parse(lastModified)); + return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()} ${d.getHours()}:${d.getMinutes()}`; + } + + const downloadBackup = (backup) => + async function openBackupLink() { + const { url } = await getBackupUrl(serverId, backup.path); + window.open(url, "_blank").focus(); + }; + + const normalizedSize = (size) => `${(size / Math.pow(1024, 3)).toFixed(2)}GB`; + + return ( + + + Backups + + {backups.map((backup, i) => ( + + + {backup.name} + + + {normalizeLastModified(backup.lastModified)} + + + {normalizedSize(backup.size)} + + + + + + + ))} + + + + + + ); +} diff --git a/src/components/servers/RconView.jsx b/src/components/servers/RconView.jsx index 8bbccbd..1eafe22 100644 --- a/src/components/servers/RconView.jsx +++ b/src/components/servers/RconView.jsx @@ -61,6 +61,7 @@ export default function RconView(props) { color: "white", borderRadius: "4px", width: "100%", + height: "100%", }} > {logs.length === 0 && diff --git a/src/components/servers/ServerCard.jsx b/src/components/servers/ServerCard.jsx index ac0ffd0..0b6f724 100644 --- a/src/components/servers/ServerCard.jsx +++ b/src/components/servers/ServerCard.jsx @@ -14,10 +14,11 @@ import PlayArrowIcon from "@mui/icons-material/PlayArrow"; import DeleteForeverIcon from "@mui/icons-material/DeleteForever"; import EditIcon from "@mui/icons-material/Edit"; import FolderIcon from "@mui/icons-material/Folder"; +import BackupIcon from "@mui/icons-material/Backup"; import { Link } from "react-router-dom"; export default function ServerCard(props) { - const { server, openRcon } = props; + const { server, openRcon, openBackups } = props; const { name, id, metrics, ftpAvailable, serverAvailable, services } = server; const startServer = useStartServer(id); const stopServer = useStopServer(id); @@ -117,6 +118,14 @@ export default function ServerCard(props) { > + + + { @@ -31,6 +35,11 @@ export default function Home() { rconToggle(); }; + const openBackups = (s) => () => { + setServer(s); + backupsToggle(); + }; + return ( @@ -51,10 +60,20 @@ export default function Home() { {!isLoading && servers.map((s, k) => ( - + ))} +