File size: 6,373 Bytes
7616a39
 
 
52f18dd
7616a39
 
 
 
c1b303d
7616a39
 
 
52f18dd
7616a39
c1b303d
 
7616a39
54c31ea
c1b303d
d3fed97
538360f
86992c4
c7aa14e
9929084
6f77b72
52f18dd
 
 
538360f
 
 
 
 
 
 
 
 
 
 
 
 
52f18dd
 
 
7616a39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52f18dd
7616a39
 
 
52f18dd
 
 
7616a39
 
 
 
 
 
 
 
 
52f18dd
7616a39
54c31ea
d3fed97
c1b303d
538360f
86992c4
c7aa14e
9929084
6f77b72
c7aa14e
52f18dd
7616a39
 
52f18dd
7616a39
 
 
 
 
52f18dd
 
 
7616a39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52f18dd
7616a39
 
 
 
 
 
4c94669
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7616a39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52f18dd
 
 
7616a39
 
5715faa
52f18dd
507fdbe
 
 
7616a39
 
5715faa
507fdbe
 
7616a39
5715faa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
"""
Main FastAPI application module.
"""
import os
import time
import logging
import json
from datetime import datetime
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from contextlib import asynccontextmanager
import warnings

from app.page_speed.config import settings
from app.page_speed.models import HealthResponse
from app.rag.routes import router as rag_router
from app.seo import routes as seo_routes
from app.page_speed import routes as page_speed_routes
from app.content_relevence import routes as content_relevance_routes
from app.keywords.routes import router as keywords_router
from app.uiux import routes as uiux_routes
from app.mobile_usability import routes as mobile_usability
from app.ads.persona_routes import router as persona_router

# ─────────────────────────────────────────────
# Suppress warnings
# ─────────────────────────────────────────────
warnings.filterwarnings(
    "ignore",
    message="Valid config keys have changed in V2:*",
    category=UserWarning,
    module="pydantic._internal._config",
)
warnings.filterwarnings("ignore", category=FutureWarning)
try:
    from langchain_core._api.deprecation import LangChainDeprecationWarning
    warnings.filterwarnings("ignore", category=LangChainDeprecationWarning)
except ImportError:
    pass

# ─────────────────────────────────────────────
# Logging setup
# ─────────────────────────────────────────────
logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter(
    "%(asctime)s | %(levelname)s | %(name)s | %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)
handler.setFormatter(formatter)
logger.addHandler(handler)

startup_time = None

@asynccontextmanager
async def lifespan(app: FastAPI):
    global startup_time
    startup_time = time.time()
    logger.info("πŸš€ Starting %s v%s", settings.app_name, settings.app_version)
    logger.info("πŸ“Š Server will run on %s:%s", settings.host, settings.port)
    yield
    logger.info("πŸ“Š Shutting down %s", settings.app_name)

# ─────────────────────────────────────────────
# FastAPI app creation
# ─────────────────────────────────────────────
app = FastAPI(
    title=settings.app_name,
    description=settings.app_description,
    version=settings.app_version,
    lifespan=lifespan,
    docs_url="/docs",
    redoc_url="/redoc"
)

# Include routers
app.include_router(rag_router)
app.include_router(seo_routes.router)
app.include_router(content_relevance_routes.router)
app.include_router(page_speed_routes.router)
app.include_router(keywords_router)
app.include_router(uiux_routes.router)
app.include_router(mobile_usability.router)
app.include_router(persona_router)


# CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # TODO: Restrict in production
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# ─────────────────────────────────────────────
# Routes
# ─────────────────────────────────────────────
@app.get("/", response_model=dict)
async def root():
    return {
        "message": f"Welcome to {settings.app_name}",
        "version": settings.app_version,
        "description": settings.app_description,
        "docs": "/docs",
        "health": "/health"
    }

@app.get("/health", response_model=HealthResponse)
async def health_check():
    if startup_time:
        uptime_seconds = time.time() - startup_time
        uptime_str = f"{uptime_seconds:.2f} seconds"
    else:
        uptime_str = "Unknown"

    return HealthResponse(
        status="healthy",
        version=settings.app_version,
        uptime=uptime_str
    )

# from app.rag.utils import cleanup_old_vectorstores

# @asynccontextmanager
# async def lifespan(app: FastAPI):
#     global startup_time
#     startup_time = time.time()
#     logger.info("πŸš€ Starting %s v%s", settings.app_name, settings.app_version)
#     logger.info("πŸ“Š Server will run on %s:%s", settings.host, settings.port)

#     # Trigger cleanup on startup
#     deleted = cleanup_old_vectorstores(days=7)
#     logger.info("🧹 Cleanup complete. %s old sessions removed.", deleted)

#     yield

#     logger.info("πŸ“Š Shutting down %s", settings.app_name)


@app.exception_handler(404)
async def not_found_handler(request, exc):
    logger.warning("404 Not Found: %s %s", request.method, request.url.path)
    return JSONResponse(
        status_code=404,
        content={
            "error": "Not Found",
            "message": "The requested endpoint was not found",
            "docs": "/docs"
        }
    )

@app.exception_handler(500)
async def internal_error_handler(request, exc):
    logger.error("500 Internal Server Error: %s %s -> %s", request.method, request.url.path, exc, exc_info=True)
    return JSONResponse(
        status_code=500,
        content={
            "error": "Internal Server Error",
            "message": "An unexpected error occurred",
            "timestamp": datetime.now().isoformat()
        }
    )

# ─────────────────────────────────────────────
# Entrypoint
# ─────────────────────────────────────────────
if __name__ == "__main__":
    import uvicorn
    import os

    # Default to 7860 (Hugging Face standard) instead of 8080
    port = int(os.environ.get("PORT", 7860))
    
    uvicorn.run(
        "app.main:app",
        host="0.0.0.0",
        port=port,
        reload=False  # optimizing for production
    )