[FEATURE] Backups View & Style fix (#22)

Co-authored-by: Dunemask <dunemask@gmail.com>
Reviewed-on: https://gitea.dunemask.dev/elysium/minecluster/pulls/22
This commit is contained in:
dunemask 2024-03-12 01:58:25 +00:00
parent 332f84972c
commit 6efa50e86b
10 changed files with 969 additions and 931 deletions

View file

@ -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);
}
}

11
lib/routes/s3-route.js Normal file
View file

@ -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;

View file

@ -8,6 +8,7 @@ import systemRoute from "../routes/system-route.js";
import serverRoute from "../routes/server-route.js";
import filesRoute from "../routes/files-route.js";
import reactRoute from "../routes/react-route.js";
import s3Route from "../routes/s3-route.js";
import {
logErrors,
clientErrorHandler,
@ -27,6 +28,7 @@ export default function buildRoutes(pg, skio) {
router.use("/api/system", systemRoute);
router.use("/api/server", serverRoute);
router.use("/api/files", filesRoute);
router.use("/api/s3", s3Route);
router.use(["/mcl", "/mcl/*"], reactRoute); // Static Build Route
/*router.use(logErrors);
router.use(clientErrorHandler);