diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97c642e..291d900 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,7 @@ jobs: - name: Check clients, WebUI and shell scripts run: | python3 -m py_compile clients/client-linux.py clients/client-psutil.py + python3 -m unittest clients/test_client_args.py sh -n clients/entrypoint.sh bash -n status.sh node --check web/js/app.js diff --git a/README.md b/README.md index 3fb2d73..797983b 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,9 @@ docker run -d --restart=always --name=serverstatus-server \ - 健康检查:http://127.0.0.1:8080/api/health - API 描述:http://127.0.0.1:8080/api/schema - OpenAPI 3.1:http://127.0.0.1:8080/api/openapi.json -- 客户端上报端口:`35601/tcp`,`ADMIN_TOKEN` 不设置时,监控页面仍可读取,管理 API 返回 `503`,WebUI 的“配置”页不能修改数据。 +- 客户端上报端口:`35601/tcp` + +`ADMIN_TOKEN` 不设置时,监控页面仍可读取,管理 API 返回 `503`,WebUI 的“配置”页不能修改数据。 ## 二、客户端 @@ -319,7 +321,7 @@ docker compose -f docker-compose-server.yml config docker compose -f docker-compose-client.yml config ``` -CI 还会检查 Go 格式、Python 客户端、Shell 脚本、WebUI JavaScript、主服务/客户端/Telegram Compose 文件和两个 Docker 镜像。 +CI 还会检查 Go 格式、Python 客户端、Shell 脚本、WebUI JavaScript、服务端/客户端 Compose 文件和两个 Docker 镜像。 ## 从旧服务端迁移 @@ -335,4 +337,4 @@ CI 还会检查 Go 格式、Python 客户端、Shell 脚本、WebUI JavaScript ## 致谢 - BotoX:https://github.com/BotoX/ServerStatus -- mojeda:https://github.com/mojeda/ServerStatus \ No newline at end of file +- mojeda:https://github.com/mojeda/ServerStatus diff --git a/clients/client-linux.py b/clients/client-linux.py index c43b3bc..315296f 100755 --- a/clients/client-linux.py +++ b/clients/client-linux.py @@ -60,6 +60,14 @@ CU = _env_str("CU", CU) CT = _env_str("CT", CT) CM = _env_str("CM", CM) +def parse_cli_args(arguments): + overrides = {} + for argument in arguments: + key, separator, value = argument.partition('=') + if separator and key in {'SERVER', 'PORT', 'USER', 'PASSWORD', 'INTERVAL'}: + overrides[key] = value + return overrides + def get_uptime(): with open('/proc/uptime', 'r') as f: uptime = f.readline().split('.', 2) @@ -509,17 +517,12 @@ def byte_str(object): print(type(object)) if __name__ == '__main__': - for argc in sys.argv: - if 'SERVER' in argc: - SERVER = argc.split('SERVER=')[-1] - elif 'PORT' in argc: - PORT = int(argc.split('PORT=')[-1]) - elif 'USER' in argc: - USER = argc.split('USER=')[-1] - elif 'PASSWORD' in argc: - PASSWORD = argc.split('PASSWORD=')[-1] - elif 'INTERVAL' in argc: - INTERVAL = int(argc.split('INTERVAL=')[-1]) + cli_args = parse_cli_args(sys.argv[1:]) + SERVER = cli_args.get('SERVER', SERVER) + PORT = int(cli_args.get('PORT', PORT)) + USER = cli_args.get('USER', USER) + PASSWORD = cli_args.get('PASSWORD', PASSWORD) + INTERVAL = int(cli_args.get('INTERVAL', INTERVAL)) socket.setdefaulttimeout(30) get_realtime_data() while True: diff --git a/clients/client-psutil.py b/clients/client-psutil.py index 10a20a7..ad85089 100755 --- a/clients/client-psutil.py +++ b/clients/client-psutil.py @@ -60,6 +60,14 @@ CU = _env_str("CU", CU) CT = _env_str("CT", CT) CM = _env_str("CM", CM) +def parse_cli_args(arguments): + overrides = {} + for argument in arguments: + key, separator, value = argument.partition('=') + if separator and key in {'SERVER', 'PORT', 'USER', 'PASSWORD', 'INTERVAL'}: + overrides[key] = value + return overrides + def get_uptime(): return int(time.time() - psutil.boot_time()) @@ -456,17 +464,12 @@ def byte_str(object): print(type(object)) if __name__ == '__main__': - for argc in sys.argv: - if 'SERVER' in argc: - SERVER = argc.split('SERVER=')[-1] - elif 'PORT' in argc: - PORT = int(argc.split('PORT=')[-1]) - elif 'USER' in argc: - USER = argc.split('USER=')[-1] - elif 'PASSWORD' in argc: - PASSWORD = argc.split('PASSWORD=')[-1] - elif 'INTERVAL' in argc: - INTERVAL = int(argc.split('INTERVAL=')[-1]) + cli_args = parse_cli_args(sys.argv[1:]) + SERVER = cli_args.get('SERVER', SERVER) + PORT = int(cli_args.get('PORT', PORT)) + USER = cli_args.get('USER', USER) + PASSWORD = cli_args.get('PASSWORD', PASSWORD) + INTERVAL = int(cli_args.get('INTERVAL', INTERVAL)) socket.setdefaulttimeout(30) get_realtime_data() while 1: diff --git a/clients/test_client_args.py b/clients/test_client_args.py new file mode 100644 index 0000000..289a0d8 --- /dev/null +++ b/clients/test_client_args.py @@ -0,0 +1,40 @@ +import importlib.util +import runpy +import sys +import types +import unittest +from pathlib import Path + + +CLIENT_DIR = Path(__file__).resolve().parent + + +class ClientArgumentTests(unittest.TestCase): + def test_password_with_user_text_does_not_replace_username(self): + if importlib.util.find_spec("psutil") is None: + sys.modules["psutil"] = types.ModuleType("psutil") + + arguments = [ + "SERVER=127.0.0.1", + "PORT=35601", + "USER=s01", + "PASSWORD=USER_DEFAULT_PASSWORD", + "INTERVAL=2", + "NOTUSER=ignored", + ] + expected = { + "SERVER": "127.0.0.1", + "PORT": "35601", + "USER": "s01", + "PASSWORD": "USER_DEFAULT_PASSWORD", + "INTERVAL": "2", + } + + for filename in ("client-linux.py", "client-psutil.py"): + with self.subTest(client=filename): + namespace = runpy.run_path(str(CLIENT_DIR / filename)) + self.assertEqual(namespace["parse_cli_args"](arguments), expected) + + +if __name__ == "__main__": + unittest.main()