Updater-Migration für Feature-Tabellen + README um neue Features erweitern

- updater/migrations/0001_feature_ui_tables.sql: legt voucher_templates und
  password_reset_tokens idempotent an (CREATE TABLE IF NOT EXISTS), damit
  bestehende Installationen die neuen Tabellen per System-Update erhalten
- Updater-UI: Button "Ausstehende Migrationen ausführen" + run_migrations-Action
  (CSRF-geschützt) im Migrations-Tab
- README: Features (Bulk, Templates, Dark Mode, i18n, Audit-Log, Passwort-Reset),
  zwei neue Screenshots (Bulk-Erstellung, Dashboard Dark Mode), Roadmap & Updater-
  Abschnitt aktualisiert
This commit is contained in:
Claude 2026-06-05 19:16:55 +00:00
parent 526c43e8ee
commit 51485810b4
No known key found for this signature in database
6 changed files with 105 additions and 6 deletions

View file

@ -133,6 +133,8 @@ $channels = \Updater\UpdateManager::CHANNELS;
<div class="card">
<h2>Migrations-Status</h2>
<div id="migList"><p class="muted">Wird geladen </p></div>
<button class="btn" id="btnRunMig" style="margin-top:16px; display:none;">Ausstehende Migrationen ausführen</button>
<div class="alert alert-ok" id="migAlert"></div>
</div>
</div>
</div>
@ -250,10 +252,36 @@ async function loadMigrations() {
'<div class="mig-item"><span>' + m.filename + '</span>' +
'<span class="badge ' + (m.applied ? 'badge-on">angewandt' : 'badge-off">offen') + '</span></div>'
).join('');
const pending = d.migrations.some(m => !m.applied);
$('btnRunMig').style.display = pending ? 'inline-block' : 'none';
} catch (e) {
el.innerHTML = '<p class="muted">Fehler: ' + e.message + '</p>';
}
}
$('btnRunMig').addEventListener('click', async () => {
$('btnRunMig').disabled = true; $('btnRunMig').textContent = 'Führe aus …';
$('migAlert').classList.remove('show');
try {
const body = new URLSearchParams({ action: 'run_migrations', csrf_token: CSRF });
const r = await fetch('update.php', { method: 'POST', body });
const d = await r.json();
if (d.error) {
$('migAlert').textContent = 'Fehler: ' + d.error;
$('migAlert').className = 'alert alert-error show';
} else {
const n = (d.applied || []).length;
$('migAlert').textContent = n > 0 ? (n + ' Migration(en) ausgeführt.') : 'Keine ausstehenden Migrationen.';
$('migAlert').className = 'alert alert-ok show';
loadMigrations();
}
} catch (e) {
$('migAlert').textContent = 'Fehler: ' + e.message;
$('migAlert').className = 'alert alert-error show';
} finally {
$('btnRunMig').disabled = false; $('btnRunMig').textContent = 'Ausstehende Migrationen ausführen';
}
});
</script>
</body>
</html>