mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
The NexusDashboard-parity dashboard (dDashboardServer) and everything built on it on the experimental branch: accounts, characters, properties and moderation tools, permissions shared with in-game slash commands, economy reports, World 3D and property 3D views with client scenery, scheduled events (features, vanity changes, live events, announcements, restarts), vanity files and events, the CDClient browser, the message inspector with saved captures, chat filter tools, community challenges, live ops, the AI moderator helper, and the server-side changes they need. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
4.8 KiB
Django/Jinja
116 lines
4.8 KiB
Django/Jinja
{% extends "base.jinja2" %}
|
|
|
|
{% block title %}Accounts - DarkflameServer{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h2 class="mb-1">Accounts</h2>
|
|
<p class="text-body-secondary mb-0">Manage user accounts — <span class="account-count">0</span> total</p>
|
|
</div>
|
|
{% if can.accounts_manage %}
|
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createAccountModal">Create Account</button>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<table id="accountsTable" class="table table-striped table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Username</th>
|
|
<th>Banned</th>
|
|
<th>Locked</th>
|
|
<th>GM Level</th>
|
|
<th>Mute Expires</th>
|
|
<th>Created</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="createAccountModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Create Account</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label for="newUsername" class="form-label">Username</label>
|
|
<input type="text" class="form-control" id="newUsername" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="newPassword" class="form-label">Password</label>
|
|
<input type="password" class="form-control" id="newPassword" minlength="6" maxlength="40" required>
|
|
<div class="form-text">6-40 characters.</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="newGmLevel" class="form-label">GM level</label>
|
|
<select id="newGmLevel" class="form-select">
|
|
{% for level in labels.gmLevels %}<option value="{{ level.value }}">{{ level.value }} - {{ level.name }}</option>{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="newPlayKey" class="form-label">Play key (optional)</label>
|
|
<input type="text" class="form-control" id="newPlayKey" autocomplete="off">
|
|
<div class="form-text">GM 0 accounts need one to log in to the game when the server uses play keys.</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" onclick="createAccount()">Create</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
$(function () {
|
|
// Only operators may create accounts at or above their own level
|
|
var gmSelect = document.getElementById('newGmLevel');
|
|
if (gmSelect) Array.prototype.forEach.call(gmSelect.options, function (opt) {
|
|
if (DASH.gmLevel < 9 && parseInt(opt.value, 10) >= DASH.gmLevel) opt.disabled = true;
|
|
});
|
|
|
|
var table = serverTable('#accountsTable', '/api/tables/accounts', [
|
|
{ data: 'id' },
|
|
{ data: 'name', render: function (d, t, row) { return fmt.link('/accounts/' + row.id, d); } },
|
|
{ data: 'banned', render: function (d) { return d ? fmt.badge('Banned', 'danger') : fmt.badge('Active', 'success'); } },
|
|
{ data: 'locked', render: function (d) { return d ? fmt.badge('Locked', 'warning') : fmt.badge('Unlocked', 'secondary'); } },
|
|
{ data: 'gm_level', render: function (d) { return d > 0 ? fmt.badge('Level ' + d, 'info') : '-'; } },
|
|
{ data: 'mute_expire', render: function (d) {
|
|
if (!d) return 'Not muted';
|
|
return d > Date.now() / 1000 ? fmt.badge('Until ' + fmt.unix(d), 'danger') : '<span class="text-body-secondary">Expired</span>';
|
|
}},
|
|
{ data: 'created_at', render: function (d) { return fmt.date(d); } },
|
|
{ data: 'id', orderable: false, searchable: false, render: function (d) {
|
|
return '<a href="/accounts/' + esc(d) + '" class="btn btn-sm btn-outline-primary">View</a>';
|
|
}}
|
|
], { liveTable: 'accounts', dataTable: { order: [[0, 'asc']] } });
|
|
table.on('draw.dt', function () { document.querySelector('.account-count').textContent = table.page.info().recordsTotal; });
|
|
});
|
|
|
|
function createAccount() {
|
|
var username = document.getElementById('newUsername').value.trim();
|
|
var password = document.getElementById('newPassword').value;
|
|
if (!username || !password) { toast('Username and password are required', 'danger'); return; }
|
|
var gmLevel = parseInt(document.getElementById('newGmLevel').value, 10);
|
|
var playKey = document.getElementById('newPlayKey').value.trim();
|
|
api.action('/api/accounts/create', { username: username, password: password, gm_level: gmLevel, play_key: playKey }, 'Account created').then(function () {
|
|
bootstrap.Modal.getInstance(document.getElementById('createAccountModal')).hide();
|
|
document.getElementById('newUsername').value = '';
|
|
document.getElementById('newPassword').value = '';
|
|
document.getElementById('newPlayKey').value = '';
|
|
}).catch(function () {});
|
|
}
|
|
</script>
|
|
{% endblock %}
|