Compare commits

..

3 commits

9 changed files with 213 additions and 11 deletions

View file

@ -1400,6 +1400,208 @@ async def knigge_page():
return HTMLResponse(content=html, headers={"Cache-Control": "max-age=7200"})
# ------------------------------------------------------------------
# /partner — Influencer-Landingpage
# ------------------------------------------------------------------
@app.get("/partner")
async def partner_landing():
from fastapi.responses import HTMLResponse
from database import db as _db
with _db() as conn:
total_founders = conn.execute("SELECT COUNT(*) FROM users WHERE is_founder=1").fetchone()[0]
partners = conn.execute(
"""SELECT label, uses FROM partner_codes WHERE grants_founder=1 ORDER BY uses DESC LIMIT 5"""
).fetchall()
open_slots = max(0, 100 - total_founders)
partner_rows = ''.join([
f'<div class="pl-partner-row"><span class="pl-partner-name">{p["label"]}</span>'
f'<span class="pl-partner-score">{p["uses"]} Gründer</span></div>'
for p in partners
]) or '<div style="color:#94a3b8;font-size:0.85rem">Noch keine Partner aktiv — sei der Erste.</div>'
html = f"""<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ban Yaro Partner Werde Teil der ersten 100</title>
<meta name="description" content="Werde Ban Yaro Partner. Gib deiner Community exklusive Gründer-Lizenzen — nur 100 Plätze weltweit, nie wieder erhältlich.">
<meta property="og:title" content="Ban Yaro Partner">
<meta property="og:description" content="Gib deiner Community etwas Besonderes. 100 Gründer-Plätze. Exklusiv. Für immer.">
<meta property="og:image" content="https://banyaro.app/icons/icon-512.png">
<style>
*,*::before,*::after{{box-sizing:border-box;margin:0;padding:0}}
body{{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#faf9f7;color:#1a1a1a;line-height:1.6}}
a{{color:#C4843A;text-decoration:none}}
.pl-wrap{{max-width:680px;margin:0 auto;padding:24px 20px 80px}}
/* Hero */
.pl-hero{{text-align:center;padding:60px 0 48px;border-bottom:1px solid #e8e4df}}
.pl-logo{{width:72px;height:72px;border-radius:18px;margin:0 auto 24px;display:block}}
.pl-eyebrow{{font-size:0.75rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:#C4843A;margin-bottom:12px}}
.pl-h1{{font-size:clamp(1.8rem,5vw,2.8rem);font-weight:800;line-height:1.15;color:#111;margin-bottom:16px}}
.pl-sub{{font-size:1.05rem;color:#555;max-width:480px;margin:0 auto 32px}}
.pl-cta{{display:inline-block;background:#C4843A;color:#fff;font-weight:700;font-size:1rem;padding:14px 32px;border-radius:999px;text-decoration:none;transition:opacity .15s}}
.pl-cta:hover{{opacity:.88}}
/* Slot-Counter */
.pl-counter{{background:#fff;border:1px solid #e8e4df;border-radius:16px;padding:28px 24px;margin:40px 0;text-align:center}}
.pl-counter-num{{font-size:3rem;font-weight:800;color:#C4843A;line-height:1}}
.pl-counter-label{{font-size:0.85rem;color:#888;margin-top:4px}}
.pl-bar-wrap{{background:#f0ece8;border-radius:999px;height:10px;margin:16px 0 8px;overflow:hidden}}
.pl-bar{{background:linear-gradient(90deg,#C4843A,#e0a870);height:100%;border-radius:999px;width:{min(100, round(total_founders/100*100))}%}}
.pl-bar-labels{{display:flex;justify-content:space-between;font-size:0.75rem;color:#aaa}}
/* Vorteile */
.pl-benefits{{margin:40px 0}}
.pl-section-title{{font-size:1.1rem;font-weight:700;margin-bottom:20px;color:#111}}
.pl-benefit{{display:flex;gap:16px;align-items:flex-start;padding:16px 0;border-bottom:1px solid #f0ece8}}
.pl-benefit:last-child{{border-bottom:none}}
.pl-benefit-icon{{font-size:1.6rem;flex-shrink:0;width:40px;text-align:center}}
.pl-benefit-title{{font-weight:700;font-size:0.95rem;margin-bottom:2px}}
.pl-benefit-text{{font-size:0.88rem;color:#555}}
/* Wie es funktioniert */
.pl-steps{{margin:40px 0}}
.pl-step{{display:flex;gap:16px;align-items:flex-start;margin-bottom:24px}}
.pl-step-num{{width:32px;height:32px;border-radius:50%;background:#C4843A;color:#fff;font-weight:800;font-size:0.9rem;display:flex;align-items:center;justify-content:center;flex-shrink:0}}
.pl-step-text{{padding-top:4px;font-size:0.92rem;color:#333}}
.pl-step-text strong{{display:block;font-weight:700;color:#111;margin-bottom:2px}}
/* Leaderboard */
.pl-leaderboard{{background:#fff;border:1px solid #e8e4df;border-radius:16px;padding:24px;margin:40px 0}}
.pl-partner-row{{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid #f5f3f0;font-size:0.9rem}}
.pl-partner-row:last-child{{border-bottom:none}}
.pl-partner-name{{font-weight:600}}
.pl-partner-score{{color:#C4843A;font-weight:700}}
/* Kontakt */
.pl-contact{{background:linear-gradient(135deg,#C4843A,#d4944a);border-radius:20px;padding:40px 32px;text-align:center;color:#fff;margin:40px 0}}
.pl-contact h2{{font-size:1.5rem;font-weight:800;margin-bottom:12px}}
.pl-contact p{{font-size:0.95rem;opacity:.9;margin-bottom:24px;max-width:400px;margin-left:auto;margin-right:auto}}
.pl-contact-btn{{display:inline-block;background:#fff;color:#C4843A;font-weight:700;padding:12px 28px;border-radius:999px;font-size:0.95rem}}
/* Footer */
.pl-footer{{text-align:center;font-size:0.78rem;color:#aaa;padding-top:32px;border-top:1px solid #f0ece8}}
</style>
</head>
<body>
<div class="pl-wrap">
<!-- Hero -->
<div class="pl-hero">
<img src="/icons/icon-192.png" alt="Ban Yaro" class="pl-logo">
<div class="pl-eyebrow">Ban Yaro · Influencer-Programm</div>
<h1 class="pl-h1">Gib deiner Community<br>etwas für immer.</h1>
<p class="pl-sub">100 Gründer-Plätze. Weltweit. Nie wieder erhältlich.<br>Als Partner bringst du deine Follower nach vorne und steigst im Ranking auf.</p>
<a href="mailto:partner@banyaro.app" class="pl-cta">Jetzt Partner werden</a>
</div>
<!-- Slot-Counter -->
<div class="pl-counter">
<div class="pl-counter-num">{open_slots}</div>
<div class="pl-counter-label">Gründer-Plätze noch frei (von 100)</div>
<div class="pl-bar-wrap"><div class="pl-bar"></div></div>
<div class="pl-bar-labels"><span>0</span><span>{total_founders} vergeben</span><span>100</span></div>
</div>
<!-- Vorteile -->
<div class="pl-benefits">
<div class="pl-section-title">Was du und deine Community bekommen</div>
<div class="pl-benefit">
<div class="pl-benefit-icon">🏆</div>
<div>
<div class="pl-benefit-title">Gründer-Lizenz für deine Follower</div>
<div class="pl-benefit-text">Jeder der sich mit deinem Code registriert bekommt einen der 100 Gründer-Plätze mit einer nummerierten Badge <strong>Gründer #N"</strong> die dauerhaft im Profil und im Forum sichtbar ist. Nie wieder erhältlich.</div>
</div>
</div>
<div class="pl-benefit">
<div class="pl-benefit-icon">🤝</div>
<div>
<div class="pl-benefit-title">Dein persönlicher Partner-Code</div>
<div class="pl-benefit-text">Du bekommst einen eigenen Code (z.B. <strong>HUNDEBLOG</strong>). Follower die sich damit registrieren werden automatisch Gründer du siehst in Echtzeit wie viele du gebracht hast.</div>
</div>
</div>
<div class="pl-benefit">
<div class="pl-benefit-icon">📊</div>
<div>
<div class="pl-benefit-title">Öffentliches Partner-Ranking</div>
<div class="pl-benefit-text">Auf der <a href="/app#gruender">Gründer-Seite</a> siehen alle wer die meisten Gründer gebracht hat. Das Ranking motiviert deine Follower mitzumachen und stärkt deine Position gegenüber anderen Influencern.</div>
</div>
</div>
<div class="pl-benefit">
<div class="pl-benefit-icon">💜</div>
<div>
<div class="pl-benefit-title">Partner-Badge für dich</div>
<div class="pl-benefit-text">Du selbst bekommst ein <strong>Partner"</strong>-Badge in deinem Profil — sichtbar für alle Nutzer der App.</div>
</div>
</div>
<div class="pl-benefit">
<div class="pl-benefit-icon">🎁</div>
<div>
<div class="pl-benefit-title">Lebenslang kostenlos für immer</div>
<div class="pl-benefit-text">Gründer zahlen nie für Premium-Features egal was wir in Zukunft einführen. Das ist ein echtes Dankeschön für die Pioniere.</div>
</div>
</div>
</div>
<!-- Wie es funktioniert -->
<div class="pl-steps">
<div class="pl-section-title">Wie es funktioniert</div>
<div class="pl-step">
<div class="pl-step-num">1</div>
<div class="pl-step-text"><strong>Kontakt aufnehmen</strong>Schreib uns kurz an partner@banyaro.app wir richten deinen persönlichen Code ein.</div>
</div>
<div class="pl-step">
<div class="pl-step-num">2</div>
<div class="pl-step-text"><strong>Code teilen</strong>Du postest deinen Code in Story, Reel oder Post deine Follower registrieren sich auf banyaro.app.</div>
</div>
<div class="pl-step">
<div class="pl-step-num">3</div>
<div class="pl-step-text"><strong>Gründer werden</strong>Jede Registrierung mit deinem Code sichert automatisch einen der 100 Gründer-Plätze. Du siehst deinen Fortschritt in Echtzeit.</div>
</div>
<div class="pl-step">
<div class="pl-step-num">4</div>
<div class="pl-step-text"><strong>Im Ranking aufsteigen</strong>Je mehr Gründer du bringst, desto höher dein Platz auf der öffentlichen Gründer-Seite.</div>
</div>
</div>
<!-- Leaderboard -->
{'<div class="pl-leaderboard"><div class="pl-section-title" style="margin-bottom:16px">🏅 Aktuelles Partner-Ranking</div>' + partner_rows + '</div>' if partners else ''}
<!-- Was ist Ban Yaro -->
<div style="background:#fff;border:1px solid #e8e4df;border-radius:16px;padding:28px 24px;margin:40px 0">
<div class="pl-section-title">Was ist Ban Yaro?</div>
<p style="font-size:0.9rem;color:#555;margin-bottom:12px">Ban Yaro ist die Hunde-App für alles was Halter brauchen Tagebuch, Gesundheit, Routen, Giftköder-Alarm, Community. Kostenlos, ohne App Store, direkt im Browser oder als PWA.</p>
<a href="https://banyaro.app" style="font-weight:700;color:#C4843A;font-size:0.9rem">banyaro.app entdecken </a>
</div>
<!-- CTA -->
<div class="pl-contact">
<h2>Bereit dabei zu sein?</h2>
<p>Schreib uns kurz wer du bist und auf welchem Kanal du aktiv bist wir richten deinen Code binnen 24h ein.</p>
<a href="mailto:partner@banyaro.app?subject=Ban Yaro Partner&body=Hallo,%0A%0Aich bin interessiert am Ban Yaro Partner-Programm.%0A%0AKanal / Reichweite:%0A%0AViele Grüße" class="pl-contact-btn">📧 partner@banyaro.app</a>
</div>
<!-- Footer -->
<div class="pl-footer">
<a href="https://banyaro.app">banyaro.app</a> ·
<a href="/impressum">Impressum</a> ·
<a href="/datenschutz">Datenschutz</a>
</div>
</div>
</body>
</html>"""
return HTMLResponse(content=html, headers={"Cache-Control": "no-cache"})
# SPA Fallback — ALLE nicht-API-Routen gehen zur index.html
@app.get("/{full_path:path}")
async def spa_fallback(full_path: str):

