monitor/web/main/diagnostics.ejs
2025-04-16 22:30:27 +07:00

315 lines
16 KiB
Plaintext

<%- await include('parts/header.ejs', locals) %>
<div class="row justify-content-md-center">
<div class="col-lg-8 col-xl-5 mw-col6">
<!-- Environment Card -->
<div class="card card-accent-danger" style="min-height: 160px;">
<div class="card-header font-weight-bold">Environment:</div>
<div class="card-body">
<% if (host.error) { %>
<%- host.error %>
<% } else { %>
<strong>Node:</strong> <%= host.static.nodeVersion %> <br>
<strong>OS:</strong> <%= host.static.osDistro %> <br>
<strong>Username:</strong> <%= host.static.username %> <br>
<strong>CPU Model:</strong> <%- host.static.cpu.manufacturer %> <%- host.static.cpu.brand %> <br>
<strong>CPU Stats:</strong>
<%- host.static.cpu.physicalCores %>c/<%- host.static.cpu.cores %>t -
<%- host.static.cpu.speedMin %> GHz
<%- host.static.cpu.clockWarning %> <br>
<% if (host.dynamic) { %>
<strong>CPU Usage:</strong> <%= host.dynamic.cpuUsage %>% <br>
<strong>Memory:</strong>
<%= host.dynamic.memory.usage %>%
(<%= host.dynamic.memory.used.toFixed(2) %>/<%= host.dynamic.memory.total.toFixed(2) %>)
<% } else { %>
<i>Dynamic usage data not available.</i>
<% } %>
<% } %>
</div>
</div>
<!-- txAdmin Config Card -->
<div class="card card-accent-danger" style="min-height: 160px;">
<div class="card-header font-weight-bold">txAdmin Runtime:</div>
<div class="card-body">
<strong>Uptime:</strong> <code><%= txadmin.uptime %></code> <br>
<strong>Versions:</strong>
<code>v<%= txAdminVersion %></code> /
<code>b<%= fxServerVersion %></code> <br>
<strong>Database File Size:</strong> <code><%= txadmin.databaseFileSize %></code> <br>
<strong>Env:</strong> <br>
├─ FXServer: <code><%= txadmin.txEnv.fxsPath %></code> <br>
├─ Profile: <code><%= txadmin.txEnv.profilePath %></code> <br>
├─ Defaults: <code><%= txadmin.txHostConfig.defaults.length > 0 ? txadmin.txHostConfig.defaults.join(', ') : '--' %></code> <br>
├─ Interface: <code><%= txadmin.txHostConfig.netInterface ?? '--' %></code> <br>
└─ Provider: <code><%= txadmin.txHostConfig.providerName ?? '--' %></code> <br>
<strong>Monitor:</strong> <br>
├─ HB Fails:
<code>HTTP <%= txadmin.monitor.hbFails.http %></code> /
<code>FD3 <%= txadmin.monitor.hbFails.fd3 %></code> <br>
└─ Restarts:
<code>BT <%= txadmin.monitor.restarts.bootTimeout %></code> /
<code>CL <%= txadmin.monitor.restarts.close %></code> /
<code>HB <%= txadmin.monitor.restarts.heartBeat %></code> /
<code>HC <%= txadmin.monitor.restarts.healthCheck %></code> /
<code>BO <%= txadmin.monitor.restarts.both %></code> <br>
<strong>Performance Times:</strong> <br>
├─ BanCheck: <code><%= txadmin.performance.banCheck %></code> <br>
├─ WhitelistCheck: <code><%= txadmin.performance.whitelistCheck %></code> <br>
├─ PlayersTable: <code><%= txadmin.performance.playersTableSearch %></code> <br>
├─ HistoryTable: <code><%= txadmin.performance.historyTableSearch %></code> <br>
├─ DatabaseSave: <code><%= txadmin.performance.databaseSave %></code> <br>
└─ PerfCollection: <code><%= txadmin.performance.perfCollection %></code> <br>
<strong>Memory:</strong> <br>
├─ Heap: <code><%= txadmin.memoryUsage.heap_used %> / <%= txadmin.memoryUsage.heap_limit %> (<%= txadmin.memoryUsage.heap_pct %>%)</code> <br>
├─ Physical: <code><%= txadmin.memoryUsage.physical %></code> <br>
└─ Peak. Alloc.: <code><%= txadmin.memoryUsage.peak_malloced %></code> <br>
<strong>Logger Status:</strong> <br>
├─ Storage Size: <code><%= txadmin.logger.storageSize %></code> <br>
├─ Admin: <code><%= txadmin.logger.statusAdmin %></code> <br>
├─ FXServer: <code><%= txadmin.logger.statusFXServer %></code> <br>
└─ Server: <code><%= txadmin.logger.statusServer %></code> <br>
</div>
</div>
<!-- Execution time -->
<div class="text-center mb-4">
<small class="text-muted"><%- message %></small>
</div>
</div>
<!-- /.col-->
<div class="col-lg-8 col-xl-5 mw-col6">
<!-- Diagnostics Report -->
<div class="card card-accent-info">
<div class="card-header font-weight-bold">Diagnostics Report:</div>
<div class="card-body text-center">
<div class="row text-center">
<div class="col-lg-9 text-lg-left">
To receive txAdmin Support, it is recommended that you send the diagnostics data directly to the Support Team.
</div>
<div class="col-lg-3 text-lg-right">
<button
class="btn btn-sm btn-outline-info"
type="button"
onclick="showReportModal()"
>
Review Details <br>
& Send Data
</button>
</div>
</div>
</div>
</div>
<!-- FXServer Info Card -->
<div class="card card-accent-info" style="min-height: 160px;">
<div class="card-header font-weight-bold">FXServer /info.json:</div>
<div class="card-body">
<% if (fxserver.versionMismatch) { %>
<div class="alert alert-danger text-center" role="alert">
<strong>This version doesn't match txAdmin's version!</strong><br>
If you just updated FXServer, restart txAdmin. <br>
Otherwise, it means FXServer was already running before txAdmin started, and nothing is going to work properly.
</div>
<% } %>
<% if (fxserver.error !== false) { %>
<%- fxserver.error %>
<% } else { %>
<strong>Status: <span class="badge badge-<%= fxserver.statusColor %>"><%= fxserver.status %></span></strong> <br>
<strong>Version:</strong> <%= fxserver.version %> <br>
<strong>Resources:</strong> <%= fxserver.resources %> <br>
<strong>OneSync:</strong> <%= fxserver.onesync %> <br>
<strong>Max Clients:</strong> <%= fxserver.maxClients %> <br>
<strong>txAdmin Version:</strong> <%= fxserver.txAdminVersion %> <br>
<% } %>
</div>
</div>
<!-- Proccesses Card -->
<div class="card card-accent-info">
<div class="card-header font-weight-bold">Processes:</div>
<div class="card-body">
<% if (!proccesses.length) { %>
Failed to retrieve processed data. <br>
Check the terminal for more information (if verbosity is enabled)
<% } else { %>
<% for (const process of proccesses) { %>
<strong>Process:</strong> (<%= process.pid %>) <%= process.name %> <br>
<strong>Parent:</strong> <%= process.ppid %> <br>
<strong>Memory:</strong> <%= process.memory.toFixed(2) %>MB <br>
<strong>CPU:</strong> <%= process.cpu.toFixed(2) %>% <br>
<br>
<% } %>
<% } %>
</div>
</div>
</div>
<!-- /.col-->
</div>
<%- await include('parts/footer.ejs', locals) %>
<!-- Submit Report modal -->
<div class="modal fade" id="modReport" tabindex="-1" role="dialog" aria-labelledby="modReport-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modReport-title">Send Diagnostics Data</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<!-- Spinner -->
<div class="d-none" id="modReport-spinnerBody">
<div style="min-height: 125px;">
<div class="txSpinner">Loading...</div>
</div>
</div>
<!-- Review -->
<div class="d-none" id="modReport-infoBody">
<p>
This <i>optional</i> feature sends a diagnostics report to the txAdmin/Cfx.re teams, and may be required to diagnose a wide range of server issues. <br>
After sending the data, you will receive a Report ID you can send in the support channels.
</p>
<ul style="padding-inline-start: 1rem;">
<li>
<strong>Which data will be sent?</strong>
<ul style="padding-inline-start: 1rem;">
<li>All diagnostics page data</li>
<li>Recent txAdmin (system), live console and server log</li>
<li>Environment variables</li>
<li>Server performance (dashboard chart) data</li>
<li>Player database statistics</li>
<li>txAdmin settings (no bot token)</li>
<li>List of admins (no passwords/hashes)</li>
<li>List of files/folders in server data and monitor folders</li>
<li>Config files in server data folder</li>
</ul>
</li>
<li>
<strong>Who can access the data?</strong> <br>
The data will be available for up to 24 hours to the txAdmin support team, as well as the Cfx.re team.
</li>
<li>
<strong>Sensitive Information Protection:</strong> <br>
<ul style="padding-inline-start: 1rem;">
<li><strong>Settings:</strong> the Discord Bot Token will be removed</li>
<li><strong>Admin List:</strong> the password hashes will not be sent</li>
<li><strong>Env Vars:</strong> parameters with <code>key, license, pass, private, secret, token</code> in their name will be masked.</li>
<li><strong>CFG Files:</strong> known secret parameters will be masked (ex license, mysql string, tebex secret, webhooks, etc).</li>
<li><strong>Logs:</strong> any identifiable IPv4 address in logs will be masked.</li>
</ul>
</li>
</ul>
</div>
<!-- Result Success -->
<div class="d-none" id="modReport-resultSuccessBody">
<style>
.reportIdCode {
font-size: 1.75rem;
letter-spacing: 0.65rem;
background-color: rgb(128 128 128 / 20%);
padding: 0.25rem 0.1rem 0.25rem 0.85rem;
text-align: center;
}
</style>
<div class="text-center">
<h2>Report ID: <code class="reportIdCode" id="modReport-reportId">??????</code></h2>
</div>
</div>
<!-- Result Error -->
<div class="d-none" id="modReport-resultErrorBody">
<div class="text-center">
<h4 class="text-danger" id="modReport-resultErrorMessage">sdfsdf sdf sdfsdf</h4>
</div>
</div>
<p class="lead mb-1 pt-2 text-center" style="font-style: italic;">
For txAdmin support, join the official Discord:
<a href="https://discord.gg/uAmsGa2" target="_blank" style="vertical-align: text-bottom;">
<img src="https://discordapp.com/api/guilds/577993482761928734/widget.png?style=shield"></img>
</a>
</p>
</div>
<div class="modal-footer text-center">
<div class="mx-auto">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
&nbsp;
&nbsp;
<button type="button" class="btn btn-success" id="modReport-saveBtn">Agree & Send Data</button>
</div>
</div>
</div>
</div>
</div>
<script>
const els = {
modal: document.getElementById('modReport'),
infoBody: document.getElementById('modReport-infoBody'),
spinnerBody: document.getElementById('modReport-spinnerBody'),
resultSuccessBody: document.getElementById('modReport-resultSuccessBody'),
resultErrorBody: document.getElementById('modReport-resultErrorBody'),
resultErrorMessage: document.getElementById('modReport-resultErrorMessage'),
saveBtn: document.getElementById('modReport-saveBtn'),
reportId: document.getElementById('modReport-reportId'),
}
function showReportModal() {
els.infoBody.classList.remove('d-none');
els.resultSuccessBody.classList.add('d-none');
els.resultErrorBody.classList.add('d-none');
els.spinnerBody.classList.add('d-none');
els.saveBtn.classList.remove('d-none');
els.saveBtn.classList.remove('disabled');
$('#modReport').modal('show');
}
els.saveBtn.onclick = () => {
els.infoBody.classList.add('d-none');
els.spinnerBody.classList.remove('d-none');
els.saveBtn.classList.add('disabled');
txAdminAPI({
type: "POST",
url: `diagnostics/sendReport`,
timeout: REQ_TIMEOUT_REALLY_LONG,
//NOTE: in NUI, empty bodies become GET requests even if you specify POST
data: {bugfix:true},
success: function (data) {
els.spinnerBody.classList.add('d-none');
els.saveBtn.classList.add('d-none');
if(data.error){
els.resultErrorBody.classList.remove('d-none');
els.resultErrorMessage.textContent = data.error;
}else if(data.reportId){
els.resultSuccessBody.classList.remove('d-none');
els.reportId.textContent = data.reportId;
}else{
els.resultErrorBody.classList.remove('d-none');
els.resultErrorMessage.textContent = 'Unknown backend error.';
}
},
error: function (xmlhttprequest, textstatus, message) {
els.resultErrorBody.classList.remove('d-none');
els.resultErrorMessage.textContent = `Error: ${message}`;
}
});
}
</script>