[TS] Initial Typescript fix

This commit is contained in:
Dunemask 2024-04-06 20:38:10 -06:00
parent 8c15dd6752
commit 813295c857
36 changed files with 4285 additions and 1951 deletions

2
.gitignore vendored
View file

@ -1,2 +1,4 @@
node_modules/ node_modules/
build
dist

View file

@ -18,6 +18,6 @@
<body> <body>
<noscript>You need to enable JavaScript to run Minecluster</noscript> <noscript>You need to enable JavaScript to run Minecluster</noscript>
<div id="mcl"></div> <div id="mcl"></div>
<script type="module" src="/src/App.jsx"></script> <script type="module" src="/src/App.tsx"></script>
</body> </body>
</html> </html>

View file

@ -1,12 +1,14 @@
// @ts-nocheck // @ts-nocheck
import k8s from "@kubernetes/client-node"; import k8s from "@kubernetes/client-node";
import Minecluster from "./Minecluster.js"; import Minecluster from "./Minecluster.js";
const mcl = new Minecluster(); const mcl = new Minecluster();
mcl.start(); mcl.start();
async function main(){ async function main() {
const kc = new k8s.KubeConfig(); const kc = new k8s.KubeConfig();
kc.loadFromDefault(); kc.loadFromDefault();
} }
main().catch((e)=>{console.error(e)}); main().catch((e) => {
console.error(e);
});

5495
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,13 +4,18 @@
"description": "Minecraft Server management using Kubernetes", "description": "Minecraft Server management using Kubernetes",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build:react": "vite build",
"start": "node dist/app.js", "start": "node dist/app.js",
"dev:server": "nodemon dist/app.js",
"dev:react": "vite",
"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": "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" "start:dev:garden": "concurrently -k \"npm run dev:server\" \"npm run dev:react\" -n s,v -p -c green,yellow",
"build:react": "vite build",
"build:server": "esbuild `find lib \\( -name '*.ts' \\)` --tsconfig=tsconfig.server.json --outdir=build/server && tsc-alias -p tsconfig.server.json",
"build:all": "rm -Rf build && concurrently --kill-others-on-fail \"npm run build:react\" \"npm run build:server\" -n s,v -c cyan,yellow",
"package:dist": "mkdir -p dist && mv build/server/* dist/ && mv build/vite dist/static && rm -Rf build && cp -R lib/database/migrations dist/database/migrations",
"package:full": "rm -Rf dist && npm run build:all && npm run package:dist",
"dev:server": "nodemon lib/app.ts",
"dev:react": "vite",
"format": "npx prettier -w src lib vite.config.ts tsconfig*.json",
"tsc": "concurrently --kill-others-on-fail \"tsc --noEmit\" \"tsc -p tsconfig.server.json --noEmit\" -n s,v -c cyan,yellow"
}, },
"keywords": [ "keywords": [
"Minecraft", "Minecraft",
@ -22,44 +27,65 @@
"author": "Dunemask", "author": "Dunemask",
"license": "LGPL-2.1", "license": "LGPL-2.1",
"devDependencies": { "devDependencies": {
"@emotion/react": "^11.11.3", "@aperturerobotics/chonky": "^0.2.8",
"@emotion/styled": "^11.11.0", "@aperturerobotics/chonky-icon-fontawesome": "^0.2.8",
"@mui/icons-material": "^5.15.9", "@emotion/react": "^11.11.4",
"@mui/material": "^5.15.9", "@emotion/styled": "^11.11.5",
"@tanstack/react-query": "^5.20.1", "@mui/icons-material": "^5.15.15",
"@mui/material": "^5.15.15",
"@tanstack/react-query": "^5.29.0",
"@types/bcrypt": "^5.0.2",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.6",
"@types/node": "^20.12.5",
"@types/pg": "^8.11.4",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"chonky": "^2.3.2", "chonky": "^2.3.2",
"chonky-icon-fontawesome": "^2.3.2", "chonky-icon-fontawesome": "^2.3.2",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"nodemon": "^3.0.3", "esbuild": "^0.20.2",
"nodemon": "^3.1.0",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-quill": "^2.0.0", "react-quill": "^2.0.0",
"react-router-dom": "^6.22.0", "react-router-dom": "^6.22.3",
"react-toastify": "^10.0.4", "react-toastify": "^10.0.5",
"socket.io-client": "^4.7.4", "socket.io-client": "^4.7.5",
"vite": "^5.1.1" "tsc-alias": "^1.8.8",
"tsx": "^4.7.2",
"typescript": "^5.4.4",
"vite": "^5.2.8",
"vite-tsconfig-paths": "^4.3.2"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.529.1", "@aws-sdk/client-s3": "^3.550.0",
"@aws-sdk/s3-request-presigner": "^3.529.1", "@aws-sdk/s3-request-presigner": "^3.550.0",
"@kubernetes/client-node": "^0.20.0", "@kubernetes/client-node": "^0.20.0",
"basic-ftp": "^5.0.4", "basic-ftp": "^5.0.5",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"chalk": "^5.3.0", "chalk": "^5.3.0",
"express": "^4.18.2", "express": "^4.19.2",
"express-bearer-token": "^2.4.0", "express-bearer-token": "^2.4.0",
"figlet": "^1.7.0", "figlet": "^1.7.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"moment": "^2.30.1", "moment": "^2.30.1",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1", "multer-s3": "^3.0.1",
"pg-promise": "^11.5.4", "pg-promise": "^11.6.0",
"postgres-migrations": "^5.3.0", "postgres-migrations": "^5.3.0",
"rcon-client": "^4.2.4", "rcon-client": "^4.2.4",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"socket.io": "^4.7.4", "socket.io": "^4.7.5",
"uuid": "^9.0.1" "uuid": "^9.0.1"
},
"nodemonConfig": {
"watch": [
"lib"
],
"ext": "ts",
"execMap": {
"ts": "tsx --tsconfig tsconfig.server.json"
}
} }
} }

