Spaces:
Paused
Paused
Download app/server/middleware.py from superxu520/G_AI: direct link, hf CLI and curl.
- Browser
- Download file 4.98 kB
-
https://huggingface.co/spaces/superxu520/G_AI/resolve/main/app/server/middleware.py
- Command line
-
hf download hf://spaces/superxu520/G_AI/app/server/middleware.py
-
curl -L -o middleware.py https://huggingface.co/spaces/superxu520/G_AI/resolve/main/app/server/middleware.py
4.98 kB
| import asyncio | |
| import hashlib | |
| import hmac | |
| import os | |
| import tempfile | |
| import time | |
| from pathlib import Path | |
| from fastapi import Depends, FastAPI, HTTPException, Request, status | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import ORJSONResponse | |
| from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer | |
| from loguru import logger | |
| from ..utils import g_config | |
| # Persistent directory for storing generated images | |
| # Support environment variable override for Docker/HF deployments | |
| # Directory will be created on first access to avoid import-time failures | |
| _IMAGE_STORE_DIR: Path | None = None | |
| def get_image_store_dir() -> Path: | |
| """Returns a persistent directory for storing images. Creates it if it doesn't exist.""" | |
| global _IMAGE_STORE_DIR | |
| if _IMAGE_STORE_DIR is None: | |
| _IMAGE_STORE_DIR = Path(os.getenv("GEMINI_IMAGE_STORE_PATH", tempfile.gettempdir())) / "ai_generated_images" | |
| try: | |
| _IMAGE_STORE_DIR.mkdir(parents=True, exist_ok=True) | |
| except OSError as e: | |
| logger.error(f"Failed to create image store directory at {_IMAGE_STORE_DIR}: {e}") | |
| logger.warning("Falling back to system temp directory") | |
| _IMAGE_STORE_DIR = Path(tempfile.gettempdir()) / "ai_generated_images" | |
| _IMAGE_STORE_DIR.mkdir(parents=True, exist_ok=True) | |
| return _IMAGE_STORE_DIR | |
| def get_image_token(filename: str) -> str: | |
| """Generate a HMAC-SHA256 token for a filename using the API key.""" | |
| secret = g_config.server.api_key | |
| if not secret: | |
| return "" | |
| msg = filename.encode("utf-8") | |
| secret_bytes = secret.encode("utf-8") | |
| return hmac.new(secret_bytes, msg, hashlib.sha256).hexdigest() | |
| def verify_image_token(filename: str, token: str | None) -> bool: | |
| """Verify the provided token against the filename.""" | |
| expected = get_image_token(filename) | |
| if not expected: | |
| return True # No auth required | |
| if not token: | |
| return False | |
| return hmac.compare_digest(token, expected) | |
| def cleanup_expired_images(retention_days: int) -> int: | |
| """Delete images in image store directory older than retention_days.""" | |
| if retention_days <= 0: | |
| return 0 | |
| image_store_dir = get_image_store_dir() | |
| now = time.time() | |
| retention_seconds = retention_days * 24 * 60 * 60 | |
| cutoff = now - retention_seconds | |
| count = 0 | |
| try: | |
| for file_path in image_store_dir.iterdir(): | |
| if not file_path.is_file(): | |
| continue | |
| try: | |
| if file_path.stat().st_mtime < cutoff: | |
| file_path.unlink() | |
| count += 1 | |
| except Exception as e: | |
| logger.warning(f"Failed to delete expired image {file_path}: {e}") | |
| except FileNotFoundError: | |
| logger.debug(f"Image store directory does not exist yet: {image_store_dir}") | |
| except Exception as e: | |
| logger.warning(f"Failed to cleanup expired images: {e}") | |
| if count > 0: | |
| logger.info(f"Cleaned up {count} expired images.") | |
| return count | |
| def global_exception_handler(request: Request, exc: Exception): | |
| if isinstance(exc, HTTPException): | |
| return ORJSONResponse( | |
| status_code=exc.status_code, | |
| content={"error": {"message": exc.detail}}, | |
| ) | |
| return ORJSONResponse( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| content={"error": {"message": str(exc)}}, | |
| ) | |
| async def get_temp_dir(): | |
| """ | |
| Async context manager for temporary directory. | |
| Ensures cleanup even if request handling fails. | |
| """ | |
| temp_dir = tempfile.TemporaryDirectory() | |
| try: | |
| yield Path(temp_dir.name) | |
| finally: | |
| # Run cleanup in thread pool to avoid blocking (Python 3.9+) | |
| try: | |
| await asyncio.to_thread(temp_dir.cleanup) | |
| except AttributeError: | |
| # Fallback for Python < 3.9 | |
| await asyncio.get_running_loop().run_in_executor(None, temp_dir.cleanup) | |
| def verify_api_key( | |
| credentials: HTTPAuthorizationCredentials = Depends(HTTPBearer(auto_error=False)), | |
| ): | |
| if not g_config.server.api_key: | |
| return "" | |
| if credentials is None or credentials.scheme.lower() != "bearer": | |
| raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail="Invalid or missing token") | |
| api_key = credentials.credentials | |
| if api_key != g_config.server.api_key: | |
| raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Wrong API key") | |
| return api_key | |
| def add_exception_handler(app: FastAPI): | |
| app.add_exception_handler(Exception, global_exception_handler) | |
| def add_cors_middleware(app: FastAPI): | |
| if g_config.cors.enabled: | |
| cors = g_config.cors | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=cors.allow_origins, | |
| allow_credentials=cors.allow_credentials, | |
| allow_methods=cors.allow_methods, | |
| allow_headers=cors.allow_headers, | |
| ) | |