diff --git a/docs/D01_环境部署.md b/docs/D01_环境部署.md index d7d4f74..4c34109 100644 --- a/docs/D01_环境部署.md +++ b/docs/D01_环境部署.md @@ -1 +1,28 @@ -# D01_环境部署 \ No newline at end of file +# D01_环境部署 + +操作系统: Windows + +Python版本:3.11.13 + +推荐使用Anaconda yml配置文件一键导入虚拟python环境 + +[配置文件路径](./../misc/okd-env.yml) + +## 使用配置文件构建虚拟环境 + +Anaconda版本 : conda 25.5.1 + +```shell +# 1.需要自行安装Anaconda, 建议安装同版本Anaconda + +# 2.将okd-env.yml文件复制到目标设备 + +# 3.打开 Anaconda Prompt / 终端,执行重建命令: +conda env create --file 路径/文件名.yml +# 例如:conda env create --file C:/Users/xxx/Desktop/okd-env.yml + +# 4.等待 conda 自动下载并安装所有依赖包,完成后激活环境验证 +conda activate okd-env +python --version +``` + diff --git a/misc/okd-env.yml b/misc/okd-env.yml new file mode 100644 index 0000000..abcffaa --- /dev/null +++ b/misc/okd-env.yml @@ -0,0 +1,56 @@ +name: okd-env +channels: + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/win-64/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/win-64/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/win-64/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/numba/label/dev/win-64/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r/win-64/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro/win-64/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/mro/win-64/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2/win-64/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/win-64/ + - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/win-64/ + - default + - defaults +dependencies: + - bzip2=1.0.8=h2466b09_7 + - ca-certificates=2025.8.3=h4c7d964_0 + - libexpat=2.7.1=hac47afa_0 + - libffi=3.4.6=h537db12_1 + - liblzma=5.8.1=h2466b09_2 + - libsqlite=3.50.4=hf5d6505_0 + - libzlib=1.3.1=h2466b09_2 + - openssl=3.5.2=h725018a_0 + - pip=25.2=pyh8b19718_0 + - python=3.11.13=h3f84c4b_0_cpython + - setuptools=80.9.0=pyhff2d567_0 + - tk=8.6.13=h2c6b04d_2 + - tzdata=2025b=h78e105d_0 + - ucrt=10.0.22621.0=h57928b3_1 + - vc=14.3=h41ae7f8_31 + - vc14_runtime=14.44.35208=h818238b_31 + - vcomp14=14.44.35208=h818238b_31 + - wheel=0.45.1=pyhd8ed1ab_1 + - pip: + - altgraph==0.17.4 + - click==8.2.1 + - colorama==0.4.6 + - packaging==25.0 + - pefile==2023.2.7 + - pyinstaller==6.15.0 + - pyinstaller-hooks-contrib==2025.8 + - pyqt6==6.4.2 + - pyqt6-plugins==6.4.2.2.3 + - pyqt6-qt6==6.4.3 + - pyqt6-sip==13.10.2 + - pyqt6-tools==6.4.2.3.3 + - python-dotenv==1.1.1 + - pywin32-ctypes==0.2.3 + - qt6-applications==6.4.3.2.3 + - qt6-tools==6.4.3.1.3 +prefix: D:\Environment\Anaconda\envs\okd-env diff --git a/src/backend/__init__.py b/src/backend/__init__.py index e69de29..c840b67 100644 --- a/src/backend/__init__.py +++ b/src/backend/__init__.py @@ -0,0 +1,6 @@ +from .back_service import ( + get_all_proj_name, + do_proj_parse +) + +__all__ = [ get_all_proj_name, do_proj_parse ] \ No newline at end of file diff --git a/src/backend/__pycache__/__init__.cpython-311.pyc b/src/backend/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..b633ab4 Binary files /dev/null and b/src/backend/__pycache__/__init__.cpython-311.pyc differ diff --git a/src/backend/__pycache__/back_service.cpython-311.pyc b/src/backend/__pycache__/back_service.cpython-311.pyc new file mode 100644 index 0000000..cc301a4 Binary files /dev/null and b/src/backend/__pycache__/back_service.cpython-311.pyc differ diff --git a/src/backend/back_service.py b/src/backend/back_service.py index e69de29..76f9c2c 100644 --- a/src/backend/back_service.py +++ b/src/backend/back_service.py @@ -0,0 +1,80 @@ +import os +import importlib + +def get_all_proj_dir(): + proj_dirs = [] + # 1.Search proj from src + from common import source_dir + + backend_dir = os.path.join(source_dir, "backend") + # 检查路径是否存在 + if not os.path.exists(backend_dir): + raise FileNotFoundError(f"指定路径不存在: {backend_dir}") + + # 检查路径是否有效 + if not os.path.isdir(backend_dir): + raise NotADirectoryError(f"指定路径不是一个目录: {backend_dir}") + + for entry in os.listdir(backend_dir): + if entry.startswith("proj_"): + proj_dirs.append(entry) + + # 2.Search proj from appdata + from common import get_user_appdata_path + app_dir = get_user_appdata_path() + usr_app_dir = os.path.join(app_dir, "onekeylog_diag") + usr_plugin_dir = os.path.join(usr_app_dir, "plugin") + + # 检查路径是否存在 + if not os.path.exists(usr_plugin_dir): + raise FileNotFoundError(f"指定路径不存在: {usr_plugin_dir}") + + # 检查路径是否有效 + if not os.path.isdir(usr_plugin_dir): + raise NotADirectoryError(f"指定路径不是一个目录: {usr_plugin_dir}") + + for entry in os.listdir(usr_plugin_dir): + if entry.startswith("proj_"): + proj_dirs.append(entry) + + return proj_dirs + +def get_all_proj_name(): + prefix_len = len("proj_") + proj_dir = get_all_proj_dir() + + proj_name = [item[prefix_len:] for item in proj_dir] + return proj_name + +def dynamic_import_lib(pacakge_name): + """ + 动态拼接包名并导入模块 + + 参数: + package_name : 包名 + + 返回: + 导入的模块对象,如果导入失败则返回None + """ + + package_path = "backend." + pacakge_name + + try: + # 动态导入模块 + module = importlib.import_module(package_path) + print(f"成功导入模块: {package_path}") + return module + except ImportError: + print(f"无法导入模块: {package_path}") + return None + except Exception as e: + print(f"导入模块时发生错误: {e}") + return None + +def do_proj_parse(key="app"): + porj_package = "proj_" + key + + pHandlers = dynamic_import_lib(porj_package) + if hasattr(pHandlers, "start_parse"): + pHandlers.start_parse() + \ No newline at end of file diff --git a/src/backend/proj_app/__init__.py b/src/backend/proj_app/__init__.py index e69de29..4cff789 100644 --- a/src/backend/proj_app/__init__.py +++ b/src/backend/proj_app/__init__.py @@ -0,0 +1,5 @@ +from .service_app import ( + start_parse +) + +__all__ = [ start_parse ] \ No newline at end of file diff --git a/src/backend/proj_app/__pycache__/__init__.cpython-311.pyc b/src/backend/proj_app/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..60017b3 Binary files /dev/null and b/src/backend/proj_app/__pycache__/__init__.cpython-311.pyc differ diff --git a/src/backend/proj_app/__pycache__/service_app.cpython-311.pyc b/src/backend/proj_app/__pycache__/service_app.cpython-311.pyc new file mode 100644 index 0000000..ad20ad1 Binary files /dev/null and b/src/backend/proj_app/__pycache__/service_app.cpython-311.pyc differ diff --git a/src/backend/proj_app/service_app.py b/src/backend/proj_app/service_app.py new file mode 100644 index 0000000..242c191 --- /dev/null +++ b/src/backend/proj_app/service_app.py @@ -0,0 +1,7 @@ +import os +from common import record_log + +SERVICE_TYPE = "APP" + +def start_parse(): + record_log("INFO", f"[{SERVICE_TYPE}] : Start Parse ...") \ No newline at end of file diff --git a/src/common/__init__.py b/src/common/__init__.py index 8207b8c..0a9e36b 100644 --- a/src/common/__init__.py +++ b/src/common/__init__.py @@ -1,8 +1,23 @@ from ._globals import ( - cache_dir + cache_dir, + source_dir ) -def init_package_coomon(): - pass +from .app_logger import ( + record_log +) -init_package_coomon() \ No newline at end of file +from .file_tool import( + force_empty_folder, + add_to_json_array, + get_user_appdata_path +) + +# From ._globals +__all__ = [ cache_dir, source_dir ] + +# From .app_logger +__all__ += [ record_log ] + +# From .file_tool +__all__ += [ force_empty_folder, add_to_json_array, get_user_appdata_path ] \ No newline at end of file diff --git a/src/common/cache_mgmt.py b/src/common/cache_mgmt.py index a4fae80..f78bb2a 100644 --- a/src/common/cache_mgmt.py +++ b/src/common/cache_mgmt.py @@ -1,12 +1,54 @@ import os +import time -from . import cache_dir -from app_logger import LOG_FILE +from ._globals import cache_dir +from .file_tool import force_empty_folder, add_to_json_array, get_user_appdata_path +from .app_logger import record_log def app_cache_create(): if not os.path.exists(cache_dir): os.makedirs(cache_dir) + + # 在用户目录创建APP目录及插件目录 + usr_appdata_path = get_user_appdata_path() + app_data_okd_path = os.path.join(usr_appdata_path, "onekeylog_diag") + if not os.path.exists(app_data_okd_path): + os.makedirs(app_data_okd_path) + record_log("Info", f"Create Dir:{app_data_okd_path}") + + app_plugin_path = os.path.join(app_data_okd_path, "plugin") + if not os.path.exists(app_plugin_path): + os.makedirs(app_plugin_path) + record_log("Info", f"Create Dir:{app_plugin_path}") + +def add_onekeylog_info_cache(log_name): + """ + 添加一份新日志时触发这个动作, 将日志名称以及解析日期全部记录到 + parse_history.json + + 参数: + log_name(str): 被解析的文件名 + """ + parseJsonFile = os.path.join(cache_dir, "parse_history.json") + # 获取当前时间戳 + current_time = time.strftime("%Y-%m-%d %H:%M:%S") + + # 生成新的记录Dict + new_entry = { + "filename" : log_name, + "timestamp" : current_time + } + + result_b = add_to_json_array(parseJsonFile, new_entry) + + if not result_b: + record_log("ERROR", "Fail to add parse history cache") + +def clean_app_cache(): + force_empty_folder(cache_dir) def clean_running_log(): + LOG_FILE = os.path.join(cache_dir, "running.log") + if os.path.exists(LOG_FILE): os.remove(LOG_FILE) \ No newline at end of file diff --git a/src/common/file_tool.py b/src/common/file_tool.py index e69de29..96c3390 100644 --- a/src/common/file_tool.py +++ b/src/common/file_tool.py @@ -0,0 +1,98 @@ +import os +import json +import shutil +import pathlib + +def force_empty_folder(folder_path): + """ + 强制清空指定文件夹内的所有内容(包括文件和子文件夹),但保留文件夹本身 + + 参数: + folder_path (str): 要清空的文件夹路径 + + 异常: + FileNotFoundError: 如果指定的文件夹不存在 + PermissionError: 如果没有足够的权限操作文件夹内容 + Exception: 其他可能出现的错误 + """ + # 检查文件夹是否存在 + if not os.path.exists(folder_path): + raise FileNotFoundError(f"文件夹不存在: {folder_path}") + + # 检查路径是否指向一个文件夹 + if not os.path.isdir(folder_path): + raise NotADirectoryError(f"指定路径不是一个文件夹: {folder_path}") + + # 遍历文件夹内的所有内容 + for item in os.listdir(folder_path): + item_path = os.path.join(folder_path, item) + + try: + # 如果是文件或符号链接,直接删除 + if os.path.isfile(item_path) or os.path.islink(item_path): + os.unlink(item_path) + # 如果是文件夹,递归删除整个文件夹 + elif os.path.isdir(item_path): + shutil.rmtree(item_path, ignore_errors=False, onerror=None) + except Exception as e: + raise Exception(f"删除 {item_path} 时出错: {str(e)}") + +def add_to_json_array(json_file_path, new_data): + """ + 向JSON文件中的数组添加一组新数据 + + 参数: + json_file_path (str): JSON文件的路径 + new_data: 要添加到数组中的新数据,可以是任何可序列化的类型(字典、列表、字符串等) + + 返回: + bool: 操作是否成功 + + 异常: + IOError: 文件操作相关错误 + json.JSONDecodeError: JSON格式解析错误 + Exception: 其他可能的错误 + """ + # 检查文件是否存在,如果不存在则创建并初始化一个空数组 + if not os.path.exists(json_file_path): + with open(json_file_path, 'w', encoding='utf-8') as f: + json.dump([], f, ensure_ascii=False, indent=2) + + # 读取现有数据 + try: + with open(json_file_path, 'r', encoding='utf-8') as f: + data = json.load(f) + + # 确保数据是一个数组 + if not isinstance(data, list): + raise ValueError("JSON文件内容不是一个数组") + + # 添加新数据 + data.append(new_data) + + # 写回文件 + with open(json_file_path, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, indent=2) + + return True + + except json.JSONDecodeError: + raise json.JSONDecodeError(f"文件 {json_file_path} 不是有效的JSON格式", json_file_path, 0) + except Exception as e: + raise Exception(f"添加数据时发生错误: {str(e)}") + +def get_user_appdata_path(): + """获取用户AppData目录路径""" + # 获取用户主目录 + user_home = pathlib.Path.home() + + # 根据操作系统获取AppData目录 + if os.name == 'nt': # Windows系统 + # 在Windows上,AppData通常通过环境变量获取 + appdata = os.getenv('APPDATA') + if appdata: + return pathlib.Path(appdata) + # 如果环境变量获取失败,使用默认路径 + return user_home / 'AppData' / 'Roaming' + else: + raise OSError(f"不支持的操作系统: {os.name}") \ No newline at end of file