diff --git a/.gitignore b/.gitignore index 48612d0..51bd8ef 100644 --- a/.gitignore +++ b/.gitignore @@ -169,4 +169,5 @@ log.txt app.build/ app.dist/ config/ +release/ ann.md \ No newline at end of file diff --git a/api/psh.py b/api/psh.py new file mode 100644 index 0000000..d5120be --- /dev/null +++ b/api/psh.py @@ -0,0 +1,108 @@ +from . import * + +import os +import requests +from urllib.parse import urlparse +from flask import request, abort, redirect + +from mod.tools import calculate_md5 + + +class Wget: + def __init__(self, url: str, headers: dict = None, save_file: str = None, chunk_size: int = 1024 * 1024): + self.url = url + self.headers = headers or {'User-Agent': 'Python/lrc-api'} + self.save_file = save_file or os.path.join(os.getcwd(), os.path.basename(urlparse(url).path)) + self.chunk_size = chunk_size + self.temp_file = calculate_md5(self.url) + '.tmp' + + def __enter__(self): + self.file = open(self.temp_file, 'wb') + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.file.close() + if exc_type is not None: + os.remove(self.temp_file) + else: + os.rename(self.temp_file, self.save_file) + + def download(self): + response = requests.get(self.url, headers=self.headers, stream=True) + response.raise_for_status() + for chunk in response.iter_content(chunk_size=self.chunk_size): + self.file.write(chunk) + + +def ls(*args, **kwargs) -> dict: + """ + 列出目录下的文件 + :param args: 目录 + :param kwargs: 参数 + :return: 文件列表 + """ + path = kwargs.get('path', os.getcwd()) + if not os.path.exists(path): + abort(404) + dirs, files, this_path = [], [], {} + for file in os.listdir(path): + if os.path.isdir(file): + dirs.append(file) + else: + files.append(file) + for d in dirs: + this_path[d] = { + "type": "dir", + "revise_time": os.path.getmtime(d), + } + for f in files: + this_path[f] = { + "type": "file", + "size": os.path.getsize(f), + "revise_time": os.path.getmtime(f), + } + return this_path + + +def dl(*args, **kwargs) -> dict: + """ + 下载文件,通过requests分片下载 + """ + url: str = kwargs.get('url', '') + headers: dict = kwargs.get('headers', {}) + save_file: str = kwargs.get('save_file', '') + chunk_size: int = kwargs.get('chunk_size', 1024 * 1024) + if not url: + return {"code": 422, "error": "Missing 'url' key in JSON."} + if not save_file: + save_file = os.path.join(os.getcwd(), os.path.basename(urlparse(url).path)) + try: + with Wget(url=url, headers=headers, save_file=save_file, chunk_size=chunk_size) as wget: + wget.download() + except requests.RequestException as e: + return {"code": 500, "error": str(e)} + return {"code": 200, "log": f"Successfully downloaded file from {url}"} + + +def interpreter(cmd: dict) -> dict: + action: str = cmd['action'] + _args: dict = cmd['args'] + match action: + case "ls": + return ls(**_args) + case "dl": + return dl(**_args) + + +@app.route('/psh', methods=['POST']) +def shell(): + """ + 模拟部分shell功能 + """ + post_data = request.json + if not post_data: + abort(400) + cmd = post_data.get('cmd') + if not cmd: + abort(400) + return interpreter(cmd) diff --git a/api/source.py b/api/source.py index 70cc0c2..ef8cb36 100644 --- a/api/source.py +++ b/api/source.py @@ -1,3 +1,5 @@ +import sys + from . import * import os @@ -8,6 +10,20 @@ from mod.auth.authentication import require_auth +def get_base_path(): + """ + 获取程序运行路径 + 如果是打包后的exe文件,则返回打包资源路径 + """ + if getattr(sys, 'frozen', False): + return sys._MEIPASS + else: + return os.getcwd() + + +path = os.path.join(get_base_path(), 'src') # 静态资源路径 + + @app.route('/') def redirect_to_welcome(): """ @@ -23,7 +39,7 @@ def favicon(): favicon位置,返回图片 :return: """ - return send_from_directory('src', 'img/Logo_Design.svg') + return send_from_directory(path, 'img/Logo_Design.svg') @app.route('/src') @@ -32,7 +48,7 @@ def return_index(): 显示主页 :return: index page """ - return send_from_directory('src', 'index.html') + return send_from_directory(path, 'index.html') @app.route('/src/') @@ -52,7 +68,7 @@ def serve_file(filename): if filename.lower().endswith(FORBIDDEN_EXTENSIONS): abort(404) try: - return send_from_directory('src', filename) + return send_from_directory(path, filename) except FileNotFoundError: abort(404) diff --git a/app.py b/app.py index 5e95a56..63d152a 100644 --- a/app.py +++ b/app.py @@ -3,7 +3,7 @@ from waitress import serve -from mod import run_process, check_update +from mod import check_update from mod.args import GlobalArgs from api import * from api import __import__ @@ -32,8 +32,7 @@ def run_server(debug=False): logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger('') logger.info("正在启动服务器") - run_process.run() - check_update.run(version="1.5.1") + check_update.run(version="1.5.2") # 注册 Blueprint 到 Flask 应用 app.register_blueprint(v1_bp) # 启动 diff --git a/buildup.py b/buildup.py new file mode 100644 index 0000000..c0781ac --- /dev/null +++ b/buildup.py @@ -0,0 +1,34 @@ +import subprocess +import platform +import sys +import codecs +import os + +sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) + +PLATFORM = platform.system() +ARCHITECTURE = platform.architecture()[0] +APP_NAME = "lrcapi" +APP_VERSION = "1.5.2" +PACK_NAME = f"{APP_NAME}-{APP_VERSION}-{PLATFORM}-{ARCHITECTURE}{'.exe' if PLATFORM == 'Windows' else ''}" + + +# 安装Pyinstaller及主程序依赖 +subprocess.run("pip install -r requirements.txt", shell=True) +subprocess.run("pip install pyinstaller", shell=True) + + +# 打包 +def generate_add_data_options(root_dir): + options = [] + for root, dirs, files in os.walk(root_dir): + if files: + # 将目录路径转换为 PyInstaller 可接受的格式 + formatted_path = root.replace("\\", "/") + options.append(f'--add-data "{formatted_path}/*;{formatted_path}/"') + return " ".join(options) + + +options = generate_add_data_options("src") +command = f"pyinstaller -F -i logo.png {options} app.py -n {PACK_NAME}" +subprocess.run(command, shell=True) diff --git a/mod/run_process.py b/mod/run_process.py deleted file mode 100644 index 58ef917..0000000 --- a/mod/run_process.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging -import pkgutil -import multiprocessing - - -logger = logging.getLogger(__name__) - -def load(package_name): - package = __import__(package_name, fromlist=['']) - sub_modules = [] - - for loader, module_name, is_pkg in pkgutil.walk_packages(package.__path__): - if not is_pkg: - sub_module = __import__(f"{package_name}.{module_name}", fromlist=['']) - sub_modules.append(sub_module) - - return sub_modules - - -def run(): - modules = load('process') - for module in modules: - if hasattr(module, 'main'): - p = multiprocessing.Process(target=module.main) - p.start() - else: - logger.warning(f"The module {module.__name__} does not have a main method.") diff --git a/process/README.MD b/process/README.MD deleted file mode 100644 index 46c2660..0000000 --- a/process/README.MD +++ /dev/null @@ -1,3 +0,0 @@ -# 进程库 - -此Package下的所有模块将被作为子进程运行,须定义入口函数`main()`。 diff --git a/process/__init__.py b/process/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/process/example.py b/process/example.py deleted file mode 100644 index b4df528..0000000 --- a/process/example.py +++ /dev/null @@ -1,2 +0,0 @@ -def main(): - print("Hello, World!") \ No newline at end of file