"""Image proxy endpoint za HNS/HBS/external slike - rješava CORS + cache.""" import hashlib, os, time from fastapi import APIRouter, HTTPException, Response from fastapi.responses import StreamingResponse import requests, redis CACHE_DIR = "/var/cache/pgz-sport-img" os.makedirs(CACHE_DIR, exist_ok=True) ALLOWED_DOMAINS = ('hns.family', 'hns.hr', 'hbs.hr', 'hrvatski-bocarski-savez.hr', 'rk-zamet.hr', 'hvs.hr', 'rezultati.hvs.hr', 'sport-pgz.hr') MAX_AGE = 86400 * 7 # 7 dana try: rds = redis.Redis(host='localhost', port=6379, db=2) rds.ping() except: rds = None router = APIRouter() @router.get("/img-proxy") def proxy_image(u: str): if not u.startswith(('http://', 'https://')): raise HTTPException(400, "Invalid URL") if not any(d in u for d in ALLOWED_DOMAINS): raise HTTPException(403, "Domain not allowed") # Cache key h = hashlib.sha1(u.encode()).hexdigest() cf = os.path.join(CACHE_DIR, h) # Disk cache check if os.path.exists(cf) and (time.time() - os.path.getmtime(cf)) < MAX_AGE: with open(cf+'.ct') as f: ct = f.read().strip() with open(cf, 'rb') as f: data = f.read() return Response(content=data, media_type=ct, headers={ "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=604800", "X-Proxy-Cache": "HIT" }) # Fetch from origin try: r = requests.get(u, timeout=10, headers={"User-Agent": "RiNET-Civic/1.0"}) if r.status_code != 200: # Graceful fallback: return 1x1 transparent PNG (avoids cascading noise) import base64 TRANS_PNG = base64.b64decode("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=") return Response(content=TRANS_PNG, media_type="image/png", headers={ "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=3600", "X-Proxy-Cache": "ORIGIN_4XX", "X-Origin-Status": str(r.status_code), }) ct = r.headers.get('content-type', 'image/jpeg') # Save to cache with open(cf, 'wb') as f: f.write(r.content) with open(cf+'.ct', 'w') as f: f.write(ct) return Response(content=r.content, media_type=ct, headers={ "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=604800", "X-Proxy-Cache": "MISS" }) except requests.RequestException as e: # Network error: return 1x1 transparent PNG instead of 502 import base64 TRANS_PNG = base64.b64decode("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=") return Response(content=TRANS_PNG, media_type="image/png", headers={ "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=300", "X-Proxy-Cache": "ORIGIN_NET_ERROR", "X-Origin-Error": str(e)[:100], })