Feature+Fix: Referral-Admin, Pro-Gates, Karten-Layer, onDogChange, Staging-Media (SW by-v855)
Features: - Admin: Referral-Tab (Virality Factor, Top-Werber, letzte Einladungen) - Karte: Regenradar (RainViewer, zoom→7, color=4), Temperatur-Layer (OWM) mit Zahlen-Grid + Legende - Wetter-Chip: Umschwung-Warnung bei ≥40%-Sprung in Niederschlagswahrscheinlichkeit - Freundschaftsanfragen: Accept/Decline direkt in Notifications (kein Pro nötig) - Freunde-Seite für Standard-User freigeschaltet Pro-Gates: - KI-Trainer, Routenvorschläge, Regenradar, Temperatur-Layer jetzt Pro-Feature - Pro-Badge (P) auf Chips für Admins/Mods in allen Welten + Welten-einrichten - Oranger Banner auf Pro-Seiten für Admin/Mod/Manager Bugfixes: - onDogChange: uebungen.js (Cache leeren + _render), trainingsplaene.js (war leer) - robots.txt vereinfacht (nur Disallow, kein Allow-Durcheinander) - Hintergrund-Foto: Querformat-Filter korrigiert (kein Fallback auf Hochformat) - Staging Media: FileResponse mit korrektem MIME-Type, no-cache statt immutable - Staging Docker: MEDIA_DIR=/data/media + /prod-media:ro Fallback-Handler - Staging-Fix: Bild-Upload auf zweitem Hund (war Read-only file system)
This commit is contained in:
parent
2f021f54c2
commit
79fa5684b9
22 changed files with 570 additions and 58 deletions
|
|
@ -179,7 +179,10 @@ class MediaCacheMiddleware(BaseHTTPMiddleware):
|
|||
async def dispatch(self, request: Request, call_next):
|
||||
response = await call_next(request)
|
||||
if request.url.path.startswith('/media/'):
|
||||
response.headers['Cache-Control'] = 'public, max-age=31536000, immutable'
|
||||
if os.getenv('STAGING') == 'true':
|
||||
response.headers['Cache-Control'] = 'no-cache'
|
||||
else:
|
||||
response.headers['Cache-Control'] = 'public, max-age=31536000, immutable'
|
||||
return response
|
||||
|
||||
app.add_middleware(MediaCacheMiddleware)
|
||||
|
|
@ -341,9 +344,39 @@ app.mount("/img", StaticFiles(directory=f"{STATIC_DIR}/img"), name="img")
|
|||
# User-generierte Medien (Fotos aus Tagebuch, Giftköder-Alarm, etc.)
|
||||
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 = "834" # muss mit APP_VER in app.js übereinstimmen
|
||||
STAGING = os.getenv("STAGING", "false").lower() == "true"
|
||||
PROD_MEDIA_DIR = "/prod-media"
|
||||
|
||||
_MIME_MAP = {
|
||||
".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".png": "image/png",
|
||||
".webp": "image/webp", ".gif": "image/gif", ".mp4": "video/mp4",
|
||||
".webm": "video/webm", ".pdf": "application/pdf",
|
||||
}
|
||||
|
||||
if STAGING and os.path.isdir(PROD_MEDIA_DIR):
|
||||
# Staging: eigene Uploads in MEDIA_DIR, Fallback auf Prod-Medien (read-only)
|
||||
from fastapi.responses import FileResponse as _FileResponse
|
||||
|
||||
def _media_response(filepath: str):
|
||||
ext = os.path.splitext(filepath)[1].lower()
|
||||
mt = _MIME_MAP.get(ext, "application/octet-stream")
|
||||
return _FileResponse(filepath, media_type=mt)
|
||||
|
||||
@app.api_route("/media/{path:path}", methods=["GET", "HEAD"])
|
||||
async def serve_media_staging(path: str):
|
||||
staging_file = os.path.join(MEDIA_DIR, path)
|
||||
if os.path.isfile(staging_file):
|
||||
return _media_response(staging_file)
|
||||
prod_file = os.path.join(PROD_MEDIA_DIR, path)
|
||||
if os.path.isfile(prod_file):
|
||||
return _media_response(prod_file)
|
||||
from fastapi import HTTPException as _HE
|
||||
raise _HE(404, "Media not found")
|
||||
else:
|
||||
app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media")
|
||||
|
||||
APP_VER = "855" # muss mit APP_VER in app.js übereinstimmen
|
||||
|
||||
@app.get("/.well-known/assetlinks.json")
|
||||
async def assetlinks():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue