Compare commits
6 commits
master
...
ep/Mar29-2
Author | SHA1 | Date | |
---|---|---|---|
5a43691d83 | |||
813295c857 | |||
8c15dd6752 | |||
e1fe6c2f3b | |||
4a6f20fa7b | |||
4d5b347767 |
95 changed files with 4515 additions and 2096 deletions
|
@ -1,31 +0,0 @@
|
|||
# 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"
|
|
@ -1,42 +0,0 @@
|
|||
# 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}}`"
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
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 }}."
|
31
.gitea/workflows/deploy-edge-proxy.yml
Normal file
31
.gitea/workflows/deploy-edge-proxy.yml
Normal file
|
@ -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"
|
|
@ -1,8 +1,8 @@
|
|||
name: Deploy USW-MC
|
||||
run-name: ${{ forgejo.actor }} Deploy USW-MC
|
||||
run-name: ${{ gitea.actor }} Deploy USW-MC
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
GARDEN_DEPLOY_ACTION: minecluster
|
||||
|
@ -10,35 +10,34 @@ env:
|
|||
jobs:
|
||||
deploy-edge:
|
||||
steps:
|
||||
# Configure proper kubeconfig (Used when cluster does not match the edge environment)
|
||||
# Configure proper kubeconfig
|
||||
- name: Get usw-mc deployment kubeconfig
|
||||
uses: https://forgejo.dunemask.dev/elysium/elysium-actions@infisical-env
|
||||
uses: https://gitea.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
|
||||
secret-paths: /kubernetes
|
||||
# Setup Oasis
|
||||
- name: Oasis Setup
|
||||
uses: https://forgejo.dunemask.dev/elysium/elysium-actions@oasis-setup-auto
|
||||
uses: https://gitea.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-paths: /alexandria
|
||||
extra-secret-envs: edge
|
||||
kubeconfig: ${{ env.KUBERNETES_CONFIG_USW_MC }}
|
||||
# Deploy to Edge
|
||||
- name: Deploy to Edge env
|
||||
run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-edge
|
||||
run: garden deploy $GARDEN_DEPLOY_ACTION --force --force-build --env usw-mc
|
||||
working-directory: ${{ env.OASIS_WORKSPACE }}
|
||||
env: # (Used when cluster does not match the edge environment)
|
||||
MCL_KUBECONFIG: ${{ env.KUBERNETES_CONFIG_USW_MC }}
|
||||
env:
|
||||
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
|
||||
uses: https://gitea.dunemask.dev/elysium/elysium-actions@discord-status
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: deployments
|
||||
header: DEPLOY MC
|
||||
additional-content: "Minecluster Server Manager Deployment"
|
||||
additional-content: "Minecluster Server Manager Deployment"
|
42
.gitea/workflows/qa-api-tests.yml
Normal file
42
.gitea/workflows/qa-api-tests.yml
Normal file
|
@ -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}}`"
|
||||
|
31
.gitea/workflows/s3-repo-backup.yml
Normal file
31
.gitea/workflows/s3-repo-backup.yml
Normal file
|
@ -0,0 +1,31 @@
|
|||
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: backups/gitea-repositories/${{ 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 }}."
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
node_modules/
|
||||
.env
|
||||
build
|
||||
dist
|
||||
|
||||
|
|
|
@ -2,3 +2,5 @@ node_modules/
|
|||
.git/
|
||||
lib/
|
||||
src/
|
||||
dist/
|
||||
build/
|
||||
|
|
12
Dockerfile
12
Dockerfile
|
@ -4,13 +4,15 @@ WORKDIR /dunemask/net/minecluster
|
|||
COPY package.json .
|
||||
COPY package-lock.json .
|
||||
RUN npm i
|
||||
# Copy react build resources over
|
||||
# Copy build resources over
|
||||
COPY public public
|
||||
COPY dist dist
|
||||
COPY src src
|
||||
COPY lib lib
|
||||
# Copy TSConfigs over
|
||||
COPY index.html .
|
||||
COPY vite.config.js .
|
||||
RUN npm run build:react
|
||||
# Copy Backend resources over
|
||||
COPY lib lib
|
||||
COPY vite.config.ts .
|
||||
COPY tsconfig.json .
|
||||
COPY tsconfig.server.json .
|
||||
RUN npm run package:full
|
||||
CMD ["npm","start"]
|
||||
|
|
11
dist/app.js
vendored
11
dist/app.js
vendored
|
@ -1,11 +0,0 @@
|
|||
import k8s from "@kubernetes/client-node";
|
||||
import Minecluster from "../lib/Minecluster.js";
|
||||
const mcl = new Minecluster();
|
||||
mcl.start();
|
||||
|
||||
async function main(){
|
||||
const kc = new k8s.KubeConfig();
|
||||
kc.loadFromDefault();
|
||||
}
|
||||
|
||||
main().catch((e)=>{console.error(e)});
|
|
@ -18,6 +18,6 @@
|
|||
<body>
|
||||
<noscript>You need to enable JavaScript to run Minecluster</noscript>
|
||||
<div id="mcl"></div>
|
||||
<script type="module" src="/src/App.jsx"></script>
|
||||
<script type="module" src="/src/App.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
// Imports
|
||||
import fig from "figlet";
|
||||
import http from "http";
|
14
lib/app.ts
Normal file
14
lib/app.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
// @ts-nocheck
|
||||
import k8s from "@kubernetes/client-node";
|
||||
import Minecluster from "./Minecluster.js";
|
||||
const mcl = new Minecluster();
|
||||
mcl.start();
|
||||
|
||||
async function main() {
|
||||
const kc = new k8s.KubeConfig();
|
||||
kc.loadFromDefault();
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import {
|
||||
createServerFolder,
|
||||
getServerItem,
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import createServerResources from "../k8s/server-create.js";
|
||||
import deleteServerResources from "../k8s/server-delete.js";
|
||||
import {
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { S3, GetObjectCommand } from "@aws-sdk/client-s3";
|
||||
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||
import { basename } from "node:path";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { getUserDeployments } from "../k8s/k8s-server-control.js";
|
||||
import { getInstances } from "../k8s/server-status.js";
|
||||
import { sendError } from "../util/ExpressClientError.js";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
// Imports
|
||||
import k8s from "@kubernetes/client-node";
|
||||
import { Rcon as RconClient } from "rcon-client";
|
|
@ -1,7 +1,7 @@
|
|||
CREATE SEQUENCE servers_id_seq;
|
||||
CREATE TABLE servers (
|
||||
id bigint NOT NULL DEFAULT nextval('servers_id_seq') PRIMARY KEY,
|
||||
owner_cairo_id varchar(63),
|
||||
owner_cairo_id bigint,
|
||||
host varchar(255) DEFAULT NULL,
|
||||
name varchar(255) DEFAULT NULL,
|
||||
version varchar(63) DEFAULT 'latest',
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
const buildPostgresEntry = (entry) => {
|
||||
const pgEntry = { ...entry };
|
||||
Object.keys(pgEntry).forEach((col) => {
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
// Imports
|
||||
import path from "node:path";
|
||||
import { URL } from "node:url";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import pg from "../postgres.js";
|
||||
import {
|
||||
deleteQuery,
|
||||
|
@ -6,6 +7,7 @@ import {
|
|||
updateWhereAllQuery,
|
||||
} from "../pg-query.js";
|
||||
import ExpressClientError from "../../util/ExpressClientError.js";
|
||||
import { VERB } from "@mcl/logging";
|
||||
const table = "servers";
|
||||
|
||||
const asExpressClientError = (e) => {
|
||||
|
@ -16,9 +18,8 @@ const getMclName = (host, id) =>
|
|||
`${host.toLowerCase().replaceAll(".", "-")}-${id}`;
|
||||
|
||||
export async function checkAuthorization(serverId, cairoId) {
|
||||
console.log(
|
||||
`Checking Authorization for user ${cairoId} for serverId ${serverId}`,
|
||||
);
|
||||
const msgLog = `Checking Authorization for user ${cairoId} for serverId ${serverId}`;
|
||||
VERB("DEBUG", msgLog);
|
||||
if (!cairoId) return false;
|
||||
const q = selectWhereAllQuery(table, {
|
||||
id: serverId,
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import k8s from "@kubernetes/client-node";
|
||||
const MCL_KUBECONFIG = process.env.MCL_KUBECONFIG;
|
||||
const envConfig = MCL_KUBECONFIG ? MCL_KUBECONFIG : null;
|
||||
|
@ -9,6 +10,4 @@ try {
|
|||
} 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;
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import k8s from "@kubernetes/client-node";
|
||||
import yaml from "js-yaml";
|
||||
import { VERB, ERR } from "../util/logging.js";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import yaml from "js-yaml";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import bcrypt from "bcrypt";
|
||||
import k8s from "@kubernetes/client-node";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import k8s from "@kubernetes/client-node";
|
||||
import { ERR } from "../util/logging.js";
|
||||
import { getServerAssets } from "./k8s-server-control.js";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import ftp from "basic-ftp";
|
||||
import { ERR } from "../util/logging.js";
|
||||
import { getServerAssets } from "./k8s-server-control.js";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import k8s from "@kubernetes/client-node";
|
||||
import {
|
||||
createExtraService,
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import k8s from "@kubernetes/client-node";
|
||||
import { getUserDeployments } from "./k8s-server-control.js";
|
||||
import { getServerEntries } from "../database/queries/server-queries.js";
|
|
@ -1,15 +1,13 @@
|
|||
// @ts-nocheck
|
||||
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}`,
|
||||
`${process.env.MCL_CAIRO_URL}/cairo/auth?redirectUri=${req.query.redirectUri}`,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
export function logErrors(err, req, res, next) {
|
||||
console.error(err.stack);
|
||||
next(err);
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { Router, json as jsonMiddleware } from "express";
|
||||
import multer from "multer";
|
||||
import {
|
|
@ -1,15 +1,16 @@
|
|||
// @ts-nocheck
|
||||
// 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 { MCL_CAIRO_URL } = 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) => {
|
||||
return fetch(`${MCL_CAIRO_URL}/api/user/info`, config).then(async (res) => {
|
||||
if (res.status >= 300) {
|
||||
const errorMessage = await res
|
||||
.json()
|
||||
|
@ -30,9 +31,9 @@ const cairoAuthHandler = (req, res, next) => {
|
|||
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;
|
||||
if (!authData.id)
|
||||
throw Error(`Cairo didn't return the expected data! ${authData.id}`);
|
||||
req.cairoId = authData.id;
|
||||
})
|
||||
.then(() => next())
|
||||
.catch((err) => {
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import express, { Router } from "express";
|
||||
import path from "path";
|
||||
const router = Router();
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { Router, json as jsonMiddleware } from "express";
|
||||
import { getS3BackupUrl, listS3Backups } from "../controllers/s3-controller.js";
|
||||
import cairoAuthMiddleware from "./middlewares/auth-middleware.js";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { Router, json as jsonMiddleware } from "express";
|
||||
import {
|
||||
createServer,
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { Router } from "express";
|
||||
import k8s from "@kubernetes/client-node";
|
||||
import { WARN } from "../util/logging.js";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { Router } from "express";
|
||||
const router = Router();
|
||||
// Get Routes
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
// Imports
|
||||
import express from "express";
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { Server as Skio } from "socket.io";
|
||||
import { VERB, WARN, ERR } from "../util/logging.js";
|
||||
import {
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { VERB } from "./logging.js";
|
||||
|
||||
export default class ExpressClientError extends Error {
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
// Imports
|
||||
import { Chalk } from "chalk";
|
||||
const { redBright, greenBright, yellowBright, cyanBright, magentaBright } =
|
5495
package-lock.json
generated
5495
package-lock.json
generated
File diff suppressed because it is too large
Load diff
68
package.json
68
package.json
|
@ -4,13 +4,18 @@
|
|||
"description": "Minecraft Server management using Kubernetes",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build:react": "vite build",
|
||||
"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: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": [
|
||||
"Minecraft",
|
||||
|
@ -22,44 +27,65 @@
|
|||
"author": "Dunemask",
|
||||
"license": "LGPL-2.1",
|
||||
"devDependencies": {
|
||||
"@emotion/react": "^11.11.3",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/icons-material": "^5.15.9",
|
||||
"@mui/material": "^5.15.9",
|
||||
"@tanstack/react-query": "^5.20.1",
|
||||
"@aperturerobotics/chonky": "^0.2.8",
|
||||
"@aperturerobotics/chonky-icon-fontawesome": "^0.2.8",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@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",
|
||||
"chonky": "^2.3.2",
|
||||
"chonky-icon-fontawesome": "^2.3.2",
|
||||
"concurrently": "^8.2.2",
|
||||
"nodemon": "^3.0.3",
|
||||
"esbuild": "^0.20.2",
|
||||
"nodemon": "^3.1.0",
|
||||
"prettier": "^3.2.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"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"
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-toastify": "^10.0.5",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"tsc-alias": "^1.8.8",
|
||||
"tsx": "^4.7.2",
|
||||
"typescript": "^5.4.4",
|
||||
"vite": "^5.2.8",
|
||||
"vite-tsconfig-paths": "^4.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.529.1",
|
||||
"@aws-sdk/s3-request-presigner": "^3.529.1",
|
||||
"@aws-sdk/client-s3": "^3.550.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.550.0",
|
||||
"@kubernetes/client-node": "^0.20.0",
|
||||
"basic-ftp": "^5.0.4",
|
||||
"basic-ftp": "^5.0.5",
|
||||
"bcrypt": "^5.1.1",
|
||||
"chalk": "^5.3.0",
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.19.2",
|
||||
"express-bearer-token": "^2.4.0",
|
||||
"figlet": "^1.7.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"moment": "^2.30.1",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"multer-s3": "^3.0.1",
|
||||
"pg-promise": "^11.5.4",
|
||||
"pg-promise": "^11.6.0",
|
||||
"postgres-migrations": "^5.3.0",
|
||||
"rcon-client": "^4.2.4",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"socket.io": "^4.7.4",
|
||||
"socket.io": "^4.7.5",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"watch": [
|
||||
"lib"
|
||||
],
|
||||
"ext": "ts",
|
||||
"execMap": {
|
||||
"ts": "tsx --tsconfig tsconfig.server.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// @ts-nocheck
|
||||
import { createRoot } from "react-dom/client";
|
||||
import MCL from "./MCL.jsx";
|
||||
import MCL from "./MCL.tsx";
|
||||
const appRoot = document.getElementById("mcl");
|
||||
const root = createRoot(appRoot);
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
// @ts-nocheck
|
||||
// Imports
|
||||
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 { SettingsProvider } from "@mcl/settings";
|
||||
import Viewport from "./nav/Viewport.jsx";
|
||||
import Viewport from "./nav/Viewport.tsx";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
// Create a query client for the app
|
||||
const queryClient = new QueryClient();
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { useState, useEffect } from "react";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
@ -7,8 +8,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";
|
||||
import TextEditor from "./TextEditor.tsx";
|
||||
import { uploadServerItem } from "@mcl/api/clients/files.ts";
|
||||
|
||||
const textFileTypes = [
|
||||
"properties",
|
||||
|
@ -60,11 +61,7 @@ export default function FilePreview(props) {
|
|||
formData.append("file", blob, name);
|
||||
formData.append("id", serverId);
|
||||
formData.append("path", filePath);
|
||||
await fetch("/api/files/upload", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: cairoAuthHeader(),
|
||||
});
|
||||
await uploadServerItem(formData);
|
||||
dialogToggle();
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { useState, useEffect, useMemo, useRef } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Dropzone from "react-dropzone";
|
||||
|
@ -10,8 +11,8 @@ import {
|
|||
FileToolbar,
|
||||
setChonkyDefaults,
|
||||
ChonkyActions,
|
||||
} from "chonky";
|
||||
import { ChonkyIconFA } from "chonky-icon-fontawesome";
|
||||
} from "@aperturerobotics/chonky";
|
||||
import { ChonkyIconFA } from "@aperturerobotics/chonky-icon-fontawesome";
|
||||
|
||||
import {
|
||||
getServerFiles,
|
||||
|
@ -20,10 +21,10 @@ import {
|
|||
getServerItem,
|
||||
moveServerItems,
|
||||
previewServerItem,
|
||||
} from "@mcl/queries";
|
||||
import { cairoAuthHeader } from "@mcl/util/auth.js";
|
||||
uploadServerItem,
|
||||
} from "@mcl/api/clients/files";
|
||||
|
||||
import { supportedFileTypes } from "./FilePreview.jsx";
|
||||
import { supportedFileTypes } from "./FilePreview.tsx";
|
||||
|
||||
export default function MineclusterFiles(props) {
|
||||
// Chonky configuration
|
||||
|
@ -117,11 +118,7 @@ export default function MineclusterFiles(props) {
|
|||
formData.append("id", serverId);
|
||||
const path = `${[...dirStack].join("/")}${filePath}`;
|
||||
formData.append("path", path);
|
||||
await fetch("/api/files/upload", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: cairoAuthHeader(),
|
||||
});
|
||||
await uploadServerItem(formData);
|
||||
}
|
||||
|
||||
async function downloadFiles(files) {
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import ReactQuill from "react-quill";
|
||||
import { useState, useEffect, useMemo, memo } from "react";
|
||||
import "react-quill/dist/quill.snow.css";
|
|
@ -1,7 +1,8 @@
|
|||
// @ts-nocheck
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSystemAvailable } from "@mcl/queries";
|
||||
import { useSystemAvailable } from "@mcl/api/clients/misc";
|
||||
import Box from "@mui/material/Box";
|
||||
import OverviewVisual from "./OverviewVisual.jsx";
|
||||
import OverviewVisual from "./OverviewVisual.tsx";
|
||||
export default function Overview(props) {
|
||||
const [memory, setMemory] = useState(100);
|
||||
const [cpu, setCpu] = useState(100);
|
||||
|
@ -10,6 +11,7 @@ export default function Overview(props) {
|
|||
|
||||
useEffect(() => {
|
||||
if (systemLoading || !props.clusterMetrics) return;
|
||||
if (!systemAvailable) return;
|
||||
setCpu((props.clusterMetrics.cpu / systemAvailable.cpu) * 100);
|
||||
setMemory((props.clusterMetrics.memory / systemAvailable.memory) * 100);
|
||||
}, [systemAvailable, props.clusterMetrics]);
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import * as React from "react";
|
||||
import CircularProgress from "@mui/material/CircularProgress";
|
||||
import Typography from "@mui/material/Typography";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import TextField from "@mui/material/TextField";
|
||||
export default function BackupBucketOption(props) {
|
||||
const { value, onChange } = props;
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import TextField from "@mui/material/TextField";
|
||||
export default function BackupHostOption(props) {
|
||||
const { value, onChange } = props;
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import TextField from "@mui/material/TextField";
|
||||
export default function BackupIdOption(props) {
|
||||
const { value, onChange } = props;
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { useState } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import TextField from "@mui/material/TextField";
|
||||
export default function BackupKeyOption(props) {
|
||||
const { value, onChange } = props;
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import TextField from "@mui/material/TextField";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { useState } from "react";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Autocomplete from "@mui/material/Autocomplete";
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import TextField from "@mui/material/TextField";
|
||||
export default function HostOption(props) {
|
||||
const { value, onChange, disabled } = props;
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import TextField from "@mui/material/TextField";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
const maxMemSupported = 10;
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import TextField from "@mui/material/TextField";
|
||||
export default function NameOption(props) {
|
||||
const { value, onChange } = props;
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import TextField from "@mui/material/TextField";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import TextField from "@mui/material/TextField";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
// @ts-nocheck
|
||||
import { useState, useEffect } from "react";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { useVersionList } from "@mcl/queries";
|
||||
import { useVersionList } from "@mcl/api/clients/misc";
|
||||
|
||||
export default function VersionOption(props) {
|
||||
const { value, onChange } = props;
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { useEffect, useState } from "react";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
@ -11,7 +12,7 @@ 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";
|
||||
import { getBackupUrl, getServerBackups } from "@mcl/api/clients/backups";
|
||||
|
||||
export function useBackupDialog(isOpen = false) {
|
||||
const [open, setOpen] = useState(isOpen);
|
||||
|
@ -35,7 +36,7 @@ export default function BackupDialog(props) {
|
|||
|
||||
function normalizeLastModified(lastModified) {
|
||||
const d = new Date(Date.parse(lastModified));
|
||||
return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()} ${d.getHours()}:${d.getMinutes()}`;
|
||||
return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()} ${d.getHours()}:${d.getMinutes()}`;
|
||||
}
|
||||
|
||||
const downloadBackup = (backup) =>
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { useState } from "react";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
@ -7,7 +8,7 @@ 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 RconView from "./RconView.jsx";
|
||||
import RconView from "./RconView.tsx";
|
||||
|
||||
export function useRconDialog(isOpen = false) {
|
||||
const [open, setOpen] = useState(isOpen);
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { io } from "socket.io-client";
|
||||
export default class RconSocket {
|
||||
constructor(logUpdate, serverId) {
|
|
@ -1,10 +1,11 @@
|
|||
// @ts-nocheck
|
||||
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 Skeleton from "@mui/material/Skeleton";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import RconSocket from "./RconSocket.js";
|
||||
import RconSocket from "./RconSocket.ts";
|
||||
import "@mcl/css/rcon.css";
|
||||
|
||||
function RconLogSkeleton() {
|
|
@ -1,5 +1,5 @@
|
|||
// @ts-nocheck
|
||||
import React from "react";
|
||||
import { useStartServer, useStopServer, useDeleteServer } from "@mcl/queries";
|
||||
import Box from "@mui/material/Box";
|
||||
import Card from "@mui/material/Card";
|
||||
import CardActions from "@mui/material/CardActions";
|
||||
|
@ -16,13 +16,18 @@ 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";
|
||||
import {
|
||||
useServerDelete,
|
||||
useServerStart,
|
||||
useServerStop,
|
||||
} from "@mcl/api/clients/server";
|
||||
|
||||
export default function ServerCard(props) {
|
||||
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);
|
||||
const startServer = useServerStart(id);
|
||||
const stopServer = useServerStop(id);
|
||||
const deleteServer = useServerDelete(id);
|
||||
function toggleRcon() {
|
||||
if (!services.includes("server")) return;
|
||||
openRcon();
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import React, { useReducer, createContext, useMemo } from "react";
|
||||
const SettingsContext = createContext();
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
// @ts-nocheck
|
||||
// React imports
|
||||
import { useState } from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
|
||||
// Internal Imports
|
||||
import pages from "./MCLPages.jsx";
|
||||
import pages from "./MCLPages.tsx";
|
||||
|
||||
// Materialui
|
||||
import AppBar from "@mui/material/AppBar";
|
|
@ -1,7 +1,8 @@
|
|||
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";
|
||||
// @ts-nocheck
|
||||
import Home from "@mcl/pages/Home.tsx";
|
||||
import Create from "@mcl/pages/Create.tsx";
|
||||
import Files from "@mcl/pages/Files.tsx";
|
||||
import Edit from "@mcl/pages/Edit.tsx";
|
||||
// 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";
|
|
@ -1,6 +1,7 @@
|
|||
// @ts-nocheck
|
||||
// Import React
|
||||
import { Routes, Route, Navigate } from "react-router-dom";
|
||||
import pages from "./MCLPages.jsx";
|
||||
import pages from "./MCLPages.tsx";
|
||||
|
||||
const defaultPage = pages[0].path;
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
// @ts-nocheck
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import MCLPortal from "./MCLPortal.jsx";
|
||||
import MCLPortal from "./MCLPortal.tsx";
|
||||
// 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";
|
||||
/*import Navbar from "./Navbar.tsx";*/
|
||||
import { useCairoAuth } from "@mcl/api/auth";
|
||||
import MCLMenu from "./MCLMenu.tsx";
|
||||
import Auth from "@mcl/pages/Auth.tsx";
|
||||
|
||||
export default function Views() {
|
||||
const auth = useCairoAuth();
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSearchParams, useNavigate } from "react-router-dom";
|
||||
import Box from "@mui/material/Box";
|
|
@ -1,5 +1,6 @@
|
|||
// @ts-nocheck
|
||||
import Box from "@mui/material/Box";
|
||||
import CreateCoreOptions from "./CreateCoreOptions.jsx";
|
||||
import CreateCoreOptions from "./CreateCoreOptions.tsx";
|
||||
export default function Create() {
|
||||
return (
|
||||
<Box className="create">
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Box from "@mui/material/Box";
|
||||
|
@ -6,31 +7,31 @@ 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";
|
||||
import { useServerCreate } from "@mcl/api/clients/server";
|
||||
|
||||
// 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 NameOption from "@mcl/components/server-options/NameOption.tsx";
|
||||
import HostOption from "@mcl/components/server-options/HostOption.tsx";
|
||||
import VersionOption from "@mcl/components/server-options/VersionOption.tsx";
|
||||
import ServerTypeOption, {
|
||||
serverTypeOptions,
|
||||
} from "@mcl/components/server-options/ServerTypeOption.jsx";
|
||||
} from "@mcl/components/server-options/ServerTypeOption.tsx";
|
||||
import CpuOption, {
|
||||
cpuOptions,
|
||||
} from "@mcl/components/server-options/CpuOption.jsx";
|
||||
} from "@mcl/components/server-options/CpuOption.tsx";
|
||||
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";
|
||||
} from "@mcl/components/server-options/MemoryOption.tsx";
|
||||
import ExtraPortsOption from "@mcl/components/server-options/ExtraPortsOption.tsx";
|
||||
import StorageOption from "@mcl/components/server-options/StorageOption.tsx";
|
||||
|
||||
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 BackupHostOption from "@mcl/components/server-options/BackupHostOption.tsx";
|
||||
import BackupBucketOption from "@mcl/components/server-options/BackupBucketOption.tsx";
|
||||
import BackupIdOption from "@mcl/components/server-options/BackupIdOption.tsx";
|
||||
import BackupKeyOption from "@mcl/components/server-options/BackupKeyOption.tsx";
|
||||
import BackupIntervalOption, {
|
||||
backupIntervalDefault,
|
||||
} from "@mcl/components/server-options/BackupIntervalOption.jsx";
|
||||
} from "@mcl/components/server-options/BackupIntervalOption.tsx";
|
||||
|
||||
const defaultServer = {
|
||||
version: "latest",
|
||||
|
@ -45,7 +46,7 @@ export default function CreateCoreOptions() {
|
|||
const [backupEnabled, setBackupEnabled] = useState(false);
|
||||
const [spec, setSpec] = useState(defaultServer);
|
||||
const nav = useNavigate();
|
||||
const createServer = useCreateServer(spec);
|
||||
const createServer = useServerCreate(spec);
|
||||
|
||||
const updateSpec = (attr, val) => {
|
||||
const s = { ...spec };
|
||||
|
@ -57,9 +58,8 @@ export default function CreateCoreOptions() {
|
|||
|
||||
async function upsertSpec() {
|
||||
if (validateSpec() !== "validated") return;
|
||||
createServer()
|
||||
.then(() => nav("/"))
|
||||
.catch(alert);
|
||||
createServer();
|
||||
nav("/");
|
||||
}
|
||||
|
||||
function validateSpec() {
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Autocomplete from "@mui/material/Autocomplete";
|
||||
|
@ -5,11 +6,10 @@ 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";
|
||||
import { useServerCreate } from "@mcl/api/clients/server";
|
||||
import { useVersionList } from "@mcl/api/clients/misc";
|
||||
|
||||
const defaultServer = {
|
||||
version: "latest",
|
||||
|
@ -28,7 +28,7 @@ export default function Create() {
|
|||
const nav = useNavigate();
|
||||
const versionList = useVersionList();
|
||||
const [versions, setVersions] = useState(["latest"]);
|
||||
const createServer = useCreateServer(spec);
|
||||
const createServer = useServerCreate(spec);
|
||||
const updateSpec = (attr, val) => {
|
||||
const s = { ...spec };
|
||||
s[attr] = val;
|
||||
|
@ -90,9 +90,8 @@ export default function Create() {
|
|||
|
||||
async function upsertSpec() {
|
||||
if (validateSpec() !== "validated") return;
|
||||
createServer(spec)
|
||||
.then(() => nav("/"))
|
||||
.catch(alert);
|
||||
createServer(spec);
|
||||
nav("/");
|
||||
}
|
||||
|
||||
function validateSpec() {
|
|
@ -1,6 +1,7 @@
|
|||
// @ts-nocheck
|
||||
import { useSearchParams, useNavigate } from "react-router-dom";
|
||||
import Box from "@mui/material/Box";
|
||||
import EditCoreOptions from "./EditCoreOptions.jsx";
|
||||
import EditCoreOptions from "./EditCoreOptions.tsx";
|
||||
export default function Edit() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const currentServer = searchParams.get("server");
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Box from "@mui/material/Box";
|
||||
|
@ -6,37 +7,37 @@ 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";
|
||||
import { useBlueprint, useServerModify } from "@mcl/api/clients/server";
|
||||
|
||||
// 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 NameOption from "@mcl/components/server-options/NameOption.tsx";
|
||||
import HostOption from "@mcl/components/server-options/HostOption.tsx";
|
||||
import VersionOption from "@mcl/components/server-options/VersionOption.tsx";
|
||||
import ServerTypeOption, {
|
||||
serverTypeOptions,
|
||||
} from "@mcl/components/server-options/ServerTypeOption.jsx";
|
||||
} from "@mcl/components/server-options/ServerTypeOption.tsx";
|
||||
import CpuOption, {
|
||||
cpuOptions,
|
||||
} from "@mcl/components/server-options/CpuOption.jsx";
|
||||
} from "@mcl/components/server-options/CpuOption.tsx";
|
||||
import MemoryOption, {
|
||||
memoryOptions,
|
||||
} from "@mcl/components/server-options/MemoryOption.jsx";
|
||||
import ExtraPortsOption from "@mcl/components/server-options/ExtraPortsOption.jsx";
|
||||
} from "@mcl/components/server-options/MemoryOption.tsx";
|
||||
import ExtraPortsOption from "@mcl/components/server-options/ExtraPortsOption.tsx";
|
||||
|
||||
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 BackupHostOption from "@mcl/components/server-options/BackupHostOption.tsx";
|
||||
import BackupBucketOption from "@mcl/components/server-options/BackupBucketOption.tsx";
|
||||
import BackupIdOption from "@mcl/components/server-options/BackupIdOption.tsx";
|
||||
import BackupKeyOption from "@mcl/components/server-options/BackupKeyOption.tsx";
|
||||
import BackupIntervalOption, {
|
||||
backupIntervalDefault,
|
||||
} from "@mcl/components/server-options/BackupIntervalOption.jsx";
|
||||
} from "@mcl/components/server-options/BackupIntervalOption.tsx";
|
||||
|
||||
export default function EditCoreOptions(props) {
|
||||
const { serverId } = props;
|
||||
const [spec, setSpec] = useState();
|
||||
const modifyServer = useModifyServer(spec);
|
||||
const modifyServer = useServerModify(spec);
|
||||
const nav = useNavigate();
|
||||
const { isLoading, data: serverBlueprint } = useGetServer(serverId);
|
||||
const { isLoading, data: serverBlueprint } = useBlueprint(serverId);
|
||||
|
||||
useEffect(() => setSpec(serverBlueprint), [serverBlueprint]);
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
// @ts-nocheck
|
||||
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";
|
||||
} from "@mcl/components/files/FilePreview.tsx";
|
||||
import MineclusterFiles from "@mcl/components/files/MineclusterFiles.tsx";
|
||||
|
||||
export default function Files() {
|
||||
const [open, dialogToggle] = useFilePreview();
|
|
@ -1,17 +1,18 @@
|
|||
// @ts-nocheck
|
||||
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 "@mcl/components/servers/ServerCard.jsx";
|
||||
import ServerCard from "@mcl/components/servers/ServerCard.tsx";
|
||||
import RconDialog, {
|
||||
useRconDialog,
|
||||
} from "@mcl/components/servers/RconDialog.jsx";
|
||||
import Overview from "@mcl/components/overview/Overview.jsx";
|
||||
} from "@mcl/components/servers/RconDialog.tsx";
|
||||
import Overview from "@mcl/components/overview/Overview.tsx";
|
||||
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 { useServerInstances } from "@mcl/api/clients/server";
|
||||
import BackupDialog, {
|
||||
useBackupDialog,
|
||||
} from "../components/servers/BackupsDialog";
|
|
@ -1,8 +1,9 @@
|
|||
// @ts-nocheck
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
const tokenStorageName = "cairoUserToken";
|
||||
const tokenQuery = "cairoUserToken";
|
||||
const tokenStorageName = "cairoAuthToken";
|
||||
const tokenQuery = "cairoAuthToken";
|
||||
|
||||
const verifyAuth = (authToken) =>
|
||||
fetch("/api/auth/verify", {
|
15
src/util/api/clients/backups.ts
Normal file
15
src/util/api/clients/backups.ts
Normal 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,
|
||||
});
|
68
src/util/api/clients/files.ts
Normal file
68
src/util/api/clients/files.ts
Normal 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);
|
||||
}
|
27
src/util/api/clients/misc.ts
Normal file
27
src/util/api/clients/misc.ts
Normal 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()),
|
||||
});
|
123
src/util/api/clients/server.ts
Normal file
123
src/util/api/clients/server.ts
Normal file
|
@ -0,0 +1,123 @@
|
|||
import { useQuery } 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) => {
|
||||
console.log("requestServerCreate being called");
|
||||
return 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),
|
||||
});
|
55
src/util/api/requests.ts
Normal file
55
src/util/api/requests.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
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?: HeadersInit;
|
||||
handleErrors?: boolean;
|
||||
}
|
||||
|
||||
async function apiRequest(apiRequestArgs: ApiRequestArgs) {
|
||||
const { subPath, json, body, method, jsonify, extraHeaders, handleErrors } =
|
||||
apiRequestArgs;
|
||||
const headers: HeadersInit = 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,
|
||||
headers,
|
||||
}).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 = (
|
||||
apiRequest: (args?: any) => Promise<any>,
|
||||
invalidate: string[],
|
||||
) => {
|
||||
const qc = useQueryClient();
|
||||
const mutate = async (...args: any) =>
|
||||
apiRequest(...args).then(() =>
|
||||
qc.invalidateQueries({ queryKey: invalidate }),
|
||||
);
|
||||
return mutate;
|
||||
};
|
|
@ -1,144 +0,0 @@
|
|||
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]);
|
||||
};
|
||||
};
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-nocheck
|
||||
// Generated using https://zenoo.github.io/mui-theme-creator/
|
||||
import { createTheme } from "@mui/material/styles";
|
||||
import { unstable_ClassNameGenerator as ClassNameGenerator } from "@mui/material/className";
|
39
tsconfig.json
Normal file
39
tsconfig.json
Normal 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
19
tsconfig.server.json
Normal 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"]
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { defineConfig } from "vite";
|
||||
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 backendUrl = MCL_VITE_BACKEND_URL ?? "http://localhost:52000";
|
||||
|
@ -8,10 +8,10 @@ const vitePort = MCL_VITE_DEV_PORT ?? 52025;
|
|||
|
||||
export default () => {
|
||||
return defineConfig({
|
||||
plugins: [react()],
|
||||
plugins: [react(), tsconfigPaths()],
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
port: vitePort,
|
||||
port: Number(vitePort),
|
||||
proxy: {
|
||||
"/api": backendUrl,
|
||||
"/socket.io": backendUrl,
|
||||
|
@ -22,18 +22,8 @@ export default () => {
|
|||
},
|
||||
},
|
||||
build: {
|
||||
outDir: "./build",
|
||||
outDir: "./build/vite",
|
||||
},
|
||||
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"),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue