a1331212cb
Self-hosted Minecraft modpack distribution + administration system.
- launcher/ Avalonia 12 desktop client; single-file win-x64 publish.
Microsoft auth via XboxAuthNet, manifest+SHA-1 mod sync,
portable install path, sidecar settings.
- server/ brass-sigil-server daemon (.NET 8, linux-x64). Wraps the
MC subprocess, embedded Kestrel admin panel with cookie
auth + rate limiting, RCON bridge, scheduled backups,
BlueMap CLI integration with player markers + skin proxy,
friend-side whitelist request flow, world wipe with seed
selection (keep current / random / custom).
- pack/ pack.lock.json (Modrinth + manual CurseForge entries),
data-only tweak source under tweaks/, build outputs in
overrides/ (gitignored).
- scripts/ Build-Pack / Build-Tweaks / Update-Pack / Check-Updates
plus Deploy-Brass.ps1 unified one-shot deploy with
version-bump pre-flight and daemon-state detection.
87 lines
3.5 KiB
JavaScript
87 lines
3.5 KiB
JavaScript
// Whitelist add / remove via the API; refreshes the panel display shortly after
|
|
// each action (server takes ~1-2 s to look up UUID via Mojang and write whitelist.json).
|
|
"use strict";
|
|
|
|
import { api, apiJson, escapeHtml } from "./api.js";
|
|
|
|
export function setupWhitelistActions(refreshSoon) {
|
|
const wlInput = document.getElementById("wlInput");
|
|
document.getElementById("wlAdd").addEventListener("click", () => addWhitelisted(refreshSoon));
|
|
wlInput.addEventListener("keydown", e => { if (e.key === "Enter") addWhitelisted(refreshSoon); });
|
|
|
|
// Delegated removal -- list items are re-rendered each tick, no static binding.
|
|
document.getElementById("whitelist").addEventListener("click", async e => {
|
|
const btn = e.target.closest(".wl-remove");
|
|
if (!btn) return;
|
|
const name = btn.dataset.name;
|
|
if (!name) return;
|
|
if (!confirm(`Remove ${name} from whitelist?`)) return;
|
|
try {
|
|
await apiJson("/api/whitelist/remove", { name });
|
|
refreshSoon();
|
|
} catch (err) { alert(err.message); }
|
|
});
|
|
|
|
// Pending whitelist requests from friends. Approve adds to whitelist + clears
|
|
// the request; Deny just marks denied so the friend's launcher knows.
|
|
const reqsList = document.getElementById("wlRequests");
|
|
const reqsBlock = document.getElementById("wlRequestsBlock");
|
|
const reqsBadge = document.getElementById("wlReqBadge");
|
|
|
|
reqsList?.addEventListener("click", async e => {
|
|
const btn = e.target.closest("button[data-req-action]");
|
|
if (!btn) return;
|
|
const name = btn.dataset.name;
|
|
const action = btn.dataset.reqAction; // "approve" | "deny"
|
|
if (!name || !action) return;
|
|
if (action === "deny" && !confirm(`Deny ${name}'s request?`)) return;
|
|
try {
|
|
await apiJson(`/api/whitelist/requests/${action}`, { name });
|
|
await refreshRequests();
|
|
// Approving fires /whitelist add via stdin -- let the server-side write
|
|
// ~1-2 s of grace before re-reading whitelist.json.
|
|
if (action === "approve") refreshSoon();
|
|
} catch (err) { alert(err.message); }
|
|
});
|
|
|
|
async function refreshRequests() {
|
|
if (!reqsList || !reqsBlock || !reqsBadge) return;
|
|
let data;
|
|
try { data = await api("/api/whitelist/requests"); }
|
|
catch { return; }
|
|
const reqs = data.requests || [];
|
|
if (reqs.length === 0) {
|
|
reqsBlock.hidden = true;
|
|
reqsBadge.hidden = true;
|
|
return;
|
|
}
|
|
reqsBlock.hidden = false;
|
|
reqsBadge.hidden = false;
|
|
reqsBadge.textContent = String(reqs.length);
|
|
reqsList.innerHTML = reqs.map(r => `
|
|
<li>
|
|
<div class="wl-req-meta">${escapeHtml(r.username)}</div>
|
|
${r.message ? `<div class="wl-req-msg">"${escapeHtml(r.message)}"</div>` : ""}
|
|
<div class="wl-req-actions">
|
|
<button data-req-action="approve" data-name="${escapeHtml(r.username)}">Approve</button>
|
|
<button class="ghost-btn" data-req-action="deny" data-name="${escapeHtml(r.username)}">Deny</button>
|
|
</div>
|
|
</li>
|
|
`).join("");
|
|
}
|
|
|
|
refreshRequests();
|
|
setInterval(refreshRequests, 15000);
|
|
}
|
|
|
|
async function addWhitelisted(refreshSoon) {
|
|
const inp = document.getElementById("wlInput");
|
|
const name = inp.value.trim();
|
|
if (!name) return;
|
|
try {
|
|
await apiJson("/api/whitelist/add", { name });
|
|
inp.value = "";
|
|
refreshSoon();
|
|
} catch (e) { alert(e.message); }
|
|
}
|