Release v2.5.0 — Client-Versions-Übersicht im Web-Editor

Jeder Client meldet beim Start seinen Status (Name, Abteilung, Plugin- und
Thunderbird-Version) als _clients/<email>.json ins Gitea-Repo — aber nur, wenn
sich tatsächlich etwas geändert hat (kein Commit-Spam pro Start).

Web-Editor: neuer Admin-Unterpunkt "User & Versionen" (Route /api/clients)
mit Tabelle je User und "veraltet"-Markierung für Clients, die nicht auf der
neuesten gemeldeten Version laufen. _clients/ ist in den Ordner-Filtern
ausgenommen, damit es nicht als Abteilung erscheint.

Nebenbei: Web-Editor-Button-Label "Abteilung" -> "Vorlage" (Templates-Tab).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015PZHd9vRsBiH8cKYdQo1Yv
This commit is contained in:
Kendrick Bollens
2026-07-22 14:42:23 +02:00
parent d244273e44
commit 02776311c3
6 changed files with 177 additions and 7 deletions

View File

@@ -8,6 +8,7 @@ const FULL_SYNC_COOLDOWN_MS = 10 * 1000; // min 10s between full pulls
const SHARED_FOLDER = '_gemeinsam';
const USER_FOLDER = '_benutzer';
const CONFIG_FOLDER = '_config';
const CLIENTS_FOLDER = '_clients';
const SCHLAGWOERTER_CACHE_KEY = 'schlagwoerter_cache';
// ── Gitea API Client ──
@@ -190,6 +191,59 @@ class SyncManager {
return this.config?.authorEmail || '';
}
get authorName() {
return this.config?.authorName || '';
}
/**
* Meldet den eigenen Client-Status (Name, Abteilung, Plugin- + TB-Version)
* als _clients/<email>.json ins Repo — Basis für die Admin-Übersicht im
* Web-Editor. Schreibt NUR, wenn sich tatsächlich etwas geändert hat
* (Plugin-Version, TB-Version, Name oder Abteilung) — sonst kein Commit.
*/
async reportClientStatus() {
if (!this.isConfigured) return { success: false, skipped: 'not-configured' };
const email = this.authorEmail.trim().toLowerCase();
if (!email) return { success: false, skipped: 'no-email' };
let tbVersion = '';
try {
const info = await browser.runtime.getBrowserInfo();
tbVersion = [info.name, info.version].filter(Boolean).join(' ');
} catch (_) {}
const status = {
name: this.authorName,
email,
department: this.department,
pluginVersion: browser.runtime.getManifest().version,
tbVersion,
lastSeen: new Date().toISOString()
};
const filepath = `${CLIENTS_FOLDER}/${email}.json`;
const body = JSON.stringify(status, null, 2);
const commitMsg = `Client-Status: ${email} (v${status.pluginVersion})`;
const existing = await this.client.getFile(filepath);
if (existing && existing.content) {
try {
const prev = JSON.parse(GiteaClient.fromBase64(existing.content));
const unchanged = prev.name === status.name
&& prev.department === status.department
&& prev.pluginVersion === status.pluginVersion
&& prev.tbVersion === status.tbVersion;
if (unchanged) {
return { success: true, skipped: 'unchanged' };
}
} catch (_) { /* kaputter Vorgänger → einfach überschreiben */ }
await this.client.updateFile(filepath, body, existing.sha, commitMsg);
} else {
await this.client.createFile(filepath, body, commitMsg);
}
return { success: true, status };
}
async autoDetect() {
if (!this.isConfigured) return null;
return await this.client.getConfig();
@@ -233,7 +287,7 @@ class SyncManager {
const entries = await this.client.listDir('');
const departments = [];
for (const entry of entries) {
if (entry.type === 'dir' && entry.name !== SHARED_FOLDER && entry.name !== USER_FOLDER && entry.name !== CONFIG_FOLDER && entry.name !== 'signatures' && !entry.name.startsWith('.')) {
if (entry.type === 'dir' && entry.name !== SHARED_FOLDER && entry.name !== USER_FOLDER && entry.name !== CONFIG_FOLDER && entry.name !== CLIENTS_FOLDER && entry.name !== 'signatures' && !entry.name.startsWith('.')) {
departments.push(entry.name);
}
}
@@ -968,6 +1022,7 @@ let lastKnownShas = null;
let lastFullSync = 0;
let lastTagSync = 0;
let syncInProgress = false;
let clientStatusReported = false;
const TAG_SYNC_INTERVAL_MS = 60 * 1000;
const HASH_STORAGE_KEY_BG = 'sync_hashes';
@@ -1009,6 +1064,15 @@ async function smartSync() {
const initialized = await syncManager.init();
if (!initialized) return;
// Einmal pro Session den eigenen Client-Status melden (Admin-Übersicht).
// Fire-and-forget, blockiert den Sync nicht.
if (!clientStatusReported) {
clientStatusReported = true;
syncManager.reportClientStatus()
.then((r) => { if (r?.status) console.log('[Sync] Client-Status gemeldet:', r.status.email, 'v' + r.status.pluginVersion); })
.catch((e) => console.error('[Sync] Client-Status fehlgeschlagen:', e));
}
syncInProgress = true;
// Tag sync every 60s (schlagwoerter.json is not in SHA-checked folders)