chore(dashboard): remove the Maintenance page

The page held one-off repairs from Nexus Dashboard (approve known pet
names, find/delete orphaned pet names, remove every buff, fix property
clone IDs, list mission rewards without a commendation price). Removed
the page, its sidebar entry, its /api/maintenance/* routes, the
`maintenance` permission and the two database calls only it used
(GetAllPetNames, FixPropertyCloneIds). The scheduled tasks (lift expired
bans, fill in pet owners, approve known pet names) and property model
import/removal stay.

Check: the Admin sidebar group has no Maintenance link; /maintenance is
a 404; the Tasks page still lists "Approve known pet names" and "Fill in
pet owners"; property import still works; the Permissions page no
longer lists "Data maintenance".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-28 23:19:41 -05:00
parent 7440e5744d
commit b0dab03807
14 changed files with 5 additions and 297 deletions

View File

@@ -118,8 +118,8 @@
</div>
{% endif %}
{% set adminPages = ["settings", "settings_history", "permissions", "tasks", "play_keys", "backups", "maintenance", "webhooks", "client_assets", "ugc"] %}
{% if can.settings or can.permissions_manage or can.tasks_view or can.play_keys_manage or can.backups or can.maintenance or can.webhooks or can.client_files or can.ugc_manage %}
{% set adminPages = ["settings", "settings_history", "permissions", "tasks", "play_keys", "backups", "webhooks", "client_assets", "ugc"] %}
{% if can.settings or can.permissions_manage or can.tasks_view or can.play_keys_manage or can.backups or can.webhooks or can.client_files or can.ugc_manage %}
<div class="nav-group">
<button class="nav-group-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#nav-admin" aria-expanded="{% if current_page in adminPages %}true{% else %}false{% endif %}" aria-controls="nav-admin">
Server Admin<span class="nav-group-dot" title="Something in here needs attention"></span>
@@ -131,7 +131,6 @@
{% if can.tasks_view %}<a href="/tasks" class="list-group-item list-group-item-action{% if current_page == "tasks" %} active{% endif %}">Scheduled Tasks</a>{% endif %}
{% if can.play_keys_manage %}<a href="/play_keys" class="list-group-item list-group-item-action{% if current_page == "play_keys" %} active{% endif %}">Play Keys</a>{% endif %}
{% if can.backups %}<a href="/backups" class="list-group-item list-group-item-action{% if current_page == "backups" %} active{% endif %}">Backups</a>{% endif %}
{% if can.maintenance %}<a href="/maintenance" class="list-group-item list-group-item-action{% if current_page == "maintenance" %} active{% endif %}">Maintenance</a>{% endif %}
{% if can.webhooks %}<a href="/webhooks" class="list-group-item list-group-item-action{% if current_page == "webhooks" %} active{% endif %}">Webhooks</a>{% endif %}
{% if can.ugc_manage %}<a href="/ugc" class="list-group-item list-group-item-action{% if current_page == "ugc" %} active{% endif %}">UGC Server</a>{% endif %}
{% if can.client_files %}<a href="/client_assets" class="list-group-item list-group-item-action{% if current_page == "client_assets" %} active{% endif %}">Client Files</a>{% endif %}

View File

@@ -1,108 +0,0 @@
{% extends "base.jinja2" %}
{% block title %}Maintenance - DarkflameServer{% endblock %}
{% block content %}
<h2 class="mb-1">Maintenance</h2>
<p class="text-body-secondary mb-4">Data repairs. Every action is recorded in the audit log. Scans read every character, so they can pause the dashboard for a moment on large servers.</p>
<div class="row g-4">
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header"><h5 class="mb-0">Pet names</h5></div>
<div class="card-body">
<p class="small">Pending names that were already approved for another pet are approved automatically every hour. Run it now:</p>
<button class="btn btn-sm btn-outline-primary mb-3" data-action="petApprove">Approve previously approved names</button>
<p class="small">Find pet names whose pet no longer exists on any character (e.g. the pet was released).</p>
<button class="btn btn-sm btn-outline-primary" data-action="petScan">Scan</button>
<button class="btn btn-sm btn-outline-danger d-none" data-action="petDelete" id="petDeleteBtn">Delete orphaned names</button>
<pre class="small mt-2 mb-0 d-none" id="petResult"></pre>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header"><h5 class="mb-0">Buffs</h5></div>
<div class="card-body">
<p class="small">Remove every buff from every character, e.g. after a buff got stuck. Only possible while nobody is online.</p>
<button class="btn btn-sm btn-outline-primary" data-action="buffScan">Count characters with buffs</button>
<button class="btn btn-sm btn-outline-danger" data-action="buffRemove">Remove all buffs</button>
<pre class="small mt-2 mb-0 d-none" id="buffResult"></pre>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header"><h5 class="mb-0">Property clone IDs</h5></div>
<div class="card-body">
<p class="small">Repair properties whose clone ID doesn't match their owner's (left behind by an old bug), which stops them loading.</p>
<button class="btn btn-sm btn-outline-primary" data-action="fixClones">Fix clone IDs</button>
<pre class="small mt-2 mb-0 d-none" id="cloneResult"></pre>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header"><h5 class="mb-0">Game data check</h5></div>
<div class="card-body">
<p class="small">Mission reward items that have no commendation vendor price in the game data.</p>
<button class="btn btn-sm btn-outline-primary" data-action="commendation">Find items</button>
<div class="small mt-2" id="commendationResult"></div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
function show(id, text) {
var el = document.getElementById(id);
el.textContent = text;
el.classList.remove('d-none');
}
var actions = {
petApprove: function () { return api.action('/api/maintenance/pet_names/auto_approve', {}).then(function (d) { toast(d.message, 'success'); }); },
petScan: function () {
show('petResult', 'Scanning every character...');
return api.job('/api/maintenance/pet_names/orphans', {}).then(function (d) {
show('petResult', d.characters + ' characters scanned, ' + d.petsFound + ' pets found, ' + d.petNames + ' pet names stored.\n' +
d.orphaned + ' orphaned' + (d.sample.length ? ', e.g.: ' + d.sample.map(function (p) { return p.name; }).join(', ') : ''));
document.getElementById('petDeleteBtn').classList.toggle('d-none', !d.orphaned);
});
},
petDelete: function () {
if (!confirm('Delete the orphaned pet names?')) return Promise.resolve();
return api.job('/api/maintenance/pet_names/orphans', { delete: true }).then(function (d) {
show('petResult', 'Deleted ' + d.deleted + ' orphaned pet name(s).');
document.getElementById('petDeleteBtn').classList.add('d-none');
});
},
buffScan: function () {
show('buffResult', 'Scanning every character...');
return api.job('/api/maintenance/remove_buffs', { dry_run: true }).then(function (d) { show('buffResult', d.characters + ' character(s) have buffs'); });
},
buffRemove: function () {
if (!confirm('Remove all buffs from every character?')) return Promise.resolve();
return api.job('/api/maintenance/remove_buffs', { dry_run: false }).then(function (d) { show('buffResult', 'Removed buffs from ' + d.characters + ' character(s)'); });
},
fixClones: function () { return api.action('/api/maintenance/fix_clone_ids', {}).then(function (d) { show('cloneResult', d.message); }); },
commendation: function () {
return api.get('/api/maintenance/missing_commendation_items').then(function (d) {
var el = document.getElementById('commendationResult');
el.innerHTML = d.items.length
? '<ul class="mb-0">' + d.items.map(function (i) { return '<li>' + esc(i.name) + ' <span class="text-body-secondary">(' + esc(i.lot) + ')</span></li>'; }).join('') + '</ul>'
: 'Every mission reward item has a commendation price.';
});
}
};
document.addEventListener('click', function (e) {
var btn = e.target.closest('[data-action]');
if (!btn) return;
btn.disabled = true;
actions[btn.getAttribute('data-action')]().catch(function () {}).finally(function () { btn.disabled = false; });
});
</script>
{% endblock %}