Files
DragonCoreSSH-NewWEB/admin/assets/js/05-resellers.js
T
2026-07-13 02:21:56 -03:00

128 lines
5.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ─── Resellers ────────────────────────────────────────────────────────────────
document.getElementById("reloadResellersBtn").addEventListener("click", loadResellers);
document.getElementById("newResellerBtn").addEventListener("click", () => navigateWorkspaceSection("resellers", "create"));
document.getElementById("cancelResellerBtn").addEventListener("click", () => {
prepareNewReseller();
setWorkspaceSection("resellers", "users");
});
function prepareNewReseller() {
resellerFormTitle.textContent = "Create Reseller";
const heading = document.getElementById("resellerFormHeading");
if (heading) heading.textContent = t("Create reseller");
resellerForm.reset();
rActive.checked = true;
resellerStatus.textContent = "New reseller.";
requestAnimationFrame(() => rUsername.focus());
}
document.querySelector("[data-tab='resellers']")?.addEventListener("click", loadResellers);
async function loadResellers() {
resellerStatus.textContent = "Loading…";
try {
const res = await api("/api/resellers");
const data = await res.json();
renderResellers(data || []);
resellerStatus.textContent = "Loaded.";
} catch (e) {
if (e.message==="auth") doAuthError();
else resellerStatus.textContent = "Error loading.";
}
}
function renderResellers(list) {
resellerCountChip.textContent = list.length;
resellersBody.innerHTML = "";
list.forEach(r => {
const expired = r.expires_at && new Date(r.expires_at) < new Date();
const max = r.max_users || 0;
const used = r.used_users || 0;
const remaining = max ? Math.max(0, max - used) : "∞";
const pct = max ? Math.min(100, Math.round((used / max) * 100)) : 0;
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${escapeHTML(r.username)}</td>
<td>
<strong>${used} / ${max || "∞"}</strong>
<div class="hint">Disponível ${remaining} · SSH ${r.used_ssh_users || 0} · Xray ${r.used_xray_users || 0}</div>
<div class="table-meter"><span style="width:${pct}%"></span></div>
</td>
<td>${r.expires_at ? escapeHTML(fmtDate(r.expires_at)) : "—"}</td>
<td><span class="${r.is_active && !expired ? 'badge-on' : 'badge-off'}">${r.is_active && !expired ? "Active" : expired ? "Expired" : "Suspended"}</span></td>
<td></td>`;
const tdA = tr.lastElementChild;
const editBtn = Object.assign(document.createElement("button"),{
className:"btn btn-ghost btn-sm", textContent:t("Edit"),
onclick: () => fillResellerForm(r),
});
const delBtn = Object.assign(document.createElement("button"),{
className:"btn btn-danger btn-sm", textContent:t("Del"),
style: "margin-left:4px;",
onclick: () => deleteReseller(r.username),
});
tdA.append(editBtn, delBtn);
resellersBody.appendChild(tr);
});
}
function fillResellerForm(r) {
setWorkspaceSection("resellers", "create");
resellerFormTitle.textContent = `Edit: ${r.username}`;
const heading = document.getElementById("resellerFormHeading");
if (heading) heading.textContent = t("Edit reseller");
rUsername.value = r.username;
rPassword.value = "";
rMaxUsers.value = r.max_users || 0;
rExpires.value = r.expires_at ? localFromISO(r.expires_at) : "";
rActive.checked = r.is_active;
resellerStatus.textContent = `Editing ${r.username}.`;
}
resellerForm.addEventListener("submit", async e => {
e.preventDefault();
const btn = document.getElementById("saveResellerBtn");
btn.disabled = true;
resellerStatus.textContent = "Saving…";
const payload = {
username: rUsername.value.trim(),
password: rPassword.value || undefined,
max_users: parseInt(rMaxUsers.value||"0",10),
expires_at: isoFromLocal(rExpires.value),
is_active: rActive.checked,
};
try {
const res = await api("/api/resellers/create", { method:"POST", body: JSON.stringify(payload) });
if (!res.ok) throw new Error(await res.text());
resellerStatus.textContent = "Saved.";
resellerForm.reset(); rActive.checked = true;
resellerFormTitle.textContent = "Create Reseller";
loadResellers();
showPanelToast(t("Reseller saved successfully."), "success", t("Resellers"));
setWorkspaceSection("resellers", "users");
} catch (e) {
if (e.message==="auth") doAuthError();
else resellerStatus.textContent = "Error: "+e.message;
} finally { btn.disabled = false; }
});
async function deleteReseller(username) {
const accepted = await panelConfirm({
tone:"danger", icon:"×", title:t("Delete reseller"),
message:t("Delete reseller \"{name}\"?", {name:username}),
detail:t("Their owned access will be removed and active SSH sessions will be disconnected."),
confirmLabel:t("Delete reseller"),
});
if (!accepted) return;
resellerStatus.textContent = `Deleting ${username}…`;
try {
const res = await api(`/api/resellers/delete?username=${encodeURIComponent(username)}`, { method:"DELETE" });
if (!res.ok && res.status !== 204) throw new Error("failed");
resellerStatus.textContent = "Deleted.";
loadResellers();
} catch (e) {
if (e.message==="auth") doAuthError();
else resellerStatus.textContent = "Error deleting.";
}
}