mirror of
https://github.com/kuitoi/kuitoi-Server.git
synced 2025-08-17 16:25:36 +00:00
Compare commits
3 Commits
9b00552912
...
3701bc441c
Author | SHA1 | Date | |
---|---|---|---|
3701bc441c | |||
90da1d4cea | |||
c428479110 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -136,3 +136,4 @@ dmypy.json
|
|||||||
/src/mods
|
/src/mods
|
||||||
/src/plugins
|
/src/plugins
|
||||||
/test/
|
/test/
|
||||||
|
*test.py
|
||||||
|
20
README.md
20
README.md
@ -25,15 +25,23 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
|
|||||||
- [ ] Players synchronizations
|
- [ ] Players synchronizations
|
||||||
- [ ] Ping
|
- [ ] Ping
|
||||||
- [ ] Player counter
|
- [ ] Player counter
|
||||||
- [x] Additional:
|
- [x] Additional:
|
||||||
|
- [x] Console:
|
||||||
|
- [x] Tabulation
|
||||||
|
- [ ] _(Deferred)_ Static text
|
||||||
- [x] Events System
|
- [x] Events System
|
||||||
|
- [x] Call events
|
||||||
|
- [x] Create custom events
|
||||||
|
- [ ] Return from events
|
||||||
- [x] Plugins support
|
- [x] Plugins support
|
||||||
- [x] MultiLanguage (i18n support)
|
- [x] MultiLanguage (i18n support)
|
||||||
- [ ] HTTP REST API Server
|
- [x] Core
|
||||||
- [ ] Console:
|
- [x] Console
|
||||||
- [x] Tabulation
|
- [x] WebAPI
|
||||||
- [ ] _(Deferred)_ Normal text scroll
|
- [x] HTTP API Server (fastapi)
|
||||||
- [x] MultiLanguage (i18n support)
|
- [x] Stop and Start with core
|
||||||
|
- [x] Custom logger
|
||||||
|
- [ ] Sync with event system
|
||||||
- [ ] [Documentation](docs/en/readme.md)
|
- [ ] [Documentation](docs/en/readme.md)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
PyYAML~=6.0
|
PyYAML~=6.0
|
||||||
prompt-toolkit~=3.0.38
|
prompt-toolkit~=3.0.38
|
||||||
aiohttp~=3.8.4
|
aiohttp~=3.8.4
|
||||||
|
uvicorn~=0.22.0
|
||||||
|
fastapi~=0.100.0
|
||||||
|
starlette~=0.27.0
|
||||||
|
pydantic~=2.0.2
|
||||||
|
click~=8.1.4
|
@ -16,13 +16,6 @@ __author_email__ = 'admin@kuitoi.su'
|
|||||||
__license__ = "FPA"
|
__license__ = "FPA"
|
||||||
__copyright__ = 'Copyright 2023 © SantaSpeen (Maxim Khomutov)'
|
__copyright__ = 'Copyright 2023 © SantaSpeen (Maxim Khomutov)'
|
||||||
|
|
||||||
from main import parser
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
if args.version:
|
|
||||||
print(f"{__title__}:\n\tVersion: {__version__}\n\tBuild: {__build__}")
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import builtins
|
import builtins
|
||||||
import os
|
import os
|
||||||
@ -31,14 +24,20 @@ import webbrowser
|
|||||||
import prompt_toolkit.shortcuts as shortcuts
|
import prompt_toolkit.shortcuts as shortcuts
|
||||||
|
|
||||||
from .utils import get_logger
|
from .utils import get_logger
|
||||||
|
from core.core import Core
|
||||||
|
from main import parser
|
||||||
from modules import ConfigProvider, EventsSystem, PluginsLoader
|
from modules import ConfigProvider, EventsSystem, PluginsLoader
|
||||||
from modules import Console
|
from modules import Console
|
||||||
from modules import MultiLanguage
|
from modules import MultiLanguage
|
||||||
from core.core import Core
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
if args.version:
|
||||||
|
print(f"{__title__}:\n\tVersion: {__version__}\n\tBuild: {__build__}")
|
||||||
|
exit(0)
|
||||||
|
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
log = get_logger("init")
|
log = get_logger("core.init")
|
||||||
|
|
||||||
# Config file init
|
# Config file init
|
||||||
config_path = "kuitoi.yml"
|
config_path = "kuitoi.yml"
|
||||||
@ -49,7 +48,7 @@ config = config_provider.open_config()
|
|||||||
if config.Server['debug'] is True:
|
if config.Server['debug'] is True:
|
||||||
utils.set_debug_status()
|
utils.set_debug_status()
|
||||||
log.info("Debug enabled!")
|
log.info("Debug enabled!")
|
||||||
log = get_logger("init")
|
log = get_logger("core.init")
|
||||||
log.debug("Debug mode enabled!")
|
log.debug("Debug mode enabled!")
|
||||||
log.debug(f"Server config: {config}")
|
log.debug(f"Server config: {config}")
|
||||||
|
|
||||||
@ -111,7 +110,6 @@ console.add_command("exit", console.stop, i18n.man_message_exit, i18n.help_messa
|
|||||||
if not os.path.exists("mods"):
|
if not os.path.exists("mods"):
|
||||||
os.mkdir("mods")
|
os.mkdir("mods")
|
||||||
|
|
||||||
|
|
||||||
log.debug("Initializing PluginsLoader...")
|
log.debug("Initializing PluginsLoader...")
|
||||||
if not os.path.exists("plugins"):
|
if not os.path.exists("plugins"):
|
||||||
os.mkdir("plugins")
|
os.mkdir("plugins")
|
||||||
|
@ -5,11 +5,16 @@
|
|||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import time
|
||||||
import zlib
|
import zlib
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
from .tcp_server import TCPServer
|
from modules.WebAPISystem import app as webapp
|
||||||
from .udp_server import UDPServer
|
from core.tcp_server import TCPServer
|
||||||
|
from core.udp_server import UDPServer
|
||||||
|
|
||||||
|
|
||||||
class Client:
|
class Client:
|
||||||
@ -145,6 +150,9 @@ class Core:
|
|||||||
self.server_port = config.Server["server_port"]
|
self.server_port = config.Server["server_port"]
|
||||||
self.tcp = TCPServer
|
self.tcp = TCPServer
|
||||||
self.udp = UDPServer
|
self.udp = UDPServer
|
||||||
|
self.web_thread = None
|
||||||
|
self.web_pool = webapp.data_pool
|
||||||
|
self.web_stop = None
|
||||||
|
|
||||||
def get_client(self, sock=None, cid=None):
|
def get_client(self, sock=None, cid=None):
|
||||||
if cid:
|
if cid:
|
||||||
@ -172,11 +180,34 @@ class Core:
|
|||||||
if d:
|
if d:
|
||||||
self.log.debug(f"Client ID: {cl.id} died...")
|
self.log.debug(f"Client ID: {cl.id} died...")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def start_web():
|
||||||
|
global uvserver
|
||||||
|
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()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def stop_me():
|
||||||
|
while webapp.data_run[0]:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
raise KeyboardInterrupt
|
||||||
|
|
||||||
async def main(self):
|
async def main(self):
|
||||||
self.tcp = self.tcp(self, self.server_ip, self.server_port)
|
self.tcp = self.tcp(self, self.server_ip, self.server_port)
|
||||||
self.udp = self.udp(self, self.server_ip, self.server_port)
|
self.udp = self.udp(self, self.server_ip, self.server_port)
|
||||||
tasks = [self.tcp.start(), self.udp.start(), console.start()] # self.check_alive()
|
tasks = [self.tcp.start(), self.udp.start(), console.start(), self.stop_me()] # self.check_alive()
|
||||||
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
||||||
|
if config.WebAPI["enabled"]:
|
||||||
|
self.log.debug("Initializing WebAPI...")
|
||||||
|
web_thread = Thread(target=self.start_web)
|
||||||
|
web_thread.start()
|
||||||
|
self.web_thread = web_thread
|
||||||
|
self.web_stop = webapp._stop
|
||||||
self.log.info(i18n.start)
|
self.log.info(i18n.start)
|
||||||
# TODO: Server auth
|
# TODO: Server auth
|
||||||
ev.call_event("on_started")
|
ev.call_event("on_started")
|
||||||
@ -198,4 +229,5 @@ class Core:
|
|||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.log.info(i18n.stop)
|
self.log.info(i18n.stop)
|
||||||
|
asyncio.run(self.web_stop())
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import asyncio
|
import asyncio
|
||||||
from asyncio import StreamWriter, StreamReader
|
from asyncio import StreamWriter, StreamReader
|
||||||
|
from threading import Thread
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
from .tcp_server import TCPServer
|
from .tcp_server import TCPServer
|
||||||
@ -46,10 +48,15 @@ class Core:
|
|||||||
self.loop = asyncio.get_event_loop()
|
self.loop = asyncio.get_event_loop()
|
||||||
self.tcp = TCPServer
|
self.tcp = TCPServer
|
||||||
self.udp = UDPServer
|
self.udp = UDPServer
|
||||||
|
self.web_thread: Thread = None
|
||||||
|
self.web_stop: Callable = lambda: None
|
||||||
def insert_client(self, client: Client) -> None: ...
|
def insert_client(self, client: Client) -> None: ...
|
||||||
def create_client(self, *args, **kwargs) -> Client: ...
|
def create_client(self, *args, **kwargs) -> Client: ...
|
||||||
async def check_alive(self) -> None: ...
|
async def check_alive(self) -> None: ...
|
||||||
|
@staticmethod
|
||||||
|
def start_web() -> None: ...
|
||||||
|
@staticmethod
|
||||||
|
def stop_me(self) -> None: ...
|
||||||
async def main(self) -> None: ...
|
async def main(self) -> None: ...
|
||||||
def start(self) -> None: ...
|
def start(self) -> None: ...
|
||||||
def stop(self) -> None: ...
|
def stop(self) -> None: ...
|
||||||
|
|
||||||
|
@ -8,15 +8,16 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
log_format = "[%(asctime)s | %(name)s | %(levelname)-5s] %(message)s"
|
log_format = "[%(asctime)s | %(name)-14s | %(levelname)-5s] %(message)s"
|
||||||
|
log_format_access = '[%(asctime)s | %(name)-14s | %(levelname)-5s] %(client_addr)s - "%(request_line)s" %(status_code)s'
|
||||||
log_file = "server.log"
|
log_file = "server.log"
|
||||||
log_level = logging.INFO
|
log_level = logging.INFO
|
||||||
# Инициализируем логирование
|
# Инициализируем логирование
|
||||||
logging.basicConfig(level=log_level, format=log_format)
|
logging.basicConfig(level=log_level, format=log_format)
|
||||||
# Настройка логирование в файл.
|
# Настройка логирование в файл.
|
||||||
if os.path.exists(log_file):
|
# if os.path.exists(log_file):
|
||||||
os.remove(log_file)
|
# os.remove(log_file)
|
||||||
fh = logging.FileHandler(log_file)
|
fh = logging.FileHandler(log_file, encoding='utf-8')
|
||||||
fh.setFormatter(logging.Formatter(log_format))
|
fh.setFormatter(logging.Formatter(log_format))
|
||||||
|
|
||||||
|
|
||||||
|
13
src/main.py
13
src/main.py
@ -14,13 +14,20 @@ parser.add_argument('-v', '--version', action="store_true", help='Print version
|
|||||||
parser.add_argument('--config', help='Patch to config file.', nargs='?', default=None, type=str)
|
parser.add_argument('--config', help='Patch to config file.', nargs='?', default=None, type=str)
|
||||||
parser.add_argument('--language', help='Setting localisation.', nargs='?', default=None, type=str)
|
parser.add_argument('--language', help='Setting localisation.', nargs='?', default=None, type=str)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
run = True
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
global run
|
||||||
from core import Core
|
from core import Core
|
||||||
core = Core()
|
core = Core()
|
||||||
|
while run:
|
||||||
try:
|
try:
|
||||||
core.start()
|
core.start()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
run = False
|
||||||
finally:
|
|
||||||
core.stop()
|
core.stop()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
@ -2,8 +2,7 @@ class Config:
|
|||||||
Auth: dict
|
Auth: dict
|
||||||
Game: dict
|
Game: dict
|
||||||
Server: dict
|
Server: dict
|
||||||
|
WebAPI: dict
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
|
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
|
||||||
|
|
||||||
class config (Config): ...
|
class config (Config): ...
|
||||||
|
@ -18,8 +18,8 @@ class Config:
|
|||||||
self.Game = game or {"map": "gridmap_v2", "players": 8, "max_cars": 1}
|
self.Game = game or {"map": "gridmap_v2", "players": 8, "max_cars": 1}
|
||||||
self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!", "language": "en",
|
self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!", "language": "en",
|
||||||
"server_ip": "0.0.0.0", "server_port": 30814, "debug": False}
|
"server_ip": "0.0.0.0", "server_port": 30814, "debug": False}
|
||||||
# self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433,
|
self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433,
|
||||||
# "secret_key": secrets.token_hex(16)}
|
"secret_key": secrets.token_hex(16)}
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
|
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
|
||||||
|
2
src/modules/WebAPISystem/__init__.py
Normal file
2
src/modules/WebAPISystem/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .app import web_app
|
||||||
|
from .app import _stop
|
129
src/modules/WebAPISystem/app.py
Normal file
129
src/modules/WebAPISystem/app.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
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
|
||||||
|
from uvicorn.config import LOGGING_CONFIG
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
LOGGING_CONFIG["formatters"]["default"]['fmt'] = core.utils.log_format
|
||||||
|
LOGGING_CONFIG["formatters"]["access"]["fmt"] = core.utils.log_format_access
|
||||||
|
LOGGING_CONFIG["formatters"].update({
|
||||||
|
"file_default": {
|
||||||
|
"fmt": core.utils.log_format
|
||||||
|
},
|
||||||
|
"file_access": {
|
||||||
|
"fmt": core.utils.log_format_access
|
||||||
|
}
|
||||||
|
})
|
||||||
|
LOGGING_CONFIG["handlers"]["default"]['stream'] = "ext://sys.stdout"
|
||||||
|
LOGGING_CONFIG["handlers"].update({
|
||||||
|
"file_default": {
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"filename": "webserver.log"
|
||||||
|
},
|
||||||
|
"file_access": {
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"filename": "webserver.log"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
LOGGING_CONFIG["loggers"]["uvicorn"]["handlers"].append("file_default")
|
||||||
|
LOGGING_CONFIG["loggers"]["uvicorn.access"]["handlers"].append("file_access")
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
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
|
0
src/modules/WebAPISystem/models.py
Normal file
0
src/modules/WebAPISystem/models.py
Normal file
87
src/modules/WebAPISystem/utils.py
Normal file
87
src/modules/WebAPISystem/utils.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import asyncio
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import click
|
||||||
|
from uvicorn.server import Server, logger
|
||||||
|
|
||||||
|
from uvicorn.lifespan import on
|
||||||
|
|
||||||
|
|
||||||
|
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():
|
||||||
|
Server.shutdown = ev_shutdown
|
||||||
|
Server._log_started_message = ev_log_started_message
|
||||||
|
on.LifespanOn.startup = on_startup
|
||||||
|
on.LifespanOn.shutdown = on_shutdown
|
@ -11,3 +11,5 @@ from .ConfigProvider import ConfigProvider, Config
|
|||||||
from .i18n import MultiLanguage
|
from .i18n import MultiLanguage
|
||||||
from .EventsSystem import EventsSystem
|
from .EventsSystem import EventsSystem
|
||||||
from .PluginsLoader import PluginsLoader
|
from .PluginsLoader import PluginsLoader
|
||||||
|
from .WebAPISystem import web_app
|
||||||
|
from .WebAPISystem import _stop as stop_web
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"GUI_enter_key_message": "Please type your key:",
|
"GUI_enter_key_message": "Please type your key:",
|
||||||
"GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}",
|
"GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}",
|
||||||
|
|
||||||
|
"": "Web phases",
|
||||||
|
"web_start": "WebAPI running on {} (Press CTRL+C to quit)",
|
||||||
|
|
||||||
"": "Command: man",
|
"": "Command: man",
|
||||||
"man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND",
|
"man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND",
|
||||||
"help_message_man": "Display the manual page for COMMAND.",
|
"help_message_man": "Display the manual page for COMMAND.",
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"GUI_enter_key_message": "Пожалуйста введите ключ:",
|
"GUI_enter_key_message": "Пожалуйста введите ключ:",
|
||||||
"GUI_cannot_open_browser": "Не получилось открыть браузер.\nИспользуй эту ссылку: {}",
|
"GUI_cannot_open_browser": "Не получилось открыть браузер.\nИспользуй эту ссылку: {}",
|
||||||
|
|
||||||
|
"": "Web phases",
|
||||||
|
"web_start": "WebAPI запустился на {} (CTRL+C для выключения)",
|
||||||
|
|
||||||
"": "Command: man",
|
"": "Command: man",
|
||||||
"man_message_man": "man - Показывает страничку помощи для COMMAND.\nИспользование: man COMMAND",
|
"man_message_man": "man - Показывает страничку помощи для COMMAND.\nИспользование: man COMMAND",
|
||||||
"help_message_man": "Показывает страничку помощи для COMMAND.",
|
"help_message_man": "Показывает страничку помощи для COMMAND.",
|
||||||
|
@ -21,6 +21,9 @@ class i18n:
|
|||||||
GUI_enter_key_message: str = data["GUI_enter_key_message"]
|
GUI_enter_key_message: str = data["GUI_enter_key_message"]
|
||||||
GUI_cannot_open_browser: str = data["GUI_cannot_open_browser"]
|
GUI_cannot_open_browser: str = data["GUI_cannot_open_browser"]
|
||||||
|
|
||||||
|
# Web phases
|
||||||
|
web_start: str = data["web_start"]
|
||||||
|
|
||||||
# Command: man
|
# Command: man
|
||||||
man_message_man: str = data["man_message_man"]
|
man_message_man: str = data["man_message_man"]
|
||||||
help_message_man: str = data["help_message_man"]
|
help_message_man: str = data["help_message_man"]
|
||||||
|
@ -38,6 +38,9 @@ class i18n:
|
|||||||
self.GUI_enter_key_message: str = data["GUI_enter_key_message"]
|
self.GUI_enter_key_message: str = data["GUI_enter_key_message"]
|
||||||
self.GUI_cannot_open_browser: str = data["GUI_cannot_open_browser"]
|
self.GUI_cannot_open_browser: str = data["GUI_cannot_open_browser"]
|
||||||
|
|
||||||
|
# Web phases
|
||||||
|
self.web_start: str = data["web_start"]
|
||||||
|
|
||||||
# Command: man
|
# Command: man
|
||||||
self.man_message_man: str = data["man_message_man"]
|
self.man_message_man: str = data["man_message_man"]
|
||||||
self.help_message_man: str = data["help_message_man"]
|
self.help_message_man: str = data["help_message_man"]
|
||||||
@ -108,6 +111,9 @@ class MultiLanguage:
|
|||||||
"GUI_enter_key_message": "Please type your key:",
|
"GUI_enter_key_message": "Please type your key:",
|
||||||
"GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}",
|
"GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}",
|
||||||
|
|
||||||
|
"": "Web phases",
|
||||||
|
"web_start": "WebAPI running on {} (Press CTRL+C to quit)",
|
||||||
|
|
||||||
"": "Command: man",
|
"": "Command: man",
|
||||||
"man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND",
|
"man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND",
|
||||||
"help_message_man": "Display the manual page for COMMAND.",
|
"help_message_man": "Display the manual page for COMMAND.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user