From 3abf974d2900f725c3be4fa51cf6ef64d2ab4978 Mon Sep 17 00:00:00 2001 From: rene Date: Mon, 25 May 2026 20:26:58 +0200 Subject: [PATCH] Feature: Parallele Bild-Uploads, Heartbeat last_seen, Admin zuletzt aktiv, SW by-v1071 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tagebuch: Bilder werden parallel hochgeladen (Promise.all), Button zeigt Fortschritt - Auth: /heartbeat Route ergänzt — aktualisiert last_seen alle 5 Min - Admin: last_seen + last_login in Nutzer-Liste angezeigt (🟢/🔵/⚪) - Bump SW by-v1071 --- backend/main.py | 2 +- backend/routes/admin.py | 2 +- backend/routes/auth.py | 7 +++++++ backend/static/index.html | 14 +++++++------- backend/static/js/app.js | 2 +- backend/static/js/pages/admin.js | 8 +++++++- backend/static/js/pages/diary.js | 32 +++++++++++++++++++------------- backend/static/sw.js | 2 +- 8 files changed, 44 insertions(+), 25 deletions(-) diff --git a/backend/main.py b/backend/main.py index d63ae64..ed8aac2 100644 --- a/backend/main.py +++ b/backend/main.py @@ -410,7 +410,7 @@ async def serve_media(path: str, request: _Request): raise _HE(404, "Nicht gefunden.") return _media_response(filepath) -APP_VER = "1070" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "1071" # muss mit APP_VER in app.js übereinstimmen @app.get("/.well-known/assetlinks.json") async def assetlinks(): diff --git a/backend/routes/admin.py b/backend/routes/admin.py index c0ec221..a1c33a6 100644 --- a/backend/routes/admin.py +++ b/backend/routes/admin.py @@ -359,7 +359,7 @@ async def list_users( SELECT u.id, u.name, {_email_col}, u.rolle, u.is_premium, u.is_moderator, u.is_banned, u.ban_reason, u.is_founder, u.is_partner, u.founder_number, - u.created_at, u.last_login, u.subscription_tier, + u.created_at, u.last_login, u.last_seen, u.subscription_tier, (SELECT COUNT(*) FROM dogs d WHERE d.user_id=u.id) AS dog_count, (SELECT COUNT(*) FROM forum_threads t WHERE t.user_id=u.id AND t.is_deleted=0) AS thread_count, ROUND(COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=u.id), 0), 1) AS total_km, diff --git a/backend/routes/auth.py b/backend/routes/auth.py index 0ccc0cd..7ee2d03 100644 --- a/backend/routes/auth.py +++ b/backend/routes/auth.py @@ -479,3 +479,10 @@ async def select_primary_dog(body: dict, user=Depends(get_current_user)): "UPDATE users SET needs_dog_selection=0 WHERE id=?", (user["id"],) ) return {"ok": True} + + +@router.post("/heartbeat") +async def heartbeat(user=Depends(get_current_user)): + with db() as conn: + conn.execute("UPDATE users SET last_seen=datetime('now') WHERE id=?", (user["id"],)) + return {"ok": True} diff --git a/backend/static/index.html b/backend/static/index.html index e8ea43a..7c87e70 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -101,9 +101,9 @@ - - - + + + @@ -616,10 +616,10 @@ - - - - + + + + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 1bec4db..8b1c343 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '1070'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '1071'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; // Cache-Bust-Parameter nach Update-Reload sofort entfernen. diff --git a/backend/static/js/pages/admin.js b/backend/static/js/pages/admin.js index b7acbf5..4b644c6 100644 --- a/backend/static/js/pages/admin.js +++ b/backend/static/js/pages/admin.js @@ -820,7 +820,13 @@ window.Page_admin = (() => {
🗺 ${u.route_count} Routen · ${u.total_km} km · 📍 ${u.poi_count} POIs - ${u.last_route ? '· zuletzt ' + new Date(u.last_route).toLocaleDateString('de-DE') : ''} +
+
+ ${u.last_seen + ? '🟢 zuletzt aktiv ' + new Date(u.last_seen).toLocaleString('de-DE', {day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'}) + : u.last_login + ? '🔵 zuletzt eingeloggt ' + new Date(u.last_login).toLocaleString('de-DE', {day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'}) + : '⚪ nie aktiv'}
diff --git a/backend/static/js/pages/diary.js b/backend/static/js/pages/diary.js index d15c9b5..2af7303 100644 --- a/backend/static/js/pages/diary.js +++ b/backend/static/js/pages/diary.js @@ -1722,29 +1722,35 @@ window.Page_diary = (() => { }; async function _uploadNewFiles(entryId) { - let failCount = 0; - const uploaded = []; - let exifGps = null; - for (const file of _newFiles) { + const total = _newFiles.length; + const saveBtn = document.querySelector('button[form="diary-form"]'); + let done = 0; + if (saveBtn) saveBtn.textContent = `0 von ${total} hochgeladen…`; + + const results = await Promise.all(_newFiles.map(async file => { + const formData = new FormData(); + formData.append('file', file); try { - const formData = new FormData(); - formData.append('file', file); const m = await API.diary.uploadMedia(_appState.activeDog.id, entryId, formData); - uploaded.push(m); - if (m.exif_lat != null && m.exif_lon != null && !exifGps) { - exifGps = { lat: m.exif_lat, lon: m.exif_lon }; - } + if (saveBtn) saveBtn.textContent = `${++done} von ${total} hochgeladen…`; + return { ok: true, m }; } catch { - failCount++; + if (saveBtn) saveBtn.textContent = `${++done} von ${total} hochgeladen…`; + return { ok: false }; } - } + })); + + const uploaded = results.filter(r => r.ok).map(r => r.m); + const failCount = results.filter(r => !r.ok).length; + const exifGps = results.find(r => r.ok && r.m.exif_lat != null)?.m; + if (failCount > 0) { UI.toast.warning(`${failCount} Medium${failCount > 1 ? 'en' : ''} konnte${failCount > 1 ? 'n' : ''} nicht hochgeladen werden.`); } if (exifGps) { UI.toast.success(`📍 Standort aus Foto-GPS übernommen`); } - return { uploaded, exifGps }; + return { uploaded, exifGps: exifGps ? { lat: exifGps.exif_lat, lon: exifGps.exif_lon } : null }; } if (isEdit) { diff --git a/backend/static/sw.js b/backend/static/sw.js index ae167cc..7394250 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -4,7 +4,7 @@ ============================================================ */ // ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab -const VER = '1070'; +const VER = '1071'; const CACHE_VERSION = `by-v${VER}`; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten