gitscope
← marzneshin / v0.3.0-alpha9 / app/marzneshin.py
https://dev.kronoz.win/r/marzneshin.git
app/marzneshin.py · Python
Blame Raw
  1import asyncio
  2import logging
  3from datetime import datetime as dt
  4from datetime import timedelta as td
  5
  6from apscheduler.schedulers.asyncio import AsyncIOScheduler
  7from fastapi import FastAPI, Request, status
  8from fastapi.encoders import jsonable_encoder
  9from fastapi.exceptions import RequestValidationError
 10from fastapi.middleware.cors import CORSMiddleware
 11from fastapi.responses import HTMLResponse, JSONResponse
 12from fastapi_pagination import add_pagination
 13from starlette.staticfiles import StaticFiles
 14from uvicorn import Config, Server
 15
 16import config
 17from app.templates import render_template
 18from config import (
 19    DEBUG,
 20    DOCS,
 21    HOME_PAGE_TEMPLATE,
 22    UVICORN_HOST,
 23    UVICORN_PORT,
 24    UVICORN_SSL_CERTFILE,
 25    UVICORN_SSL_KEYFILE,
 26    UVICORN_UDS,
 27    DASHBOARD_PATH,
 28)
 29
 30from . import __version__, telegram
 31from .routes import api_router
 32from .tasks import (
 33    delete_expired_reminders,
 34    nodes_startup,
 35    record_realtime_bandwidth,
 36    record_user_usages,
 37    reset_user_data_usage,
 38    review_users,
 39    send_notifications,
 40)
 41
 42logger = logging.getLogger(__name__)
 43
 44app = FastAPI(
 45    title="MarzneshinAPI",
 46    description="Unified GUI Censorship Resistant Solution Powered by Xray",
 47    version=__version__,
 48    docs_url="/docs" if DOCS else None,
 49    redoc_url="/redoc" if DOCS else None,
 50)
 51
 52app.include_router(api_router)
 53add_pagination(app)
 54
 55
 56@app.get("/", response_class=HTMLResponse)
 57def home_page():
 58    return render_template(HOME_PAGE_TEMPLATE)
 59
 60
 61app.add_middleware(
 62    CORSMiddleware,
 63    allow_origins=["*"],
 64    allow_credentials=True,
 65    allow_methods=["*"],
 66    allow_headers=["*"],
 67)
 68
 69scheduler = AsyncIOScheduler(timezone="UTC")
 70scheduler.add_job(record_user_usages, "interval", coalesce=True, seconds=30)
 71scheduler.add_job(review_users, "interval", seconds=600, coalesce=True, max_instances=1)
 72scheduler.add_job(reset_user_data_usage, "interval", coalesce=True, hours=1)
 73scheduler.add_job(
 74    record_realtime_bandwidth, "interval", seconds=1, coalesce=True, max_instances=1
 75)
 76
 77if config.WEBHOOK_ADDRESS:
 78    scheduler.add_job(send_notifications, "interval", seconds=30, replace_existing=True)
 79    scheduler.add_job(
 80        delete_expired_reminders,
 81        "interval",
 82        hours=2,
 83        start_date=dt.utcnow() + td(minutes=1),
 84    )
 85
 86
 87@app.on_event("startup")
 88async def on_start():
 89    await nodes_startup()
 90
 91
 92@app.on_event("shutdown")
 93async def on_shutdown():
 94    scheduler.shutdown()
 95    logger.info("Sending pending notifications before shutdown...")
 96    await send_notifications()
 97
 98
 99@app.exception_handler(RequestValidationError)
100def validation_exception_handler(request: Request, exc: RequestValidationError):
101    details = {}
102    for error in exc.errors():
103        details[error["loc"][-1]] = error.get("msg")
104    return JSONResponse(
105        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
106        content=jsonable_encoder({"detail": details}),
107    )
108
109
110async def main():
111    if not DEBUG:
112        app.mount(
113            DASHBOARD_PATH,
114            StaticFiles(directory="dashboard/dist", html=True),
115            name="dashboard",
116        )
117        app.mount(
118            "/static/",
119            StaticFiles(directory="dashboard/dist/static"),
120            name="static",
121        )
122        app.mount(
123            "/locales/",
124            StaticFiles(directory="dashboard/dist/locales"),
125            name="locales",
126        )
127    scheduler.start()
128    asyncio.create_task(telegram.start_bot())
129    cfg = Config(
130        app=app,
131        host=UVICORN_HOST,
132        port=UVICORN_PORT,
133        uds=(None if DEBUG else UVICORN_UDS),
134        ssl_certfile=UVICORN_SSL_CERTFILE,
135        ssl_keyfile=UVICORN_SSL_KEYFILE,
136        workers=1,
137        reload=DEBUG,
138        log_level=logging.DEBUG if DEBUG else logging.INFO,
139    )
140    server = Server(cfg)
141    await server.serve()