diff --git a/backend/database.py b/backend/database.py index cff0f89..c56a2f6 100644 --- a/backend/database.py +++ b/backend/database.py @@ -2075,18 +2075,6 @@ def _migrate(conn_factory): _seed_help_articles(conn) logger.info("Migration: Hilfe/FAQ-Tabelle bereit.") - conn.executescript(""" - CREATE TABLE IF NOT EXISTS bday_ki_cache ( - dog_id INTEGER NOT NULL, - year INTEGER NOT NULL, - mode TEXT NOT NULL, -- 'tomorrow' | 'today' - content TEXT NOT NULL, - created_at TEXT DEFAULT (datetime('now')), - PRIMARY KEY (dog_id, year, mode) - ); - """) - logger.info("Migration: bday_ki_cache Tabelle bereit.") - # ---- Feature: Subscription-Tier ---- try: conn.execute("ALTER TABLE users ADD COLUMN subscription_tier TEXT DEFAULT 'standard'") diff --git a/backend/main.py b/backend/main.py index cfd6cc7..78ab952 100644 --- a/backend/main.py +++ b/backend/main.py @@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") os.makedirs(MEDIA_DIR, exist_ok=True) app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") -APP_VER = "781" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "779" # muss mit APP_VER in app.js übereinstimmen @app.get("/.well-known/assetlinks.json") async def assetlinks(): diff --git a/backend/routes/ki.py b/backend/routes/ki.py index 708d571..80d663c 100644 --- a/backend/routes/ki.py +++ b/backend/routes/ki.py @@ -167,82 +167,6 @@ def _log_rasse_request(user_id: int): ) -# ------------------------------------------------------------------ -# POST /ki/geburtstag — Geburtstags-Überraschungsideen (kostenlos für alle) -# ------------------------------------------------------------------ - -class BirthdayRequest(BaseModel): - dog_id: int - name: str - rasse: Optional[str] = None - alter: Optional[int] = None - mode: str = "tomorrow" # "tomorrow" | "today" - -@router.post("/geburtstag") -async def ki_geburtstag(req: BirthdayRequest, request: Request, - user=Depends(get_current_user)): - """Kostenlose KI-Geburtstagsideen — kein Premium nötig, 1x/Tag, DB-gecacht.""" - from datetime import date - year = date.today().year - mode = req.mode if req.mode in ("tomorrow", "today") else "tomorrow" - - # Aus DB-Cache zurückgeben wenn bereits generiert - with db() as conn: - cached = conn.execute( - "SELECT content FROM bday_ki_cache WHERE dog_id=? AND year=? AND mode=?", - (req.dog_id, year, mode) - ).fetchone() - if cached: - return {"answer": cached["content"], "cached": True} - - name = req.name.strip()[:40] or "deinen Hund" - rasse = req.rasse or None - alter = req.alter - rasse_str = f"({rasse})" if rasse else "" - - if mode == "today": - # Aus Sicht des Hundes — was er sich für seinen Geburtstag vorstellt - alter_str = f"{alter}. Geburtstag" if alter else "Geburtstag" - system = ( - "Du bist ein Hund und erzählst aus deiner eigenen Perspektive. " - "Schreibe auf Deutsch, verspielt, liebevoll und mit Hundelogik. " - "Verwende typische Hundegedanken: Fressen, Gassi, Schmusen, Spielen, Gerüche." - ) - prompt = ( - f"Ich bin {name} {rasse_str} und heute ist mein {alter_str}! " - f"Erzähl in meiner Stimme (als Hund), wie ich mir den perfekten Geburtstagstag vorgestellt habe — " - f"von Morgen bis Abend. Was möchte ich erleben, fressen, riechen, spielen? " - f"Ca. 150 Wörter, herzlich und humorvoll." - ) - else: - # Überraschungsideen für morgen - alter_str = f"{alter}. Geburtstag" if alter else "Geburtstag" - system = ( - "Du bist ein begeisterter Hundefreund mit vielen kreativen Ideen. " - "Antworte auf Deutsch, herzlich, konkret und mit einer Prise Humor. " - "Fokus auf praktische, umsetzbare Überraschungen." - ) - prompt = ( - f"Morgen ist der {alter_str} von {name} {rasse_str}! " - f"Was können wir {name} besonders gönnen? " - f"Gib 5 konkrete, liebevolle Überraschungsideen — von einfach bis aufwendig, " - f"jeweils mit einem Satz warum Hunde das lieben." - ) - - try: - answer = await ki_module.complete( - system=system, prompt=prompt, max_tokens=600, requires_premium=False, - ) - with db() as conn: - conn.execute( - "INSERT OR REPLACE INTO bday_ki_cache (dog_id, year, mode, content) VALUES (?,?,?,?)", - (req.dog_id, year, mode, answer) - ) - return {"answer": answer, "cached": False} - except ki_module.KIUnavailableError: - raise HTTPException(503, "KI momentan nicht verfügbar.") - - # ------------------------------------------------------------------ # POST /ki/rasse-erkennung — Vision-basierte Rassenerkennung # ------------------------------------------------------------------ diff --git a/backend/static/index.html b/backend/static/index.html index 1ed294a..bade895 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -575,10 +575,10 @@ - - - - + + + + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 639ec6c..8131af6 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 = '781'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '779'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.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/worlds.js b/backend/static/js/worlds.js index 12f0deb..6daaaf4 100644 --- a/backend/static/js/worlds.js +++ b/backend/static/js/worlds.js @@ -1322,8 +1322,8 @@ window.Worlds = (() => { .bday-fw2 { display:inline-block; animation: bday-fw2 1.1s ease-in-out infinite .2s; } .bday-fw3 { display:inline-block; animation: bday-fw3 1.6s ease-in-out infinite .4s; } -
🎆 @@ -1341,20 +1341,7 @@ window.Worlds = (() => { ${bdayYear ? `
${bday === 'today' ? `${bdayYear} Jahr${bdayYear !== 1 ? 'e' : ''} gemeinsam 🐾` : `Wird ${bdayYear} Jahr${bdayYear !== 1 ? 'e' : ''} alt`}
` : ''} -
- ${bday === 'today' ? '🐾 Was hat sich Ban Yaro gewünscht? →' : '✨ KI-Überraschungsideen →'} -
-
- ${bday === 'today' && new Date().getHours() >= 18 ? ` -
- - - - - Halte den besonderen Tag im Tagebuch fest 🐾 - -
` : ''}` : ''} +
` : ''}
${!_hasBgPhoto ? ` @@ -1404,82 +1391,6 @@ window.Worlds = (() => { if (!isNaN(idx) && idx !== _dogIdx) { _dogIdx = idx; _renderHund(); } }); }); - - // Geburtstags-Banner → KI - el.querySelector('#wh-bday-banner')?.addEventListener('click', () => _openBdayKI(dog, bday)); - // Abend-Banner: nach 18 Uhr am echten Geburtstag → Tagebucheintrag anregen - if (bday === 'today' && new Date().getHours() >= 18) { - el.querySelector('#wh-bday-evening')?.addEventListener('click', () => { - navigateTo('diary'); - setTimeout(() => window.App?.callModule?.('diary', 'openNew'), 400); - }); - } - } - - async function _openBdayKI(dog, bdayMode) { - const isToday = bdayMode === 'today'; - const title = isToday ? `🎂 ${_esc(dog.name)}s Geburtstagstraum` : `🎁 Überraschungen für ${_esc(dog.name)}`; - - const ov = document.createElement('div'); - ov.className = 'w3-sheet-overlay'; - ov.innerHTML = ` -
-
-
-
${title}
- -
-
-
- - KI denkt nach… -
-
- ${isToday ? ` - ` : ''} -
`; - document.body.appendChild(ov); - const _close = () => ov.remove(); - ov.querySelector('.w3-backdrop').addEventListener('click', _close); - ov.querySelector('#bday-ki-close').addEventListener('click', _close); - ov.querySelector('#bday-diary-btn')?.addEventListener('click', () => { - _close(); - navigateTo('diary'); - setTimeout(() => window.App?.callModule?.('diary', 'openNew'), 400); - }); - - try { - const res = await API.post('/ki/geburtstag', { - dog_id: dog.id, - name: dog.name, - rasse: dog.rasse || null, - alter: dog.alter_jahre ? Math.round(dog.alter_jahre) : null, - mode: bdayMode, - }); - const body = ov.querySelector('#bday-ki-body'); - if (body) { - const html = _esc(res.answer || '') - .replace(/\*\*(.+?)\*\*/g, '$1') - .replace(/\n/g, '
'); - body.innerHTML = `
${html}
`; - } - } catch (err) { - const body = ov.querySelector('#bday-ki-body'); - if (body) { - const msg = err?.status === 429 - ? 'Heute bereits abgerufen — morgen gibt es neue Ideen 🐾' - : 'KI momentan nicht verfügbar. Versuch es später nochmal 🐾'; - body.innerHTML = `
${msg}
`; - } - } } // ── WELT WORLD ─────────────────────────────────────────────── diff --git a/backend/static/sw.js b/backend/static/sw.js index 96eaace..28f026d 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v781'; +const CACHE_VERSION = 'by-v779'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml index f61395a..800d3f0 100644 --- a/docker-compose.staging.yml +++ b/docker-compose.staging.yml @@ -14,7 +14,6 @@ services: - DB_PATH=/data/banyaro.db - MEDIA_DIR=/prod-media - STAGING=true - - KI_MODE=cloud - VAPID_PUBLIC_KEY=BMKbFAmpsqJ-eFef_4XJcYpuxPWqBNAoy9buMNnMSa6ijcPzltboHi_YccPKJrUD0isBez-vJIzAgjnLTWkzcC0 - VAPID_PRIVATE_KEY=8PWa9vvwMqtqsJEJGcwmiLhR0_Yl7duVX3wmWiKS878 - VAPID_CONTACT=mailto:admin@banyaro.app