初始化

This commit is contained in:
2026-04-10 10:57:44 +08:00
parent ec6e7573de
commit a19a468298
70 changed files with 5676 additions and 0 deletions

205
server.py Normal file
View File

@@ -0,0 +1,205 @@
import os
import re
import platform
import subprocess
import psutil
import winreg
from flask import Flask, jsonify, request
from flask_cors import CORS
app = Flask(__name__)
# <--- 2. 启用全局跨域支持
# 默认允许所有域名跨域访问所有接口。
# 如果想只允许特定前端域名,可以改成 CORS(app, origins=["http://localhost:8080", "http://你的域名.com"])
CORS(app, supports_credentials=True)
# Windows 下分离进程的标志位,确保 exe 独立运行
DETACHED_PROCESS = 0x00000008
def get_cpu_model():
"""
通过读取 Windows 注册表获取准确的 CPU 型号
"""
try:
# 打开注册表路径
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System\CentralProcessor\0")
# 读取 ProcessorNameString 的值
cpu_model, _ = winreg.QueryValueEx(key, "ProcessorNameString")
return cpu_model.strip()
except Exception:
return "Unknown CPU"
def is_valid_task_id(task_id):
"""
校验 task_id只允许字母、数字、下划线和连字符防止目录穿越攻击
"""
return re.match(r'^[\w\-]+$', task_id) is not None
@app.route('/api/system_info', methods=['GET'])
def get_system_info():
"""
接口 1获取当前设备(Windows)的负载情况和硬件信息(附带字段说明)
"""
try:
# 获取基础监控数据
cpu_usage = psutil.cpu_percent(interval=0.5)
cpu_cores_physical = psutil.cpu_count(logical=False)
cpu_cores_logical = psutil.cpu_count(logical=True)
cpu_freq = psutil.cpu_freq()
virtual_mem = psutil.virtual_memory()
disk_usage = psutil.disk_usage('C:\\')
# 构建返回数据,为每个返回值添加 desc 注释说明
info = {
# "os": {
# "system": platform.system(), # 操作系统名称 (如 Windows)
# "release": platform.release(), # 操作系统主要版本号 (如 10, 11)
# "version": platform.version(), # 操作系统的详细内部构建版本号
# "machine": platform.machine() # 系统架构 (如 AMD64 代表 64位)
# },
"os": f"{platform.system()} {platform.version()} ({platform.machine()})", # 综合描述操作系统的字符串
"hardware": {
"cpu": f"{get_cpu_model()} ({cpu_cores_physical}P/{cpu_cores_logical}L) {cpu_freq.max if cpu_freq else None}GHz", # CPU 型号和核心数的综合描述字符串
# "cpu_model": get_cpu_model(), # CPU 具体型号名称
# "cpu_cores_physical": cpu_cores_physical, # CPU 物理核心数 (真实核心)
# "cpu_cores_logical": cpu_cores_logical, # CPU 逻辑核心数 (包含超线程)
# "cpu_max_freq_mhz": cpu_freq.max if cpu_freq else None, # CPU 最大设计频率 (单位: MHz)
"ram_total_gb": round(virtual_mem.total / (1024 ** 3), 2), # 物理内存 (RAM) 总容量 (单位: GB)
"disk_c_total_gb": round(disk_usage.total / (1024 ** 3), 2) # 系统盘 (C盘) 总容量 (单位: GB)
},
"load": {
"cpu_usage_percent": cpu_usage, # 当前 CPU 总利用率 (单位: %)
"ram_usage_percent": virtual_mem.percent, # 当前物理内存使用率 (单位: %)
"ram_available_gb": round(virtual_mem.available / (1024 ** 3), 2), # 当前可用的物理内存大小 (单位: GB)
"disk_c_usage_percent": disk_usage.percent, # 系统盘 (C盘) 存储空间使用率 (单位: %)
"disk_c_free_gb": round(disk_usage.free / (1024 ** 3), 2) # 系统盘 (C盘) 剩余可用空间 (单位: GB)
}
}
return jsonify({"code": 200, "status": "success", "data": info}), 200
except Exception as e:
return jsonify({"code": 500, "status": "error", "message": str(e)}), 500
@app.route('/api/run_exe', methods=['POST'])
def run_exe():
"""
接口 2非阻塞地运行一个 exe 程序
"""
data = request.get_json()
if not data or 'exe_path' not in data:
return jsonify({"status": "error", "message": "Missing 'exe_path' in request body."}), 400
exe_path = data['exe_path']
args = data.get('args', [])
if not os.path.exists(exe_path):
return jsonify({"status": "error", "message": f"File not found: {exe_path}"}), 404
try:
command = [exe_path] + args
process = subprocess.Popen(
command,
creationflags=DETACHED_PROCESS,
close_fds=True,
shell=False
)
return jsonify({
"code": 200,
"status": "success",
"message": "Executable started successfully.",
"pid": process.pid
}), 200
except Exception as e:
return jsonify({"code": 500, "status": "error", "message": str(e)}), 500
@app.route('/api/upload', methods=['POST'])
def upload_files():
"""
接口 3上传文件
接收 form-data 格式数据:
- task_id: 任务ID (字符串)
- file: 上传的文件 (可以同时上传多个文件)
"""
# 1. 获取并校验 task_id
task_id = request.form.get('task_id')
if not task_id:
return jsonify({"status": "error", "message": "Missing 'task_id' in form data."}), 400
if not is_valid_task_id(task_id):
return jsonify({"status": "error", "message": "Invalid 'task_id'. Only alphanumeric, dash, and underscore are allowed."}), 400
# 2. 准备目录
# os.path.join 会自动根据当前系统(Windows)处理路径分隔符
data_dir = os.path.join("data", task_id)
lib_dir = "lib/module"
# 自动创建不存在的目录 (exist_ok=True 避免目录已存在时报错)
os.makedirs(data_dir, exist_ok=True)
os.makedirs(lib_dir, exist_ok=True)
saved_files = []
# 3. 遍历并保存文件
# request.files 是一个字典,通过 .getlist('file') 获取所有上传的文件(假设表单键名为 file
# 为了兼容不同的前端传法,我们遍历 request.files 中的所有文件对象
for key in request.files:
for file in request.files.getlist(key):
if file and file.filename:
# 获取文件后缀,转换为小写
ext = os.path.splitext(file.filename)[1].lower()
if ext == '.txt':
# .txt 储存至 data/{task_id}/goods.txt
save_path = os.path.join(data_dir, "goods.txt")
file.save(save_path)
saved_files.append({"original_name": file.filename, "saved_path": save_path})
elif ext == '.py':
# .py 储存至 lib/{task_id}.py
save_path = os.path.join(lib_dir, f"{task_id}.py")
file.save(save_path)
saved_files.append({"original_name": file.filename, "saved_path": save_path})
else:
# 忽略不支持的文件格式
pass
if not saved_files:
return jsonify({"status": "error", "message": "No valid .txt or .py files were uploaded."}), 400
return jsonify({
"status": "success",
"message": "Files uploaded successfully.",
"data": saved_files
}), 200
@app.route('/api/get_status', methods=['GET'])
def get_status():
params = request.args
task_id = params.get('task_id')
progress = 0
status = 0
if os.path.exists(f"data/{task_id}/progress"):
with open(f"data/{task_id}/progress", "r", encoding="utf-8") as f:
progress = f.read().strip()
if os.path.exists(f"data/{task_id}/status"):
with open(f"data/{task_id}/status", "r", encoding="utf-8") as f:
status = f.read().strip()
return jsonify({
"status": "success",
"message": f"Task '{task_id}' is ready.",
"data": {
"task_id": task_id,
"progress": progress,
"status": status
}
}), 200
if __name__ == '__main__':
# 解决 jsonify 返回中文字符时变成 Unicode 编码的问题
app.config['JSON_AS_ASCII'] = False
app.run(host='127.0.0.1', port=5000, debug=True)