diff --git a/.forgejo/workflows/deploy-edge-proxy.yml b/.forgejo/workflows/deploy-edge-proxy.yml new file mode 100644 index 0000000..87125e3 --- /dev/null +++ b/.forgejo/workflows/deploy-edge-proxy.yml @@ -0,0 +1,31 @@ +# name: Deploy Edge Proxy +# run-name: ${{ gitea.actor }} Deploy Edge Proxy +# on: +# push: +# branches: [ master ] + +# env: +# GARDEN_DEPLOY_ACTION: minecluster-proxy + +# jobs: +# deploy-edge: +# steps: +# # 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 }} +# # 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/.forgejo/workflows/deploy-edge.yml b/.forgejo/workflows/deploy-edge.yml new file mode 100644 index 0000000..afb0c78 --- /dev/null +++ b/.forgejo/workflows/deploy-edge.yml @@ -0,0 +1,44 @@ +name: Deploy USW-MC +run-name: ${{ forgejo.actor }} Deploy USW-MC +on: + push: + branches: [master] + +env: + GARDEN_DEPLOY_ACTION: minecluster + +jobs: + deploy-edge: + steps: + # Configure proper kubeconfig (Used when cluster does not match the edge environment) + - name: Get usw-mc deployment kubeconfig + uses: https://forgejo.dunemask.dev/elysium/elysium-actions@infisical-env + with: + infisical-token: ${{ secrets.INFISICAL_ELYSIUM_EDGE_READ_TOKEN }} + project-id: ${{ vars.INFISICAL_DEPLOYMENTS_PROJECT_ID }} + secret-envs: edge + secret-paths: /kubernetes/usw-mc + # Setup Oasis + - name: Oasis Setup + uses: https://forgejo.dunemask.dev/elysium/elysium-actions@oasis-setup-auto + with: + deploy-env: edge + infisical-token: ${{ secrets.INFISICAL_ELYSIUM_EDGE_READ_TOKEN }} + infisical-project: ${{ vars.INFISICAL_DEPLOYMENTS_PROJECT_ID }} + extra-secret-paths: /dashboard + 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 }} + env: # (Used when cluster does not match the edge environment) + MCL_KUBECONFIG: ${{ env.KUBERNETES_CONFIG_USW_MC }} + # Alert via Discord + - name: Discord Alert + if: always() + uses: https://forgejo.dunemask.dev/elysium/elysium-actions@discord-status + with: + status: ${{ job.status }} + channel: deployments + header: DEPLOY MC + additional-content: "Minecluster Server Manager Deployment" diff --git a/.forgejo/workflows/qa-api-tests.yml b/.forgejo/workflows/qa-api-tests.yml new file mode 100644 index 0000000..596e11a --- /dev/null +++ b/.forgejo/workflows/qa-api-tests.yml @@ -0,0 +1,42 @@ +# name: QA API Tests +# run-name: ${{ gitea.actor }} QA API Test +# on: +# pull_request: +# branches: [ master ] + +# env: +# REPO_DIR: ${{ gitea.workspace }}/minecluster +# GARDEN_LINK_ACTION: build.minecluster-image + +# jobs: +# qa-api-tests: +# steps: +# # Setup Oasis +# - name: Oasis Setup +# uses: https://gitea.dunemask.dev/elysium/elysium-actions@oasis-setup-auto +# with: +# 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 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=25m +# working-directory: ${{ env.OASIS_WORKSPACE }} +# # Discord Alert +# - name: Discord Alert +# if: always() +# 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 diff --git a/.forgejo/workflows/s3-repo-backup.yml b/.forgejo/workflows/s3-repo-backup.yml new file mode 100644 index 0000000..4e4e7fd --- /dev/null +++ b/.forgejo/workflows/s3-repo-backup.yml @@ -0,0 +1,17 @@ +name: S3 Repo Backup +run-name: ${{ forgejo.actor }} S3 Repo Backup +on: + push: + branches: [ master ] + +jobs: + s3-repo-backup: + steps: + - name: S3 Backup + uses: https://forgejo.dunemask.dev/elysium/elysium-actions@s3-backup + with: + infisical-token: ${{ secrets.INFISICAL_ELYSIUM_EDGE_READ_TOKEN }} + infisical-project: ${{ vars.INFISICAL_DEPLOYMENTS_PROJECT_ID }} + - name: Status Alert + if: always() + run: echo "The Job ended with status ${{ job.status }}." diff --git a/.gitea/workflows/s3-repo-backup.yml b/.gitea/workflows/s3-repo-backup.yml deleted file mode 100644 index 68c426c..0000000 --- a/.gitea/workflows/s3-repo-backup.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: S3 Repo Backup -run-name: ${{ gitea.actor }} S3 Repo Backup -on: - push: - branches: [ master ] - -env: - S3_BACKUP_ENDPOINT: https://s3.dunemask.dev - S3_BACKUP_KEY_ID: gitea-repo-backup - S3_BACKUP_KEY: ${{ secrets.S3_REPO_BACKUP_KEY }} - REPO_DIR: ${{ gitea.workspace }}/${{ gitea.respository }} -jobs: - s3-repo-backup: - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - path: ${{ env.REPO_DIR }} - - name: S3 Backup - uses: peter-evans/s3-backup@v1 - env: - 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 }} - STORAGE_SERVICE_URL: ${{env.S3_BACKUP_ENDPOINT}} - with: - args: --overwrite --remove - - name: Status Alert - if: always() - run: echo "The Job ended with status ${{ job.status }}." diff --git a/.gitignore b/.gitignore index d570088..5c86a6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ +.env diff --git a/Dockerfile b/Dockerfile index c80bac8..c578d05 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,8 +8,9 @@ RUN npm i COPY public public COPY dist dist COPY src src -COPY lib lib COPY index.html . COPY vite.config.js . RUN npm run build:react +# Copy Backend resources over +COPY lib lib CMD ["npm","start"] diff --git a/README.md b/README.md index 3348a0b..ed786b8 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,6 @@ Minecluster or MCL is a web interface used to manage multiple instance of Minecraft Servers in Kubernetes. This app is built to be an all in one for self-hosting Minecraft server. It uses rendered helm charts based on itzg/minecraft-server More info coming soon. + +## ⚠ Warning ⚠ +Development is very active and there is no garuntee for compatability or migration across versions 1/15/24 diff --git a/dist/app.js b/dist/app.js index 74122c5..d87897e 100644 --- a/dist/app.js +++ b/dist/app.js @@ -1,4 +1,3 @@ -import stream from "stream"; import k8s from "@kubernetes/client-node"; import Minecluster from "../lib/Minecluster.js"; const mcl = new Minecluster(); @@ -7,11 +6,6 @@ mcl.start(); async function main(){ const kc = new k8s.KubeConfig(); kc.loadFromDefault(); - -/*const k8sApi = kc.makeApiClient(k8s.CoreV1Api); -const res = await k8sApi.listNamespacedPod('mc-garden-default'); -const pods = res.body.items.map((vp1) => vp1.metadata.name); -console.log(pods);*/ - } -main().catch((e)=>{console.log(e)}); + +main().catch((e)=>{console.error(e)}); 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/lib/Minecluster.js b/lib/Minecluster.js index 0fa918b..983371e 100644 --- a/lib/Minecluster.js +++ b/lib/Minecluster.js @@ -7,6 +7,7 @@ import { INFO, OK, logInfo } from "./util/logging.js"; // Import Core Modules import buildRoutes from "./server/router.js"; import injectSockets from "./server/sockets.js"; +import pg from "./database/postgres.js"; // Constants const title = "MCL"; @@ -23,6 +24,7 @@ export default class Minecluster { logInfo(fig.textSync(title, "Larry 3D")); INFO("INIT", "Initializing..."); this.app = express(); + this.pg = pg; this.server = http.createServer(this.app); this.sockets = injectSockets(this.server, this.jobs); this.routes = buildRoutes(this.sockets); @@ -31,11 +33,12 @@ export default class Minecluster { } async _connect() { - // await this.pg.connect(); + await this.pg.connect(); } start() { const mcl = this; + return new Promise(async function init(res) { mcl._preinitialize(); await mcl._connect(); diff --git a/lib/controllers/file-controller.js b/lib/controllers/file-controller.js new file mode 100644 index 0000000..e5e7075 --- /dev/null +++ b/lib/controllers/file-controller.js @@ -0,0 +1,97 @@ +import { + createServerFolder, + getServerItem, + listServerFiles, + removeServerItem, + uploadServerItem, + moveServerItems, +} 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) => ({ + name: fi.name, + isDir: fi.type === 2, + id: `${fi.name}-${i}`, + isHidden: fi.name.startsWith("."), + isSymLink: !!fi.link, + size: fi.size, + })); + res.json(fileData); + }) + .catch(sendError(res)); +} + +export async function createFolder(req, res) { + const serverSpec = req.body; + 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)); +} + +export async function deleteItem(req, res) { + const serverSpec = req.body; + 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!"); + 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)); +} + +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)); +} + +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)); + return ftpTransfer; + }) + .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/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js new file mode 100644 index 0000000..1916157 --- /dev/null +++ b/lib/controllers/lifecycle-controller.js @@ -0,0 +1,179 @@ +import createServerResources from "../k8s/server-create.js"; +import deleteServerResources from "../k8s/server-delete.js"; +import { + createServerEntry, + deleteServerEntry, + getServerEntry, + modifyServerEntry, +} 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"; +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]))*$`, +); + +function backupPayloadFilter(req, res) { + const serverSpec = req.body; + const { + storage, + backupHost, + backupBucket, + backupId, + backupKey, + backupInterval, + } = serverSpec; + // TODO: Impliment non creation time backups + if ( + !!backupHost || + !!backupBucket || + !!backupId || + !!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 ( + !( + !!backupHost && + !!backupBucket && + !!backupId && + !!backupKey && + !!backupInterval + ) + ) + return res.status(400).send("All backup keys are required!"); + if (!dnsRegex.test(backupHost)) + return res.status(400).send("Backup Host invalid!"); + } + 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!"); + if (host !== host.toLowerCase()) + WARN("CREATE", "Host automatically being lowercasified..."); + req.body.host = host.toLowerCase(); + return "filtered"; +} + +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) { + if (payloadFilter(req, res) !== "filtered") return; + if (backupPayloadFilter(req, res) !== "filtered") return; + const serverSpec = req.body; + try { + const serverEntry = await createServerEntry(req.cairoId, serverSpec); + await createServerResources(serverEntry); + res.json(serverEntry); + } catch (e) { + sendError(res)(e); + } +} + +export async function deleteServer(req, res) { + // Ensure spec is safe + const serverSpec = req.body; + try { + await checkServerId(req.cairoId, serverSpec); + } catch (e) { + return sendError(res)(e); + } + const deleteEntry = deleteServerEntry(serverSpec.id); + const deleteResources = deleteServerResources(serverSpec); + Promise.all([deleteEntry, deleteResources]) + .then(() => res.sendStatus(200)) + .catch(sendError(res)); +} + +export async function startServer(req, res) { + // Ensure spec is safe + const serverSpec = req.body; + try { + await checkServerId(req.cairoId, serverSpec); + } catch (e) { + return sendError(res)(e); + } + const { id } = serverSpec; + toggleServer(id, true) + .then(() => res.sendStatus(200)) + .catch(sendError(res)); +} + +export async function stopServer(req, res) { + // Ensure spec is safe + const serverSpec = req.body; + try { + await checkServerId(req.cairoId, serverSpec); + } catch (e) { + return sendError(res)(e); + } + const { id } = serverSpec; + toggleServer(id, false) + .then(() => res.sendStatus(200)) + .catch(sendError(res)); +} + +export async function getServer(req, res) { + // Ensure spec is safe + const serverSpec = req.body; + try { + await checkServerId(req.cairoId, 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; + 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 modifyServerResources(serverEntry); + res.sendStatus(200); + } catch (e) { + sendError(res)(e); + } +} 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/controllers/status-controller.js b/lib/controllers/status-controller.js new file mode 100644 index 0000000..a36a53b --- /dev/null +++ b/lib/controllers/status-controller.js @@ -0,0 +1,18 @@ +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) { + getUserDeployments(req.cairoId) + .then((sd) => res.json(sd.map((s) => s.metadata.name.substring(4)))) + .catch((e) => { + ERR("STATUS CONTROLLER", e); + res.status(500).send("Couldn't get server list"); + }); +} + +export function serverInstances(req, res) { + 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 new file mode 100644 index 0000000..b272f03 --- /dev/null +++ b/lib/controllers/sub-controllers/console-controller.js @@ -0,0 +1,71 @@ +// Imports +import k8s from "@kubernetes/client-node"; +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 k8sCore = kc.makeApiClient(k8s.CoreV1Api); +const namespace = process.env.MCL_SERVER_NAMESPACE; + +// Retrieves logs from the minecraft server container +export async function webConsoleLogs(socket) { + const { serverId } = socket.mcs; + const server = await getServerEntry(serverId); + const podName = `mcl-${server.mclName}`; + const containerName = `${podName}-server`; + const podResponse = await k8sCore.listNamespacedPod(namespace); + const pods = podResponse.body.items.map((vp1) => vp1.metadata.name); + const mcsPods = pods.filter((p) => p.startsWith(podName)); + if (mcsPods.length === 0) + throw Error(`Could not find a pod that starts with ${podName}`); + if (mcsPods.length > 1) + throw Error(`Multiple pods match the name ${podName}`); + + const log = new k8s.Log(kc); + const logStream = new stream.PassThrough(); + 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, + pretty: false, + timestamps: false, + }) + .catch((e) => ERR("CONSOLE CONTROLLER", "Error streaming logs", e)); +} + +// Creates an RCON connection to the minecraft container +export async function webConsoleRcon(socket) { + if (socket.rconClient) + return VERB("RCON", "Socket already connected to RCON"); + const { serverId } = socket.mcs; + const server = await getServerEntry(serverId); + const rconSecret = `mcl-${server.mclName}-rcon-secret`; + const rconRes = await k8sCore.readNamespacedSecret(rconSecret, namespace); + const rconPassword = Buffer.from( + rconRes.body.data["rcon-password"], + "base64", + ).toString("utf8"); + const rconHost = `mcl-${server.mclName}-rcon.${namespace}.svc.cluster.local`; + const rcon = new RconClient({ + host: rconHost, + port: 25575, + password: rconPassword, + }); + rcon.on("error", (error) => socket.emit("push", error)); + try { + await rcon.connect(); + } catch (error) { + socket.emit("rcon-error", "Could not connect RCON Input to server!"); + WARN("RCON", `Could not connect to '${rconHost}'`); + } + socket.rconClient = rcon; +} diff --git a/lib/database/migrations/1_create_servers_table.sql b/lib/database/migrations/1_create_servers_table.sql new file mode 100644 index 0000000..6386fc5 --- /dev/null +++ b/lib/database/migrations/1_create_servers_table.sql @@ -0,0 +1,21 @@ +CREATE SEQUENCE servers_id_seq; +CREATE TABLE servers ( + id bigint NOT NULL DEFAULT nextval('servers_id_seq') PRIMARY KEY, + owner_cairo_id varchar(63), + host varchar(255) DEFAULT NULL, + name varchar(255) DEFAULT NULL, + version varchar(63) DEFAULT 'latest', + 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, + backup_id varchar(255) DEFAULT NULL, + backup_key varchar(255) DEFAULT NULL, + backup_interval varchar(255) DEFAULT NULL, + extra_ports varchar(7)[] DEFAULT NULL, + CONSTRAINT unique_host UNIQUE(host) +); +ALTER SEQUENCE servers_id_seq OWNED BY servers.id; \ No newline at end of file diff --git a/lib/database/pg-query.js b/lib/database/pg-query.js new file mode 100644 index 0000000..ce21af6 --- /dev/null +++ b/lib/database/pg-query.js @@ -0,0 +1,121 @@ +const buildPostgresEntry = (entry) => { + const pgEntry = { ...entry }; + Object.keys(pgEntry).forEach((col) => { + if (pgEntry[col] === undefined) delete pgEntry[col]; + }); + return pgEntry; +}; + +export const buildPostgresValue = (jsVar) => { + if (jsVar === null) return "null"; + if (typeof jsVar === "string") return buildPostgresString(jsVar); + if (Array.isArray(jsVar) && jsVar.length === 0) return "null"; + if (Array.isArray(jsVar) && isTypeArray(jsVar, "string")) + return buildPostgresStringArray(jsVar); + return jsVar; +}; + +const buildPostgresStringArray = (jsonArray) => { + if (jsonArray.length === 0) return null; + var pgArray = [...jsonArray]; + var arrayString = "ARRAY ["; + pgArray.forEach((e, i) => (pgArray[i] = `'${e}'`)); + arrayString += pgArray.join(","); + arrayString += "]"; + return arrayString; +}; + +const isTypeArray = (jsonArray, type) => + jsonArray.every((e) => typeof e === type); + +const buildPostgresString = (jsonString) => + (jsonString && `'${jsonString.replaceAll("'", "''")}'`) || null; + +export const insertQuery = (table, jsEntry) => { + if (typeof jsEntry !== "object") throw Error("PG Inserts must be objects!"); + const entry = buildPostgresEntry(jsEntry); + const cols = Object.keys(entry); + cols.forEach((col, i) => { + entry[col] = buildPostgresValue(entry[col]); + cols[i] = `"${col}"`; + }); + var query = `INSERT INTO ${table}(${cols.join(",")})\n`; + query += `VALUES(${Object.values(entry).join(",")})`; + return query; +}; + +export const deleteQuery = (table, jsEntry) => { + if (typeof jsEntry !== "object") + throw Error("PG Delete conditionals must be an object!"); + const entry = buildPostgresEntry(jsEntry); + const cols = Object.keys(entry); + const conditionals = []; + for (var col of cols) { + entry[col] = buildPostgresValue(entry[col]); + if (entry[col] === "null") conditionals.push(`x.${col} IS NULL`); + else conditionals.push(`x.${col}=${entry[col]}`); + } + return `DELETE FROM ${table} x WHERE ${conditionals.join(" AND ")}`; +}; +export const onConflictUpdate = (conflicts, updates) => { + if (!Array.isArray(conflicts)) throw Error("PG Conflicts must be an array!"); + if (typeof updates !== "object") throw Error("PG Updates must be objects!"); + const entry = buildPostgresEntry(updates); + var query = `ON CONFLICT (${conflicts.join(",")}) DO UPDATE SET\n`; + const cols = Object.keys(entry); + for (var col of cols) { + entry[col] = buildPostgresValue(entry[col]); + } + query += cols.map((c) => `${c}=${entry[c]}`).join(","); + return query; +}; +export const clearTableQuery = (table) => { + return `TRUNCATE ${table}`; +}; + +export const selectWhereQuery = (table, jsEntry, joinWith) => { + if (typeof jsEntry !== "object") throw Error("PG Where must be an object!"); + const where = buildPostgresEntry(jsEntry); + const cols = Object.keys(where); + var query = `SELECT * FROM ${table} AS x WHERE\n`; + for (var col of cols) { + where[col] = buildPostgresValue(where[col]); + } + return (query += cols.map((c) => `x.${c}=${where[c]}`).join(joinWith)); +}; +export const updateWhereQuery = (table, updates, wheres, joinWith) => { + if (typeof updates !== "object") throw Error("PG Updates must be an object!"); + if (typeof wheres !== "object") throw Error("PG Wheres must be an object!"); + const update = buildPostgresEntry(updates); + const where = buildPostgresEntry(wheres); + const updateCols = Object.keys(update); + const whereCols = Object.keys(where); + var query = `UPDATE ${table}\n`; + var updateQuery = updateCols + .map((c) => `${c} = ${buildPostgresValue(update[c])}`) + .join(","); + var whereQuery = whereCols + .map((c) => `${c} = ${buildPostgresValue(where[c])}`) + .join(joinWith); + return (query += `SET ${updateQuery} WHERE ${whereQuery}`); +}; +export const updateWhereAnyQuery = (table, updates, wheres) => + updateWhereQuery(table, updates, wheres, " OR "); +export const updateWhereAllQuery = (table, updates, wheres) => + updateWhereQuery(table, updates, wheres, " AND "); +export const selectWhereAnyQuery = (table, where) => + selectWhereQuery(table, where, " OR "); +export const selectWhereAllQuery = (table, where) => + selectWhereQuery(table, where, " AND "); + +export default { + selectWhereAnyQuery, + selectWhereAllQuery, + updateWhereAnyQuery, + updateWhereAllQuery, + insertQuery, + deleteQuery, + buildPostgresValue, + onConflictUpdate, + clearTableQuery, +}; diff --git a/lib/database/postgres.js b/lib/database/postgres.js new file mode 100644 index 0000000..452b53c --- /dev/null +++ b/lib/database/postgres.js @@ -0,0 +1,63 @@ +// Imports +import path from "node:path"; +import { URL } from "node:url"; +import { migrate } from "postgres-migrations"; +import createPgp from "pg-promise"; +import moment from "moment"; +import { INFO, WARN, OK, VERB } from "../util/logging.js"; + +// Environment Variables +const { + MCL_POSTGRES_DATABASE: database, + MCL_POSTGRES_ENABLED: pgEnabled, + MCL_POSTGRES_HOST: host, + MCL_POSTGRES_PASS: password, + MCL_POSTGRES_PORT: port, + MCL_POSTGRES_USER: user, +} = process.env; + +// Postgres-promise Configuration +// Ensure dates get saved as UTC date strings +// This prevents the parser from doing strange datetime operations +const pgp = createPgp(); +pgp.pg.types.setTypeParser(1114, (str) => moment.utc(str).format()); + +// Database Config +const dbConfig = { + database: database ?? "minecluster", + user: user ?? "postgres", + password: password ?? "postgres", + host: host ?? "localhost", + port: port ?? 5432, + ensureDatabaseExists: true, +}; + +const databaseDir = new URL(".", import.meta.url).pathname; +const migrationsDir = path.resolve(databaseDir, "migrations/"); + +const queryMock = (str) => INFO("POSTGRES MOCK", str); + +const connect = (pg) => async () => { + if (pgEnabled === "false") { + WARN("POSTGRES", "Postgres Disabled!"); + return { query: queryMock }; + } + VERB("POSTGRES", "Migrating..."); + await migrate(dbConfig, migrationsDir); + // Override fake methods + const pgInstance = pgp(dbConfig); + for (var k in pgInstance) pg[k] = pgInstance[k]; + VERB("POSTGRES", "Migrated Successfully!"); + await pg.connect(); + VERB("POSTGRES", "Postgres connected Successfully!"); + + OK("POSTGRES", `Connected to database ${dbConfig.database}!`); +}; + +const buildPostgres = () => { + var pg = { query: queryMock }; + pg.connect = connect(pg); + return pg; +}; + +export default buildPostgres(); diff --git a/lib/database/queries/server-queries.js b/lib/database/queries/server-queries.js new file mode 100644 index 0000000..59dea89 --- /dev/null +++ b/lib/database/queries/server-queries.js @@ -0,0 +1,252 @@ +import pg from "../postgres.js"; +import { + deleteQuery, + insertQuery, + selectWhereAllQuery, + updateWhereAllQuery, +} from "../pg-query.js"; +import ExpressClientError from "../../util/ExpressClientError.js"; +const table = "servers"; + +const asExpressClientError = (e) => { + throw new ExpressClientError({ m: e.message, c: 409 }); +}; + +const getMclName = (host, id) => + `${host.toLowerCase().replaceAll(".", "-")}-${id}`; + +export async function checkAuthorization(serverId, cairoId) { + console.log( + `Checking Authorization for user ${cairoId} for serverId ${serverId}`, + ); + if (!cairoId) return false; + 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, + 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, + backupId: backup_id, + backupKey: backup_key, + backupInterval: backup_interval, + } = serverSpec; + + var q = insertQuery(table, { + name, + owner_cairo_id: cairoId, + 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, + backup_bucket_path, + backup_id, + backup_key, + backup_interval, + }); + q += "\n RETURNING *"; + try { + const entries = await pg.query(q); + const { + id, + owner_cairo_id: ownerCairoId, + name, + 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, + backup_bucket_path: backupPath, + backup_id: backupId, + backup_key: backupKey, + backup_interval: backupInterval, + } = entries[0]; + const mclName = getMclName(host, id); + return { + name, + mclName, + id, + ownerCairoId, + host, + version, + serverType, + cpu, // TODO: Ignored for now by the K8S manifests + memory, + storage, + extraPorts, + backupEnabled, + backupHost, + backupPath, + backupId, + backupKey, + backupInterval, + }; + } catch (e) { + asExpressClientError(e); + } +} + +export async function deleteServerEntry(serverId) { + if (!serverId) asExpressClientError({ message: "Server ID Required!" }); + const q = deleteQuery(table, { id: serverId }); + return pg.query(q).catch(asExpressClientError); +} + +export async function getServerEntry(serverId) { + if (!serverId) asExpressClientError({ message: "Server ID Required!" }); + const q = selectWhereAllQuery(table, { id: serverId }); + try { + const serverSpecs = await pg.query(q); + if (serverSpecs.length === 0) return []; + if (!serverSpecs.length === 1) + throw Error("Multiple servers found with the same name!"); + const { + id, + owner_cairo_id: ownerCairoId, + name, + 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, + backup_bucket_path: backupPath, + backup_id: backupId, + backup_key: backupKey, + backup_interval: backupInterval, + } = serverSpecs[0]; + const mclName = getMclName(host, id); + return { + name, + mclName, + id, + ownerCairoId, + host, + version, + serverType, + cpu, // TODO: Ignored for now by the K8S manifests + memory, + storage, + extraPorts, + backupEnabled, + backupHost, + backupPath, + backupId, + backupKey, + backupInterval, + }; + } catch (e) { + asExpressClientError(e); + } +} + +export async function modifyServerEntry(serverSpec) { + const { + id, + // ownerCairoId: owner_cairo_id, // DIsabled! If these becomes a reqest, please create a new function! + name, + // 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 + 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, + backupBucket: backup_bucket_path, + backupId: backup_id, + backupKey: backup_key, + backupInterval: backup_interval, + } = serverSpec; + + 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, // 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, this returns the host from the database + version, + serverType, + cpu, // TODO: Ignored for now by the K8S manifests + memory, + storage, + extraPorts, + backupEnabled, + backupHost, + backupPath, + backupId, + backupKey, + backupInterval, + }; + } catch (e) { + asExpressClientError(e); + } +} + +export async function getServerEntries() { + const q = `SELECT * FROM ${table}`; + return pg.query(q); +} diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/lib/k8s.js b/lib/k8s.js deleted file mode 100644 index b842159..0000000 --- a/lib/k8s.js +++ /dev/null @@ -1,8 +0,0 @@ -import k8s from "@kubernetes/client-node"; -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); - -const k8sApi = kc.makeApiClient(k8s.CoreV1Api); -k8sApi.listNamespacedPod("mc-garden-default").then((res) => { - console.log(res.body); -}); diff --git a/lib/k8s/configs/backup-secret.yml b/lib/k8s/configs/backup-secret.yml new file mode 100644 index 0000000..eae59e3 --- /dev/null +++ b/lib/k8s/configs/backup-secret.yml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + annotations: + minecluster.dunemask.net/id: changeme-server-id + labels: + app: changeme-app-label + name: changeme-backup-secret +type: Opaque +data: + rclone.conf: "" diff --git a/lib/k8s/configs/containers/ftp-server.yml b/lib/k8s/configs/containers/ftp-server.yml new file mode 100644 index 0000000..3544592 --- /dev/null +++ b/lib/k8s/configs/containers/ftp-server.yml @@ -0,0 +1,34 @@ +env: + - name: FTP_USER + value: "minecluster" + - name: FTP_PASS + value: "minecluster" +image: garethflowers/ftp-server +imagePullPolicy: IfNotPresent +livenessProbe: + exec: { command: ["/bin/sh", "-c", "netstat -a | grep -q ftp"] } + failureThreshold: 20 + initialDelaySeconds: 0 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 1 +name: changeme-name-ftp +ports: [] # Programatically add all the ports for easier readability, Ports include: 20,21,40000-400009 +readinessProbe: + exec: { command: ["/bin/sh", "-c", "netstat -a | grep -q ftp"] } + failureThreshold: 20 + initialDelaySeconds: 0 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 1 +resources: + requests: + cpu: 50m + memory: 64Mi +stdin: true +terminationMessagePath: /dev/termination-log +terminationMessagePolicy: File +tty: true +volumeMounts: + - mountPath: /home/minecluster + name: datadir diff --git a/lib/k8s/configs/containers/minecraft-backup.yml b/lib/k8s/configs/containers/minecraft-backup.yml new file mode 100644 index 0000000..3caeae8 --- /dev/null +++ b/lib/k8s/configs/containers/minecraft-backup.yml @@ -0,0 +1,63 @@ +env: + - name: SRC_DIR + value: /data + - name: BACKUP_NAME + value: world + - name: INITIAL_DELAY + value: 2m + - name: BACKUP_INTERVAL + value: 24h + - name: PRUNE_BACKUPS_DAYS + value: "2" + - name: PAUSE_IF_NO_PLAYERS + value: "true" + - name: SERVER_PORT + value: "25565" + - name: RCON_HOST + value: localhost + - name: RCON_PORT + value: "25575" + - name: RCON_PASSWORD + valueFrom: + secretKeyRef: + key: rcon-password + name: changeme-rcon-secret + - name: RCON_RETRIES + value: "5" + - name: RCON_RETRY_INTERVAL + value: 10s + - name: EXCLUDES + value: "*.jar,cache,logs" + - name: BACKUP_METHOD + value: rclone + - name: DEST_DIR + value: /backups + - name: LINK_LATEST + value: "true" + - name: TAR_COMPRESS_METHOD + value: gzip + - name: ZSTD_PARAMETERS + value: -3 --long=25 --single-thread + - name: RCLONE_REMOTE + value: mcl-backup-changeme + - name: RCLONE_DEST_DIR + value: /mcl/backups/changeme + - name: RCLONE_COMPRESS_METHOD + value: gzip +image: itzg/mc-backup:latest +imagePullPolicy: IfNotPresent +name: mcl-backup-changeme +resources: + requests: + cpu: 500m + memory: 512Mi +terminationMessagePath: /dev/termination-log +terminationMessagePolicy: File +volumeMounts: + - mountPath: /data + name: datadir + readOnly: true + - mountPath: /backups + name: backupdir + - mountPath: /config/rclone + name: rclone-config diff --git a/lib/k8s/configs/containers/minecraft-server.yml b/lib/k8s/configs/containers/minecraft-server.yml new file mode 100644 index 0000000..56e9b1b --- /dev/null +++ b/lib/k8s/configs/containers/minecraft-server.yml @@ -0,0 +1,109 @@ +env: + # System Values + - name: JVM_OPTS + - name: JVM_XX_OPTS + - name: OVERRIDE_SERVER_PROPERTIES + value: "false" + - name: EULA + value: "TRUE" + # Updated at recreation + - name: MEMORY + value: 1024M + - name: TYPE + value: VANILLA + - name: VERSION + value: "latest" + # Set at creation but not updated on recreation + - name: DIFFICULTY + value: easy + - name: WHITELIST + - name: OPS + - name: MAX_PLAYERS + value: "20" + - name: MAX_WORLD_SIZE + value: "10000" + - name: ALLOW_NETHER + value: "true" + - name: ANNOUNCE_PLAYER_ACHIEVEMENTS + value: "true" + - name: ENABLE_COMMAND_BLOCK + value: "true" + - name: FORCE_GAMEMODE + value: "false" + - name: GENERATE_STRUCTURES + value: "true" + - name: HARDCORE + value: "false" + - name: MAX_BUILD_HEIGHT + value: "256" + - name: MAX_TICK_TIME + value: "60000" + - name: SPAWN_ANIMALS + value: "true" + - name: SPAWN_MONSTERS + value: "true" + - name: SPAWN_NPCS + value: "true" + - name: SPAWN_PROTECTION + value: "16" + - name: VIEW_DISTANCE + value: "10" + - name: SEED + - name: MODE + value: survival + - name: MOTD + value: §6Minecluster Hosting + - name: PVP + value: "true" + - name: LEVEL_TYPE + value: DEFAULT + - name: GENERATOR_SETTINGS + - name: LEVEL + value: world + - name: ONLINE_MODE + value: "true" + - name: ENABLE_RCON + value: "true" + - name: RCON_PASSWORD + valueFrom: + secretKeyRef: + key: rcon-password + name: changeme-rcon-secret +image: itzg/minecraft-server:latest +imagePullPolicy: IfNotPresent +livenessProbe: + exec: { command: [mc-health] } + failureThreshold: 200 + initialDelaySeconds: 30 + periodSeconds: 3 + successThreshold: 1 + timeoutSeconds: 1 +name: changeme-name-server +ports: + - containerPort: 25565 + name: minecraft + protocol: TCP + - containerPort: 25575 + name: rcon + protocol: TCP +# readinessProbe: # Disabling this allows for users to manipulate files even if the container is starting +# exec: {command: [mc-health]} +# failureThreshold: 200 +# initialDelaySeconds: 30 +# periodSeconds: 3 +# successThreshold: 1 +# timeoutSeconds: 1 +resources: + requests: + cpu: 500m + memory: 512Mi +stdin: true +terminationMessagePath: /dev/termination-log +terminationMessagePolicy: File +tty: true +volumeMounts: + - mountPath: /data + name: datadir + - mountPath: /backups + name: backupdir + readOnly: true diff --git a/lib/k8s/configs/extra-svc.yml b/lib/k8s/configs/extra-svc.yml new file mode 100644 index 0000000..dee717f --- /dev/null +++ b/lib/k8s/configs/extra-svc.yml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + minecluster.dunemask.net/id: changeme-server-id + labels: + app: changeme-app + name: changeme-extra + namespace: changeme-namespace +spec: + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + # ports: Programatically generated + # - name: port-name + # port: 1234 + # protocol: TCP + # targetPort: port-name + selector: + app: changeme-app + sessionAffinity: None + type: LoadBalancer diff --git a/lib/k8s/configs/rcon-secret.yml b/lib/k8s/configs/rcon-secret.yml index 6b7bd80..a90c319 100644 --- a/lib/k8s/configs/rcon-secret.yml +++ b/lib/k8s/configs/rcon-secret.yml @@ -3,6 +3,8 @@ data: rcon-password: UEphT3V2aGJlQjNvc3M0dElwQU5YTUZrSkltR1RsRVl0ZGx3elFqZjJLdVZrZXNtV0hja1VhUUd3bmZDcElpbA== kind: Secret metadata: + annotations: + minecluster.dunemask.net/id: changeme-server-id labels: app: changeme-app-label name: changeme-rcon-secret diff --git a/lib/k8s/configs/rcon-svc.yml b/lib/k8s/configs/rcon-svc.yml index bcb34a7..49a7089 100644 --- a/lib/k8s/configs/rcon-svc.yml +++ b/lib/k8s/configs/rcon-svc.yml @@ -2,6 +2,7 @@ apiVersion: v1 kind: Service metadata: annotations: + minecluster.dunemask.net/id: changeme-server-id labels: app: changeme-app name: changeme-rcon diff --git a/lib/k8s/configs/server-deployment.yml b/lib/k8s/configs/server-deployment.yml index bb6611a..0acd0cf 100644 --- a/lib/k8s/configs/server-deployment.yml +++ b/lib/k8s/configs/server-deployment.yml @@ -1,6 +1,8 @@ apiVersion: apps/v1 kind: Deployment metadata: + annotations: + minecluster.dunemask.net/id: changeme-server-id name: changeme-name namespace: changeme-namespace spec: @@ -14,202 +16,31 @@ spec: type: Recreate template: metadata: + annotations: + minecluster.dunemask.net/id: changeme-server-id labels: app: changeme-app spec: - containers: - - env: - - name: SRC_DIR - value: /data - - name: BACKUP_NAME - value: world - - name: INITIAL_DELAY - value: 2m - - name: BACKUP_INTERVAL - value: 24h - - name: PRUNE_BACKUPS_DAYS - value: "2" - - name: PAUSE_IF_NO_PLAYERS - value: "true" - - name: SERVER_PORT - value: "25565" - - name: RCON_HOST - value: localhost - - name: RCON_PORT - value: "25575" - - name: RCON_PASSWORD - valueFrom: - secretKeyRef: - key: rcon-password - name: changeme-rcon-secret - - name: RCON_RETRIES - value: "5" - - name: RCON_RETRY_INTERVAL - value: 10s - - name: EXCLUDES - value: "*.jar,cache,logs" - - name: BACKUP_METHOD - value: rclone - - name: DEST_DIR - value: /backups - - name: LINK_LATEST - value: "false" - - name: TAR_COMPRESS_METHOD - value: gzip - - name: ZSTD_PARAMETERS - value: -3 --long=25 --single-thread - - name: RCLONE_REMOTE - value: mc-dunemask-net - - name: RCLONE_DEST_DIR - value: /minecraft-backups/deltasmp-backups - - name: RCLONE_COMPRESS_METHOD - value: gzip - image: itzg/mc-backup:latest - imagePullPolicy: IfNotPresent - name: mcs-deltasmp-minecraft-mc-backup - resources: - requests: - cpu: 500m - memory: 512Mi - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - volumeMounts: - - mountPath: /data - name: datadir - readOnly: true - - mountPath: /backups - name: backupdir - - mountPath: /config/rclone - name: rclone-config - - env: - - name: EULA - value: "TRUE" - - name: TYPE - value: VANILLA - - name: VERSION - value: "latest" - - name: DIFFICULTY - value: easy - - name: WHITELIST - - name: OPS - - name: ICON - - name: MAX_PLAYERS - value: "20" - - name: MAX_WORLD_SIZE - value: "10000" - - name: ALLOW_NETHER - value: "true" - - name: ANNOUNCE_PLAYER_ACHIEVEMENTS - value: "true" - - name: ENABLE_COMMAND_BLOCK - value: "true" - - name: FORCE_GAMEMODE - value: "false" - - name: GENERATE_STRUCTURES - value: "true" - - name: HARDCORE - value: "false" - - name: MAX_BUILD_HEIGHT - value: "256" - - name: MAX_TICK_TIME - value: "60000" - - name: SPAWN_ANIMALS - value: "true" - - name: SPAWN_MONSTERS - value: "true" - - name: SPAWN_NPCS - value: "true" - - name: SPAWN_PROTECTION - value: "16" - - name: VIEW_DISTANCE - value: "10" - - name: SEED - - name: MODE - value: survival - - name: MOTD - value: §6Minecluster Hosting - - name: PVP - value: "true" - - name: LEVEL_TYPE - value: DEFAULT - - name: GENERATOR_SETTINGS - - name: LEVEL - value: world - - name: MODPACK - - name: ONLINE_MODE - value: "true" - - name: MEMORY - value: 1024M - - name: JVM_OPTS - - name: JVM_XX_OPTS - - name: OVERRIDE_SERVER_PROPERTIES - value: "true" - - name: ENABLE_RCON - value: "true" - - name: RCON_PASSWORD - valueFrom: - secretKeyRef: - key: rcon-password - name: changeme-rcon-secret - image: itzg/minecraft-server:latest - imagePullPolicy: IfNotPresent - livenessProbe: - exec: - command: - - mc-health - failureThreshold: 20 - initialDelaySeconds: 30 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 1 - name: changeme-name - ports: - - containerPort: 25565 - name: minecraft - protocol: TCP - - containerPort: 25575 - name: rcon - protocol: TCP - readinessProbe: - exec: - command: - - mc-health - failureThreshold: 20 - initialDelaySeconds: 30 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 1 - resources: - requests: - cpu: 500m - memory: 512Mi - stdin: true - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - tty: true - volumeMounts: - - mountPath: /data - name: datadir - - mountPath: /backups - name: backupdir - readOnly: true + containers: [] dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler - securityContext: - fsGroup: 2000 - runAsUser: 1000 + # securityContext: + # fsGroup: 2000 + # runAsUser: 1000 terminationGracePeriodSeconds: 30 volumes: - - name: datadir - persistentVolumeClaim: - claimName: changeme-pvc-name + - emptyDir: {} + name: datadir - emptyDir: {} name: backupdir - - name: rclone-config - secret: - defaultMode: 420 - items: - - key: rclone.conf - path: rclone.conf - secretName: rclone-config + # - name: datadir + # persistentVolumeClaim: + # claimName: changeme-pvc-name + # - name: rclone-config + # secret: + # defaultMode: 420 + # items: + # - key: rclone.conf + # path: rclone.conf + # secretName: rclone-config diff --git a/lib/k8s/configs/server-pvc.yml b/lib/k8s/configs/server-pvc.yml index caeaa2b..bf21ea4 100644 --- a/lib/k8s/configs/server-pvc.yml +++ b/lib/k8s/configs/server-pvc.yml @@ -1,6 +1,8 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: + annotations: + minecluster.dunemask.net/id: changeme-server-id labels: service: changeme-service-name name: changeme-pvc-name diff --git a/lib/k8s/configs/server-svc.yml b/lib/k8s/configs/server-svc.yml index c6678e6..a6f520e 100644 --- a/lib/k8s/configs/server-svc.yml +++ b/lib/k8s/configs/server-svc.yml @@ -4,20 +4,23 @@ metadata: annotations: ingress.qumine.io/hostname: changeme-url ingress.qumine.io/portname: minecraft + minecluster.dunemask.net/id: changeme-server-id labels: app: changeme-app name: changeme-name namespace: changeme-namespace spec: internalTrafficPolicy: Cluster - ipFamilies: - - IPv4 ipFamilyPolicy: SingleStack - ports: + ports: # Programatically add all FTP ports. Port range includes 20, 21, 40000-40001 - name: minecraft port: 25565 protocol: TCP targetPort: minecraft + # - name: ftp-data + # port: 20 + # protocol: TCP + # targetPort: ftp-data selector: app: changeme-app sessionAffinity: None diff --git a/lib/k8s/k8s-config.js b/lib/k8s/k8s-config.js new file mode 100644 index 0000000..cbe55b6 --- /dev/null +++ b/lib/k8s/k8s-config.js @@ -0,0 +1,14 @@ +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(); +} +if(kc.contexts.length === 1) kc.setCurrentContext(kc.contexts[0].name); +if(!kc.currentContext) throw new Error("Could not infer current context! Please set it manually in the Kubeconfig!"); +export default kc; diff --git a/lib/k8s/k8s-server-control.js b/lib/k8s/k8s-server-control.js new file mode 100644 index 0000000..c708328 --- /dev/null +++ b/lib/k8s/k8s-server-control.js @@ -0,0 +1,154 @@ +import k8s from "@kubernetes/client-node"; +import yaml from "js-yaml"; +import { VERB, ERR } from "../util/logging.js"; +import { getServerEntry } from "../database/queries/server-queries.js"; +import { + getFtpContainer, + getCoreServerContainer, + getBackupContainer, +} from "./server-containers.js"; +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); + +const namespace = process.env.MCL_SERVER_NAMESPACE; + +const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8")); + +const mineclusterManaged = (o) => + o.metadata && + o.metadata.annotations && + o.metadata.annotations["minecluster.dunemask.net/id"] !== undefined; + +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); + return serverDeployments; +} + +export async function getServices() { + const serviceRes = await k8sCore.listNamespacedService(namespace); + const serverServices = serviceRes.body.items.filter(mineclusterManaged); + return serverServices; +} + +export async function getSecrets() { + const secretRes = await k8sCore.listNamespacedSecret(namespace); + const serverSecrets = secretRes.body.items.filter(mineclusterManaged); + return serverSecrets; +} + +export async function getVolumes() { + const volumeRes = + await k8sCore.listNamespacedPersistentVolumeClaim(namespace); + const serverVolumes = volumeRes.body.items.filter(mineclusterManaged); + return serverVolumes; +} + +export function getServerAssets(serverId) { + const serverFilter = serverMatch(serverId); + return Promise.all([ + getDeployments(), + getServices(), + getSecrets(), + getVolumes(), + ]) + .then(([deps, svcs, scrts, vols]) => { + const deployments = deps.filter(serverFilter); + const services = svcs.filter(serverFilter); + const secrets = scrts.filter(serverFilter); + const volumes = vols.filter(serverFilter); + + if (deployments.length > 1) throw Error("Deployment filter broken!"); + if (volumes.length > 1) throw Error("Volume filter broken!"); + if (secrets.length > 2) throw Error("Secrets broken!"); + const serverAssets = { + deployment: deployments[0], + service: services.find((s) => s.metadata.name.endsWith("-server")), + volume: volumes[0], + rconService: services.find((s) => s.metadata.name.endsWith("-rcon")), + rconSecret: secrets.find((s) => + s.metadata.name.endsWith("-rcon-secret"), + ), + backupSecret: secrets.find((s) => + s.metadata.name.endsWith("-backup-secret"), + ), + extraService: services.find((s) => s.metadata.name.endsWith("-extra")), + }; + for (var k in serverAssets) if (serverAssets[k]) return serverAssets; + // If no assets exist, return nothing + }) + .catch((e) => ERR("SERVER ASSETS", e)); +} + +export async function getDeployment(serverId) { + const servers = await getDeployments(); + const serverDeployment = servers.find( + (s) => s.metadata.annotations["minecluster.dunemask.net/id"] === serverId, + ); + if (!serverDeployment) + throw Error(`MCL Deployment with ID '${serverId}' could not be found!`); + + return serverDeployment; +} + +export async function getContainers(serverId) { + const deployment = await getDeployment(serverId); + return deployment.spec.template.spec.containers; +} + +async function containerControl(serverSpec, deployment, scaleUp) { + const { containers } = deployment.spec.template.spec; + const depFtp = containers.find((c) => c.name.endsWith("-ftp")); + const depServer = containers.find((c) => c.name.endsWith("-server")); + const depBackup = containers.find((c) => c.name.endsWith("-backup")); + const ftpContainer = depFtp ?? getFtpContainer(serverSpec); + const serverContainer = depServer ?? getCoreServerContainer(serverSpec); + const backupContainer = depBackup ?? getBackupContainer(serverSpec); + if (scaleUp && serverSpec.backupEnabled) + return [ftpContainer, serverContainer, backupContainer]; + else if (scaleUp) return [ftpContainer, serverContainer]; + return [ftpContainer]; +} + +export function terminationControl(containers) { + return containers.length > 1 ? 30 /*seconds*/ : 1 /*seconds */; +} + +export async function toggleServer(serverId, scaleUp = false) { + const [deployment, serverSpec] = await Promise.all([ + getDeployment(serverId), + getServerEntry(serverId), + ]); + const containers = await containerControl(serverSpec, deployment, scaleUp); + const ts = terminationControl(containers); + + // Speed up container termination if not running a server + deployment.spec.template.spec.terminationGracePeriodSeconds = ts; + deployment.spec.template.spec.containers = containers; + + return k8sDeps.replaceNamespacedDeployment( + deployment.metadata.name, + namespace, + deployment, + ); +} diff --git a/lib/k8s/live-logging.js b/lib/k8s/live-logging.js deleted file mode 100644 index c65b34b..0000000 --- a/lib/k8s/live-logging.js +++ /dev/null @@ -1,30 +0,0 @@ -import stream from "stream"; -import k8s from "@kubernetes/client-node"; -import { ERR } from "../util/logging.js"; - -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); -const k8sApi = kc.makeApiClient(k8s.CoreV1Api); -export default async function liveLogging(socket, serverNamespace) { - const containerName = `mcl-${socket.mcs.serverName}`; - const podResponse = await k8sApi.listNamespacedPod(serverNamespace); - const pods = podResponse.body.items.map((vp1) => vp1.metadata.name); - const mcsPods = pods.filter((p) => p.startsWith(containerName)); - if (mcsPods.length === 0) - throw Error(`Could not find a pod that starts with ${containerName}`); - if (mcsPods.length > 1) - throw Error(`Multiple pods match the name ${containerName}`); - - const log = new k8s.Log(kc); - const logStream = new stream.PassThrough(); - logStream.on("data", (chunk) => - socket.emit("push", Buffer.from(chunk).toString()) - ); - log - .log(serverNamespace, mcsPods[0], containerName, logStream, { - follow: true, - pretty: false, - timestamps: false, - }) - .catch((e) => ERR("K8S", e)); -} diff --git a/lib/k8s/server-containers.js b/lib/k8s/server-containers.js new file mode 100644 index 0000000..a969c69 --- /dev/null +++ b/lib/k8s/server-containers.js @@ -0,0 +1,90 @@ +import fs from "node:fs"; +import path from "node:path"; +import yaml from "js-yaml"; +const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8")); + +export function getFtpContainer(serverSpec) { + const { mclName, storage } = serverSpec; + const ftpContainer = loadYaml("lib/k8s/configs/containers/ftp-server.yml"); + ftpContainer.name = `mcl-${mclName}-ftp`; + const ftpPortList = [ + { p: 20, n: "ftp-data" }, + { p: 21, n: "ftp-commands" }, + ]; + for (var p = 40000; p <= 40009; p++) + ftpPortList.push({ p, n: `ftp-passive-${p - 40000}` }); + ftpContainer.ports = ftpPortList.map(({ p: containerPort, n: name }) => ({ + containerPort, + name, + protocol: "TCP", + })); + if (!storage) delete ftpContainer.volumeMounts; + return ftpContainer; +} + +export function getCoreServerContainer(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`; + container.resources.requests.memory = `${memory}Mi`; + + const findEnv = (k) => container.env.find(({ name: n }) => n === k); + const updateEnv = (k, v) => (findEnv(k).value = v); + + // Enviornment variables + updateEnv("TYPE", serverType); + updateEnv("VERSION", version); + updateEnv("MEMORY", `${memory}M`); + // 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, + storage, + } = serverSpec; + const container = getCoreServerContainer(serverSpec); + + const findEnv = (k) => container.env.find(({ name: n }) => n === k); + const updateEnv = (k, v) => (findEnv(k).value = v); + + // Enviornment variables + /*updateEnv("DIFFICULTY", difficulty); + updateEnv("MODE", gamemode); + updateEnv("MOTD", motd); + updateEnv("MAX_PLAYERS", maxPlayers); + updateEnv("SEED", seed); + updateEnv("OPS", ops); + updateEnv("WHITELIST", whitelist); */ + if (!storage) delete container.volumeMounts; + + return container; +} + +export function getBackupContainer(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); + const updateEnv = (k, v) => (findEnv(k).value = v); + updateEnv("RCLONE_REMOTE", `${mclName}-backup`); + updateEnv("RCLONE_DEST_DIR", backupPath); + container.name = `mcl-${mclName}-backup`; + // 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-control.js b/lib/k8s/server-control.js deleted file mode 100644 index 4bda593..0000000 --- a/lib/k8s/server-control.js +++ /dev/null @@ -1,95 +0,0 @@ -import k8s from "@kubernetes/client-node"; -import { ERR } from "../util/logging.js"; -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); - -const k8sDeps = kc.makeApiClient(k8s.AppsV1Api); -const k8sCore = kc.makeApiClient(k8s.CoreV1Api); -const k8sMetrics = new k8s.Metrics(kc); -const namespace = process.env.MCL_SERVER_NAMESPACE; - -export async function startServer(req, res) { - const serverSpec = req.body; - if (!serverSpec) return res.sendStatus(400); - if (!serverSpec.name) return res.status(400).send("Server name required!"); - const { name } = serverSpec; - const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); - const dep = deploymentRes.body.items.find( - (i) => i.metadata.name === `mcl-${name}` - ); - if (!dep) return res.status(409).send("Server does not exist!"); - if (dep.spec.replicas === 1) - return res.status(409).send("Server already started!"); - dep.spec.replicas = 1; - k8sDeps.replaceNamespacedDeployment(`mcl-${name}`, namespace, dep); - res.sendStatus(200); -} - -export async function stopServer(req, res) { - const serverSpec = req.body; - if (!serverSpec) return res.sendStatus(400); - if (!serverSpec.name) return res.status(400).send("Server name required!"); - const { name } = serverSpec; - const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); - const dep = deploymentRes.body.items.find( - (i) => i.metadata.name === `mcl-${name}` - ); - if (!dep) return res.status(409).send("Server does not exist!"); - if (dep.spec.replicas === 0) - return res.status(409).send("Server already stopped!"); - dep.spec.replicas = 0; - k8sDeps.replaceNamespacedDeployment(`mcl-${name}`, namespace, dep); - res.sendStatus(200); -} - -export async function serverList(req, res) { - const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); - const deployments = deploymentRes.body.items.map((i) => i.metadata.name); - // TODO Add an annotation and manage using that - const serverDeployments = deployments.filter((d) => d.startsWith("mcl-")); - res.json(serverDeployments.map((sd) => sd.substring(4))); -} - -export async function getServers(req, res) { - const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); - const deployments = deploymentRes.body.items; - const podMetricsResponse = await k8sMetrics.getPodMetrics(namespace); - // TODO Add an annotation and manage using that - const serverDeployments = deployments.filter((d) => - d.metadata.name.startsWith("mcl-") - ); - var name, metrics, started; - const servers = serverDeployments.map((s) => { - name = s.metadata.name.substring(4); - metrics = null; - started = !!s.spec.replicas; - const pod = podMetricsResponse.items.find(({ metadata: md }) => { - return md.labels && md.labels.app && md.labels.app === `mcl-${name}-app`; - }); - if (pod) { - const podCpus = pod.containers.map( - ({ usage }) => parseInt(usage.cpu) / 1_000_000 - ); - const podMems = pod.containers.map( - ({ usage }) => parseInt(usage.memory) / 1024 - ); - metrics = { - cpu: Math.ceil(podCpus.reduce((a, b) => a + b)), - memory: Math.ceil(podMems.reduce((a, b) => a + b)), - }; - } - - return { name, metrics, started }; - }); - var clusterMetrics = { cpu: 0, memory: 0 }; - if (servers.length > 1) { - const clusterCpu = servers - .map(({ metrics }) => (metrics ? metrics.cpu : 0)) - .reduce((a, b) => a + b); - const clusterMem = servers - .map(({ metrics }) => (metrics ? metrics.memory : 0)) - .reduce((a, b) => a + b); - clusterMetrics = { cpu: clusterCpu, memory: clusterMem }; - } - res.json({ servers, clusterMetrics }); -} diff --git a/lib/k8s/server-create.js b/lib/k8s/server-create.js index 0cf0e2e..7d09e61 100644 --- a/lib/k8s/server-create.js +++ b/lib/k8s/server-create.js @@ -4,168 +4,226 @@ import k8s from "@kubernetes/client-node"; import yaml from "js-yaml"; import fs from "node:fs"; import path from "node:path"; -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); + +import { + getFtpContainer, + getServerContainer, + getBackupContainer, +} from "./server-containers.js"; + +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; -function payloadFilter(req, res) { - const serverSpec = req.body; - if (!serverSpec) return res.sendStatus(400); - const { name, url, version, serverType, difficulty, gamemode, memory } = - serverSpec; - if (!name) return res.status(400).send("Server name is required!"); - if (!url) return res.status(400).send("Server url is required!"); - if (!version) return res.status(400).send("Server version is required!"); - if (!difficulty) - return res.status(400).send("Server difficulty is required!"); - if (!serverType) return res.status(400).send("Server type is required!"); - if (!gamemode) return res.status(400).send("Server Gamemode is required!"); - if (!memory) return res.status(400).send("Memory is required!"); - req.body.name = req.body.name.toLowerCase(); - return "filtered"; +const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8")); + +export function createExtraService(serverSpec) { + const { mclName, id, extraPorts } = serverSpec; + if (!extraPorts) return; + const serviceYaml = loadYaml("lib/k8s/configs/extra-svc.yml"); + serviceYaml.metadata.labels.app = `mcl-${mclName}-app`; + serviceYaml.metadata.name = `mcl-${mclName}-extra`; + serviceYaml.metadata.namespace = namespace; + serviceYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; + serviceYaml.spec.selector.app = `mcl-${mclName}-app`; + // Port List: + const portList = extraPorts.map((p) => ({ + port: parseInt(p), + name: `mcl-extra-${p}`, + })); + const tcpPorts = portList.map(({ port, name }) => ({ + port, + name: `${name}-tcp`, + protocol: "TCP", + targetPort: port, + })); + const udpPorts = portList.map(({ port, name }) => ({ + port, + name: `${name}-udp`, + protocol: "UDP", + targetPort: port, + })); + + serviceYaml.spec.ports = [...tcpPorts, ...udpPorts]; + return serviceYaml; +} + +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"); + backupYaml.metadata.labels.app = `mcl-${mclName}-app`; + backupYaml.metadata.name = `mcl-${mclName}-backup-secret`; + backupYaml.metadata.namespace = namespace; + backupYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; + const rcloneConfig = [ + `[${mclName}-backup]`, + "type = s3", + "provider = Minio", + "env_auth = false", + `access_key_id = ${backupId}`, + `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; } function createRconSecret(serverSpec) { - const { name } = serverSpec; - const rconYaml = yaml.load( - fs.readFileSync(path.resolve("lib/k8s/configs/rcon-secret.yml"), "utf8") - ); - + const { mclName, id } = serverSpec; + const rconYaml = loadYaml("lib/k8s/configs/rcon-secret.yml"); // TODO: Dyamic rconPassword const rconPassword = bcrypt.hashSync(uuidv4(), 10); rconYaml.data["rcon-password"] = Buffer.from(rconPassword).toString("base64"); - rconYaml.metadata.labels.app = `mcl-${name}-app`; - rconYaml.metadata.name = `mcl-${name}-rcon-secret`; + rconYaml.metadata.labels.app = `mcl-${mclName}-app`; + rconYaml.metadata.name = `mcl-${mclName}-rcon-secret`; rconYaml.metadata.namespace = namespace; + rconYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; return rconYaml; } function createServerVolume(serverSpec) { - const { name } = serverSpec; - const volumeYaml = yaml.load( - fs.readFileSync(path.resolve("lib/k8s/configs/server-pvc.yml"), "utf8") - ); - volumeYaml.metadata.labels.service = `mcl-${name}-server`; - volumeYaml.metadata.name = `mcl-${name}-volume`; + 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.spec.resources.requests.storage = "1Gi"; // TODO: Changeme + volumeYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; + volumeYaml.spec.resources.requests.storage = `${storage}Gi`; return volumeYaml; } function createServerDeploy(serverSpec) { - const { - name, - version, - serverType, - difficulty, - gamemode, - memory, - motd, - maxPlayers, - seed, - modpack, - ops, - whitelist, - } = serverSpec; - const deployYaml = yaml.load( - fs.readFileSync( - path.resolve("lib/k8s/configs/server-deployment.yml"), - "utf8" - ) - ); - deployYaml.metadata.name = `mcl-${name}`; - deployYaml.metadata.namespace = namespace; - deployYaml.spec.replicas = 0; // TODO: User control for autostart - deployYaml.spec.selector.matchLabels.app = `mcl-${name}-app`; - deployYaml.spec.template.metadata.labels.app = `mcl-${name}-app`; - deployYaml.spec.template.spec.containers.splice(0, 1); //TODO: Currently removing backup container - const serverContainer = deployYaml.spec.template.spec.containers[0]; + const { mclName, id, backupEnabled, storage } = serverSpec; + const deployYaml = loadYaml("lib/k8s/configs/server-deployment.yml"); + const { metadata } = deployYaml; + const serverContainer = getServerContainer(serverSpec); + const backupContainer = getBackupContainer(serverSpec); + const ftpContainer = getFtpContainer(serverSpec); - // Enviornment variables - serverContainer.env.find(({ name: n }) => n === "TYPE").value = serverType; - serverContainer.env.find(({ name: n }) => n === "VERSION").value = version; - serverContainer.env.find(({ name: n }) => n === "DIFFICULTY").value = - difficulty; - serverContainer.env.find(({ name: n }) => n === "MODE").value = gamemode; - serverContainer.env.find(({ name: n }) => n === "MOTD").value = motd; - serverContainer.env.find(({ name: n }) => n === "MAX_PLAYERS").value = - maxPlayers; - serverContainer.env.find(({ name: n }) => n === "SEED").value = seed; - serverContainer.env.find(({ name: n }) => n === "OPS").value = ops; - serverContainer.env.find(({ name: n }) => n === "WHITELIST").value = - whitelist; - serverContainer.env.find( - ({ name: n }) => n === "MEMORY" - ).value = `${memory}M`; - if (version !== "VANILLA") - delete serverContainer.env.find(({ name: n }) => n === "MODPACK").value; - else - serverContainer.env.find(({ name: n }) => n === "MODPACK").value = modpack; + // Configure Metadata; + metadata.name = `mcl-${mclName}`; + metadata.namespace = namespace; + metadata.annotations["minecluster.dunemask.net/id"] = id; + deployYaml.metadata = metadata; + + deployYaml.spec.template.spec.terminationGracePeriodSeconds = 1; + + // Configure Lables & Selectors + deployYaml.spec.selector.matchLabels.app = `mcl-${mclName}-app`; + deployYaml.spec.template.metadata.labels.app = `mcl-${mclName}-app`; + deployYaml.spec.template.metadata.annotations["minecluster.dunemask.net/id"] = + id; - serverContainer.env.find( - ({ name }) => name === "RCON_PASSWORD" - ).valueFrom.secretKeyRef.name = `mcl-${name}-rcon-secret`; - // Server Container Name - serverContainer.name = `mcl-${name}`; - // Resources - serverContainer.resources.requests.memory = `${memory}Mi`; - // serverContainer.resources.limits.memory = `${memory}Mi`; // TODO Allow for limits beyond initial startup // Volumes - deployYaml.spec.template.spec.volumes.find( - ({ name }) => name === "datadir" - ).persistentVolumeClaim.claimName = `mcl-${name}-volume`; - deployYaml.spec.template.spec.containers[0] = serverContainer; + 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) { + deployYaml.spec.template.spec.volumes.push({ + name: "rclone-config", + secret: { + defaultMode: 420, + items: [{ key: "rclone.conf", path: "rclone.conf" }], + secretName: `mcl-${mclName}-backup-secret`, + }, + }); + } + + // Apply Containers TODO: User control for autostart + // deployYaml.spec.template.spec.containers.push(serverContainer); + deployYaml.spec.template.spec.containers.push(ftpContainer); + deployYaml.spec.replicas = 1; return deployYaml; } -function createServerService(serverSpec) { - const { name, url } = serverSpec; - const serviceYaml = yaml.load( - fs.readFileSync(path.resolve("lib/k8s/configs/server-svc.yml"), "utf8") - ); - serviceYaml.metadata.annotations["ingress.qumine.io/hostname"] = url; - serviceYaml.metadata.labels.app = `mcl-${name}-app`; - serviceYaml.metadata.name = `mcl-${name}-server`; +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; + serviceYaml.metadata.annotations["mc-router.itzg.me/externalServerName"] = + host; + serviceYaml.metadata.labels.app = `mcl-${mclName}-app`; + serviceYaml.metadata.name = `mcl-${mclName}-server`; serviceYaml.metadata.namespace = namespace; - serviceYaml.spec.selector.app = `mcl-${name}-app`; + serviceYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; + serviceYaml.spec.selector.app = `mcl-${mclName}-app`; + // Port List: + const serverPortList = [{ p: 25565, n: "minecraft" }]; + + // Apply FTP Port List + const ftpPortList = [ + { p: 20, n: "ftp-data" }, + { p: 21, n: "ftp-commands" }, + ]; + for (var p = 40000; p <= 40009; p++) + ftpPortList.push({ p, n: `ftp-passive-${p - 40000}` }); + + const portList = [...serverPortList, ...ftpPortList]; + serviceYaml.spec.ports = portList.map(({ p: port, n: name }) => ({ + port, + name, + protocol: "TCP", + targetPort: port, + })); return serviceYaml; } -function createRconService(serverSpec) { - const { name, url } = serverSpec; - const rconSvcYaml = yaml.load( - fs.readFileSync(path.resolve("lib/k8s/configs/rcon-svc.yml"), "utf8") - ); - rconSvcYaml.metadata.labels.app = `mcl-${name}-app`; - rconSvcYaml.metadata.name = `mcl-${name}-rcon`; +function createRconService(createSpec) { + const { id, mclName } = createSpec; + const rconSvcYaml = loadYaml("lib/k8s/configs/rcon-svc.yml"); + rconSvcYaml.metadata.labels.app = `mcl-${mclName}-app`; + rconSvcYaml.metadata.name = `mcl-${mclName}-rcon`; rconSvcYaml.metadata.namespace = namespace; - rconSvcYaml.spec.selector.app = `mcl-${name}-app`; + rconSvcYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; + rconSvcYaml.spec.selector.app = `mcl-${mclName}-app`; return rconSvcYaml; } -export default async function createServer(req, res) { - if (payloadFilter(req, res) !== "filtered") return; - const serverSpec = req.body; - const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); - const deployments = deploymentRes.body.items.map((i) => i.metadata.name); - if (deployments.includes(`mcl-${serverSpec.name}`)) - return res.status(409).send("Server already exists!"); - const pvcRes = await k8sCore.listNamespacedPersistentVolumeClaim(namespace); - const pvcs = pvcRes.body.items.map((i) => i.metadata.name); - if (pvcs.includes(`mcl-${serverSpec.name}-volume`)) - return res.status(409).send("Server PVC already exists!"); - const rconSecret = createRconSecret(serverSpec); - const serverVolume = createServerVolume(serverSpec); - const serverDeploy = createServerDeploy(serverSpec); - const serverService = createServerService(serverSpec); - const rconService = createRconService(serverSpec); - k8sCore.createNamespacedPersistentVolumeClaim(namespace, serverVolume); - k8sCore.createNamespacedSecret(namespace, rconSecret); - k8sCore.createNamespacedService(namespace, serverService); - k8sCore.createNamespacedService(namespace, rconService); - k8sDeps.createNamespacedDeployment(namespace, serverDeploy); - - res.sendStatus(200); +export default async function createServerResources(createSpec) { + const backupSecret = createBackupSecret(createSpec); + const rconSecret = createRconSecret(createSpec); + const serverVolume = createServerVolume(createSpec); + const serverDeploy = createServerDeploy(createSpec); + const serverService = createServerService(createSpec); + const rconService = createRconService(createSpec); + const extraService = createExtraService(createSpec); + const serverResources = []; + if (!!serverVolume) + serverResources.push( + k8sCore.createNamespacedPersistentVolumeClaim(namespace, serverVolume), + ); + if (!!extraService) + serverResources.push( + k8sCore.createNamespacedService(namespace, extraService), + ); + if (!!backupSecret) + serverResources.push( + k8sCore.createNamespacedSecret(namespace, backupSecret), + ); + serverResources.push(k8sCore.createNamespacedSecret(namespace, rconSecret)); + serverResources.push( + k8sCore.createNamespacedService(namespace, serverService), + ); + serverResources.push(k8sCore.createNamespacedService(namespace, rconService)); + serverResources.push( + k8sDeps.createNamespacedDeployment(namespace, serverDeploy), + ); + return await Promise.all(serverResources); } diff --git a/lib/k8s/server-delete.js b/lib/k8s/server-delete.js index 76b540c..c0364a8 100644 --- a/lib/k8s/server-delete.js +++ b/lib/k8s/server-delete.js @@ -1,55 +1,69 @@ import k8s from "@kubernetes/client-node"; import { ERR } from "../util/logging.js"; -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); +import { getServerAssets } from "./k8s-server-control.js"; +import ExpressClientError from "../util/ExpressClientError.js"; +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; -const deleteError = (res) => (err) => { - res.status(500).send("Error deleting a resource!"); +const deleteError = (err) => { ERR("K8S", "An error occurred while deleting a resource", err); + throw new ExpressClientError({ + c: 500, + m: "Error deleting a resource!\n" + err, + }); }; -export default async function deleteServer(req, res) { - const serverSpec = req.body; - if (!serverSpec) return res.sendStatus(400); - if (!serverSpec.name) return res.status(400).send("Server name required!"); - const { name } = serverSpec; +function deleteOnExist(o, fn) { + if (o) return fn(o.metadata.name); +} + +export default async function deleteServerResources(serverSpec) { + const { id } = serverSpec; // Ensure deployment exists - const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); - const deployments = deploymentRes.body.items.map((i) => i.metadata.name); - if (!deployments.includes(`mcl-${serverSpec.name}`)) - return res.status(409).send("Server does not exist!"); + const server = await getServerAssets(id); + if (!server) + throw new ExpressClientError({ + c: 404, + m: "No Resources for that server were found!", + }); + // Delete in reverse order - const deleteDeploy = k8sDeps.deleteNamespacedDeployment( - `mcl-${serverSpec.name}`, - namespace + const deleteDeploy = deleteOnExist(server.deployment, (name) => + k8sDeps.deleteNamespacedDeployment(name, namespace), ); - const deleteService = k8sCore.deleteNamespacedService( - `mcl-${name}-server`, - namespace + + const deleteService = deleteOnExist(server.service, (name) => + k8sCore.deleteNamespacedService(name, namespace), ); - const deleteRconService = k8sCore.deleteNamespacedService( - `mcl-${name}-rcon`, - namespace + const deleteRconService = deleteOnExist(server.rconService, (name) => + k8sCore.deleteNamespacedService(name, namespace), ); - await deleteDeploy.catch(deleteError(res)); - const deleteRconSecret = k8sCore.deleteNamespacedSecret( - `mcl-${name}-rcon-secret`, - namespace + if (deleteDeploy) await deleteDeploy.catch(deleteError); + + const deleteRconSecret = deleteOnExist(server.rconSecret, (name) => + k8sCore.deleteNamespacedSecret(name, namespace), ); - const deleteVolume = k8sCore.deleteNamespacedPersistentVolumeClaim( - `mcl-${name}-volume`, - namespace + + const deleteBackupSecret = deleteOnExist(server.backupSecret, (name) => + k8sCore.deleteNamespacedSecret(name, namespace), ); - Promise.all([ + + const deleteExtraService = deleteOnExist(server.extraService, (name) => + k8sCore.deleteNamespacedService(name, namespace), + ); + const deleteVolume = deleteOnExist(server.volume, (name) => + k8sCore.deleteNamespacedPersistentVolumeClaim(name, namespace), + ); + + return Promise.all([ deleteService, deleteRconService, deleteRconSecret, + deleteExtraService, + deleteBackupSecret, deleteVolume, - ]) - .then(() => res.sendStatus(200)) - .catch(deleteError(res)); + ]).catch(deleteError); } diff --git a/lib/k8s/server-files.js b/lib/k8s/server-files.js new file mode 100644 index 0000000..36dcd4a --- /dev/null +++ b/lib/k8s/server-files.js @@ -0,0 +1,109 @@ +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, Transform } from "node:stream"; +import { dirname, basename } from "node:path"; + +const namespace = process.env.MCL_SERVER_NAMESPACE; + +const pathSecurityCheck = (path) => { + if (!path.startsWith(".")) + throw new ExpressClientError({ + m: "Only relative directories can be created", + c: 409, + }); +}; + +const handleError = (e) => { + ERR("SERVER FILES", "Error occurred while preforming FTP operation!", e); + throw new ExpressClientError({ + c: 500, + m: "Error occurred while performing FTP operation!", + }); +}; + +export async function getFtpClient(serverService) { + const { name } = serverService.metadata; + const client = new ftp.Client(); + await client.access({ + host: `${name}.${namespace}.svc.cluster.local`, + user: "minecluster", + password: "minecluster", + }); + return client; +} + +export async function useServerFtp(serverSpec, fn) { + const { id } = serverSpec; + const server = await getServerAssets(id); + if (!server) + throw new ExpressClientError({ + c: 404, + m: "No resources for that server were found!", + }); + if (!server.service) + throw new ExpressClientError({ + c: 409, + m: "Service doesn't exist, please contact your hosting provider!", + }); + const client = await getFtpClient(server.service); + const result = await fn(client); + client.close(); + return result; +} + +export async function listServerFiles(serverSpec) { + const { path } = serverSpec; + const files = useServerFtp(serverSpec, async (c) => await c.list(path)).catch( + handleError, + ); + return files; +} + +export async function createServerFolder(serverSpec) { + const { path } = serverSpec; + pathSecurityCheck(path); + await useServerFtp(serverSpec, async (c) => c.ensureDir(path)).catch( + handleError, + ); +} + +export async function removeServerItem(serverSpec) { + const { path, isDir } = serverSpec; + pathSecurityCheck(path); + await useServerFtp(serverSpec, async (c) => { + if (isDir) await c.removeDir(path); + else await c.remove(path); + }).catch(handleError); +} + +export async function uploadServerItem(serverSpec, file) { + const fileStream = Readable.from(file.buffer); + const { path } = serverSpec; + pathSecurityCheck(path); + await useServerFtp(serverSpec, async (c) => { + await c.ensureDir(dirname(path)); + await c.uploadFrom(fileStream, basename(path)); + }).catch(handleError); +} + +export async function getServerItem(serverSpec) { + const { path } = serverSpec; + 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/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/lib/k8s/server-status.js b/lib/k8s/server-status.js new file mode 100644 index 0000000..0c421c1 --- /dev/null +++ b/lib/k8s/server-status.js @@ -0,0 +1,85 @@ +import k8s from "@kubernetes/client-node"; +import { getUserDeployments } from "./k8s-server-control.js"; +import { getServerEntries } from "../database/queries/server-queries.js"; +import kc from "./k8s-config.js"; + +const k8sMetrics = new k8s.Metrics(kc); +const namespace = process.env.MCL_SERVER_NAMESPACE; + +function getServerMetrics(podMetricsRes, serverId, serverAvailable) { + const pod = podMetricsRes.items.find(({ metadata: md }) => { + return ( + md.annotations && + md.annotations["minecluster.dunemask.net/id"] === serverId + ); + }); + if (!serverAvailable || !pod) return null; + const podCpus = pod.containers.map( + ({ usage }) => parseInt(usage.cpu) / 1_000_000, + ); + const podMems = pod.containers.map( + ({ usage }) => parseInt(usage.memory) / 1024, + ); + return { + cpu: Math.ceil(podCpus.reduce((a, b) => a + b)), + memory: Math.ceil(podMems.reduce((a, b) => a + b)), + }; +} + +function getServerStatus(server) { + const { containers } = server.spec.template.spec; + const services = containers.map(({ name }) => name.split("-").pop()); + const serverStatusList = server.status.conditions.map( + ({ type: statusType, status: sts }) => ({ statusType, sts }), + ); + const deploymentAvailable = + serverStatusList.find( + (ss) => ss.statusType === "Available" && ss.sts === "True", + ) !== undefined; + const serverAvailable = services.includes(`server`) && deploymentAvailable; + const ftpAvailable = services.includes("ftp"); // TODO this needs some handling for container creation + return { serverAvailable, ftpAvailable, services, deploymentAvailable }; +} + +export async function getInstances(cairoId) { + const [serverDeployments, podMetricsRes, entries] = await Promise.all([ + getUserDeployments(cairoId), + k8sMetrics.getPodMetrics(namespace), + getServerEntries(), + ]); + + var serverId, metrics; + 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, 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; +} + +export async function getNamespaceMetrics() { + const serverInstances = await getInstances(); + var clusterMetrics = { cpu: 0, memory: 0 }; + if (servers.length > 1) { + const clusterCpu = serverInstances + .map(({ metrics }) => (metrics ? metrics.cpu : 0)) + .reduce((a, b) => a + b); + const clusterMem = serverInstances + .map(({ metrics }) => (metrics ? metrics.memory : 0)) + .reduce((a, b) => a + b); + clusterMetrics = { cpu: clusterCpu, memory: clusterMem }; + } + return clusterMetrics; +} diff --git a/lib/routes/auth-route.js b/lib/routes/auth-route.js new file mode 100644 index 0000000..638e163 --- /dev/null +++ b/lib/routes/auth-route.js @@ -0,0 +1,19 @@ +import { Router } from "express"; +import cairoAuthMiddleware from "./middlewares/auth-middleware.js"; +const router = Router(); + +const cairoProjectId = process.env.MCL_CAIRO_PROJECT; +if(!cairoProjectId) throw Error("Cairo Project Required!"); + +const ok = (_r, res) => res.sendStatus(200); + +function cairoRedirect(req, res) { + res.redirect( + `${process.env.MCL_CAIRO_URL}/cairo/authenticate?redirectUri=${req.query.redirectUri}&projectId=${cairoProjectId}`, + ); +} + +router.get("/verify", cairoAuthMiddleware, ok); +router.get("/redirect", cairoRedirect); + +export default router; diff --git a/lib/routes/error-route.js b/lib/routes/error-route.js new file mode 100644 index 0000000..7b4e2ce --- /dev/null +++ b/lib/routes/error-route.js @@ -0,0 +1,17 @@ +export function logErrors(err, req, res, next) { + console.error(err.stack); + next(err); +} + +export function clientErrorHandler(err, req, res, next) { + if (req.xhr) { + res.status(500).send({ error: "Something failed!" }); + } else { + next(err); + } +} + +export function errorHandler(err, req, res, next) { + res.status(500); + res.render("error", { error: err }); +} diff --git a/lib/routes/files-route.js b/lib/routes/files-route.js new file mode 100644 index 0000000..d82f780 --- /dev/null +++ b/lib/routes/files-route.js @@ -0,0 +1,25 @@ +import { Router, json as jsonMiddleware } from "express"; +import multer from "multer"; +import { + createFolder, + deleteItem, + listFiles, + uploadItem, + getItem, + moveItems, +} from "../controllers/file-controller.js"; + +import cairoAuthMiddleware from "./middlewares/auth-middleware.js"; + +const router = Router(); +router.use([jsonMiddleware(), cairoAuthMiddleware]); +const multerMiddleware = multer(); + +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 new file mode 100644 index 0000000..5758cb4 --- /dev/null +++ b/lib/routes/middlewares/auth-middleware.js @@ -0,0 +1,47 @@ +// Imports +import { Router } from "express"; +import bearerTokenMiddleware from "express-bearer-token"; +import { ERR, VERB } from "../../util/logging.js"; + +// Constants +const { MCL_CAIRO_URL, MCL_CAIRO_PROJECT } = process.env; +const cairoAuthMiddleware = Router(); + +const cairoAuthenticate = async (token) => { + const config = { headers: { Authorization: `Bearer ${token}` } }; + return fetch(`${MCL_CAIRO_URL}/api/${MCL_CAIRO_PROJECT}/auth/credentials`, config).then(async (res) => { + if (res.status >= 300) { + const errorMessage = await res + .json() + .then((data) => JSON.stringify(data)) + .catch(() => res.statusText); + throw Error( + `Could not authenticate with user, receieved message: ${errorMessage}`, + ); + } + + return res.json(); + }); +}; + +// Middleware +const cairoAuthHandler = (req, res, next) => { + if (!req.token) return res.status(401).send("Cairo auth required!"); + cairoAuthenticate(req.token) + .then((authData) => { + console.log(authData); + if (!authData?.user?.id) + throw Error(`Cairo didn't return the expected data! ${authData?.user?.id}`); + req.cairoId = authData?.user?.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/react-route.js b/lib/routes/react-route.js index 9438566..e581fe3 100644 --- a/lib/routes/react-route.js +++ b/lib/routes/react-route.js @@ -3,6 +3,6 @@ import path from "path"; const router = Router(); router.use("/", express.static(path.resolve("./build"))); router.get("/*", (req, res) => - res.sendFile(path.resolve("./build/index.html")) + res.sendFile(path.resolve("./build/index.html")), ); export default router; 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/routes/server-route.js b/lib/routes/server-route.js index 4c7eb35..d8ae832 100644 --- a/lib/routes/server-route.js +++ b/lib/routes/server-route.js @@ -1,19 +1,28 @@ import { Router, json as jsonMiddleware } from "express"; import { + createServer, + deleteServer, startServer, stopServer, + getServer, + modifyServer, +} from "../controllers/lifecycle-controller.js"; +import { + serverInstances, serverList, - getServers, -} from "../k8s/server-control.js"; -import createServer from "../k8s/server-create.js"; -import deleteServer from "../k8s/server-delete.js"; +} 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); router.post("/start", startServer); router.post("/stop", stopServer); router.get("/list", serverList); -router.get("/instances", getServers); +router.get("/instances", serverInstances); +router.post("/blueprint", getServer); +router.post("/modify", modifyServer); export default router; diff --git a/lib/routes/system-route.js b/lib/routes/system-route.js index ddc5638..ef913ed 100644 --- a/lib/routes/system-route.js +++ b/lib/routes/system-route.js @@ -1,12 +1,17 @@ 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) => { + return res.json({ cpu: 8000, memory: 16000 }); + // TODO Workaround to detect available k8sApi.listNode().then((nodeRes) => { const nodeAllocatable = nodeRes.body.items.map((i) => i.status.allocatable); const nodeResources = nodeAllocatable.map(({ cpu, memory }) => ({ diff --git a/lib/server/rcon.js b/lib/server/rcon.js deleted file mode 100644 index d2535b1..0000000 --- a/lib/server/rcon.js +++ /dev/null @@ -1,31 +0,0 @@ -import k8s from "@kubernetes/client-node"; -import { Rcon as RconClient } from "rcon-client"; -import { ERR } from "../util/logging.js"; -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); -const k8sCore = kc.makeApiClient(k8s.CoreV1Api); -const namespace = process.env.MCL_SERVER_NAMESPACE; - -export default async function rconInterface(socket) { - if (socket.rconClient) - return VERB("RCON", "Socket already connected to RCON"); - const rconSecret = `mcl-${socket.mcs.serverName}-rcon-secret`; - const rconRes = await k8sCore.readNamespacedSecret(rconSecret, namespace); - const rconPassword = Buffer.from( - rconRes.body.data["rcon-password"], - "base64" - ).toString("utf8"); - const rconHost = `mcl-${socket.mcs.serverName}-rcon`; - const rcon = new RconClient({ - host: rconHost, - port: 25575, - password: rconPassword, - }); - rcon.on("error", (error) => socket.emit("push", error)); - try { - await rcon.connect(); - } catch (error) { - ERR("RCON", `Could not connect to 'mcl-${socket.mcs.serverName}-rcon'`); - } - socket.rconClient = rcon; -} diff --git a/lib/server/router.js b/lib/server/router.js index 58326bc..685dd1d 100644 --- a/lib/server/router.js +++ b/lib/server/router.js @@ -3,9 +3,17 @@ 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"; import reactRoute from "../routes/react-route.js"; +import s3Route from "../routes/s3-route.js"; +import { + logErrors, + clientErrorHandler, + errorHandler, +} from "../routes/error-route.js"; export default function buildRoutes(pg, skio) { const router = express.Router(); @@ -16,9 +24,15 @@ 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(["/mcl","/mcl/*"], reactRoute); // Static Build Route + router.use("/api/files", filesRoute); + router.use("/api/s3", s3Route); + router.use(["/mcl", "/mcl/*"], reactRoute); // Static Build Route + /*router.use(logErrors); + router.use(clientErrorHandler); + router.use(errorHandler);*/ return router; } diff --git a/lib/server/sockets.js b/lib/server/sockets.js index 2b63ede..d9804ef 100644 --- a/lib/server/sockets.js +++ b/lib/server/sockets.js @@ -1,9 +1,9 @@ import { Server as Skio } from "socket.io"; import { VERB, WARN, ERR } from "../util/logging.js"; -import liveLogging from "../k8s/live-logging.js"; -import rconInterface from "./rcon.js"; - -const namespace = process.env.MCL_SERVER_NAMESPACE; +import { + webConsoleLogs, + webConsoleRcon, +} from "../controllers/sub-controllers/console-controller.js"; async function rconSend(socket, m) { if (!socket.rconClient) @@ -18,10 +18,10 @@ async function rconSend(socket, m) { const socketConnect = async (io, socket) => { VERB("WS", "Websocket connecting"); - socket.mcs = { serverName: socket.handshake.query.serverName }; + socket.mcs = { serverId: socket.handshake.query.serverId }; try { - await liveLogging(socket, namespace); - await rconInterface(socket); + await webConsoleLogs(socket); + await webConsoleRcon(socket); socket.on("msg", (m) => rconSend(socket, m)); } catch (err) { ERR("SOCKETS", err); diff --git a/lib/util/ExpressClientError.js b/lib/util/ExpressClientError.js new file mode 100644 index 0000000..d079c30 --- /dev/null +++ b/lib/util/ExpressClientError.js @@ -0,0 +1,28 @@ +import { VERB } from "./logging.js"; + +export default class ExpressClientError extends Error { + constructor(message, clientOptions = {}) { + var msg; + if (typeof message === "object" && message.m !== undefined) msg = message.m; + else if (typeof message === "object") msg = "Unknown Express Client Error!"; + super(msg); + if (typeof message === "object") this.clientOptions = message; + else this.clientOptions = { message: msg, ...clientOptions }; + } + + sendError(res) { + if (!this.clientOptions.m && this.clientOptions.c) + res.sendStatus(this.clientOptions.c); + else res.status(this.clientOptions.c ?? 500).send(this.toString()); + } + + toString() { + return this.message; + } +} + +export const sendError = (res) => (e) => { + VERB("V", e); + if (e instanceof ExpressClientError) e.sendError(res); + else res.status(500).send(e); +}; diff --git a/package-lock.json b/package-lock.json index d8e509a..cc9f68d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,39 +1,53 @@ { "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.18.1", + "@aws-sdk/client-s3": "^3.529.1", + "@aws-sdk/s3-request-presigner": "^3.529.1", + "@kubernetes/client-node": "^0.20.0", + "basic-ftp": "^5.0.4", "bcrypt": "^5.1.1", "chalk": "^5.3.0", "express": "^4.18.2", - "figlet": "^1.6.0", + "express-bearer-token": "^2.4.0", + "figlet": "^1.7.0", "js-yaml": "^4.1.0", - "rcon-client": "^4.2.3", - "socket.io": "^4.7.2", - "uuid": "^9.0.0" + "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", + "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.3", - "@mui/material": "^5.14.5", - "@tanstack/react-query": "^4.33.0", - "@vitejs/plugin-react": "^4.0.4", - "concurrently": "^8.2.0", - "nodemon": "^3.0.1", - "prettier": "^3.0.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.3", + "prettier": "^3.2.5", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.15.0", - "socket.io-client": "^4.7.2", - "vite": "^4.4.9" + "react-quill": "^2.0.0", + "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": { @@ -49,13 +63,881 @@ "node": ">=6.0.0" } }, + "node_modules/@aws-crypto/crc32": { + "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==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "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==" + }, + "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==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "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==" + }, + "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==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "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==" + }, + "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==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "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==" + }, + "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==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "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==" + }, + "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==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "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==" + }, + "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==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "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==" + }, + "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==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "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==" + }, + "node_modules/@aws-sdk/client-s3": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "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/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-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/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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "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/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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "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/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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/lib-storage": { + "version": "3.427.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.427.0.tgz", + "integrity": "sha512-JE26Zo4SMMY2SGlD/FilF5MpLuP8D2IrW+ye/J77dwh91gyGnNa/lubJ7WbVjIAxgeaDHsAkcpNO4VR5k6JCKg==", + "dependencies": { + "@smithy/abort-controller": "^2.0.1", + "@smithy/middleware-endpoint": "^2.0.10", + "@smithy/smithy-client": "^2.1.9", + "buffer": "5.6.0", + "events": "3.3.0", + "stream-browserify": "3.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.0.0" + } + }, + "node_modules/@aws-sdk/lib-storage/node_modules/buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "node_modules/@aws-sdk/lib-storage/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "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.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "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.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "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.523.0", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "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.523.0", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "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.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "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.523.0", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "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": { + "@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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "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/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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "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-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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "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.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "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==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "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.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.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.523.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "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==", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "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": { + "node": ">=14.0.0" + } + }, "node_modules/@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.10", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -113,34 +995,34 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", - "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", + "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.10", - "@babel/parser": "^7.22.10", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.10", - "@babel/types": "^7.22.10", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.5", + "@babel/parser": "^7.23.5", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { @@ -151,13 +1033,19 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", + "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.23.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -166,14 +1054,26 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", - "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -183,22 +1083,22 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -217,28 +1117,28 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -281,53 +1181,53 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz", - "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", + "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.10", - "@babel/types": "^7.22.10" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -386,9 +1286,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", - "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", + "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -397,10 +1297,25 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", - "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -413,9 +1328,9 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", - "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -428,9 +1343,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz", - "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==", + "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" @@ -440,33 +1355,33 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", - "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", + "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.10", - "@babel/types": "^7.22.10", + "@babel/parser": "^7.23.5", + "@babel/types": "^7.23.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -475,13 +1390,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", + "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -542,15 +1457,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", @@ -566,9 +1481,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", @@ -607,6 +1522,12 @@ } } }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", + "dev": true + }, "node_modules/@emotion/unitless": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", @@ -634,250 +1555,10 @@ "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", "dev": true }, - "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.8.tgz", + "integrity": "sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==", "cpu": [ "x64" ], @@ -890,100 +1571,197 @@ "node": ">=12" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], + "node_modules/@floating-ui/core": { + "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, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@floating-ui/utils": "^0.2.1" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], + "node_modules/@floating-ui/dom": { + "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, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.1" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], + "node_modules/@floating-ui/react-dom": { + "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, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@floating-ui/dom": "^1.6.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], + "node_modules/@floating-ui/utils": { + "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": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], + "node_modules/@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "dependencies": { + "tslib": "^2.1.0" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz", + "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "typescript": "^4.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@formatjs/intl-displaynames": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz", + "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-listformat": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz", + "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", + "dev": true, + "hasInstallScript": true, "engines": { - "node": ">=12" + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.13.1.tgz", + "integrity": "sha512-dKwF+NpIV2LVCNBA7hibH53k+ChF4Wu59P2z35gu3zwRBZpmpLVhS9k1/RiSqUqkyXUQvA2rSv48GY6wp5axZQ==", + "dev": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.29" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz", + "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==", + "dev": true, + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.x" } }, "node_modules/@jridgewell/gen-mapping": { @@ -1025,9 +1803,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1035,12 +1813,12 @@ } }, "node_modules/@kubernetes/client-node": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.18.1.tgz", - "integrity": "sha512-F3JiK9iZnbh81O/da1tD0h8fQMi/MDttWc/JydyUVnjPEom55wVfnpl4zQ/sWD4uKB8FlxYRPiLwV2ZXB+xPKw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.20.0.tgz", + "integrity": "sha512-xxlv5GLX4FVR/dDKEsmi4SPeuB49aRc35stndyxcC73XnUEEwF39vXbROpHOirmDse8WE9vxOjABnSVS+jb7EA==", "dependencies": { "@types/js-yaml": "^4.0.1", - "@types/node": "^18.11.17", + "@types/node": "^20.1.1", "@types/request": "^2.47.1", "@types/ws": "^8.5.3", "byline": "^5.0.0", @@ -1051,9 +1829,7 @@ "rfc4648": "^1.3.0", "stream-buffers": "^3.0.2", "tar": "^6.1.11", - "tmp-promise": "^3.0.2", "tslib": "^2.4.1", - "underscore": "^1.13.6", "ws": "^8.11.0" }, "optionalDependencies": { @@ -1123,27 +1899,40 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", + "dev": true, + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/base": { - "version": "5.0.0-beta.11", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.11.tgz", - "integrity": "sha512-FdKZGPd8qmC3ZNke7CNhzcEgToc02M6WYZc9hcBsNQ17bgAd3s9F//1bDDYgMVBYxDM71V0sv/hBHlOY4I1ZVA==", + "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.22.6", - "@emotion/is-prop-valid": "^1.2.1", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.14.5", + "@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", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "clsx": "^2.1.0", + "prop-types": "^15.8.1" }, "engines": { "node": ">=12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -1157,29 +1946,29 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.5.tgz", - "integrity": "sha512-+wpGH1USwPcKMFPMvXqYPC6fEvhxM3FzxC8lyDiNK/imLyyJ6y2DPb1Oue7OGIKJWBmYBqrWWtfovrxd1aJHTA==", + "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", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "5.14.3", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.14.3.tgz", - "integrity": "sha512-XkxWPhageu1OPUm2LWjo5XqeQ0t2xfGe8EiLkRW9oz2LHMMZmijvCxulhgquUVTF1DnoSh+3KoDLSsoAFtVNVw==", + "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.22.6" + "@babel/runtime": "^7.23.9" }, "engines": { "node": ">=12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@mui/material": "^5.0.0", @@ -1193,20 +1982,20 @@ } }, "node_modules/@mui/material": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.5.tgz", - "integrity": "sha512-4qa4GMfuZH0Ai3mttk5ccXP8a3sf7aPlAJwyMrUSz6h9hPri6BPou94zeu3rENhhmKLby9S/W1y+pmficy8JKA==", + "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.22.6", - "@mui/base": "5.0.0-beta.11", - "@mui/core-downloads-tracker": "^5.14.5", - "@mui/system": "^5.14.5", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.14.5", - "@types/react-transition-group": "^4.4.6", - "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" @@ -1216,7 +2005,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.5.0", @@ -1238,13 +2027,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.5.tgz", - "integrity": "sha512-cC4C5RrpXpDaaZyH9QwmPhRLgz+f2SYbOty3cPkk4qPSOSfif2ZEcDD9HTENKDDd9deB+xkPKzzZhi8cxIx8Ig==", + "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.22.6", - "@mui/utils": "^5.14.5", + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.9", "prop-types": "^15.8.1" }, "engines": { @@ -1252,7 +2041,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -1265,14 +2054,14 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.13.2.tgz", - "integrity": "sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw==", + "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.21.0", + "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", - "csstype": "^3.1.2", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { @@ -1280,7 +2069,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.4.1", @@ -1297,18 +2086,18 @@ } }, "node_modules/@mui/system": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.5.tgz", - "integrity": "sha512-mextXZHDeGcR7E1kx43TRARrVXy+gI4wzpUgNv7MqZs1dvTVXQGVeAT6ydj9d6FUqHBPMNLGV/21vJOrpqsL+w==", + "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.22.6", - "@mui/private-theming": "^5.14.5", - "@mui/styled-engine": "^5.13.2", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.14.5", - "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": { @@ -1316,7 +2105,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.5.0", @@ -1337,12 +2126,12 @@ } }, "node_modules/@mui/types": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.4.tgz", - "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==", + "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": "*" + "@types/react": "^17.0.0 || ^18.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1351,14 +2140,13 @@ } }, "node_modules/@mui/utils": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.5.tgz", - "integrity": "sha512-6Hzw63VR9C5xYv+CbjndoRLU6Gntal8rJ5W+GUzkyHrGWIyYPWZPa6AevnyGioySNETATe1H9oXS8f/7qgIHJA==", + "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.22.6", - "@types/prop-types": "^15.7.5", - "@types/react-is": "^18.2.1", + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.11", "prop-types": "^15.8.1", "react-is": "^18.2.0" }, @@ -1367,10 +2155,16 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/@popperjs/core": { @@ -1383,11 +2177,719 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@remix-run/router": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.8.0.tgz", - "integrity": "sha512-mrfKqIHnSZRyIzBcanNJmVQELTnX+qagEDlcKO90RgRBVOZGSGvZKeDihTRfWcqoDn5N/NkUcwWTccnpN18Tfg==", + "node_modules/@react-dnd/asap": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz", + "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==", + "dev": true + }, + "node_modules/@react-dnd/invariant": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-2.0.0.tgz", + "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==", + "dev": true + }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", + "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==", "dev": true, + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@remix-run/router": { + "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-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", + "integrity": "sha512-cCkoGlGWfBobdDtiiypxf79q6k3/iRVGu1HVLbD92gWV5WZbmuWJCgRM4x2N6i7ljGn1cGytPn9ZAfS8UwF6vg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.7.0.tgz", + "integrity": "sha512-R2oBf2p/Arc1m+tWmiWbpHBjEcJnHVnv6bsypu4tcKdrYTpDfl1UT9qTyfkIL1iiii5D4WHxUHCg5X0pzqmxFg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@smithy/abort-controller": { + "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.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "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.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.2.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/config-resolver": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "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.2.5", + "@smithy/property-provider": "^2.1.4", + "@smithy/types": "^2.11.0", + "@smithy/url-parser": "^2.1.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "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.11.0", + "@smithy/util-hex-encoding": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "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.1.4", + "@smithy/types": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "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.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "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.1.4", + "@smithy/types": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "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.1.4", + "@smithy/types": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "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.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.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.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.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.11.0", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "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.11.0", + "@smithy/util-utf8": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "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.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "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.11.0", + "@smithy/util-utf8": "^2.2.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "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.2.2", + "@smithy/types": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "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.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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "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.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "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.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "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.1.4", + "@smithy/shared-ini-file-loader": "^2.3.5", + "@smithy/types": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "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.1.4", + "@smithy/protocol-http": "^3.2.2", + "@smithy/querystring-builder": "^2.1.4", + "@smithy/types": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "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.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "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.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "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.11.0", + "@smithy/util-uri-escape": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "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.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "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.11.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "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.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "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-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": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types": { + "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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "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.1.4", + "@smithy/types": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/util-base64": { + "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.1.1", + "@smithy/util-utf8": "^2.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "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.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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "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.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "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.1.4", + "@smithy/smithy-client": "^2.4.4", + "@smithy/types": "^2.11.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "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.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.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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "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.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "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.1.4", + "@smithy/types": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "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.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": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "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.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "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.1.4", + "@smithy/types": "^2.11.0", + "tslib": "^2.5.0" + }, "engines": { "node": ">=14.0.0" } @@ -1398,9 +2900,9 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, "node_modules/@tanstack/query-core": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.33.0.tgz", - "integrity": "sha512-qYu73ptvnzRh6se2nyBIDHGBQvPY1XXl3yR769B7B6mIDD7s+EZhdlWHQ67JI6UOTFRaI7wupnTnwJ3gE0Mr/g==", + "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", @@ -1408,30 +2910,60 @@ } }, "node_modules/@tanstack/react-query": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.33.0.tgz", - "integrity": "sha512-97nGbmDK0/m0B86BdiXzx3EW9RcDYKpnyL2+WwyuLHEgpfThYAnXFaMMmnTDuAO4bQJXEhflumIEUfKmP7ESGA==", + "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": "4.33.0", - "use-sync-external-store": "^1.2.0" + "@tanstack/query-core": "5.20.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-native": "*" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } + "react": "^18.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", + "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", + "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" } }, "node_modules/@types/caseless": { @@ -1439,6 +2971,16 @@ "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" }, + "node_modules/@types/classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-zeOWb0JGBoVmlQoznvqXbE0tEC/HONsnoUNH19Hc96NFsTAwTXbTqb8FMYkru1F/iqp7a18Ws3nWJvtA1sHD1A==", + "deprecated": "This is a stub types definition. classnames provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "classnames": "*" + } + }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -1452,15 +2994,37 @@ "@types/node": "*" } }, + "node_modules/@types/fuzzy-search": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/fuzzy-search/-/fuzzy-search-2.1.5.tgz", + "integrity": "sha512-Yw8OsjhVKbKw83LMDOZ9RXc+N+um48DmZYMrz7QChpHkQuygsc5O40oCL7SfvWgpaaviCx2TbNXYUBwhMtBH5w==", + "dev": true + }, + "node_modules/@types/hoist-non-react-statics": { + "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, + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/js-yaml": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" }, + "node_modules/@types/memoizee": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@types/memoizee/-/memoizee-0.4.11.tgz", + "integrity": "sha512-2gyorIBZu8GoDr9pYjROkxWWcFtHCquF7TVbN2I+/OvgZhnIGQS0vX5KJz4lXNKb8XOSfxFOSG5OLru1ESqLUg==", + "dev": true + }, "node_modules/@types/node": { - "version": "18.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.6.tgz", - "integrity": "sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==" + "version": "20.8.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.3.tgz", + "integrity": "sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -1469,15 +3033,24 @@ "dev": true }, "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", "dev": true }, + "node_modules/@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "dev": true, + "dependencies": { + "parchment": "^1.1.2" + } + }, "node_modules/@types/react": { - "version": "18.0.28", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", - "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", + "version": "17.0.73", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.73.tgz", + "integrity": "sha512-6AcjgPIVsXTIsFDgsGW0iQhvg0xb2vt2qAWgXyncnVNRaW9ZXTTwAh7RQoh7PzK1AhjPoGDvUBkdAREih9n5oQ==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -1485,24 +3058,51 @@ "csstype": "^3.0.2" } }, - "node_modules/@types/react-is": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.1.tgz", - "integrity": "sha512-wyUkmaaSZEzFZivD8F2ftSyAfk6L+DfFliVj/mYdOXbVjRcS87fQJLTnhk6dRZPuJjI+9g6RZJO4PNCngUrmyw==", + "node_modules/@types/react-redux": { + "version": "7.1.33", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.33.tgz", + "integrity": "sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==", + "dev": true, + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", "dev": true, "dependencies": { "@types/react": "*" } }, - "node_modules/@types/react-transition-group": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", - "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", + "node_modules/@types/react-virtualized-auto-sizer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.4.tgz", + "integrity": "sha512-nhYwlFiYa8M3S+O2T9QO/e1FQUYMr/wJENUdf/O0dhRi1RS/93rjrYQFYdbUqtdFySuhrtnEDX29P6eKOttY+A==", "dev": true, "dependencies": { "@types/react": "*" } }, + "node_modules/@types/react-window": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz", + "integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/redux-watch": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/redux-watch/-/redux-watch-1.1.2.tgz", + "integrity": "sha512-pooK4MLNYmTyfOU7jy/ZmS2mpbSHkEVpWIcK4/BRbaegOnCB4PYA2JY88vBXE2LVkhPM+du1Z+lVzRiewMz6wg==", + "dev": true + }, "node_modules/@types/request": { "version": "2.48.8", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz", @@ -1520,6 +3120,12 @@ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "dev": true }, + "node_modules/@types/shortid": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz", + "integrity": "sha512-9BCYD9btg2CY4kPcpMQ+vCR8U6V8f/KvixYD5ZbxoWlkhddNF5IeZMVL3p+QFUkg+Hb+kPAG9Jgk4bnnF1v/Fw==", + "dev": true + }, "node_modules/@types/tough-cookie": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", @@ -1534,21 +3140,22 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.4.tgz", - "integrity": "sha512-7wU921ABnNYkETiMaZy7XqpueMnpu5VxvVps13MjmCo+utBdD79sZzrApHawHtVX66cCJQQTXFcjH0y9dSUK8g==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", + "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==", "dev": true, "dependencies": { - "@babel/core": "^7.22.9", - "@babel/plugin-transform-react-jsx-self": "^7.22.5", - "@babel/plugin-transform-react-jsx-source": "^7.22.5", + "@babel/core": "^7.23.5", + "@babel/plugin-transform-react-jsx-self": "^7.23.3", + "@babel/plugin-transform-react-jsx-source": "^7.23.3", + "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.2.0" + "vite": "^4.2.0 || ^5.0.0" } }, "node_modules/abbrev": { @@ -1630,6 +3237,11 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -1665,6 +3277,14 @@ "safer-buffer": "~2.1.0" } }, + "node_modules/assert-options": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.1.tgz", + "integrity": "sha512-5lNGRB5g5i2bGIzb+J1QQE1iKU/WEMVBReFIc5pPDWjcPj23otPL0eI6PB2v7QPi0qU6Mhym5D3y0ZiSIOf3GA==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -1678,6 +3298,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/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1706,11 +3334,46 @@ "npm": ">=6" } }, + "node_modules/babel-plugin-styled-components": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", + "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "lodash": "^4.17.21", + "picomatch": "^2.3.1" + }, + "peerDependencies": { + "styled-components": ">= 2" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -1719,6 +3382,14 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/basic-ftp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", + "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", @@ -1785,6 +3456,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "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==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1807,9 +3483,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "dev": true, "funding": [ { @@ -1826,10 +3502,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -1838,6 +3514,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", @@ -1875,10 +3575,19 @@ "node": ">=6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { - "version": "1.0.30001522", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", - "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==", + "version": "1.0.30001566", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz", + "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==", "dev": true, "funding": [ { @@ -1938,6 +3647,270 @@ "fsevents": "~2.3.2" } }, + "node_modules/chonky": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/chonky/-/chonky-2.3.2.tgz", + "integrity": "sha512-ed2u+SEjEPSn8bv/zC0sXfMG/XS6Ydm4J2leLCvRb7a/2BZxKqE1DFETxqfLeJ2OA1IRujvwnuvmJO8ZgyOGyA==", + "dev": true, + "dependencies": { + "@material-ui/core": "4.11.3", + "@reduxjs/toolkit": "^1.5.0", + "@types/classnames": "^2.2.11", + "@types/fuzzy-search": "^2.1.0", + "@types/memoizee": "^0.4.5", + "@types/react": "^17.0.3", + "@types/react-redux": "^7.1.16", + "@types/react-virtualized-auto-sizer": "^1.0.0", + "@types/react-window": "^1.8.2", + "@types/redux-watch": "^1.1.0", + "@types/shortid": "^0.0.29", + "classnames": "^2.2.6", + "deepmerge": "^4.2.2", + "exact-trie": "^1.0.13", + "fast-sort": "^2.2.0", + "filesize": "^6.1.0", + "fuzzy-search": "^3.2.1", + "hotkeys-js": "^3.8.3", + "react-dnd": "11", + "react-dnd-html5-backend": "11", + "react-intl": "^5.13.2", + "react-jss": "^10.5.1", + "react-redux": "^7.2.2", + "react-virtualized-auto-sizer": "^1.0.5", + "react-window": "^1.8.6", + "redux-watch": "^1.2.0", + "shortid": "^2.2.16", + "styled-components": "^5.3.0", + "tsdef": "^0.0.14" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/chonky-icon-fontawesome": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/chonky-icon-fontawesome/-/chonky-icon-fontawesome-2.3.2.tgz", + "integrity": "sha512-19Duy25JxteIlQJfZKLH7ZSujU1zYBMLKovWDOLlw1EdPlXb9exsfYUUkwfNS+31KpwttfaqnGuN8Gyed/R6UQ==", + "dev": true, + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^1.2.32", + "@fortawesome/free-brands-svg-icons": "5.13.1", + "@fortawesome/free-solid-svg-icons": "^5.13.1", + "@fortawesome/react-fontawesome": "^0.1.12", + "chonky": "^2.3.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/chonky/node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "dev": true + }, + "node_modules/chonky/node_modules/@material-ui/core": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.3.tgz", + "integrity": "sha512-Adt40rGW6Uds+cAyk3pVgcErpzU/qxc7KBR94jFHBYretU4AtWZltYcNsbeMn9tXL86jjVL1kuGcIHsgLgFGRw==", + "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.11.3", + "@material-ui/system": "^4.11.3", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.11.2", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/chonky/node_modules/@material-ui/core/node_modules/@material-ui/styles": { + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", + "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", + "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.5.1", + "jss-plugin-camel-case": "^10.5.1", + "jss-plugin-default-unit": "^10.5.1", + "jss-plugin-global": "^10.5.1", + "jss-plugin-nested": "^10.5.1", + "jss-plugin-props-sort": "^10.5.1", + "jss-plugin-rule-value-function": "^10.5.1", + "jss-plugin-vendor-prefixer": "^10.5.1", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/chonky/node_modules/@material-ui/core/node_modules/@material-ui/system": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", + "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.3", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/chonky/node_modules/@material-ui/core/node_modules/@material-ui/utils": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", + "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "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", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chonky/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "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", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "dev": true, + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "peerDependencies": { + "react": "17.0.2" + } + }, + "node_modules/chonky/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/chonky/node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dev": true, + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -1946,6 +3919,12 @@ "node": ">=10" } }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==", + "dev": true + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1960,10 +3939,19 @@ "node": ">=12" } }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, "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" @@ -2011,10 +3999,51 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/concurrently": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.0.tgz", - "integrity": "sha512-nnLMxO2LU492mTUj9qX/az/lESonSZu81UznYDoXtz1IQf996ixVqPAgHXwvHiHCAef/7S8HIK+fTFK7Ifk8YA==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", "dev": true, "dependencies": { "chalk": "^4.1.2", @@ -2128,6 +4157,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", @@ -2166,10 +4215,51 @@ "node": ">=10" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/css-jss/-/css-jss-10.10.0.tgz", + "integrity": "sha512-YyMIS/LsSKEGXEaVJdjonWe18p4vXLo8CMA4FrW/kcaEyqdIGKCFXao31gbJddXEdIxSXFFURWrenBJPlKTgAA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "^10.10.0", + "jss-preset-default": "^10.10.0" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dev": true, + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, "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==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, "node_modules/dashdash": { @@ -2215,6 +4305,66 @@ } } }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dev": true, + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2253,6 +4403,17 @@ "node": ">=8" } }, + "node_modules/dnd-core": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-11.1.3.tgz", + "integrity": "sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA==", + "dev": true, + "dependencies": { + "@react-dnd/asap": "^4.0.0", + "@react-dnd/invariant": "^2.0.0", + "redux": "^4.0.4" + } + }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -2278,9 +4439,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.497", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.497.tgz", - "integrity": "sha512-9cvj6XkrgyxDySKJWYVIyq7p9bOAkH3M3jANgvWNX/F2jIAfbBN4oBNLJg1i68I8wAKVuih2IL4y1n9hqbL3Aw==", + "version": "1.4.608", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.608.tgz", + "integrity": "sha512-J2f/3iIIm3Mo0npneITZ2UPe4B1bg8fTNrFjD8715F/k1BvbviRuqYGkET1PgprrczXYTHFvotbBOmUp6KE0uA==", "dev": true }, "node_modules/emoji-regex": { @@ -2355,9 +4516,9 @@ } }, "node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.8.tgz", + "integrity": "sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==", "dev": true, "hasInstallScript": true, "bin": { @@ -2367,28 +4528,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" + "@esbuild/android-arm": "0.19.8", + "@esbuild/android-arm64": "0.19.8", + "@esbuild/android-x64": "0.19.8", + "@esbuild/darwin-arm64": "0.19.8", + "@esbuild/darwin-x64": "0.19.8", + "@esbuild/freebsd-arm64": "0.19.8", + "@esbuild/freebsd-x64": "0.19.8", + "@esbuild/linux-arm": "0.19.8", + "@esbuild/linux-arm64": "0.19.8", + "@esbuild/linux-ia32": "0.19.8", + "@esbuild/linux-loong64": "0.19.8", + "@esbuild/linux-mips64el": "0.19.8", + "@esbuild/linux-ppc64": "0.19.8", + "@esbuild/linux-riscv64": "0.19.8", + "@esbuild/linux-s390x": "0.19.8", + "@esbuild/linux-x64": "0.19.8", + "@esbuild/netbsd-x64": "0.19.8", + "@esbuild/openbsd-x64": "0.19.8", + "@esbuild/sunos-x64": "0.19.8", + "@esbuild/win32-arm64": "0.19.8", + "@esbuild/win32-ia32": "0.19.8", + "@esbuild/win32-x64": "0.19.8" } }, "node_modules/escalade": { @@ -2425,6 +4586,18 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==", + "dev": true + }, + "node_modules/exact-trie": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/exact-trie/-/exact-trie-1.0.13.tgz", + "integrity": "sha512-2N0sx9jMlzZxRmSOpFKmcuaPcLXYLGRp69DohigW5E7R/uo9i6S1zJ/PuAckf70099am1ts7YBRMLO8Nr8AJLg==", + "dev": true + }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -2466,6 +4639,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", @@ -2497,15 +4690,48 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", + "dev": true + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "node_modules/fast-sort": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/fast-sort/-/fast-sort-2.2.0.tgz", + "integrity": "sha512-W7zqnn2zsYoQA87FKmYtgOsbJohOrh7XrtZrCVHN5XZKqTBTv5UG+rSS3+iWbg/nepRQUOu+wnas8BwtK8kiCg==", + "dev": true + }, + "node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/figlet": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.6.0.tgz", - "integrity": "sha512-31EQGhCEITv6+hi2ORRPyn3bulaV9Fl4xOdR169cBzH/n1UqcxsiSB/noo6SJdD7Kfb1Ljit+IgR1USvF/XbdA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.7.0.tgz", + "integrity": "sha512-gO8l3wvqo0V7wEFLXPbkX83b7MVjRrk1oRLfYlZXol8nEpb/ON9pcKLI4qpBv5YtOTfrINtqb7b40iYY2FTWFg==", "bin": { "figlet": "bin/index.js" }, @@ -2513,6 +4739,34 @@ "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", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/filesize": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.4.0.tgz", + "integrity": "sha512-mjFIpOHC4jbfcTfoh4rkWpI31mF7viw9ikj/JyLoKzqlwG/YsefKfvYlYhdYdg/9mtK2z1AzgN/0LvVQ3zdlSQ==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2630,24 +4884,28 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "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", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fuzzy-search": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/fuzzy-search/-/fuzzy-search-3.2.1.tgz", + "integrity": "sha512-vAcPiyomt1ioKAsAL2uxSABHJ4Ju/e4UeDM+g1OlR0vV4YhLGMNsdLNvZTpEDY4JCSt0E4hASCNM5t2ETtsbyg==", + "dev": true }, "node_modules/gauge": { "version": "3.0.2", @@ -2687,13 +4945,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2747,6 +5006,18 @@ "node": ">=4" } }, + "node_modules/gopd": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -2772,6 +5043,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -2788,6 +5060,29 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -2799,11 +5094,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -2819,6 +5140,20 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/hotkeys-js": { + "version": "3.13.2", + "resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.13.2.tgz", + "integrity": "sha512-SXd+x90nhOdo5s+DZNPtrvXs0ZahQLXT0tWQ68bzjxCNUZ7eV4ZlNqjHLi3Kt2URpR8EBJOB2dBLdNfwIeql1A==", + "dev": true, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -2860,6 +5195,12 @@ "node": ">= 6" } }, + "node_modules/hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", + "dev": true + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2871,12 +5212,27 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2907,6 +5263,18 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/intl-messageformat": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2915,6 +5283,22 @@ "node": ">= 0.10" } }, + "node_modules/is-arguments": { + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -2945,6 +5329,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2974,6 +5373,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==", + "dev": true + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2983,11 +5388,32 @@ "node": ">=0.12.0" } }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "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", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/isomorphic-ws": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", @@ -3013,8 +5439,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", @@ -3099,6 +5524,172 @@ "node": ">=0.6.0" } }, + "node_modules/jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", + "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/jss" + } + }, + "node_modules/jss-plugin-camel-case": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", + "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-compose": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-compose/-/jss-plugin-compose-10.10.0.tgz", + "integrity": "sha512-F5kgtWpI2XfZ3Z8eP78tZEYFdgTIbpA/TMuX3a8vwrNolYtN1N4qJR/Ob0LAsqIwCMLojtxN7c7Oo/+Vz6THow==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-default-unit": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", + "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-expand": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-expand/-/jss-plugin-expand-10.10.0.tgz", + "integrity": "sha512-ymT62W2OyDxBxr7A6JR87vVX9vTq2ep5jZLIdUSusfBIEENLdkkc0lL/Xaq8W9s3opUq7R0sZQpzRWELrfVYzA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-extend": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-extend/-/jss-plugin-extend-10.10.0.tgz", + "integrity": "sha512-sKYrcMfr4xxigmIwqTjxNcHwXJIfvhvjTNxF+Tbc1NmNdyspGW47Ey6sGH8BcQ4FFQhLXctpWCQSpDwdNmXSwg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-global": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", + "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-nested": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", + "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-props-sort": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", + "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-rule-value-function": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", + "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-rule-value-observable": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-observable/-/jss-plugin-rule-value-observable-10.10.0.tgz", + "integrity": "sha512-ZLMaYrR3QE+vD7nl3oNXuj79VZl9Kp8/u6A1IbTPDcuOu8b56cFdWRZNZ0vNr8jHewooEeq2doy8Oxtymr2ZPA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "symbol-observable": "^1.2.0" + } + }, + "node_modules/jss-plugin-template": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-template/-/jss-plugin-template-10.10.0.tgz", + "integrity": "sha512-ocXZBIOJOA+jISPdsgkTs8wwpK6UbsvtZK5JI7VUggTD6LWKbtoxUzadd2TpfF+lEtlhUmMsCkTRNkITdPKa6w==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-vendor-prefixer": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", + "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.10.0" + } + }, + "node_modules/jss-preset-default": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-preset-default/-/jss-preset-default-10.10.0.tgz", + "integrity": "sha512-GL175Wt2FGhjE+f+Y3aWh+JioL06/QWFgZp53CbNNq6ZkVU0TDplD8Bxm9KnkotAYn3FlplNqoW5CjyLXcoJ7Q==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "jss-plugin-camel-case": "10.10.0", + "jss-plugin-compose": "10.10.0", + "jss-plugin-default-unit": "10.10.0", + "jss-plugin-expand": "10.10.0", + "jss-plugin-extend": "10.10.0", + "jss-plugin-global": "10.10.0", + "jss-plugin-nested": "10.10.0", + "jss-plugin-props-sort": "10.10.0", + "jss-plugin-rule-value-function": "10.10.0", + "jss-plugin-rule-value-observable": "10.10.0", + "jss-plugin-template": "10.10.0", + "jss-plugin-vendor-prefixer": "10.10.0" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -3115,7 +5706,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" }, @@ -3154,6 +5744,12 @@ "node": ">= 0.6" } }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "dev": true + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -3208,6 +5804,14 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.4.tgz", @@ -3255,15 +5859,68 @@ "node": ">=10" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer-s3": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/multer-s3/-/multer-s3-3.0.1.tgz", + "integrity": "sha512-BFwSO80a5EW4GJRBdUuSHblz2jhVSAze33ZbnGpcfEicoT0iRolx4kWR+AJV07THFRCQ78g+kelKFdjkCCaXeQ==", + "dependencies": { + "@aws-sdk/lib-storage": "^3.46.0", + "file-type": "^3.3.0", + "html-comment-regex": "^1.1.2", + "run-parallel": "^1.1.6" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.0.0" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, "funding": [ { @@ -3311,19 +5968,19 @@ } }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "node_modules/nodemon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", - "integrity": "sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==", + "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", - "debug": "^3.2.7", + "debug": "^4", "ignore-by-default": "^1.0.1", "minimatch": "^3.1.2", "pstree.remy": "^1.1.8", @@ -3344,15 +6001,6 @@ "url": "https://opencollective.com/nodemon" } }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/nodemon/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3454,6 +6102,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-path": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz", + "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==", + "dev": true, + "engines": { + "node": ">= 10.12.0" + } + }, "node_modules/oidc-token-hash": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", @@ -3515,6 +6197,17 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "optional": true }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "node_modules/parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3586,6 +6279,111 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-minify": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.6.3.tgz", + "integrity": "sha512-NoSsPqXxbkD8RIe+peQCqiea4QzXgosdTKY8p7PsbbGsh2F8TifDj/vJxfuR8qJwNYrijdSs7uf0tAe6WOyCsQ==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-promise": { + "version": "11.5.4", + "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-11.5.4.tgz", + "integrity": "sha512-esYSkDt2h6NQOkfotGAm1Ld5OjoITJLpB88Z1PIlcAU/RQ0XQE2PxW0bLJEOMHPGV5iaRnj1Y7ARznXbgN4FNw==", + "dependencies": { + "assert-options": "0.8.1", + "pg": "8.11.3", + "pg-minify": "1.6.3", + "spex": "3.3.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3604,10 +6402,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==", + "dev": true + }, "node_modules/postcss": { - "version": "8.4.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", - "integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "funding": [ { @@ -3624,7 +6428,7 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -3632,10 +6436,66 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-migrations": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postgres-migrations/-/postgres-migrations-5.3.0.tgz", + "integrity": "sha512-gnTHWJZVWbW8T3mXIxJm1JRU853TqBVWkhgfsTJr7zqT3VexjRmJj9kNi96rVhfTezDU4FVW6pf701kLOZiKIA==", + "dependencies": { + "pg": "^8.6.0", + "sql-template-strings": "^2.2.2" + }, + "bin": { + "pg-validate-migrations": "dist/bin/validate.js" + }, + "engines": { + "node": ">10.17.0" + } + }, "node_modules/prettier": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz", - "integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==", + "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" @@ -3647,11 +6507,15 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/prop-types": { "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", @@ -3661,8 +6525,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", @@ -3709,6 +6572,53 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "dev": true, + "dependencies": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "node_modules/quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "dev": true, + "dependencies": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -3732,9 +6642,9 @@ } }, "node_modules/rcon-client": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/rcon-client/-/rcon-client-4.2.3.tgz", - "integrity": "sha512-g+DEzI3O0Nl4Y3mPp6DHhSpYeiv+wTq8iKxCumFGO0/SMCsdNfx9bpT5RyHa7phR5dvspKfQK0ZYJp1YCtXNng==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/rcon-client/-/rcon-client-4.2.4.tgz", + "integrity": "sha512-sTHYLbcu2qrrOX4TOD3rvcLZNs7xiAhTsnZZIvxviSpa0yeV63sPAVI0PeScn5IvhKOCMURGt1NmnQ8h/GxjtA==", "dependencies": { "typed-emitter": "^0.1.0" } @@ -3743,7 +6653,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" }, @@ -3751,6 +6660,21 @@ "node": ">=0.10.0" } }, + "node_modules/react-display-name": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/react-display-name/-/react-display-name-0.2.5.tgz", + "integrity": "sha512-I+vcaK9t4+kypiSgaiVWAipqHRXYmZIuAiS8vzFvXHHXVigg/sMKwlRgLy6LH2i3rmP+0Vzfl5lFsFRwF1r3pg==", + "dev": 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", + "integrity": "sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw==", + "dev": true, + "dependencies": { + "dnd-core": "^11.1.3" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -3764,12 +6688,138 @@ "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", + "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl": "2.2.1", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": "^16.3.0 || 17 || 18", + "typescript": "^4.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/react-jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/react-jss/-/react-jss-10.10.0.tgz", + "integrity": "sha512-WLiq84UYWqNBF6579/uprcIUnM1TSywYq6AIjKTTTG5ziJl9Uy+pwuvpN3apuyVwflMbD60PraeTKT7uWH9XEQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "@emotion/is-prop-valid": "^0.7.3", + "css-jss": "10.10.0", + "hoist-non-react-statics": "^3.2.0", + "is-in-browser": "^1.1.3", + "jss": "10.10.0", + "jss-preset-default": "10.10.0", + "prop-types": "^15.6.0", + "shallow-equal": "^1.2.0", + "theming": "^3.3.0", + "tiny-warning": "^1.0.2" + }, + "peerDependencies": { + "react": ">=16.8.6" + } + }, + "node_modules/react-jss/node_modules/@emotion/is-prop-valid": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz", + "integrity": "sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==", + "dev": true, + "dependencies": { + "@emotion/memoize": "0.7.1" + } + }, + "node_modules/react-jss/node_modules/@emotion/memoize": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz", + "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==", + "dev": true + }, + "node_modules/react-quill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", + "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", + "dev": true, + "dependencies": { + "@types/quill": "^1.3.10", + "lodash": "^4.17.4", + "quill": "^1.3.7" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, + "node_modules/react-redux": { + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -3780,12 +6830,12 @@ } }, "node_modules/react-router": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.15.0.tgz", - "integrity": "sha512-NIytlzvzLwJkCQj2HLefmeakxxWHWAP+02EGqWEZy+DgfHHKQMUoBBjUQLOtFInBMhWtb3hiUy6MfFgwLjXhqg==", + "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.8.0" + "@remix-run/router": "1.15.0" }, "engines": { "node": ">=14.0.0" @@ -3795,13 +6845,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.15.0.tgz", - "integrity": "sha512-aR42t0fs7brintwBGAv2+mGlCtgtFQeOzK0BM1/OiqEzRejOZtpMZepvgkscpMUnKb8YO84G7s3LsHnnDNonbQ==", + "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.8.0", - "react-router": "6.15.0" + "@remix-run/router": "1.15.0", + "react-router": "6.22.0" }, "engines": { "node": ">=14.0.0" @@ -3811,6 +6861,19 @@ "react-dom": ">=16.8" } }, + "node_modules/react-toastify": { + "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": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -3827,6 +6890,33 @@ "react-dom": ">=16.6.0" } }, + "node_modules/react-virtualized-auto-sizer": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.20.tgz", + "integrity": "sha512-OdIyHwj4S4wyhbKHOKM1wLSj/UDXm839Z3Cvfg2a9j+He6yDa6i5p0qQvEiCnyQlGO/HyfSnigQwuxvYalaAXA==", + "dev": true, + "peerDependencies": { + "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc", + "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc" + } + }, + "node_modules/react-window": { + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz", + "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + }, + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -3852,12 +6942,56 @@ "node": ">=8.10.0" } }, + "node_modules/redux": { + "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" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "dev": true, + "peerDependencies": { + "redux": "^4" + } + }, + "node_modules/redux-watch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redux-watch/-/redux-watch-1.2.0.tgz", + "integrity": "sha512-Ws4Q+e5zFGMyy1H709c1Ws8apSd6MqoJRIzBDHbI4nikome/IZWVTYXdQNz+VJxPjyX/h2E+lYEo41fXgjCF8g==", + "dev": true, + "dependencies": { + "object-path": "^0.11.5" + } + }, "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 }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -3928,6 +7062,12 @@ "node": ">=0.10.0" } }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "dev": true + }, "node_modules/resolve": { "version": "1.22.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", @@ -3974,21 +7114,56 @@ } }, "node_modules/rollup": { - "version": "3.28.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.0.tgz", - "integrity": "sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.7.0.tgz", + "integrity": "sha512-7Kw0dUP4BWH78zaZCqF1rPyQ8D5DSU6URG45v1dqS/faNsx9WXyess00uTOZxKr7oR/4TOjO1CPudT8L1UsEgw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.7.0", + "@rollup/rollup-android-arm64": "4.7.0", + "@rollup/rollup-darwin-arm64": "4.7.0", + "@rollup/rollup-darwin-x64": "4.7.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.7.0", + "@rollup/rollup-linux-arm64-gnu": "4.7.0", + "@rollup/rollup-linux-arm64-musl": "4.7.0", + "@rollup/rollup-linux-riscv64-gnu": "4.7.0", + "@rollup/rollup-linux-x64-gnu": "4.7.0", + "@rollup/rollup-linux-x64-musl": "4.7.0", + "@rollup/rollup-win32-arm64-msvc": "4.7.0", + "@rollup/rollup-win32-ia32-msvc": "4.7.0", + "@rollup/rollup-win32-x64-msvc": "4.7.0", "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -4099,11 +7274,37 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", + "dev": true + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "dev": true + }, "node_modules/shell-quote": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", @@ -4113,6 +7314,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/shortid": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz", + "integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "dependencies": { + "nanoid": "^2.1.0" + } + }, + "node_modules/shortid/node_modules/nanoid": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", + "dev": true + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -4177,9 +7394,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", @@ -4202,9 +7419,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", @@ -4252,6 +7469,30 @@ "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", "dev": true }, + "node_modules/spex": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/spex/-/spex-3.3.0.tgz", + "integrity": "sha512-VNiXjFp6R4ldPbVRYbpxlD35yRHceecVXlct1J4/X80KuuPnW2AXMq3sGwhnJOhKkUsOxAT6nRGfGE5pocVw5w==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sql-template-strings": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/sql-template-strings/-/sql-template-strings-2.2.2.tgz", + "integrity": "sha512-UXhXR2869FQaD+GMly8jAMCRZ94nU5KcrFetZfWEMd+LVVG6y0ExgHAhatEcKZ/wk8YcKPdi+hiD2wm75lq3/Q==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -4284,6 +7525,15 @@ "node": ">= 0.8" } }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, "node_modules/stream-buffers": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", @@ -4292,6 +7542,14 @@ "node": ">= 0.10.0" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -4324,6 +7582,47 @@ "node": ">=8" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, + "node_modules/styled-components": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", + "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-is": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "dev": true + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -4354,6 +7653,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tar": { "version": "6.1.13", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", @@ -4375,24 +7683,29 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "node_modules/theming": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/theming/-/theming-3.3.0.tgz", + "integrity": "sha512-u6l4qTJRDaWZsqa8JugaNt7Xd8PPl9+gonZaIe28vAhqgHMIG/DOyFPqiKN/gQLQYj05tHv+YQdNILL4zoiAVA==", + "dev": true, "dependencies": { - "rimraf": "^3.0.0" + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.8", + "react-display-name": "^0.2.4", + "tiny-warning": "^1.0.2" }, "engines": { - "node": ">=8.17.0" + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.3" } }, - "node_modules/tmp-promise": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", - "dependencies": { - "tmp": "^0.2.0" - } + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true }, "node_modules/to-fast-properties": { "version": "2.0.0", @@ -4461,6 +7774,12 @@ "tree-kill": "cli.js" } }, + "node_modules/tsdef": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/tsdef/-/tsdef-0.0.14.tgz", + "integrity": "sha512-UjMD4XKRWWFlFBfwKVQmGFT5YzW/ZaF8x6KpCDf92u9wgKeha/go3FU0e5WqDjXsCOdfiavCkfwfVHNDxRDGMA==", + "dev": true + }, "node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", @@ -4499,17 +7818,17 @@ "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-0.1.0.tgz", "integrity": "sha512-Tfay0l6gJMP5rkil8CzGbLthukn+9BN/VXWcABVFPjOoelJ+koW8BuPZYk+h/L+lEeIp1fSzVRiWRPIjKVjPdg==" }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, - "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -4519,9 +7838,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -4556,15 +7875,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "dev": true, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4579,9 +7889,13 @@ } }, "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -4608,29 +7922,29 @@ } }, "node_modules/vite": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", - "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", + "integrity": "sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==", "dev": true, "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": ">= 14", + "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", @@ -4735,6 +8049,14 @@ "node": ">=0.4.0" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index d51e3a1..3332706 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" }, @@ -22,30 +22,44 @@ "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.3", - "@mui/material": "^5.14.5", - "@tanstack/react-query": "^4.33.0", - "@vitejs/plugin-react": "^4.0.4", - "concurrently": "^8.2.0", - "nodemon": "^3.0.1", - "prettier": "^3.0.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.3", + "prettier": "^3.2.5", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.15.0", - "socket.io-client": "^4.7.2", - "vite": "^4.4.9" + "react-quill": "^2.0.0", + "react-router-dom": "^6.22.0", + "react-toastify": "^10.0.4", + "socket.io-client": "^4.7.4", + "vite": "^5.1.1" }, "dependencies": { - "@kubernetes/client-node": "^0.18.1", + "@aws-sdk/client-s3": "^3.529.1", + "@aws-sdk/s3-request-presigner": "^3.529.1", + "@kubernetes/client-node": "^0.20.0", + "basic-ftp": "^5.0.4", "bcrypt": "^5.1.1", "chalk": "^5.3.0", "express": "^4.18.2", - "figlet": "^1.6.0", + "express-bearer-token": "^2.4.0", + "figlet": "^1.7.0", "js-yaml": "^4.1.0", - "rcon-client": "^4.2.3", - "socket.io": "^4.7.2", - "uuid": "^9.0.0" + "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", + "react-dropzone": "^14.2.3", + "socket.io": "^4.7.4", + "uuid": "^9.0.1" } } diff --git a/public/icons/android-chrome-192x192.png b/public/icons/android-chrome-192x192.png new file mode 100644 index 0000000..5f91c4a Binary files /dev/null and b/public/icons/android-chrome-192x192.png differ diff --git a/public/icons/android-chrome-512x512.png b/public/icons/android-chrome-512x512.png new file mode 100644 index 0000000..ffad351 Binary files /dev/null and b/public/icons/android-chrome-512x512.png differ 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 0000000..1fae26b Binary files /dev/null and b/public/icons/apple-touch-icon-120x120-precomposed.png differ diff --git a/public/icons/apple-touch-icon-120x120.png b/public/icons/apple-touch-icon-120x120.png new file mode 100644 index 0000000..11e2054 Binary files /dev/null and b/public/icons/apple-touch-icon-120x120.png differ diff --git a/public/icons/apple-touch-icon-152x152-precomposed.png b/public/icons/apple-touch-icon-152x152-precomposed.png new file mode 100644 index 0000000..cbf2337 Binary files /dev/null and b/public/icons/apple-touch-icon-152x152-precomposed.png differ diff --git a/public/icons/apple-touch-icon-152x152.png b/public/icons/apple-touch-icon-152x152.png new file mode 100644 index 0000000..e6b3d43 Binary files /dev/null and b/public/icons/apple-touch-icon-152x152.png differ diff --git a/public/icons/apple-touch-icon-180x180-precomposed.png b/public/icons/apple-touch-icon-180x180-precomposed.png new file mode 100644 index 0000000..b3fc31a Binary files /dev/null and b/public/icons/apple-touch-icon-180x180-precomposed.png differ diff --git a/public/icons/apple-touch-icon-180x180.png b/public/icons/apple-touch-icon-180x180.png new file mode 100644 index 0000000..952340d Binary files /dev/null and b/public/icons/apple-touch-icon-180x180.png differ 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 0000000..26178b4 Binary files /dev/null and b/public/icons/apple-touch-icon-60x60-precomposed.png differ diff --git a/public/icons/apple-touch-icon-60x60.png b/public/icons/apple-touch-icon-60x60.png new file mode 100644 index 0000000..c23ddf3 Binary files /dev/null and b/public/icons/apple-touch-icon-60x60.png differ 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 0000000..1e0a2f8 Binary files /dev/null and b/public/icons/apple-touch-icon-76x76-precomposed.png differ diff --git a/public/icons/apple-touch-icon-76x76.png b/public/icons/apple-touch-icon-76x76.png new file mode 100644 index 0000000..954eb27 Binary files /dev/null and b/public/icons/apple-touch-icon-76x76.png differ diff --git a/public/icons/apple-touch-icon-precomposed.png b/public/icons/apple-touch-icon-precomposed.png new file mode 100644 index 0000000..b3fc31a Binary files /dev/null and b/public/icons/apple-touch-icon-precomposed.png differ diff --git a/public/icons/apple-touch-icon.png b/public/icons/apple-touch-icon.png new file mode 100644 index 0000000..952340d Binary files /dev/null and b/public/icons/apple-touch-icon.png differ 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 0000000..821bb9f Binary files /dev/null and b/public/icons/favicon-16x16.png differ diff --git a/public/icons/favicon-32x32.png b/public/icons/favicon-32x32.png new file mode 100644 index 0000000..2e5456d Binary files /dev/null and b/public/icons/favicon-32x32.png differ diff --git a/public/icons/favicon.ico b/public/icons/favicon.ico new file mode 100644 index 0000000..a19896d Binary files /dev/null and b/public/icons/favicon.ico differ diff --git a/public/icons/mstile-150x150.png b/public/icons/mstile-150x150.png new file mode 100644 index 0000000..f04e6ad Binary files /dev/null and b/public/icons/mstile-150x150.png differ 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" +} diff --git a/src/MCL.jsx b/src/MCL.jsx index c24ec3e..e15b85e 100644 --- a/src/MCL.jsx +++ b/src/MCL.jsx @@ -1,4 +1,6 @@ // Imports +import { ThemeProvider } from "@mui/material/styles"; +import mclTheme from "./util/theme.js"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { SettingsProvider } from "@mcl/settings"; import Viewport from "./nav/Viewport.jsx"; @@ -11,9 +13,11 @@ export default function MCL() {
- - - + + + + +
diff --git a/src/components/files/FilePreview.jsx b/src/components/files/FilePreview.jsx new file mode 100644 index 0000000..c6e1002 --- /dev/null +++ b/src/components/files/FilePreview.jsx @@ -0,0 +1,103 @@ +import { useState, useEffect } 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 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", + "toml", + "tml", + "text", +]; +const imageFileTypes = ["png", "jpeg", "jpg"]; + +export const supportedFileTypes = [...textFileTypes, ...imageFileTypes]; + +export function useFilePreview(isOpen = false) { + const [open, setOpen] = useState(isOpen); + const dialogToggle = () => setOpen(!open); + return [open, dialogToggle]; +} + +export default function FilePreview(props) { + const [fileText, setFileText] = useState(); + const [modifiedText, setModifiedText] = useState(); + const theme = useTheme(); + const fullScreen = useMediaQuery(theme.breakpoints.down("md")); + + const { previewData, open, dialogToggle, server: serverId } = props; + const { fileData, name, filePath } = previewData ?? {}; + const ext = name ? name.split(".").pop() : null; + const isTextFile = textFileTypes.includes(ext); + + useEffect(() => { + onPreviewChange(); + }, [fileData]); + const editorChange = (v) => setModifiedText(v); + + async function onPreviewChange() { + if (!isTextFile) return; + const text = await fileData.text(); + setFileText(text); + } + + async function onSave() { + if (!isTextFile) return; + const formData = new FormData(); + const blob = new Blob([modifiedText], { type: "plain/text" }); + formData.append("file", blob, name); + formData.append("id", serverId); + formData.append("path", filePath); + await fetch("/api/files/upload", { + method: "POST", + body: formData, + headers: cairoAuthHeader(), + }); + dialogToggle(); + } + + return ( + + + {name} + + {isTextFile && } + + + + + + + ); +} diff --git a/src/components/files/MineclusterFiles.jsx b/src/components/files/MineclusterFiles.jsx new file mode 100644 index 0000000..eb48283 --- /dev/null +++ b/src/components/files/MineclusterFiles.jsx @@ -0,0 +1,200 @@ +import { useState, useEffect, useMemo, useRef } from "react"; +import Box from "@mui/material/Box"; +import Dropzone from "react-dropzone"; + +import { + FileBrowser, + FileContextMenu, + FileList, + FileNavbar, + FileToolbar, + setChonkyDefaults, + ChonkyActions, +} from "chonky"; +import { ChonkyIconFA } from "chonky-icon-fontawesome"; + +import { + getServerFiles, + createServerFolder, + deleteServerItem, + getServerItem, + moveServerItems, + previewServerItem, +} from "@mcl/queries"; +import { cairoAuthHeader } from "@mcl/util/auth.js"; + +import { supportedFileTypes } from "./FilePreview.jsx"; + +export default function MineclusterFiles(props) { + // Chonky configuration + setChonkyDefaults({ iconComponent: ChonkyIconFA }); + const fileActions = useMemo( + () => [ + ChonkyActions.CreateFolder, + ChonkyActions.UploadFiles, + ChonkyActions.DownloadFiles, + ChonkyActions.CopyFiles, + ChonkyActions.DeleteFiles, + ChonkyActions.MoveFiles, + ], + [], + ); + const { server: serverId, changePreview } = props; + const inputRef = useRef(null); + const [dirStack, setDirStack] = useState(["."]); + const [files, setFiles] = useState([]); + + const updateFiles = () => { + const dir = dirStack.join("/"); + getServerFiles(serverId, dir) + .then((f) => { + const files = f.map((fi) => ({ ...fi, id: `${dir}/${fi.name}` })); + setFiles(files ?? []); + }) + .catch(() => + console.error( + "Couldn't update files, server likely hasn't started yet", + ), + ); + }; + + useEffect(() => { + updateFiles(); + }, [dirStack]); + + const getFolderChain = () => { + if (dirStack.length === 1) return [{ id: "./", name: "Home", isDir: true }]; + return dirStack.map((d, i) => ({ + id: `${dirStack.slice(0, i + 1).join("/")}`, + name: i === 0 ? "Home" : d, + isDir: true, + })); + }; + + const openParentFolder = () => setDirStack(dirStack.slice(0, -1)); + + function openItem(payload) { + const { targetFile: file } = payload; + if (file && file.isDir) return setDirStack(file.id.split("/")); + if (!file || file.isDir) return; // Ensure file exists or is dir + if (supportedFileTypes.includes(file.name.split(".").pop())) + return previewFile(file); + return downloadFiles([file]); + } + + function createFolder() { + const name = prompt("What is the name of the new folder?"); + const path = [...dirStack, name].join("/"); + createServerFolder(serverId, path).then(updateFiles); + } + + function deleteItems(files) { + Promise.all( + files.map((f) => + deleteServerItem(serverId, [...dirStack, f.name].join("/"), f.isDir), + ), + ) + .catch((e) => console.error("Error deleting some files!", e)) + .then(updateFiles); + } + + function uploadFileSelection(e) { + if (!e.target.files || e.target.files.length === 0) return; + const { files } = e.target; + uploadMultipleFiles(files); + } + + function uploadMultipleFiles(files) { + Promise.all([...files].map((f) => uploadFile(f))) + .catch((e) => console.log("Error uploading a file", e)) + .then(updateFiles); + } + + 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); + const path = `${[...dirStack].join("/")}${filePath}`; + formData.append("path", path); + await fetch("/api/files/upload", { + method: "POST", + body: formData, + headers: cairoAuthHeader(), + }); + } + + async function downloadFiles(files) { + Promise.all( + files.map((f) => + getServerItem(serverId, f.name, [...dirStack, f.name].join("/")), + ), + ) + .then(() => console.log("Done downloading files!")) + .catch((e) => console.error("Error Downloading files!", e)); + } + + function previewFile(file) { + const { name } = file; + previewServerItem(serverId, [...dirStack, name].join("/")).then( + (fileData) => + changePreview(name, fileData, [...dirStack, name].join("/")), + ); + } + + function moveFile(movePayload) { + const { files: filePayload, destination: destinationPayload } = movePayload; + if (!destinationPayload.isDir || filePayload.length === 0) return; + const files = filePayload.map((f) => f.name); + const dest = destinationPayload.id; + const origin = dirStack.join("/"); + moveServerItems(serverId, files, dest, origin).then(updateFiles); + } + + function fileClick(chonkyEvent) { + const { id: clickEvent, payload } = chonkyEvent; + if (clickEvent === "open_parent_folder") return openParentFolder(); + if (clickEvent === "create_folder") return createFolder(); + if (clickEvent === "upload_files") return inputRef.current.click(); + if (clickEvent === "download_files") + return downloadFiles(chonkyEvent.state.selectedFilesForAction); + if (clickEvent === "delete_files") + return deleteItems(chonkyEvent.state.selectedFilesForAction); + if (clickEvent === "move_files") return moveFile(payload); + if (clickEvent !== "open_files") return; // console.log(clickEvent); + openItem(payload); + } + + return ( + + {({ getRootProps }) => ( + + + + + + + + + + )} + + ); +} diff --git a/src/components/files/TextEditor.jsx b/src/components/files/TextEditor.jsx new file mode 100644 index 0000000..6151cf8 --- /dev/null +++ b/src/components/files/TextEditor.jsx @@ -0,0 +1,21 @@ +import ReactQuill from "react-quill"; +import { useState, useEffect, useMemo, memo } from "react"; +import "react-quill/dist/quill.snow.css"; + +const buildDelta = (t) => { + if (!t) return; + const ops = t.split("\n").map((l) => ({ insert: `${l}\n` })); + return { ops }; +}; + +function TextEditor(props) { + const { text, onChange } = props; + const [delta, setDelta] = useState(); + const constructDelta = useMemo(() => buildDelta(text), [text]); + useEffect(() => setDelta(constructDelta), [text]); + + const onEditorChange = (c, d, s, editor) => onChange(editor.getText()); + + return ; +} +export default memo(TextEditor, (a, b) => a.text === b.text); diff --git a/src/overview/Overview.jsx b/src/components/overview/Overview.jsx similarity index 100% rename from src/overview/Overview.jsx rename to src/components/overview/Overview.jsx diff --git a/src/overview/OverviewVisual.jsx b/src/components/overview/OverviewVisual.jsx similarity index 100% rename from src/overview/OverviewVisual.jsx rename to src/components/overview/OverviewVisual.jsx diff --git a/src/components/server-options/BackupBucketOption.jsx b/src/components/server-options/BackupBucketOption.jsx new file mode 100644 index 0000000..7650187 --- /dev/null +++ b/src/components/server-options/BackupBucketOption.jsx @@ -0,0 +1,15 @@ +import TextField from "@mui/material/TextField"; +export default function BackupBucketOption(props) { + const { value, onChange } = props; + + return ( + + ); +} diff --git a/src/components/server-options/BackupHostOption.jsx b/src/components/server-options/BackupHostOption.jsx new file mode 100644 index 0000000..5d96002 --- /dev/null +++ b/src/components/server-options/BackupHostOption.jsx @@ -0,0 +1,15 @@ +import TextField from "@mui/material/TextField"; +export default function BackupHostOption(props) { + const { value, onChange } = props; + + return ( + + ); +} diff --git a/src/components/server-options/BackupIdOption.jsx b/src/components/server-options/BackupIdOption.jsx new file mode 100644 index 0000000..80b54b5 --- /dev/null +++ b/src/components/server-options/BackupIdOption.jsx @@ -0,0 +1,15 @@ +import TextField from "@mui/material/TextField"; +export default function BackupIdOption(props) { + const { value, onChange } = props; + + return ( + + ); +} diff --git a/src/components/server-options/BackupIntervalOption.jsx b/src/components/server-options/BackupIntervalOption.jsx new file mode 100644 index 0000000..610c6fd --- /dev/null +++ b/src/components/server-options/BackupIntervalOption.jsx @@ -0,0 +1,55 @@ +import { useState } from "react"; +import Box from "@mui/material/Box"; +import MenuItem from "@mui/material/MenuItem"; +import TextField from "@mui/material/TextField"; + +const backupIntervalStepDisplay = ["Minutes", "Hours", "Days"]; +export const backupIntervalDefault = "1d"; +export const backupIntervalStepOptions = ["m", "h", "d"]; +export default function BackupIntervalOption(props) { + const { onChange } = props; + const [interval, setInterval] = useState(1); + const [intervalStep, setIntervalStep] = useState( + backupIntervalStepOptions[2], + ); + + const changeStep = (e) => { + setIntervalStep(e.target.value); + onChange({ target: { value: `${interval}${e.target.value}` } }); + }; + + const changeInterval = (e) => { + setInterval(e.target.value); + onChange({ target: { value: `${e.target.value}${intervalStep}` } }); + }; + + return ( + + + + {backupIntervalStepOptions.map((o, i) => ( + + {backupIntervalStepDisplay[i]} + + ))} + + + ); +} diff --git a/src/components/server-options/BackupKeyOption.jsx b/src/components/server-options/BackupKeyOption.jsx new file mode 100644 index 0000000..13cec43 --- /dev/null +++ b/src/components/server-options/BackupKeyOption.jsx @@ -0,0 +1,15 @@ +import TextField from "@mui/material/TextField"; +export default function BackupKeyOption(props) { + const { value, onChange } = props; + + return ( + + ); +} diff --git a/src/components/server-options/CpuOption.jsx b/src/components/server-options/CpuOption.jsx new file mode 100644 index 0000000..ebc812c --- /dev/null +++ b/src/components/server-options/CpuOption.jsx @@ -0,0 +1,26 @@ +import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; + +const maxCpuSupported = 8; +export const cpuOptions = new Array(2 * maxCpuSupported) + .fill(0) + .map((v, i) => (i + 1) * 0.5); + +export default function CpuOption(props) { + const { value, onChange } = props; + return ( + + {cpuOptions.map((o, i) => ( + {`${o} CPU`} + ))} + + ); +} diff --git a/src/components/server-options/ExtraPortsOption.jsx b/src/components/server-options/ExtraPortsOption.jsx new file mode 100644 index 0000000..893a067 --- /dev/null +++ b/src/components/server-options/ExtraPortsOption.jsx @@ -0,0 +1,50 @@ +import { useState } from "react"; +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 && parseInt(p) < 60_000; + +export default function ExtraPortsOption(props) { + const { extraPorts: initExtraPorts } = props; + const [extraPorts, setExtraPorts] = useState(initExtraPorts ?? []); + const { onChange } = props; + + function portChange(e, val, optionType, changedValue) { + if (optionType === "clear") { + setExtraPorts([]); + onChange("extraPorts", []); + return; + } + if (!validatePort(changedValue.option)) + return alert("That port cannot be added/removed as an extra port!"); + setExtraPorts(val); + onChange("extraPorts", val); + } + + return ( + ( + + )} + renderTags={(value, getTagProps) => + value.map((option, index) => { + const defaultChipProps = getTagProps({ index }); + return ; + }) + } + /> + ); +} diff --git a/src/components/server-options/HostOption.jsx b/src/components/server-options/HostOption.jsx new file mode 100644 index 0000000..15d14e9 --- /dev/null +++ b/src/components/server-options/HostOption.jsx @@ -0,0 +1,21 @@ +import TextField from "@mui/material/TextField"; +export default function HostOption(props) { + const { value, onChange, disabled } = props; + + function onTextChange(e) { + e.target.value = e.target.value.toLowerCase(); + onChange(e); + } + + return ( + + ); +} diff --git a/src/components/server-options/MemoryOption.jsx b/src/components/server-options/MemoryOption.jsx new file mode 100644 index 0000000..97fe871 --- /dev/null +++ b/src/components/server-options/MemoryOption.jsx @@ -0,0 +1,24 @@ +import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; +const maxMemSupported = 10; +export const memoryOptions = new Array(2 * maxMemSupported) + .fill(0) + .map((v, i) => (i + 1) * 512); + +export default function Option(props) { + const { value, onChange } = props; + return ( + + {memoryOptions.map((o, i) => ( + {`${o / 1024} Gi`} + ))} + + ); +} diff --git a/src/components/server-options/NameOption.jsx b/src/components/server-options/NameOption.jsx new file mode 100644 index 0000000..18a0f0d --- /dev/null +++ b/src/components/server-options/NameOption.jsx @@ -0,0 +1,15 @@ +import TextField from "@mui/material/TextField"; +export default function NameOption(props) { + const { value, onChange } = props; + + return ( + + ); +} diff --git a/src/components/server-options/ServerTypeOption.jsx b/src/components/server-options/ServerTypeOption.jsx new file mode 100644 index 0000000..f123528 --- /dev/null +++ b/src/components/server-options/ServerTypeOption.jsx @@ -0,0 +1,25 @@ +import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; + +const displayOption = (o) => o.charAt(0) + o.toLowerCase().slice(1); + +export const serverTypeOptions = ["VANILLA", "FABRIC", "PAPER", "SPIGOT"]; +export default function ServerTypeOption(props) { + const { value, onChange } = props; + return ( + + {serverTypeOptions.map((o, i) => ( + + {displayOption(o)} + + ))} + + ); +} diff --git a/src/components/server-options/StorageOption.jsx b/src/components/server-options/StorageOption.jsx new file mode 100644 index 0000000..d1b7ed2 --- /dev/null +++ b/src/components/server-options/StorageOption.jsx @@ -0,0 +1,26 @@ +import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; + +const maxStorageSupported = 80; +export const storageOptions = new Array(2 * maxStorageSupported) + .fill(0) + .map((v, i) => (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/server-options/VersionOption.jsx b/src/components/server-options/VersionOption.jsx new file mode 100644 index 0000000..8293e0d --- /dev/null +++ b/src/components/server-options/VersionOption.jsx @@ -0,0 +1,37 @@ +import { useState, useEffect } from "react"; +import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; +import { useVersionList } from "@mcl/queries"; + +export default function VersionOption(props) { + const { value, onChange } = props; + const versionList = useVersionList(); + const [versions, setVersions] = useState(["latest"]); + + useEffect(() => { + if (!versionList.data) return; + setVersions([ + "latest", + ...versionList.data.versions + .filter(({ type: releaseType }) => releaseType === "release") + .map(({ id }) => id), + ]); + }, [versionList.data]); + + return ( + + {versions.map((v, k) => ( + + {v} + + ))} + + ); +} 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/servers/RconDialog.jsx b/src/components/servers/RconDialog.jsx similarity index 71% rename from src/servers/RconDialog.jsx rename to src/components/servers/RconDialog.jsx index 5183d1a..ad69761 100644 --- a/src/servers/RconDialog.jsx +++ b/src/components/servers/RconDialog.jsx @@ -16,24 +16,23 @@ export function useRconDialog(isOpen = false) { } export default function RconDialog(props) { - const { serverName, open, dialogToggle } = props; + const { server, open, dialogToggle } = props; + const { name: serverName, id: serverId } = server ?? {}; const theme = useTheme(); - const fullScreen = useMediaQuery(theme.breakpoints.down("sm")); + const fullScreen = useMediaQuery(theme.breakpoints.down("md")); + return ( - + RCON - {serverName} - - + + + )} + {!(rcon && rcon.rconLive && !rcon.rconError) && ( + + )} + + + ); +} diff --git a/src/servers/ServerCard.jsx b/src/components/servers/ServerCard.jsx similarity index 63% rename from src/servers/ServerCard.jsx rename to src/components/servers/ServerCard.jsx index ac61a5c..0b6f724 100644 --- a/src/servers/ServerCard.jsx +++ b/src/components/servers/ServerCard.jsx @@ -11,18 +11,20 @@ import Typography from "@mui/material/Typography"; import StopIcon from "@mui/icons-material/Stop"; import TerminalIcon from "@mui/icons-material/Terminal"; import PlayArrowIcon from "@mui/icons-material/PlayArrow"; -import PendingIcon from "@mui/icons-material/Pending"; 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 { name, metrics, started } = server; - const startServer = useStartServer(name); - const stopServer = useStopServer(name); - const deleteServer = useDeleteServer(name); + const { server, openRcon, openBackups } = props; + const { name, id, metrics, ftpAvailable, serverAvailable, services } = server; + const startServer = useStartServer(id); + const stopServer = useStopServer(id); + const deleteServer = useDeleteServer(id); function toggleRcon() { - if (!started) return; + if (!services.includes("server")) return; openRcon(); } @@ -58,14 +60,26 @@ export default function ServerCard(props) { )}
- {started && ( + {services.includes("server") && ( )} - {!started && ( + {!services.includes("server") && ( - + + + + + + + { if (settingsUpdate[k] === undefined) continue; settingsToUpdate[k] = settingsUpdate[k]; } + console.log("SAVING", settingsToUpdate); localStorage.setItem("settings", JSON.stringify(settingsToUpdate)); }; diff --git a/src/nav/MCLMenu.jsx b/src/nav/MCLMenu.jsx index aa688b3..6108f0a 100644 --- a/src/nav/MCLMenu.jsx +++ b/src/nav/MCLMenu.jsx @@ -36,8 +36,8 @@ export default function MCLMenu() { theme.zIndex.modal + 2 - (isDrawer ? 1 : 0); return ( - - + + - {pages.map((page, index) => ( - - {page.icon} - - - ))} + {pages.map( + (page, index) => + page.visible && ( + + {page.icon} + + + ), + )} diff --git a/src/nav/MCLPages.jsx b/src/nav/MCLPages.jsx index cf7b86c..c2518c7 100644 --- a/src/nav/MCLPages.jsx +++ b/src/nav/MCLPages.jsx @@ -1,5 +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"; @@ -10,11 +12,27 @@ export default [ path: "/mcl/home", icon: , component: , + visible: true, }, { name: "Create", path: "/mcl/create", icon: , component: , + visible: true, + }, + { + name: "Files", + path: "/mcl/files", + icon: , + component: , + visible: false, + }, + { + name: "Edit", + path: "/mcl/edit", + icon: , + component: , + visible: false, }, ]; diff --git a/src/nav/Viewport.jsx b/src/nav/Viewport.jsx index af78dff..fabe10c 100644 --- a/src/nav/Viewport.jsx +++ b/src/nav/Viewport.jsx @@ -1,11 +1,14 @@ -import Box from "@mui/material/Box"; import Toolbar from "@mui/material/Toolbar"; import MCLPortal from "./MCLPortal.jsx"; // 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/pages/Create.jsx b/src/pages/Create.jsx index 7507514..b8e6963 100644 --- a/src/pages/Create.jsx +++ b/src/pages/Create.jsx @@ -1,152 +1,11 @@ -import { useState, useEffect } from "react"; -import TextField from "@mui/material/TextField"; import Box from "@mui/material/Box"; -import Button from "@mui/material/Button"; -import Select from "@mui/material/Select"; -import MenuItem from "@mui/material/MenuItem"; -import InputLabel from "@mui/material/InputLabel"; -import FormControl from "@mui/material/FormControl"; -import { useCreateServer, useVersionList } from "@mcl/queries"; - -const defaultServer = { - version: "latest", - name: "example", - serverType: "VANILLA", - difficulty: "easy", - maxPlayers: "20", - gamemode: "survival", - memory: "1024", - motd: "Minecluster Server Hosting", -}; - +import CreateCoreOptions from "./CreateCoreOptions.jsx"; export default function Create() { - const [spec, setSpec] = useState(defaultServer); - const versionList = useVersionList(); - const [versions, setVersions] = useState(["latest"]); - const createServer = useCreateServer(spec); - const updateSpec = (attr, val) => { - const s = { ...spec }; - s[attr] = val; - setSpec(s); - }; - - useEffect(() => { - if (!versionList.data) return; - setVersions([ - "latest", - ...versionList.data.versions - .filter(({ type: releaseType }) => releaseType === "release") - .map(({ id }) => id), - ]); - }, [versionList.data]); - - const coreUpdate = (attr) => (e) => updateSpec(attr, e.target.value); - - function upsertSpec() { - if (validateSpec() !== "validated") return; - createServer(spec); - } - - function validateSpec() { - console.log("TODO CREATE VALIDATION"); - if (!spec.name) return alertValidationError("Name not included"); - if (!spec.version) return alertValidationError("Version cannot be blank"); - if (!spec.url) return alertValidationError("Url cannot be blank"); - return "validated"; - } - - function alertValidationError(reason) { - alert(`Could not validate spec because: ${reason}`); - } - return ( - - - - - {versions.map((v, k) => ( - - {v} - - ))} - - - Vanilla - Fabric - Paper - Spigot - - - Peaceful - Easy - Medium - Hard - - - - - - - - Survival - Creative - Adventure - Spectator - - - - - - - + + + ); } diff --git a/src/pages/CreateCoreOptions.jsx b/src/pages/CreateCoreOptions.jsx new file mode 100644 index 0000000..ad983ae --- /dev/null +++ b/src/pages/CreateCoreOptions.jsx @@ -0,0 +1,152 @@ +import { useState } 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 { useCreateServer } 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 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"; +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"; + +const defaultServer = { + version: "latest", + serverType: serverTypeOptions[0], + cpu: cpuOptions[0], + memory: memoryOptions[2], // 1.5GB + storage: 0, + extraPorts: [], +}; + +export default function CreateCoreOptions() { + const [backupEnabled, setBackupEnabled] = useState(false); + const [spec, setSpec] = useState(defaultServer); + const nav = useNavigate(); + const createServer = useCreateServer(spec); + + const updateSpec = (attr, val) => { + const s = { ...spec }; + s[attr] = val; + setSpec(s); + }; + + const coreUpdate = (attr) => (e) => updateSpec(attr, e.target.value); + + async function upsertSpec() { + if (validateSpec() !== "validated") return; + createServer() + .then(() => nav("/")) + .catch(alert); + } + + 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}`); + } + + const toggleBackupEnabled = () => { + const s = { ...spec }; + if (!backupEnabled) { + (s.backupInterval = backupIntervalDefault), + (s.backupBucket = `/mcl/server-backups/${( + s.name ?? "my-server" + ).toLowerCase()}`); + } else for (var k in s) if (k.startsWith("backup")) delete s[k]; + setSpec(s); + setBackupEnabled(!backupEnabled); + }; + + return ( + + + + + + + + + + + {spec.storage !== 0 && ( + + } + label="Enable Backups?" + labelPlacement="start" + sx={{ mr: "auto" }} + /> + )} + {backupEnabled && spec.storage !== 0 && ( + + Backups + + + + + + + )} + + + + + ); +} diff --git a/src/pages/CreateOptionsFull.jsx b/src/pages/CreateOptionsFull.jsx new file mode 100644 index 0000000..1d486f8 --- /dev/null +++ b/src/pages/CreateOptionsFull.jsx @@ -0,0 +1,250 @@ +import { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import Autocomplete from "@mui/material/Autocomplete"; +import TextField from "@mui/material/TextField"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Chip from "@mui/material/Chip"; +import Select from "@mui/material/Select"; +import MenuItem from "@mui/material/MenuItem"; +import InputLabel from "@mui/material/InputLabel"; +import FormControl from "@mui/material/FormControl"; +import { useCreateServer, useVersionList } from "@mcl/queries"; + +const defaultServer = { + version: "latest", + serverType: "VANILLA", + difficulty: "easy", + maxPlayers: "5", + gamemode: "survival", + memory: "512", + motd: `\\u00A7e\\u00A7ka\\u00A7l\\u00A7aMine\\u00A76Cluster\\u00A7r\\u00A78\\u00A7b\\u00A7ka`, +}; + +export default function Create() { + const [wl, setWl] = useState([]); + const [ops, setOps] = useState([]); + const [spec, setSpec] = useState(defaultServer); + const nav = useNavigate(); + const versionList = useVersionList(); + const [versions, setVersions] = useState(["latest"]); + const createServer = useCreateServer(spec); + const updateSpec = (attr, val) => { + const s = { ...spec }; + s[attr] = val; + setSpec(s); + console.log(s); + }; + + useEffect(() => { + if (!versionList.data) return; + setVersions([ + "latest", + ...versionList.data.versions + .filter(({ type: releaseType }) => releaseType === "release") + .map(({ id }) => id), + ]); + }, [versionList.data]); + + const coreUpdate = (attr) => (e) => updateSpec(attr, e.target.value); + + function opsAdd(e) { + const opEntry = e.target.innerHTML ?? e.target.value; + if (!opEntry) return; + const newOps = [...ops, opEntry]; + setOps(newOps); + updateSpec("ops", newOps.join(",")); + } + + function whitelistAdd(e) { + const wlEntry = e.target.value; + if (!wlEntry) return; + const newWl = [...wl, wlEntry]; + setWl(newWl); + updateSpec("whitelist", newWl.join(",")); + } + + const opsRemove = + (name, { onDelete: updateAutoComplete }) => + (e) => { + updateAutoComplete(e); + const newOps = [...ops]; + const entryIndex = newOps.indexOf(name); + if (entryIndex === -1) return; + newOps.splice(entryIndex, 1); + setOps(newOps); + updateSpec("ops", newOps.join(",")); + }; + + const whitelistRemove = + (name, { onDelete: updateAutocomplete }) => + (e) => { + updateAutocomplete(e); + const newWl = [...wl]; + const entryIndex = newWl.indexOf(name); + if (entryIndex === -1) return; + newWl.splice(entryIndex, 1); + setWl(newWl); + updateSpec("whitelist", newWl.join(",")); + }; + + async function upsertSpec() { + if (validateSpec() !== "validated") return; + createServer(spec) + .then(() => nav("/")) + .catch(alert); + } + + function validateSpec() { + console.log("TODO CREATE VALIDATION"); + if (!spec.name) return alertValidationError("Name not included"); + if (!spec.version) return alertValidationError("Version cannot be blank"); + if (!spec.host) return alertValidationError("Host cannot be blank"); + return "validated"; + } + + function alertValidationError(reason) { + alert(`Could not validate spec because: ${reason}`); + } + + return ( + + + + + + {versions.map((v, k) => ( + + {v} + + ))} + + + Vanilla + Fabric + Paper + Spigot + + + Peaceful + Easy + Medium + Hard + + + } + renderTags={(value, getTagProps) => + value.map((option, index) => { + const defaultChipProps = getTagProps({ index }); + return ( + + ); + }) + } + /> + } + renderTags={(value, getTagProps) => + value.map((option, index) => { + const defaultChipProps = getTagProps({ index }); + return ( + + ); + }) + } + /> + {/**/} + {/**/} + + + Survival + Creative + Adventure + Spectator + + + {/**/} + + + + + + ); +} diff --git a/src/pages/Edit.jsx b/src/pages/Edit.jsx new file mode 100644 index 0000000..c84429f --- /dev/null +++ b/src/pages/Edit.jsx @@ -0,0 +1,14 @@ +import { useSearchParams, useNavigate } from "react-router-dom"; +import Box from "@mui/material/Box"; +import EditCoreOptions from "./EditCoreOptions.jsx"; +export default function Edit() { + const [searchParams] = useSearchParams(); + const currentServer = searchParams.get("server"); + return ( + + + + + + ); +} diff --git a/src/pages/EditCoreOptions.jsx b/src/pages/EditCoreOptions.jsx new file mode 100644 index 0000000..bda2c34 --- /dev/null +++ b/src/pages/EditCoreOptions.jsx @@ -0,0 +1,137 @@ +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 nav = useNavigate(); + 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().then(() => nav("/")); + + 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/pages/Files.jsx b/src/pages/Files.jsx new file mode 100644 index 0000000..e970b7f --- /dev/null +++ b/src/pages/Files.jsx @@ -0,0 +1,35 @@ +import { useState, useEffect } from "react"; +import { useSearchParams, useNavigate } from "react-router-dom"; +import Box from "@mui/material/Box"; +import FilePreview, { + useFilePreview, +} from "@mcl/components/files/FilePreview.jsx"; +import MineclusterFiles from "@mcl/components/files/MineclusterFiles.jsx"; + +export default function Files() { + const [open, dialogToggle] = useFilePreview(); + const [previewData, setPreviewData] = useState(); + const [searchParams] = useSearchParams(); + const currentServer = searchParams.get("server"); + const nav = useNavigate(); + useEffect(() => { + if (!currentServer) nav("/"); + }, [currentServer]); + + function changePreview(name, fileData, filePath) { + setPreviewData({ name, fileData, filePath }); + dialogToggle(); + } + + return ( + + + + + ); +} diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index 1c79043..47b5f6b 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -1,19 +1,29 @@ +import { Link } from "react-router-dom"; import { useState, useEffect } from "react"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; -import ServerCard from "../servers/ServerCard.jsx"; -import RconDialog, { useRconDialog } from "../servers/RconDialog.jsx"; -import Overview from "../overview/Overview.jsx"; +import ServerCard from "@mcl/components/servers/ServerCard.jsx"; +import RconDialog, { + useRconDialog, +} from "@mcl/components/servers/RconDialog.jsx"; +import Overview from "@mcl/components/overview/Overview.jsx"; +import Button from "@mui/material/Button"; +import SpeedDialIcon from "@mui/material/SpeedDialIcon"; import "@mcl/css/server-card.css"; import "@mcl/css/overview.css"; import { useServerInstances } from "@mcl/queries"; +import BackupDialog, { + useBackupDialog, +} from "../components/servers/BackupsDialog"; export default function Home() { + const clusterMetrics = { cpu: 0, memory: 0 }; const [server, setServer] = useState(); const [servers, setServers] = useState([]); const [rdOpen, rconToggle] = useRconDialog(); + const [bkOpen, backupsToggle] = useBackupDialog(); const { isLoading, data: serversData } = useServerInstances(); - const { servers: serverInstances, clusterMetrics } = serversData ?? {}; + const serverInstances = serversData ?? []; useEffect(() => { if (!serverInstances) return; serverInstances.sort((a, b) => a.name.localeCompare(b.name)); @@ -24,6 +34,12 @@ export default function Home() { setServer(s); rconToggle(); }; + + const openBackups = (s) => () => { + setServer(s); + backupsToggle(); + }; + return ( @@ -44,15 +60,37 @@ export default function Home() { {!isLoading && servers.map((s, k) => ( - + ))} - + + ); } diff --git a/src/servers/RconView.jsx b/src/servers/RconView.jsx deleted file mode 100644 index e6823b3..0000000 --- a/src/servers/RconView.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import { useState, useEffect, useRef } from "react"; -import Box from "@mui/material/Box"; -import Button from "@mui/material/Button"; -import TextField from "@mui/material/TextField"; -import RconSocket from "./RconSocket.js"; -import "@mcl/css/rcon.css"; - -export default function RconView(props) { - const { serverName } = props; - const logsRef = useRef(0); - const [cmd, setCmd] = useState(""); - const [logs, setLogs] = useState([]); - const [rcon, setRcon] = useState({}); - const updateCmd = (e) => setCmd(e.target.value); - useEffect(function () { - setRcon(new RconSocket(setLogs, serverName)); - return () => { - if (rcon && typeof rcon.disconnect === "function") rcon.disconnect(); - }; - }, []); - - useEffect(() => { - logsRef.current.scrollTo(0, logsRef.current.scrollHeight); - }, [rcon.logs]); - - function sendCommand() { - rcon.send(cmd); - setCmd(""); - } - - return ( - -
- {logs.map((v, k) => ( - - {v} -
-
- ))} -
- - - - -
- ); -} diff --git a/src/util/auth.js b/src/util/auth.js new file mode 100644 index 0000000..69b9d8c --- /dev/null +++ b/src/util/auth.js @@ -0,0 +1,47 @@ +import { useState, useEffect } from "react"; +import { useSearchParams } from "react-router-dom"; + +const tokenStorageName = "cairoUserToken"; +const tokenQuery = "cairoUserToken"; + +const verifyAuth = (authToken) => + fetch("/api/auth/verify", { + headers: { Authorization: `Bearer ${authToken}` }, + }) + .then((res) => res.status === 200) + .catch(() => false); + +export function useCairoAuth() { + const [authToken, setAuthToken] = useState( + localStorage.getItem(tokenStorageName), + ); + const [auth, setAuth] = useState(false); + const [searchParams, setSearchParams] = useSearchParams(); + + useEffect(() => { + if (!authToken) return; + verifyAuth(authToken).then((authorized) => { + if (!authorized) localStorage.removeItem(tokenStorageName); + setAuth(authorized); + }); + }, [authToken]); + + useEffect(() => { + const webToken = searchParams.get(tokenQuery); + if (!webToken) return; + localStorage.setItem(tokenStorageName, webToken); + searchParams.delete(tokenQuery); + setAuthToken(webToken); + setSearchParams(searchParams); + }, [searchParams]); + + return auth; +} + +export function getAuthTokenFromStorage() { + return localStorage.getItem(tokenStorageName); +} + +export function cairoAuthHeader() { + return { Authorization: `Bearer ${getAuthTokenFromStorage()}` }; +} diff --git a/src/util/queries.js b/src/util/queries.js index 9636fe4..94a39c6 100644 --- a/src/util/queries.js +++ b/src/util/queries.js @@ -1,49 +1,132 @@ 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)); const fetchApiPost = (subPath, json) => async () => fetch(`/api${subPath}`, { method: "POST", headers: { "Content-Type": "application/json", + ...cairoAuthHeader(), }, body: JSON.stringify(json), }).then((res) => res.json()); -export const useServerStatus = (server) => - useQuery( - [`server-status-${server}`], - fetchApiPost("/server/status", { name: server }) - ); -export const useServerMetrics = (server) => - useQuery( - [`server-metrics-${server}`], - fetchApiPost("/server/metrics", { name: server }), - { refetchInterval: 10000 } - ); -export const useStartServer = (server) => - postJsonApi("/server/start", { name: server }, "server-instances"); -export const useStopServer = (server) => - postJsonApi("/server/stop", { name: server }, "server-instances"); -export const useDeleteServer = (server) => - postJsonApi("/server/delete", { name: server }, "server-instances", "DELETE"); +export const useServerStatus = (serverId) => + useQuery({ + queryKey: [`server-status-${serverId}`], + queryFn: fetchApiPost("/server/status", { id: serverId }), + }); +export const useServerMetrics = (serverId) => + useQuery({ + queryKey: [`server-metrics-${serverId}`], + queryFn: fetchApiPost("/server/metrics", { id: serverId }), + refetchInterval: 10000, + }); +export const useStartServer = (serverId) => + postJsonApi("/server/start", { id: serverId }, "server-instances"); +export const useStopServer = (serverId) => + postJsonApi("/server/stop", { id: serverId }, "server-instances"); +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 getServerBackups = (serverId) => + fetchApiCore("/s3/backups", { id: serverId }, "POST", true); +export const getBackupUrl = (serverId, backupPath) => + fetchApiCore("/s3/backup-url", { id: serverId, backupPath }, "POST", true); + +export const getServerFiles = async (serverId, path) => + fetchApiCore("/files/list", { id: serverId, path }, "POST", true); +export const createServerFolder = async (serverId, path) => + fetchApiCore("/files/folder", { + id: 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"); + const blob = await resp.blob(); + return blob; +} + +export const getServerItem = async (serverId, name, path) => + fetchApiCore("/files/item", { id: serverId, path }) + .then((resp) => + resp.status === 200 + ? resp.blob() + : Promise.reject("something went wrong"), + ) + .then((blob) => { + const url = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + a.style.display = "none"; + a.href = url; + a.download = name; + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + }); + +export const useInvalidator = () => { + const qc = useQueryClient(); + return (q) => qc.invalidateQueries([q]); +}; + export const useServerList = () => - useQuery(["server-list"], fetchApi("/server/list")); + useQuery({ queryKey: ["server-list"], queryFn: fetchApi("/server/list") }); export const useServerInstances = () => - useQuery(["server-instances"], fetchApi("/server/instances"), { + useQuery({ + queryKey: ["server-instances"], + queryFn: fetchApi("/server/instances"), refetchInterval: 5000, }); export const useSystemAvailable = () => - useQuery(["system-available"], fetchApi("/system/available")); + useQuery({ + queryKey: ["system-available"], + queryFn: fetchApi("/system/available"), + }); export const useVersionList = () => - useQuery(["minecraft-versions"], () => - fetch("https://piston-meta.mojang.com/mc/game/version_manifest.json").then( - (r) => r.json() - ) - ); + useQuery({ + queryKey: ["minecraft-versions"], + queryFn: () => + fetch( + "https://piston-meta.mojang.com/mc/game/version_manifest.json", + ).then((r) => r.json()), + }); const postJsonApi = (subPath, body, invalidate, method = "POST") => { const qc = useQueryClient(); @@ -52,9 +135,10 @@ const postJsonApi = (subPath, body, invalidate, method = "POST") => { method, headers: { "Content-Type": "application/json", + ...cairoAuthHeader(), }, body: JSON.stringify(body), }); - qc.invalidateQueries([invalidate]); + if (invalidate) qc.invalidateQueries([invalidate]); }; }; diff --git a/src/util/theme.js b/src/util/theme.js new file mode 100644 index 0000000..f694037 --- /dev/null +++ b/src/util/theme.js @@ -0,0 +1,20 @@ +// Generated using https://zenoo.github.io/mui-theme-creator/ +import { createTheme } from "@mui/material/styles"; +import { unstable_ClassNameGenerator as ClassNameGenerator } from "@mui/material/className"; +// This fixes style clashing with Chonky which has not been updated to Material 5 +// see https://github.com/TimboKZ/Chonky/issues/101#issuecomment-1362949314 +ClassNameGenerator.configure((componentName) => `mcl-${componentName}`); + +const themeOptions = { + palette: { + mode: "light", + primary: { + main: "#29985c", + }, + secondary: { + main: "#f50057", + }, + }, +}; + +export default createTheme(themeOptions); 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 diff --git a/vite.config.js b/vite.config.js index c71a2b2..5d25a00 100644 --- a/vite.config.js +++ b/vite.config.js @@ -15,6 +15,7 @@ export default () => { proxy: { "/api": backendUrl, "/socket.io": backendUrl, + "/healthz": backendUrl, }, hmr: { protocol: process.env.MCL_VITE_DEV_PROTOCOL, @@ -26,10 +27,11 @@ export default () => { base: "/mcl/", resolve: { alias: { - "@mcl/css": path.resolve("./public/css"), + "@mcl/css": path.resolve("./src/css"), "@mcl/settings": path.resolve("./src/ctx/SettingsContext.jsx"), "@mcl/pages": path.resolve("./src/pages"), "@mcl/queries": path.resolve("./src/util/queries.js"), + "@mcl/components": path.resolve("./src/components"), "@mcl": path.resolve("./src"), }, },