file uploader

This commit is contained in:
2023-10-05 10:27:52 -06:00
parent 6e582e11dc
commit 5779cd9efc
17 changed files with 847 additions and 37 deletions

View File

@@ -31,8 +31,8 @@
"@preact/signals": "https://esm.sh/*@preact/signals@1.1.3", "@preact/signals": "https://esm.sh/*@preact/signals@1.1.3",
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.2.3", "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.2.3",
"$std/": "https://deno.land/std@0.193.0/", "$std/": "https://deno.land/std@0.193.0/",
"puppet": "https://deno.land/x/sockpuppet@0.6.0/mod.ts", "puppet": "https://deno.land/x/sockpuppet@0.6.1/mod.ts",
"puppet/client": "https://deno.land/x/sockpuppet@0.6.0/client/mod.ts" "puppet/client": "https://deno.land/x/sockpuppet@0.6.1/client/mod.ts"
}, },
"compilerOptions": { "compilerOptions": {
"jsx": "react-jsx", "jsx": "react-jsx",

View File

@@ -8,16 +8,19 @@ import * as $2 from "./routes/api/fabric/index.ts";
import * as $3 from "./routes/api/manage.ts"; import * as $3 from "./routes/api/manage.ts";
import * as $4 from "./routes/api/players.ts"; import * as $4 from "./routes/api/players.ts";
import * as $5 from "./routes/index.tsx"; import * as $5 from "./routes/index.tsx";
import * as $6 from "./routes/players.tsx"; import * as $6 from "./routes/mods/index.tsx";
import * as $7 from "./routes/properties.tsx"; import * as $7 from "./routes/players.tsx";
import * as $8 from "./routes/setup/eula.tsx"; import * as $8 from "./routes/properties.tsx";
import * as $9 from "./routes/setup/fabric.tsx"; import * as $9 from "./routes/setup/eula.tsx";
import * as $10 from "./routes/setup/index.tsx"; import * as $10 from "./routes/setup/fabric.tsx";
import * as $11 from "./routes/terminal.tsx"; import * as $11 from "./routes/setup/index.tsx";
import * as $12 from "./routes/terminal.tsx";
import * as $13 from "./routes/upload.ts";
import * as $$0 from "./islands/fabricVersions.tsx"; import * as $$0 from "./islands/fabricVersions.tsx";
import * as $$1 from "./islands/players.tsx"; import * as $$1 from "./islands/fileUploader.tsx";
import * as $$2 from "./islands/statusManager.tsx"; import * as $$2 from "./islands/players.tsx";
import * as $$3 from "./islands/terminal.tsx"; import * as $$3 from "./islands/statusManager.tsx";
import * as $$4 from "./islands/terminal.tsx";
const manifest = { const manifest = {
routes: { routes: {
@@ -27,18 +30,21 @@ const manifest = {
"./routes/api/manage.ts": $3, "./routes/api/manage.ts": $3,
"./routes/api/players.ts": $4, "./routes/api/players.ts": $4,
"./routes/index.tsx": $5, "./routes/index.tsx": $5,
"./routes/players.tsx": $6, "./routes/mods/index.tsx": $6,
"./routes/properties.tsx": $7, "./routes/players.tsx": $7,
"./routes/setup/eula.tsx": $8, "./routes/properties.tsx": $8,
"./routes/setup/fabric.tsx": $9, "./routes/setup/eula.tsx": $9,
"./routes/setup/index.tsx": $10, "./routes/setup/fabric.tsx": $10,
"./routes/terminal.tsx": $11, "./routes/setup/index.tsx": $11,
"./routes/terminal.tsx": $12,
"./routes/upload.ts": $13,
}, },
islands: { islands: {
"./islands/fabricVersions.tsx": $$0, "./islands/fabricVersions.tsx": $$0,
"./islands/players.tsx": $$1, "./islands/fileUploader.tsx": $$1,
"./islands/statusManager.tsx": $$2, "./islands/players.tsx": $$2,
"./islands/terminal.tsx": $$3, "./islands/statusManager.tsx": $$3,
"./islands/terminal.tsx": $$4,
}, },
baseUrl: import.meta.url, baseUrl: import.meta.url,
}; };

71
islands/fileUploader.tsx Normal file
View File

@@ -0,0 +1,71 @@
import { useState } from "preact/hooks";
import { FunctionComponent } from "preact";
export const FileUploader: FunctionComponent<{ path: string }> = (
{ children, path },
) => {
const [hovered, setHovered] = useState(false);
const playSound = () => {
const sound = new Audio(
"https://cdn.pixabay.com/audio/2023/07/21/audio_5634777127.mp3",
);
sound.play();
};
const defaultPreventer = (e: Event) => {
e.preventDefault();
e.stopPropagation();
};
const uploadFiles = async (files: File[]) => {
if (!files.length) return;
const formData = new FormData();
for (const file of files) {
formData.append(file.name, file);
}
await fetch("/upload", {
method: "POST",
body: formData,
headers: {
"x-grizz-path": path,
},
});
location.reload();
};
return (
<div
class="relative grid"
onDragEnter={(e) => {
defaultPreventer(e);
setHovered(true);
}}
onDragOver={(e) => {
defaultPreventer(e);
setHovered(true);
}}
onDrop={(e) => {
defaultPreventer(e);
playSound();
const files = Array.from(e.dataTransfer?.files || []);
uploadFiles(files);
setHovered(false);
}}
onDragLeave={(e) => {
defaultPreventer(e);
setHovered(false);
}}
>
{children}
{hovered && (
<div class="absolute place-self-center">
<i class="fas fa-cloud-arrow-up fa-4x"></i>
</div>
)}
</div>
);
};

112
lib/modrinth.ts Normal file
View File

@@ -0,0 +1,112 @@
import { Loader } from "../types/mcgrizzconf.ts";
export type ModrinthProjectSearchResult = {
project_id: string;
project_type: string;
slug: string;
author: string;
title: string;
description: string;
categories: string[];
display_categories: string[];
versions: string[];
downloads: number;
follows: number;
icon_url: string;
date_created: string;
date_modified: string;
latest_version: string;
license: string;
client_side: string;
server_side: string;
gallery: string[];
featured_gallery: string;
color: number;
};
export type ModrinthProject = {
id: string;
slug: string;
project_type: string;
team: string;
title: string;
description: string;
// Markdown
body: string;
body_url: string | null;
published: string;
updated: string;
approved: string;
queued: string;
status: string;
requested_status: string;
moderator_message: string | null;
license: {
id: string;
name: string;
url: string | null;
};
client_side: string;
server_side: string;
downloads: number;
followers: number;
categories: string[];
additional_categories: [];
game_versions: string[];
loaders: string[];
versions: string[];
icon_url: string;
issues_url: string;
source_url: string;
wiki_url: string | null;
discord_url: string;
donation_urls: string[];
gallery: {
url: string;
featured: boolean;
title: string;
description: string;
created: string;
ordering: number;
}[];
color: number;
thread_id: string;
monetization_status: string;
};
export class Modrinth {
static apiRoot = "https://api.modrinth.com/v2";
static async searchMods(
q: string,
version: string,
loader: Loader,
offset = 0,
limit = 12,
) {
const facets = [
`"versions:${version}"`,
'"project_type:mod"',
];
if (loader && loader !== "vanilla" && loader !== "unset") {
facets.push(`"categories:${loader}"`);
}
const qString = `/search?query=${q}&facets=[[${
facets.join("],[")
}]]&offset=${offset * limit}&limit=${limit}`.trim();
const res = await fetch(this.apiRoot + qString);
return await res.json() as {
hits: ModrinthProjectSearchResult;
offset: number;
limit: number;
total_hits: number;
};
}
static async getProject(id: string) {
const res = await fetch(this.apiRoot + "/project/" + id);
return await res.json() as ModrinthProject;
}
}

View File

@@ -21,7 +21,7 @@ export default function App({ Component }: AppProps) {
> >
</link> </link>
<link rel="stylesheet" href="/styles/tailwind.css" /> <link rel="stylesheet" href="/styles/tailwind.css" />
</head> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" crossOrigin="anonymous" referrerpolicy="no-referrer" /> </head>
<body> <body>
<div class="flex h-[100vh]"> <div class="flex h-[100vh]">
<div class="relative h-full min-w-[400px] bg-licorice overflow-y-auto border-r-2 p-4 dark:border-sky-950 border-sky text-white flex flex-col"> <div class="relative h-full min-w-[400px] bg-licorice overflow-y-auto border-r-2 p-4 dark:border-sky-950 border-sky text-white flex flex-col">

61
routes/mods/index.tsx Normal file
View File

@@ -0,0 +1,61 @@
import { FunctionComponent } from "preact";
import { Content } from "../../components/Content.tsx";
import { SERVER_STATE } from "../../state/serverState.ts";
import { FileUploader } from "../../islands/fileUploader.tsx";
export default async function ModsFolder() {
const files: string[] = [];
if (
SERVER_STATE.serverType !== "unset" && SERVER_STATE.serverType !== "vanilla"
) {
for await (const fileEntry of Deno.readDir("./server/mods")) {
if (fileEntry.isFile) {
files.push(fileEntry.name);
}
}
}
return (
<div className="container p-8">
<Content>
<h2 class="font-pixel text-xl">Active Mods</h2>
<FileUploader path="./server/mods">
<div className="relative grid lg:grid-cols-3 min-h-[100px]">
{!files.length && (
<div class="absolute place-self-center">Drop files here to upload</div>
)}
{files.map((f) => (
<div class="flex gap-2 items-center">
<FileIcon fileName={f} />
{f}
</div>
))}
</div>
</FileUploader>
</Content>
</div>
);
}
const FileIcon: FunctionComponent<{ fileName: string }> = ({ fileName }) => {
let icon;
switch (fileName.split(".")[1]) {
case "jar":
icon = "fa-brand fa-java";
break;
case "tmp":
case "temp":
icon = "fas fa-ghost";
break;
case "png":
case "jpg":
case "jpeg":
case "webp":
icon = "fas fa-image";
break;
default:
icon = "fas fa-file";
}
return <i class={icon}></i>;
};

21
routes/upload.ts Normal file
View File

@@ -0,0 +1,21 @@
import { Handlers } from "$fresh/server.ts";
import { ensureFile } from "$std/fs/ensure_file.ts";
export const handler: Handlers = {
async POST(req, _ctx) {
const path = req.headers.get("x-grizz-path");
if (!path) return new Response("Upload path not included", { status: 400 });
const files = Array.from((await req.formData()).values()) as File[];
for (const file of files) {
const filePath = path.replace(/.$/, (e) => e.replace("/", "") + "/") +
file.name;
await ensureFile(filePath);
const newFile = await Deno.open(filePath, { write: true });
file.stream().pipeTo(newFile.writable);
}
return new Response("Success");
},
};

View File

@@ -1,7 +1,7 @@
import { Sockpuppet } from "puppet/client"; import { Sockpuppet } from "puppet/client";
import { acceptEULA, checkEULA } from "../util/EULA.ts"; import { acceptEULA, checkEULA } from "../util/EULA.ts";
import { Loader } from "../types/mcgrizzconf.ts"; import { Loader } from "../types/mcgrizzconf.ts";
import { updateConfFile } from "../util/confFile.ts"; import { getConfFile, updateConfFile } from "../util/confFile.ts";
import { IS_BROWSER } from "$fresh/runtime.ts"; import { IS_BROWSER } from "$fresh/runtime.ts";
type MCServerEvent = 'message'; type MCServerEvent = 'message';
@@ -16,7 +16,7 @@ class ServerState {
private command!: Deno.Command; private command!: Deno.Command;
private process!: Deno.ChildProcess; private process!: Deno.ChildProcess;
private _eulaAccepted: boolean; private _eulaAccepted = false;
private sockpuppet!: Sockpuppet; private sockpuppet!: Sockpuppet;
private _channelId = "blanaba"; private _channelId = "blanaba";
@@ -30,15 +30,29 @@ class ServerState {
private _serverType: Loader = 'unset'; private _serverType: Loader = 'unset';
public get serverType(): Loader { public get serverType(): Loader {
return this.serverType; return this._serverType;
} }
public set serverType(loader: Loader) { public set serverType(loader: Loader) {
updateConfFile({loader}); updateConfFile({loader});
this._serverType = loader; this._serverType = loader;
} }
private _serverVersion: string;
public get serverVersion(): string {
return this._serverVersion;
}
public set serverVersion(version: string) {
updateConfFile({version});
this._serverVersion = version;
}
constructor() { constructor() {
this._eulaAccepted = checkEULA(); const conf = getConfFile();
this._serverType = conf.loader;
this._serverVersion = conf.version;
// if (this.serverType !== 'unset') this._eulaAccepted = checkEULA();
this.sockpuppet = new Sockpuppet( this.sockpuppet = new Sockpuppet(
"ws://sockpuppet.cyborggrizzly.com", "ws://sockpuppet.cyborggrizzly.com",
() => { () => {

BIN
static/javaicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

File diff suppressed because one or more lines are too long

469
test.json Normal file
View File

@@ -0,0 +1,469 @@
{
"hits": [
{
"project_id": "FTeXqI9v",
"project_type": "mod",
"slug": "create-new-age",
"author": "nullBlade",
"title": "Create: New Age",
"description": "Create: New Age is an addon for the Create mod that adds integration with electricity.",
"categories": ["technology", "fabric", "forge"],
"display_categories": ["technology", "fabric", "forge"],
"versions": ["1.19.2", "1.20.1", "1.20.2"],
"downloads": 473,
"follows": 10,
"icon_url": "https://cdn.modrinth.com/data/FTeXqI9v/fe75695f6f2e085ac9fb56204de7f88b6d716e8d.png",
"date_created": "2023-08-30T02:15:52.063627Z",
"date_modified": "2023-09-24T07:07:15.073267Z",
"latest_version": "1.20.2",
"license": "BSD-3-Clause",
"client_side": "required",
"server_side": "required",
"gallery": [
"https://cdn.modrinth.com/data/FTeXqI9v/images/156cd2e6ce38d8647a7c1e073753baaebb7c0474.png",
"https://cdn.modrinth.com/data/FTeXqI9v/images/197a77d6e98b80486481a7b7fe7cb28fa8b87f30.png",
"https://cdn.modrinth.com/data/FTeXqI9v/images/59be34a6c51e72f31616917af146d02c0a5a3cb3.png",
"https://cdn.modrinth.com/data/FTeXqI9v/images/e35cc2dc1e0aa47bc15fa2ef1aa9f2121f4d0539.png"
],
"featured_gallery": "https://cdn.modrinth.com/data/FTeXqI9v/images/8bdaf7546c4d46ff9305ed9aba18df6e32d9234a.jpeg",
"color": 3220514
},
{
"project_id": "nr7cSJlY",
"project_type": "mod",
"slug": "brewery",
"author": "Patbox",
"title": "Patbox's Brewery",
"description": "Create alcoholic and non-alcoholic drinks with cauldrons and barrels!",
"categories": ["food", "game-mechanics", "fabric", "quilt"],
"display_categories": ["food", "game-mechanics", "fabric", "quilt"],
"versions": [
"1.19.2",
"1.19.3",
"1.19.4",
"1.19.4-rc2",
"1.20",
"1.20.1",
"1.20.2",
"1.20.2-rc2",
"1.20-rc1"
],
"downloads": 3924,
"follows": 45,
"icon_url": "https://cdn.modrinth.com/data/nr7cSJlY/c9c1d9b61922adda30b53d739922231c18d2823c.png",
"date_created": "2022-09-25T20:30:15.568551Z",
"date_modified": "2023-09-21T13:19:47.739240Z",
"latest_version": "1.20-rc1",
"license": "LGPL-3.0-only",
"client_side": "optional",
"server_side": "required",
"gallery": [],
"featured_gallery": "https://cdn.modrinth.com/data/nr7cSJlY/images/8965a6402256474bb5d7a5ff7141751f1f22992a.png",
"color": 13625834
},
{
"project_id": "sUlkLN1E",
"project_type": "mod",
"slug": "azure-paxels",
"author": "AzureDoom",
"title": "Azure Paxels",
"description": "Created becasue Fabric 1.19.4 has no good paxel mods updated.",
"categories": ["equipment", "fabric", "neoforge", "quilt"],
"display_categories": ["equipment", "fabric", "neoforge", "quilt"],
"versions": [
"1.19.4",
"1.20",
"1.20.1",
"1.20.2",
"1.20-pre1",
"1.20-rc1"
],
"downloads": 573,
"follows": 10,
"icon_url": "https://cdn.modrinth.com/data/sUlkLN1E/1e026932468f5dc227444892b11cc5e06f1587a7.png",
"date_created": "2023-05-07T01:19:46.079261Z",
"date_modified": "2023-10-03T18:20:58.140878Z",
"latest_version": "1.20-rc1",
"license": "MIT",
"client_side": "required",
"server_side": "required",
"gallery": [
"https://cdn.modrinth.com/data/sUlkLN1E/images/2f5f219a20627ab1e3d344d99eb4e725848df1b7.png"
],
"featured_gallery": null,
"color": 3549487
},
{
"project_id": "llV8wfkk",
"project_type": "mod",
"slug": "betterconsolemc",
"author": "Jonas_Jones",
"title": "BetterConsoleMC",
"description": "Create custom ingame commads that run system commands and tasks.\nThis is a new and improved version of the ConsoleMC mod. It works by defining the command first to avoid the big security risk.",
"categories": ["utility", "fabric", "quilt", "transportation"],
"display_categories": ["utility", "fabric", "quilt"],
"versions": [
"1.17.1",
"1.18",
"1.18.1",
"1.18.2",
"1.19",
"1.19.1",
"1.19.2",
"1.19.3",
"1.19.4",
"1.20",
"1.20.1",
"1.20.2"
],
"downloads": 119,
"follows": 3,
"icon_url": "https://cdn.modrinth.com/data/llV8wfkk/04f4393f5149d5b29a20b5170ac57eb4dfb7f303.png",
"date_created": "2022-12-16T01:35:12.431459Z",
"date_modified": "2023-09-26T21:59:10.564546Z",
"latest_version": "1.20.2",
"license": "CC0-1.0",
"client_side": "unsupported",
"server_side": "required",
"gallery": [],
"featured_gallery": null,
"color": 16516316
},
{
"project_id": "NWvsqJ2Z",
"project_type": "mod",
"slug": "areas",
"author": "Serilum",
"title": "Areas",
"description": "✍️ Create custom named regions/towns/zones with a radius using signs, with join/leave messages via GUI.",
"categories": [
"decoration",
"game-mechanics",
"library",
"fabric",
"forge",
"neoforge",
"quilt"
],
"display_categories": [
"decoration",
"game-mechanics",
"library",
"fabric",
"forge",
"neoforge",
"quilt"
],
"versions": [
"1.16.5",
"1.18.2",
"1.19.2",
"1.19.3",
"1.19.4",
"1.20",
"1.20.1",
"1.20.2"
],
"downloads": 96662,
"follows": 114,
"icon_url": "https://cdn.modrinth.com/data/NWvsqJ2Z/icon.png",
"date_created": "2022-09-01T15:43:14.157586Z",
"date_modified": "2023-09-22T00:10:53.971185Z",
"latest_version": "1.20.2",
"license": "LicenseRef-All-Rights-Reserved",
"client_side": "required",
"server_side": "optional",
"gallery": [],
"featured_gallery": null,
"color": 4234903
},
{
"project_id": "Ot5JFxuv",
"project_type": "mod",
"slug": "death-backup",
"author": "Serilum",
"title": "Death Backup",
"description": "💾 Creates back-ups of player inventories before death, which can be loaded via commands.",
"categories": [
"management",
"utility",
"fabric",
"forge",
"neoforge",
"quilt"
],
"display_categories": [
"management",
"utility",
"fabric",
"forge",
"neoforge",
"quilt"
],
"versions": [
"1.16.5",
"1.18.2",
"1.19.2",
"1.19.3",
"1.19.4",
"1.20",
"1.20.1",
"1.20.2"
],
"downloads": 2684,
"follows": 23,
"icon_url": "https://cdn.modrinth.com/data/Ot5JFxuv/icon.jpg",
"date_created": "2022-09-01T14:40:08.430843Z",
"date_modified": "2023-09-21T23:20:00.640383Z",
"latest_version": "1.20.2",
"license": "LicenseRef-All-Rights-Reserved",
"client_side": "optional",
"server_side": "required",
"gallery": [],
"featured_gallery": null,
"color": 5655906
},
{
"project_id": "IPbFTPzw",
"project_type": "mod",
"slug": "quick-paths",
"author": "Serilum",
"title": "Quick Paths",
"description": "🚶 Create long paths instantly by setting a start and end point.",
"categories": [
"transportation",
"utility",
"fabric",
"forge",
"neoforge",
"quilt"
],
"display_categories": [
"transportation",
"utility",
"fabric",
"forge",
"neoforge",
"quilt"
],
"versions": [
"1.16.5",
"1.18.2",
"1.19.2",
"1.19.3",
"1.19.4",
"1.20",
"1.20.1",
"1.20.2"
],
"downloads": 2213,
"follows": 25,
"icon_url": "https://cdn.modrinth.com/data/IPbFTPzw/icon.jpg",
"date_created": "2022-09-01T09:45:53.732436Z",
"date_modified": "2023-09-21T22:01:33.176715Z",
"latest_version": "1.20.2",
"license": "LicenseRef-All-Rights-Reserved",
"client_side": "optional",
"server_side": "required",
"gallery": [],
"featured_gallery": null,
"color": 6455609
},
{
"project_id": "fgmhI8kH",
"project_type": "mod",
"slug": "ct-overhaul-village",
"author": "ChoiceTheorem",
"title": "ChoiceTheorem's Overhauled Village",
"description": "Enhances and creates new villages and pillager outposts, that perfectly fit into your Minecraft world.",
"categories": [
"adventure",
"worldgen",
"fabric",
"forge",
"neoforge",
"quilt",
"economy",
"utility"
],
"display_categories": [
"adventure",
"worldgen",
"fabric",
"forge",
"neoforge",
"quilt"
],
"versions": [
"1.18.2",
"1.18.2-rc1",
"1.19",
"1.19.1",
"1.19.2",
"1.19.3",
"1.19.4",
"1.20",
"1.20.1",
"1.20.2"
],
"downloads": 158413,
"follows": 630,
"icon_url": "https://cdn.modrinth.com/data/fgmhI8kH/76dd7230a35c12d4956985317ffd0c079d6a9148.jpeg",
"date_created": "2022-05-16T03:06:55.644362Z",
"date_modified": "2023-09-29T16:52:26.973688Z",
"latest_version": "1.20.2",
"license": "CC-BY-NC-ND-4.0",
"client_side": "optional",
"server_side": "required",
"gallery": [
"https://cdn.modrinth.com/data/fgmhI8kH/images/2f7b8e2fc46cbb9e7a83368a93ab1ba1feb110ed.webp",
"https://cdn.modrinth.com/data/fgmhI8kH/images/576fb3e920f242512932434a51cba40b14da2750.png"
],
"featured_gallery": "https://cdn.modrinth.com/data/fgmhI8kH/images/15d7bf1aa1b7174fde4a5dac2ed81d4b8adb4b06.png",
"color": 5587502
},
{
"project_id": "kOuPUitF",
"project_type": "mod",
"slug": "healing-campfire",
"author": "Serilum",
"title": "Healing Campfire",
"description": "🔥🩹 Creates an area around the (soul) campfire where players and passive mobs receive regeneration.",
"categories": [
"adventure",
"game-mechanics",
"utility",
"fabric",
"forge",
"neoforge",
"quilt"
],
"display_categories": [
"adventure",
"game-mechanics",
"utility",
"fabric",
"forge",
"neoforge",
"quilt"
],
"versions": [
"1.16.5",
"1.18.2",
"1.19.2",
"1.19.3",
"1.19.4",
"1.20",
"1.20.1",
"1.20.2"
],
"downloads": 16604,
"follows": 111,
"icon_url": "https://cdn.modrinth.com/data/kOuPUitF/icon.png",
"date_created": "2022-09-01T13:26:56.104570Z",
"date_modified": "2023-09-21T22:40:49.277734Z",
"latest_version": "1.20.2",
"license": "LicenseRef-All-Rights-Reserved",
"client_side": "optional",
"server_side": "required",
"gallery": [],
"featured_gallery": null,
"color": 5921840
},
{
"project_id": "QktnymFN",
"project_type": "mod",
"slug": "lightning-podoboo",
"author": "LostLuma",
"title": "Lightning Podoboo",
"description": "Makes fire created by natural lightning cosmetic, meaning no blocks are destroyed during thunderstorms.",
"categories": ["game-mechanics", "utility", "fabric", "quilt", "mobs"],
"display_categories": ["game-mechanics", "utility", "fabric", "quilt"],
"versions": [
"1.18",
"1.18.1",
"1.18.2",
"1.19",
"1.19.1",
"1.19.2",
"1.19.3",
"1.19.4",
"1.20",
"1.20.1",
"1.20.2"
],
"downloads": 488,
"follows": 12,
"icon_url": "",
"date_created": "2022-02-08T11:09:41.222962Z",
"date_modified": "2023-09-21T16:15:18.747778Z",
"latest_version": "1.20.2",
"license": "MIT",
"client_side": "optional",
"server_side": "required",
"gallery": [],
"featured_gallery": null,
"color": null
},
{
"project_id": "hQbzUScT",
"project_type": "mod",
"slug": "dynamichud",
"author": "tanishisherewithhh",
"title": "DynamicHUD",
"description": "A library to create Hud Widgets and display them on the screen. AutoSave and Autoload included. Fabric only",
"categories": ["game-mechanics", "library", "utility", "fabric"],
"display_categories": ["game-mechanics", "library", "utility", "fabric"],
"versions": ["1.19.4", "1.20", "1.20.1", "1.20.2"],
"downloads": 249,
"follows": 6,
"icon_url": "https://cdn.modrinth.com/data/hQbzUScT/76bde52a3696a533af510f47e8f93b35ad4eb2ab.png",
"date_created": "2023-06-17T00:27:34.710323Z",
"date_modified": "2023-10-02T06:26:09.404685Z",
"latest_version": "1.20.2",
"license": "MIT",
"client_side": "required",
"server_side": "unsupported",
"gallery": [
"https://cdn.modrinth.com/data/hQbzUScT/images/76bde52a3696a533af510f47e8f93b35ad4eb2ab.png"
],
"featured_gallery": "https://cdn.modrinth.com/data/hQbzUScT/images/de25273afeb40a17b77821ae4b56434c3953a47b.png",
"color": 9757053
},
{
"project_id": "O5BnVXcp",
"project_type": "mod",
"slug": "shulker-nbt-fix",
"author": "qpcrummer",
"title": "ShulkerNbtFix",
"description": "All shulkers are created equal: placed and unplaced",
"categories": ["game-mechanics", "fabric", "quilt", "utility"],
"display_categories": ["game-mechanics", "fabric", "quilt"],
"versions": [
"1.19",
"1.19.1",
"1.19.2",
"1.19.3",
"1.19.4",
"1.20",
"1.20.1",
"1.20.2"
],
"downloads": 120,
"follows": 4,
"icon_url": "https://cdn.modrinth.com/data/O5BnVXcp/096802aeb7ee5284d3dca7f38b6e00b46c1530ce.png",
"date_created": "2023-06-17T00:38:18.136269Z",
"date_modified": "2023-09-22T00:16:16.841957Z",
"latest_version": "1.20.2",
"license": "LGPL-3.0-only",
"client_side": "unsupported",
"server_side": "required",
"gallery": [],
"featured_gallery": null,
"color": 5980251
}
],
"offset": 0,
"limit": 12,
"total_hits": 23
}

View File

@@ -1,5 +1,13 @@
export type Loader = 'forge' | 'fabric' | 'vanilla' | 'unset'; import { ModrinthProject } from "../lib/modrinth.ts";
export type Loader = "forge" | "fabric" | "vanilla" | "unset";
export type MCGrizzConf = { export type MCGrizzConf = {
loader: Loader loader: Loader;
} version: string;
mods?: {
source: "modrinth";
details: ModrinthProject;
jarName?: string;
}[];
};

View File

@@ -3,7 +3,6 @@ import { IS_BROWSER } from "$fresh/runtime.ts";
const eulaRegex = /(eula=false)/; const eulaRegex = /(eula=false)/;
export const checkEULA = (instance = "server") => export const checkEULA = (instance = "server") =>
!IS_BROWSER && !eulaRegex.test(Deno.readTextFileSync(`./${instance}/eula.txt`)); !IS_BROWSER && !eulaRegex.test(Deno.readTextFileSync(`./${instance}/eula.txt`));
// true;
export const acceptEULA = (instance = "server") => { export const acceptEULA = (instance = "server") => {
const eula = Deno.readTextFileSync(`./${instance}/eula.txt`); const eula = Deno.readTextFileSync(`./${instance}/eula.txt`);

View File

@@ -1,7 +1,10 @@
import { ensureFileSync } from "$std/fs/ensure_file.ts";
import { IS_BROWSER } from "$fresh/runtime.ts";
import { MCGrizzConf } from "../types/mcgrizzconf.ts"; import { MCGrizzConf } from "../types/mcgrizzconf.ts";
const defaultConf: MCGrizzConf = { const defaultConf: MCGrizzConf = {
loader: 'unset', loader: 'unset',
version: ''
} }
const confPath = 'mcgrizz.json' const confPath = 'mcgrizz.json'
@@ -14,7 +17,9 @@ export function makeConfFile(): MCGrizzConf {
} }
export function getConfFile(): MCGrizzConf { export function getConfFile(): MCGrizzConf {
const conf = JSON.parse(Deno.readTextFileSync(confPath)); if (IS_BROWSER) return defaultConf;
ensureFileSync(confPath);
const conf = JSON.parse(Deno.readTextFileSync(confPath) || 'null');
if (!conf) { if (!conf) {
return makeConfFile(); return makeConfFile();
@@ -24,7 +29,8 @@ export function getConfFile(): MCGrizzConf {
} }
export async function updateConfFile(newConf: Partial<MCGrizzConf>) { export async function updateConfFile(newConf: Partial<MCGrizzConf>) {
const conf = {...getConfFile(), newConf}; if (IS_BROWSER) return;
const conf = {...getConfFile(), ...newConf};
await Deno.writeTextFile(confPath, JSON.stringify(conf, null, 2)); await Deno.writeTextFile(confPath, JSON.stringify(conf, null, 2));
} }

35
util/download.ts Normal file
View File

@@ -0,0 +1,35 @@
import { ensureFile } from "$std/fs/ensure_file.ts";
/**
*
* @param src url of file
* @param dest destination file. If `useFileName` is true, this should be the destination directory
* @param [useFileName] whether to use the inferred file name from `src`
*/
export async function downloadFile(src: string, dest: string, useFileName?:boolean) {
if (!(src.startsWith("http://") || src.startsWith("https://"))) {
throw new TypeError("URL must start with be http:// or https://");
}
const fileName = src.split('/').at(-1);
const resp = await fetch(src);
if (!resp.ok) {
throw new Deno.errors.BadResource(
`Request failed with status ${resp.status}`,
);
} else if (!resp.body) {
throw new Deno.errors.UnexpectedEof(
`The download url ${src} doesn't contain a file to download`,
);
} else if (resp.status === 404) {
throw new Deno.errors.NotFound(
`The requested url "${src}" could not be found`,
);
}
await ensureFile(useFileName ? dest + fileName : dest);
const file = await Deno.open(dest, { truncate: true, write: true });
resp.body.pipeTo(file.writable);
}

View File

@@ -1,3 +1,4 @@
// deno-lint-ignore-file no-fallthrough
import { MCGrizzConf } from "../types/mcgrizzconf.ts"; import { MCGrizzConf } from "../types/mcgrizzconf.ts";
import { NavItem } from "../types/nav.ts"; import { NavItem } from "../types/nav.ts";
import { makeConfFile } from "./confFile.ts"; import { makeConfFile } from "./confFile.ts";
@@ -13,16 +14,20 @@ export function getNavItems(): NavItem[] {
conf = makeConfFile(); conf = makeConfFile();
} }
const items: NavItem[] = [];
switch (conf.loader) { switch (conf.loader) {
case "unset": case "unset":
return [{ items.push({
title: "Setup", title: "Setup",
href: "/", href: "/",
}]; });
break;
case "forge": case "forge":
case "fabric": case "fabric":
items.push({ title: "Mods", href: "/mods" });
case "vanilla": case "vanilla":
return [ items.unshift(
{ {
title: "Server Terminal", title: "Server Terminal",
href: "/terminal", href: "/terminal",
@@ -35,6 +40,8 @@ export function getNavItems(): NavItem[] {
title: "Server Properties", title: "Server Properties",
href: "/properties", href: "/properties",
}, },
]; );
break;
} }
return items;
} }

View File

@@ -1,3 +1,4 @@
import { ensureFile } from "$std/fs/ensure_file.ts";
import { SERVER_STATE } from "../state/serverState.ts"; import { SERVER_STATE } from "../state/serverState.ts";
import { filterTruthy } from "./filters.ts"; import { filterTruthy } from "./filters.ts";
@@ -30,7 +31,7 @@ export const getPlayerData = async (username: string) => {
username = username.trim(); username = username.trim();
if (!username) return; if (!username) return;
const cacheFile = 'players.cache.json' const cacheFile = 'players.cache.json'
await Deno.create(cacheFile); await ensureFile(cacheFile);
const cache = JSON.parse(await Deno.readTextFile(cacheFile) || '{}'); const cache = JSON.parse(await Deno.readTextFile(cacheFile) || '{}');
if (!cache[username]) { if (!cache[username]) {
const req = await fetch('https://playerdb.co/api/player/minecraft/' + username, { const req = await fetch('https://playerdb.co/api/player/minecraft/' + username, {