添加网页根目录、网站地址和端口的配置选项

This commit is contained in:
mofeng-git 2025-01-01 15:23:18 +00:00
parent d5a0b1a8b3
commit 9a29dc6c2e
7 changed files with 81 additions and 71 deletions

View File

@ -353,6 +353,9 @@ def _get_config_scheme() -> dict:
"heartbeat": Option(15.0, type=valid_float_f01),
"access_log_format": Option("[%P / %{X-Real-IP}i] '%r' => %s; size=%b ---"
" referer='%{Referer}i'; user_agent='%{User-Agent}i'"),
"web_path": Option("/usr/share/kvmd/web", type=valid_abs_path),
"host": Option("127.0.0.1", type=valid_ip_or_host),
"port": Option(8080, type=valid_port),
},
"auth": {

View File

@ -103,6 +103,9 @@ def main(argv: (list[str] | None)=None) -> None:
),
keymap_path=config.hid.keymap,
web_path=config.server.web_path,
host=config.server.host,
port=config.server.port,
stream_forever=config.streamer.forever,
).run(**config.server._unpack())

View File

@ -19,19 +19,8 @@
# #
# ========================================================================== #
import os
from aiohttp import ClientConnectionError, ClientSession
from aiohttp import UnixConnector
from aiohttp.web import Request
from aiohttp.web import Response
from aiohttp.web import HTTPForbidden
from aiohttp.web import HTTPNotFound
from aiohttp.web import FileResponse
from aiohttp.web import HTTPInternalServerError
from aiohttp.web import StreamResponse
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode
from ....htserver import exposed_http
from ....htserver import make_json_response
@ -45,12 +34,9 @@ from ..info import InfoManager
class InfoApi:
def __init__(self, info_manager: InfoManager) -> None:
self.__info_manager = info_manager
self.static_dir = 'kvmd_data/usr/share/kvmd/web'
self.target_stream_server = 'http://127.0.0.1:8081'
# =====
@exposed_http("GET", "/api/info")
async def __common_state_handler(self, req: Request) -> Response:
fields = self.__valid_info_fields(req)
@ -61,53 +47,4 @@ class InfoApi:
return sorted(valid_info_fields(
arg=req.query.get("fields", ",".join(available)),
variants=available,
) or available)
@exposed_http("GET", "/streamer/stream")
async def proxy_stream_handler(self, request):
socket_path = '/home/mofeng/One-KVM/kvmd_data/run/kvmd/ustreamer.sock'
query_string = urlencode(request.query)
headers = request.headers.copy()
try:
async with ClientSession(connector=UnixConnector(path=socket_path)) as session:
backend_url = f'http://localhost/stream?{query_string}' if query_string else 'http://localhost/stream'
async with session.get(backend_url, headers=headers) as resp:
response = StreamResponse(status=resp.status, reason=resp.reason, headers=resp.headers)
await response.prepare(request)
while True:
chunk = await resp.content.read(512000)
if not chunk:
break
await response.write(chunk)
return response
except ClientConnectionError:
return Response(status=500, text="Client connection was closed")
@exposed_http("GET", "/{path:.*}", auth_required=False)
async def __html_file_handler(self, req: Request) -> Response:
path = req.match_info['path']
full_path = os.path.normpath(os.path.join(self.static_dir, path))
print("---------------")
print(full_path)
# 安全检查:确保请求的文件在允许的基础目录内
if not full_path.startswith(self.static_dir):
raise HTTPForbidden(text="Access denied.")
if os.path.isdir(full_path):
index_path = os.path.join(full_path, 'index.html')
if os.path.isfile(index_path):
full_path = index_path
else:
raise HTTPNotFound(text="Directory does not contain an index.html file.")
# 检查调整后的路径是否为现有文件
if not (os.path.exists(full_path) and os.path.isfile(full_path)):
raise HTTPNotFound(text="File not found.")
try:
return FileResponse(full_path)
except IOError as e:
raise HTTPInternalServerError(text=str(e))
) or available)

