-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun.py
More file actions
120 lines (95 loc) · 4.09 KB
/
Copy pathrun.py
File metadata and controls
120 lines (95 loc) · 4.09 KB
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
import logging
from datetime import datetime
from fastapi import FastAPI, BackgroundTasks
import uvicorn
from contextlib import asynccontextmanager
from tenacity import AsyncRetrying, RetryError, stop_after_attempt, wait_fixed
from config import Config, LOCALHOST_ADDRESSES
from main.utils.connectivity import checkDatabaseConnection, checkServiceConnection
from main.utils.service_manager import runAll
from main.utils.migrator import runMigrations
from main.utils.request_id import RequestIDMiddleware
from main.utils.errors import registerErrorHandlers
from main.service.authentication_service import AuthenticationService
from main.service.user_service import UserService
from main.service.prometheus_service import PrometheusService
from main.service.scraper_service import ScraperService, runScraper
from main.service.stocksapi_service import StocksAPIService
logger = logging.getLogger(__name__)
appStartTime = datetime.now()
@asynccontextmanager
async def lifespan(app: FastAPI):
dbConnected = False
try:
async for attempt in AsyncRetrying(stop=stop_after_attempt(10), wait=wait_fixed(3)):
with attempt:
dbResults = checkDatabaseConnection()
if all(r["status"] == "connected" for r in dbResults.values()):
dbConnected = True
else:
logger.info(f"Retrying database connection ({attempt.retry_state.attempt_number}/10)")
raise ConnectionError("database not ready")
if dbConnected:
break
except RetryError:
logger.error("Database connection failed after retries.")
else:
runMigrations()
services = [
("USER", Config.USER, lambda port: (AuthenticationService.initialize(port), UserService.initialize(port))),
("STOCKS_API", Config.STOCKS_API, StocksAPIService.initialize),
("PROMETHEUS", Config.PROMETHEUS, PrometheusService.initialize),
]
for name, config, init in services:
if config.ENABLED:
if config.HOST in LOCALHOST_ADDRESSES:
init(config.PORT)
elif not checkServiceConnection(name):
logger.error(f"Remote connection to the {name} Service failed")
if Config.SCRAPER.ENABLED:
ScraperService.initialize()
runAll()
logger.info("All services initialized!")
yield
app = FastAPI(title="Mansa Server", lifespan=lifespan)
app.add_middleware(RequestIDMiddleware)
registerErrorHandlers(app)
@app.get("/health")
async def health():
return {"status": "ok", "message": "Mansa Server is running"}
@app.get("/status")
async def status():
uptime = datetime.now() - appStartTime
hours, remainder = divmod(int(uptime.total_seconds()), 3600)
minutes, seconds = divmod(remainder, 60)
days, hours = divmod(hours, 24)
databases = checkDatabaseConnection()
services = {}
for name, config in [
("user", Config.USER),
("stocks_api", Config.STOCKS_API),
("prometheus", Config.PROMETHEUS),
]:
if not config.ENABLED:
services[name] = {"status": "disabled"}
continue
isLocal = config.HOST in LOCALHOST_ADDRESSES
services[name] = {"status": "running", "port": config.PORT, "type": "local" if isLocal else "remote"}
if not isLocal:
services[name]["host"] = config.HOST
if Config.SCRAPER.ENABLED:
services["scraper"] = {"status": "running", "type": "local"}
return {
"status": "healthy" if all(r["status"] == "connected" for r in databases.values()) else "degraded",
"uptime": f"{days}d {hours}h {minutes}m {seconds}s",
"databases": databases,
"services": services,
}
@app.post("/scraper/run")
async def triggerScraper(background_tasks: BackgroundTasks):
if not Config.DEBUG_MODE:
return {"status": "error", "message": "Scraper trigger is only available in debug mode"}
background_tasks.add_task(runScraper)
return {"status": "ok", "message": "Scraper triggered in background (debug mode only)"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)