From 85475a49be01a7a92967f012fb0e4ea45395275d Mon Sep 17 00:00:00 2001 From: SantaSpeen Date: Sat, 27 Jul 2024 03:45:59 +0300 Subject: [PATCH] [-] WebAPI --- src/core/__init__.py | 6 +- src/core/core.py | 41 +------- src/modules/ConfigProvider/__init__.py | 11 +-- src/modules/WebAPISystem/__init__.py | 2 - src/modules/WebAPISystem/app.py | 105 --------------------- src/modules/WebAPISystem/models.py | 0 src/modules/WebAPISystem/utils.py | 126 ------------------------- src/modules/__init__.py | 7 +- 8 files changed, 12 insertions(+), 286 deletions(-) delete mode 100644 src/modules/WebAPISystem/__init__.py delete mode 100644 src/modules/WebAPISystem/app.py delete mode 100644 src/modules/WebAPISystem/models.py delete mode 100644 src/modules/WebAPISystem/utils.py diff --git a/src/core/__init__.py b/src/core/__init__.py index f49c091..23bc8e4 100644 --- a/src/core/__init__.py +++ b/src/core/__init__.py @@ -9,8 +9,8 @@ __title__ = 'KuiToi-Server' __description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.' __url__ = 'https://github.com/kuitoi/kuitoi-Server' -__version__ = '0.4.7' -__build__ = 2473 # Я это считаю лог файлами +__version__ = '0.4.8 (pre)' +__build__ = 2474 # Я это считаю лог файлами __author__ = 'SantaSpeen' __author_email__ = 'admin@kuitoi.su' __license__ = "FPA" @@ -91,7 +91,7 @@ if not config.Auth['private'] and not config.Auth['key']: text=i18n.GUI_enter_key_message, ok_text=i18n.GUI_ok, cancel_text=i18n.GUI_cancel).run() - config_provider.save() +config_provider.save() if not config.Auth['private'] and not config.Auth['key']: log.error(i18n.auth_empty_key) log.info(i18n.stop) diff --git a/src/core/core.py b/src/core/core.py index 7a7b2f0..885edd4 100644 --- a/src/core/core.py +++ b/src/core/core.py @@ -9,17 +9,14 @@ import math import os import random import time -from threading import Thread import aiohttp -import uvicorn from core import utils, __version__ from core.Client import Client from core.tcp_server import TCPServer from core.udp_server import UDPServer from modules import PluginsLoader -from modules.WebAPISystem import app as webapp # noinspection PyProtectedMember @@ -41,9 +38,6 @@ class Core: self.server_port = config.Server["server_port"] self.tcp = TCPServer self.udp = UDPServer - self.web_thread = None - self.web_pool = webapp.data_pool - self.web_stop = None self.lock_upload = False @@ -129,24 +123,6 @@ class Core: continue await client.kick("Server shutdown!") - @staticmethod - def start_web(): - uvconfig = uvicorn.Config("modules.WebAPISystem.app:web_app", - host=config.WebAPI["server_ip"], - port=config.WebAPI["server_port"], - loop="asyncio") - uvserver = uvicorn.Server(uvconfig) - webapp.uvserver = uvserver - uvserver.run() - - async def stop_me(self): - if not config.WebAPI['enabled']: - return - while webapp.data_run[0]: - await asyncio.sleep(1) - self.run = False - raise KeyboardInterrupt - # noinspection SpellCheckingInspection,PyPep8Naming async def heartbeat(self, test=False): try: @@ -282,17 +258,6 @@ class Core: lpl.load() try: - # WebApi Start - if config.WebAPI["enabled"]: - self.log.debug("Initializing WebAPI...") - web_thread = Thread(target=self.start_web, name="WebApiThread") - web_thread.start() - self.log.debug(f"WebAPI started at new thread: {web_thread.name}") - self.web_thread = web_thread - # noinspection PyProtectedMember - self.web_stop = webapp._stop - await asyncio.sleep(.3) - # Mods handler self.log.debug("Listing mods..") if not os.path.exists(self.mods_dir): @@ -313,8 +278,8 @@ class Core: for i in range(int(config.Game["players"] * 2.3)): # * 2.3 For down sock and buffer. self.clients.append(None) tasks = [] - # self.udp.start, - f_tasks = [self.tcp.start, self.udp._start, console.start, self.stop_me, self.heartbeat, self.check_alive] + # self.udp.start + f_tasks = [self.tcp.start, self.udp._start, console.start, self.heartbeat, self.check_alive] if config.RCON['enabled']: console.rcon.version = f"KuiToi {__version__}" rcon = console.rcon(config.RCON['password'], config.RCON['server_ip'], config.RCON['server_port']) @@ -353,8 +318,6 @@ class Core: ev.call_event("_lua_plugins_unload") await ev.call_async_event("_plugins_unload") self.run = False - if config.WebAPI["enabled"]: - asyncio.run(self.web_stop()) total_time = time.monotonic() - self.start_time hours = int(total_time // 3600) minutes = int((total_time % 3600) // 60) diff --git a/src/modules/ConfigProvider/__init__.py b/src/modules/ConfigProvider/__init__.py index 32b3ea6..86567f5 100644 --- a/src/modules/ConfigProvider/__init__.py +++ b/src/modules/ConfigProvider/__init__.py @@ -13,7 +13,7 @@ import yaml class Config: - def __init__(self, auth=None, game=None, server=None, rcon=None, options=None, web=None): + def __init__(self, auth=None, game=None, server=None, rcon=None, options=None): self.Auth = auth or {"key": None, "private": True} self.Game = game or {"map": "gridmap_v2", "players": 8, "cars": 1} self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!", "tags": "Freroam", @@ -22,12 +22,10 @@ class Config: "use_lua": False, "log_chat": True} self.RCON = rcon or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 10383, "password": secrets.token_hex(6)} - self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433, - "access_token": secrets.token_hex(16)} def __repr__(self): - return f"{self.__class__.__name__}(Auth={self.Auth!r}, Game={self.Game!r}, Server={self.Server!r}, " \ - f"RCON={self.RCON!r}, Options={self.Options!r}, WebAPI={self.WebAPI!r})" + return (f"{self.__class__.__name__}(Auth={self.Auth!r}, Game={self.Game!r}, Server={self.Server!r}, " + f"RCON={self.RCON!r}, Options={self.Options!r})") class ConfigProvider: @@ -51,7 +49,7 @@ class ConfigProvider: if _again: print("Error: empty configuration.") exit(1) - print("Empty config?..") + print("Reconfig: empty configuration.") os.remove(self.config_path) self.config = Config() return self.read(True) @@ -68,5 +66,6 @@ class ConfigProvider: del _config.enc del _config.Options['debug'] del _config.Options['encoding'] + os.remove(self.config_path) with open(self.config_path, "w", encoding="utf-8") as f: yaml.dump(_config, f) diff --git a/src/modules/WebAPISystem/__init__.py b/src/modules/WebAPISystem/__init__.py deleted file mode 100644 index 3130ee7..0000000 --- a/src/modules/WebAPISystem/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .app import web_app -from .app import _stop diff --git a/src/modules/WebAPISystem/app.py b/src/modules/WebAPISystem/app.py deleted file mode 100644 index f6bce89..0000000 --- a/src/modules/WebAPISystem/app.py +++ /dev/null @@ -1,105 +0,0 @@ -import asyncio -from asyncio import CancelledError - -import uvicorn -from fastapi import FastAPI, Request, HTTPException -from fastapi.exceptions import RequestValidationError -from starlette import status -from starlette.exceptions import HTTPException as StarletteHTTPException -from starlette.responses import JSONResponse - -import core.utils -from . import utils - -# from .models import SecretKey - -web_app = FastAPI() -log = core.utils.get_logger("web") - -uvserver = None -data_pool = [] -data_run = [True] - - -def response(data=None, code=status.HTTP_200_OK, error_code=0, error_message=None): - if 200 >= code <= 300: - return JSONResponse(content={"result": data, "error": None}, status_code=code) - return JSONResponse( - content={"error": {"code": error_code if error_code else code, "message": f"{error_message}"}, "result": None}, - status_code=code) - - -@web_app.get("/") -async def index(): - log.debug("Request IndexPage;") - return response("Index page") - - -@web_app.get("/method/{method}") -async def _method(method, secret_key: str = None): - # log.debug(f"Request method; kwargs: {kwargs}") - is_auth = secret_key == config.WebAPI["secret_key"] - spl = method.split(".") - if len(spl) != 2: - raise StarletteHTTPException(405) - api_class, api_method = spl - match api_class: - case "events": - match api_method, is_auth: - case "get", False: - return response(data_pool) - raise StarletteHTTPException(404) - - -async def _stop(): - await asyncio.sleep(1) - if uvserver is not None: - uvserver.should_exit = True - data_run[0] = False - - -@web_app.get("/stop") -async def stop(secret_key: str): - log.debug(f"Request stop; secret key: {secret_key}") - if secret_key == config.WebAPI["secret_key"]: - log.info("Stopping Web server") - asyncio.create_task(_stop()) - return response("Web server stopped") - - -@web_app.exception_handler(HTTPException) -async def default_exception_handler(request: Request, exc: HTTPException): - return response( - code=status.HTTP_500_INTERNAL_SERVER_ERROR, - error_code=exc.status_code, error_message=f"Internal Server Error: {exc.status_code}" - ) - - -@web_app.exception_handler(StarletteHTTPException) -async def http_exception_handler(request: Request, exc: StarletteHTTPException): - code = exc.status_code - if code == status.HTTP_405_METHOD_NOT_ALLOWED: - return response(code=code, error_message="Method Not Allowed") - if code == status.HTTP_404_NOT_FOUND: - return response(code=code, error_message="Method not Found") - return response(code=code, error_message="Unhandled error..") - - -@web_app.exception_handler(RequestValidationError) -async def request_validation_exception_handler(request: Request, exc: RequestValidationError): - code = status.HTTP_422_UNPROCESSABLE_ENTITY - return response(code=code, error_message="Request Validation Error") - - -utils.hack_fastapi() - -if __name__ == '__main__': - try: - uvconfig = uvicorn.Config(web_app, - host=config.WebAPI["server_ip"], - port=config.WebAPI["server_port"], - loop="asyncio") - uvserver = uvicorn.Server(uvconfig) - uvserver.run() - except KeyboardInterrupt or CancelledError: - pass diff --git a/src/modules/WebAPISystem/models.py b/src/modules/WebAPISystem/models.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/WebAPISystem/utils.py b/src/modules/WebAPISystem/utils.py deleted file mode 100644 index b2fb592..0000000 --- a/src/modules/WebAPISystem/utils.py +++ /dev/null @@ -1,126 +0,0 @@ -import asyncio -import sys - -import click -import uvicorn.server as uvs -from uvicorn.config import LOGGING_CONFIG - -from uvicorn.lifespan import on - -import core.utils - -# logger = core.utils.get_logger("uvicorn") -# uvs.logger = logger -logger = uvs.logger - - -def ev_log_started_message(self, listeners) -> None: - cfg = self.config - if cfg.fd is not None: - sock = listeners[0] - logger.info(i18n.web_start.format(sock.getsockname())) - elif cfg.uds is not None: - logger.info(i18n.web_start.format(cfg.uds)) - else: - addr_format = "%s://%s:%d" - host = "0.0.0.0" if cfg.host is None else cfg.host - if ":" in host: - addr_format = "%s://[%s]:%d" - port = cfg.port - if port == 0: - port = listeners[0].getsockname()[1] - protocol_name = "https" if cfg.ssl else "http" - message = i18n.web_start.format(addr_format) - color_message = (i18n.web_start.format(click.style(addr_format, bold=True))) - logger.info(message, protocol_name, host, port, extra={"color_message": color_message}) - - -async def ev_shutdown(self, sockets=None) -> None: - logger.debug("Shutting down") - for server in self.servers: - server.close() - for sock in sockets or []: - sock.close() - for server in self.servers: - await server.wait_closed() - for connection in list(self.server_state.connections): - connection.shutdown() - await asyncio.sleep(0.1) - try: - await asyncio.wait_for(self._wait_tasks_to_complete(), timeout=self.config.timeout_graceful_shutdown) - except asyncio.TimeoutError: - logger.error("Cancel %s running task(s), timeout graceful shutdown exceeded", len(self.server_state.tasks)) - for t in self.server_state.tasks: - if sys.version_info < (3, 9): - t.cancel() - else: - t.cancel(msg="Task cancelled, timeout graceful shutdown exceeded") - if not self.force_exit: - await self.lifespan.shutdown() - - -async def on_startup(self) -> None: - self.logger.debug("Waiting for application startup.") - loop = asyncio.get_event_loop() - main_lifespan_task = loop.create_task(self.main()) # noqa: F841 - startup_event = {"type": "lifespan.startup"} - await self.receive_queue.put(startup_event) - await self.startup_event.wait() - if self.startup_failed or (self.error_occured and self.config.lifespan == "on"): - self.logger.error("Application startup failed. Exiting.") - self.should_exit = True - else: - self.logger.debug("Application startup complete.") - - -async def on_shutdown(self) -> None: - if self.error_occured: - return - self.logger.debug("Waiting for application shutdown.") - shutdown_event = {"type": "lifespan.shutdown"} - await self.receive_queue.put(shutdown_event) - await self.shutdown_event.wait() - if self.shutdown_failed or (self.error_occured and self.config.lifespan == "on"): - self.logger.error("Application shutdown failed. Exiting.") - self.should_exit = True - else: - self.logger.debug("Application shutdown complete.") - - -def hack_fastapi(): - uvs.Server.shutdown = ev_shutdown - uvs.Server._log_started_message = ev_log_started_message - on.LifespanOn.startup = on_startup - on.LifespanOn.shutdown = on_shutdown - - LOGGING_CONFIG["formatters"]["default"]['fmt'] = core.utils.log_format - LOGGING_CONFIG["formatters"]["access"]["fmt"] = core.utils.log_format - LOGGING_CONFIG["formatters"].update({ - "file_default": { - "()": "logging.Formatter", - "fmt": core.utils.log_format - }, - "file_access": { - "()": "logging.Formatter", - "fmt": core.utils.log_format - } - }) - LOGGING_CONFIG["handlers"]["default"]['stream'] = "ext://sys.stdout" - LOGGING_CONFIG["handlers"].update({ - "file_default": { - "class": "logging.handlers.RotatingFileHandler", - "filename": "./logs/web.log", - "encoding": "utf-8", - "formatter": "file_default" - }, - "file_access": { - "class": "logging.handlers.RotatingFileHandler", - "filename": "./logs/web_access.log", - "encoding": "utf-8", - "formatter": "file_access" - } - }) - LOGGING_CONFIG["loggers"]["uvicorn"]["handlers"].append("file_default") - LOGGING_CONFIG["loggers"]["uvicorn.access"]["handlers"].append("file_access") - - diff --git a/src/modules/__init__.py b/src/modules/__init__.py index cce6df9..32f296f 100644 --- a/src/modules/__init__.py +++ b/src/modules/__init__.py @@ -1,16 +1,13 @@ # -*- coding: utf-8 -*- - # Developed by KuiToi Dev # File modules.__init__.py # Written by: SantaSpeen # Version 1.1 # Licence: FPA # (c) kuitoi.su 2023 -from .ConsoleSystem import Console from .ConfigProvider import ConfigProvider, Config -from .i18n import MultiLanguage +from .ConsoleSystem import Console from .EventsSystem import EventsSystem from .PluginsLoader import PluginsLoader -from .WebAPISystem import web_app -from .WebAPISystem import _stop as stop_web from .RateLimiter import RateLimiter +from .i18n import MultiLanguage