diff --git a/backend/main.py b/backend/main.py index 8c5b390..8eebbd2 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 = "760" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "741" # muss mit APP_VER in app.js übereinstimmen @app.get("/api/version") async def app_version(): diff --git a/backend/routes/outreach.py b/backend/routes/outreach.py index d738998..4fbd03c 100644 --- a/backend/routes/outreach.py +++ b/backend/routes/outreach.py @@ -116,17 +116,11 @@ def _send_smtp(to: str, subject: str, body: str, account: str = "partner", html: msg = _build_message(to, subject, body + _LEGAL_FOOTER, account, html=html) msg_bytes = msg.as_bytes() ctx = ssl.create_default_context() - if _SMTP_PORT == 465: - with smtplib.SMTP_SSL(_SMTP_HOST, _SMTP_PORT, context=ctx, timeout=15) as s: - s.login(acc["user"], acc["pass"]) - s.sendmail(acc["from"], [to], msg_bytes) - else: # 587 oder 25 mit STARTTLS - with smtplib.SMTP(_SMTP_HOST, _SMTP_PORT, timeout=15) as s: - s.ehlo() - if s.has_extn("starttls"): - s.starttls(context=ctx) - s.login(acc["user"], acc["pass"]) - s.sendmail(acc["from"], [to], msg_bytes) + with smtplib.SMTP(_SMTP_HOST, _SMTP_PORT, timeout=15) as s: + s.ehlo() + s.starttls(context=ctx) + s.login(acc["user"], acc["pass"]) + s.sendmail(acc["from"], [to], msg_bytes) _imap_save_sent(msg_bytes, account) diff --git a/backend/static/index.html b/backend/static/index.html index c3abcea..2f22a0a 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -578,7 +578,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 0fe4e0e..263fa80 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 = '760'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '741'; // ← 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'; @@ -555,15 +555,20 @@ const App = (() => { } async function _checkNearbyAlerts() { + const nav = document.getElementById('bottom-nav'); + if (!nav) return; try { const pos = await new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject, { timeout: 5000, maximumAge: 120_000 }) ); const { latitude: lat, longitude: lon } = pos.coords; - await API.get(`/alerts?lat=${lat}&lon=${lon}`); - // Standort-Update für Push-Subscriptions (serverseitig in /alerts gespeichert) + const data = await API.get(`/alerts?lat=${lat}&lon=${lon}`); + nav.classList.toggle('alert-poison', !!data.poison); + nav.classList.toggle('alert-lost', !data.poison && !!data.lost); + // Burger-Badge: Giftköder/Verlorener Hund in der Nähe + document.getElementById('notif-nav-badge')?.classList.toggle('hidden', !data.poison && !data.lost); } catch { - // Kein Standort verfügbar — ignorieren + // Kein Standort verfügbar — kein Alert anzeigen } } @@ -843,14 +848,6 @@ const App = (() => { // INITIALISIERUNG // ---------------------------------------------------------- async function init() { - // Spezielle Hash-Parameter → in App bleiben (kein /info-Redirect) - const _rawHash = location.hash.replace('#', ''); - const _hashQuery = _rawHash.split('?')[1] || ''; - const _hashP = new URLSearchParams(_hashQuery); - if (_hashP.get('verified') || _hashP.get('token') || location.pathname.startsWith('/teilen/')) { - sessionStorage.setItem('by_stay_in_app', '1'); - } - // Referral-Code aus URL ?ref=CODE speichern const urlParams = new URLSearchParams(window.location.search); const refCode = urlParams.get('ref'); diff --git a/backend/static/js/pages/onboarding.js b/backend/static/js/pages/onboarding.js index a8fd9b7..d2beee7 100644 --- a/backend/static/js/pages/onboarding.js +++ b/backend/static/js/pages/onboarding.js @@ -440,12 +440,9 @@ window.Page_onboarding = (() => { function _finish() { localStorage.setItem('by_onboarding_done', '1'); if (_appState.dogs.length > 0) { - if (window.Worlds) window.Worlds.init(_appState); - else App.navigate('diary'); + App.navigate('diary'); } else { - // Ohne Hund: Welten zeigen (HUND-Tab mit "Jetzt anlegen"-Karte) - if (window.Worlds) window.Worlds.init(_appState); - else App.navigate('welcome'); + App.navigate('map'); } } diff --git a/backend/static/js/pages/settings.js b/backend/static/js/pages/settings.js index 0197f58..743004e 100644 --- a/backend/static/js/pages/settings.js +++ b/backend/static/js/pages/settings.js @@ -50,12 +50,10 @@ window.Page_settings = (() => { // ---------------------------------------------------------- // INIT / REFRESH // ---------------------------------------------------------- - async function init(container, appState, params = {}) { + async function init(container, appState) { _container = container; _appState = appState; _render(); - if (params.tab === 'login') setTimeout(() => _renderAuth('login'), 50); - if (params.tab === 'register') setTimeout(() => _renderAuth('register'), 50); // Frischen User-State laden damit Badges (is_founder, is_partner) aktuell sind if (_appState.user) { try { @@ -1691,11 +1689,11 @@ window.Page_settings = (() => { _offerPushNotifications(); } - // Nach Login: Welten initialisieren oder Onboarding (mit Skip-Option) + // Nach Login: Welten initialisieren (mit User-State) oder Profil anlegen if (_appState.activeDog) { window.Worlds?.init(_appState); } else { - App.navigate('onboarding'); + App.navigate('dog-profile'); } }); }); diff --git a/backend/static/js/pages/welcome.js b/backend/static/js/pages/welcome.js index 9e60fb0..a6111f1 100644 --- a/backend/static/js/pages/welcome.js +++ b/backend/static/js/pages/welcome.js @@ -87,6 +87,7 @@ window.Page_welcome = (() => { // LANDING PAGE — nicht eingeloggte Besucher // ---------------------------------------------------------- function _renderLanding(isInstalled) { + // Browser-Besucher (kein PWA) ohne Login → auf /info weiterleiten const isPWA = window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true; if (!isPWA && !sessionStorage.getItem('by_stay_in_app')) { @@ -112,10 +113,17 @@ window.Page_welcome = (() => {
- Ban Yaro ist eine Web-App (PWA). Das bedeutet: kein App-Store-Download, automatische Updates ohne dein Zutun, und sie verhält sich genau wie eine native App — mit Icon, Vollbild und Offline-Modus. +
Halte jeden gemeinsamen Moment fest — Fotos, Einträge, Stimmungen. Nur für dich, privat und sicher.
+Impfungen, Gewicht, Tierarzttermine — alles an einem Ort. Du siehst immer, wann was ansteht.
+Giftköder-Warnungen, Gassi-Treffen, Events — was in deiner Gegend gerade passiert.
+Über 100 Übungen mit Schritt-für-Schritt-Anleitungen. Mit KI-Unterstützung, die deinen Hund kennt.
+
+ Kein Facebook. Kein Google. Keine Werbung.
+ Ban Yaro läuft auf einem eigenen Server in Deutschland —
+ dein Tagebuch, deine Routen, deine Gesundheitsdaten
+ bleiben privat.
- Falls eine Sicherheitswarnung erscheint: Das ist normal für neuere Webseiten — unabhängig vom Inhalt. Ban Yaro läuft auf einem deutschen Server, ist DSGVO-konform und enthält keine Schadsoftware. Tippe auf „Trotzdem hinzufügen". -
-- Falls eine Sicherheitswarnung erscheint: Das ist normal für neuere Webseiten. Tippe auf „Trotzdem hinzufügen". -
-7-Tage-Wetter, tägliche Bewertung 1–10 für Gassi-Eignung, automatische Zecken-Warnung.
Kalorienbedarf berechnen, BARF-Guide, Giftliste und KI-Futterberater.
Reisecheckliste und EU-Länder-Guide mit länderspezifischen Einreiseregeln und Impfvorschriften.
Öffentliche Profilseite für jeden Hund. Finder kontaktiert dich anonym — ohne deine Nummer preiszugeben.
Täglich neue Rundroute — 2, 4 oder 6 km ab deinem Standort. Berechnet via OpenRouteService.
Spontane oder geplante Gassi-Treffen erstellen und finden.
Öffentlich lesbar ohne Anmeldung. Kategorien nach Rasse, Region, Gesundheit und Erziehung.
Hundeo ist stark im Training — Dogorama stark in der Community. Ban Yaro vereint beides und geht weit darüber hinaus: Zucht-Management, KI-Trainer, Gesundheitsakte und Sitting in einer einzigen App — ohne App Store.
+Andere Apps decken einzelne Bereiche ab — Ban Yaro vereint alles in einer Plattform. Kein anderer Anbieter kombiniert Community, Training, Zucht und KI auf Deutsch.
| Funktion | Ban Yaro | -Hundeo | +Hundeo (DE) | Dogorama | +Tractive | +PetDesk | |||
|---|---|---|---|---|---|---|---|---|---|
| Kostenlos nutzbar | ✓ Ja | -Freemium | -Freemium | +Begrenzt | +Begrenzt | +✗ Abo | +✗ Nein | ||
| DSGVO / EU-konform | +DSGVO / EU-Hosting | ✓ DE | -✓ EU | ✓ DE | +✗ Nein | +Teilweise | +✗ USA | ||
| Kein App Store nötig (PWA) | -✓ | +Kein App Store nötig | +✓ PWA | +✗ | +✗ | ✗ | ✗ | ✓ | ✓ | ✗ | +✗ | +✗ |
| Giftköder-Alarm | ✓ | ✗ | -✓ | +✗ | +✗ | +✗ | |||
| Gesundheitsakte & Impfpass | +Digitaler Impfpass | ✓ | ✗ | +✗ | +✗ | ✓ | |||
| Forum & Community | +Forum & Community | ✓ | ✗ | ✓ | -|||||
| Gassi-Treffen & Playdates | -✓ | -✗ | -✓ | -||||||
| Wurfbörse & Zucht-Management | -✓ | ✗ | ✗ | ||||||
| Stammbaum & Inzucht-Check | +Gassi-Treffen & Community | +✓ | +✗ | ✓ | ✗ | ✗ | |||
| Hundesitting-Vermittlung | -✓ kostenlos | +Wurfbörse & Zucht-Management | +✓ | +✗ | +✗ | +✗ | +✗ | +||
| Stammbaum & Inzucht-Check | +✓ | +✗ | +✗ | +✗ | +✗ | +||||
| Hundesitting | +✓ 8% | +✗ | +✗ | ✗ | ✗ | ✓ | ✗ | ✓ | +✓ GPS | +✗ |
| Rassen-Wiki (KI-angereichert) | -✓ 1.003 | -200+ | -Basis | +Rassen-Wiki (1003 Rassen, KI) | +✓ | +✗ | +✗ | +✗ | +✗ |
| Offline-Modus | ✓ | -nur Premium | +✗ | +✗ | +✗ | ✗ | |||
| KI-Routenvorschlag | +Täglicher Routenvorschlag | ✓ | ✗ | ✗ | +✗ | +✗ |
Ban Yaro wurde von Hundebesitzern für Hundebesitzer entwickelt — mit einem klaren Standpunkt zu Datenschutz und Fairness.
Hosting in Deutschland, deutschsprachiger Support, auf DACH-Nutzer zugeschnitten.
Keine Datenweitergabe an US-Konzerne. Cookielose Analytics (Umami). Transparente Datennutzung.
Als Progressive Web App direkt über den Browser installierbar — auf iOS und Android. Sofort updatebar.
Service Worker sorgt dafür dass die App auch ohne Internet funktioniert — beim Gassi gehen in der Natur.
Hundesitting-Vermittlung kostenlos — keine Plattform-Provision, ihr einigt euch direkt. Rover und Pawshake nehmen 20%.
+Hundesitting nur 8% Provision — Rover und Pawshake nehmen 20%. Mehr Geld bleibt beim Sitter.
Karten von OpenStreetMap statt Google — keine Tracking-Cookies, kein API-Lock-in, günstiger für alle.
HSTS, Content-Security-Policy, Rate Limiting auf allen Endpunkten, Account-Lockout nach Fehlversuchen, E-Mail-Verifikation. Sicherheit by Default, nicht als Nachgedanke.
KI-Funktionen laufen standardmäßig lokal auf unserem Server in Deutschland — deine Anfragen verlassen Europa nicht. Bei Überlast Fallback auf Claude Sonnet (Anthropic, USA). Übertragen wird nur deine Frage und der Kontext (Rasse, Alter) — keine Fotos, keine sensiblen Profildaten. Kein Training mit deinen Daten.
+Alle KI-Funktionen laufen über Claude (Anthropic) — kein Training mit deinen Daten, kein Opt-out nötig, deine Daten bleiben deine Daten.
Ban Yaro — Die deutschsprachige Hunde-Plattform
banyaro.app · DSGVO-konform · Hosting in Deutschland · Made with 🐾