const { useState, useEffect, useCallback, useRef } = React; const TOKEN_KEY = "jimportal_token"; const getToken = () => window.sessionStorage.getItem(TOKEN_KEY); const setToken = (t) => window.sessionStorage.setItem(TOKEN_KEY, t); const clearToken = () => window.sessionStorage.removeItem(TOKEN_KEY); async function api(path, { method = "GET", body } = {}) { const headers = { "Content-Type": "application/json" }; const t = getToken(); if (t) headers["Authorization"] = `Bearer ${t}`; const res = await fetch(`/api/v1${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined, }); if (res.status === 401) { clearToken(); window.dispatchEvent(new Event("bp-unauth")); throw new Error("Session expired."); } const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data.detail || `Request failed (${res.status})`); return data; } function Cube({ size = 26 }) { return ( ); } function Toast({ msg }) { return msg ?
{msg}
: null; } const STATUS_LABEL = { creating: "creating", active: "active", suspended: "suspended", error: "error", deleted: "deleted" }; function StatusPill({ status }) { const cls = status === "active" ? "st-running" : status === "creating" ? "st-installing" : status === "error" ? "st-error" : "st-queued"; return {STATUS_LABEL[status] || status}; } /* ── Auth ── */ function AuthScreen({ onAuthed }) { const [mode, setMode] = useState("login"); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [err, setErr] = useState(""); const [busy, setBusy] = useState(false); const submit = async (e) => { e.preventDefault(); setErr(""); setBusy(true); try { const path = mode === "login" ? "/auth/login" : "/auth/register"; const { access_token } = await api(path, { method: "POST", body: { email, password } }); setToken(access_token); onAuthed(); } catch (e) { setErr(e.message); } finally { setBusy(false); } }; return (

jimshosting

{mode === "login" ? "Sign in" : "Create your free account"}

{err &&
{err}
}
setEmail(e.target.value)} />
setPassword(e.target.value)} />
{mode === "login" ? "New here? " : "Already have an account? "}
); } /* ── Create free server ── */ function CreateFree({ onClose, onCreated, notify }) { const [name, setName] = useState(""); const [types, setTypes] = useState(null); const [eggId, setEggId] = useState(null); const [version, setVersion] = useState(""); const [err, setErr] = useState(""); const [busy, setBusy] = useState(false); useEffect(() => { (async () => { try { const t = await api("/server-types"); setTypes(t); if (t.length) setEggId(t[0].egg_id); } catch (e) { setErr(e.message); setTypes([]); } })(); }, []); const submit = async (e) => { e.preventDefault(); setErr(""); setBusy(true); try { await api("/servers", { method: "POST", body: { name, egg_id: eggId, mc_version: version || null } }); notify("Free server is being created!"); onCreated(); onClose(); } catch (e) { setErr(e.message); } finally { setBusy(false); } }; const selected = types && types.find((t) => t.egg_id === eggId); return (
e.stopPropagation()}>

Create your free server

{err &&
{err}
}
{types === null ?
Loading types…
: types.length === 0 ?
No server types configured.
:
{types.map((t) => ( ))}
}
{selected && (

{selected.label}: {(selected.memory_mb / 1024).toFixed(selected.memory_mb % 1024 ? 1 : 0)} GB RAM, {(selected.disk_mb / 1024).toFixed(selected.disk_mb % 1024 ? 1 : 0)} GB disk, {selected.cpu_pct}% CPU. One free server at a time.

)}
setName(e.target.value)} placeholder="my-survival-world" required pattern="[A-Za-z0-9 ._\-]+" title="Letters, numbers, spaces, . _ -" />
this becomes your address, e.g. my-survival-world.mc.jimshosting.com
setVersion(e.target.value)} placeholder="latest" className="mono" />
leave blank for the newest. e.g. 1.21.4
); } /* ── Server card ── */ function ServerCard({ server, onDelete, onManage, notify }) { const connect = server.connect_address; const copy = () => { navigator.clipboard.writeText(connect).then(() => notify("Address copied.")); }; return (
{server.name}
{(server.memory_mb / 1024)}G
RAM
{(server.disk_mb / 1024)}G
Disk
{server.cpu_pct}%
CPU
{server.type_label || server.tier}
Type
{connect && server.status === "active" ? (
{connect}
) : server.status === "creating" ? (
Building your server… this takes a minute or two.
) : server.status === "error" ? (
{server.error_msg || "Something went wrong."}
) : null}
); } function EmptyState({ onCreate }) { return (

You don't have a server yet

Spin up your free Minecraft server — ready in a couple minutes.

); } /* ── Admin panel ── */ function AdminServers({ notify }) { const [rows, setRows] = useState(null); const load = useCallback(async () => { try { setRows(await api("/admin/servers")); } catch (e) { notify(e.message); } }, [notify]); useEffect(() => { load(); const t = setInterval(load, 5000); return () => clearInterval(t); }, [load]); if (rows === null) return
Loading…
; if (rows.length === 0) return
No servers yet.
; return (
{rows.map((s) => ( ))}
ServerOwnerStatusNode AllocationAddress
{s.name} {s.owner_email} {s.node_id ?? "—"} {s.allocation ?? "—"} {s.connect_address ?? "—"}
); } function AdminUsers({ me, notify }) { const [rows, setRows] = useState(null); const load = useCallback(async () => { try { setRows(await api("/admin/users")); } catch (e) { notify(e.message); } }, [notify]); useEffect(() => { load(); }, [load]); const patch = async (id, body) => { try { await api(`/admin/users/${id}`, { method: "PATCH", body }); notify("Updated."); load(); } catch (e) { notify(e.message); } }; const delUser = async (u) => { if (!window.confirm(`Delete ${u.email}? This removes their account AND destroys their servers.`)) return; try { await api(`/admin/users/${u.id}`, { method: "DELETE" }); notify("User deleted."); load(); } catch (e) { notify(e.message); } }; const setQuota = (u) => { const v = window.prompt(`Free-server limit for ${u.email}:`, u.max_free_servers); if (v === null) return; const n = parseInt(v, 10); if (Number.isNaN(n) || n < 0) { notify("Enter a non-negative number."); return; } patch(u.id, { max_free_servers: n }); }; if (rows === null) return
Loading…
; return (
{rows.map((u) => ( ))}
EmailServersLimitAdminActions
{u.email}{u.id === me.id && you} {u.active_servers} {u.max_free_servers} {u.is_admin ? admin : user}
); } function AdminPanel({ me, notify }) { const [sub, setSub] = useState("servers"); return (

Admin

Manage all servers and users.

{sub === "servers" ? : }
); } function Dashboard({ me, onLogout, reloadMe }) { const [tab, setTab] = useState("mine"); const [servers, setServers] = useState(null); const [showCreate, setShowCreate] = useState(false); const [manageServer, setManageServer] = useState(null); const [toast, setToast] = useState(""); const notify = useCallback((m) => { setToast(m); setTimeout(() => setToast(""), 2600); }, []); const load = useCallback(async () => { try { setServers(await api("/servers")); reloadMe(); } catch (e) { notify(e.message); } }, [notify, reloadMe]); useEffect(() => { if (tab === "mine") { load(); const t = setInterval(load, 5000); return () => clearInterval(t); } }, [load, tab]); const onDelete = async (server) => { if (!window.confirm(`Delete "${server.name}"? This permanently removes the server and its world.`)) return; try { await api(`/servers/${server.id}`, { method: "DELETE" }); notify("Server deleted."); load(); } catch (e) { notify(e.message); } }; const atLimit = me.active_servers >= me.max_free_servers; return (

jimshosting

{me.is_admin && }
{me.email} {me.is_admin && ADMIN}
{tab === "admin" && me.is_admin ? ( ) : (

Your server

Free tier · {me.active_servers}/{me.max_free_servers} used

{servers === null ?
Loading…
: servers.length === 0 ? setShowCreate(true)} /> :
{servers.map((s) => )}
}
)}
{showCreate && setShowCreate(false)} onCreated={load} notify={notify} />} {manageServer && setManageServer(null)} notify={notify} />}
); } function App() { const [me, setMe] = useState(undefined); const loadMe = useCallback(async () => { if (!getToken()) { setMe(null); return; } try { setMe(await api("/auth/me")); } catch { clearToken(); setMe(null); } }, []); useEffect(() => { loadMe(); const onUnauth = () => setMe(null); window.addEventListener("bp-unauth", onUnauth); return () => window.removeEventListener("bp-unauth", onUnauth); }, [loadMe]); const logout = () => { clearToken(); setMe(null); }; if (me === undefined) return
Loading…
; if (me === null) return ; return ; } /* Full server management view — tabbed, Pterodactyl-parity. Loaded after app.jsx; uses the same global `api`, `getToken`, helpers. */ function fmtBytes(mb) { if (mb == null) return "—"; if (mb >= 1024) return (mb / 1024).toFixed(1) + " GB"; return mb + " MB"; } function fmtSize(bytes) { if (bytes == null) return "—"; const u = ["B", "KB", "MB", "GB"]; let i = 0; let n = bytes; while (n >= 1024 && i < u.length - 1) { n /= 1024; i++; } return n.toFixed(i ? 1 : 0) + " " + u[i]; } /* ── Console tab (live) ── */ function ConsoleTab({ server, notify }) { const [lines, setLines] = useState([]); const [stats, setStats] = useState(null); const [state, setState] = useState("connecting"); const [cmd, setCmd] = useState(""); const [history, setHistory] = useState([]); const wsRef = useRef(null); const logRef = useRef(null); const append = useCallback((t) => setLines((p) => { const n = [...p, t]; return n.length > 800 ? n.slice(-800) : n; }), []); useEffect(() => { const proto = window.location.protocol === "https:" ? "wss:" : "ws:"; const url = `${proto}//${window.location.host}/api/v1/servers/${server.id}/console?token=${encodeURIComponent(getToken())}`; const ws = new WebSocket(url); wsRef.current = ws; ws.onmessage = (ev) => { let m; try { m = JSON.parse(ev.data); } catch { return; } const { event, args } = m; if (event === "auth success") { setState((s) => s === "connecting" ? "running" : s); ws.send(JSON.stringify({ event: "send logs", args: [null] })); ws.send(JSON.stringify({ event: "send stats", args: [null] })); } else if (event === "console output" && args) append(args[0]); else if (event === "status" && args) setState(args[0]); else if (event === "stats" && args) { try { const s = JSON.parse(args[0]); setStats({ cpu: s.cpu_absolute, mem: Math.round(s.memory_bytes / 1048576), disk: Math.round(s.disk_bytes / 1048576), state: s.state, netrx: s.network?.rx_bytes, nettx: s.network?.tx_bytes, uptime: s.uptime, }); setState(s.state); } catch {} } else if (event === "portal error" && args) { append(`[portal] ${args[0]}`); setState("error"); } }; ws.onclose = () => setState((s) => s === "error" ? s : "disconnected"); ws.onerror = () => setState("error"); return () => { try { ws.close(); } catch {} }; }, [server.id, append]); useEffect(() => { if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight; }, [lines]); const power = async (signal) => { try { await api(`/servers/${server.id}/power`, { method: "POST", body: { signal } }); notify(`Sent ${signal}.`); } catch (e) { notify(e.message); } }; const send = (e) => { e.preventDefault(); if (!cmd.trim()) return; const ws = wsRef.current; if (ws && ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ event: "send command", args: [cmd] })); setHistory((h) => [...h, cmd]); setCmd(""); }; const sc = state === "running" ? "on" : state === "error" ? "err" : "off"; return (
{state}
{stats && (
{stats.cpu?.toFixed(0) ?? 0}%
CPU
{fmtBytes(stats.mem)}
Memory
{fmtBytes(stats.disk)}
Disk
{stats.uptime ? Math.floor(stats.uptime / 60000) + "m" : "—"}
Uptime
)}
{lines.length === 0 ?
Connecting to console…
: lines.map((l, i) =>
{l}
)}
/ setCmd(e.target.value)} placeholder="type a command and press enter" autoComplete="off" spellCheck="false" />
); } /* ── Files tab ── */ function FilesTab({ server, notify }) { const [dir, setDir] = useState("/"); const [items, setItems] = useState(null); const [editing, setEditing] = useState(null); const [content, setContent] = useState(""); const load = useCallback(async () => { try { setItems(await api(`/servers/${server.id}/files?directory=${encodeURIComponent(dir)}`)); } catch (e) { notify(e.message); } }, [server.id, dir, notify]); useEffect(() => { load(); }, [load]); const open = async (f) => { const path = (dir === "/" ? "" : dir) + "/" + f.name; if (!f.is_file) { setDir(path); return; } if (f.size > 2 * 1024 * 1024) { notify("File too large to edit inline."); return; } try { const r = await api(`/servers/${server.id}/files/contents?file=${encodeURIComponent(path)}`); setContent(r.content); setEditing(path); } catch (e) { notify(e.message); } }; const save = async () => { try { await api(`/servers/${server.id}/files/write`, { method: "POST", body: { file: editing, content } }); notify("Saved."); setEditing(null); } catch (e) { notify(e.message); } }; const del = async (f) => { if (!window.confirm(`Delete ${f.name}?`)) return; try { await api(`/servers/${server.id}/files/delete`, { method: "POST", body: { root: dir, files: [f.name] } }); notify("Deleted."); load(); } catch (e) { notify(e.message); } }; const up = () => { if (dir === "/") return; setDir(dir.split("/").slice(0, -1).join("/") || "/"); }; if (editing !== null) { return (
{editing}