View file

@ -21,7 +21,7 @@ _TZ = ZoneInfo("Europe/Berlin")
BREEDER_DOCS_DIR = os.getenv("BREEDER_DOCS_DIR", "/data/breeder_docs")
os.makedirs(BREEDER_DOCS_DIR, exist_ok=True)
ADMIN_EMAIL = os.getenv("ADMIN_EMAIL", "mail@motocamp.de")
ADMIN_EMAIL = os.getenv("ADMIN_EMAIL", "admin@banyaro.app")
APP_URL = os.getenv("APP_URL", "https://banyaro.app")

View file

@ -258,7 +258,7 @@ async def welfare_confirm(litter_id: int, user=Depends(_require_breeder)):
"FROM breeder_profiles bp JOIN users u ON u.id=bp.user_id "
"WHERE bp.user_id=?", (user["id"],)
).fetchone()
admin_email = os.getenv("ADMIN_EMAIL", "mail@motocamp.de")
admin_email = os.getenv("ADMIN_EMAIL", "admin@banyaro.app")
app_url = os.getenv("APP_URL", "https://banyaro.app")
zuechter = profile["name"] if profile else user.get("name", "Unbekannt")
zwinger = profile["zwingername"] if profile else ""

View file

@ -28,7 +28,7 @@ OVERPASS_URLS = [
_overpass_sem = asyncio.Semaphore(1)
_overpass_last_req = 0.0
_OVERPASS_MIN_DELAY = 2.0 # Sekunden zwischen Anfragen
_OVERPASS_UA = 'BanYaro/1.0 (https://banyaro.app; dog-walking PWA; contact: mail@motocamp.de)'
_OVERPASS_UA = 'BanYaro/1.0 (https://banyaro.app; dog-walking PWA; contact: admin@banyaro.app)'
_OVERPASS_HEADERS = {
'User-Agent': _OVERPASS_UA,
'Referer': 'https://banyaro.app/', # von overpass-api.de verlangt gegen 406

View file

@ -230,7 +230,7 @@
border-top:1px solid var(--c-border,#e5e7eb);
font-size:var(--text-xs);color:var(--c-text-muted);
display:flex;flex-direction:column;gap:var(--space-2);padding-bottom:var(--space-2)">
<div style="display:flex;gap:var(--space-3)">
<div style="display:flex;gap:var(--space-3);justify-content:center">
<span data-page="impressum" style="cursor:pointer;text-decoration:underline">Impressum</span>
<span data-page="datenschutz" style="cursor:pointer;text-decoration:underline">Datenschutz</span>
</div>

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '539'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '542'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.1.4'; // ← semantische Version, wird bei make release gesetzt
const IS_STAGING = location.hostname === 'staging.banyaro.app';

View file

@ -29,7 +29,7 @@ window.Page_datenschutz = (() => {
${sec('Verantwortlicher', `
<p style="${S.p}">
René Degelmann, Ringstr. 26, 85560 Ebersberg<br>
E-Mail: <a href="mailto:mail@motocamp.de" style="${S.a}">mail@motocamp.de</a>
E-Mail: <a href="mailto:hallo@banyaro.app" style="${S.a}">hallo@banyaro.app</a>
</p>`)}
${sec('Deine Daten gehören dir', `
@ -169,7 +169,7 @@ window.Page_datenschutz = (() => {
(Art. 18) sowie <strong>Datenportabilität</strong> (Art. 20). Erteilte Einwilligungen
kannst du jederzeit mit Wirkung für die Zukunft widerrufen (Art. 7 Abs. 3 DSGVO).
Zur Ausübung deiner Rechte wende dich per E-Mail an
<a href="mailto:mail@motocamp.de" style="${S.a}">mail@motocamp.de</a>.<br><br>
<a href="mailto:hallo@banyaro.app" style="${S.a}">hallo@banyaro.app</a>.<br><br>
Du hast außerdem das Recht, bei der zuständigen Datenschutz-Aufsichtsbehörde
Beschwerde einzulegen:<br>
<strong>Bayerisches Landesamt für Datenschutzaufsicht (BayLDA)</strong><br>

View file

@ -25,9 +25,9 @@ window.Page_impressum = (() => {
<h2 style="font-size:var(--text-base);font-weight:var(--weight-semibold);
color:var(--c-text);margin:0 0 var(--space-2)">Kontakt</h2>
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);line-height:1.7;margin:0">
E-Mail: <a href="mailto:mail@motocamp.de"
style="color:var(--c-primary)">mail@motocamp.de</a><br>
Kontaktformular: <a href="mailto:mail@motocamp.de"
E-Mail: <a href="mailto:hallo@banyaro.app"
style="color:var(--c-primary)">hallo@banyaro.app</a><br>
Kontaktformular: <a href="mailto:hallo@banyaro.app"
style="color:var(--c-primary)">Nachricht senden</a>
</p>
</section>

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache
============================================================ */
const CACHE_VERSION = 'by-v562';
const CACHE_VERSION = 'by-v565';
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