diff --git a/backend/main.py b/backend/main.py index 8eebbd2..8c5b390 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 = "741" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "760" # 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 4fbd03c..d738998 100644 --- a/backend/routes/outreach.py +++ b/backend/routes/outreach.py @@ -116,11 +116,17 @@ 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() - 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) + 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) _imap_save_sent(msg_bytes, account) diff --git a/backend/static/index.html b/backend/static/index.html index 2f22a0a..c3abcea 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 263fa80..0fe4e0e 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 = '741'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '760'; // ← 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,20 +555,15 @@ 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; - 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); + await API.get(`/alerts?lat=${lat}&lon=${lon}`); + // Standort-Update für Push-Subscriptions (serverseitig in /alerts gespeichert) } catch { - // Kein Standort verfügbar — kein Alert anzeigen + // Kein Standort verfügbar — ignorieren } } @@ -848,6 +843,14 @@ 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 d2beee7..a8fd9b7 100644 --- a/backend/static/js/pages/onboarding.js +++ b/backend/static/js/pages/onboarding.js @@ -440,9 +440,12 @@ window.Page_onboarding = (() => { function _finish() { localStorage.setItem('by_onboarding_done', '1'); if (_appState.dogs.length > 0) { - App.navigate('diary'); + if (window.Worlds) window.Worlds.init(_appState); + else App.navigate('diary'); } else { - App.navigate('map'); + // Ohne Hund: Welten zeigen (HUND-Tab mit "Jetzt anlegen"-Karte) + if (window.Worlds) window.Worlds.init(_appState); + else App.navigate('welcome'); } } diff --git a/backend/static/js/pages/settings.js b/backend/static/js/pages/settings.js index 743004e..0197f58 100644 --- a/backend/static/js/pages/settings.js +++ b/backend/static/js/pages/settings.js @@ -50,10 +50,12 @@ window.Page_settings = (() => { // ---------------------------------------------------------- // INIT / REFRESH // ---------------------------------------------------------- - async function init(container, appState) { + async function init(container, appState, params = {}) { _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 { @@ -1689,11 +1691,11 @@ window.Page_settings = (() => { _offerPushNotifications(); } - // Nach Login: Welten initialisieren (mit User-State) oder Profil anlegen + // Nach Login: Welten initialisieren oder Onboarding (mit Skip-Option) if (_appState.activeDog) { window.Worlds?.init(_appState); } else { - App.navigate('dog-profile'); + App.navigate('onboarding'); } }); }); diff --git a/backend/static/js/pages/welcome.js b/backend/static/js/pages/welcome.js index a6111f1..9e60fb0 100644 --- a/backend/static/js/pages/welcome.js +++ b/backend/static/js/pages/welcome.js @@ -87,7 +87,6 @@ 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')) { @@ -113,17 +112,10 @@ window.Page_welcome = (() => {
- ${hasPrompt ? ` - - ` : ` - - `} + @@ -145,119 +137,37 @@ window.Page_welcome = (() => {
- -
-
- -
-
-

Tagebuch & Erinnerungen

-

Halte jeden gemeinsamen Moment fest — Fotos, Einträge, Stimmungen. Nur für dich, privat und sicher.

-
-
- - -
-
- -
-
-

Gesundheit im Blick

-

Impfungen, Gewicht, Tierarzttermine — alles an einem Ort. Du siehst immer, wann was ansteht.

-
-
- - -
-
- -
-
-

Community vor Ort

-

Giftköder-Warnungen, Gassi-Treffen, Events — was in deiner Gegend gerade passiert.

-
-
- - -
-
- -
-
-

Training & KI-Trainer

-

Über 100 Übungen mit Schritt-für-Schritt-Anleitungen. Mit KI-Unterstützung, die deinen Hund kennt.

-
-
- - -
-
- -
-

Deine Daten gehören dir.

-

- Kein Facebook. Kein Google. Keine Werbung.
- Ban Yaro läuft auf einem eigenen Server in Deutschland — - dein Tagebuch, deine Routen, deine Gesundheitsdaten - bleiben privat. -

-
- - -
- -
- ${FEATURES.map(f => ` -
-
- -
- ${f.label} -
- `).join('')} -
-
- - -
- ${hasPrompt ? ` - - ` : ` - - `} -

Kein App Store · Direkt auf den Home-Bildschirm

- - ${!isInstalled ? ` - - ` : ''} -
- - - ${_showInstall ? ` -
-
-
- - Immer griffbereit — kein App Store -
-
${_installHTML()}
+ + ${!isInstalled ? ` +
+
+
+ +
+
+
Kein App Store nötig
+
Füge Ban Yaro zum Home-Bildschirm hinzu — einmal, dann immer griffbereit
+

+ 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. +

+ ${hasPrompt ? ` + + ` : ` +
${_installHTML()}
+ `} +
` : ''} - +
`; } @@ -811,6 +721,43 @@ window.Page_welcome = (() => { } .wc-grid.wc-grid--collapsed { display: none; } + /* Install Block */ + .wc-install-block { + background: var(--c-bg-card); + border-top: 3px solid var(--c-primary); + padding: var(--space-5) var(--space-5) var(--space-6); + } + .wc-install-block-header { + display: flex; align-items: flex-start; gap: var(--space-3); + margin-bottom: var(--space-3); + } + .wc-install-block-icon { + width: 44px; height: 44px; border-radius: var(--radius-md); + background: var(--c-primary-subtle); flex-shrink: 0; + display: flex; align-items: center; justify-content: center; + } + .wc-install-block-icon .ph-icon { width: 22px; height: 22px; color: var(--c-primary); } + .wc-install-block-title { + font-size: var(--text-base); font-weight: var(--weight-bold); + color: var(--c-text); margin-bottom: 2px; + } + .wc-install-block-sub { + font-size: var(--text-sm); color: var(--c-text-secondary); line-height: 1.4; + } + .wc-install-block-why { + font-size: var(--text-sm); color: var(--c-text-secondary); + line-height: 1.6; margin: 0 0 var(--space-4); + padding: var(--space-3) var(--space-4); + background: var(--c-surface); border-radius: var(--radius-md); + border-left: 3px solid var(--c-primary); + } + .wc-install-block-btn { + width: 100%; font-size: var(--text-base); + padding: 14px; border-radius: var(--radius-lg); + display: flex; align-items: center; justify-content: center; gap: var(--space-2); + } + .wc-install-block-steps { margin-top: var(--space-2); } + /* Bottom CTA */ .wc-bottom-cta { padding: var(--space-8) var(--space-5) var(--space-6); @@ -1127,6 +1074,15 @@ window.Page_welcome = (() => { ['dots-three-circle','Tippe auf das Menü ⋮ oben rechts'], ['download-simple', 'Wähle „App installieren" oder
„Zum Startbildschirm hinzufügen"'], ])} +
+ +

+ 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". +

+
@@ -636,14 +635,6 @@

Wetter, Gassi-Score & Zecken-Alarm

7-Tage-Wetter, tägliche Bewertung 1–10 für Gassi-Eignung, automatische Zecken-Warnung.

-
- -

Ernährung & Futter

Kalorienbedarf berechnen, BARF-Guide, Giftliste und KI-Futterberater.

-
-
- -

Reise mit Hund

Reisecheckliste und EU-Länder-Guide mit länderspezifischen Einreiseregeln und Impfvorschriften.

-

NFC-Halsband-Tags

Öffentliche Profilseite für jeden Hund. Finder kontaktiert dich anonym — ohne deine Nummer preiszugeben.

@@ -666,10 +657,6 @@

Tages-Gassirunde

Täglich neue Rundroute — 2, 4 oder 6 km ab deinem Standort. Berechnet via OpenRouteService.

-
- -

Gassi-Treffen

Spontane oder geplante Gassi-Treffen erstellen und finden.

-

Forum

Öffentlich lesbar ohne Anmeldung. Kategorien nach Rasse, Region, Gesundheit und Erziehung.

@@ -714,7 +701,6 @@
- Züchter-Profil anlegen — kostenlos @@ -748,48 +734,39 @@ - Wurfbörse durchstöbern — kostenlos

Ban Yaro vs. Konkurrenz

-

Andere Apps decken einzelne Bereiche ab — Ban Yaro vereint alles in einer Plattform. Kein anderer Anbieter kombiniert Community, Training, Zucht und KI auf Deutsch.

+

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.

- + - - - - - - + + - + + - - - - - - - + + @@ -798,62 +775,46 @@ - - - - - + - + - - - + + + + + + + + + + + - - - + - - - - - - - - - - - - - - - - - - - - + + @@ -862,32 +823,24 @@ - - - - - - - - + + + + - - - + - + - -
Funktion Ban YaroHundeo (DE)Hundeo DogoramaTractivePetDesk
Kostenlos nutzbar ✓ JaBegrenztBegrenzt✗ Abo✗ NeinFreemiumFreemium
DSGVO / EU-HostingDSGVO / EU-konform ✓ DE✓ EU ✓ DE✗ NeinTeilweise✗ USA
Kein App Store nötig✓ PWAKein App Store nötig (PWA)
Giftköder-Alarm
Digitaler ImpfpassGesundheitsakte & Impfpass
Forum & CommunityForum & Community
Gassi-Treffen & Playdates
Wurfbörse & Zucht-Management
Gassi-Treffen & CommunityStammbaum & Inzucht-Check
Wurfbörse & Zucht-Management
Stammbaum & Inzucht-Check
Hundesitting✓ 8%Hundesitting-Vermittlung✓ kostenlos
✓ GPS
Rassen-Wiki (1003 Rassen, KI)Rassen-Wiki (KI-angereichert)✓ 1.003200+Basis
Offline-Modus nur Premium
Täglicher RoutenvorschlagKI-Routenvorschlag
@@ -948,59 +901,59 @@

Ban Yaro wurde von Hundebesitzern für Hundebesitzer entwickelt — mit einem klaren Standpunkt zu Datenschutz und Fairness.

- 🇩🇪 +

Deutsche Plattform

Hosting in Deutschland, deutschsprachiger Support, auf DACH-Nutzer zugeschnitten.

- 🔒 +

Deine Daten. Dein Eigentum.

Keine Datenweitergabe an US-Konzerne. Cookielose Analytics (Umami). Transparente Datennutzung.

- 📱 +

Kein App Store

Als Progressive Web App direkt über den Browser installierbar — auf iOS und Android. Sofort updatebar.

- 📡 +

Offline-fähig

Service Worker sorgt dafür dass die App auch ohne Internet funktioniert — beim Gassi gehen in der Natur.

- 💸 +
-

Faire Provision

-

Hundesitting nur 8% Provision — Rover und Pawshake nehmen 20%. Mehr Geld bleibt beim Sitter.

+

Sitting ohne Provision

+

Hundesitting-Vermittlung kostenlos — keine Plattform-Provision, ihr einigt euch direkt. Rover und Pawshake nehmen 20%.

- 🗺️ +

OpenStreetMap

Karten von OpenStreetMap statt Google — keine Tracking-Cookies, kein API-Lock-in, günstiger für alle.

- 🔐 +

Aktive Sicherheit

HSTS, Content-Security-Policy, Rate Limiting auf allen Endpunkten, Account-Lockout nach Fehlversuchen, E-Mail-Verifikation. Sicherheit by Default, nicht als Nachgedanke.

- 🤖 +
-

KI Made in Europe

-

Alle KI-Funktionen laufen über Claude (Anthropic) — kein Training mit deinen Daten, kein Opt-out nötig, deine Daten bleiben deine Daten.

+

KI mit Datenschutz

+

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.

@@ -1068,14 +1021,9 @@

Ban Yaro — Die deutschsprachige Hunde-Plattform

banyaro.app · DSGVO-konform · Hosting in Deutschland · Made with 🐾

diff --git a/backend/static/manifest.json b/backend/static/manifest.json index 6df0d11..2442ee3 100644 --- a/backend/static/manifest.json +++ b/backend/static/manifest.json @@ -7,6 +7,8 @@ "start_url": "/", "scope": "/", "display": "standalone", + "display_override": ["window-controls-overlay", "standalone"], + "launch_handler": { "client_mode": "focus-existing" }, "orientation": "portrait-primary", "background_color": "#FAF7F2", "theme_color": "#C4843A", diff --git a/backend/static/sw.js b/backend/static/sw.js index 9069442..ba39608 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-v741'; +const CACHE_VERSION = 'by-v760'; 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