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:
Aryan SinghandClaude Fable 5 committed 2026-07-10 11:07:07 -05:00
commit 279d137ce5
98 files changed
+9925

No files matched your search

+59
View File
@@ -0,0 +1,59 @@
import { inspectContainer, containerEnv } from "@/server/docker";
import { listOnlinePlayers } from "@/server/mc";
import { db } from "@/server/db";
import { env } from "@/server/env";
import { getWorldSizeBytes } from "@/server/sampler";
import { readWorldInfo } from "@/server/playerstats";
import { ok, handle } from "@/server/api";
export const dynamic = "force-dynamic";
export async function GET() {
return handle(async () => {
let container;
let dockerAvailable = true;
try {
container = await inspectContainer(env.mcContainer);
} catch {
dockerAvailable = false;
container = { exists: false, running: false, status: "unknown" as const };
}
let players: { online: number; max: number; names: string[] } = { online: 0, max: 0, names: [] };
let version: string | null = null;
let serverType: string | null = null;
if (container.running) {
try {
players = await listOnlinePlayers();
} catch {}
try {
const cenv = await containerEnv(env.mcContainer);
version = cenv["VERSION"] ?? null;
serverType = cenv["TYPE"] ?? null;
} catch {}
}
const latest = db()
.prepare("SELECT * FROM metrics ORDER BY ts DESC LIMIT 1")
.get() as Record<string, number | null> | undefined;
const pendingCount = (
db().prepare("SELECT COUNT(*) AS n FROM approved_players WHERE status = 'pending'").get() as { n: number }
).n;
return ok({
dockerAvailable,
container,
players,
version,
serverType,
metrics: latest ?? null,
pendingApprovals: pendingCount,
world: readWorldInfo(getWorldSizeBytes()),
uptimeMs:
container.running && "startedAt" in container && container.startedAt
? Date.now() - new Date(container.startedAt).getTime()
: null,
});
});
}