class FastapiProvider(BaseProvider, protocol=constants.Protocol.HTTP): def __init__( self, cafile: str, app_name: str = '', ): super().__init__(cafile) self.app = FastAPI() self.policy_map: Dict[str, RegistrationRule] = {} def add_provided_service(self, rule: RegistrationRule, ) -> None: self.policy_map[rule.service_uri] = rule if rule.protocol == constants.Protocol.HTTP: self.app.add_api_route( path=f'/{rule.service_uri.lstrip("/")}', endpoint=rule.func, methods=[rule.method], ) elif rule.protocol == constants.Protocol.WS: self.app.add_api_websocket_route( path=f'/{rule.service_uri}', endpoint=rule.func, ) def run_forever( self, address: str, port: int, keyfile: str, certfile: str, ): # TODO: Should http requests be redirected to https requests automatically in secure mode? # self.app.add_middleware(HTTPSRedirectMiddleware) self.app.add_middleware(ArrowheadAccessPolicyMiddleware, policy_map=self.policy_map) uvicorn.run( self.app, host=address, port=port, ssl_keyfile=keyfile, ssl_certfile=certfile, ssl_ca_certs=self.cafile, ) def add_startup_routine(self, func: Callable): self.app.add_event_handler('startup', func) def add_shutdown_routine(self, func: Callable): self.app.add_event_handler('shutdown', func)
@app.get("/settings/", response_model=UpdateSettings) async def get_settings(): return UpdateSettings(settings=settings.serialize()) app.include_router(routers.frontend.router, tags=["Frontend"]) app.include_router(routers.sources.router, tags=["Sources"]) app.include_router(routers.nodes.router, tags=["Nodes"]) if not settings.DISABLE_PROFILE_REPORTS: app.include_router(routers.profile_reports.router, tags=["Profile Reports"]) if settings.ENABLE_WEBSOCKET_CONNECTIONS: app.add_api_websocket_route(websocket_path, websocket_endpoint) def run(): if not settings.DISABLE_OPEN_BROWSER: launch_browser_opener(f"http://{settings.HOST}:{settings.PORT}") dtale_app.run() uvicorn.run( app, host=socket.gethostbyname(settings.HOST), port=settings.PORT, debug=True, ) if __name__ == "__main__": run()
class Driver(ReverseDriver): """FastAPI 驱动框架。""" def __init__(self, env: Env, config: NoneBotConfig): super(Driver, self).__init__(env, config) self.fastapi_config: Config = Config(**config.dict()) self._server_app = FastAPI( openapi_url=self.fastapi_config.fastapi_openapi_url, docs_url=self.fastapi_config.fastapi_docs_url, redoc_url=self.fastapi_config.fastapi_redoc_url, ) @property @overrides(ReverseDriver) def type(self) -> str: """驱动名称: `fastapi`""" return "fastapi" @property @overrides(ReverseDriver) def server_app(self) -> FastAPI: """`FastAPI APP` 对象""" return self._server_app @property @overrides(ReverseDriver) def asgi(self) -> FastAPI: """`FastAPI APP` 对象""" return self._server_app @property @overrides(ReverseDriver) def logger(self) -> logging.Logger: """fastapi 使用的 logger""" return logging.getLogger("fastapi") @overrides(ReverseDriver) def setup_http_server(self, setup: HTTPServerSetup): async def _handle(request: Request) -> Response: return await self._handle_http(request, setup) self._server_app.add_api_route( setup.path.path, _handle, name=setup.name, methods=[setup.method], include_in_schema=self.fastapi_config. fastapi_include_adapter_schema, ) @overrides(ReverseDriver) def setup_websocket_server(self, setup: WebSocketServerSetup) -> None: async def _handle(websocket: WebSocket) -> None: await self._handle_ws(websocket, setup) self._server_app.add_api_websocket_route( setup.path.path, _handle, name=setup.name, ) @overrides(ReverseDriver) def on_startup(self, func: Callable) -> Callable: """参考文档: `Events <https://fastapi.tiangolo.com/advanced/events/#startup-event>`_""" return self.server_app.on_event("startup")(func) @overrides(ReverseDriver) def on_shutdown(self, func: Callable) -> Callable: """参考文档: `Events <https://fastapi.tiangolo.com/advanced/events/#shutdown-event>`_""" return self.server_app.on_event("shutdown")(func) @overrides(ReverseDriver) def run( self, host: Optional[str] = None, port: Optional[int] = None, *, app: Optional[str] = None, **kwargs, ): """使用 `uvicorn` 启动 FastAPI""" super().run(host, port, app, **kwargs) LOGGING_CONFIG = { "version": 1, "disable_existing_loggers": False, "handlers": { "default": { "class": "nonebot.log.LoguruHandler", }, }, "loggers": { "uvicorn.error": { "handlers": ["default"], "level": "INFO" }, "uvicorn.access": { "handlers": ["default"], "level": "INFO", }, }, } uvicorn.run( app or self.server_app, # type: ignore host=host or str(self.config.host), port=port or self.config.port, reload=self.fastapi_config.fastapi_reload, reload_dirs=self.fastapi_config.fastapi_reload_dirs, reload_delay=self.fastapi_config.fastapi_reload_delay, reload_includes=self.fastapi_config.fastapi_reload_includes, reload_excludes=self.fastapi_config.fastapi_reload_excludes, log_config=LOGGING_CONFIG, **kwargs, ) async def _handle_http( self, request: Request, setup: HTTPServerSetup, ) -> Response: json: Any = None try: json = await request.json() except Exception: pass data: Optional[dict] = None files: Optional[List[Tuple[str, FileTypes]]] = None try: form = await request.form() data = {} files = [] for key, value in form.multi_items(): if isinstance(value, UploadFile): files.append((key, (value.filename, value.file, value.content_type))) else: data[key] = value except Exception: pass http_request = BaseRequest( request.method, str(request.url), headers=request.headers.items(), cookies=request.cookies, content=await request.body(), data=data, json=json, files=files, version=request.scope["http_version"], ) response = await setup.handle_func(http_request) return Response(response.content, response.status_code, dict(response.headers)) async def _handle_ws(self, websocket: WebSocket, setup: WebSocketServerSetup): request = BaseRequest( "GET", str(websocket.url), headers=websocket.headers.items(), cookies=websocket.cookies, version=websocket.scope.get("http_version", "1.1"), ) ws = FastAPIWebSocket( request=request, websocket=websocket, ) await setup.handle_func(ws)