Files
Task_Reporter/app/main.py
egg 44822a561a feat: Improve file display, timezone handling, and LOT management
Changes:
- Fix datetime serialization with UTC 'Z' suffix for correct timezone display
- Add PDF upload support with extension fallback for MIME detection
- Fix LOT add/remove by creating new list for SQLAlchemy JSON change detection
- Add file message components (FileMessage, ImageLightbox, UploadPreview)
- Add multi-file upload support with progress tracking
- Link uploaded files to chat messages via message_id
- Include file attachments in AI report generation
- Update specs for file-storage, realtime-messaging, and ai-report-generation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 12:39:15 +08:00

139 lines
4.4 KiB
Python

"""Main FastAPI application
生產線異常即時反應系統 (Task Reporter)
"""
import os
import json
from pathlib import Path
from datetime import datetime
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, JSONResponse
from app.core.config import get_settings
class UTCDateTimeEncoder(json.JSONEncoder):
"""Custom JSON encoder that formats datetime with 'Z' suffix for UTC"""
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat() + 'Z'
return super().default(obj)
class UTCJSONResponse(JSONResponse):
"""JSONResponse that uses UTCDateTimeEncoder"""
def render(self, content) -> bytes:
return json.dumps(
content,
ensure_ascii=False,
allow_nan=False,
indent=None,
separators=(",", ":"),
cls=UTCDateTimeEncoder,
).encode("utf-8")
from app.modules.auth import router as auth_router
from app.modules.auth.users_router import router as users_router
from app.modules.auth.middleware import auth_middleware
from app.modules.chat_room import router as chat_room_router
from app.modules.realtime import router as realtime_router
from app.modules.file_storage import router as file_storage_router
from app.modules.report_generation import router as report_generation_router
from app.modules.report_generation import health_router as report_health_router
# Frontend build directory
FRONTEND_DIR = Path(__file__).parent.parent / "frontend" / "dist"
settings = get_settings()
# Database tables are managed by Alembic migrations
# Run: alembic upgrade head
# Initialize FastAPI app with custom JSON response for UTC datetime
app = FastAPI(
title="Task Reporter API",
description="Production Line Incident Response System - 生產線異常即時反應系統",
version="1.0.0",
debug=settings.DEBUG,
default_response_class=UTCJSONResponse,
)
# CORS middleware - origins configured via CORS_ORIGINS environment variable
app.add_middleware(
CORSMiddleware,
allow_origins=settings.get_cors_origins(),
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Authentication middleware (applies to all /api routes except login/logout)
app.middleware("http")(auth_middleware)
# Include routers
app.include_router(auth_router)
app.include_router(users_router)
app.include_router(chat_room_router)
app.include_router(realtime_router)
app.include_router(file_storage_router)
app.include_router(report_generation_router)
app.include_router(report_health_router)
@app.on_event("startup")
async def startup_event():
"""Initialize application on startup"""
from app.core.minio_client import initialize_bucket
import logging
logger = logging.getLogger(__name__)
# Initialize MinIO bucket
try:
if initialize_bucket():
logger.info("MinIO bucket initialized successfully")
else:
logger.warning("MinIO bucket initialization failed - file uploads may not work")
except Exception as e:
logger.warning(f"MinIO connection failed: {e} - file uploads will be unavailable")
# Check DIFY API Key configuration
if not settings.DIFY_API_KEY:
logger.warning("DIFY_API_KEY not configured - AI report generation will be unavailable")
@app.get("/api/health")
async def health_check():
"""Health check for monitoring"""
return {
"status": "healthy",
"service": "Task Reporter API",
"version": "1.0.0",
}
# Serve frontend static files (only if build exists)
if FRONTEND_DIR.exists():
# Mount static assets (JS, CSS, images)
app.mount("/assets", StaticFiles(directory=FRONTEND_DIR / "assets"), name="static")
@app.get("/{full_path:path}")
async def serve_spa(full_path: str):
"""Serve the React SPA for all non-API routes"""
# Try to serve the exact file if it exists
file_path = FRONTEND_DIR / full_path
if file_path.exists() and file_path.is_file():
return FileResponse(file_path)
# Otherwise serve index.html for client-side routing
return FileResponse(FRONTEND_DIR / "index.html")
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app.main:app", host=settings.HOST, port=settings.PORT, reload=settings.DEBUG, log_level="info"
)