From b5e4eab84d0f1a6dd023ca36d330c4efb11a12ca Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 24 Apr 2026 09:35:55 +0200 Subject: [PATCH] =?UTF-8?q?Push=20geo-filter:=20Giftk=C3=B6der-Alert=20nur?= =?UTF-8?q?=20im=2030km-Radius,=20Standort=20via=20Alerts-Check=20gespeich?= =?UTF-8?q?ert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/database.py | 8 ++++++++ backend/routes/alerts.py | 12 ++++++++++-- backend/routes/poison.py | 6 +++--- backend/routes/push.py | 23 +++++++++++++++++++++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/backend/database.py b/backend/database.py index 0292c32..dd55484 100644 --- a/backend/database.py +++ b/backend/database.py @@ -1009,3 +1009,11 @@ def _migrate(conn_factory): CREATE INDEX IF NOT EXISTS idx_wp_dog_week ON weekly_praise(dog_id, week_key DESC); """) logger.info("Migration: weekly_praise Tabelle bereit.") + + # Push: Standort-Filter (last_lat/lon für geo-basierte Alerts) + for col in ["last_lat REAL", "last_lon REAL"]: + try: + conn.execute(f"ALTER TABLE push_subscriptions ADD COLUMN {col}") + except Exception: + pass + logger.info("Migration: push_subscriptions last_lat/lon bereit.") diff --git a/backend/routes/alerts.py b/backend/routes/alerts.py index e5afb2a..4ffdcd0 100644 --- a/backend/routes/alerts.py +++ b/backend/routes/alerts.py @@ -1,9 +1,10 @@ """BAN YARO — Nearby Alerts (Giftköder + Vermisste Hunde)""" import math from datetime import datetime -from fastapi import APIRouter +from fastapi import APIRouter, Depends from database import db +from auth import get_current_user_optional as get_optional_user router = APIRouter() @@ -20,7 +21,7 @@ def _haversine(lat1: float, lon1: float, lat2: float, lon2: float) -> float: @router.get("") -async def nearby_alerts(lat: float, lon: float): +async def nearby_alerts(lat: float, lon: float, user=Depends(get_optional_user)): now = datetime.utcnow().isoformat() with db() as conn: poisons = conn.execute( @@ -29,6 +30,13 @@ async def nearby_alerts(lat: float, lon: float): lost = conn.execute( "SELECT lat, lon FROM lost_dogs WHERE is_active=1" ).fetchall() + # Letzten Standort des Users für geo-basierte Push-Filter speichern + if user: + conn.execute( + """UPDATE push_subscriptions SET last_lat=?, last_lon=? + WHERE user_id=?""", + (lat, lon, user["id"]) + ) has_poison = any(_haversine(lat, lon, r["lat"], r["lon"]) <= _RADIUS_M for r in poisons) has_lost = any(_haversine(lat, lon, r["lat"], r["lon"]) <= _RADIUS_M for r in lost) diff --git a/backend/routes/poison.py b/backend/routes/poison.py index a7041e8..2372e74 100644 --- a/backend/routes/poison.py +++ b/backend/routes/poison.py @@ -7,7 +7,7 @@ from pydantic import BaseModel from typing import Optional from database import db from auth import get_current_user -from routes.push import send_push_to_all +from routes.push import send_push_nearby from media_utils import convert_media from ratelimit import check as rl_check @@ -91,8 +91,8 @@ async def report_poison(data: PoisonCreate, request: Request, ).fetchone() entry = dict(row) - # Push-Notification an alle User - send_push_to_all({ + # Push nur an User im Umkreis von 30 km + send_push_nearby(data.lat, data.lon, 30_000, { "type": "poison_alert", "title": "⚠️ Giftköder gemeldet!", "body": f"{data.typ or 'Verdächtiger Fund'} in deiner Nähe — bitte vorsichtig sein.", diff --git a/backend/routes/push.py b/backend/routes/push.py index dd11fbc..3ee73d9 100644 --- a/backend/routes/push.py +++ b/backend/routes/push.py @@ -140,3 +140,26 @@ def send_push_to_all(payload: dict): sent += 1 logger.info(f"Push an {sent}/{len(rows)} Subscriptions gesendet.") return sent + + +def send_push_nearby(lat: float, lon: float, radius_m: float, payload: dict): + """Schickt Push nur an User deren letzter bekannter Standort innerhalb radius_m liegt. + User ohne gespeicherten Standort werden übersprungen.""" + import math + def _dist(la1, lo1, la2, lo2): + R = 6_371_000 + p1, p2 = math.radians(la1), math.radians(la2) + a = math.sin(math.radians(la2-la1)/2)**2 + math.cos(p1)*math.cos(p2)*math.sin(math.radians(lo2-lo1)/2)**2 + return 2*R*math.asin(math.sqrt(a)) + + with db() as conn: + rows = conn.execute( + "SELECT * FROM push_subscriptions WHERE last_lat IS NOT NULL" + ).fetchall() + sent = 0 + for row in rows: + if _dist(lat, lon, row["last_lat"], row["last_lon"]) <= radius_m: + if send_push(row, payload): + sent += 1 + logger.info(f"Push nearby ({radius_m/1000:.0f}km): {sent}/{len(rows)} gesendet.") + return sent