View File

@ -30,6 +30,18 @@ from typing import Any
from aiohttp.web import Request
from aiohttp.web import Response
from aiohttp.web import WebSocketResponse
from aiohttp.web import HTTPForbidden
from aiohttp.web import HTTPNotFound
from aiohttp.web import FileResponse
from aiohttp.web import HTTPInternalServerError
from aiohttp.web import StreamResponse
import os
from aiohttp import ClientConnectionError
from aiohttp import ClientSession
from aiohttp import UnixConnector
from urllib.parse import urlencode
from ... import __version__
@ -153,7 +165,9 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
snapshoter: Snapshoter,
keymap_path: str,
web_path: str,
host: str,
port: int,
stream_forever: bool,
) -> None:
@ -194,6 +208,9 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
self.__streamer_notifier = aiotools.AioNotifier()
self.__reset_streamer = False
self.__new_streamer_params: dict = {}
self.__web_path = web_path
self.__host = host
self.__port = port
# ===== STREAMER CONTROLLER
@ -306,6 +323,53 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
self._get_wss(),
)))
# ===== WEB TASKS
@exposed_http("GET", "/streamer/stream")
async def proxy_stream_handler(self, request):
socket_path = self.__streamer.get_path()
query_string = urlencode(request.query)
headers = request.headers.copy()
try:
async with ClientSession(connector=UnixConnector(path=socket_path)) as session:
backend_url = f'http://localhost/stream?{query_string}' if query_string else 'http://localhost/stream'
async with session.get(backend_url, headers=headers) as resp:
response = StreamResponse(status=resp.status, reason=resp.reason, headers=resp.headers)
await response.prepare(request)
while True:
chunk = await resp.content.read(512000)
if not chunk:
break
await response.write(chunk)
return response
except ClientConnectionError:
return Response(status=500, text="Client connection was closed")
@exposed_http("GET", "/{path:.*}", auth_required=False)
async def __html_file_handler(self, req: Request) -> Response:
path = req.match_info['path']
if not os.path.exists(self.__web_path):
raise HTTPNotFound(text="Web root directory not found.")
full_path = os.path.normpath(os.path.join(self.__web_path, path))
if not full_path.startswith(self.__web_path):
raise HTTPForbidden(text="Access denied.")
if os.path.isdir(full_path):
index_path = os.path.join(full_path, 'index.html')
if os.path.isfile(index_path):
full_path = index_path
else:
raise HTTPNotFound(text="404 Not Found")
if not (os.path.exists(full_path) and os.path.isfile(full_path)):
raise HTTPNotFound(text="404 Not Found")
try:
return FileResponse(full_path)
except IOError as e:
raise HTTPInternalServerError(text=str(e))
# ===== SYSTEM TASKS
async def __stream_controller(self) -> None:

View File

@ -271,7 +271,9 @@ class Streamer: # pylint: disable=too-many-instance-attributes
def get_params(self) -> dict:
return self.__params.get_params()
def get_path(self) -> str:
return self.__unix_path
# =====
async def get_state(self) -> dict:

View File

@ -288,12 +288,12 @@ class HttpServer:
unix_mode: int,
heartbeat: float,
access_log_format: str,
web_path: str,
host: str,
port: int,
) -> None:
self.__ws_heartbeat = heartbeat
# 默认绑定到所有地址
host = '0.0.0.0'
port = 8080
#if unix_rm and os.path.exists(unix_path):
#os.remove(unix_path)
#server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

View File

@ -17,8 +17,9 @@ kvmd:
server:
unix: kvmd_data/run/kvmd/kvmd.sock
#unix_mode: 432
#unix_rm: true
web_path: kvmd_data/usr/share/kvmd/web
host: 0.0.0.0
port: 8080
atx:
type: disabled