# Copyright (C) 2020, 2023 by Thomas Lindner # Copyright (C) 2020 by Cathy Hu # Copyright (C) 2020 by Martin Rey # # SPDX-License-Identifier: 0BSD """Entrypoint of Kibicara.""" from argparse import ArgumentParser from asyncio import run as asyncio_run from logging import DEBUG, INFO, WARNING, basicConfig, getLogger from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from hypercorn.asyncio import serve from hypercorn.config import Config from pytoml import load from tortoise import Tortoise from kibicara.config import config from kibicara.platformapi import Spawner from kibicara.webapi import router logger = getLogger(__name__) class Main: """Entrypoint for Kibicara. Initializes the platform bots and starts the hypercorn webserver serving the Kibicara application and the specified frontend on port 8000. """ def __init__(self): parser = ArgumentParser() parser.add_argument( "-f", "--config", dest="configfile", default="/etc/kibicara.conf", help="path to config file", ) parser.add_argument( "-v", "--verbose", action="count", help="Raise verbosity level", ) args = parser.parse_args() try: with open(args.configfile) as configfile: config.update(load(configfile)) except FileNotFoundError: # run with default config pass LOGLEVELS = { None: WARNING, 1: INFO, 2: DEBUG, } basicConfig( level=LOGLEVELS.get(args.verbose, DEBUG), format="%(asctime)s %(name)s %(message)s", ) getLogger("aiosqlite").setLevel(WARNING) asyncio_run(self.__run()) async def __run(self): await Tortoise.init( db_url=config["database_connection"], modules={ "models": [ "kibicara.model", "kibicara.platforms.email.model", "kibicara.platforms.telegram.model", "kibicara.platforms.test.model", "kibicara.platforms.twitter.model", ] }, ) await Tortoise.generate_schemas() await Spawner.init_all() await self.__start_webserver() await Tortoise.close_connections() async def __start_webserver(self): class SinglePageApplication(StaticFiles): async def get_response(self, path, scope): response = await super().get_response(path, scope) if response.status_code == 404: response = await super().get_response(".", scope) return response app = FastAPI() server_config = Config() server_config.accesslog = "-" server_config.behind_proxy = config["behind_proxy"] server_config.keyfile = config["keyfile"] server_config.certfile = config["certfile"] if config["production"]: server_config.bind = ["0.0.0.0:8000", "[::]:8000"] api = FastAPI() api.include_router(router) app.mount("/api", api) if not config["production"] and config["cors_allow_origin"]: app.add_middleware( CORSMiddleware, allow_origins=config["cors_allow_origin"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) if config["frontend_path"] is not None: app.mount( "/", app=SinglePageApplication(directory=config["frontend_path"], html=True), ) await serve(app, server_config)