[FEATURE] Integrated Minecluster with Cairo & Established Gitea workflows (#12)
Co-authored-by: Dunemask <dunemask@gmail.com> Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/12
This commit is contained in:
parent
edbfc2348a
commit
78c5b72482
30 changed files with 391 additions and 53 deletions
|
@ -7,8 +7,8 @@ import DialogContent from "@mui/material/DialogContent";
|
|||
import DialogActions from "@mui/material/DialogActions";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
|
||||
import TextEditor from "./TextEditor.jsx";
|
||||
import { cairoAuthHeader } from "@mcl/util/auth.js";
|
||||
|
||||
const textFileTypes = ["properties", "txt", "yaml", "yml", "json", "env"];
|
||||
const imageFileTypes = ["png", "jpeg", "jpg"];
|
||||
|
@ -52,6 +52,7 @@ export default function FilePreview(props) {
|
|||
await fetch("/api/files/upload", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: cairoAuthHeader(),
|
||||
});
|
||||
dialogToggle();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
getServerItem,
|
||||
} from "@mcl/queries";
|
||||
import { previewServerItem } from "../../util/queries";
|
||||
import { cairoAuthHeader } from "@mcl/util/auth.js";
|
||||
|
||||
import { supportedFileTypes } from "./FilePreview.jsx";
|
||||
|
||||
|
@ -109,6 +110,7 @@ export default function MineclusterFiles(props) {
|
|||
await fetch("/api/files/upload", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: cairoAuthHeader(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ export default class RconSocket {
|
|||
this.sk.on("rcon-error", this.onRconError.bind(this));
|
||||
this.sk.on("error", () => console.log("WHOOSPSIE I GUESS?"));
|
||||
this.rconLive = false;
|
||||
this.rconError = false;
|
||||
}
|
||||
|
||||
onPush(p) {
|
||||
|
@ -22,6 +23,7 @@ export default class RconSocket {
|
|||
|
||||
onRconError(v) {
|
||||
this.rconLive = false;
|
||||
this.rconError = true;
|
||||
console.log("Server sent: ", v);
|
||||
}
|
||||
|
||||
|
|
|
@ -55,10 +55,12 @@ export default function RconView(props) {
|
|||
variant="outlined"
|
||||
value={cmd}
|
||||
onChange={updateCmd}
|
||||
disabled={!(rcon && rcon.rconLive)}
|
||||
disabled={!(rcon && rcon.rconLive && !rcon.rconError)}
|
||||
/>
|
||||
{rcon && rcon.rconLive && <Button onClick={sendCommand}>Send</Button>}
|
||||
{!(rcon && rcon.rconLive) && (
|
||||
{rcon && rcon.rconLive && !rcon.rconError && (
|
||||
<Button onClick={sendCommand}>Send</Button>
|
||||
)}
|
||||
{!(rcon && rcon.rconLive && !rcon.rconError) && (
|
||||
<Button color="secondary">Not Connected</Button>
|
||||
)}
|
||||
</Box>
|
||||
|
|
|
@ -10,6 +10,7 @@ const defaultSettings = {
|
|||
simplifiedControls: false,
|
||||
logAppDetails: true,
|
||||
defaultPage: "home",
|
||||
cairoAuth: null,
|
||||
};
|
||||
|
||||
const settings = localSettings ? JSON.parse(localSettings) : defaultSettings;
|
||||
|
@ -27,6 +28,7 @@ const settingsUpdater = (oldState, settingsUpdate) => {
|
|||
if (settingsUpdate[k] === undefined) continue;
|
||||
settingsToUpdate[k] = settingsUpdate[k];
|
||||
}
|
||||
console.log("SAVING", settingsToUpdate);
|
||||
localStorage.setItem("settings", JSON.stringify(settingsToUpdate));
|
||||
};
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import Box from "@mui/material/Box";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import MCLPortal from "./MCLPortal.jsx";
|
||||
import Button from "@mui/material/Button";
|
||||
import SpeedDialIcon from "@mui/material/SpeedDialIcon";
|
||||
// Import Navbar
|
||||
/*import Navbar from "./Navbar.jsx";*/
|
||||
import { useCairoAuth } from "@mcl/util/auth.js";
|
||||
import MCLMenu from "./MCLMenu.jsx";
|
||||
import Auth from "@mcl/pages/Auth.jsx";
|
||||
|
||||
export default function Views() {
|
||||
const auth = useCairoAuth();
|
||||
if (!auth) return <Auth />;
|
||||
return (
|
||||
<div className="view">
|
||||
<MCLMenu />
|
||||
|
|
63
src/pages/Auth.jsx
Normal file
63
src/pages/Auth.jsx
Normal file
|
@ -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 (
|
||||
<Box
|
||||
className="auth"
|
||||
sx={{
|
||||
height: "100%",
|
||||
backgroundColor: (theme) => theme.palette.primary.main,
|
||||
}}
|
||||
>
|
||||
<Box className="auth-display" sx={{ display: "flex", height: "95vh" }}>
|
||||
<Box
|
||||
sx={{
|
||||
height: "50%",
|
||||
width: "50%",
|
||||
m: "auto",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "white",
|
||||
display: "inline-flex",
|
||||
m: "auto",
|
||||
borderRadius: "8px",
|
||||
height: "5rem",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
onClick={cairoLogin}
|
||||
sx={{ p: "1.5rem" }}
|
||||
endIcon={
|
||||
<img
|
||||
src="https://cairo.dunemask.net/cairo/icons/apple-touch-icon-120x120.png"
|
||||
width="48px"
|
||||
style={{ borderRadius: "4px" }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
Login with Cairo
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
40
src/util/auth.js
Normal file
40
src/util/auth.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { useState, useContext, useEffect } from "react";
|
||||
import { useSearchParams, useNavigate } from "react-router-dom";
|
||||
import SettingsContext from "@mcl/settings";
|
||||
|
||||
const verifyAuth = (authToken) =>
|
||||
fetch("/api/auth/verify", {
|
||||
headers: { Authorization: `Bearer ${authToken}` },
|
||||
})
|
||||
.then((res) => res.status === 200)
|
||||
.catch(() => false);
|
||||
|
||||
export function useCairoAuth() {
|
||||
const { state: settings, updateSettings } = useContext(SettingsContext);
|
||||
const [auth, setAuth] = useState(!!settings.cairoAuth);
|
||||
const [searchParams] = useSearchParams();
|
||||
const nav = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const webToken = searchParams.get("cairoAuthToken");
|
||||
if (!webToken) return;
|
||||
verifyAuth(webToken).then(setAuth);
|
||||
updateSettings({ cairoAuth: webToken });
|
||||
nav("/");
|
||||
}, [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
verifyAuth(settings.cairoAuth).then(setAuth);
|
||||
nav("/");
|
||||
}, [settings.cairoAuth]);
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
export function getAuthTokenFromStorage() {
|
||||
return JSON.parse(localStorage.getItem("settings")).cairoAuth;
|
||||
}
|
||||
|
||||
export function cairoAuthHeader() {
|
||||
return { Authorization: `Bearer ${getAuthTokenFromStorage()}` };
|
||||
}
|
|
@ -1,12 +1,17 @@
|
|||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { cairoAuthHeader } from "@mcl/util/auth.js";
|
||||
|
||||
const fetchApi = (subPath) => async () =>
|
||||
fetch(`/api${subPath}`).then((res) => res.json());
|
||||
fetch(`/api${subPath}`, { headers: cairoAuthHeader() }).then((res) =>
|
||||
res.json(),
|
||||
);
|
||||
|
||||
const fetchApiCore = async (subPath, json, method = "POST", jsonify = false) =>
|
||||
fetch(`/api${subPath}`, {
|
||||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...cairoAuthHeader(),
|
||||
},
|
||||
body: JSON.stringify(json),
|
||||
}).then((res) => (jsonify ? res.json() : res));
|
||||
|
@ -16,6 +21,7 @@ const fetchApiPost = (subPath, json) => async () =>
|
|||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...cairoAuthHeader(),
|
||||
},
|
||||
body: JSON.stringify(json),
|
||||
}).then((res) => res.json());
|
||||
|
@ -117,6 +123,7 @@ const postJsonApi = (subPath, body, invalidate, method = "POST") => {
|
|||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...cairoAuthHeader(),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue