"""BAN YARO — Verlorener Hund Routes""" import os, uuid, math from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, UploadFile, File 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 media_utils import convert_media router = APIRouter() MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") # ------------------------------------------------------------------ # Haversine-Distanz in Metern # ------------------------------------------------------------------ def _haversine(lat1: float, lon1: float, lat2: float, lon2: float) -> float: R = 6_371_000 p1 = math.radians(lat1) p2 = math.radians(lat2) dp = math.radians(lat2 - lat1) dl = math.radians(lon2 - lon1) a = math.sin(dp / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2 return 2 * R * math.asin(math.sqrt(a)) # ------------------------------------------------------------------ # Schemas # ------------------------------------------------------------------ class LostDogCreate(BaseModel): name: str rasse: Optional[str] = None beschreibung: str lat: float lon: float dog_id: Optional[int] = None # ------------------------------------------------------------------ # GET /api/lost — aktive Meldungen (optional nach Distanz gefiltert) # ------------------------------------------------------------------ @router.get("") async def list_lost(lat: Optional[float] = None, lon: Optional[float] = None, radius_km: float = 25): with db() as conn: rows = conn.execute( """SELECT l.*, u.name AS melder_name FROM lost_dogs l LEFT JOIN users u ON u.id = l.user_id WHERE l.is_active = 1 ORDER BY l.created_at DESC""" ).fetchall() results = [] for r in rows: entry = dict(r) if lat is not None and lon is not None: dist = _haversine(lat, lon, entry["lat"], entry["lon"]) if dist > radius_km * 1000: continue entry["distanz_m"] = round(dist) results.append(entry) if lat is not None and lon is not None: results.sort(key=lambda x: x.get("distanz_m", 0)) return results # ------------------------------------------------------------------ # POST /api/lost — Hund vermisst melden (Login erforderlich) # ------------------------------------------------------------------ @router.post("", status_code=201) async def report_lost(data: LostDogCreate, user=Depends(get_current_user)): with db() as conn: conn.execute( """INSERT INTO lost_dogs (user_id, dog_id, name, rasse, beschreibung, lat, lon) VALUES (?, ?, ?, ?, ?, ?, ?)""", (user["id"], data.dog_id, data.name, data.rasse, data.beschreibung, data.lat, data.lon) ) row = conn.execute( "SELECT * FROM lost_dogs WHERE user_id=? ORDER BY id DESC LIMIT 1", (user["id"],) ).fetchone() entry = dict(row) send_push_to_all({ "type": "lost_dog_alert", "title": f"🔍 {data.name} wird vermisst!", "body": f"{data.rasse or 'Hund'} in deiner Nähe vermisst. Hilf bei der Suche!", "tag": f"lost-{entry['id']}", "data": {"page": "lost"}, }) return entry # ------------------------------------------------------------------ # POST /api/lost/{id}/foto — Foto hochladen (Login, eigene Meldung) # ------------------------------------------------------------------ @router.post("/{lost_id}/foto") async def upload_foto( lost_id: int, file: UploadFile = File(...), user=Depends(get_current_user), ): with db() as conn: entry = conn.execute( "SELECT id FROM lost_dogs WHERE id=? AND user_id=?", (lost_id, user["id"]) ).fetchone() if not entry: raise HTTPException(404, "Meldung nicht gefunden oder keine Berechtigung.") data, ext = convert_media(await file.read(), file.filename or "") if not ext: ext = ".jpg" filename = f"lost_{lost_id}_{uuid.uuid4().hex[:8]}{ext}" path = os.path.join(MEDIA_DIR, "lost", filename) os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, "wb") as f: f.write(data) foto_url = f"/media/lost/{filename}" with db() as conn: conn.execute("UPDATE lost_dogs SET foto_url=? WHERE id=?", (foto_url, lost_id)) return {"foto_url": foto_url} # ------------------------------------------------------------------ # POST /api/lost/{id}/found — als gefunden markieren (Login, eigene Meldung) # ------------------------------------------------------------------ @router.post("/{lost_id}/found") async def mark_found(lost_id: int, user=Depends(get_current_user)): with db() as conn: entry = conn.execute( "SELECT * FROM lost_dogs WHERE id=?", (lost_id,) ).fetchone() if not entry: raise HTTPException(404, "Meldung nicht gefunden.") e = dict(entry) if e["user_id"] != user["id"] and user.get("rolle") != "admin": raise HTTPException(403, "Keine Berechtigung.") conn.execute( """UPDATE lost_dogs SET is_active=0, gefunden_at=datetime('now') WHERE id=?""", (lost_id,) ) return {"ok": True} # ------------------------------------------------------------------ # DELETE /api/lost/{id} — eigene Meldung löschen (Login) # ------------------------------------------------------------------ @router.delete("/{lost_id}", status_code=204) async def delete_lost(lost_id: int, user=Depends(get_current_user)): with db() as conn: entry = conn.execute( "SELECT * FROM lost_dogs WHERE id=?", (lost_id,) ).fetchone() if not entry: raise HTTPException(404, "Meldung nicht gefunden.") e = dict(entry) if e["user_id"] != user["id"] and user.get("rolle") != "admin": raise HTTPException(403, "Keine Berechtigung.") conn.execute("DELETE FROM lost_dogs WHERE id=?", (lost_id,)) return None