diff --git a/diagnostic/Dockerfile b/diagnostic/Dockerfile
index 5f039bc..a6d5948 100644
--- a/diagnostic/Dockerfile
+++ b/diagnostic/Dockerfile
@@ -1,7 +1,8 @@
FROM node:22-alpine
WORKDIR /app
# zip fuer Multi-Datei-Downloads (Brain-Export nutzt tar.gz, Datei-Manager zip)
-RUN apk add --no-cache zip
+# git fuer Auto-Versionierung von /shared/uploads/ (siehe server.js)
+RUN apk add --no-cache zip git
COPY package.json ./
RUN npm install --production
COPY . .
diff --git a/diagnostic/index.html b/diagnostic/index.html
index fce8737..4f7f1d1 100644
--- a/diagnostic/index.html
+++ b/diagnostic/index.html
@@ -4039,11 +4039,83 @@
${fmtSize(f.size)} ยท ${fmtDate(f.mtime)}
+
`;
}).join('');
}
+ // โโ Versions-Modal โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ async function showVersions(fileName) {
+ // path-relative-to-/shared/uploads ist hier == fileName, weil unser
+ // file-Manager-Verzeichnis flach ist
+ const rel = fileName;
+ const modal = document.getElementById('versions-modal');
+ const title = document.getElementById('versions-title');
+ const body = document.getElementById('versions-body');
+ title.textContent = `Versionen โ ${fileName}`;
+ body.innerHTML = 'Lade...
';
+ modal.style.display = 'flex';
+ modal.dataset.path = rel;
+ try {
+ const r = await fetch('/api/files-versions?path=' + encodeURIComponent(rel));
+ const d = await r.json();
+ if (!d.ok) throw new Error(d.error || 'Fehler');
+ if (!d.versions.length) {
+ body.innerHTML = 'Noch keine Versions-Historie (Datei kommt erst nach naechstem Auto-Commit in den Index).
';
+ return;
+ }
+ const fmtDate = (ms) => new Date(ms).toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' });
+ body.innerHTML = d.versions.map(v => {
+ const isCur = v.isCurrent
+ ? 'AKTIV'
+ : '';
+ const subjShort = (v.subject || '').slice(0, 60);
+ return `
+
+
${isCur}${v.hash.slice(0,7)} ยท ${escapeHtml(subjShort)}
+
${fmtDate(v.ts)}
+
+
+ ${v.isCurrent ? '' : `
`}
+
`;
+ }).join('');
+ } catch (e) {
+ body.innerHTML = `${escapeHtml(e.message)}
`;
+ }
+ }
+
+ function closeVersionsModal() {
+ document.getElementById('versions-modal').style.display = 'none';
+ }
+
+ function downloadVersion(rel, hash) {
+ const url = '/api/files-version-content?path=' + encodeURIComponent(rel) + '&hash=' + encodeURIComponent(hash);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = '';
+ document.body.appendChild(a); a.click();
+ setTimeout(() => a.remove(), 100);
+ }
+
+ async function restoreVersion(rel, hash) {
+ if (!confirm(`Diese Version (${hash.slice(0,7)}) als aktive Version setzen?\n\nDie aktuelle Version bleibt rollback-bar in der Historie.`)) return;
+ try {
+ const r = await fetch('/api/files-version-restore', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ path: rel, hash }),
+ });
+ const d = await r.json();
+ if (!d.ok) throw new Error(d.error || 'Fehler');
+ // Modal neu laden mit aktualisierter Liste
+ showVersions(rel);
+ loadFiles();
+ } catch (e) {
+ alert('Restore fehlgeschlagen: ' + e.message);
+ }
+ }
+
async function downloadSelected() {
const paths = [...filesSelected];
if (!paths.length) return;
@@ -5612,5 +5684,16 @@
// History gleich nach Seitenstart laden damit Browser-Reload nichts verliert.
loadAriaStreamHistory();
+
+
+
+
+
+ Versionen
+
+
+
+
+