View file

@ -1,6 +1,6 @@
// @ts-nocheck // @ts-nocheck
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import MCL from "./MCL.jsx"; import MCL from "./MCL.tsx";
const appRoot = document.getElementById("mcl"); const appRoot = document.getElementById("mcl");
const root = createRoot(appRoot); const root = createRoot(appRoot);

View file

@ -1,10 +1,10 @@
// @ts-nocheck // @ts-nocheck
// Imports // Imports
import { ThemeProvider } from "@mui/material/styles"; import { ThemeProvider } from "@mui/material/styles";
import mclTheme from "./util/theme.js"; import mclTheme from "./util/theme.ts";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { SettingsProvider } from "@mcl/settings"; import { SettingsProvider } from "@mcl/settings";
import Viewport from "./nav/Viewport.jsx"; import Viewport from "./nav/Viewport.tsx";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
// Create a query client for the app // Create a query client for the app
const queryClient = new QueryClient(); const queryClient = new QueryClient();

View file

@ -8,8 +8,8 @@ import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions"; import DialogActions from "@mui/material/DialogActions";
import Dialog from "@mui/material/Dialog"; import Dialog from "@mui/material/Dialog";
import Toolbar from "@mui/material/Toolbar"; import Toolbar from "@mui/material/Toolbar";
import TextEditor from "./TextEditor.jsx"; import TextEditor from "./TextEditor.tsx";
import { cairoAuthHeader } from "@mcl/util/auth.js"; import { uploadServerItem } from "@mcl/api/clients/files.ts";
const textFileTypes = [ const textFileTypes = [
"properties", "properties",
@ -61,11 +61,7 @@ export default function FilePreview(props) {
formData.append("file", blob, name); formData.append("file", blob, name);
formData.append("id", serverId); formData.append("id", serverId);
formData.append("path", filePath); formData.append("path", filePath);
await fetch("/api/files/upload", { await uploadServerItem(formData);
method: "POST",
body: formData,
headers: cairoAuthHeader(),
});
dialogToggle(); dialogToggle();
} }

View file

@ -21,10 +21,10 @@ import {
getServerItem, getServerItem,
moveServerItems, moveServerItems,
previewServerItem, previewServerItem,
} from "@mcl/queries"; uploadServerItem,
import { cairoAuthHeader } from "@mcl/util/auth.js"; } from "@mcl/api/clients/files";
import { supportedFileTypes } from "./FilePreview.jsx"; import { supportedFileTypes } from "./FilePreview.tsx";
export default function MineclusterFiles(props) { export default function MineclusterFiles(props) {
// Chonky configuration // Chonky configuration
@ -118,11 +118,7 @@ export default function MineclusterFiles(props) {
formData.append("id", serverId); formData.append("id", serverId);
const path = `${[...dirStack].join("/")}${filePath}`; const path = `${[...dirStack].join("/")}${filePath}`;
formData.append("path", path); formData.append("path", path);
await fetch("/api/files/upload", { await uploadServerItem(formData);
method: "POST",
body: formData,
headers: cairoAuthHeader(),
});
} }
async function downloadFiles(files) { async function downloadFiles(files) {

View file

@ -1,8 +1,8 @@
// @ts-nocheck // @ts-nocheck
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useSystemAvailable } from "@mcl/queries"; import { useSystemAvailable } from "@mcl/api/clients/misc";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import OverviewVisual from "./OverviewVisual.jsx"; import OverviewVisual from "./OverviewVisual.tsx";
export default function Overview(props) { export default function Overview(props) {
const [memory, setMemory] = useState(100); const [memory, setMemory] = useState(100);
const [cpu, setCpu] = useState(100); const [cpu, setCpu] = useState(100);

View file

@ -2,7 +2,7 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import { useVersionList } from "@mcl/queries"; import { useVersionList } from "@mcl/api/clients/misc";
export default function VersionOption(props) { export default function VersionOption(props) {
const { value, onChange } = props; const { value, onChange } = props;

View file

@ -12,7 +12,7 @@ import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Stack from "@mui/material/Stack"; import Stack from "@mui/material/Stack";
import DownloadIcon from "@mui/icons-material/Download"; import DownloadIcon from "@mui/icons-material/Download";
import { getBackupUrl, getServerBackups } from "../../util/queries"; import { getBackupUrl, getServerBackups } from "@mcl/api/clients/backups";
export function useBackupDialog(isOpen = false) { export function useBackupDialog(isOpen = false) {
const [open, setOpen] = useState(isOpen); const [open, setOpen] = useState(isOpen);

View file

@ -8,7 +8,7 @@ import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions"; import DialogActions from "@mui/material/DialogActions";
import Dialog from "@mui/material/Dialog"; import Dialog from "@mui/material/Dialog";
import Toolbar from "@mui/material/Toolbar"; import Toolbar from "@mui/material/Toolbar";
import RconView from "./RconView.jsx"; import RconView from "./RconView.tsx";
export function useRconDialog(isOpen = false) { export function useRconDialog(isOpen = false) {
const [open, setOpen] = useState(isOpen); const [open, setOpen] = useState(isOpen);

View file

@ -5,7 +5,7 @@ import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Skeleton from "@mui/material/Skeleton"; import Skeleton from "@mui/material/Skeleton";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import RconSocket from "./RconSocket.js"; import RconSocket from "./RconSocket.ts";
import "@mcl/css/rcon.css"; import "@mcl/css/rcon.css";
function RconLogSkeleton() { function RconLogSkeleton() {

View file

@ -1,6 +1,5 @@
// @ts-nocheck // @ts-nocheck
import React from "react"; import React from "react";
import { useStartServer, useStopServer, useDeleteServer } from "@mcl/queries";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Card from "@mui/material/Card"; import Card from "@mui/material/Card";
import CardActions from "@mui/material/CardActions"; import CardActions from "@mui/material/CardActions";
@ -17,13 +16,14 @@ import EditIcon from "@mui/icons-material/Edit";
import FolderIcon from "@mui/icons-material/Folder"; import FolderIcon from "@mui/icons-material/Folder";
import BackupIcon from "@mui/icons-material/Backup"; import BackupIcon from "@mui/icons-material/Backup";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { useServerDelete, useServerStart, useServerStop } from "@mcl/api/clients/server";
export default function ServerCard(props) { export default function ServerCard(props) {
const { server, openRcon, openBackups } = props; const { server, openRcon, openBackups } = props;
const { name, id, metrics, ftpAvailable, serverAvailable, services } = server; const { name, id, metrics, ftpAvailable, serverAvailable, services } = server;
const startServer = useStartServer(id); const startServer = useServerStart(id);
const stopServer = useStopServer(id); const stopServer = useServerStop(id);
const deleteServer = useDeleteServer(id); const deleteServer = useServerDelete(id);
function toggleRcon() { function toggleRcon() {
if (!services.includes("server")) return; if (!services.includes("server")) return;
openRcon(); openRcon();

View file

@ -4,7 +4,7 @@ import { useState } from "react";
import { Link, useLocation } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
// Internal Imports // Internal Imports
import pages from "./MCLPages.jsx"; import pages from "./MCLPages.tsx";
// Materialui // Materialui
import AppBar from "@mui/material/AppBar"; import AppBar from "@mui/material/AppBar";

View file

@ -1,8 +1,8 @@
// @ts-nocheck // @ts-nocheck
import Home from "@mcl/pages/Home.jsx"; import Home from "@mcl/pages/Home.tsx";
import Create from "@mcl/pages/Create.jsx"; import Create from "@mcl/pages/Create.tsx";
import Files from "@mcl/pages/Files.jsx"; import Files from "@mcl/pages/Files.tsx";
import Edit from "@mcl/pages/Edit.jsx"; import Edit from "@mcl/pages/Edit.tsx";
// Go To https://mui.com/material-ui/material-icons/ for more! // Go To https://mui.com/material-ui/material-icons/ for more!
import HomeIcon from "@mui/icons-material/Home"; import HomeIcon from "@mui/icons-material/Home";
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";

View file

@ -1,7 +1,7 @@
// @ts-nocheck // @ts-nocheck
// Import React // Import React
import { Routes, Route, Navigate } from "react-router-dom"; import { Routes, Route, Navigate } from "react-router-dom";
import pages from "./MCLPages.jsx"; import pages from "./MCLPages.tsx";
const defaultPage = pages[0].path; const defaultPage = pages[0].path;

View file

@ -1,11 +1,11 @@
// @ts-nocheck // @ts-nocheck
import Toolbar from "@mui/material/Toolbar"; import Toolbar from "@mui/material/Toolbar";
import MCLPortal from "./MCLPortal.jsx"; import MCLPortal from "./MCLPortal.tsx";
// Import Navbar // Import Navbar
/*import Navbar from "./Navbar.jsx";*/ /*import Navbar from "./Navbar.tsx";*/
import { useCairoAuth } from "@mcl/util/auth.js"; import { useCairoAuth } from "@mcl/api/auth";
import MCLMenu from "./MCLMenu.jsx"; import MCLMenu from "./MCLMenu.tsx";
import Auth from "@mcl/pages/Auth.jsx"; import Auth from "@mcl/pages/Auth.tsx";
export default function Views() { export default function Views() {
const auth = useCairoAuth(); const auth = useCairoAuth();

View file

@ -1,6 +1,6 @@
// @ts-nocheck // @ts-nocheck
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import CreateCoreOptions from "./CreateCoreOptions.jsx"; import CreateCoreOptions from "./CreateCoreOptions.tsx";
export default function Create() { export default function Create() {
return ( return (
<Box className="create"> <Box className="create">

View file

@ -7,31 +7,31 @@ import FormControl from "@mui/material/FormControl";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { useCreateServer } from "@mcl/queries"; import { useServerCreate } from "@mcl/api/clients/server";
// Core Options // Core Options
import NameOption from "@mcl/components/server-options/NameOption.jsx"; import NameOption from "@mcl/components/server-options/NameOption.tsx";
import HostOption from "@mcl/components/server-options/HostOption.jsx"; import HostOption from "@mcl/components/server-options/HostOption.tsx";
import VersionOption from "@mcl/components/server-options/VersionOption.jsx"; import VersionOption from "@mcl/components/server-options/VersionOption.tsx";
import ServerTypeOption, { import ServerTypeOption, {
serverTypeOptions, serverTypeOptions,
} from "@mcl/components/server-options/ServerTypeOption.jsx"; } from "@mcl/components/server-options/ServerTypeOption.tsx";
import CpuOption, { import CpuOption, {
cpuOptions, cpuOptions,
} from "@mcl/components/server-options/CpuOption.jsx"; } from "@mcl/components/server-options/CpuOption.tsx";
import MemoryOption, { import MemoryOption, {
memoryOptions, memoryOptions,
} from "@mcl/components/server-options/MemoryOption.jsx"; } from "@mcl/components/server-options/MemoryOption.tsx";
import ExtraPortsOption from "@mcl/components/server-options/ExtraPortsOption.jsx"; import ExtraPortsOption from "@mcl/components/server-options/ExtraPortsOption.tsx";
import StorageOption from "@mcl/components/server-options/StorageOption.jsx"; import StorageOption from "@mcl/components/server-options/StorageOption.tsx";
import BackupHostOption from "@mcl/components/server-options/BackupHostOption.jsx"; import BackupHostOption from "@mcl/components/server-options/BackupHostOption.tsx";
import BackupBucketOption from "@mcl/components/server-options/BackupBucketOption.jsx"; import BackupBucketOption from "@mcl/components/server-options/BackupBucketOption.tsx";
import BackupIdOption from "@mcl/components/server-options/BackupIdOption.jsx"; import BackupIdOption from "@mcl/components/server-options/BackupIdOption.tsx";
import BackupKeyOption from "@mcl/components/server-options/BackupKeyOption.jsx"; import BackupKeyOption from "@mcl/components/server-options/BackupKeyOption.tsx";
import BackupIntervalOption, { import BackupIntervalOption, {
backupIntervalDefault, backupIntervalDefault,
} from "@mcl/components/server-options/BackupIntervalOption.jsx"; } from "@mcl/components/server-options/BackupIntervalOption.tsx";
const defaultServer = { const defaultServer = {
version: "latest", version: "latest",
@ -46,7 +46,7 @@ export default function CreateCoreOptions() {
const [backupEnabled, setBackupEnabled] = useState(false); const [backupEnabled, setBackupEnabled] = useState(false);
const [spec, setSpec] = useState(defaultServer); const [spec, setSpec] = useState(defaultServer);
const nav = useNavigate(); const nav = useNavigate();
const createServer = useCreateServer(spec); const createServer = useServerCreate(spec);
const updateSpec = (attr, val) => { const updateSpec = (attr, val) => {
const s = { ...spec }; const s = { ...spec };

View file

@ -10,7 +10,7 @@ import Select from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import InputLabel from "@mui/material/InputLabel"; import InputLabel from "@mui/material/InputLabel";
import FormControl from "@mui/material/FormControl"; import FormControl from "@mui/material/FormControl";
import { useCreateServer, useVersionList } from "@mcl/queries"; import { useCreateServer, useVersionList } from "@mcl/api/clients/server";
const defaultServer = { const defaultServer = {
version: "latest", version: "latest",

View file

@ -1,7 +1,7 @@
// @ts-nocheck // @ts-nocheck
import { useSearchParams, useNavigate } from "react-router-dom"; import { useSearchParams, useNavigate } from "react-router-dom";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import EditCoreOptions from "./EditCoreOptions.jsx"; import EditCoreOptions from "./EditCoreOptions.tsx";
export default function Edit() { export default function Edit() {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const currentServer = searchParams.get("server"); const currentServer = searchParams.get("server");

View file

@ -7,37 +7,37 @@ import FormControl from "@mui/material/FormControl";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { useGetServer, useModifyServer } from "@mcl/queries"; import { useBlueprint, useServerModify } from "@mcl/api/clients/server";
// Core Options // Core Options
import NameOption from "@mcl/components/server-options/NameOption.jsx"; import NameOption from "@mcl/components/server-options/NameOption.tsx";
import HostOption from "@mcl/components/server-options/HostOption.jsx"; import HostOption from "@mcl/components/server-options/HostOption.tsx";
import VersionOption from "@mcl/components/server-options/VersionOption.jsx"; import VersionOption from "@mcl/components/server-options/VersionOption.tsx";
import ServerTypeOption, { import ServerTypeOption, {
serverTypeOptions, serverTypeOptions,
} from "@mcl/components/server-options/ServerTypeOption.jsx"; } from "@mcl/components/server-options/ServerTypeOption.tsx";
import CpuOption, { import CpuOption, {
cpuOptions, cpuOptions,
} from "@mcl/components/server-options/CpuOption.jsx"; } from "@mcl/components/server-options/CpuOption.tsx";
import MemoryOption, { import MemoryOption, {
memoryOptions, memoryOptions,
} from "@mcl/components/server-options/MemoryOption.jsx"; } from "@mcl/components/server-options/MemoryOption.tsx";
import ExtraPortsOption from "@mcl/components/server-options/ExtraPortsOption.jsx"; import ExtraPortsOption from "@mcl/components/server-options/ExtraPortsOption.tsx";
import BackupHostOption from "@mcl/components/server-options/BackupHostOption.jsx"; import BackupHostOption from "@mcl/components/server-options/BackupHostOption.tsx";
import BackupBucketOption from "@mcl/components/server-options/BackupBucketOption.jsx"; import BackupBucketOption from "@mcl/components/server-options/BackupBucketOption.tsx";
import BackupIdOption from "@mcl/components/server-options/BackupIdOption.jsx"; import BackupIdOption from "@mcl/components/server-options/BackupIdOption.tsx";
import BackupKeyOption from "@mcl/components/server-options/BackupKeyOption.jsx"; import BackupKeyOption from "@mcl/components/server-options/BackupKeyOption.tsx";
import BackupIntervalOption, { import BackupIntervalOption, {
backupIntervalDefault, backupIntervalDefault,
} from "@mcl/components/server-options/BackupIntervalOption.jsx"; } from "@mcl/components/server-options/BackupIntervalOption.tsx";
export default function EditCoreOptions(props) { export default function EditCoreOptions(props) {
const { serverId } = props; const { serverId } = props;
const [spec, setSpec] = useState(); const [spec, setSpec] = useState();
const modifyServer = useModifyServer(spec); const modifyServer = useServerModify(spec);
const nav = useNavigate(); const nav = useNavigate();
const { isLoading, data: serverBlueprint } = useGetServer(serverId); const { isLoading, data: serverBlueprint } = useBlueprint(serverId);
useEffect(() => setSpec(serverBlueprint), [serverBlueprint]); useEffect(() => setSpec(serverBlueprint), [serverBlueprint]);

View file

@ -4,8 +4,8 @@ import { useSearchParams, useNavigate } from "react-router-dom";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import FilePreview, { import FilePreview, {
useFilePreview, useFilePreview,
} from "@mcl/components/files/FilePreview.jsx"; } from "@mcl/components/files/FilePreview.tsx";
import MineclusterFiles from "@mcl/components/files/MineclusterFiles.jsx"; import MineclusterFiles from "@mcl/components/files/MineclusterFiles.tsx";
export default function Files() { export default function Files() {
const [open, dialogToggle] = useFilePreview(); const [open, dialogToggle] = useFilePreview();

View file

@ -3,16 +3,16 @@ import { Link } from "react-router-dom";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import ServerCard from "@mcl/components/servers/ServerCard.jsx"; import ServerCard from "@mcl/components/servers/ServerCard.tsx";
import RconDialog, { import RconDialog, {
useRconDialog, useRconDialog,
} from "@mcl/components/servers/RconDialog.jsx"; } from "@mcl/components/servers/RconDialog.tsx";
import Overview from "@mcl/components/overview/Overview.jsx"; import Overview from "@mcl/components/overview/Overview.tsx";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import SpeedDialIcon from "@mui/material/SpeedDialIcon"; import SpeedDialIcon from "@mui/material/SpeedDialIcon";
import "@mcl/css/server-card.css"; import "@mcl/css/server-card.css";
import "@mcl/css/overview.css"; import "@mcl/css/overview.css";
import { useServerInstances } from "@mcl/queries"; import { useServerInstances } from "@mcl/api/clients/server";
import BackupDialog, { import BackupDialog, {
useBackupDialog, useBackupDialog,
} from "../components/servers/BackupsDialog"; } from "../components/servers/BackupsDialog";

View file

@ -0,0 +1,15 @@
import { mclAuthenticatedApiRequest } from "@mcl/api/requests";
export const getServerBackups = (serverId: string) =>
mclAuthenticatedApiRequest({
subPath: "/s3/backups",
json: { id: serverId },
jsonify: true,
});
export const getBackupUrl = (serverId: string, backupPath: string) =>
mclAuthenticatedApiRequest({
subPath: "/s3/backup-url",
json: { id: serverId, backupPath },
jsonify: true,
});

View file

@ -0,0 +1,68 @@
import { mclAuthenticatedApiRequest } from "@mcl/api/requests";
// ===== API Requests =====
export const getServerFiles = (serverId: string, path: string) =>
mclAuthenticatedApiRequest({
subPath: "/files/list",
json: { id: serverId, path },
});
export const createServerFolder = (serverId: string, path: string) =>
mclAuthenticatedApiRequest({
subPath: "/files/folder",
json: { id: serverId, path },
});
export const uploadServerItem = (body:FormData) =>
mclAuthenticatedApiRequest({
subPath: "/files/upload",
body,
})
export const deleteServerItem = (
serverId: string,
path: string,
isDir: boolean,
) =>
mclAuthenticatedApiRequest({
subPath: "/files/item",
json: { id: serverId, path, isDir },
method: "DELETE",
});
export const moveServerItems = (
serverId: string,
files: string,
destination: string,
origin: string,
) =>
mclAuthenticatedApiRequest({
subPath: "/files/move",
json: { id: serverId, files, destination, origin },
});
export const previewServerItem = (serverId: string, path: string) =>
mclAuthenticatedApiRequest({
subPath: "/files/item",
json: { id: serverId, path },
}).then((res) => res.blob());
export const getServerItem = (serverId: string, name: string, path: string) =>
mclAuthenticatedApiRequest({
subPath: "/files/item",
json: { id: serverId, path },
})
.then((res) => res.blob())
.then((blob) => downloadBlob(blob, name));
async function downloadBlob(blob: Blob, name: string) {
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);
}

View file

@ -0,0 +1,27 @@
import { useQuery } from "@tanstack/react-query";
import { mclAuthenticatedApiRequest } from "../requests";
// ===== API Requests =====
export const getSystemAvailable = () =>
mclAuthenticatedApiRequest({
subPath: "/system/available",
jsonify: true,
});
// ===== API Queries =====
export const useSystemAvailable = () =>
useQuery({
queryKey: ["system-available"],
queryFn: getSystemAvailable,
});
export const useVersionList = () =>
useQuery({
queryKey: ["minecraft-versions"],
queryFn: () =>
fetch(
"https://piston-meta.mojang.com/mc/game/version_manifest.json",
).then((r) => r.json()),
});

View file

@ -0,0 +1,122 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { mclAuthenticatedApiRequest, useMutator } from "../requests";
// ===== API Utils =====
const useInstanceControl = (apiRequest: (args: any) => Promise<any>) =>
useMutator(apiRequest, ["server-instances"]);
const useListControl = (apiRequest: (args: any) => Promise<any>) =>
useMutator(apiRequest, ["server-list"]);
// ===== API Requests =====
export const getServerList = () =>
mclAuthenticatedApiRequest({
subPath: "/server/path",
jsonify: true,
});
export const getServerInstances = () =>
mclAuthenticatedApiRequest({
subPath: "/server/instances",
jsonify: true,
});
export const getServerMetrics = (serverId: string) =>
mclAuthenticatedApiRequest({
subPath: "/server/metrics",
json: { id: serverId },
jsonify: true,
});
export const getServerStatus = (serverId: string) =>
mclAuthenticatedApiRequest({
subPath: "/server/status",
json: { id: serverId },
jsonify: true,
});
export const requestServerStart = (serverId: string) =>
mclAuthenticatedApiRequest({
subPath: "/server/start",
json: { id: serverId },
});
export const requestServerStop = (serverId: string) =>
mclAuthenticatedApiRequest({
subPath: "/server/stop",
json: { id: serverId },
});
export const requestServerDelete = (serverId: string) =>
mclAuthenticatedApiRequest({
subPath: "/server/delete",
json: { id: serverId },
method: "DELETE",
});
export const requestServerCreate = (serverSpec: any) =>
mclAuthenticatedApiRequest({
subPath: "/server/create",
json: serverSpec,
});
export const requestServerModify = (serverSpec: any) =>
mclAuthenticatedApiRequest({
subPath: "/server/modify",
json: serverSpec,
});
export const getServerBlueprint = (serverId: string) =>
mclAuthenticatedApiRequest({
subPath: "/server/blueprint",
json: { id: serverId },
jsonify: true
})
// ===== API Queries =====
export const useServerList = () =>
useQuery({ queryKey: ["server-list"], queryFn: getServerList });
export const useServerInstances = () =>
useQuery({
queryKey: ["server-instances"],
queryFn: getServerInstances,
refetchInterval: 5000,
});
export const useServerMetrics = (serverId: string) =>
useQuery({
queryKey: [`server-metrics-${serverId}`],
queryFn: () => getServerMetrics(serverId),
refetchInterval: 10000,
});
export const useServerStatus = (serverId: string) =>
useQuery({
queryKey: [`server-status-${serverId}`],
queryFn: () => getServerStatus(serverId),
});
export const useServerStart = (serverId: string) =>
useInstanceControl(() => requestServerStart(serverId));
export const useServerStop = (serverId: string) =>
useInstanceControl(() => requestServerStop(serverId));
export const useServerDelete = (serverId: string) =>
useInstanceControl(() => requestServerDelete(serverId));
export const useServerCreate = (spec: any) =>
useListControl(() => requestServerCreate(spec));
export const useServerModify = (spec: any) =>
useListControl(() => requestServerModify(spec));
export const useBlueprint = (serverId: string) =>
useQuery({
queryKey: [`server-blueprint-${serverId}`],
queryFn: ()=>getServerBlueprint(serverId),
})

56
src/util/api/requests.ts Normal file
View file

@ -0,0 +1,56 @@
import {
useMutation,
useQueryClient,
} from "@tanstack/react-query";
import { cairoAuthHeader } from "./auth";
declare interface ApiRequestArgs {
subPath: string;
body?: any | undefined;
json?: object | undefined;
method?: string;
jsonify?: boolean;
extraHeaders?: object;
handleErrors?: boolean;
}
async function apiRequest(apiRequestArgs: ApiRequestArgs) {
const { subPath, json, body, method, jsonify, extraHeaders, handleErrors } =
apiRequestArgs;
const headers = extraHeaders ?? {};
const requestMethod = method ?? !!json ? "POST" : "GET";
const requestBody = body ?? !!json ? JSON.stringify(json) : undefined;
if (!!json) headers["Content-Type"] = "application/json";
return fetch(`/api${subPath}`, {
method: requestMethod,
body: requestBody
}).then((res) => {
if (res.status >= 300 && handleErrors) throw Error(res.statusText);
if (jsonify) return res.json();
return res;
});
}
export async function mclAuthenticatedApiRequest(
apiRequestArgs: ApiRequestArgs,
) {
const extraHeaders = apiRequestArgs.extraHeaders ?? {};
const authHeaders = cairoAuthHeader();
apiRequestArgs.extraHeaders = { ...extraHeaders, ...authHeaders };
return apiRequest(apiRequestArgs);
}
export async function mclApiRequest(apiRequestArgs: ApiRequestArgs) {
return apiRequest(apiRequestArgs);
}
export const useMutator = async (
apiRequest: (args: any) => Promise<any>,
invalidate: string[],
) => {
const qc = useQueryClient();
useMutation({
mutationFn: apiRequest,
onSuccess: () => qc.invalidateQueries({ queryKey: invalidate }),
});
};

View file

@ -1,145 +0,0 @@
// @ts-nocheck
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { cairoAuthHeader } from "@mcl/util/auth.js";
const fetchApi = (subPath) => async () =>
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 = (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({ queryKey: ["server-list"], queryFn: fetchApi("/server/list") });
export const useServerInstances = () =>
useQuery({
queryKey: ["server-instances"],
queryFn: fetchApi("/server/instances"),
refetchInterval: 5000,
});
export const useSystemAvailable = () =>
useQuery({
queryKey: ["system-available"],
queryFn: fetchApi("/system/available"),
});
export const useVersionList = () =>
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();
return async () => {
const res = await fetch(`/api${subPath}`, {
method,
headers: {
"Content-Type": "application/json",
...cairoAuthHeader(),
},
body: JSON.stringify(body),
});
if (invalidate) qc.invalidateQueries([invalidate]);
};
};

39
tsconfig.json Normal file
View file

@ -0,0 +1,39 @@
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"target": "ESNext",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
/* Vite Compile Options */
"allowImportingTsExtensions": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"paths": {
/* Common */
"@mcl/enums/*": ["./lib/types/enums/*"],
"@mcl/types/*": ["./lib/types/*"],
"@mcl/logging": ["./lib/util/logging.ts"],
/* Vite */
"@mcl/settings": ["./src/ctx/SettingsContext.tsx"],
"@mcl/api/*": ["./src/util/api/*"],
"@mcl/src/util/*": ["./src/util/*"],
"@mcl/pages/*": ["./src/pages/*"],
"@mcl/components/*": ["./src/components/*"],
"@mcl/css/*": ["./src/css/*"],
/* Server */
"@mcl/db/*": ["./lib/database/*"],
"@mcl/controllers/*": ["./lib/controllers/*"],
"@mcl/svc/*": ["./lib/services/*"],
"@mcl/ClientErrors": ["./lib/router/ClientErrors.ts"]
}
},
"exclude": ["node_modules"],
"tsc-alias": {
"resolveFullPaths": true,
"verbose": false
}
}

19
tsconfig.server.json Normal file
View file

@ -0,0 +1,19 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "bundler",
"allowJs": true,
"esModuleInterop": true,
"allowImportingTsExtensions": false,
"isolatedModules": true,
"noEmit": false,
"resolveJsonModule": true,
"outDir": "./build/server",
"strict": true,
"strictPropertyInitialization": false
},
"include": ["lib", "lib/**/*.ts"],
"exclude": ["src", "node_modules", "old-updater"]
}

View file

@ -1,6 +1,6 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import path from "node:path"; import tsconfigPaths from "vite-tsconfig-paths";
const { MCL_VITE_BACKEND_URL, MCL_VITE_DEV_PORT } = process.env; const { MCL_VITE_BACKEND_URL, MCL_VITE_DEV_PORT } = process.env;
const backendUrl = MCL_VITE_BACKEND_URL ?? "http://localhost:52000"; const backendUrl = MCL_VITE_BACKEND_URL ?? "http://localhost:52000";
@ -8,10 +8,10 @@ const vitePort = MCL_VITE_DEV_PORT ?? 52025;
export default () => { export default () => {
return defineConfig({ return defineConfig({
plugins: [react()], plugins: [react(), tsconfigPaths()],
server: { server: {
host: "0.0.0.0", host: "0.0.0.0",
port: vitePort, port: Number(vitePort),
proxy: { proxy: {
"/api": backendUrl, "/api": backendUrl,
"/socket.io": backendUrl, "/socket.io": backendUrl,
@ -22,18 +22,8 @@ export default () => {
}, },
}, },
build: { build: {
outDir: "./build", outDir: "./build/vite",
}, },
base: "/mcl/", base: "/mcl/",
resolve: {
alias: {
"@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"),
},
},
}); });
}; };