Spaces:
Sleeping
Sleeping
| """ | |
| SmartEyeSsen Backend - FastAPI Main Application | |
| ================================================ | |
| FastAPI ๋ฉ์ธ ์ ํ๋ฆฌ์ผ์ด์ ๋ฐ ๋ผ์ฐํฐ ์ค์ | |
| ์ฃผ์ ๊ธฐ๋ฅ: | |
| - FastAPI ์ฑ ์ด๊ธฐํ | |
| - CORS ์ค์ | |
| - ๋ผ์ฐํฐ ๋ฑ๋ก | |
| - ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๊ธฐํ | |
| - API ๋ฌธ์ํ | |
| """ | |
| import os | |
| from pathlib import Path | |
| from dotenv import load_dotenv | |
| from fastapi import Depends, FastAPI, HTTPException, status | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import JSONResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from sqlalchemy import text | |
| from sqlalchemy.orm import Session | |
| from .database import engine, get_db, init_db, test_connection | |
| from . import models | |
| from .routers import analysis, downloads, pages, projects | |
| from .services.model_registry import model_registry | |
| # ํ๊ฒฝ ๋ณ์ ๋ก๋ | |
| load_dotenv() | |
| # ํ๊ฒฝ ์ค์ (development | production) | |
| ENVIRONMENT = os.getenv("ENVIRONMENT", "development") | |
| UPLOAD_DIR = Path(os.getenv("UPLOAD_DIR", "uploads")).resolve() | |
| UPLOAD_DIR.mkdir(parents=True, exist_ok=True) | |
| # ============================================================================ | |
| # FastAPI ์ฑ ์ด๊ธฐํ | |
| # ============================================================================ | |
| app = FastAPI( | |
| title="SmartEyeSsen API", | |
| description=""" | |
| ## SmartEyeSsen Backend API | |
| ์๊ฐ์ฅ์ ํ์์ ์ํ AI ๊ธฐ๋ฐ ํ์ต ์๋ฃ ๋ถ์ ์์คํ | |
| ### ์ฃผ์ ๊ธฐ๋ฅ | |
| * ๐ **๋ค์ค ํ์ด์ง ๋ฌธ์ ์ฒ๋ฆฌ**: Worksheet ๋ฐ Document ์ ํ ์ง์ | |
| * ๐ค **AI ๋ ์ด์์ ๋ถ์**: DocLayout-YOLO ๊ธฐ๋ฐ ๋ ์ด์์ ๊ฐ์ง | |
| * ๐ **OCR ํ ์คํธ ์ถ์ถ**: Tesseract OCR ๊ธฐ๋ฐ ํ ์คํธ ์ธ์ | |
| * โ๏ธ **ํ ์คํธ ํธ์ง ๋ฐ ๋ฒ์ ๊ด๋ฆฌ**: TinyMCE ํธ์ง๊ธฐ ์ง์ | |
| * ๐ผ๏ธ **AI ์ค๋ช ์์ฑ**: GPT-4-turbo ๊ธฐ๋ฐ figure/table/flowchart ์ค๋ช | |
| * ๐ **๋ฌธ์ ๊ธฐ๋ฐ ์ ๋ ฌ**: Worksheet ์ ์ฉ ๋ฌธ์ ๋ฒํธ ๊ธฐ๋ฐ ์ ๋ ฌ | |
| * ๐ **์ขํ ๊ธฐ๋ฐ ์ ๋ ฌ**: Document ์ ์ฉ ์ขํ ๊ธฐ๋ฐ ์ ๋ ฌ | |
| * ๐ฅ **ํตํฉ ๋ฌธ์ ๋ค์ด๋ก๋**: DOCX ํ์ ์ง์ | |
| ### ๊ธฐ์ ์คํ | |
| * **Backend**: FastAPI + SQLAlchemy | |
| * **Database**: MySQL 8.0 | |
| * **AI Models**: DocLayout-YOLO, Tesseract OCR, GPT-4-turbo | |
| * **Document**: python-docx | |
| """, | |
| version="1.0.1", | |
| docs_url="/docs", | |
| redoc_url="/redoc", | |
| openapi_url="/openapi.json", | |
| ) | |
| # ============================================================================ | |
| # CORS ์ค์ (ํ๊ฒฝ๋ณ ๋ถ๋ฆฌ) | |
| # ============================================================================ | |
| if ENVIRONMENT == "production": | |
| # ํ๋ก๋์ : Vercel + HF Spaces ๋ฐฐํฌ ํ๊ฒฝ | |
| # ํ๊ฒฝ ๋ณ์๋ก CORS_ORIGINS ์ค์ ๊ฐ๋ฅ (์ผํ๋ก ๊ตฌ๋ถ) | |
| # ์: CORS_ORIGINS="https://yourapp.vercel.app,https://www.yourapp.com" | |
| CORS_ORIGINS_ENV = os.getenv("CORS_ORIGINS", "") | |
| if CORS_ORIGINS_ENV: | |
| CORS_ORIGINS = CORS_ORIGINS_ENV.split(",") | |
| else: | |
| # ๊ธฐ๋ณธ๊ฐ: ๋ชจ๋ ๋๋ฉ์ธ ํ์ฉ (์ด๊ธฐ ๋ฐฐํฌ/ํ ์คํธ์ฉ) | |
| # ๋ณด์ ๊ฐํ๊ฐ ํ์ํ๋ฉด ํ๊ฒฝ ๋ณ์๋ก ํน์ ๋๋ฉ์ธ ์ง์ | |
| CORS_ORIGINS = ["*"] | |
| CORS_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"] | |
| CORS_HEADERS = ["Content-Type", "Authorization", "X-Requested-With"] | |
| else: | |
| # ๊ฐ๋ฐ: ์ ์ฐํ CORS | |
| CORS_ORIGINS = os.getenv("CORS_ORIGINS", "http://localhost:5173,http://localhost:3000,http://localhost:8080,http://127.0.0.1:5173").split(",") | |
| CORS_METHODS = ["*"] | |
| CORS_HEADERS = ["*"] | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=CORS_ORIGINS, # ํ์ฉํ ์ถ์ฒ | |
| allow_credentials=True, | |
| allow_methods=CORS_METHODS, # ํ๊ฒฝ๋ณ ๋ฉ์๋ ์ ํ | |
| allow_headers=CORS_HEADERS, # ํ๊ฒฝ๋ณ ํค๋ ์ ํ | |
| ) | |
| # ์ ๋ก๋ ํ์ผ ์ ์ ์๋น (ํ๋ก ํธ์๋ ์ธ๋ค์ผ ํ์ ๋ฑ) | |
| app.mount( | |
| "/uploads", | |
| StaticFiles(directory=str(UPLOAD_DIR)), | |
| name="uploads", | |
| ) | |
| # ============================================================================ | |
| # ์์ ์ด๋ฒคํธ | |
| # ============================================================================ | |
| async def startup_event(): | |
| """ | |
| ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ ์คํ | |
| - ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํ ์คํธ | |
| - ํ ์ด๋ธ ์์ฑ (๊ฐ๋ฐ ํ๊ฒฝ) | |
| """ | |
| print("=" * 60) | |
| print("๐ SmartEyeSsen Backend Starting...") | |
| print("=" * 60) | |
| # ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํ ์คํธ | |
| if test_connection(): | |
| print("โ Database connection successful") | |
| else: | |
| print("โ Database connection failed") | |
| print("โ ๏ธ Please check your database configuration") | |
| # ํ ์ด๋ธ ์์ฑ (๊ฐ๋ฐ ํ๊ฒฝ์์๋ง) | |
| if os.getenv("ENVIRONMENT", "development") == "development": | |
| try: | |
| init_db() | |
| print("โ Database tables initialized") | |
| except Exception as e: | |
| print(f"โ ๏ธ Table initialization warning: {e}") | |
| preload_env = os.getenv("MODEL_PRELOAD", "SmartEyeSsen") | |
| preload_targets = [ | |
| name.strip() | |
| for name in preload_env.split(",") | |
| if name.strip() | |
| ] | |
| if preload_targets: | |
| try: | |
| model_registry.preload(preload_targets) | |
| print(f"๐ง Preloaded models: {', '.join(preload_targets)}") | |
| except Exception as e: | |
| print(f"โ ๏ธ Model preload failed: {e}") | |
| print("=" * 60) | |
| print("โ SmartEyeSsen Backend Ready!") | |
| print(f"๐ API Docs: http://localhost:{os.getenv('API_PORT', 8000)}/docs") | |
| print("=" * 60) | |
| async def shutdown_event(): | |
| """์ ํ๋ฆฌ์ผ์ด์ ์ข ๋ฃ ์ ์คํ""" | |
| print("\n" + "=" * 60) | |
| print("๐ SmartEyeSsen Backend Shutting down...") | |
| print("=" * 60) | |
| # ============================================================================ | |
| # ๋ฃจํธ ์๋ํฌ์ธํธ | |
| # ============================================================================ | |
| async def root(): | |
| """ | |
| ๋ฃจํธ ์๋ํฌ์ธํธ | |
| ์๋ฒ ์ํ ๋ฐ ๊ธฐ๋ณธ ์ ๋ณด ๋ฐํ | |
| """ | |
| return { | |
| "message": "Welcome to SmartEyeSsen API", | |
| "version": "1.0.1", | |
| "status": "running", | |
| "docs": "/docs", | |
| "redoc": "/redoc" | |
| } | |
| async def health_check(db: Session = Depends(get_db)): | |
| """ | |
| ํฌ์ค ์ฒดํฌ ์๋ํฌ์ธํธ | |
| ์๋ฒ ๋ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ํ ํ์ธ | |
| """ | |
| try: | |
| # ๊ฐ๋จํ ์ฟผ๋ฆฌ๋ก DB ์ฐ๊ฒฐ ํ์ธ | |
| db.execute(text("SELECT 1")) | |
| db_status = "connected" | |
| except Exception as e: | |
| db_status = f"error: {str(e)}" | |
| return { | |
| "status": "healthy", | |
| "database": db_status, | |
| "api_version": "1.0.0" | |
| } | |
| # ============================================================================ | |
| # ์์ธ ํธ๋ค๋ฌ | |
| # ============================================================================ | |
| async def http_exception_handler(request, exc): | |
| """HTTP ์์ธ ํธ๋ค๋ฌ""" | |
| return JSONResponse( | |
| status_code=exc.status_code, | |
| content={ | |
| "error": exc.detail, | |
| "status_code": exc.status_code | |
| } | |
| ) | |
| async def general_exception_handler(request, exc): | |
| """์ผ๋ฐ ์์ธ ํธ๋ค๋ฌ""" | |
| return JSONResponse( | |
| status_code=500, | |
| content={ | |
| "error": "Internal Server Error", | |
| "detail": str(exc), | |
| "status_code": 500 | |
| } | |
| ) | |
| # ========================================================================= | |
| # ๋ผ์ฐํฐ ๋ฑ๋ก | |
| # ========================================================================= | |
| app.include_router(projects.router) | |
| app.include_router(pages.router) | |
| app.include_router(analysis.router) | |
| app.include_router(downloads.router) | |
| # ============================================================================ | |
| # ๊ฐ๋ฐ ์๋ฒ ์คํ (์ง์ ์คํ ์) | |
| # ============================================================================ | |
| if __name__ == "__main__": | |
| import uvicorn | |
| HOST = os.getenv("API_HOST", "0.0.0.0") | |
| PORT = int(os.getenv("API_PORT", 8000)) | |
| RELOAD = os.getenv("API_RELOAD", "True").lower() == "true" | |
| uvicorn.run( | |
| "main:app", | |
| host=HOST, | |
| port=PORT, | |
| reload=RELOAD, | |
| log_level="info" | |
| ) | |