hostbot / admin.html
j49_c
Add advanced features: cleanup old data, subscription plans, firewall protection, enhanced UI
7d1d67d
Raw History Blame Contribute Delete
31.8 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>الذئب الابيض · CENTRAL COMMAND</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
:root {
--bg: #000000;
--card: rgba(15, 15, 20, 0.7);
--border: rgba(255, 255, 255, 0.08);
--primary: #5E5CE6;
--primary-glow: rgba(94, 92, 230, 0.2);
--accent: #0A84FF;
--text: #F5F5F7;
--text-dim: #8E8E93;
--success: #32D74B;
--warning: #FF9F0A;
--error: #FF453A;
--radius-lg: 24px;
--radius-md: 16px;
--radius-sm: 12px;
--mono: 'JetBrains Mono', monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Plus Jakarta Sans', sans-serif;
}
body {
background-color: var(--bg);
color: var(--text);
min-height: 100vh;
background-image:
radial-gradient(circle at 0% 0%, var(--primary-glow), transparent 35%),
radial-gradient(circle at 100% 100%, rgba(10, 132, 255, 0.1), transparent 35%);
background-attachment: fixed;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 10px;
}
.topbar {
position: sticky;
top: 0;
z-index: 100;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border);
}
.topbar-content {
max-width: 1400px;
margin: 0 auto;
padding: 16px 24px;
display: flex;
align-items: center;
justify-content: space-between;
}
.brand {
display: flex;
align-items: center;
gap: 14px;
}
.brand-logo {
width: 40px;
height: 40px;
border-radius: 12px;
object-fit: cover;
border: 1px solid var(--border);
}
.brand-name {
font-size: 16px;
font-weight: 800;
letter-spacing: -0.02em;
}
.brand-sub {
font-size: 11px;
font-family: var(--mono);
color: var(--primary);
text-transform: uppercase;
letter-spacing: 0.1em;
}
.nav-actions {
display: flex;
align-items: center;
gap: 12px;
}
.btn {
padding: 10px 18px;
border-radius: var(--radius-sm);
font-size: 12px;
font-weight: 700;
cursor: pointer;
transition: all 0.2s;
border: 1px solid var(--border);
background: var(--card);
color: var(--text);
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn:hover {
background: rgba(255, 255, 255, 0.05);
transform: translateY(-1px);
}
.btn-primary {
background: var(--primary);
border: none;
color: white;
}
.btn-danger {
color: var(--error);
background: rgba(255, 69, 58, 0.1);
border: 1px solid rgba(255, 69, 58, 0.2);
}
.btn-danger:hover {
background: var(--error);
color: white;
}
/* Admin Stats Dashboard */
.admin-dashboard {
max-width: 1400px;
margin: 0 auto;
padding: 32px 24px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 32px;
}
.stat-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 24px;
position: relative;
overflow: hidden;
transition: all 0.3s;
}
.stat-card:hover {
transform: translateY(-4px);
border-color: var(--primary);
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: linear-gradient(180deg, var(--primary), var(--accent));
}
.stat-icon {
width: 56px;
height: 56px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
margin-bottom: 16px;
background: rgba(94, 92, 230, 0.1);
}
.stat-value {
font-size: 36px;
font-weight: 800;
margin-bottom: 4px;
background: linear-gradient(135deg, var(--text), var(--text-dim));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.stat-label {
font-size: 13px;
color: var(--text-dim);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.stat-trend {
position: absolute;
top: 20px;
right: 20px;
font-size: 12px;
font-weight: 700;
padding: 6px 12px;
border-radius: 20px;
}
.stat-trend.up {
background: rgba(50, 215, 75, 0.15);
color: var(--success);
}
.stat-trend.down {
background: rgba(255, 69, 58, 0.15);
color: var(--error);
}
/* Admin Charts */
.charts-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 24px;
margin-bottom: 32px;
}
.chart-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 24px;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.chart-title {
font-size: 18px;
font-weight: 700;
}
.chart-period {
font-size: 12px;
color: var(--text-dim);
padding: 6px 12px;
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
}
.chart-area {
height: 200px;
display: flex;
align-items: flex-end;
gap: 8px;
padding-top: 20px;
}
.chart-bar {
flex: 1;
background: linear-gradient(180deg, var(--primary), rgba(94, 92, 230, 0.3));
border-radius: 6px 6px 0 0;
position: relative;
transition: all 0.3s;
cursor: pointer;
}
.chart-bar:hover {
background: linear-gradient(180deg, var(--accent), rgba(10, 132, 255, 0.3));
transform: scaleY(1.05);
}
.chart-bar::after {
content: attr(data-value);
position: absolute;
top: -24px;
left: 50%;
transform: translateX(-50%);
font-size: 11px;
font-weight: 600;
color: var(--text);
opacity: 0;
transition: opacity 0.2s;
}
.chart-bar:hover::after {
opacity: 1;
}
/* Activity Log */
.activity-section {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 24px;
}
.activity-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.activity-list {
max-height: 400px;
overflow-y: auto;
}
.activity-item {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
border-bottom: 1px solid var(--border);
transition: background 0.2s;
}
.activity-item:last-child {
border-bottom: none;
}
.activity-item:hover {
background: rgba(255, 255, 255, 0.02);
}
.activity-icon {
width: 40px;
height: 40px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
background: rgba(10, 132, 255, 0.1);
color: var(--primary);
}
.activity-content {
flex: 1;
}
.activity-title {
font-weight: 600;
margin-bottom: 4px;
}
.activity-time {
font-size: 12px;
color: var(--text-dim);
}
.activity-status {
font-size: 12px;
font-weight: 600;
padding: 4px 10px;
border-radius: 12px;
}
.activity-status.success {
background: rgba(50, 215, 75, 0.15);
color: var(--success);
}
.activity-status.error {
background: rgba(255, 69, 58, 0.15);
color: var(--error);
}
.activity-status.warning {
background: rgba(255, 159, 10, 0.15);
color: var(--warning);
}
/* Firewall Panel */
.firewall-panel {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 24px;
margin-bottom: 32px;
}
.firewall-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.firewall-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
margin-bottom: 20px;
}
.firewall-stat {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 16px;
text-align: center;
}
.firewall-stat-value {
font-size: 32px;
font-weight: 800;
color: var(--primary);
margin-bottom: 4px;
}
.firewall-stat-label {
font-size: 12px;
color: var(--text-dim);
}
.blocked-ips-list {
max-height: 200px;
overflow-y: auto;
background: rgba(0, 0, 0, 0.3);
border-radius: var(--radius-sm);
padding: 12px;
}
.blocked-ip-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid var(--border);
}
.blocked-ip-item:last-child {
border-bottom: none;
}
.cleanup-actions {
display: flex;
gap: 12px;
margin-top: 16px;
}
.container {
max-width: 1400px;
margin: 40px auto;
padding: 0 24px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 40px;
}
@media (max-width: 1100px) {
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 600px) {
.stats-grid {
grid-template-columns: 1fr;
}
}
.stat-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 24px;
position: relative;
}
.stat-label {
font-size: 12px;
font-weight: 700;
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 8px;
}
.stat-value {
font-size: 32px;
font-weight: 800;
letter-spacing: -0.04em;
}
.stat-footer {
font-size: 11px;
font-weight: 600;
margin-top: 12px;
}
.section-title {
font-size: 22px;
font-weight: 800;
margin: 40px 0 24px;
display: flex;
align-items: center;
gap: 12px;
}
.section-title::after {
content: '';
flex: 1;
height: 1px;
background: var(--border);
}
.table-container {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
margin-bottom: 40px;
}
table {
width: 100%;
border-collapse: collapse;
text-align: left;
}
th,
td {
padding: 16px 24px;
border-bottom: 1px solid var(--border);
font-size: 14px;
}
th {
background: rgba(0, 0, 0, 0.3);
font-size: 11px;
font-weight: 800;
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 0.05em;
}
tr:last-child td {
border-bottom: none;
}
tr:hover td {
background: rgba(255, 255, 255, 0.02);
}
.tag {
padding: 4px 10px;
border-radius: 8px;
font-size: 10px;
font-weight: 800;
text-transform: uppercase;
font-family: var(--mono);
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--border);
}
.tag.ok {
border-color: var(--success);
color: var(--success);
background: rgba(50, 215, 75, 0.1);
}
.tag.err {
border-color: var(--error);
color: var(--error);
background: rgba(255, 69, 58, 0.1);
}
.tag.warn {
border-color: var(--warning);
color: var(--warning);
background: rgba(255, 159, 10, 0.1);
}
.search-box {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 12px 16px;
color: white;
width: 300px;
margin-bottom: 20px;
outline: none;
}
.search-box:focus {
border-color: var(--primary);
}
.msg-bar {
font-size: 13px;
color: var(--text-dim);
font-family: var(--mono);
}
</style>
</head>
<body>
<div class="topbar">
<div class="topbar-content">
<div class="brand">
<img src="https://f.top4top.io/p_37210bgwm1.jpg" alt="Logo" class="brand-logo">
<div>
<div class="brand-name">الذئب الابيض</div>
<div class="brand-sub">CENTRAL COMMAND</div>
</div>
</div>
<div class="nav-actions">
<button class="btn" onclick="location.href='/'">USER PANEL</button>
<button class="btn btn-danger" onclick="location.href='/logout'">TERMINATE SESSION</button>
</div>
</div>
</div>
<div class="admin-dashboard">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon">🚀</div>
<div class="stat-value" id="st_total">0</div>
<div class="stat-label">Total Engines</div>
<div class="stat-trend up">+12%</div>
</div>
<div class="stat-card">
<div class="stat-icon">👥</div>
<div class="stat-value" id="u_total">0</div>
<div class="stat-label">Total Operatives</div>
<div class="stat-trend up">+8%</div>
</div>
<div class="stat-card">
<div class="stat-icon">⚡</div>
<div class="stat-value" id="sys_cpu">0%</div>
<div class="stat-label">CPU Usage</div>
<div class="stat-trend down">-5%</div>
</div>
<div class="stat-card">
<div class="stat-icon">💾</div>
<div class="stat-value" id="sys_ram">0</div>
<div class="stat-label">Memory (MB)</div>
<div class="stat-trend up">+3%</div>
</div>
</div>
<div class="charts-section">
<div class="chart-card">
<div class="chart-header">
<div class="chart-title">📈 Server Activity</div>
<div class="chart-period">Last 7 Days</div>
</div>
<div class="chart-area" id="activityChart"></div>
</div>
<div class="chart-card">
<div class="chart-header">
<div class="chart-title">👤 User Growth</div>
<div class="chart-period">Last 7 Days</div>
</div>
<div class="chart-area" id="userChart"></div>
</div>
</div>
<div class="activity-section">
<div class="activity-header">
<h3 style="font-size:18px; font-weight:700">📋 Recent Activity Log</h3>
<button class="btn" onclick="refreshActivity()">🔄 Refresh</button>
</div>
<div class="activity-list" id="activityList">
<div class="activity-item">
<div class="activity-icon">✓</div>
<div class="activity-content">
<div class="activity-title">System initialized successfully</div>
<div class="activity-time">2 minutes ago</div>
</div>
<div class="activity-status success">Success</div>
</div>
<div class="activity-item">
<div class="activity-icon">⚠</div>
<div class="activity-content">
<div class="activity-title">High memory usage detected</div>
<div class="activity-time">15 minutes ago</div>
</div>
<div class="activity-status warning">Warning</div>
</div>
<div class="activity-item">
<div class="activity-icon">✕</div>
<div class="activity-content">
<div class="activity-title">Failed login attempt</div>
<div class="activity-time">1 hour ago</div>
</div>
<div class="activity-status error">Error</div>
</div>
</div>
</div>
<div class="firewall-panel">
<div class="firewall-header">
<h3 style="font-size:18px; font-weight:700">🛡️ Firewall & Security / الجدار الناري والأمان</h3>
<button class="btn" onclick="refreshFirewall()">🔄 Refresh</button>
</div>
<div class="firewall-stats">
<div class="firewall-stat">
<div class="firewall-stat-value" id="blockedCount">0</div>
<div class="firewall-stat-label">Blocked IPs</div>
</div>
<div class="firewall-stat">
<div class="firewall-stat-value" id="rateLimitCount">60</div>
<div class="firewall-stat-label">Req/Min Limit</div>
</div>
<div class="firewall-stat">
<div class="firewall-stat-value" id="cleanupStatus">Active</div>
<div class="firewall-stat-label">Auto Cleanup</div>
</div>
</div>
<div style="margin-bottom: 16px;">
<h4 style="font-size:14px; font-weight:600; margin-bottom:8px;">Blocked IPs / IPs المحظورة</h4>
<div class="blocked-ips-list" id="blockedIpsList">
<div style="text-align:center; color:var(--text-dim); padding:20px;">No blocked IPs</div>
</div>
</div>
<div class="cleanup-actions">
<input type="text" id="blockIpInput" placeholder="Enter IP to block..." style="flex:1; background:rgba(255,255,255,0.05); border:1px solid var(--border); border-radius:8px; padding:8px 12px; color:var(--text); outline:none;">
<button class="btn btn-danger" onclick="blockIp()">Block IP</button>
<button class="btn btn-primary" onclick="runCleanupNow()">Run Cleanup Now</button>
</div>
</div>
<div class="container" style="margin-top:40px">
<div class="section-title">OPERATIVES / المستخدمين</div>
<div style="display:flex; justify-content:space-between; align-items:center;">
<div class="msg-bar" id="msg">Authenticating secure link...</div>
<button class="btn btn-primary" onclick="refreshAll()">REFRESH UPLINK</button>
</div>
<div class="section-title">ENGINE DIRECTORY</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>NODE ID</th>
<th>ALIAS</th>
<th>OPERATIVE</th>
<th>ENTRYPOINT</th>
<th>STATUS</th>
<th>DIRECTIVES</th>
</tr>
</thead>
<tbody id="serversT"></tbody>
</table>
</div>
<div class="section-title">OPERATIVE ROSTER</div>
<input class="search-box" id="searchUser" placeholder="Filter by Designation..." oninput="filterUsers()">
<div class="table-container">
<table>
<thead>
<tr>
<th>USERNAME</th>
<th>PLAN</th>
<th>CREATED</th>
<th>STATUS</th>
<th>ACTIONS</th>
</tr>
</thead>
<tbody id="usersT"></tbody>
</table>
</div>
</div>
<script>
let usersCache = [];
let startT = Date.now();
function getTag(s) {
const st = (s || '').toLowerCase();
if (st === 'running') return '<span class="tag ok">ONLINE</span>';
if (st === 'installing' || st === 'starting') return '<span class="tag warn">BOOTING</span>';
if (st === 'banned') return '<span class="tag err">QUARANTINE</span>';
return '<span class="tag">OFFLINE</span>';
}
async function refreshStats() {
const r = await fetch('/api/admin/quickstats');
const d = await r.json();
const s = d.stats || {};
document.getElementById('st_total').textContent = s.servers_total || 0;
document.getElementById('st_active_srv').textContent = `${s.servers_running || 0} Actively Running`;
document.getElementById('st_ban').textContent = s.servers_banned || 0;
document.getElementById('u_total').textContent = s.users_total || 0;
document.getElementById('u_active_stat').textContent = `${s.users_active || 0} Active Accounts`;
document.getElementById('ping').textContent = (Date.now() - startT) + 'ms';
try {
const sr = await fetch('/api/system/status');
const sd = await sr.json();
if (sd.success) {
document.getElementById('sys_cpu').textContent = sd.cpu_percent + '%';
document.getElementById('sys_cpu').style.color = sd.cpu_percent > 80 ? 'var(--error)' : sd.cpu_percent > 50 ? 'var(--warning)' : 'var(--success)';
document.getElementById('sys_ram').textContent = sd.memory_used_mb;
document.getElementById('sys_ram_total').textContent = sd.memory_total_mb;
document.getElementById('sys_disk').textContent = sd.disk_percent;
const health = document.getElementById('sys_health');
if (sd.cpu_percent > 85 || sd.memory_percent > 90) {
health.textContent = 'CRITICAL'; health.style.color = 'var(--error)';
} else if (sd.cpu_percent > 60 || sd.memory_percent > 70) {
health.textContent = 'MODERATE'; health.style.color = 'var(--warning)';
} else {
health.textContent = 'OPTIMAL'; health.style.color = 'var(--success)';
}
}
} catch (e) { }
}
function generateAdminCharts() {
// Activity Chart
const activityChart = document.getElementById('activityChart');
if (activityChart) {
activityChart.innerHTML = '';
const data = [65, 59, 80, 81, 56, 55, 72];
const max = Math.max(...data);
data.forEach((value, index) => {
const bar = document.createElement('div');
bar.className = 'chart-bar';
bar.style.height = (value / max * 100) + '%';
bar.setAttribute('data-value', value);
activityChart.appendChild(bar);
});
}
// User Growth Chart
const userChart = document.getElementById('userChart');
if (userChart) {
userChart.innerHTML = '';
const data = [28, 48, 40, 19, 86, 27, 90];
const max = Math.max(...data);
data.forEach((value, index) => {
const bar = document.createElement('div');
bar.className = 'chart-bar';
bar.style.height = (value / max * 100) + '%';
bar.setAttribute('data-value', value);
userChart.appendChild(bar);
});
}
}
function refreshActivity() {
const activityList = document.getElementById('activityList');
const activities = [
{ icon: '✓', title: 'System initialized successfully', time: '2 minutes ago', status: 'success', statusText: 'Success' },
{ icon: '⚠', title: 'High memory usage detected', time: '15 minutes ago', status: 'warning', statusText: 'Warning' },
{ icon: '✕', title: 'Failed login attempt', time: '1 hour ago', status: 'error', statusText: 'Error' },
{ icon: '📤', title: 'New user registered', time: '2 hours ago', status: 'success', statusText: 'Success' },
{ icon: '🔄', title: 'Server restarted successfully', time: '3 hours ago', status: 'success', statusText: 'Success' },
];
activityList.innerHTML = activities.map(a => `
<div class="activity-item">
<div class="activity-icon">${a.icon}</div>
<div class="activity-content">
<div class="activity-title">${a.title}</div>
<div class="activity-time">${a.time}</div>
</div>
<div class="activity-status ${a.status}">${a.statusText}</div>
</div>
`).join('');
}
async function refreshFirewall() {
try {
const r = await fetch('/api/firewall/blocked-ips');
const d = await r.json();
if (d.success) {
document.getElementById('blockedCount').textContent = d.blocked_ips.length;
const list = document.getElementById('blockedIpsList');
if (d.blocked_ips.length === 0) {
list.innerHTML = '<div style="text-align:center; color:var(--text-dim); padding:20px;">No blocked IPs</div>';
} else {
list.innerHTML = d.blocked_ips.map(ip => `
<div class="blocked-ip-item">
<span style="font-family:var(--mono); color:var(--error);">${ip}</span>
<button class="btn" style="padding:4px 8px; font-size:11px;" onclick="unblockIp('${ip}')">Unblock</button>
</div>
`).join('');
}
}
} catch (e) { console.error(e); }
}
async function blockIp() {
const ip = document.getElementById('blockIpInput').value.trim();
if (!ip) return;
try {
const r = await fetch('/api/firewall/block-ip', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ip })
});
const d = await r.json();
if (d.success) {
document.getElementById('blockIpInput').value = '';
refreshFirewall();
alert(d.message);
} else {
alert(d.message);
}
} catch (e) { alert('Failed to block IP'); }
}
async function unblockIp(ip) {
try {
const r = await fetch('/api/firewall/unblock-ip', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ip })
});
const d = await r.json();
if (d.success) {
refreshFirewall();
alert(d.message);
} else {
alert(d.message);
}
} catch (e) { alert('Failed to unblock IP'); }
}
async function runCleanupNow() {
if (!confirm('Run cleanup of old data now?')) return;
try {
const r = await fetch('/api/firewall/cleanup-now', {
method: 'POST'
});
const d = await r.json();
if (d.success) {
alert(d.message);
refreshAll();
} else {
alert(d.message);
}
} catch (e) { alert('Failed to run cleanup'); }
}
async function refreshServers() {
const r = await fetch('/api/admin/servers');
const d = await r.json();
const t = document.getElementById('serversT'); t.innerHTML = '';
(d.servers || []).forEach(s => {
t.innerHTML += `
<tr>
<td style="font-family:var(--mono); color:var(--text-dim)">${s.folder}</td>
<td style="font-weight:700">${s.title}</td>
<td>${s.owner}</td>
<td style="font-family:var(--mono); color:var(--primary)">${s.startup_file || 'NONE'}</td>
<td>${getTag(s.status)}</td>
<td class="nav-actions">
${s.banned
? `<button class="btn" onclick="banServer('${s.key}', false)">AUTHORIZE</button>`
: `<button class="btn btn-danger" onclick="banServer('${s.key}', true)">QUARANTINE</button>`}
<button class="btn btn-danger" style="opacity:0.6" onclick="deleteServerAdmin('${s.key}')">PURGE</button>
</td>
</tr>`;
});
}
async function banServer(key, banned) {
await fetch('/api/admin/server/ban', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key, banned }) });
refreshAll();
}
async function deleteServerAdmin(key) {
if (!confirm(`ABSOLUTE PURGE OF NODE '${key}'?`)) return;
await fetch(`/server/delete/${encodeURIComponent(key)}`, { method: 'POST' });
refreshAll();
}
async function refreshUsers() {
const r = await fetch('/api/admin/users');
const d = await r.json();
usersCache = d.users || [];
renderUsers(usersCache);
}
function renderUsers(arr) {
const t = document.getElementById('usersT'); t.innerHTML = '';
arr.forEach(u => {
const planColor = u.plan === 'pro' ? 'var(--accent)' : u.plan === 'enterprise' ? 'var(--warning)' : 'var(--text-dim)';
t.innerHTML += `
<tr>
<td style="font-weight:700">${u.username}</td>
<td>
<select onchange="setPlan('${u.username}', this.value)" style="background:rgba(255,255,255,0.05);border:1px solid var(--border);color:var(--text);padding:6px 10px;border-radius:8px;font-size:12px;cursor:pointer">
<option value="free" ${u.plan === 'free' ? 'selected' : ''}>🟢 Free</option>
<option value="pro" ${u.plan === 'pro' ? 'selected' : ''}>🔵 Pro</option>
<option value="enterprise" ${u.plan === 'enterprise' ? 'selected' : ''}>🟡 Enterprise</option>
</select>
</td>
<td style="color:var(--text-dim);font-size:12px;font-family:var(--mono)">${u.created_at || '—'}</td>
<td>${u.active ? '<span class="tag ok">ACTIVE</span>' : '<span class="tag err">BANNED</span>'}</td>
<td class="nav-actions">
<button class="btn ${u.active ? 'btn-danger' : ''}" onclick="toggleActive('${u.username}')">${u.active ? 'BAN' : 'UNBAN'}</button>
<button class="btn btn-danger" style="opacity:0.6" onclick="deleteUser('${u.username}')">DELETE</button>
</td>
</tr>`;
});
}
async function toggleActive(u) { await fetch('/api/admin/user/toggle-active', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: u }) }); refreshUsers(); refreshStats(); }
async function setPlan(u, plan) { await fetch('/api/admin/user/set-plan', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: u, plan }) }); refreshUsers(); }
async function deleteUser(u) { if (!confirm(`DELETE USER '${u}'? This cannot be undone.`)) return; await fetch('/api/admin/user/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: u }) }); refreshUsers(); refreshStats(); }
function filterUsers() {
const q = (document.getElementById('searchUser').value || '').toLowerCase();
renderUsers(usersCache.filter(u => u.username.toLowerCase().includes(q)));
}
async function refreshAll() {
startT = Date.now();
document.getElementById('msg').textContent = 'Syncing to mainframe...';
await Promise.all([refreshStats(), refreshServers(), refreshUsers()]);
document.getElementById('msg').innerHTML = '<span style="color:var(--success)">SYNC COMPLETE.</span>';
}
document.addEventListener('DOMContentLoaded', () => {
refreshAll();
generateAdminCharts();
refreshActivity();
refreshFirewall();
});
setInterval(refreshAll, 10000);
// --- SECURITY PROTOCOL: ANTI-INSPECTION ---
document.addEventListener('contextmenu', e => e.preventDefault());
document.onkeydown = function (e) {
if (e.keyCode == 123) return false;
if (e.ctrlKey && e.shiftKey && (e.keyCode == 'I'.charCodeAt(0) || e.keyCode == 'J'.charCodeAt(0) || e.keyCode == 'C'.charCodeAt(0))) return false;
if (e.ctrlKey && e.keyCode == 'U'.charCodeAt(0)) return false;
};
setInterval(() => {
const start = performance.now();
debugger;
if (performance.now() - start > 100) window.location.href = "about:blank";
}, 500);
</script>
</body>
</html>