Minecraft 26.2 Fabric server stack with CraftDeck management panel
- docker-compose: itzg/minecraft-server (Fabric, offline mode, whitelist enforced, Modrinth-managed mods), itzg/mc-backup sidecar, custom dashboard - Gated auth for offline mode: approve username -> whitelisted with a short registration window -> EasyAuth /register + /login; auto-revoke on expiry - CraftDeck panel (Next.js 16, zero native deps): live console (SSE), players & stats, lag diagnostics (server TPS vs player ping via spark), performance graphs, Modrinth mod search/manager, backups & restore, sandboxed file browser/editor, .env config editor with compose apply, cron schedules, Discord alerts, scrypt-hashed panel login - scripts/setup.sh generates secrets; docs/ covers auth, mods, updating, backups, networking (Tailscale), troubleshooting Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
commit
279d137ce5
98 files changed
+9925
No files matched your search
@@ -0,0 +1,41 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { readWorldInfo } from "@/server/playerstats";
|
||||
import { getWorldSizeBytes } from "@/server/sampler";
|
||||
import { inspectContainer, powerContainer } from "@/server/docker";
|
||||
import { safetyBackup } from "@/server/backups";
|
||||
import { logActivity } from "@/server/db";
|
||||
import { notify } from "@/server/notify";
|
||||
import { env } from "@/server/env";
|
||||
import { ok, fail, readJson, handle } from "@/server/api";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function GET() {
|
||||
return handle(async () => ok(readWorldInfo(getWorldSizeBytes())));
|
||||
}
|
||||
|
||||
/** Danger zone: archive the current world and generate a fresh one. */
|
||||
export async function POST(req: NextRequest) {
|
||||
return handle(async () => {
|
||||
const { op, confirm } = await readJson<{ op?: string; confirm?: string }>(req);
|
||||
if (op !== "reset") return fail("Unknown operation");
|
||||
if (confirm !== "reset the world") return fail('Type "reset the world" to confirm');
|
||||
|
||||
const worldDir = path.join(env.mcDataDir, "world");
|
||||
if (!fs.existsSync(worldDir)) return fail("No world folder found");
|
||||
|
||||
await safetyBackup("world reset");
|
||||
const state = await inspectContainer(env.mcContainer);
|
||||
if (state.running) await powerContainer(env.mcContainer, "stop");
|
||||
|
||||
const archived = `world.old-${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
||||
fs.renameSync(worldDir, path.join(env.mcDataDir, archived));
|
||||
|
||||
await powerContainer(env.mcContainer, "start");
|
||||
logActivity("world_reset", null, `old world kept as ${archived}`);
|
||||
notify("warn", "World reset", `A fresh world is generating. Old world kept as ${archived}.`);
|
||||
return ok({ archived });
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user