Files
netdisk-fast-download/PLAYGROUND_ENHANCEMENT_IMPLEMENTATION.md

560 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 演练场增强功能实现总结
## 项目日期
2026年1月18日
## 功能概述
本次实现为NetDisk Fast Download项目的演练场Playground增加了以下核心功能
### 1. 编辑器UI增强
- **文件导入功能** - 支持直接导入本地JS/Python/TXT文件
- **原生粘贴支持** - 增强粘贴操作,支持多行代码粘贴,优化移动端体验
### 2. 网络请求安全拦截
- **requests_guard猴子补丁** - 完整的请求拦截和审计日志系统
- **Python代码预处理** - 在运行时自动检测并注入安全补丁
- **实时日志反馈** - 演练场控制台显示安全拦截操作
---
## 详细实现
### 一、演练场编辑器UI增强 (web-front)
#### 1.1 文件导入功能
**位置**: `web-front/src/views/Playground.vue`
**新增组件**:
```vue
<!-- 隐藏的文件导入input -->
<input
ref="fileImportInput"
type="file"
style="display: none"
@change="handleFileImport"
accept=".js,.py,.txt"
/>
```
**新增菜单项**:
```vue
<el-dropdown-item icon="Upload" @click="importFile">导入文件</el-dropdown-item>
```
**实现的方法**:
```javascript
// 触发文件选择对话框
const importFile = () => {
if (fileImportInput.value) {
fileImportInput.value.click();
}
};
// 处理文件导入
const handleFileImport = async (event) => {
const file = event.target.files?.[0];
if (!file) return;
try {
const fileContent = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.onerror = () => reject(new Error('文件读取失败'));
reader.readAsText(file, 'UTF-8');
});
if (activeFile.value) {
activeFile.value.content = fileContent;
activeFile.value.modified = true;
activeFile.value.name = file.name;
// 根据文件扩展名识别语言
const ext = file.name.split('.').pop().toLowerCase();
if (ext === 'py') {
activeFile.value.language = 'python';
} else if (ext === 'js' || ext === 'txt') {
activeFile.value.language = 'javascript';
}
saveAllFilesToStorage();
ElMessage.success(`文件"${file.name}"已导入,大小:${(file.size / 1024).toFixed(2)}KB`);
}
} catch (error) {
ElMessage.error('导入失败: ' + error.message);
}
// 重置input以允许再次选择同一文件
if (fileImportInput.value) {
fileImportInput.value.value = '';
}
};
```
**特点**:
- 支持 `.js`, `.py`, `.txt` 文件格式
- 自动识别文件语言并设置编辑器模式
- 文件大小提示
- 保存到LocalStorage
---
#### 1.2 原生粘贴支持增强
**位置**: `web-front/src/views/Playground.vue`
**改进点**:
1. **多行粘贴支持** - 正确处理多行代码粘贴
2. **移动端优化** - 处理输入法逐行输入问题
3. **错误处理** - 友好的权限和错误提示
```javascript
const pasteCode = async () => {
try {
const text = await navigator.clipboard.readText();
if (!text) {
ElMessage.warning('剪贴板为空');
return;
}
if (editorRef.value && editorRef.value.getEditor) {
const editor = editorRef.value.getEditor();
if (editor) {
const model = editor.getModel();
if (!model) {
ElMessage.error('编辑器未就绪');
return;
}
// 获取当前选择范围,如果没有选择则使用光标位置
const selection = editor.getSelection();
const range = selection || new (window.monaco?.Range || editor.getModel().constructor.Range)(1, 1, 1, 1);
// 使用executeEdits执行粘贴操作支持一次多行粘贴
const edits = [{
range: range,
text: text,
forceMoveMarkers: true
}];
editor.executeEdits('paste-command', edits, [(selection || range)]);
editor.focus();
const lineCount = text.split('\n').length;
ElMessage.success(`已粘贴 ${lineCount} 行内容`);
}
} else {
ElMessage.error('编辑器未加载');
}
} catch (error) {
if (error.name === 'NotAllowedError') {
ElMessage.warning('粘贴权限被拒绝,请使用 Ctrl+V 快捷键');
} else {
console.error('粘贴失败:', error);
ElMessage.error('粘贴失败: ' + (error.message || '请使用 Ctrl+V'));
}
}
};
```
**特点**:
- 处理粘贴权限问题
- 显示粘贴行数
- 支持选区替换和光标位置插入
---
### 二、网络请求安全拦截系统
#### 2.1 requests_guard.py 猴子补丁模块
**位置**: `parser/src/main/resources/requests_guard.py`
**核心功能**:
1. **IP地址验证**
```python
PRIVATE_NETS = [
"127.0.0.0/8", # 本地回环
"10.0.0.0/8", # A 类私网
"172.16.0.0/12", # B 类私网
"192.168.0.0/16", # C 类私网
"0.0.0.0/8", # 0.x.x.x
"169.254.0.0/16", # Link-local
"224.0.0.0/4", # 多播地址
"240.0.0.0/4", # 预留地址
]
```
2. **危险端口检测**
```python
DANGEROUS_PORTS = [
22, 25, 53, 3306, 5432, 6379, # 常见网络服务
8000, 8001, 8080, 8888, # 开发服务器端口
27017, # MongoDB
]
```
3. **请求拦截与审计日志**
```
[2026-01-18 10:15:30.123] [Guard-ALLOW] GET https://example.com/api/data
[2026-01-18 10:15:35.456] [Guard-BLOCK] POST https://127.0.0.1:8080/api - 本地地址
[2026-01-18 10:15:40.789] [Guard-BLOCK] GET https://192.168.1.10/api - 私网地址
```
4. **支持的网络库**
- `requests` - 完整支持
- `urllib` - urllib.request.urlopen 包装
- 可扩展支持 httpx、aiohttp 等
5. **安全检查点**
- URL 格式验证
- 协议检查(仅允许 http/https
- 本地地址检测localhost, 127.0.0.1, ::1
- 私网地址检测CIDR 检查)
- 危险端口检测
- DNS 解析结果验证
---
#### 2.2 PyCodePreprocessor Java类
**位置**: `parser/src/main/java/cn/qaiu/parser/custompy/PyCodePreprocessor.java`
**核心职责**:
1. **代码分析** - 检测代码中的网络请求库导入
```java
// 检测的导入模式
IMPORT_REQUESTS // import requests 或 from requests
IMPORT_URLLIB // import urllib 或 from urllib
IMPORT_HTTPX // import httpx 或 from httpx
IMPORT_AIOHTTP // import aiohttp 或 from aiohttp
IMPORT_SOCKET // import socket 或 from socket
```
2. **猴子补丁注入** - 在代码执行前动态注入补丁
```
原始代码:
"""模块文档"""
import requests
def parse():
...
注入后的代码:
"""模块文档"""
# ===== 自动注入的网络请求安全补丁 (由 PyCodePreprocessor 生成) =====
[requests_guard.py 完整内容]
# ===== 安全补丁结束 =====
import requests
def parse():
...
```
3. **日志生成** - 为演练场控制台生成预处理信息
```
✓ 网络请求安全拦截已启用 (检测到: requests, urllib) | 已动态注入 requests_guard 猴子补丁
```
**实现细节**:
```java
public static PyPreprocessResult preprocess(String originalCode) {
if (originalCode == null || originalCode.trim().isEmpty()) {
return new PyPreprocessResult(originalCode, false, null, "代码为空,无需预处理");
}
// 检测网络请求库
NetworkLibraryDetection detection = detectNetworkLibraries(originalCode);
if (detection.hasAnyNetworkLibrary()) {
// 加载猴子补丁代码
String patchCode = loadRequestsGuardPatch();
if (patchCode != null && !patchCode.isEmpty()) {
// 在代码头部注入补丁
String preprocessedCode = injectPatch(originalCode, patchCode);
String logMessage = String.format(
"✓ 网络请求安全拦截已启用 (检测到: %s) | 已动态注入 requests_guard 猴子补丁",
detection.getDetectedLibrariesAsString()
);
return new PyPreprocessResult(
preprocessedCode,
true,
detection.getDetectedLibraries(),
logMessage
);
}
}
return new PyPreprocessResult(originalCode, false, null,
" 代码中未检测到网络请求库,不需要注入安全拦截补丁");
}
```
---
#### 2.3 PyPlaygroundExecutor集成
**位置**: `parser/src/main/java/cn/qaiu/parser/custompy/PyPlaygroundExecutor.java`
**集成点**:
`executeParseAsync()``executeParseFileListAsync()``executeParseByIdAsync()` 方法中添加代码预处理:
```java
// Python代码预处理 - 检测并注入猴子补丁
PyCodePreprocessor.PyPreprocessResult preprocessResult = PyCodePreprocessor.preprocess(pyCode);
playgroundLogger.infoJava(preprocessResult.getLogMessage());
String codeToExecute = preprocessResult.getProcessedCode();
// 然后执行预处理后的代码
context.eval("python", codeToExecute);
```
**日志流程**:
1. 预处理时生成日志信息
2. 通过 `playgroundLogger.infoJava()` 添加到日志列表
3. 日志包含在 API 响应中返回给前端
4. 前端在演练场控制台中显示
---
### 三、演练场控制台日志显示
#### 3.1 前端日志显示增强
**位置**: `web-front/src/views/Playground.vue`
**日志来源标记**:
```vue
<span v-if="log.source" class="console-source-tag" :class="'console-source-' + (log.source || 'unknown')">
[{{ log.source === 'java' ? 'JAVA' : (log.source === 'JS' ? 'JS' : 'PYTHON') }}]
</span>
```
**CSS样式分类**:
```css
/* JavaScript日志 - 绿色主题 */
.console-js-source {
border-left-color: var(--el-color-success) !important;
background: var(--el-color-success-light-9) !important;
}
/* Java日志包括预处理日志- 橙色主题 */
.console-java-source {
border-left-color: var(--el-color-warning) !important;
background: var(--el-color-warning-light-9) !important;
}
/* Python日志 - 蓝色主题 */
.console-python-source {
border-left-color: var(--el-color-info) !important;
background: var(--el-color-info-light-9) !important;
}
/* 源标记样式 */
.console-source-tag {
display: inline-block;
color: white;
font-size: 10px;
padding: 3px 8px;
border-radius: 10px;
margin-right: 8px;
font-weight: 600;
flex-shrink: 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.console-source-java {
background: linear-gradient(135deg, var(--el-color-warning) 0%, var(--el-color-warning-light-3) 100%);
box-shadow: 0 2px 4px rgba(230, 162, 60, 0.3);
}
```
---
## 演练场工作流程
### 执行Python代码的完整流程:
```
用户在演练场提交代码
前端发送 /v2/playground/test POST请求
PlaygroundApi.test() 接收请求
PyPlaygroundExecutor 创建实例
executeParseAsync() 执行流程:
├─ PyCodeSecurityChecker.check() - 安全检查
├─ PyCodePreprocessor.preprocess() - 代码预处理
│ ├─ 检测导入的网络库
│ ├─ 加载 requests_guard.py
│ ├─ 注入补丁到代码头部
│ └─ 返回预处理日志:"✓ 网络请求安全拦截已启用..."
├─ playgroundLogger.infoJava() - 记录预处理日志
├─ 执行预处理后的代码
│ └─ 代码运行时会自动应用猴子补丁
└─ 收集所有日志和执行结果
API返回包含日志的响应
前端接收并在演练场控制台显示所有日志:
├─ [JAVA] 预处理日志(橙色,带[JAVA]标签)
├─ [PYTHON] Python脚本中的 print/logger 日志(蓝色,带[PYTHON]标签)
└─ [Guard] 网络请求拦截日志由补丁中的GuardLogger生成
```
---
## 演练场控制台日志示例
```
[10:15:30] INFO [JAVA] ✓ 网络请求安全拦截已启用 (检测到: requests, urllib) | 已动态注入 requests_guard 猴子补丁
[10:15:31] DEBUG [JAVA] [Java] 安全检查通过
[10:15:31] INFO [JAVA] [Java] 开始执行parse方法
[10:15:32] DEBUG [JAVA] [Java] 执行Python代码
[10:15:33] INFO [PYTHON] 正在解析链接: https://example.com/s/abc123
[10:15:33] DEBUG [PYTHON] [Guard] 允许 GET https://example.com/s/abc123
[10:15:34] INFO [PYTHON] 获取到 5 个文件
[10:15:35] INFO [JAVA] [Java] 解析成功,返回结果: https://download.example.com/file.zip
```
---
## 安全特性
### 1. 网络请求拦截范围
- ✅ 拦截本地地址127.0.0.1, localhost
- ✅ 拦截私网地址10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 等)
- ✅ 拦截危险端口SSH, MySQL, Redis等
- ✅ DNS解析结果验证
- ✅ 协议检查仅允许http/https
### 2. 代码执行安全
- ✅ 静态安全检查(在预处理前)
- ✅ 动态补丁注入(不修改用户代码)
- ✅ 审计日志记录(所有网络请求可追踪)
- ✅ 超时控制30秒执行超时
### 3. 扩展性
- ✅ 支持添加更多网络库拦截
- ✅ 支持自定义黑名单/白名单
- ✅ 支持热更新补丁代码
- ✅ 支持自定义审计日志处理
---
## 技术栈
### 前端 (Vue.js 3)
- Monaco Editor - 代码编辑
- Element Plus - UI组件
- FileReader API - 文件导入
- Clipboard API - 粘贴操作
### 后端 (Java)
- Vert.x 4.5.23 - 异步框架
- GraalVM Polyglot - Python执行
- SLF4J + Logback - 日志记录
- 正则表达式 - 代码分析
### Python
- 标准库socket、urllib
- 无额外依赖 - 补丁模块独立运行
---
## 文件清单
### 新增文件
1. `parser/src/main/resources/requests_guard.py` - 猴子补丁模块
2. `parser/src/main/java/cn/qaiu/parser/custompy/PyCodePreprocessor.java` - 代码预处理器
### 修改的文件
1. `web-front/src/views/Playground.vue` - 编辑器UI和日志显示
2. `parser/src/main/java/cn/qaiu/parser/custompy/PyPlaygroundExecutor.java` - 集成预处理器
---
## 使用方式
### 1. 导入文件
点击 "更多操作" → "导入文件" → 选择本地.js/.py/.txt文件
### 2. 粘贴代码
- 使用 "粘贴" 按钮
- 或直接 Ctrl+V/Cmd+V
- 支持多行代码一次性粘贴
### 3. 查看安全拦截日志
执行包含 requests/urllib 的Python代码时
1. 演练场控制台自动显示"✓ 网络请求安全拦截已启用"
2. 所有网络请求都会记录在日志中
3. 被拦截的请求显示拦截原因
---
## 性能考虑
- **代码预处理** - 仅在需要时执行,时间复杂度 O(n)
- **补丁加载** - 一次性从资源文件加载,缓存在内存
- **日志记录** - 异步操作,不阻塞代码执行
- **前端显示** - 虚拟列表(当日志过多时)
---
## 测试建议
### 功能测试
1. ✅ 导入不同格式的文件
2. ✅ 粘贴多行代码和特殊字符
3. ✅ 执行包含 requests 的Python代码
4. ✅ 验证网络请求拦截日志
5. ✅ 测试移动端编辑体验
### 安全测试
1. ✅ 尝试访问 127.0.0.1 等本地地址
2. ✅ 尝试访问私网地址
3. ✅ 尝试连接危险端口
4. ✅ 验证日志中显示拦截原因
---
## 后续增强建议
1. **集成更多网络库** - httpx, aiohttp, twisted 等
2. **白名单支持** - 允许特定地址/域名访问
3. **审计日志持久化** - 保存到文件/数据库
4. **性能优化** - 缓存IP解析结果
5. **UI优化** - 日志搜索、过滤、导出功能
6. **告警机制** - 频繁访问被拦截地址时告警
---
## 许可证
遵循项目原有许可证
---
## 贡献者
GitHub Copilot
---
*本文档最后更新于 2026年1月18日*