from aiohttp.web import Application, AppRunner, Response, RouteTableDef, TCPSite from dependency_injector.containers import DeclarativeContainer from dependency_injector.resources import AsyncResource from itertools import chain from loguru import logger from pydantic import ConfigDict, PrivateAttr from typing import Self, Sequence from ctp_slack_bot.controllers.base import ControllerBase, Route from ctp_slack_bot.core import ApplicationComponentBase, Settings class HTTPServer(ApplicationComponentBase): model_config = ConfigDict(frozen=True) settings: Settings routes: Sequence[Route] _app_runner: AppRunner = PrivateAttr() _site: TCPSite = PrivateAttr() async def initialize(self: Self) -> None: app = Application() for route in self.routes: app.router.add_route(route.method, route.path, route.handler) logger.info("Registered HTTP route: {} {}", route.method, route.path) self._app_runner = AppRunner(app) await self._app_runner.setup() self._site = TCPSite(self._app_runner, self.settings.http_host, self.settings.http_port) async def start(self: Self) -> None: await self._site.start() async def stop(self: Self) -> None: await self._app_runner.cleanup() @property def name(self: Self) -> str: return "http_server" class HTTPServerResource(AsyncResource): async def init(self: Self, settings: Settings, controllers: Sequence[ControllerBase]) -> HTTPServer: routes = tuple(chain.from_iterable(controller.get_routes() for controller in controllers)) http_server = HTTPServer(settings=settings, routes=routes) await http_server.initialize() return http_server async def shutdown(self: Self, http_server: HTTPServer) -> None: await http_server.stop()