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