1586 lines
121 KiB
HTML
1586 lines
121 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en" class="i18n-pending">
|
||
<head>
|
||
<meta charset="UTF-8"/>
|
||
<title>DragonCore Command</title>
|
||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||
<style>html.i18n-pending body{visibility:hidden}</style>
|
||
<script>
|
||
(function(){
|
||
try{
|
||
var saved=localStorage.getItem("PANEL_LANG");
|
||
var langs=(navigator.languages&&navigator.languages.length?navigator.languages:[navigator.language||""]).join(" ").toLowerCase();
|
||
var lang=saved || (langs.indexOf("pt")!==-1 ? "pt-BR" : "en-US");
|
||
document.documentElement.lang=lang.toLowerCase();
|
||
}catch(e){}
|
||
setTimeout(function(){document.documentElement.classList.remove("i18n-pending");},2500);
|
||
})();
|
||
</script>
|
||
<link rel="stylesheet" href="assets/app.css?v=20260720xraylist1"/>
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
<div class="shell">
|
||
|
||
<!-- Login overlay -->
|
||
<div class="overlay" id="loginOverlay">
|
||
<div class="overlay-inner">
|
||
<div class="ov-title">DragonCore Command</div>
|
||
<div class="ov-sub">Access the private control node with admin or reseller credentials.</div>
|
||
<input id="loginUser" class="ov-field" placeholder="Username" autocomplete="username"/>
|
||
<input id="loginPass" class="ov-field" type="password" placeholder="Password" autocomplete="current-password"/>
|
||
<button class="btn" id="loginBtn" style="width:100%;margin-top:6px;justify-content:center;">Sign in</button>
|
||
<div id="loginErr" style="margin-top:6px;font-size:.72rem;color:var(--danger);"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Main app -->
|
||
<div id="mainApp" class="hidden">
|
||
<div class="panel-layout">
|
||
<aside class="sidebar" id="sidebar">
|
||
<div class="brand-block">
|
||
<div class="brand-mark">DC</div>
|
||
<div class="brand-copy">
|
||
<strong>DragonCore</strong>
|
||
<span>Command Node</span>
|
||
</div>
|
||
</div>
|
||
<nav id="mainNav" class="side-nav">
|
||
<button class="tab-btn active" data-tab="dashboard"><span class="nav-icon">⌂</span><span>Painel</span></button>
|
||
<div class="nav-group-label">Contas</div>
|
||
<button class="tab-btn" data-tab="ssh"><span class="nav-icon">👥</span><span>SSH / SlowDNS</span></button>
|
||
<button class="tab-btn" data-tab="xray"><span class="nav-icon">◇</span><span>Xray Users</span></button>
|
||
<div class="nav-group-label superadmin-only hidden">Administração</div>
|
||
<button class="tab-btn superadmin-only hidden" data-tab="resellers"><span class="nav-icon">🏪</span><span>Revendedores</span></button>
|
||
<button class="tab-btn superadmin-only hidden" data-tab="servers"><span class="nav-icon">▣</span><span>Infraestrutura</span></button>
|
||
<button class="tab-btn superadmin-only hidden" data-tab="logs"><span class="nav-icon">☰</span><span>Logs</span></button>
|
||
<button class="tab-btn superadmin-only hidden" data-tab="bot"><span class="nav-icon">🤖</span><span>Bot / Vendas</span></button>
|
||
<button class="tab-btn superadmin-only hidden" data-tab="server"><span class="nav-icon">⚙</span><span>Configurações</span></button>
|
||
</nav>
|
||
</aside>
|
||
<div class="drawer-backdrop" id="drawerBackdrop"></div>
|
||
<section class="workspace">
|
||
<header class="topbar">
|
||
<div class="topbar-left">
|
||
<button class="icon-btn" id="menuToggle" type="button" aria-label="Open menu">☰</button>
|
||
<div class="topbar-title">
|
||
<span id="pageEyebrow">Painel</span>
|
||
<strong id="pageTitle">Visão geral</strong>
|
||
</div>
|
||
</div>
|
||
<div class="topbar-actions">
|
||
<select id="languageSelect" class="language-select" title="Language" aria-label="Language">
|
||
<option value="pt-BR">PT-BR</option>
|
||
<option value="en-US">EN-US</option>
|
||
</select>
|
||
<div class="user-pill">
|
||
<span id="roleChip"></span>
|
||
<strong id="meUsername"></strong>
|
||
</div>
|
||
<button class="btn btn-ghost btn-sm" id="logoutBtn">Sair</button>
|
||
</div>
|
||
</header>
|
||
<main class="workspace-main">
|
||
|
||
<!-- ═══════════ Dashboard Tab ═══════════ -->
|
||
<div class="tab-pane active" id="tab-dashboard">
|
||
<div class="dash-grid">
|
||
<div class="dash-card accent-blue">
|
||
<div class="dash-card-main">
|
||
<span class="dash-label">Total de contas</span>
|
||
<strong id="dashTotalUsers">--</strong>
|
||
<small><span id="dashActiveUsers">--</span> ativas · <span id="dashExpiredUsers">--</span> expiradas</small>
|
||
<small id="dashAccountBreakdown">SSH -- · Xray --</small>
|
||
</div>
|
||
<div class="dash-icon">👥</div>
|
||
</div>
|
||
<div class="dash-card accent-green superadmin-only hidden">
|
||
<div class="dash-card-main">
|
||
<span class="dash-label">Servidores</span>
|
||
<strong id="dashServers">--</strong>
|
||
<small id="dashServerStatus">monitoramento em tempo real</small>
|
||
</div>
|
||
<div class="dash-icon">▣</div>
|
||
</div>
|
||
<div class="dash-card accent-green reseller-only hidden" id="dashQuotaSummaryCard">
|
||
<div class="dash-card-main">
|
||
<span class="dash-label">Limite disponível</span>
|
||
<strong id="dashQuotaRemaining">--</strong>
|
||
<small id="dashQuotaSummaryText">Carregando cota…</small>
|
||
<div class="mini-meter"><span id="dashQuotaMiniBar"></span></div>
|
||
</div>
|
||
<div class="dash-icon">✓</div>
|
||
</div>
|
||
<div class="dash-card accent-purple">
|
||
<div class="dash-card-main">
|
||
<span class="dash-label">Conexões ativas</span>
|
||
<strong id="dashConnections">--</strong>
|
||
<small id="dashConnectionsText">SSH + Xray online agora</small>
|
||
</div>
|
||
<div class="dash-icon">⌁</div>
|
||
</div>
|
||
<div class="dash-card accent-orange">
|
||
<div class="dash-card-main">
|
||
<span class="dash-label">Xray clients</span>
|
||
<strong id="dashXrayClients">--</strong>
|
||
<small id="dashXrayStatus">Pronto para revendedores</small>
|
||
</div>
|
||
<div class="dash-icon">◇</div>
|
||
</div>
|
||
<div class="dash-card dash-resource accent-blue superadmin-only hidden">
|
||
<div class="dash-card-main">
|
||
<span class="dash-label">CPU</span>
|
||
<strong id="dashCpuVal">--%</strong>
|
||
<small id="dashCpuText">Carga do processador</small>
|
||
<div class="mini-meter dashboard-meter"><span id="dashCpuBar"></span></div>
|
||
</div>
|
||
<div class="dash-icon">◴</div>
|
||
</div>
|
||
<div class="dash-card dash-resource accent-green superadmin-only hidden">
|
||
<div class="dash-card-main">
|
||
<span class="dash-label">RAM</span>
|
||
<strong id="dashRamVal">--%</strong>
|
||
<small id="dashRamText">Memória usada</small>
|
||
<div class="mini-meter dashboard-meter"><span id="dashRamBar"></span></div>
|
||
</div>
|
||
<div class="dash-icon">▣</div>
|
||
</div>
|
||
<div class="dash-card dash-resource accent-purple superadmin-only hidden">
|
||
<div class="dash-card-main">
|
||
<span class="dash-label">Rede</span>
|
||
<strong id="dashNetVal">--</strong>
|
||
<small id="dashNetText">RX -- · TX -- Mb/s</small>
|
||
<small id="dashNetTotal">Total --</small>
|
||
</div>
|
||
<div class="dash-icon">⇅</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card superadmin-only hidden" id="updateStatusCard" style="margin-top:18px;">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Panel Updates <span class="chip" id="updateStateChip">Checking…</span></div>
|
||
<div class="card-actions">
|
||
<a class="btn btn-ghost btn-sm" id="updateRepoLink" href="https://git.dr2.site/penguinehis/DragonCoreSSH-NewWEB" target="_blank" rel="noopener noreferrer">Open Git</a>
|
||
<button class="btn btn-ghost btn-sm" type="button" id="checkUpdateBtn">Check now</button>
|
||
</div>
|
||
</div>
|
||
<div class="metrics">
|
||
<div class="metric"><div class="m-label">Installed version</div><div class="m-val update-commit" id="updateCurrentCommit">--</div></div>
|
||
<div class="metric"><div class="m-label">Latest Git version</div><div class="m-val update-commit" id="updateLatestCommit">--</div></div>
|
||
<div class="metric"><div class="m-label">Branch</div><div class="m-val update-commit" id="updateBranch">main</div></div>
|
||
<div class="metric"><div class="m-label">Last checked</div><div class="m-val update-check-time" id="updateCheckedAt">--</div></div>
|
||
</div>
|
||
<div class="statusbar update-statusbar">
|
||
<span id="updateStatusText">Comparing the installed version with the Git repository.</span>
|
||
<span class="hint hidden" id="updateCommandWrap">To update: <code>sudo bash /opt/sshpanel/update.sh</code> <button class="btn btn-ghost btn-xs" type="button" id="copyUpdateCommandBtn">Copy command</button></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid2 dashboard-lower">
|
||
<div class="card hidden" id="dnsttDashboardCard">
|
||
<div class="card-hdr">
|
||
<div class="card-title">DNSTT Health <span class="chip">5s window</span></div>
|
||
<span class="hint" id="dnsttHealthUpdated">--</span>
|
||
</div>
|
||
<div class="metrics">
|
||
<div class="metric"><div class="m-label">Active Sessions</div><div class="m-val" id="dnsttActiveSessions">--</div></div>
|
||
<div class="metric"><div class="m-label">Active Streams</div><div class="m-val" id="dnsttActiveStreams">--</div></div>
|
||
<div class="metric"><div class="m-label">DNS RX / 5s</div><div class="m-val" id="dnsttDNSRx">--</div></div>
|
||
<div class="metric"><div class="m-label">Queue</div><div class="m-val" id="dnsttQueueLen">--</div></div>
|
||
</div>
|
||
<div class="tbl-wrap" style="margin-top:10px;">
|
||
<table>
|
||
<thead><tr><th>Counter</th><th>Value</th><th>Counter</th><th>Value</th></tr></thead>
|
||
<tbody id="dnsttHealthBody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="statusbar"><span id="dnsttHealthSummary">DNSTT counters are loaded from /api/dnstt.</span><span class="hint">Reject/drop/panic counters should normally stay at 0.</span></div>
|
||
</div>
|
||
<div class="card" id="dashboardQuotaCard">
|
||
<div class="card-hdr"><div class="card-title">Minha cota</div><span class="chip" id="dashQuotaChip">--</span></div>
|
||
<div class="quota-meter"><span id="dashQuotaBar"></span></div>
|
||
<div class="statusbar"><span id="dashQuotaText">Carregando…</span><span id="dashQuotaBreakdown"></span></div>
|
||
</div>
|
||
</div>
|
||
</div><!-- /tab-dashboard -->
|
||
|
||
<!-- ═══════════ SSH Users Tab ═══════════ -->
|
||
<div class="tab-pane" id="tab-ssh">
|
||
<section class="page-hero status-hero" data-tone="green">
|
||
<div class="page-hero-copy"><span class="page-kicker">Access workspace</span><h2>Contas SSH</h2><p>Crie, limite e acompanhe acessos SSH e SlowDNS com segurança.</p></div>
|
||
<div class="workspace-hero-actions"><span class="workspace-live-status" id="sshLiveStatus">Aguardando dados</span><button class="btn btn-ghost btn-sm" id="sshHeroRefreshBtn" type="button">Atualizar</button></div>
|
||
<div class="workspace-overview-grid">
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon green">●</span><div><small>Operação</small><strong id="sshMetricState">Carregando</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon purple">◆</span><div><small>Contas</small><strong id="sshMetricTotal">0</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon cyan">✓</span><div><small>Ativas</small><strong id="sshMetricActive">0</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon amber">↗</span><div><small>Online agora</small><strong id="sshMetricOnline">0</strong></div></article>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="workspace-nav-shell" data-tone="green">
|
||
<nav class="workspace-section-nav" id="sshSectionNav" aria-label="Áreas SSH e SlowDNS" style="--workspace-nav-columns:2">
|
||
<button class="active" type="button" data-workspace="ssh" data-workspace-section="users"><span>◎</span> Usuários</button>
|
||
<button type="button" data-workspace="ssh" data-workspace-section="create"><span>+</span> Criar usuário</button>
|
||
</nav>
|
||
<select id="sshSection" class="workspace-section-select" data-workspace-select="ssh" aria-label="Área SSH e SlowDNS">
|
||
<option value="users">Usuários</option><option value="create">Criar usuário</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Reseller info card (visible to resellers only) -->
|
||
<div id="resellerInfoCard" class="card hidden" style="margin-bottom:12px;">
|
||
<div class="card-hdr"><div class="card-title">My Account</div></div>
|
||
<div class="metrics">
|
||
<div class="metric">
|
||
<div class="m-label">Users (used / max)</div>
|
||
<div class="m-val" id="rUsedMax">-- / --</div>
|
||
</div>
|
||
<div class="metric">
|
||
<div class="m-label">Expires</div>
|
||
<div class="m-val" id="rExpiry">--</div>
|
||
</div>
|
||
<div class="metric">
|
||
<div class="m-label">Status</div>
|
||
<div class="m-val" id="rStatus">--</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="sshServerPickerCard" class="card hidden" style="margin-bottom:12px;">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Target server</div>
|
||
<span class="chip">master/slave</span>
|
||
</div>
|
||
<div class="form-grid" style="grid-template-columns:1fr auto;align-items:end;">
|
||
<div class="field"><label>Create/list SSH users on</label><select id="sshServerSelect"></select></div>
|
||
<button class="btn btn-ghost btn-sm" type="button" id="reloadServersBtn">Reload servers</button>
|
||
</div>
|
||
<div class="hint" id="sshServerHint">Servers with SSH enabled are available here.</div>
|
||
</div>
|
||
|
||
<section class="workspace-section active" data-workspace-panel="ssh" data-workspace-section-panel="users">
|
||
<div class="workspace-section-heading"><div><span>01 · Gestão</span><h3>Usuários SSH e SlowDNS</h3><p>Consulte conexões, limites, validade e ações de cada conta.</p></div></div>
|
||
<!-- Users list -->
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Users <span class="chip" id="userCountChip">0</span></div>
|
||
<div style="display:flex;gap:5px;">
|
||
<button class="btn btn-ghost btn-sm" id="newUserBtn">+ New</button>
|
||
<button class="btn btn-ghost btn-sm" id="reloadUsersBtn">Reload</button>
|
||
</div>
|
||
</div>
|
||
<div class="tbl-wrap">
|
||
<table>
|
||
<thead><tr>
|
||
<th data-sort-key="username">User</th><th data-sort-key="status">Status</th><th data-sort-key="auth">Auth</th>
|
||
<th data-sort-key="conn">Conn</th><th data-sort-key="max">Max</th><th data-sort-key="up">Up</th><th data-sort-key="down">Dn</th><th data-sort-key="usage">Traffic</th><th data-sort-key="expires">Expires</th>
|
||
<th id="ownerColHead" data-sort-key="owner" class="superadmin-only hidden">Owner</th>
|
||
<th>Actions</th>
|
||
</tr></thead>
|
||
<tbody id="usersBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="workspace-section" data-workspace-panel="ssh" data-workspace-section-panel="create">
|
||
<div class="workspace-section-heading"><div><span>02 · Cadastro</span><h3 id="userFormHeading">Criar usuário</h3><p>Defina autenticação, validade, conexões e velocidade em uma tela dedicada.</p></div></div>
|
||
<!-- Create / edit user form -->
|
||
<div class="card workspace-form-card">
|
||
<div class="card-hdr">
|
||
<div class="card-title" id="userFormTitle">Criar usuário SSH</div>
|
||
</div>
|
||
<div id="userFormWrap">
|
||
<form id="userForm">
|
||
<div class="form-grid">
|
||
<div class="field"><label>Username</label><input id="fUsername" required autocomplete="off"/></div>
|
||
<div class="field"><label>Password <span class="hint">(blank = keep)</span></label><input id="fPassword" type="password" autocomplete="new-password"/></div>
|
||
<div class="field"><label>TOTP Secret</label>
|
||
<div class="field-row">
|
||
<input id="fTotpSecret" placeholder="Leave blank to disable"/>
|
||
<button class="btn btn-ghost btn-sm" type="button" id="genTotpBtn">Gen</button>
|
||
<button class="btn btn-ghost btn-sm" type="button" id="clearTotpBtn">X</button>
|
||
</div>
|
||
</div>
|
||
<div class="field"><label>TOTP Period (s)</label><input id="fTotpPeriod" type="number" min="15" step="15" placeholder="60"/></div>
|
||
<div class="field"><label>TOTP Window</label><input id="fTotpWindow" type="number" min="0" placeholder="1"/></div>
|
||
<div class="field"><label>TOTP Digits</label><input id="fTotpDigits" type="number" min="6" max="8" placeholder="6"/></div>
|
||
<div class="field"><label>Allow static password too</label><input id="fAllowStatic" type="checkbox" style="width:16px;height:16px;margin-top:10px;"/></div>
|
||
<div class="field"><label>Max connections</label><input id="fMaxConn" type="number" min="0" placeholder="0 = unlimited"/></div>
|
||
<div class="field"><label>Expires at</label><input id="fExpires" type="datetime-local"/></div>
|
||
<div class="field"><label>Max Upload (Mb/s)</label><input id="fUp" type="number" min="0" placeholder="0 = default"/></div>
|
||
<div class="field"><label>Max Download (Mb/s)</label><input id="fDown" type="number" min="0" placeholder="0 = default"/></div>
|
||
<div class="field"><label>Data quota (GB) <span class="hint">0 = unlimited · 1024 = 1 TB</span></label><input id="fQuotaGB" type="number" min="0" step="0.01" placeholder="0"/></div>
|
||
<div class="field"><label>When quota is reached</label><select id="fQuotaAction"><option value="block">Block user</option><option value="throttle" selected>Reduce speed</option></select></div>
|
||
<div class="field"><label>Post-quota speed (Mb/s)</label><input id="fQuotaThrottle" type="number" min="1" value="1"/></div>
|
||
<div class="field"><label>Current usage</label><input id="fUsageDisplay" readonly value="0 B"/></div>
|
||
<div class="field"><label>Reset traffic counter</label><input id="fResetUsage" type="checkbox" style="width:16px;height:16px;margin-top:10px;"/></div>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button class="btn" type="submit" id="saveUserBtn">Save user</button>
|
||
<button class="btn btn-ghost" type="button" id="cancelUserBtn">Cancel</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<div class="statusbar workspace-section-status"><span id="userStatus">Ready.</span><span id="lastReload"></span></div>
|
||
</div><!-- /tab-ssh -->
|
||
|
||
<!-- ═══════════ Xray Tab (superadmin only) ═══════════ -->
|
||
<div class="tab-pane" id="tab-xray">
|
||
<section class="page-hero status-hero" data-tone="purple">
|
||
<div class="page-hero-copy"><span class="page-kicker">Proxy studio</span><h2>Xray visual</h2><p>Gerencie clientes, inbounds e um endpoint XHTTP compartilhado sem editar JSON.</p></div>
|
||
<div class="workspace-hero-actions"><span class="workspace-live-status" id="xrayChip">Aguardando dados</span><button class="btn btn-ghost btn-sm" id="xRefreshBtn" type="button">Atualizar</button></div>
|
||
<div class="workspace-overview-grid five">
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon green">●</span><div><small>Status</small><strong id="xRunning">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon cyan">↗</span><div><small>Online</small><strong id="xOnlineUsers">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon purple">#</span><div><small>PID</small><strong id="xPID">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon amber">◷</span><div><small>Uptime</small><strong id="xUptime">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon blue">API</span><div><small>Contadores</small><strong id="xStatsConfig">--</strong></div></article>
|
||
</div>
|
||
<div class="workspace-hero-toolbar">
|
||
<span id="xStatus" class="workspace-toolbar-status">Pronto.</span>
|
||
<div class="workspace-toolbar-actions xray-admin-only">
|
||
<select id="xCoreMode" class="input-sm" title="Xray runtime mode"><option value="native">Internal native emulator</option><option value="external">External xray binary</option></select>
|
||
<button class="btn btn-ghost btn-sm" id="xSaveModeBtn">Salvar modo</button>
|
||
<button class="btn btn-ghost btn-sm" id="xStartBtn">Iniciar</button>
|
||
<button class="btn btn-danger btn-sm" id="xStopBtn">Parar</button>
|
||
<button class="btn btn-ghost btn-sm" id="xRestartBtn">Reiniciar</button>
|
||
<button class="btn btn-ghost btn-sm" id="xRepairStatsBtn">Reparar contadores</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="workspace-nav-shell" data-tone="purple">
|
||
<nav class="workspace-section-nav" id="xraySectionNav" aria-label="Áreas do Xray" style="--workspace-nav-columns:4">
|
||
<button class="active" type="button" data-workspace="xray" data-workspace-section="users"><span>◎</span> Usuários</button>
|
||
<button type="button" data-workspace="xray" data-workspace-section="create"><span>+</span> Criar usuário</button>
|
||
<button class="xray-admin-only" type="button" data-workspace="xray" data-workspace-section="config"><span>⚙</span> Configuração</button>
|
||
<button class="xray-admin-only" type="button" data-workspace="xray" data-workspace-section="logs"><span>☰</span> Logs</button>
|
||
</nav>
|
||
<select id="xraySection" class="workspace-section-select" data-workspace-select="xray" aria-label="Área do Xray">
|
||
<option value="users">Usuários</option><option value="create">Criar usuário</option>
|
||
<option class="xray-admin-only" value="config">Configuração</option><option class="xray-admin-only" value="logs">Logs</option>
|
||
</select>
|
||
</div>
|
||
|
||
<section class="workspace-section active" data-workspace-panel="xray" data-workspace-section-panel="users">
|
||
<!-- Edit Client Panel (hidden by default) -->
|
||
<div id="editXrayClientPanel" class="card hidden" style="margin-bottom:12px;border-color:rgba(245,158,11,.4);">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Edit Client <span id="editClientUUID" style="font-family:monospace;font-size:.65rem;color:var(--muted);"></span></div>
|
||
<button class="btn btn-ghost btn-sm" onclick="closeEditXrayClient()">Cancel</button>
|
||
</div>
|
||
<div class="form-grid">
|
||
<div class="field"><label>Display Name</label><input id="editXrayName" autocomplete="off"/></div>
|
||
<div class="field"><label>Email / Label</label><input id="editXrayEmail" autocomplete="off"/></div>
|
||
<div class="field"><label>Expiry Date</label><input type="datetime-local" id="editXrayExpiry" style="color-scheme:dark;"/></div>
|
||
<div class="field"><label>Max Connections <span class="hint">(0 = unlimited)</span></label><input type="number" min="0" id="editXrayMaxConns"/></div>
|
||
<div class="field"><label>Data quota (GB) <span class="hint">0 = unlimited · 1024 = 1 TB</span></label><input type="number" min="0" step="0.01" id="editXrayQuotaGB"/></div>
|
||
<div class="field"><label>When quota is reached</label><select id="editXrayQuotaAction"><option value="block">Block user</option><option value="throttle">Reduce speed</option></select></div>
|
||
<div class="field"><label>Post-quota speed (Mb/s)</label><input type="number" min="1" id="editXrayQuotaThrottle" value="1"/></div>
|
||
<div class="field"><label>Current usage</label><input id="editXrayUsage" readonly value="0 B"/></div>
|
||
<div class="field"><label>Reset traffic counter</label><input id="editXrayResetUsage" type="checkbox" style="width:16px;height:16px;margin-top:10px;"/></div>
|
||
</div>
|
||
<div class="form-actions" style="margin-top:8px;">
|
||
<button class="btn btn-sm" onclick="saveEditXrayClient()">Save Changes</button>
|
||
<button class="btn btn-ghost btn-sm" onclick="closeEditXrayClient()">Cancel</button>
|
||
</div>
|
||
<div id="editXrayClientStatus" class="hint" style="margin-top:4px;"></div>
|
||
</div>
|
||
</section>
|
||
<div class="card reseller-only hidden reseller-helper-card">
|
||
<div class="card-hdr"><div class="card-title">Área do revendedor</div><span class="chip green">cota única</span></div>
|
||
<p class="hint">Crie clientes Xray com a mesma experiência do painel principal. Cada cliente Xray desconta do mesmo limite usado pelas contas SSH.</p>
|
||
<div class="mini-summary">
|
||
<span><strong id="xrayResellerQuotaUsed">--</strong><small>usadas</small></span>
|
||
<span><strong id="xrayResellerQuotaRemaining">--</strong><small>disponíveis</small></span>
|
||
<span><strong id="xrayResellerQuotaMix">SSH -- · Xray --</strong><small>divisão</small></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="xrayServerPickerCard" class="card hidden" style="margin-bottom:12px;">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Target server</div>
|
||
<span class="chip">master/slave</span>
|
||
</div>
|
||
<div class="form-grid" style="grid-template-columns:1fr auto;align-items:end;">
|
||
<div class="field"><label>Manage Xray users and config on</label><select id="xrayServerSelect"></select></div>
|
||
<button class="btn btn-ghost btn-sm" type="button" id="reloadServersBtn2">Reload servers</button>
|
||
</div>
|
||
<div class="hint hidden" id="xrayServerHint"></div>
|
||
</div>
|
||
|
||
<section class="workspace-section active" data-workspace-panel="xray" data-workspace-section-panel="users">
|
||
<div class="workspace-section-heading"><div><span>01 · Gestão</span><h3>Usuários Xray</h3><p>Consulte clientes, conexões, validade e tráfego separados por inbound.</p></div><button class="btn btn-sm" id="xrayOpenCreateBtn" type="button">Criar usuário</button></div>
|
||
<!-- Inbounds & clients -->
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Inbounds & Clients</div>
|
||
<div class="card-actions"><button class="btn btn-ghost btn-sm" id="xLoadInboundsBtn">Reload</button></div>
|
||
</div>
|
||
<div class="xray-list-controls" id="xrayListControls" aria-label="Xray user list controls">
|
||
<div class="xray-list-control-group">
|
||
<span class="xray-list-control-label" id="xraySortLabel"></span>
|
||
<div class="xray-list-buttons" id="xraySortButtons"></div>
|
||
</div>
|
||
<div class="xray-list-control-group">
|
||
<span class="xray-list-control-label" id="xrayFilterLabel"></span>
|
||
<div class="xray-list-buttons" id="xrayFilterButtons"></div>
|
||
</div>
|
||
<span class="chip xray-list-count" id="xrayListCount"></span>
|
||
</div>
|
||
<div id="inboundsContainer">
|
||
<div class="hint" style="padding:8px 0;">Loading inbounds…</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="workspace-section" data-workspace-panel="xray" data-workspace-section-panel="create">
|
||
<div class="workspace-section-heading"><div><span>02 · Cadastro</span><h3>Criar usuário Xray</h3><p>Escolha o inbound e cadastre o cliente sem abrir a tabela de usuários.</p></div></div>
|
||
<div class="card workspace-form-card">
|
||
<div class="card-hdr"><div class="card-title">Novo cliente Xray</div><span class="chip" id="xCreateProtocolChip">inbound</span></div>
|
||
<form id="xCreateClientForm">
|
||
<div class="form-grid">
|
||
<div class="field" style="grid-column:1/-1"><label>Inbound</label><select id="xCreateInbound" required><option value="">Carregando inbounds…</option></select><span class="hint" id="xCreateInboundHint">Selecione onde o cliente será adicionado.</span></div>
|
||
<div class="field"><label>UUID</label><div class="field-row"><input id="xCreateUUID" required autocomplete="off" placeholder="Gerado automaticamente"/><button class="btn btn-ghost btn-sm" id="xCreateUUIDBtn" type="button">Gerar</button></div></div>
|
||
<div class="field"><label>Nome de exibição</label><input id="xCreateName" autocomplete="off" placeholder="Ex.: Cliente 01"/></div>
|
||
<div class="field"><label>Email / identificação</label><input id="xCreateEmail" autocomplete="off" placeholder="cliente@example"/></div>
|
||
<div class="field"><label>Expira em</label><input id="xCreateExpiry" type="datetime-local"/></div>
|
||
<div class="field"><label>Máximo de conexões <span class="hint">0 = ilimitado</span></label><input id="xCreateMaxConns" type="number" min="0" value="0"/></div>
|
||
<div class="field"><label>Cota de dados (GB) <span class="hint">0 = ilimitado · 1024 = 1 TB</span></label><input id="xCreateQuotaGB" type="number" min="0" step="0.01" value="0"/></div>
|
||
<div class="field"><label>Ao atingir a cota</label><select id="xCreateQuotaAction"><option value="block">Bloquear usuário</option><option value="throttle">Reduzir velocidade</option></select></div>
|
||
<div class="field"><label>Velocidade após a cota (Mb/s)</label><input id="xCreateQuotaThrottle" type="number" min="1" value="1"/></div>
|
||
</div>
|
||
<div class="form-actions"><button class="btn" id="xCreateClientBtn" type="submit">Criar usuário</button><button class="btn btn-ghost" id="xCreateCancelBtn" type="button">Voltar aos usuários</button></div>
|
||
<div class="statusbar"><span id="xCreateClientStatus">Preencha os dados do novo cliente.</span></div>
|
||
</form>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="workspace-section xray-admin-only" data-workspace-panel="xray" data-workspace-section-panel="config">
|
||
<div class="workspace-section-heading"><div><span>03 · Serviço</span><h3>Configuração Xray</h3><p>Edite endpoint, inbounds, TLS e opções avançadas visualmente ou em JSON.</p></div></div>
|
||
<!-- Config editor -->
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Xray Config</div>
|
||
<div class="card-actions">
|
||
<button class="btn btn-sm" id="xrayWizardTabBtn" onclick="setXrayCfgMode('wizard')">Visual</button>
|
||
<button class="btn btn-ghost btn-sm" id="xrayJsonTabBtn" onclick="setXrayCfgMode('json')">JSON</button>
|
||
</div>
|
||
</div>
|
||
<!-- Wizard pane -->
|
||
<div id="xrayWizardPane">
|
||
<section class="shared-endpoint-card">
|
||
<div class="shared-endpoint-head">
|
||
<div><span class="page-kicker">Shared XHTTP endpoint</span><h3>Um domínio e uma porta</h3><p>O protocolo selecionado usa <code>/</code>; SSH usa <code>/ssh</code>. Disponível no modo Xray nativo.</p></div>
|
||
<span class="chip green">path routing</span>
|
||
</div>
|
||
<div class="shared-route-preview" aria-label="Shared endpoint route preview">
|
||
<span><strong id="sharedProxyRouteLabel">VLESS</strong><code>/</code></span>
|
||
<i></i>
|
||
<span><strong>SSH</strong><code>/ssh</code></span>
|
||
</div>
|
||
<div class="form-grid shared-endpoint-grid">
|
||
<div class="field"><label>Protocolo em /</label><select id="sharedXHTTPProtocol"><option value="vless">VLESS</option><option value="vmess">VMess</option></select></div>
|
||
<div class="field"><label>Porta compartilhada</label><input id="sharedXHTTPPort" type="number" min="1" max="65535" value="443"/></div>
|
||
<div class="field"><label>IP de listen</label><input id="sharedXHTTPListen" value="0.0.0.0" placeholder="0.0.0.0"/></div>
|
||
<div class="field"><label>Host HTTP <span class="hint">opcional</span></label><input id="sharedXHTTPHost" placeholder="vpn.seudominio.com"/></div>
|
||
<div class="field"><label>Modo XHTTP</label><select id="sharedXHTTPMode"><option value="auto">auto</option><option value="packet-up">packet-up</option><option value="stream-up">stream-up</option><option value="stream-down">stream-down</option><option value="stream-one">stream-one</option></select></div>
|
||
<div class="field"><label>Segurança</label><select id="sharedXHTTPSecurity"><option value="none">Sem TLS</option><option value="tls">TLS</option></select></div>
|
||
<div class="field shared-tls-field hidden"><label>Arquivo do certificado</label><input id="sharedXHTTPCert" placeholder="/opt/sshpanel/certs/domain/cert.pem"/></div>
|
||
<div class="field shared-tls-field hidden"><label>Arquivo da chave</label><input id="sharedXHTTPKey" placeholder="/opt/sshpanel/certs/domain/key.pem"/></div>
|
||
</div>
|
||
<div class="shared-endpoint-actions"><span id="sharedXHTTPStatus" class="hint">Carregue a configuração para detectar um endpoint existente.</span><button class="btn" id="sharedXHTTPApplyBtn" type="button">Criar / atualizar endpoint</button></div>
|
||
</section>
|
||
|
||
<aside class="legacy-xhttp-migration" aria-label="Migração de configuração XHTTP antiga">
|
||
<span class="legacy-xhttp-icon" aria-hidden="true">SSH+</span>
|
||
<div>
|
||
<strong>Configuração XHTTP antiga?</strong>
|
||
<p>Use “Ativar SSH /ssh” no cartão do inbound existente. O painel mantém o inbound, os clientes e todas as opções atuais; adiciona somente a rota SSH e reinicia o Xray.</p>
|
||
</div>
|
||
</aside>
|
||
|
||
<div class="visual-config-toolbar">
|
||
<div class="field">
|
||
<label>Log Level</label>
|
||
<select id="wzLogLevel">
|
||
<option value="none">none</option>
|
||
<option value="error">error</option>
|
||
<option value="warning" selected>warning</option>
|
||
<option value="info">info</option>
|
||
<option value="debug">debug</option>
|
||
</select>
|
||
</div>
|
||
<div class="visual-config-toolbar-copy"><strong>Inbounds configurados</strong><span>Edite qualquer cartão visualmente ou use JSON para campos avançados.</span></div>
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="wzToggleAddInbound()">+ Novo inbound</button>
|
||
</div>
|
||
<div id="wzInboundsList" class="visual-inbound-list"></div>
|
||
<!-- Add inbound form -->
|
||
<div id="wzAddInboundForm" class="visual-inbound-editor hidden">
|
||
<div class="visual-editor-heading"><div><span class="page-kicker">Visual editor</span><h3 id="wzInboundFormTitle">Novo inbound</h3></div><span id="wzEditingBadge" class="chip hidden">editing</span></div>
|
||
<div class="form-grid">
|
||
<div class="field">
|
||
<label>Protocol</label>
|
||
<select id="wzProtocol" onchange="onWzProtoChange(this.value)">
|
||
<option value="vless">VLESS</option>
|
||
<option value="vmess">VMess</option>
|
||
<option value="ssh">SSH Tunnel (over XHTTP)</option>
|
||
<option value="trojan">Trojan</option>
|
||
<option value="shadowsocks">Shadowsocks</option>
|
||
<option value="socks">SOCKS5 (local)</option>
|
||
</select>
|
||
</div>
|
||
<div class="field"><label>Port</label><input type="number" id="wzPort" min="1" max="65535" placeholder="10086"/></div>
|
||
<div class="field"><label>Listen IP</label><input type="text" id="wzListenIP" placeholder="0.0.0.0"/></div>
|
||
<div class="field"><label>Tag</label><input type="text" id="wzTag" placeholder="vless-in"/></div>
|
||
<!-- VLESS / VMess transport fields -->
|
||
<div id="wzVlessFields" style="grid-column:1/-1;display:grid;grid-template-columns:1fr 1fr;gap:8px;">
|
||
<div class="field">
|
||
<label>Network</label>
|
||
<select id="wzNetwork" onchange="onWzNetworkChange(this.value)">
|
||
<option value="tcp">TCP</option>
|
||
<option value="ws">WebSocket</option>
|
||
<option value="xhttp">XHTTP (SplitHTTP)</option>
|
||
<option value="httpupgrade">HTTPUpgrade</option>
|
||
<option value="h2">HTTP/2</option>
|
||
<option value="grpc">gRPC</option>
|
||
</select>
|
||
</div>
|
||
<!-- TCP: no extra fields -->
|
||
<!-- WebSocket -->
|
||
<div class="field" id="wzWSPathField" style="display:none;"><label>Path</label><input type="text" id="wzWSPath" placeholder="/ws"/></div>
|
||
<!-- XHTTP -->
|
||
<div class="field" id="wzXHTTPPathField" style="display:none;"><label>Path</label><input type="text" id="wzXHTTPPath" placeholder="/xhttp" value="/xhttp"/></div>
|
||
<div class="field" id="wzXHTTPHostField" style="display:none;"><label>HTTP Host <span class="hint">(separate from TLS SNI)</span></label><input type="text" id="wzXHTTPHost" placeholder="example.com"/></div>
|
||
<div class="field" id="wzXHTTPModeField" style="display:none;">
|
||
<label>Mode</label>
|
||
<select id="wzXHTTPMode">
|
||
<option value="auto">auto</option>
|
||
<option value="packet-up">packet-up</option>
|
||
<option value="stream-up">stream-up</option>
|
||
<option value="stream-down">stream-down</option>
|
||
<option value="stream-one">stream-one</option>
|
||
</select>
|
||
</div>
|
||
<!-- HTTPUpgrade -->
|
||
<div class="field" id="wzHUPathField" style="display:none;"><label>Path</label><input type="text" id="wzHUPath" placeholder="/upgrade"/></div>
|
||
<div class="field" id="wzHUHostField" style="display:none;"><label>Host <span class="hint">(SNI)</span></label><input type="text" id="wzHUHost" placeholder="example.com"/></div>
|
||
<!-- H2 -->
|
||
<div class="field" id="wzH2PathField" style="display:none;"><label>Path</label><input type="text" id="wzH2Path" placeholder="/h2"/></div>
|
||
<div class="field" id="wzH2HostField" style="display:none;"><label>Host</label><input type="text" id="wzH2Host" placeholder="example.com"/></div>
|
||
<!-- gRPC -->
|
||
<div class="field" id="wzGRPCServiceField" style="display:none;"><label>Service Name</label><input type="text" id="wzGRPCService" placeholder="grpc-service"/></div>
|
||
<div class="field" id="wzGRPCMultiField" style="display:none;">
|
||
<label style="display:flex;align-items:center;gap:5px;margin-top:20px;">
|
||
<input type="checkbox" id="wzGRPCMulti"/> Multi-mode
|
||
</label>
|
||
</div>
|
||
<!-- TLS (all transports) -->
|
||
<div class="field" style="grid-column:1/-1;">
|
||
<label>TLS</label>
|
||
<select id="wzTLS" onchange="onWzTLSChange(this.value)">
|
||
<option value="none">None</option>
|
||
<option value="tls">TLS</option>
|
||
<option value="reality">Reality</option>
|
||
</select>
|
||
</div>
|
||
<!-- TLS cert/key picker — shown when wzTLS=tls -->
|
||
<div id="wzTLSCertBlock" style="display:none;grid-column:1/-1;padding:8px;background:var(--card-bg);border:1px solid var(--border);border-radius:6px;">
|
||
<input type="hidden" id="wzTLSCert"/>
|
||
<input type="hidden" id="wzTLSKey"/>
|
||
<div style="margin-bottom:6px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;">
|
||
<span style="font-size:.73rem;color:var(--muted);">Certificate source:</span>
|
||
<button class="btn btn-sm" id="wzCertSrcFileBtn" type="button" onclick="setWzCertSrc('file')">File Path</button>
|
||
<button class="btn btn-ghost btn-sm" id="wzCertSrcPasteBtn" type="button" onclick="setWzCertSrc('paste')">Paste PEM</button>
|
||
<button class="btn btn-ghost btn-sm" id="wzCertSrcGenBtn" type="button" onclick="setWzCertSrc('gen')">Self-Signed</button>
|
||
</div>
|
||
<!-- File Path sub-panel -->
|
||
<div id="wzCertSrcFile" style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">
|
||
<div class="field" style="margin:0;"><label style="font-size:.72rem;">Cert File Path</label><input type="text" id="wzTLSCertPath" placeholder="/opt/sshpanel/certs/…/cert.pem" oninput="document.getElementById('wzTLSCert').value=this.value"/></div>
|
||
<div class="field" style="margin:0;"><label style="font-size:.72rem;">Key File Path</label><input type="text" id="wzTLSKeyPath" placeholder="/opt/sshpanel/certs/…/key.pem" oninput="document.getElementById('wzTLSKey').value=this.value"/></div>
|
||
</div>
|
||
<!-- Paste PEM sub-panel -->
|
||
<div id="wzCertSrcPaste" style="display:none;">
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">
|
||
<div class="field" style="margin:0;"><label style="font-size:.72rem;">Certificate PEM</label><textarea id="wzPastedCert" rows="5" placeholder="-----BEGIN CERTIFICATE----- …" style="font-family:monospace;font-size:.7rem;width:100%;box-sizing:border-box;resize:vertical;background:var(--input-bg);border:1px solid var(--border);border-radius:4px;color:inherit;padding:4px;"></textarea></div>
|
||
<div class="field" style="margin:0;"><label style="font-size:.72rem;">Private Key PEM</label><textarea id="wzPastedKey" rows="5" placeholder="-----BEGIN EC PRIVATE KEY----- …" style="font-family:monospace;font-size:.7rem;width:100%;box-sizing:border-box;resize:vertical;background:var(--input-bg);border:1px solid var(--border);border-radius:4px;color:inherit;padding:4px;"></textarea></div>
|
||
</div>
|
||
<div style="display:flex;gap:8px;align-items:flex-end;margin-top:6px;flex-wrap:wrap;">
|
||
<div class="field" style="flex:1;min-width:120px;margin:0;"><label style="font-size:.72rem;">Name <span class="hint">(storage folder)</span></label><input type="text" id="wzPastedName" placeholder="my-cert"/></div>
|
||
<button class="btn btn-sm" type="button" onclick="wzSavePastedCert()">Save PEM</button>
|
||
<span id="wzPasteCertStatus" class="hint"></span>
|
||
</div>
|
||
</div>
|
||
<!-- Self-Signed sub-panel -->
|
||
<div id="wzCertSrcGen" style="display:none;">
|
||
<div style="display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap;">
|
||
<div class="field" style="flex:1;min-width:140px;margin:0;"><label style="font-size:.72rem;">Domain <span class="hint">(CN)</span></label><input type="text" id="wzGenDomain" placeholder="example.com"/></div>
|
||
<button class="btn btn-sm" type="button" onclick="wzGenerateCert()">Generate</button>
|
||
<span id="wzGenCertStatus" class="hint"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Reality extra fields -->
|
||
<div class="field" id="wzRealityDestField" style="display:none;"><label>Dest <span class="hint">(e.g. example.com:443)</span></label><input type="text" id="wzRealityDest" placeholder="example.com:443"/></div>
|
||
<div class="field" id="wzRealitySNIField" style="display:none;"><label>Server Name</label><input type="text" id="wzRealitySNI" placeholder="example.com"/></div>
|
||
<div class="field" id="wzRealityPrivField" style="display:none;"><label>Private Key</label><input type="text" id="wzRealityPriv" placeholder="reality private key"/></div>
|
||
<div class="field" id="wzRealityShortIDField" style="display:none;"><label>Short ID</label><input type="text" id="wzRealityShortID" placeholder="short id (hex)"/></div>
|
||
</div>
|
||
<!-- Trojan fields -->
|
||
<div id="wzTrojanFields" class="hidden" style="grid-column:1/-1;">
|
||
<div class="field"><label>Password</label><input type="text" id="wzTrojanPass" placeholder="trojan-password"/></div>
|
||
</div>
|
||
<!-- Shadowsocks fields -->
|
||
<div id="wzSSFields" class="hidden" style="grid-column:1/-1;display:none;grid-template-columns:1fr 1fr;gap:8px;">
|
||
<div class="field"><label>Password</label><input type="text" id="wzSSPass" placeholder="password"/></div>
|
||
<div class="field"><label>Method</label>
|
||
<select id="wzSSMethod">
|
||
<option value="chacha20-ietf-poly1305">chacha20-ietf-poly1305</option>
|
||
<option value="aes-256-gcm">aes-256-gcm</option>
|
||
<option value="aes-128-gcm">aes-128-gcm</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-actions" style="margin-top:8px;">
|
||
<button class="btn btn-sm" id="wzSaveInboundBtn" type="button" onclick="wzSaveInbound()">Adicionar inbound</button>
|
||
<button class="btn btn-ghost btn-sm" id="wzCancelInboundBtn" type="button" onclick="wzCancelInbound()">Cancelar</button>
|
||
</div>
|
||
</div>
|
||
<div class="visual-save-bar">
|
||
<span class="hint" id="wzStatus"></span>
|
||
<button class="btn" type="button" onclick="applyWizardConfig()">Salvar configuração e reiniciar</button>
|
||
</div>
|
||
</div>
|
||
<!-- JSON pane (hidden by default) -->
|
||
<div id="xrayCfgPaneJson" class="hidden">
|
||
<textarea id="xCfgEditor" class="code-area" rows="14" placeholder="Xray JSON config…"></textarea>
|
||
<div class="statusbar"><span id="xCfgStatus">Ready.</span></div>
|
||
<div class="form-actions" style="margin-top:8px;">
|
||
<button class="btn btn-ghost btn-sm" id="xLoadCfgBtn">Load JSON</button>
|
||
<button class="btn btn-sm" id="xSaveCfgBtn">Save & Restart</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="workspace-section xray-admin-only" data-workspace-panel="xray" data-workspace-section-panel="logs">
|
||
<div class="workspace-section-heading"><div><span>04 · Diagnóstico</span><h3>Logs do Xray</h3><p>Acompanhe as últimas mensagens do serviço em uma tela focada.</p></div></div>
|
||
<!-- Logs -->
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Logs <span class="chip">last 200 lines</span></div>
|
||
<div class="card-actions"><button class="btn btn-ghost btn-sm" id="xLoadLogsBtn">Refresh</button></div>
|
||
</div>
|
||
<pre class="log-box" id="xLogsBox"></pre>
|
||
</div>
|
||
</section>
|
||
</div><!-- /tab-xray -->
|
||
|
||
<!-- ═══════════ Resellers Tab (superadmin only) ═══════════ -->
|
||
<div class="tab-pane" id="tab-resellers">
|
||
<section class="page-hero" data-tone="amber"><div class="page-hero-copy"><span class="page-kicker">Partner operations</span><h2>Revendedores</h2><p>Controle cotas, validade e acesso dos parceiros em um só lugar.</p></div></section>
|
||
<div class="workspace-nav-shell" data-tone="amber">
|
||
<nav class="workspace-section-nav" id="resellerSectionNav" aria-label="Áreas de revendedores" style="--workspace-nav-columns:2">
|
||
<button class="active" type="button" data-workspace="resellers" data-workspace-section="users"><span>◎</span> Revendedores</button>
|
||
<button type="button" data-workspace="resellers" data-workspace-section="create"><span>+</span> Criar revendedor</button>
|
||
</nav>
|
||
<select id="resellerSection" class="workspace-section-select" data-workspace-select="resellers" aria-label="Área de revendedores">
|
||
<option value="users">Revendedores</option><option value="create">Criar revendedor</option>
|
||
</select>
|
||
</div>
|
||
<section class="workspace-section active" data-workspace-panel="resellers" data-workspace-section-panel="users">
|
||
<div class="workspace-section-heading"><div><span>01 · Gestão</span><h3>Revendedores cadastrados</h3><p>Consulte cotas, consumo compartilhado, validade e situação de cada parceiro.</p></div></div>
|
||
<!-- Resellers list -->
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Resellers <span class="chip" id="resellerCountChip">0</span></div>
|
||
<div style="display:flex;gap:5px;">
|
||
<button class="btn btn-ghost btn-sm" id="newResellerBtn">+ New</button>
|
||
<button class="btn btn-ghost btn-sm" id="reloadResellersBtn">Reload</button>
|
||
</div>
|
||
</div>
|
||
<div class="tbl-wrap">
|
||
<table>
|
||
<thead><tr>
|
||
<th>Username</th><th>Users (used/max)</th><th>Expires</th><th>Status</th><th>Actions</th>
|
||
</tr></thead>
|
||
<tbody id="resellersBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="workspace-section" data-workspace-panel="resellers" data-workspace-section-panel="create">
|
||
<div class="workspace-section-heading"><div><span>02 · Cadastro</span><h3 id="resellerFormHeading">Criar revendedor</h3><p>Defina login, limite compartilhado, validade e acesso em uma tela dedicada.</p></div></div>
|
||
<!-- Create / edit reseller form -->
|
||
<div class="card workspace-form-card">
|
||
<div class="card-hdr">
|
||
<div class="card-title" id="resellerFormTitle">Create Reseller</div>
|
||
<button class="btn btn-ghost btn-sm" id="cancelResellerBtn">Cancel</button>
|
||
</div>
|
||
<form id="resellerForm">
|
||
<div class="form-grid">
|
||
<div class="field"><label>Username</label><input id="rUsername" required autocomplete="off"/></div>
|
||
<div class="field"><label>Password <span class="hint">(blank = keep)</span></label><input id="rPassword" type="password" autocomplete="new-password"/></div>
|
||
<div class="field"><label>Max SSH users (0 = unlimited)</label><input id="rMaxUsers" type="number" min="0" placeholder="30"/></div>
|
||
<div class="field"><label>Expires at</label><input id="rExpires" type="datetime-local"/></div>
|
||
<div class="field"><label>Active</label><input id="rActive" type="checkbox" checked style="width:16px;height:16px;margin-top:10px;"/></div>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button class="btn" type="submit" id="saveResellerBtn">Save reseller</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</section>
|
||
<div class="statusbar workspace-section-status"><span id="resellerStatus">Ready.</span></div>
|
||
</div><!-- /tab-resellers -->
|
||
|
||
<!-- ═══════════ Servers Tab (superadmin only) ═══════════ -->
|
||
<div class="tab-pane" id="tab-servers">
|
||
<section class="page-hero status-hero" data-tone="blue">
|
||
<div class="page-hero-copy"><span class="page-kicker">Fleet control</span><h2>Infraestrutura</h2><p>Gerencie nós, acompanhe a saúde do sistema e consulte tráfego em um único espaço.</p></div>
|
||
<div class="workspace-hero-actions"><span class="workspace-live-status" id="fleetLiveStatus">Aguardando dados</span><button class="btn btn-ghost btn-sm" id="refreshServersBtn" type="button">Atualizar</button></div>
|
||
<div class="workspace-overview-grid">
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon blue">▣</span><div><small>Nós</small><strong id="fleetMetricNodes">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon green">●</span><div><small>Ativos</small><strong id="fleetMetricActive">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon cyan">SSH</span><div><small>SSH habilitado</small><strong id="fleetMetricSSH">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon purple">◇</span><div><small>Xray habilitado</small><strong id="fleetMetricXray">--</strong></div></article>
|
||
</div>
|
||
</section>
|
||
<div class="infra-nav-mount"></div>
|
||
<div id="serversListView">
|
||
<div class="grid2 servers-grid">
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Managed servers <span class="chip" id="serversCountChip">0</span></div>
|
||
</div>
|
||
<div class="tbl-wrap">
|
||
<table>
|
||
<thead><tr><th>Name</th><th>URL</th><th>Options</th><th>Status</th><th>Actions</th></tr></thead>
|
||
<tbody id="serversBody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="statusbar"><span id="serversStatus">Add slave nodes so the master can create SSH/Xray users remotely.</span></div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-hdr"><div class="card-title" id="serverFormTitle">Add / edit server</div></div>
|
||
<form id="serverForm">
|
||
<input type="hidden" id="srvID"/>
|
||
<div class="form-grid server-form-grid">
|
||
<div class="field"><label>Name</label><input id="srvName" placeholder="Brazil Node 01"/></div>
|
||
<div class="field"><label>Base URL</label><input id="srvBaseURL" placeholder="https://node.example.com:8080"/></div>
|
||
<div class="field"><label>Admin username</label><input id="srvAdminUser" placeholder="admin"/></div>
|
||
<div class="field"><label>Admin key / password</label><input id="srvAdminKey" type="password" placeholder="leave blank to keep saved key"/></div>
|
||
<label class="toggle-field"><input type="checkbox" id="srvEnableSSH" checked/> SSH enabled</label>
|
||
<label class="toggle-field"><input type="checkbox" id="srvEnableXray" checked/> Xray enabled</label>
|
||
<label class="toggle-field"><input type="checkbox" id="srvIsActive" checked/> Active</label>
|
||
</div>
|
||
<div class="form-actions" style="margin-top:8px;">
|
||
<button class="btn" type="submit" id="saveServerBtn">Save server</button>
|
||
<button class="btn btn-ghost" type="button" id="testServerBtn">Test</button>
|
||
<button class="btn btn-ghost" type="button" id="clearServerFormBtn">New</button>
|
||
</div>
|
||
<div id="serverFormStatus" class="hint" style="margin-top:6px;"></div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="serverConfigSubpage" class="hidden">
|
||
<div class="card" style="margin-bottom:12px;">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Configure server <span class="chip" id="cfgServerName">--</span></div>
|
||
<div class="card-actions">
|
||
<button class="btn btn-ghost btn-sm" id="backToServersBtn" type="button">Back</button>
|
||
<button class="btn btn-ghost btn-sm" id="loadManagedConfigBtn" type="button">Reload config</button>
|
||
<button class="btn btn-sm" id="saveManagedConfigBtn" type="button">Save config</button>
|
||
</div>
|
||
</div>
|
||
<div class="statusbar"><span id="managedConfigStatus">Select Configure on a server to edit that node config.</span><span class="hint">This visual editor writes the same config.json on the selected node.</span></div>
|
||
</div>
|
||
|
||
<div class="grid2">
|
||
<div>
|
||
<div class="card">
|
||
<div class="card-hdr"><div class="card-title">Network</div><span class="chip green">live</span></div>
|
||
<div class="field">
|
||
<label>Main Listen (SSH / HTTP)</label>
|
||
<input type="text" id="managedCfgListen" placeholder="0.0.0.0:80"/>
|
||
</div>
|
||
<div class="field" style="margin-top:8px;">
|
||
<label>Extra Listen Addresses <span class="hint">(one per line, e.g. 0.0.0.0:8080)</span></label>
|
||
<textarea id="managedCfgExtraListen" rows="4" style="width:100%;box-sizing:border-box;background:var(--input-bg);border:1px solid var(--border);border-radius:8px;color:inherit;padding:10px;resize:vertical;"></textarea>
|
||
</div>
|
||
<div class="form-grid" style="margin-top:8px;">
|
||
<div class="field"><label>Proxy Auto Restart Interval <span class="hint">0s/off disables</span></label><input type="text" id="managedCfgProxyAutoRestart" placeholder="24h"/></div>
|
||
<div class="field"><label>Proxy Restart Grace Delay</label><input type="text" id="managedCfgProxyRestartGrace" placeholder="2s"/></div>
|
||
<div class="hint" style="grid-column:1/-1;margin-top:-4px;">Hard restart: closes public proxy listeners and active SSH sessions, then starts the listeners again. Use duration values like 6h, 12h, or 24h.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top:12px;">
|
||
<div class="card-hdr"><div class="card-title">SSH & General</div></div>
|
||
<div class="form-grid">
|
||
<div class="field"><label>Default Upload Limit (Mbps)</label><input type="number" id="managedCfgLimitUp" min="0" placeholder="0"/></div>
|
||
<div class="field"><label>Default Download Limit (Mbps)</label><input type="number" id="managedCfgLimitDown" min="0" placeholder="0"/></div>
|
||
<div class="field"><label>SSH Idle Timeout <span class="hint">0s/off = disabled</span></label><input type="text" id="managedCfgSSHIdleTimeout" placeholder="0s" title="Keep disabled for VPN/XHTTP connections."/></div>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;"><input type="checkbox" id="managedCfgQuiet"/> Quiet Logs</label>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;"><input type="checkbox" id="managedCfgUserCount"/> User Count Display</label>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;" title="Verify SSH logins against the Linux system password (/etc/shadow); regular accounts (UID ≥ 1000) are auto-imported."><input type="checkbox" id="managedCfgPamAuth"/> Linux PAM Login (auto-import)</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top:12px;">
|
||
<div class="card-hdr"><div class="card-title">SSH Banner</div><span class="chip green">live</span></div>
|
||
<div class="field">
|
||
<label>Banner Text <span class="hint">(shown to connecting SSH clients)</span></label>
|
||
<textarea id="managedCfgBanner" rows="5" style="width:100%;box-sizing:border-box;background:var(--input-bg);border:1px solid var(--border);border-radius:8px;color:inherit;padding:10px;resize:vertical;"></textarea>
|
||
</div>
|
||
<div class="hint" style="margin-top:6px;">Banner file: /opt/sshpanel/banner.txt</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">DNSTT Tunnel</div>
|
||
<span class="chip green">live</span>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;margin-left:auto;">
|
||
<input type="checkbox" id="managedCfgDnsttEnabled" onchange="toggleManagedDnsttFields(this.checked)"/> Enabled
|
||
</label>
|
||
</div>
|
||
<div id="managedDnsttFields" class="form-grid" style="opacity:.4;pointer-events:none;">
|
||
<div class="field" style="grid-column:1/-1"><label>NS / Root Domains <span class="hint">one per line</span></label><textarea id="managedCfgDnsttDomains" rows="3" placeholder="t.example.com t.local.lan"></textarea></div>
|
||
<div class="field"><label>UDP Listen</label><input type="text" id="managedCfgDnsttUDP" placeholder="[::]:5300"/></div>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1"><input type="checkbox" id="managedCfgDnsttFakeEnabled"/> Built-in Local DNS / Fake DNS</label>
|
||
<div class="field"><label>Local DNS Listen <span class="hint">IPv6 ok</span></label><input type="text" id="managedCfgDnsttFakeListen" placeholder="[2001:db8::1234]:53"/></div>
|
||
<div class="field"><label>Local DNS Domain</label><input type="text" id="managedCfgDnsttFakeDomain" placeholder="t.local.lan"/></div>
|
||
<div class="field"><label>Local DNS Workers <span class="hint">0=auto</span></label><input type="number" id="managedCfgDnsttFakeWorkers" min="0" placeholder="4"/></div>
|
||
<div class="field"><label>Response Workers <span class="hint">0=safe</span></label><input type="number" id="managedCfgDnsttRespWorkers" min="0" placeholder="1"/></div>
|
||
<div class="field"><label>Auto Restart Interval <span class="hint">0s/off disables</span></label><input type="text" id="managedCfgDnsttAutoRestart" placeholder="6h"/></div>
|
||
<div class="field"><label>Restart Grace Delay</label><input type="text" id="managedCfgDnsttRestartGrace" placeholder="2s"/></div>
|
||
<div class="hint" style="grid-column:1/-1;margin-top:-4px;">Restarts only the DNSTT UDP listener, not the VPS or the full panel. Use duration values like 30m, 2h, or 6h.</div>
|
||
<div class="field"><label>Max Sessions <span class="hint">0=default, -1=unlimited</span></label><input type="number" id="managedCfgDnsttMaxSessions" placeholder="10000"/></div>
|
||
<div class="field"><label>Max Streams <span class="hint">0=default, -1=unlimited</span></label><input type="number" id="managedCfgDnsttMaxStreams" placeholder="15000"/></div>
|
||
<div class="field"><label>Pending DNS Responses</label><input type="number" id="managedCfgDnsttPendingResponses" placeholder="20000"/></div>
|
||
<div class="field"><label>Stream Buffer Bytes</label><input type="number" id="managedCfgDnsttStreamBuffer" placeholder="262144"/></div>
|
||
<div class="field"><label>UDP Read Buffer Bytes</label><input type="number" id="managedCfgDnsttUDPReadBuffer" placeholder="16777216"/></div>
|
||
<div class="field"><label>UDP Write Buffer Bytes</label><input type="number" id="managedCfgDnsttUDPWriteBuffer" placeholder="16777216"/></div>
|
||
<div class="hint" style="grid-column:1/-1;margin-top:-4px;">Scale guard: bounded sessions/streams stop RAM explosions under thousands of DNSTT users. Keep verbose connection logs disabled on busy servers.</div>
|
||
<div class="field" style="grid-column:1/-1">
|
||
<label>Private Key <span class="hint">auto-saved to /opt/sshpanel/dnstt.key</span></label>
|
||
<div class="field-row">
|
||
<input type="text" id="managedCfgDnsttKey" readonly placeholder="Generate a key first…"/>
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="generateManagedDnsttKey()">Generate</button>
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="loadManagedDnsttPubkey()">Public Key</button>
|
||
</div>
|
||
<div id="managedDnsttPubkeyWrap" class="hidden" style="margin-top:6px;padding:8px;border-radius:8px;background:rgba(15,23,42,.9);border:1px solid rgba(55,65,81,.9);">
|
||
<div style="font-size:.68rem;color:var(--muted);margin-bottom:4px;">Public Key — share with dnstt clients</div>
|
||
<div class="field-row">
|
||
<input type="text" id="managedDnsttPubkeyVal" readonly style="font-family:monospace;font-size:.65rem;"/>
|
||
<button class="btn btn-ghost btn-sm" type="button" id="managedDnsttCopyBtn" onclick="navigator.clipboard.writeText(document.getElementById('managedDnsttPubkeyVal').value);document.getElementById('managedDnsttCopyBtn').textContent='Copied!';setTimeout(()=>document.getElementById('managedDnsttCopyBtn').textContent='Copy',1500)">Copy</button>
|
||
</div>
|
||
</div>
|
||
<div id="managedDnsttKeyStatus" class="hint" style="margin-top:4px;"></div>
|
||
</div>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1"><input type="checkbox" id="managedCfgDnsttNoStats"/> Disable Stats Log</label>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1"><input type="checkbox" id="managedCfgDnsttNoConsole"/> Disable Console Log</label>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1"><input type="checkbox" id="managedCfgDnsttLogConnections"/> Verbose Connection Logs</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top:12px">
|
||
<div class="card-hdr">
|
||
<div class="card-title">UDP Gateway</div>
|
||
<span class="chip green">live</span>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;margin-left:auto;">
|
||
<input type="checkbox" id="managedCfgUdpgwEnabled" onchange="toggleManagedUdpgwFields(this.checked)"/> Enabled
|
||
</label>
|
||
</div>
|
||
<div id="managedUdpgwFields" class="form-grid" style="opacity:.4;pointer-events:none;">
|
||
<div class="field"><label>Listen</label><input type="text" id="managedCfgUdpgwListen" placeholder="0.0.0.0:7400"/></div>
|
||
<div class="field"><label>Max UDP Sessions Per Client <span class="hint">(not total server users)</span></label><input type="number" id="managedCfgUdpgwMaxConns" min="0" placeholder="10"/></div>
|
||
<div class="field"><label>Idle Timeout</label><input type="text" id="managedCfgUdpgwIdle" placeholder="2m"/></div>
|
||
<div class="field"><label>Map TTL</label><input type="text" id="managedCfgUdpgwMapTTL" placeholder="90s"/></div>
|
||
<div class="field"><label>Auto Restart Interval <span class="hint">0s/off disables</span></label><input type="text" id="managedCfgUdpgwAutoRestart" placeholder="24h"/></div>
|
||
<div class="field"><label>Restart Grace Delay</label><input type="text" id="managedCfgUdpgwRestartGrace" placeholder="2s"/></div>
|
||
<div class="hint" style="grid-column:1/-1;margin-top:-4px;">Hard restart: closes the UDPGW listener and every connected UDPGW client, then starts UDPGW again.</div>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1"><input type="checkbox" id="managedCfgUdpgwDebug"/> Debug Logging</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top:12px">
|
||
<div class="card-hdr">
|
||
<div class="card-title">TLS Forwarders <span class="chip" id="managedTlsCountChip">0</span></div>
|
||
<span class="chip green">live</span>
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="toggleManagedAddTLSForm()">+ Add</button>
|
||
</div>
|
||
<div id="managedTlsForwardersList" style="margin-bottom:4px;"></div>
|
||
<div id="managedAddTLSPanel" class="hidden" style="border:1px solid var(--border);border-radius:8px;padding:10px;margin-top:6px;">
|
||
<div class="form-grid">
|
||
<div class="field" style="grid-column:1/-1"><label>Listen Address</label><input type="text" id="managedTlsListenAddr" placeholder="0.0.0.0:443"/></div>
|
||
<div class="field" style="grid-column:1/-1">
|
||
<label>Certificate</label>
|
||
<select id="managedTlsCertType" onchange="onManagedTLSTypeChange(this.value)">
|
||
<option value="selfsigned">Generate Self-Signed</option>
|
||
<option value="letsencrypt">Let's Encrypt (certbot)</option>
|
||
<option value="paste">Paste PEM text</option>
|
||
<option value="custom">Custom file paths</option>
|
||
</select>
|
||
</div>
|
||
<div id="managedTlsSSFields" style="grid-column:1/-1"><div class="field"><label>Domain Name <span class="hint">(CN for certificate)</span></label><input type="text" id="managedTlsSSLDomain" placeholder="example.com"/></div></div>
|
||
<div id="managedTlsLEFields" class="hidden" style="grid-column:1/-1;display:none;grid-template-columns:1fr 1fr;gap:8px;">
|
||
<div class="field"><label>Domain</label><input type="text" id="managedTlsLEDomain" placeholder="example.com"/></div>
|
||
<div class="field"><label>Email</label><input type="text" id="managedTlsLEEmail" placeholder="admin@example.com"/></div>
|
||
</div>
|
||
<div id="managedTlsCustomFields" class="hidden" style="grid-column:1/-1;display:none;grid-template-columns:1fr 1fr;gap:8px;">
|
||
<div class="field"><label>Cert File</label><input type="text" id="managedTlsCustomCert" placeholder="/path/to/fullchain.pem"/></div>
|
||
<div class="field"><label>Key File</label><input type="text" id="managedTlsCustomKey" placeholder="/path/to/privkey.pem"/></div>
|
||
</div>
|
||
<div id="managedTlsPasteFields" class="hidden" style="grid-column:1/-1;display:none;">
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">
|
||
<div class="field"><label>Certificate PEM</label><textarea id="managedTlsPasteCert" rows="5" placeholder="-----BEGIN CERTIFICATE----- …" style="font-family:monospace;font-size:.7rem;width:100%;box-sizing:border-box;resize:vertical;background:var(--input-bg);border:1px solid var(--border);border-radius:4px;color:inherit;padding:4px;"></textarea></div>
|
||
<div class="field"><label>Private Key PEM</label><textarea id="managedTlsPasteKey" rows="5" placeholder="-----BEGIN EC PRIVATE KEY----- …" style="font-family:monospace;font-size:.7rem;width:100%;box-sizing:border-box;resize:vertical;background:var(--input-bg);border:1px solid var(--border);border-radius:4px;color:inherit;padding:4px;"></textarea></div>
|
||
</div>
|
||
<div class="field" style="margin-top:6px;"><label>Name <span class="hint">(storage folder, e.g. my-cert)</span></label><input type="text" id="managedTlsPasteName" placeholder="my-cert"/></div>
|
||
</div>
|
||
</div>
|
||
<div class="form-actions" style="margin-top:8px;">
|
||
<button class="btn btn-sm" type="button" onclick="addManagedTLSForwarder()">Add Forwarder</button>
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="toggleManagedAddTLSForm()">Cancel</button>
|
||
</div>
|
||
<div id="managedTlsAddStatus" class="hint" style="margin-top:4px;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top:12px">
|
||
<div class="card-hdr"><div class="card-title">Xray Core</div><span class="chip green">live</span></div>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;margin-top:4px;"><input type="checkbox" id="managedCfgXrayEnabled"/> Enabled</label>
|
||
<div class="field" style="margin-top:8px;"><label>Runtime mode</label><select id="managedCfgXrayMode"><option value="native">Internal native emulator</option><option value="external">External xray binary</option></select></div>
|
||
<div class="hint" style="margin-top:6px;color:var(--muted);">Native runs inside DragonCoreSSH. External uses /opt/sshpanel/xray with DB-backed /opt/sshpanel/xray_config.json and Stats API on 127.0.0.1:10085.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="save-bar">
|
||
<div class="card-actions save-bar-actions">
|
||
<button class="btn" id="saveManagedConfigBottomBtn" type="button">Save Config</button>
|
||
<button class="btn btn-ghost" id="reloadManagedConfigBottomBtn" type="button">Reload</button>
|
||
</div>
|
||
<span class="hint">All service changes apply live on the selected node.</span>
|
||
</div>
|
||
</div>
|
||
</div><!-- /tab-servers -->
|
||
|
||
<!-- ═══════════ Servers Status Tab (superadmin only) ═══════════ -->
|
||
<div class="tab-pane" id="tab-servers-status">
|
||
<section class="page-hero status-hero" data-tone="green">
|
||
<div class="page-hero-copy"><span class="page-kicker">Live fleet</span><h2>Status dos servidores</h2><p>Saúde, carga e sessões ativas de cada nó gerenciado.</p></div>
|
||
<div class="workspace-hero-actions"><span class="workspace-live-status" id="serversStatusPageStatus">Aguardando dados</span><button class="btn btn-ghost btn-sm" id="refreshServersStatusBtn" type="button">Atualizar</button></div>
|
||
<div class="workspace-overview-grid">
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon blue">▣</span><div><small>Total</small><strong id="serversStatusCountChip">0</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon green">●</span><div><small>Online</small><strong id="fleetStatusOnline">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon red">●</span><div><small>Offline</small><strong id="fleetStatusOffline">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon amber">↗</span><div><small>Sessões</small><strong id="fleetStatusSessions">--</strong></div></article>
|
||
</div>
|
||
</section>
|
||
<div class="infra-nav-mount"></div>
|
||
<div class="servers-status-grid" id="serversStatusGrid"></div>
|
||
</div><!-- /tab-servers-status -->
|
||
|
||
<!-- ═══════════ Stats Tab (superadmin only) ═══════════ -->
|
||
<div class="tab-pane" id="tab-stats">
|
||
<section class="page-hero status-hero" data-tone="cyan">
|
||
<div class="page-hero-copy"><span class="page-kicker">Observability</span><h2>Monitoramento</h2><p>Recursos, interfaces e capacidade do servidor em tempo real.</p></div>
|
||
<div class="workspace-hero-actions"><span class="workspace-live-status is-ok" id="statsUpdated">Atualização ao vivo</span><button class="btn btn-ghost btn-sm" id="refreshStatsBtn" type="button">Atualizar</button></div>
|
||
<div class="workspace-overview-grid">
|
||
<article class="workspace-overview-card has-meter"><span class="workspace-overview-icon cyan">CPU</span><div><small>Processador</small><strong id="cpuVal">--%</strong><div class="workspace-mini-meter"><span id="cpuBar"></span></div></div></article>
|
||
<article class="workspace-overview-card has-meter"><span class="workspace-overview-icon purple">RAM</span><div><small>Memória</small><strong id="memVal">--%</strong><span class="workspace-card-note" id="memDetail">--</span><div class="workspace-mini-meter"><span id="memBar"></span></div></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon green">⇅</span><div><small>Rede agora</small><strong id="statsNetVal">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon amber">#</span><div><small>Interfaces</small><strong id="statsIfaceVal">--</strong></div></article>
|
||
</div>
|
||
</section>
|
||
<div class="infra-nav-mount"></div>
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Interfaces <span class="chip">rx/tx Mbps</span><span class="chip warn">30-day rolling</span></div>
|
||
<button class="btn btn-danger btn-sm" id="resetIfaceStatsBtn" type="button">Clean usage</button>
|
||
</div>
|
||
<div class="tbl-wrap">
|
||
<table>
|
||
<thead><tr><th>Interface</th><th>Rx Mbps</th><th>Tx Mbps</th><th>Rx Total</th><th>Tx Total</th></tr></thead>
|
||
<tbody id="ifaceBody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="statusbar"><span id="ifaceSummary"></span><span class="hint">Totals can be cleaned here and auto-clean every 30 days. VnStat history is separate.</span></div>
|
||
</div>
|
||
</div><!-- /tab-stats -->
|
||
|
||
<!-- ═══════════ VnStat Tab (superadmin only) ═══════════ -->
|
||
<div class="tab-pane" id="tab-vnstat">
|
||
<section class="page-hero status-hero" data-tone="purple">
|
||
<div class="page-hero-copy"><span class="page-kicker">Traffic intelligence</span><h2>Tráfego de rede</h2><p>Histórico diário e mensal para entender o consumo da infraestrutura.</p></div>
|
||
<div class="workspace-hero-actions"><span class="workspace-live-status" id="vnstatStatus">Aguardando dados</span><button class="btn btn-ghost btn-sm" id="reloadVnstatBtn" type="button">Atualizar</button><button class="btn btn-danger btn-sm" id="resetVnstatBtn" type="button">Limpar histórico</button></div>
|
||
<div class="workspace-overview-grid">
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon cyan">D</span><div><small>Hoje</small><strong id="vnTodayTotal">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon purple">M</span><div><small>Este mês</small><strong id="vnMonthTotal">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon green">⇅</span><div><small>Interfaces</small><strong id="vnIfaceCount">--</strong></div></article>
|
||
<article class="workspace-overview-card"><span class="workspace-overview-icon amber">◷</span><div><small>Período recente</small><strong id="vnLatestPeriod">--</strong></div></article>
|
||
</div>
|
||
</section>
|
||
<div class="infra-nav-mount"></div>
|
||
|
||
<div class="grid2" style="margin-top:12px;">
|
||
<div class="card">
|
||
<div class="card-hdr"><div class="card-title">Daily usage <span class="chip">last 31 days</span></div></div>
|
||
<div class="tbl-wrap">
|
||
<table>
|
||
<thead><tr><th>Day</th><th>Interface</th><th>Rx</th><th>Tx</th><th>Total</th></tr></thead>
|
||
<tbody id="vnstatDailyBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-hdr"><div class="card-title">Monthly usage <span class="chip">last 12 months</span></div></div>
|
||
<div class="tbl-wrap">
|
||
<table>
|
||
<thead><tr><th>Month</th><th>Interface</th><th>Rx</th><th>Tx</th><th>Total</th></tr></thead>
|
||
<tbody id="vnstatMonthlyBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div><!-- /tab-vnstat -->
|
||
|
||
<!-- ═══════════ Logs Tab (superadmin only) ═══════════ -->
|
||
<div class="tab-pane" id="tab-logs">
|
||
<section class="page-hero" data-tone="amber"><div class="page-hero-copy"><span class="page-kicker">Diagnostics</span><h2>Logs do sistema</h2><p>Investigue painel, DNSTT e Xray com uma visualização focada.</p></div><div class="page-hero-pills"><span>Panel</span><span>DNSTT</span><span>Xray</span></div></section>
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">System Logs</div>
|
||
<div class="form-actions" style="margin-top:0">
|
||
<select id="logSource" class="btn-ghost" style="border-radius:999px;padding:4px 8px;background:rgba(15,23,42,.9);color:var(--text);border:1px solid rgba(55,65,81,.9);font-size:.7rem;">
|
||
<option value="panel">Panel / system</option>
|
||
<option value="dnstt">DNSTT</option>
|
||
<option value="xray">Xray</option>
|
||
</select>
|
||
<button class="btn btn-sm" type="button" onclick="loadSystemLogs()">Refresh</button>
|
||
<button class="btn btn-danger btn-sm" type="button" id="clearPanelLogBtn">Clean panel log</button>
|
||
</div>
|
||
</div>
|
||
<pre class="log-box" id="systemLogBox" style="max-height:430px;min-height:260px;">Select a log source and click Refresh.</pre>
|
||
<div class="statusbar"><span id="systemLogStatus">Ready.</span></div>
|
||
</div>
|
||
</div><!-- /tab-logs -->
|
||
|
||
<!-- ═══════════ Bot / Vendas Tab (superadmin only) ═══════════ -->
|
||
<div class="tab-pane" id="tab-bot">
|
||
<section class="bot-hero">
|
||
<div class="bot-hero-copy">
|
||
<span class="bot-eyebrow">Central de automação</span>
|
||
<h2>Bot de vendas</h2>
|
||
<p>Telegram, PIX, planos e clientes em um único fluxo.</p>
|
||
</div>
|
||
<div class="bot-hero-actions">
|
||
<span id="botConfigStatus" class="bot-live-status">Aguardando dados</span>
|
||
<button class="btn btn-ghost btn-sm" id="botTestBtn" type="button">Testar integrações</button>
|
||
<button class="btn btn-ghost btn-sm" id="botConfigReloadBtn" type="button">Atualizar tudo</button>
|
||
</div>
|
||
<div class="bot-overview-grid">
|
||
<article class="bot-overview-card"><span class="bot-overview-icon">●</span><div><small>Operação</small><strong id="botMetricState">Carregando</strong></div></article>
|
||
<article class="bot-overview-card"><span class="bot-overview-icon bot-purple">◆</span><div><small>Planos</small><strong id="botMetricPlans">0</strong></div></article>
|
||
<article class="bot-overview-card"><span class="bot-overview-icon bot-green">↗</span><div><small>Clientes</small><strong id="botMetricUsers">0</strong></div></article>
|
||
<article class="bot-overview-card"><span class="bot-overview-icon bot-amber">$</span><div><small>PIX pendentes</small><strong id="botMetricPending">0</strong></div></article>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="bot-nav-shell">
|
||
<nav class="bot-section-nav" id="botSectionNav" aria-label="Áreas do bot">
|
||
<button class="active" type="button" data-bot-section="config"><span>⚙</span> Integrações</button>
|
||
<button type="button" data-bot-section="plans"><span>◆</span> Planos</button>
|
||
<button type="button" data-bot-section="packages"><span>▤</span> Créditos</button>
|
||
<button type="button" data-bot-section="messages"><span>✉</span> Mensagens</button>
|
||
<button type="button" data-bot-section="users"><span>◎</span> Clientes</button>
|
||
<button type="button" data-bot-section="transactions"><span>$</span> Pagamentos</button>
|
||
</nav>
|
||
<select id="botSection" class="bot-section-select" aria-label="Área do bot">
|
||
<option value="config">Integrações</option><option value="plans">Planos</option><option value="packages">Créditos</option>
|
||
<option value="messages">Mensagens</option><option value="users">Clientes</option><option value="transactions">Pagamentos</option>
|
||
</select>
|
||
</div>
|
||
|
||
<section class="bot-section active" id="botSec-config" data-bot-panel="config">
|
||
<div class="bot-section-heading"><div><span>01 · Integrações</span><h3>Conecte os serviços</h3><p>Tokens ficam criptografados. Campos vazios mantêm o valor atual.</p></div><button class="btn btn-sm" id="botConfigSaveBtn" type="button">Salvar e reiniciar bot</button></div>
|
||
<div class="bot-config-grid">
|
||
<article class="card bot-integration-card">
|
||
<div class="bot-card-heading"><div class="bot-service-icon telegram">✈</div><div><h4>Telegram</h4><p>Canal de atendimento e entrega</p></div><label class="bot-switch"><input id="botEnabled" type="checkbox"/><span></span></label></div>
|
||
<div class="field"><label>Token do @BotFather <span class="bot-secret-state" id="botHasTgToken"></span></label><input id="botTelegramToken" type="password" autocomplete="new-password" placeholder="Em branco para manter o token salvo"/></div>
|
||
<div class="field"><label>IDs dos administradores</label><input id="botAdminIDs" inputmode="numeric" placeholder="111111111, 222222222"/><span class="hint">Separe mais de um ID por vírgula.</span></div>
|
||
</article>
|
||
|
||
<article class="card bot-integration-card">
|
||
<div class="bot-card-heading"><div class="bot-service-icon mercado">MP</div><div><h4>Mercado Pago</h4><p>Recebimentos PIX automáticos</p></div></div>
|
||
<div class="field"><label>Access Token <span class="bot-secret-state" id="botHasMpToken"></span></label><input id="botMPToken" type="password" autocomplete="new-password" placeholder="APP_USR-… (vazio mantém o atual)"/></div>
|
||
<div class="form-grid">
|
||
<div class="field"><label>Confirmação</label><select id="botMPConfirmMode"><option value="polling">Polling — recomendado</option><option value="webhook">Webhook assinado</option></select></div>
|
||
<div class="field"><label>Intervalo</label><input id="botMPPollInterval" value="20s" placeholder="20s"/></div>
|
||
<div class="field"><label>Validade do PIX</label><div class="bot-input-suffix"><input id="botPixExp" type="number" min="5" max="1440" value="30"/><span>min</span></div></div>
|
||
</div>
|
||
<div id="botMPWebhookBox" class="bot-webhook-box hidden">
|
||
<div class="field"><label>Webhook Secret <span class="bot-secret-state" id="botHasMpSecret"></span></label><input id="botMPWebhookSecret" type="password" autocomplete="new-password" placeholder="Mínimo de 16 caracteres"/></div>
|
||
<div class="bot-copy-row"><code id="botMPWebhookURL">https://SEU_HOST/api/mp/webhook</code><button class="btn btn-ghost btn-sm" id="botCopyWebhookBtn" type="button">Copiar</button></div>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="card bot-integration-card">
|
||
<div class="bot-card-heading"><div class="bot-service-icon trial">◷</div><div><h4>Teste grátis</h4><p>Entrada controlada para novos clientes</p></div><label class="bot-switch"><input id="botTrialEnabled" type="checkbox"/><span></span></label></div>
|
||
<div class="form-grid">
|
||
<div class="field"><label>Duração</label><div class="bot-input-suffix"><input id="botTrialHours" type="number" min="1" max="720" value="1"/><span>h</span></div></div>
|
||
<div class="field"><label>Conexões</label><input id="botTrialMaxConns" type="number" min="1" max="1000" value="1"/></div>
|
||
<div class="field"><label>Tipo</label><select id="botTrialKind"><option value="ssh">SSH</option><option value="xray">Xray</option></select></div>
|
||
<div class="field"><label>Inbound Xray</label><input id="botTrialInbound" list="botInboundList" placeholder="Somente para Xray"/></div>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="card bot-integration-card">
|
||
<div class="bot-card-heading"><div class="bot-service-icon host">⌁</div><div><h4>Entrega</h4><p>Endereços enviados após a compra</p></div></div>
|
||
<div class="field"><label>Host/IP para SSH</label><input id="botPublicHost" placeholder="vpn.seudominio.com ou IP"/></div>
|
||
<div class="field"><label>Host para links Xray</label><input id="botXrayPublicHost" placeholder="xray.seudominio.com"/></div>
|
||
<div class="bot-note">Esses endereços aparecem nas credenciais entregues ao cliente.</div>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="bot-section" id="botSec-plans" data-bot-panel="plans">
|
||
<div class="bot-section-heading"><div><span>02 · Catálogo</span><h3>Planos de acesso</h3><p>Configure preço, validade, protocolo e limites.</p></div><div class="card-actions"><button class="btn btn-ghost btn-sm" id="botReloadPlansBtn" type="button">Atualizar</button><button class="btn btn-sm" id="botNewPlanBtn" type="button">Novo plano</button></div></div>
|
||
<div class="bot-master-detail">
|
||
<article class="card"><div class="bot-list-heading"><strong>Planos cadastrados</strong><span class="chip" id="botPlanCount">0</span></div><div class="tbl-wrap"><table class="bot-table"><thead><tr><th>Plano</th><th>Entrega</th><th>Validade</th><th>Preço</th><th>Status</th><th></th></tr></thead><tbody id="botPlansBody"></tbody></table></div><div class="statusbar"><span id="botPlansStatus">Aguardando.</span></div></article>
|
||
<article class="card bot-editor-card"><div class="card-hdr"><div><div class="card-title" id="botPlanFormTitle">Novo plano</div><p class="hint">Defina como a conta será criada.</p></div><button class="btn btn-ghost btn-sm" id="botCancelPlanBtn" type="button">Limpar</button></div>
|
||
<form id="botPlanForm"><input type="hidden" id="planId"/><div class="form-grid">
|
||
<div class="field bot-span-2"><label>Nome do plano</label><input id="planName" maxlength="120" required placeholder="Ex.: Premium 30 dias"/></div>
|
||
<div class="field"><label>Tipo</label><select id="planKind"><option value="ssh">SSH</option><option value="xray">Xray</option></select></div><div class="field"><label>Dias</label><input id="planDays" type="number" min="1" max="3650" value="30"/></div>
|
||
<div class="field"><label>Máx. conexões</label><input id="planMaxConns" type="number" min="0" max="10000" value="1"/></div><div class="field"><label>Preço (R$)</label><input id="planPrice" type="number" step="0.01" min="0" value="0"/></div>
|
||
<div class="field"><label>Upload Mbps</label><input id="planUpMbps" type="number" min="0" value="0"/></div><div class="field"><label>Download Mbps</label><input id="planDownMbps" type="number" min="0" value="0"/></div>
|
||
<div class="field"><label>Inbound Xray</label><input id="planInbound" list="botInboundList" placeholder="tag do inbound"/><datalist id="botInboundList"></datalist></div><div class="field"><label>Protocolo Xray</label><select id="planProtocol"><option value="">Automático</option><option value="vless">VLESS</option><option value="vmess">VMess</option><option value="trojan">Trojan</option></select></div>
|
||
<div class="field"><label>Custo em créditos</label><input id="planCreditCost" type="number" min="0" value="1"/></div><div class="field"><label>Servidor</label><input id="planServerId" placeholder="Vazio = local"/></div>
|
||
<div class="field"><label>Ordem</label><input id="planSort" type="number" value="0"/></div><label class="bot-check-field"><input id="planActive" type="checkbox" checked/><span>Plano visível no bot</span></label>
|
||
</div><div class="form-actions"><button class="btn" type="submit">Salvar plano</button></div></form>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="bot-section" id="botSec-packages" data-bot-panel="packages">
|
||
<div class="bot-section-heading"><div><span>03 · Revendedores</span><h3>Pacotes de créditos</h3><p>Crie recargas para revendedores comprarem planos.</p></div><div class="card-actions"><button class="btn btn-ghost btn-sm" id="botReloadPkgsBtn" type="button">Atualizar</button><button class="btn btn-sm" id="botNewPkgBtn" type="button">Novo pacote</button></div></div>
|
||
<div class="bot-master-detail">
|
||
<article class="card"><div class="bot-list-heading"><strong>Pacotes cadastrados</strong><span class="chip" id="botPkgCount">0</span></div><div class="tbl-wrap"><table class="bot-table"><thead><tr><th>Pacote</th><th>Créditos</th><th>Preço</th><th>Status</th><th></th></tr></thead><tbody id="botPkgsBody"></tbody></table></div><div class="statusbar"><span id="botPkgStatus">Aguardando.</span></div></article>
|
||
<article class="card bot-editor-card"><div class="card-hdr"><div><div class="card-title">Editar pacote</div><p class="hint">Defina o saldo entregue e o preço PIX.</p></div></div><form id="botPkgForm"><input type="hidden" id="pkgId"/><div class="form-grid">
|
||
<div class="field bot-span-2"><label>Nome</label><input id="pkgName" maxlength="120" required placeholder="Ex.: Recarga 50 créditos"/></div><div class="field"><label>Créditos</label><input id="pkgCredits" type="number" min="1" value="10"/></div><div class="field"><label>Preço (R$)</label><input id="pkgPrice" type="number" step="0.01" min="0" value="0"/></div><div class="field"><label>Ordem</label><input id="pkgSort" type="number" value="0"/></div><label class="bot-check-field"><input id="pkgActive" type="checkbox" checked/><span>Pacote visível</span></label>
|
||
</div><div class="form-actions"><button class="btn" type="submit">Salvar pacote</button><button class="btn btn-ghost" id="botClearPkgBtn" type="button">Limpar</button></div></form></article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="bot-section" id="botSec-messages" data-bot-panel="messages">
|
||
<div class="bot-section-heading"><div><span>04 · Conteúdo</span><h3>Mensagens do bot</h3><p>Personalize os textos vistos pelo cliente.</p></div><button class="btn btn-ghost btn-sm" id="botReloadSettingsBtn" type="button">Restaurar salvo</button></div>
|
||
<article class="card bot-message-editor"><div class="bot-message-grid"><div class="field"><label>Mensagem de boas-vindas</label><textarea id="setWelcome" rows="7" maxlength="4096" placeholder="Olá, {name}! Escolha uma opção abaixo."></textarea><span class="hint">Use <code>{name}</code> para inserir o primeiro nome.</span></div><div class="field"><label>Contato e suporte</label><textarea id="setContact" rows="7" maxlength="4096" placeholder="Explique como falar com o suporte."></textarea></div><div class="field"><label>Texto para baixar o app</label><textarea id="setAppText" rows="5" maxlength="4096" placeholder="Baixe nosso aplicativo para conectar."></textarea></div><div class="field"><label>Link do aplicativo</label><input id="setAppUrl" type="url" maxlength="2048" placeholder="https://..."/><span class="hint">Somente links HTTP ou HTTPS.</span></div></div><div class="bot-save-row"><span id="botSettingsStatus" class="hint">Aguardando.</span><button class="btn" id="botSaveSettingsBtn" type="button">Salvar mensagens</button></div></article>
|
||
</section>
|
||
|
||
<section class="bot-section" id="botSec-users" data-bot-panel="users">
|
||
<div class="bot-section-heading"><div><span>05 · Relacionamento</span><h3>Clientes do Telegram</h3><p>Gerencie funções, vínculos e saldo sem sair da lista.</p></div><button class="btn btn-ghost btn-sm" id="botReloadUsersBtn" type="button">Atualizar clientes</button></div>
|
||
<article class="card"><div class="bot-list-heading"><div><strong>Base de clientes</strong><span class="hint"> Ações administrativas são registradas no livro de créditos.</span></div><span class="chip" id="botUserCount">0</span></div><div class="tbl-wrap"><table class="bot-table"><thead><tr><th>Cliente</th><th>Telegram ID</th><th>Função</th><th>Revenda vinculada</th><th>Saldo</th><th></th></tr></thead><tbody id="botUsersBody"></tbody></table></div><div class="statusbar"><span id="botUsersStatus">Aguardando.</span></div></article>
|
||
</section>
|
||
|
||
<section class="bot-section" id="botSec-transactions" data-bot-panel="transactions">
|
||
<div class="bot-section-heading"><div><span>06 · Financeiro</span><h3>Pagamentos PIX</h3><p>Acompanhe confirmações e reprocessamentos.</p></div><div class="card-actions"><select id="botTxnFilter" class="input-sm"><option value="">Todos os status</option><option value="pending">Pendentes</option><option value="approved">Aprovados</option><option value="expired">Expirados</option><option value="refunded">Marcados como estornados</option><option value="error">Com erro</option></select><button class="btn btn-ghost btn-sm" id="botReloadTxnsBtn" type="button">Atualizar</button></div></div>
|
||
<article class="card"><div class="bot-list-heading"><strong>Histórico recente</strong><span class="chip" id="botTxnCount">0</span></div><div class="tbl-wrap"><table class="bot-table"><thead><tr><th>Pagamento</th><th>Cliente</th><th>Tipo</th><th>Valor</th><th>Status</th><th>Entrega</th><th>Data</th><th></th></tr></thead><tbody id="botTxnsBody"></tbody></table></div><div class="statusbar"><span id="botTxnStatus">Aguardando.</span></div></article>
|
||
</section>
|
||
|
||
<div class="bot-modal hidden" id="botUserActionModal" role="dialog" aria-modal="true" aria-labelledby="botUserActionTitle">
|
||
<div class="bot-modal-backdrop" data-bot-modal-close></div>
|
||
<div class="bot-modal-card">
|
||
<div class="card-hdr"><div><div class="card-title" id="botUserActionTitle">Editar cliente</div><p class="hint" id="botUserActionSubtitle"></p></div><button class="btn btn-ghost btn-sm" type="button" data-bot-modal-close>Fechar</button></div>
|
||
<form id="botUserActionForm"><input type="hidden" id="botActionTelegramID"/><input type="hidden" id="botActionMode"/>
|
||
<div id="botRoleFields"><div class="field"><label>Função</label><select id="botActionRole"><option value="customer">Cliente</option><option value="reseller">Revendedor</option><option value="blocked">Bloqueado</option></select></div><div class="field" id="botActionLinkedField"><label>Usuário da revenda vinculada</label><input id="botActionLinked" placeholder="username em Revendedores"/></div></div>
|
||
<div id="botCreditFields" class="hidden"><div class="field"><label>Ajuste de créditos</label><input id="botActionCredits" type="number" placeholder="Ex.: 10 ou -5"/><span class="hint">Use número negativo para remover saldo.</span></div></div>
|
||
<div class="form-actions"><button class="btn" type="submit">Confirmar alteração</button></div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div><!-- /tab-bot -->
|
||
|
||
<!-- ═══════════ Server Config Tab (superadmin only) ═══════════ -->
|
||
<div class="tab-pane" id="tab-server">
|
||
<section class="page-hero" data-tone="blue"><div class="page-hero-copy"><span class="page-kicker">System studio</span><h2>Configurações</h2><p>Rede, SSH, túneis e TLS organizados em blocos visuais e aplicados ao vivo.</p></div></section>
|
||
|
||
<div class="workspace-nav-shell" data-tone="blue">
|
||
<nav class="workspace-section-nav" id="configSectionNav" aria-label="Áreas de configuração" style="--workspace-nav-columns:5">
|
||
<button class="active" type="button" data-workspace="config" data-workspace-section="general"><span>⌁</span> Rede e SSH</button>
|
||
<button type="button" data-workspace="config" data-workspace-section="slowdns"><span>DNS</span> SlowDNS</button>
|
||
<button type="button" data-workspace="config" data-workspace-section="udp"><span>⇅</span> UDP Gateway</button>
|
||
<button type="button" data-workspace="config" data-workspace-section="tls"><span>◇</span> TLS</button>
|
||
<button type="button" data-workspace="config" data-workspace-section="xray"><span>◆</span> Xray</button>
|
||
</nav>
|
||
<select id="configSection" class="workspace-section-select" data-workspace-select="config" aria-label="Área de configuração">
|
||
<option value="general">Rede e SSH</option><option value="slowdns">SlowDNS</option><option value="udp">UDP Gateway</option><option value="tls">TLS</option><option value="xray">Xray</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="settings-workspace">
|
||
<section class="workspace-section active" data-workspace-panel="config" data-workspace-section-panel="general">
|
||
<div class="workspace-section-heading"><div><span>01 · Base</span><h3>Rede e SSH</h3><p>Configure listeners, limites padrão, tempo ocioso e o banner de conexão.</p></div></div>
|
||
<div class="settings-panel-grid">
|
||
|
||
<!-- Network -->
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Network</div>
|
||
<span class="chip green">live</span>
|
||
</div>
|
||
<div class="form-grid">
|
||
<div class="field" style="grid-column:1/-1">
|
||
<label>Main Listen (SSH / HTTP)</label>
|
||
<input type="text" id="cfgListen" placeholder="0.0.0.0:80"/>
|
||
</div>
|
||
</div>
|
||
<div class="field" style="margin-top:6px">
|
||
<label>Extra Listen Addresses <span class="hint">(one per line, e.g. 0.0.0.0:8080)</span></label>
|
||
<textarea id="cfgExtraListen" rows="3" style="resize:vertical"></textarea>
|
||
</div>
|
||
<div class="form-grid" style="margin-top:8px;">
|
||
<div class="field">
|
||
<label>Proxy Auto Restart Interval <span class="hint">0s/off disables</span></label>
|
||
<input type="text" id="cfgProxyAutoRestart" placeholder="24h"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Proxy Restart Grace Delay</label>
|
||
<input type="text" id="cfgProxyRestartGrace" placeholder="2s"/>
|
||
</div>
|
||
<div class="hint" style="grid-column:1/-1;margin-top:-4px;">Hard restart: closes public proxy listeners and active SSH sessions, then starts the listeners again. Use duration values like 6h, 12h, or 24h.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SSH & general -->
|
||
<div class="card" style="margin-top:12px">
|
||
<div class="card-hdr">
|
||
<div class="card-title">SSH & General</div>
|
||
</div>
|
||
<div class="form-grid">
|
||
<div class="field">
|
||
<label>Default Upload Limit (Mbps)</label>
|
||
<input type="number" id="cfgLimitUp" min="0" placeholder="0 = unlimited"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Default Download Limit (Mbps)</label>
|
||
<input type="number" id="cfgLimitDown" min="0" placeholder="0 = unlimited"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Max Total SSH Connections <span class="hint">0 = default (10000) · -1 = unlimited</span></label>
|
||
<input type="number" id="cfgMaxTotalConns" min="-1" placeholder="10000" title="Global cap across all users. New connections past this are rejected before the SSH handshake, so a surge cannot exhaust memory/CPU. Raise it if the server has resources for more; set -1 to remove the cap entirely."/>
|
||
</div>
|
||
<div class="field">
|
||
<label>SSH Idle Timeout <span class="hint">0s/off = disabled</span></label>
|
||
<input type="text" id="cfgSSHIdleTimeout" placeholder="0s" title="Closes an authenticated SSH connection only after this period with no bytes in either direction. Keep disabled for VPN/XHTTP connections."/>
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;gap:16px;margin-top:8px;flex-wrap:wrap;">
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;">
|
||
<input type="checkbox" id="cfgQuiet"/> Quiet Logs
|
||
</label>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;">
|
||
<input type="checkbox" id="cfgUserCount"/> User Count Display
|
||
</label>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;" title="Verify SSH logins against the Linux system password (/etc/shadow). Regular accounts (UID ≥ 1000) that log in successfully are auto-imported into the panel.">
|
||
<input type="checkbox" id="cfgPamAuth"/> Linux PAM Login (auto-import)
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Banner -->
|
||
<div class="card settings-span-all" style="margin-top:12px">
|
||
<div class="card-hdr">
|
||
<div class="card-title">SSH Banner</div>
|
||
<span class="chip green">live</span>
|
||
</div>
|
||
<div class="field">
|
||
<label>Banner Text <span class="hint">(shown to connecting SSH clients)</span></label>
|
||
<textarea id="cfgBanner" rows="4" style="resize:vertical"></textarea>
|
||
</div>
|
||
<div class="hint" style="margin-top:6px;">Banner file: /opt/sshpanel/banner.txt</div>
|
||
</div>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
<section class="workspace-section" data-workspace-panel="config" data-workspace-section-panel="slowdns">
|
||
<div class="workspace-section-heading"><div><span>02 · Túnel DNS</span><h3>SlowDNS / DNSTT</h3><p>Gerencie domínios, DNS local, capacidade, filas e reinício controlado.</p></div></div>
|
||
<!-- DNSTT -->
|
||
<div class="card">
|
||
<div class="card-hdr">
|
||
<div class="card-title">DNSTT Tunnel</div>
|
||
<span class="chip green">live</span>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;margin-left:auto;">
|
||
<input type="checkbox" id="cfgDnsttEnabled" onchange="toggleDnsttFields(this.checked)"/> Enabled
|
||
</label>
|
||
</div>
|
||
<div id="dnsttFields" class="form-grid" style="opacity:.4;pointer-events:none;">
|
||
<div class="field" style="grid-column:1/-1">
|
||
<label>NS / Root Domains <span class="hint">one per line</span></label>
|
||
<textarea id="cfgDnsttDomains" rows="3" placeholder="t.example.com t.local.lan"></textarea>
|
||
</div>
|
||
<div class="field">
|
||
<label>UDP Listen</label>
|
||
<input type="text" id="cfgDnsttUDP" placeholder="[::]:5300"/>
|
||
</div>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1">
|
||
<input type="checkbox" id="cfgDnsttFakeEnabled"/> Built-in Local DNS / Fake DNS
|
||
</label>
|
||
<div class="field">
|
||
<label>Local DNS Listen <span class="hint">IPv6 ok</span></label>
|
||
<input type="text" id="cfgDnsttFakeListen" placeholder="[2001:db8::1234]:53"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Local DNS Domain</label>
|
||
<input type="text" id="cfgDnsttFakeDomain" placeholder="t.local.lan"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Local DNS Workers <span class="hint">0=auto</span></label>
|
||
<input type="number" id="cfgDnsttFakeWorkers" min="0" placeholder="4"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Response Workers <span class="hint">0=safe</span></label>
|
||
<input type="number" id="cfgDnsttRespWorkers" min="0" placeholder="1"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Auto Restart Interval <span class="hint">0s/off disables</span></label>
|
||
<input type="text" id="cfgDnsttAutoRestart" placeholder="6h"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Restart Grace Delay</label>
|
||
<input type="text" id="cfgDnsttRestartGrace" placeholder="2s"/>
|
||
</div>
|
||
<div class="hint" style="grid-column:1/-1;margin-top:-4px;">Restarts only the DNSTT UDP listener, not the VPS or the full panel. Use duration values like 30m, 2h, or 6h.</div>
|
||
<div class="field"><label>Max Sessions <span class="hint">0=default, -1=unlimited</span></label><input type="number" id="cfgDnsttMaxSessions" placeholder="10000"/></div>
|
||
<div class="field"><label>Max Streams <span class="hint">0=default, -1=unlimited</span></label><input type="number" id="cfgDnsttMaxStreams" placeholder="15000"/></div>
|
||
<div class="field"><label>Pending DNS Responses</label><input type="number" id="cfgDnsttPendingResponses" placeholder="20000"/></div>
|
||
<div class="field"><label>Stream Buffer Bytes</label><input type="number" id="cfgDnsttStreamBuffer" placeholder="262144"/></div>
|
||
<div class="field"><label>UDP Read Buffer Bytes</label><input type="number" id="cfgDnsttUDPReadBuffer" placeholder="16777216"/></div>
|
||
<div class="field"><label>UDP Write Buffer Bytes</label><input type="number" id="cfgDnsttUDPWriteBuffer" placeholder="16777216"/></div>
|
||
<div class="hint" style="grid-column:1/-1;margin-top:-4px;">Scale guard: bounded sessions/streams stop RAM explosions under thousands of DNSTT users. Keep verbose connection logs disabled on busy servers.</div>
|
||
<div class="field" style="grid-column:1/-1">
|
||
<label>Private Key <span class="hint">auto-saved to /opt/sshpanel/dnstt.key</span></label>
|
||
<div class="field-row">
|
||
<input type="text" id="cfgDnsttKey" readonly placeholder="Generate a key first…"/>
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="generateDnsttKey()">Generate</button>
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="loadDnsttPubkey()">Public Key</button>
|
||
</div>
|
||
<div id="dnsttPubkeyWrap" class="hidden" style="margin-top:6px;padding:8px;border-radius:8px;background:rgba(15,23,42,.9);border:1px solid rgba(55,65,81,.9);">
|
||
<div style="font-size:.68rem;color:var(--muted);margin-bottom:4px;">Public Key — share with dnstt clients</div>
|
||
<div class="field-row">
|
||
<input type="text" id="dnsttPubkeyVal" readonly style="font-family:monospace;font-size:.65rem;"/>
|
||
<button class="btn btn-ghost btn-sm" type="button" id="dnsttCopyBtn" onclick="navigator.clipboard.writeText(document.getElementById('dnsttPubkeyVal').value);document.getElementById('dnsttCopyBtn').textContent='Copied!';setTimeout(()=>document.getElementById('dnsttCopyBtn').textContent='Copy',1500)">Copy</button>
|
||
</div>
|
||
</div>
|
||
<div id="dnsttKeyStatus" class="hint" style="margin-top:4px;"></div>
|
||
</div>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1">
|
||
<input type="checkbox" id="cfgDnsttNoStats"/> Disable Stats Log
|
||
</label>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1">
|
||
<input type="checkbox" id="cfgDnsttNoConsole"/> Disable Console Log
|
||
</label>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1">
|
||
<input type="checkbox" id="cfgDnsttLogConnections"/> Verbose Connection Logs
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
</section>
|
||
|
||
<section class="workspace-section" data-workspace-panel="config" data-workspace-section-panel="udp">
|
||
<div class="workspace-section-heading"><div><span>03 · UDP</span><h3>UDP Gateway</h3><p>Defina listener, capacidade, expiração de mapa e reinício do serviço.</p></div></div>
|
||
<!-- UDPGW -->
|
||
<div class="card" style="margin-top:12px">
|
||
<div class="card-hdr">
|
||
<div class="card-title">UDP Gateway</div>
|
||
<span class="chip green">live</span>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;margin-left:auto;">
|
||
<input type="checkbox" id="cfgUdpgwEnabled" onchange="toggleUdpgwFields(this.checked)"/> Enabled
|
||
</label>
|
||
</div>
|
||
<div id="udpgwFields" class="form-grid" style="opacity:.4;pointer-events:none;">
|
||
<div class="field">
|
||
<label>Listen</label>
|
||
<input type="text" id="cfgUdpgwListen" placeholder="0.0.0.0:7400"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Max UDP Sessions Per Client <span class="hint">(not total server users)</span></label>
|
||
<input type="number" id="cfgUdpgwMaxConns" min="0" placeholder="10" title="Per connected client. This is not the total number of UDPGW users allowed on the server."/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Max Total Clients <span class="hint">0 = default (10000)</span></label>
|
||
<input type="number" id="cfgUdpgwMaxClients" min="0" placeholder="10000" title="Total concurrent UDPGW client connections allowed on the server. New clients past this are rejected. Raise it if the server has resources for more."/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Idle Timeout</label>
|
||
<input type="text" id="cfgUdpgwIdle" placeholder="2m"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Map TTL</label>
|
||
<input type="text" id="cfgUdpgwMapTTL" placeholder="90s"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Auto Restart Interval <span class="hint">0s/off disables</span></label>
|
||
<input type="text" id="cfgUdpgwAutoRestart" placeholder="24h"/>
|
||
</div>
|
||
<div class="field">
|
||
<label>Restart Grace Delay</label>
|
||
<input type="text" id="cfgUdpgwRestartGrace" placeholder="2s"/>
|
||
</div>
|
||
<div class="hint" style="grid-column:1/-1;margin-top:-4px;">Hard restart: closes the UDPGW listener and every connected UDPGW client, then starts UDPGW again.</div>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1">
|
||
<input type="checkbox" id="cfgUdpgwDebug"/> Debug Logging
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
</section>
|
||
|
||
<section class="workspace-section" data-workspace-panel="config" data-workspace-section-panel="tls">
|
||
<div class="workspace-section-heading"><div><span>04 · Segurança</span><h3>Encaminhadores TLS</h3><p>Crie listeners TLS com certificado automático, colado ou armazenado em arquivo.</p></div></div>
|
||
<!-- TLS Forwarders -->
|
||
<div class="card" style="margin-top:12px">
|
||
<div class="card-hdr">
|
||
<div class="card-title">TLS Forwarders <span class="chip" id="tlsCountChip">0</span></div>
|
||
<span class="chip green">live</span>
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="toggleAddTLSForm()">+ Add</button>
|
||
</div>
|
||
<div id="tlsForwardersList" style="margin-bottom:4px;"></div>
|
||
<div id="addTLSPanel" class="hidden" style="border:1px solid var(--border);border-radius:8px;padding:10px;margin-top:6px;">
|
||
<div class="form-grid">
|
||
<div class="field" style="grid-column:1/-1">
|
||
<label>Listen Address</label>
|
||
<input type="text" id="tlsListenAddr" placeholder="0.0.0.0:443"/>
|
||
</div>
|
||
<div class="field" style="grid-column:1/-1">
|
||
<label>Certificate</label>
|
||
<select id="tlsCertType" onchange="onTLSTypeChange(this.value)">
|
||
<option value="selfsigned">Generate Self-Signed</option>
|
||
<option value="letsencrypt">Let's Encrypt (certbot)</option>
|
||
<option value="paste">Paste PEM text</option>
|
||
<option value="custom">Custom file paths</option>
|
||
</select>
|
||
</div>
|
||
<div id="tlsSSFields" style="grid-column:1/-1">
|
||
<div class="field"><label>Domain Name <span class="hint">(CN for certificate)</span></label><input type="text" id="tlsSSLDomain" placeholder="example.com"/></div>
|
||
</div>
|
||
<div id="tlsLEFields" class="hidden" style="grid-column:1/-1;display:none;grid-template-columns:1fr 1fr;gap:8px;">
|
||
<div class="field"><label>Domain</label><input type="text" id="tlsLEDomain" placeholder="example.com"/></div>
|
||
<div class="field"><label>Email</label><input type="text" id="tlsLEEmail" placeholder="admin@example.com"/></div>
|
||
</div>
|
||
<div id="tlsCustomFields" class="hidden" style="grid-column:1/-1;display:none;grid-template-columns:1fr 1fr;gap:8px;">
|
||
<div class="field"><label>Cert File</label><input type="text" id="tlsCustomCert" placeholder="/path/to/fullchain.pem"/></div>
|
||
<div class="field"><label>Key File</label><input type="text" id="tlsCustomKey" placeholder="/path/to/privkey.pem"/></div>
|
||
</div>
|
||
<div id="tlsPasteFields" class="hidden" style="grid-column:1/-1;display:none;">
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">
|
||
<div class="field"><label>Certificate PEM</label><textarea id="tlsPasteCert" rows="5" placeholder="-----BEGIN CERTIFICATE----- …" style="font-family:monospace;font-size:.7rem;width:100%;box-sizing:border-box;resize:vertical;background:var(--input-bg);border:1px solid var(--border);border-radius:4px;color:inherit;padding:4px;"></textarea></div>
|
||
<div class="field"><label>Private Key PEM</label><textarea id="tlsPasteKey" rows="5" placeholder="-----BEGIN EC PRIVATE KEY----- …" style="font-family:monospace;font-size:.7rem;width:100%;box-sizing:border-box;resize:vertical;background:var(--input-bg);border:1px solid var(--border);border-radius:4px;color:inherit;padding:4px;"></textarea></div>
|
||
</div>
|
||
<div class="field" style="margin-top:6px;"><label>Name <span class="hint">(storage folder, e.g. my-cert)</span></label><input type="text" id="tlsPasteName" placeholder="my-cert"/></div>
|
||
</div>
|
||
</div>
|
||
<div class="form-actions" style="margin-top:8px;">
|
||
<button class="btn btn-sm" type="button" onclick="addTLSForwarder()">Add Forwarder</button>
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="toggleAddTLSForm()">Cancel</button>
|
||
</div>
|
||
<div id="tlsAddStatus" class="hint" style="margin-top:4px;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
</section>
|
||
|
||
<section class="workspace-section" data-workspace-panel="config" data-workspace-section-panel="xray">
|
||
<div class="workspace-section-heading"><div><span>05 · Core</span><h3>Xray</h3><p>Ative o core, escolha o runtime e aplique os ajustes nativos seguros.</p></div></div>
|
||
<!-- Xray -->
|
||
<div class="card" style="margin-top:12px">
|
||
<div class="card-hdr">
|
||
<div class="card-title">Xray Core</div>
|
||
<span class="chip green">live</span>
|
||
</div>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;margin-top:4px;">
|
||
<input type="checkbox" id="cfgXrayEnabled"/> Enabled
|
||
</label>
|
||
<div class="field" style="margin-top:8px;"><label>Runtime mode</label><select id="cfgXrayMode"><option value="native">Internal native emulator</option><option value="external">External xray binary</option></select></div>
|
||
<div class="hint" style="margin-top:6px;color:var(--muted);">Native runs inside DragonCoreSSH. External uses /opt/sshpanel/xray with DB-backed /opt/sshpanel/xray_config.json and Stats API on 127.0.0.1:10085.</div>
|
||
<details style="margin-top:10px;" open>
|
||
<summary style="cursor:pointer;font-size:.76rem;font-weight:700;color:var(--text);">Native Xray scale tuning</summary>
|
||
<div class="grid2" style="margin-top:10px;gap:8px;">
|
||
<div class="field"><label>Go CPU threads (GOMAXPROCS)</label><input type="number" min="0" id="cfgXrayRuntimeGomaxprocs" placeholder="0 = all CPU cores"/></div>
|
||
<div class="field"><label>Global mux backend sessions</label><input type="number" min="1" id="cfgXrayMuxGlobalSessions" placeholder="32768"/></div>
|
||
<div class="field"><label>Global transport connections <span class="hint">0=32768, -1=unlimited</span></label><input type="number" min="-1" id="cfgXrayMaxConnections" placeholder="32768"/></div>
|
||
<div class="field"><label>XHTTP web request cap <span class="hint">disabled for VPN traffic</span></label><input type="number" min="-1" id="cfgXrayMaxXHTTPRequests" value="-1" readonly/></div>
|
||
<div class="field"><label>Active XHTTP sessions <span class="hint">0=32768, -1=unlimited</span></label><input type="number" min="-1" id="cfgXrayMaxXHTTPSessions" placeholder="32768"/></div>
|
||
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1"><input type="checkbox" id="cfgXrayTracePackets"/> Trace every XHTTP/mux packet <span class="hint">debug only, slows QUIC</span></label>
|
||
<div class="card-actions" style="grid-column:1/-1;">
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="setXrayNativeTuningDefaults('high')">Apply high-traffic VPN defaults</button>
|
||
<button class="btn btn-ghost btn-sm" type="button" onclick="setXrayNativeTuningDefaults('safe')">Apply safe defaults</button>
|
||
</div>
|
||
<div class="hint" style="grid-column:1/-1;margin-top:-4px;">XHTTP is handled as VPN tunnel traffic: packet requests use bounded backpressure and are never rejected by an HTTP request-rate ceiling. At the transport ceiling, new sockets wait in the kernel backlog instead of being reset. Keep the safe defaults unless the server is sized and load-tested for the high-traffic profile. HTTP/2 retains a 1024-stream flow-control guard per connection, while upload memory stays globally bounded. Saved in the panel config and applied live on restart/reload.</div>
|
||
</div>
|
||
</details>
|
||
</div>
|
||
|
||
</section>
|
||
</div><!-- /settings-workspace -->
|
||
|
||
<!-- Save bar -->
|
||
<div class="save-bar">
|
||
<div class="card-actions save-bar-actions">
|
||
<button class="btn" onclick="saveServerConfig()">Save Config</button>
|
||
<button class="btn btn-ghost" onclick="loadServerConfig()">Reload</button>
|
||
</div>
|
||
<span id="srvCfgStatus" class="hint">All service changes apply live.</span>
|
||
</div>
|
||
|
||
</div><!-- /tab-server -->
|
||
|
||
</main>
|
||
</section>
|
||
</div><!-- /panel-layout -->
|
||
</div><!-- /mainApp -->
|
||
</div><!-- /shell -->
|
||
</div><!-- /app -->
|
||
|
||
<div class="panel-dialog hidden" id="panelConfirmDialog" aria-hidden="true">
|
||
<div class="panel-dialog-backdrop" data-panel-dialog-close></div>
|
||
<section class="panel-dialog-card" role="dialog" aria-modal="true" aria-labelledby="panelDialogTitle" aria-describedby="panelDialogMessage">
|
||
<div class="panel-dialog-head"><span class="panel-dialog-icon" id="panelDialogIcon" aria-hidden="true">?</span><div><span class="page-kicker" id="panelDialogEyebrow">Confirmação</span><h3 id="panelDialogTitle">Confirmar ação</h3></div></div>
|
||
<p id="panelDialogMessage"></p>
|
||
<div class="panel-dialog-detail hidden" id="panelDialogDetail"></div>
|
||
<div class="panel-dialog-actions"><button class="btn btn-ghost" id="panelDialogCancelBtn" type="button">Cancelar</button><button class="btn" id="panelDialogConfirmBtn" type="button">Confirmar</button></div>
|
||
</section>
|
||
</div>
|
||
<div class="panel-toast-stack" id="panelToastStack" aria-live="polite" aria-atomic="false"></div>
|
||
|
||
<!-- app.js was split into ordered modules for maintainability. They are plain
|
||
classic scripts sharing one global scope; `defer` preserves execution order,
|
||
so behavior is identical to the old single file. Keep this load order. -->
|
||
<script defer src="assets/js/01-core.js?v=20260720xraylist1"></script>
|
||
<script defer src="assets/js/02-shell.js?v=20260714pamfix1"></script>
|
||
<script defer src="assets/js/03-ssh-users.js?v=20260719quotaaudit1"></script>
|
||
<script defer src="assets/js/04-xray.js?v=20260720xraylist1"></script>
|
||
<script defer src="assets/js/05-resellers.js?v=20260714pamfix1"></script>
|
||
<script defer src="assets/js/06-servers.js?v=20260714pamfix1"></script>
|
||
<script defer src="assets/js/07-stats-logs.js?v=20260714pamfix1"></script>
|
||
<script defer src="assets/js/08-server-config.js?v=20260719xhttp502fix1"></script>
|
||
<script defer src="assets/js/09-xray-wizard.js?v=20260714quota1"></script>
|
||
<script defer src="assets/js/11-update-status.js?v=20260714pamfix1"></script>
|
||
<script defer src="assets/js/12-bot.js?v=20260714pamfix1"></script>
|
||
<script defer src="assets/js/10-boot.js?v=20260714pamfix1"></script>
|
||
</body>
|
||
</html>
|