from apscheduler.executors.asyncio import AsyncIOExecutor from apscheduler.schedulers.asyncio import AsyncIOScheduler from contextlib import asynccontextmanager from datetime import datetime from fastapi import FastAPI, Request from fastapi.responses import Response, FileResponse from typing import Any import httpx import json import subprocess class PrettyJSONResponse(Response): media_type = "application/json" def render(self, content: Any) -> bytes: return json.dumps(content, indent=2).encode("utf-8") @asynccontextmanager async def lifespan(app: FastAPI): scheduler.start() yield scheduler.shutdown() app = FastAPI(lifespan=lifespan) @app.middleware("http") async def log_request(request: Request, call_next: Any): ts = datetime.now().strftime("%y%m%d%H%M%S%f") data = { "day": int(ts[:6]), "dt": int(ts[:-3]), "url": request.url, "query_params": request.query_params, "client": request.client.host, "method": request.method, "headers": dict(request.headers), } output = json.dumps( obj=data, default=str, indent=None, separators=(", ", ":"), ) with open("a.json", "a") as f: separator = "\n" if f.tell() else "" f.write(separator + output) response = await call_next(request) return response @app.get("/") def read_root(request: Request): return {"Hello": request.client.host} @app.get("/a", response_class=PrettyJSONResponse) def get_analytics(n: int = 5): if n == 0: cmd = "cat a.json" else: cmd = f"tail -{n} a.json" json_lines = subprocess.run(cmd.split(), capture_output=True).stdout content = json.loads(f"[{json_lines.replace(b"\n", b",").decode()}]") return content @app.api_route("/qa", response_class=FileResponse, methods=["GET", "HEAD"]) def query_analytics(): return "a.json" @app.get("/favicon.ico") async def favicon(): return {"message": "woof!"} @app.get("/ping") async def ping(): return {"message": "woof!"} async def self_ping(): async with httpx.AsyncClient() as client: _ = await client.get("http://0.0.0.0:7860/ping") scheduler = AsyncIOScheduler(executors={"default": AsyncIOExecutor()}) scheduler.add_job(self_ping, "interval", minutes=60)