mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-14 10:10:29 +08:00
添加网页根目录、网站地址和端口的配置选项
This commit is contained in:
parent
d5a0b1a8b3
commit
9a29dc6c2e
@ -353,6 +353,9 @@ def _get_config_scheme() -> dict:
|
|||||||
"heartbeat": Option(15.0, type=valid_float_f01),
|
"heartbeat": Option(15.0, type=valid_float_f01),
|
||||||
"access_log_format": Option("[%P / %{X-Real-IP}i] '%r' => %s; size=%b ---"
|
"access_log_format": Option("[%P / %{X-Real-IP}i] '%r' => %s; size=%b ---"
|
||||||
" referer='%{Referer}i'; user_agent='%{User-Agent}i'"),
|
" 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": {
|
"auth": {
|
||||||
|
|||||||
@ -103,6 +103,9 @@ def main(argv: (list[str] | None)=None) -> None:
|
|||||||
),
|
),
|
||||||
|
|
||||||
keymap_path=config.hid.keymap,
|
keymap_path=config.hid.keymap,
|
||||||
|
web_path=config.server.web_path,
|
||||||
|
host=config.server.host,
|
||||||
|
port=config.server.port,
|
||||||
|
|
||||||
stream_forever=config.streamer.forever,
|
stream_forever=config.streamer.forever,
|
||||||
).run(**config.server._unpack())
|
).run(**config.server._unpack())
|
||||||
|
|||||||
@ -19,19 +19,8 @@
|
|||||||
# #
|
# #
|
||||||
# ========================================================================== #
|
# ========================================================================== #
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
from aiohttp import ClientConnectionError, ClientSession
|
|
||||||
from aiohttp import UnixConnector
|
|
||||||
from aiohttp.web import Request
|
from aiohttp.web import Request
|
||||||
from aiohttp.web import Response
|
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 exposed_http
|
||||||
from ....htserver import make_json_response
|
from ....htserver import make_json_response
|
||||||
@ -45,12 +34,9 @@ from ..info import InfoManager
|
|||||||
class InfoApi:
|
class InfoApi:
|
||||||
def __init__(self, info_manager: InfoManager) -> None:
|
def __init__(self, info_manager: InfoManager) -> None:
|
||||||
self.__info_manager = info_manager
|
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")
|
@exposed_http("GET", "/api/info")
|
||||||
async def __common_state_handler(self, req: Request) -> Response:
|
async def __common_state_handler(self, req: Request) -> Response:
|
||||||
fields = self.__valid_info_fields(req)
|
fields = self.__valid_info_fields(req)
|
||||||
@ -61,53 +47,4 @@ class InfoApi:
|
|||||||
return sorted(valid_info_fields(
|
return sorted(valid_info_fields(
|
||||||
arg=req.query.get("fields", ",".join(available)),
|
arg=req.query.get("fields", ",".join(available)),
|
||||||
variants=available,
|
variants=available,
|
||||||
) or 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))
|
|
||||||
|
|
||||||
@ -30,6 +30,18 @@ from typing import Any
|
|||||||
from aiohttp.web import Request
|
from aiohttp.web import Request
|
||||||
from aiohttp.web import Response
|
from aiohttp.web import Response
|
||||||
from aiohttp.web import WebSocketResponse
|
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__
|
from ... import __version__
|
||||||
|
|
||||||
@ -153,7 +165,9 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
|
|||||||
snapshoter: Snapshoter,
|
snapshoter: Snapshoter,
|
||||||
|
|
||||||
keymap_path: str,
|
keymap_path: str,
|
||||||
|
web_path: str,
|
||||||
|
host: str,
|
||||||
|
port: int,
|
||||||
stream_forever: bool,
|
stream_forever: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
@ -194,6 +208,9 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
|
|||||||
self.__streamer_notifier = aiotools.AioNotifier()
|
self.__streamer_notifier = aiotools.AioNotifier()
|
||||||
self.__reset_streamer = False
|
self.__reset_streamer = False
|
||||||
self.__new_streamer_params: dict = {}
|
self.__new_streamer_params: dict = {}
|
||||||
|
self.__web_path = web_path
|
||||||
|
self.__host = host
|
||||||
|
self.__port = port
|
||||||
|
|
||||||
# ===== STREAMER CONTROLLER
|
# ===== STREAMER CONTROLLER
|
||||||
|
|
||||||
@ -306,6 +323,53 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
|
|||||||
self._get_wss(),
|
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
|
# ===== SYSTEM TASKS
|
||||||
|
|
||||||
async def __stream_controller(self) -> None:
|
async def __stream_controller(self) -> None:
|
||||||
|
|||||||
@ -271,7 +271,9 @@ class Streamer: # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
def get_params(self) -> dict:
|
def get_params(self) -> dict:
|
||||||
return self.__params.get_params()
|
return self.__params.get_params()
|
||||||
|
|
||||||
|
def get_path(self) -> str:
|
||||||
|
return self.__unix_path
|
||||||
# =====
|
# =====
|
||||||
|
|
||||||
async def get_state(self) -> dict:
|
async def get_state(self) -> dict:
|
||||||
|
|||||||
@ -288,12 +288,12 @@ class HttpServer:
|
|||||||
unix_mode: int,
|
unix_mode: int,
|
||||||
heartbeat: float,
|
heartbeat: float,
|
||||||
access_log_format: str,
|
access_log_format: str,
|
||||||
|
web_path: str,
|
||||||
|
host: str,
|
||||||
|
port: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
self.__ws_heartbeat = heartbeat
|
self.__ws_heartbeat = heartbeat
|
||||||
# 默认绑定到所有地址
|
|
||||||
host = '0.0.0.0'
|
|
||||||
port = 8080
|
|
||||||
#if unix_rm and os.path.exists(unix_path):
|
#if unix_rm and os.path.exists(unix_path):
|
||||||
#os.remove(unix_path)
|
#os.remove(unix_path)
|
||||||
#server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
#server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
|||||||
@ -17,8 +17,9 @@ kvmd:
|
|||||||
|
|
||||||
server:
|
server:
|
||||||
unix: kvmd_data/run/kvmd/kvmd.sock
|
unix: kvmd_data/run/kvmd/kvmd.sock
|
||||||
#unix_mode: 432
|
web_path: kvmd_data/usr/share/kvmd/web
|
||||||
#unix_rm: true
|
host: 0.0.0.0
|
||||||
|
port: 8080
|
||||||
|
|
||||||
atx:
|
atx:
|
||||||
type: disabled
|
type: disabled
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user