diff --git a/TYPESCRIPT_IMPLEMENTATION_SUMMARY_CN.md b/TYPESCRIPT_IMPLEMENTATION_SUMMARY_CN.md deleted file mode 100644 index e5720aa..0000000 --- a/TYPESCRIPT_IMPLEMENTATION_SUMMARY_CN.md +++ /dev/null @@ -1,293 +0,0 @@ -# TypeScript编译器集成 - 实现总结 - -## 概述 - -成功为JavaScript解析器演练场添加了完整的TypeScript支持。用户现在可以使用现代TypeScript语法编写解析器代码,系统会自动编译为ES5并在后端执行。 - -## 实现范围 - -### ✅ 前端实现 - -1. **TypeScript编译器集成** - - 添加 `typescript` npm 包依赖 - - 创建 `tsCompiler.js` 编译器工具类 - - 支持所有标准 TypeScript 特性 - - 编译目标:ES5(与后端Nashorn引擎兼容) - -2. **用户界面增强** - - 工具栏语言选择器(JavaScript ⟷ TypeScript) - - 实时编译错误提示 - - TypeScript 示例模板(包含 async/await) - - 语言偏好本地存储 - -3. **编译逻辑** - ``` - 用户输入TS代码 → 自动编译为ES5 → 发送到后端执行 - ``` - -### ✅ 后端实现 - -1. **数据库模型** - - 新表:`playground_typescript_code` - - 存储原始 TypeScript 代码 - - 存储编译后的 ES5 代码 - - 通过 `parserId` 关联到 `playground_parser` - -2. **API端点** - - `POST /v2/playground/typescript` - 保存TS代码 - - `GET /v2/playground/typescript/:parserId` - 获取TS代码 - - `PUT /v2/playground/typescript/:parserId` - 更新TS代码 - -3. **数据库服务** - - `DbService` 新增 TypeScript 相关方法 - - `DbServiceImpl` 实现具体的数据库操作 - - 支持自动建表 - -### ✅ 文档 - -1. **用户指南** (`TYPESCRIPT_PLAYGROUND_GUIDE.md`) - - 快速开始教程 - - TypeScript 特性说明 - - API 参考 - - 最佳实践 - - 故障排除 - -2. **代码示例** - - JavaScript 示例(ES5) - - TypeScript 示例(包含类型注解和 async/await) - -## 架构设计 - -``` -┌─────────────────────────────────────────────┐ -│ 浏览器前端 (Vue 3) │ -├─────────────────────────────────────────────┤ -│ 1. 用户编写 TypeScript 代码 │ -│ 2. TypeScript 编译器编译为 ES5 │ -│ 3. 显示编译错误(如有) │ -│ 4. 发送 ES5 代码到后端 │ -└─────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────┐ -│ 后端服务器 (Java + Vert.x) │ -├─────────────────────────────────────────────┤ -│ 1. 接收 ES5 代码 │ -│ 2. 注入 fetch-runtime.js (已实现) │ -│ 3. Nashorn 引擎执行 │ -│ 4. 返回执行结果 │ -└─────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────┐ -│ 数据库 (SQLite) │ -├─────────────────────────────────────────────┤ -│ playground_parser (ES5代码) │ -│ playground_typescript_code (TS源代码) │ -└─────────────────────────────────────────────┘ -``` - -## 技术细节 - -### TypeScript 编译配置 - -```javascript -{ - target: 'ES5', // 目标ES5(Nashorn兼容) - module: 'None', // 不使用模块系统 - noEmitOnError: true, // 有错误时不生成代码 - downlevelIteration: true, // 支持迭代器降级 - esModuleInterop: true, // ES模块互操作 - lib: ['es5', 'dom'] // 类型库 -} -``` - -### 支持的 TypeScript 特性 - -- ✅ 类型注解 (Type Annotations) -- ✅ 接口 (Interfaces) -- ✅ 类型别名 (Type Aliases) -- ✅ 枚举 (Enums) -- ✅ 泛型 (Generics) -- ✅ async/await → Promise 转换 -- ✅ 箭头函数 -- ✅ 模板字符串 -- ✅ 解构赋值 -- ✅ 可选链 (Optional Chaining) -- ✅ 空值合并 (Nullish Coalescing) - -### 代码示例对比 - -#### 输入 (TypeScript) -```typescript -async function parse( - shareLinkInfo: any, - http: any, - logger: any -): Promise { - const url: string = shareLinkInfo.getShareUrl(); - logger.info(`开始解析: ${url}`); - - const response = await fetch(url); - const html: string = await response.text(); - - return html.match(/url="([^"]+)"/)?.[1] || ""; -} -``` - -#### 输出 (ES5) -```javascript -function parse(shareLinkInfo, http, logger) { - return __awaiter(this, void 0, void 0, function () { - var url, response, html, _a; - return __generator(this, function (_b) { - switch (_b.label) { - case 0: - url = shareLinkInfo.getShareUrl(); - logger.info("开始解析: " + url); - return [4, fetch(url)]; - case 1: - response = _b.sent(); - return [4, response.text()]; - case 2: - html = _b.sent(); - return [2, ((_a = html.match(/url="([^"]+)"/)) === null || _a === void 0 ? void 0 : _a[1]) || ""]; - } - }); - }); -} -``` - -## 代码质量改进 - -基于代码审查反馈,进行了以下改进: - -1. **编译器配置优化** - - ✅ `noEmitOnError: true` - 防止执行有错误的代码 - -2. **代码可维护性** - - ✅ 使用常量替代魔术字符串 - - ✅ 添加 `LANGUAGE` 常量对象 - -3. **用户体验优化** - - ✅ 优先使用显式语言选择 - - ✅ TypeScript语法检测作为辅助提示 - - ✅ 清晰的错误消息 - -4. **代码清理** - - ✅ 移除无关的生成文件 - -## 测试结果 - -### 构建测试 -- ✅ Maven 编译:成功 -- ✅ npm 构建:成功(预期的大小警告) -- ✅ TypeScript 编译:正常工作 -- ✅ 数据库模型:有效 - -### 功能测试(需手动验证) -- [ ] UI 语言选择器 -- [ ] TypeScript 编译 -- [ ] 数据库表自动创建 -- [ ] API 端点 -- [ ] 发布工作流(TS → 数据库 → ES5执行) -- [ ] 错误处理 - -## 安全性 - -- ✅ 输入验证(代码长度限制:128KB) -- ✅ SQL注入防护(参数化查询) -- ✅ IP日志记录(审计追踪) -- ✅ 继承现有SSRF防护 -- ✅ 无新安全漏洞 - -## 数据库结构 - -### playground_typescript_code 表 - -| 字段 | 类型 | 说明 | -|------|------|------| -| id | BIGINT | 主键 | -| parser_id | BIGINT | 关联解析器ID(外键) | -| ts_code | TEXT | TypeScript源代码 | -| es5_code | TEXT | 编译后ES5代码 | -| compile_errors | VARCHAR(2000) | 编译错误 | -| compiler_version | VARCHAR(32) | 编译器版本 | -| compile_options | VARCHAR(1000) | 编译选项(JSON) | -| create_time | DATETIME | 创建时间 | -| update_time | DATETIME | 更新时间 | -| is_valid | BOOLEAN | 编译是否成功 | -| ip | VARCHAR(64) | 创建者IP | - -### 关系 -- `playground_typescript_code.parser_id` → `playground_parser.id` (外键) -- 一对一关系:一个解析器对应一个TypeScript代码记录 - -## 文件清单 - -### 新增文件 (3) -1. `web-front/src/utils/tsCompiler.js` - TS编译器工具 -2. `web-service/src/main/java/cn/qaiu/lz/web/model/PlaygroundTypeScriptCode.java` - 数据模型 -3. `parser/doc/TYPESCRIPT_PLAYGROUND_GUIDE.md` - 用户文档 - -### 修改文件 (5) -1. `web-front/package.json` - 添加typescript依赖 -2. `web-front/src/views/Playground.vue` - UI和编译逻辑 -3. `web-front/src/utils/playgroundApi.js` - TS API方法 -4. `web-service/src/main/java/cn/qaiu/lz/web/service/DbService.java` - 接口定义 -5. `web-service/src/main/java/cn/qaiu/lz/web/service/impl/DbServiceImpl.java` - 实现 -6. `web-service/src/main/java/cn/qaiu/lz/web/controller/PlaygroundApi.java` - API端点 - -## 未来改进计划 - -- [ ] 显示编译后的ES5代码预览 -- [ ] 添加专用的编译错误面板 -- [ ] 提供完整的TypeScript类型定义文件(.d.ts) -- [ ] 支持代码自动补全 -- [ ] TypeScript代码片段库 -- [ ] 更多编译选项配置 - -## 使用方法 - -### 快速开始 - -1. **选择语言** - - 点击工具栏中的"TypeScript"按钮 - -2. **编写代码** - - 点击"加载示例"查看TypeScript示例 - - 编写自己的TypeScript代码 - -3. **运行测试** - - 点击"运行"按钮 - - 查看编译结果和执行结果 - -4. **发布脚本** - - 测试通过后点击"发布脚本" - - 系统自动保存TS源码和ES5编译结果 - -## 兼容性 - -- ✅ 与现有JavaScript功能完全兼容 -- ✅ 不影响现有解析器 -- ✅ 向后兼容 -- ✅ 无破坏性更改 - -## 性能 - -- **编译时间**:几毫秒到几百毫秒(取决于代码大小) -- **运行时开销**:无(编译在前端完成) -- **存储开销**:额外存储TypeScript源码(TEXT类型) - -## 总结 - -成功实现了完整的TypeScript支持,包括: -- ✅ 前端编译器集成 -- ✅ 后端数据存储 -- ✅ API端点 -- ✅ 用户界面 -- ✅ 完整文档 -- ✅ 代码质量优化 -- ✅ 安全验证 - -**状态:生产就绪 ✅** - -该功能已经过全面测试,所有代码审查问题已解决,可以安全地部署到生产环境。 diff --git a/parser/doc/TYPESCRIPT_ES5_IMPLEMENTATION.md b/parser/doc/TYPESCRIPT_ES5_IMPLEMENTATION.md deleted file mode 100644 index ea2a1b3..0000000 --- a/parser/doc/TYPESCRIPT_ES5_IMPLEMENTATION.md +++ /dev/null @@ -1,378 +0,0 @@ -# TypeScript/ES6+ 浏览器编译与Fetch API实现 - -## 项目概述 - -本实现提供了**纯前端TypeScript编译 + 后端ES5引擎 + Fetch API适配**的完整解决方案,允许用户在浏览器中编写TypeScript/ES6+代码(包括async/await),编译为ES5后在后端Nashorn JavaScript引擎中执行。 - -## 架构图 - -``` -┌─────────────────────────────────────────────────────────┐ -│ 浏览器端 (计划中) │ -├─────────────────────────────────────────────────────────┤ -│ 用户编写 TypeScript/ES6+ 代码 (async/await) │ -│ ↓ │ -│ TypeScript.js 浏览器内编译为 ES5 │ -│ ↓ │ -│ 生成的 ES5 代码发送到后端 │ -└─────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────┐ -│ 后端 (已实现) │ -├─────────────────────────────────────────────────────────┤ -│ 1. 接收 ES5 代码 │ -│ 2. 注入 fetch-runtime.js (Promise + fetch polyfill) │ -│ 3. 注入 JavaFetch 桥接对象 │ -│ 4. Nashorn 引擎执行 ES5 代码 │ -│ 5. fetch() → JavaFetch → JsHttpClient → Vert.x │ -└─────────────────────────────────────────────────────────┘ -``` - -## 已实现功能 - -### ✅ 后端 ES5 执行环境 - -#### 1. Promise Polyfill (完整的 Promise/A+ 实现) - -文件: `parser/src/main/resources/fetch-runtime.js` - -**功能特性:** -- ✅ `new Promise(executor)` 构造函数 -- ✅ `promise.then(onFulfilled, onRejected)` 链式调用 -- ✅ `promise.catch(onRejected)` 错误处理 -- ✅ `promise.finally(onFinally)` 清理操作 -- ✅ `Promise.resolve(value)` 静态方法 -- ✅ `Promise.reject(reason)` 静态方法 -- ✅ `Promise.all(promises)` 并行等待 -- ✅ `Promise.race(promises)` 竞速等待 - -**实现细节:** -- 纯 ES5 语法,无ES6+特性依赖 -- 使用 `setTimeout(fn, 0)` 实现异步执行 -- 支持 Promise 链式调用和错误传播 -- 自动处理 Promise 嵌套和展开 - -#### 2. Fetch API Polyfill (标准 fetch 接口) - -文件: `parser/src/main/resources/fetch-runtime.js` - -**支持的 HTTP 方法:** -- ✅ GET -- ✅ POST -- ✅ PUT -- ✅ DELETE -- ✅ PATCH -- ✅ HEAD - -**Request 选项支持:** -```javascript -fetch(url, { - method: 'POST', // HTTP 方法 - headers: { // 请求头 - 'Content-Type': 'application/json', - 'Authorization': 'Bearer token' - }, - body: JSON.stringify({ // 请求体 - key: 'value' - }) -}) -``` - -**Response 对象方法:** -- ✅ `response.text()` - 获取文本响应 (返回 Promise) -- ✅ `response.json()` - 解析 JSON 响应 (返回 Promise) -- ✅ `response.arrayBuffer()` - 获取字节数组 -- ✅ `response.status` - HTTP 状态码 -- ✅ `response.ok` - 请求是否成功 (2xx) -- ✅ `response.statusText` - 状态文本 -- ✅ `response.headers.get(name)` - 获取响应头 - -#### 3. Java 桥接层 - -文件: `parser/src/main/java/cn/qaiu/parser/customjs/JsFetchBridge.java` - -**核心功能:** -- 接收 JavaScript fetch API 调用 -- 转换为 JsHttpClient 调用 -- 处理请求头、请求体、HTTP 方法 -- 返回 JsHttpResponse 对象 -- 自动继承现有的 SSRF 防护机制 - -**代码示例:** -```java -public class JsFetchBridge { - private final JsHttpClient httpClient; - - public JsHttpResponse fetch(String url, Map options) { - // 解析 method、headers、body - // 调用 httpClient.get/post/put/delete/patch - // 返回 JsHttpResponse - } -} -``` - -#### 4. 自动注入机制 - -文件: -- `parser/src/main/java/cn/qaiu/parser/customjs/JsParserExecutor.java` -- `parser/src/main/java/cn/qaiu/parser/customjs/JsPlaygroundExecutor.java` - -**注入流程:** -1. 创建 JavaScript 引擎 -2. 注入 JavaFetch 桥接对象 -3. 加载 fetch-runtime.js -4. 执行用户 JavaScript 代码 - -**代码示例:** -```java -// 注入 JavaFetch -engine.put("JavaFetch", new JsFetchBridge(httpClient)); - -// 加载 fetch runtime -String fetchRuntime = loadFetchRuntime(); -engine.eval(fetchRuntime); - -// 现在 JavaScript 环境中可以使用 Promise 和 fetch -``` - -## 使用示例 - -### ES5 风格 (当前可用) - -```javascript -function parse(shareLinkInfo, http, logger) { - logger.info("开始解析"); - - // 使用 fetch API - fetch("https://api.example.com/data") - .then(function(response) { - logger.info("状态码: " + response.status); - return response.json(); - }) - .then(function(data) { - logger.info("数据: " + JSON.stringify(data)); - return data.downloadUrl; - }) - .catch(function(error) { - logger.error("错误: " + error.message); - throw error; - }); - - // 或者继续使用传统的 http 对象 - var response = http.get("https://api.example.com/data"); - return response.body(); -} -``` - -### TypeScript/ES6+ 风格 (需前端编译) - -用户在浏览器中编写: - -```typescript -async function parse( - shareLinkInfo: ShareLinkInfo, - http: JsHttpClient, - logger: JsLogger -): Promise { - try { - logger.info("开始解析"); - - // 使用标准 fetch API - const response = await fetch("https://api.example.com/data"); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const data = await response.json(); - logger.info(`下载链接: ${data.downloadUrl}`); - - return data.downloadUrl; - - } catch (error) { - logger.error(`解析失败: ${error.message}`); - throw error; - } -} -``` - -浏览器编译为 ES5 后: - -```javascript -function parse(shareLinkInfo, http, logger) { - return __awaiter(this, void 0, void 0, function() { - var response, data, error_1; - return __generator(this, function(_a) { - switch(_a.label) { - case 0: - _a.trys.push([0, 3, , 4]); - logger.info("开始解析"); - return [4, fetch("https://api.example.com/data")]; - case 1: - response = _a.sent(); - if (!response.ok) { - throw new Error("HTTP " + response.status + ": " + response.statusText); - } - return [4, response.json()]; - case 2: - data = _a.sent(); - logger.info("下载链接: " + data.downloadUrl); - return [2, data.downloadUrl]; - case 3: - error_1 = _a.sent(); - logger.error("解析失败: " + error_1.message); - throw error_1; - case 4: return [2]; - } - }); - }); -} -``` - -## 文件结构 - -``` -parser/ -├── src/main/ -│ ├── java/cn/qaiu/parser/customjs/ -│ │ ├── JsFetchBridge.java # Java 桥接层 -│ │ ├── JsParserExecutor.java # 解析器执行器 (已更新) -│ │ └── JsPlaygroundExecutor.java # 演练场执行器 (已更新) -│ └── resources/ -│ ├── fetch-runtime.js # Promise + fetch polyfill -│ └── custom-parsers/ -│ └── fetch-demo.js # Fetch 示例解析器 -├── src/test/java/cn/qaiu/parser/customjs/ -│ └── JsFetchBridgeTest.java # 单元测试 -└── doc/ - └── TYPESCRIPT_FETCH_GUIDE.md # 详细使用指南 -``` - -## 测试验证 - -### 运行测试 - -```bash -# 编译项目 -mvn clean compile -pl parser - -# 运行所有测试 -mvn test -pl parser - -# 运行 fetch 测试 -mvn test -pl parser -Dtest=JsFetchBridgeTest -``` - -### 测试内容 - -文件: `parser/src/test/java/cn/qaiu/parser/customjs/JsFetchBridgeTest.java` - -1. **testFetchPolyfillLoaded** - 验证 Promise 和 fetch 是否正确注入 -2. **testPromiseBasicUsage** - 验证 Promise 基本功能 -3. **示例解析器** - `fetch-demo.js` 展示完整用法 - -## 兼容性说明 - -### 支持的特性 - -- ✅ Promise/A+ 完整实现 -- ✅ Fetch API 标准接口 -- ✅ async/await (通过 TypeScript 编译) -- ✅ 所有 HTTP 方法 -- ✅ Request headers 和 body -- ✅ Response 解析 (text, json, arrayBuffer) -- ✅ 错误处理和 Promise 链 -- ✅ 与现有 http 对象共存 - -### 不支持的特性 - -- ❌ Blob 对象 (使用 arrayBuffer 替代) -- ❌ FormData 对象 (使用简单对象替代) -- ❌ Request/Response 构造函数 -- ❌ Streams API -- ❌ Service Worker 相关 API -- ❌ AbortController (取消请求) - -## 安全性 - -### SSRF 防护 - -继承自 `JsHttpClient` 的 SSRF 防护: -- ✅ 拦截内网 IP (127.0.0.1, 10.x.x.x, 192.168.x.x 等) -- ✅ 拦截云服务元数据 API (169.254.169.254 等) -- ✅ DNS 解析检查 -- ✅ 危险域名黑名单 - -### 沙箱隔离 - -- ✅ SecurityClassFilter 限制类访问 -- ✅ 禁用 Java 对象直接访问 -- ✅ 限制文件系统操作 - -## 性能优化 - -1. **Fetch runtime 缓存** - - 首次加载后缓存在静态变量 - - 避免重复读取文件 - -2. **Promise 异步执行** - - 使用 setTimeout(0) 实现非阻塞 - - 避免阻塞 JavaScript 主线程 - -3. **工作线程池** - - JsParserExecutor: Vert.x 工作线程池 - - JsPlaygroundExecutor: 独立线程池 - - 避免阻塞 Event Loop - -## 前端 TypeScript 编译 (计划中) - -### 待实现步骤 - -1. **添加 TypeScript 编译器** - ```bash - cd web-front - npm install typescript - ``` - -2. **创建编译工具** - ```javascript - // web-front/src/utils/tsCompiler.js - import * as ts from 'typescript'; - - export function compileToES5(sourceCode) { - return ts.transpileModule(sourceCode, { - compilerOptions: { - target: ts.ScriptTarget.ES5, - module: ts.ModuleKind.None, - lib: ['es5', 'dom'] - } - }); - } - ``` - -3. **更新 Playground UI** - - 添加语言选择器 (JavaScript / TypeScript) - - 编译前先检查语法错误 - - 显示编译后的 ES5 代码 (可选) - -## 相关文档 - -- [详细使用指南](parser/doc/TYPESCRIPT_FETCH_GUIDE.md) -- [JavaScript 解析器开发指南](parser/doc/JAVASCRIPT_PARSER_GUIDE.md) -- [自定义解析器扩展指南](parser/doc/CUSTOM_PARSER_GUIDE.md) - -## 总结 - -本实现成功提供了: - -1. **无需 Node 环境** - 纯浏览器编译 + Java 后端执行 -2. **标准 API** - 使用标准 fetch 和 Promise API -3. **向后兼容** - 现有 http 对象仍然可用 -4. **安全可靠** - SSRF 防护和沙箱隔离 -5. **易于使用** - 简单的 API,无学习成本 - -用户可以用现代 JavaScript/TypeScript 编写代码,自动编译为 ES5 后在后端安全执行,同时享受 fetch API 的便利性。 - -## 许可证 - -本项目遵循主项目的许可证。 diff --git a/parser/doc/TYPESCRIPT_FETCH_GUIDE.md b/parser/doc/TYPESCRIPT_FETCH_GUIDE.md deleted file mode 100644 index 35dccf5..0000000 --- a/parser/doc/TYPESCRIPT_FETCH_GUIDE.md +++ /dev/null @@ -1,451 +0,0 @@ -# 浏览器TypeScript编译和Fetch API支持指南 - -## 概述 - -本项目实现了**纯前端TypeScript编译 + 后端ES5引擎 + Fetch API适配**的完整方案,允许用户在浏览器中编写TypeScript/ES6+代码,编译为ES5后在后端JavaScript引擎中执行。 - -## 架构设计 - -### 1. 浏览器端(前端编译) - -``` -用户编写TS/ES6+代码 - ↓ -TypeScript.js (浏览器内编译) - ↓ -ES5 JavaScript代码 - ↓ -发送到后端执行 -``` - -### 2. 后端(ES5执行环境) - -``` -接收ES5代码 - ↓ -注入fetch polyfill + Promise - ↓ -注入JavaFetch桥接对象 - ↓ -Nashorn引擎执行ES5代码 - ↓ -fetch() 调用 → JavaFetch → JsHttpClient → Vert.x HTTP Client -``` - -## 已实现的功能 - -### ✅ 后端支持 - -1. **Promise Polyfill** (`fetch-runtime.js`) - - 完整的Promise/A+实现 - - 支持 `then`、`catch`、`finally` - - 支持 `Promise.all`、`Promise.race` - - 支持 `Promise.resolve`、`Promise.reject` - -2. **Fetch API Polyfill** (`fetch-runtime.js`) - - 标准fetch接口实现 - - 支持所有HTTP方法(GET、POST、PUT、DELETE、PATCH) - - 支持headers、body等选项 - - Response对象支持: - - `text()` - 获取文本响应 - - `json()` - 解析JSON响应 - - `arrayBuffer()` - 获取字节数组 - - `status` - HTTP状态码 - - `ok` - 请求成功标志 - - `headers` - 响应头访问 - -3. **Java桥接** (`JsFetchBridge.java`) - - 将fetch调用转换为JsHttpClient调用 - - 自动处理请求头、请求体 - - 支持代理配置 - - 安全的SSRF防护 - -4. **自动注入** (`JsParserExecutor.java` & `JsPlaygroundExecutor.java`) - - 在JavaScript引擎初始化时自动注入fetch runtime - - 提供`JavaFetch`全局对象 - - 与现有http对象共存 - -## 使用示例 - -### ES5风格(当前支持) - -```javascript -function parse(shareLinkInfo, http, logger) { - // 使用fetch API - fetch("https://api.example.com/data") - .then(function(response) { - return response.json(); - }) - .then(function(data) { - logger.info("数据: " + JSON.stringify(data)); - }) - .catch(function(error) { - logger.error("错误: " + error.message); - }); - - // 或者使用传统的http对象 - var response = http.get("https://api.example.com/data"); - return response.body(); -} -``` - -### TypeScript风格(需要前端编译) - -用户在浏览器中编写: - -```typescript -async function parse(shareLinkInfo: ShareLinkInfo, http: JsHttpClient, logger: JsLogger): Promise { - try { - // 使用标准fetch API - const response = await fetch("https://api.example.com/data"); - const data = await response.json(); - - logger.info(`获取到数据: ${data.downloadUrl}`); - return data.downloadUrl; - } catch (error) { - logger.error(`解析失败: ${error.message}`); - throw error; - } -} -``` - -浏览器内编译后的ES5代码(简化示例): - -```javascript -function parse(shareLinkInfo, http, logger) { - return __awaiter(this, void 0, void 0, function() { - var response, data; - return __generator(this, function(_a) { - switch(_a.label) { - case 0: - return [4, fetch("https://api.example.com/data")]; - case 1: - response = _a.sent(); - return [4, response.json()]; - case 2: - data = _a.sent(); - logger.info("获取到数据: " + data.downloadUrl); - return [2, data.downloadUrl]; - } - }); - }); -} -``` - -## 前端TypeScript编译(待实现) - -### 计划实现步骤 - -#### 1. 添加TypeScript编译器 - -在前端项目中添加`typescript.js`: - -```bash -# 下载TypeScript编译器浏览器版本 -cd webroot/static -wget https://cdn.jsdelivr.net/npm/typescript@latest/lib/typescript.js -``` - -或者在Vue项目中: - -```bash -npm install typescript -``` - -#### 2. 创建编译工具类 - -`web-front/src/utils/tsCompiler.js`: - -```javascript -import * as ts from 'typescript'; - -export function compileToES5(sourceCode, fileName = 'script.ts') { - const result = ts.transpileModule(sourceCode, { - compilerOptions: { - target: ts.ScriptTarget.ES5, - module: ts.ModuleKind.None, - lib: ['es5', 'dom'], - experimentalDecorators: false, - emitDecoratorMetadata: false, - downlevelIteration: true - }, - fileName: fileName - }); - - return { - js: result.outputText, - diagnostics: result.diagnostics, - sourceMap: result.sourceMapText - }; -} -``` - -#### 3. 更新Playground组件 - -在`Playground.vue`中添加编译选项: - -```vue - - - -``` - -## Fetch Runtime详解 - -### Promise实现特性 - -```javascript -// 基本用法 -var promise = new SimplePromise(function(resolve, reject) { - setTimeout(function() { - resolve("成功"); - }, 1000); -}); - -promise.then(function(value) { - console.log(value); // "成功" -}); - -// 链式调用 -promise - .then(function(value) { - return value + " - 第一步"; - }) - .then(function(value) { - return value + " - 第二步"; - }) - .catch(function(error) { - console.error(error); - }) - .finally(function() { - console.log("完成"); - }); -``` - -### Fetch API特性 - -```javascript -// GET请求 -fetch("https://api.example.com/data") - .then(function(response) { - console.log("状态码:", response.status); - console.log("成功:", response.ok); - return response.json(); - }) - .then(function(data) { - console.log("数据:", data); - }); - -// POST请求 -fetch("https://api.example.com/submit", { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ key: "value" }) -}) - .then(function(response) { - return response.json(); - }) - .then(function(data) { - console.log("响应:", data); - }); -``` - -## 兼容性说明 - -### 支持的特性 - -- ✅ Promise/A+ 完整实现 -- ✅ Fetch API 标准接口 -- ✅ async/await(编译后) -- ✅ 所有HTTP方法(GET、POST、PUT、DELETE、PATCH) -- ✅ Request headers配置 -- ✅ Request body(string、JSON、FormData) -- ✅ Response.text()、Response.json() -- ✅ 与现有http对象共存 - -### 不支持的特性 - -- ❌ Blob对象(返回字节数组替代) -- ❌ FormData对象(使用简单对象替代) -- ❌ Request/Response对象构造函数 -- ❌ Streams API -- ❌ Service Worker相关API - -## 测试验证 - -### 1. 创建测试解析器 - -参考 `parser/src/main/resources/custom-parsers/fetch-demo.js` - -### 2. 测试步骤 - -```bash -# 1. 编译项目 -mvn clean package -DskipTests - -# 2. 运行服务 -java -jar web-service/target/netdisk-fast-download.jar - -# 3. 访问演练场 -浏览器打开: http://localhost:6401/playground - -# 4. 加载fetch-demo.js并测试 -``` - -### 3. 验证fetch功能 - -在演练场中运行: - -```javascript -function parse(shareLinkInfo, http, logger) { - logger.info("测试fetch API"); - - var result = null; - fetch("https://httpbin.org/get") - .then(function(response) { - logger.info("状态码: " + response.status); - return response.json(); - }) - .then(function(data) { - logger.info("响应: " + JSON.stringify(data)); - result = "SUCCESS"; - }) - .catch(function(error) { - logger.error("错误: " + error.message); - }); - - // 等待完成 - var timeout = 5000; - var start = Date.now(); - while (result === null && (Date.now() - start) < timeout) { - java.lang.Thread.sleep(10); - } - - return result || "https://example.com/download"; -} -``` - -## 安全性 - -### SSRF防护 - -JsHttpClient已实现SSRF防护: -- 拦截内网IP访问(127.0.0.1、10.x.x.x、192.168.x.x等) -- 拦截云服务元数据API(169.254.169.254等) -- DNS解析检查 - -### 沙箱隔离 - -- JavaScript引擎使用SecurityClassFilter -- 禁用Java对象访问 -- 限制文件系统访问 - -## 性能优化 - -1. **Fetch runtime缓存** - - 首次加载后缓存在静态变量中 - - 避免重复读取资源文件 - -2. **Promise异步执行** - - 使用setTimeout(0)实现异步 - - 避免阻塞主线程 - -3. **工作线程池** - - JsParserExecutor使用Vert.x工作线程池 - - JsPlaygroundExecutor使用独立线程池 - -## 相关文件 - -### 后端代码 -- `parser/src/main/resources/fetch-runtime.js` - Fetch和Promise polyfill -- `parser/src/main/java/cn/qaiu/parser/customjs/JsFetchBridge.java` - Java桥接层 -- `parser/src/main/java/cn/qaiu/parser/customjs/JsParserExecutor.java` - 解析器执行器 -- `parser/src/main/java/cn/qaiu/parser/customjs/JsPlaygroundExecutor.java` - 演练场执行器 - -### 示例代码 -- `parser/src/main/resources/custom-parsers/fetch-demo.js` - Fetch API演示 - -### 前端代码(待实现) -- `web-front/src/utils/tsCompiler.js` - TypeScript编译工具 -- `web-front/src/views/Playground.vue` - 演练场界面 - -## 下一步计划 - -1. ✅ 实现后端fetch polyfill -2. ✅ 实现Promise polyfill -3. ✅ 集成到JsParserExecutor -4. ⏳ 前端添加TypeScript编译器 -5. ⏳ 更新Playground UI支持TS/ES6+ -6. ⏳ 添加Monaco编辑器类型提示 -7. ⏳ 编写更多示例和文档 - -## 总结 - -通过这个方案,我们实现了: -1. **无需Node环境** - 纯浏览器编译 + Java后端执行 -2. **标准API** - 使用标准fetch和Promise API -3. **向后兼容** - 现有http对象仍然可用 -4. **安全可靠** - SSRF防护和沙箱隔离 -5. **易于使用** - 简单的API,无需学习成本 - -用户可以在浏览器中用现代JavaScript/TypeScript编写代码,自动编译为ES5后在后端安全执行,同时享受fetch API的便利性。 diff --git a/parser/doc/TYPESCRIPT_PLAYGROUND_GUIDE.md b/parser/doc/TYPESCRIPT_PLAYGROUND_GUIDE.md deleted file mode 100644 index a78e828..0000000 --- a/parser/doc/TYPESCRIPT_PLAYGROUND_GUIDE.md +++ /dev/null @@ -1,483 +0,0 @@ -# TypeScript 支持文档 - -## 概述 - -演练场现在支持 TypeScript!您可以使用现代 TypeScript 语法编写解析器代码,系统会自动将其编译为 ES5 并在后端执行。 - -## 功能特性 - -### 🎯 核心功能 - -- ✅ **TypeScript 编译器集成**:内置 TypeScript 编译器,实时将 TS 代码编译为 ES5 -- ✅ **语言选择器**:在演练场工具栏轻松切换 JavaScript 和 TypeScript -- ✅ **编译错误提示**:友好的编译错误提示和建议 -- ✅ **双代码存储**:同时保存原始 TypeScript 代码和编译后的 ES5 代码 -- ✅ **无缝集成**:与现有演练场功能完全兼容 - -### 📝 TypeScript 特性支持 - -支持所有标准 TypeScript 特性,包括但不限于: - -- 类型注解(Type Annotations) -- 接口(Interfaces) -- 类型别名(Type Aliases) -- 枚举(Enums) -- 泛型(Generics) -- async/await(编译为 Promise) -- 箭头函数 -- 模板字符串 -- 解构赋值 -- 可选链(Optional Chaining) -- 空值合并(Nullish Coalescing) - -## 快速开始 - -### 1. 选择语言 - -在演练场工具栏中,点击 **JavaScript** 或 **TypeScript** 按钮选择您要使用的语言。 - -### 2. 编写代码 - -选择 TypeScript 后,点击"加载示例"按钮可以加载 TypeScript 示例代码。 - -#### TypeScript 示例 - -```typescript -// ==UserScript== -// @name TypeScript示例解析器 -// @type ts_example_parser -// @displayName TypeScript示例网盘 -// @description 使用TypeScript实现的示例解析器 -// @match https?://example\.com/s/(?\w+) -// @author yourname -// @version 1.0.0 -// ==/UserScript== - -/** - * 解析单个文件下载链接 - */ -async function parse( - shareLinkInfo: any, - http: any, - logger: any -): Promise { - const url: string = shareLinkInfo.getShareUrl(); - logger.info(`开始解析: ${url}`); - - // 使用fetch API (已在后端实现polyfill) - try { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`请求失败: ${response.status}`); - } - - const html: string = await response.text(); - - // 解析逻辑 - const match = html.match(/download-url="([^"]+)"/); - if (match) { - return match[1]; - } - - return "https://example.com/download/file.zip"; - } catch (error: any) { - logger.error(`解析失败: ${error.message}`); - throw error; - } -} -``` - -### 3. 运行测试 - -点击"运行"按钮(或按 Ctrl+Enter)。系统会: - -1. 自动检测代码是否为 TypeScript -2. 将 TypeScript 编译为 ES5 -3. 显示编译结果(成功/失败) -4. 如果编译成功,使用 ES5 代码执行测试 -5. 显示测试结果 - -### 4. 发布解析器 - -编译成功后,点击"发布脚本"即可保存解析器。系统会自动: - -- 保存原始 TypeScript 代码到 `playground_typescript_code` 表 -- 保存编译后的 ES5 代码到 `playground_parser` 表 -- 通过 `parserId` 关联两者 - -## 编译选项 - -TypeScript 编译器使用以下配置: - -```javascript -{ - target: 'ES5', // 目标版本:ES5 - module: 'None', // 不使用模块系统 - lib: ['es5', 'dom'], // 包含ES5和DOM类型定义 - removeComments: false, // 保留注释 - downlevelIteration: true, // 支持ES5迭代器降级 - esModuleInterop: true // 启用ES模块互操作性 -} -``` - -## 类型定义 - -### 可用的 API 对象 - -虽然 TypeScript 支持类型注解,但由于后端运行时环境的限制,建议使用 `any` 类型: - -```typescript -function parse( - shareLinkInfo: any, // 分享链接信息 - http: any, // HTTP客户端 - logger: any // 日志对象 -): Promise { - // ... -} -``` - -### 常用方法 - -#### shareLinkInfo 对象 - -```typescript -shareLinkInfo.getShareUrl(): string // 获取分享URL -shareLinkInfo.getShareKey(): string // 获取分享Key -shareLinkInfo.getSharePassword(): string // 获取分享密码 -shareLinkInfo.getOtherParam(key: string): any // 获取其他参数 -``` - -#### logger 对象 - -```typescript -logger.info(message: string): void // 记录信息日志 -logger.debug(message: string): void // 记录调试日志 -logger.error(message: string): void // 记录错误日志 -logger.warn(message: string): void // 记录警告日志 -``` - -#### fetch API(后端 Polyfill) - -```typescript -async function fetchData(url: string): Promise { - const response = await fetch(url, { - method: 'GET', - headers: { - 'User-Agent': 'Mozilla/5.0...', - 'Content-Type': 'application/json' - } - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}`); - } - - const data = await response.json(); - return data; -} -``` - -## 最佳实践 - -### 1. 使用类型注解 - -虽然后端不强制类型检查,但类型注解可以提高代码可读性: - -```typescript -function parseFileList( - shareLinkInfo: any, - http: any, - logger: any -): Promise> { - // 实现... -} -``` - -### 2. 利用 async/await - -TypeScript 的 async/await 会编译为 Promise,后端已实现 Promise polyfill: - -```typescript -async function parse( - shareLinkInfo: any, - http: any, - logger: any -): Promise { - try { - const response = await fetch(url); - const data = await response.json(); - return data.downloadUrl; - } catch (error) { - logger.error(`错误: ${error.message}`); - throw error; - } -} -``` - -### 3. 使用模板字符串 - -模板字符串让代码更清晰: - -```typescript -logger.info(`开始解析: ${url}, 密码: ${pwd}`); -const apiUrl = `https://api.example.com/file/${fileId}`; -``` - -### 4. 错误处理 - -使用类型化的错误处理: - -```typescript -try { - const result = await parseUrl(url); - return result; -} catch (error: any) { - logger.error(`解析失败: ${error.message}`); - throw new Error(`无法解析链接: ${url}`); -} -``` - -## 编译错误处理 - -### 常见编译错误 - -#### 1. 类型不匹配 - -```typescript -// ❌ 错误 -const count: number = "123"; - -// ✅ 正确 -const count: number = 123; -``` - -#### 2. 缺少返回值 - -```typescript -// ❌ 错误 -function parse(shareLinkInfo: any): string { - const url = shareLinkInfo.getShareUrl(); - // 缺少 return -} - -// ✅ 正确 -function parse(shareLinkInfo: any): string { - const url = shareLinkInfo.getShareUrl(); - return url; -} -``` - -#### 3. 使用未声明的变量 - -```typescript -// ❌ 错误 -function parse() { - console.log(unknownVariable); -} - -// ✅ 正确 -function parse() { - const knownVariable = "value"; - console.log(knownVariable); -} -``` - -### 查看编译错误 - -编译失败时,系统会显示详细的错误信息,包括: - -- 错误类型(Error/Warning) -- 错误位置(行号、列号) -- 错误代码(TS错误代码) -- 错误描述 - -## 数据库结构 - -### playground_typescript_code 表 - -存储 TypeScript 源代码的表结构: - -| 字段 | 类型 | 说明 | -|------|------|------| -| id | BIGINT | 主键,自增 | -| parser_id | BIGINT | 关联的解析器ID(外键) | -| ts_code | TEXT | TypeScript原始代码 | -| es5_code | TEXT | 编译后的ES5代码 | -| compile_errors | VARCHAR(2000) | 编译错误信息 | -| compiler_version | VARCHAR(32) | 编译器版本 | -| compile_options | VARCHAR(1000) | 编译选项(JSON格式) | -| create_time | DATETIME | 创建时间 | -| update_time | DATETIME | 更新时间 | -| is_valid | BOOLEAN | 编译是否成功 | -| ip | VARCHAR(64) | 创建者IP | - -### 与 playground_parser 表的关系 - -- `playground_typescript_code.parser_id` 外键关联到 `playground_parser.id` -- 一个解析器(parser)可以有一个对应的 TypeScript 代码记录 -- 编译后的 ES5 代码存储在 `playground_parser.js_code` 字段中 - -## API 端点 - -### 保存 TypeScript 代码 - -```http -POST /v2/playground/typescript -Content-Type: application/json - -{ - "parserId": 1, - "tsCode": "...", - "es5Code": "...", - "compileErrors": null, - "compilerVersion": "5.x", - "compileOptions": "{}", - "isValid": true -} -``` - -### 获取 TypeScript 代码 - -```http -GET /v2/playground/typescript/:parserId -``` - -### 更新 TypeScript 代码 - -```http -PUT /v2/playground/typescript/:parserId -Content-Type: application/json - -{ - "tsCode": "...", - "es5Code": "...", - "compileErrors": null, - "compilerVersion": "5.x", - "compileOptions": "{}", - "isValid": true -} -``` - -## 迁移指南 - -### 从 JavaScript 迁移到 TypeScript - -1. **添加类型注解**: - ```typescript - // JavaScript - function parse(shareLinkInfo, http, logger) { - var url = shareLinkInfo.getShareUrl(); - return url; - } - - // TypeScript - function parse( - shareLinkInfo: any, - http: any, - logger: any - ): string { - const url: string = shareLinkInfo.getShareUrl(); - return url; - } - ``` - -2. **使用 const/let 替代 var**: - ```typescript - // JavaScript - var url = "https://example.com"; - var count = 0; - - // TypeScript - const url: string = "https://example.com"; - let count: number = 0; - ``` - -3. **使用模板字符串**: - ```typescript - // JavaScript - var message = "URL: " + url + ", Count: " + count; - - // TypeScript - const message: string = `URL: ${url}, Count: ${count}`; - ``` - -4. **使用 async/await**: - ```typescript - // JavaScript - function parse(shareLinkInfo, http, logger) { - return new Promise(function(resolve, reject) { - fetch(url).then(function(response) { - resolve(response.text()); - }).catch(reject); - }); - } - - // TypeScript - async function parse( - shareLinkInfo: any, - http: any, - logger: any - ): Promise { - const response = await fetch(url); - return await response.text(); - } - ``` - -## 常见问题 - -### Q: TypeScript 代码会在哪里编译? - -A: TypeScript 代码在浏览器前端编译为 ES5,然后发送到后端执行。这确保了后端始终执行标准的 ES5 代码。 - -### Q: 编译需要多长时间? - -A: 通常在几毫秒到几百毫秒之间,取决于代码大小和复杂度。 - -### Q: 可以使用 npm 包吗? - -A: 不可以。目前不支持 import/require 外部模块。所有代码必须自包含。 - -### Q: 类型检查严格吗? - -A: 不严格。编译器配置为允许隐式 any 类型,不进行严格的 null 检查。主要目的是支持现代语法,而非严格的类型安全。 - -### Q: 编译后的代码可以查看吗? - -A: 目前编译后的 ES5 代码存储在数据库中,但 UI 中暂未提供预览功能。这是未来的增强计划。 - -### Q: 原有的 JavaScript 代码会受影响吗? - -A: 不会。JavaScript 和 TypeScript 模式完全独立,互不影响。 - -## 故障排除 - -### 编译失败 - -1. **检查语法**:确保 TypeScript 语法正确 -2. **查看错误信息**:仔细阅读编译错误提示 -3. **简化代码**:从简单的示例开始,逐步添加功能 -4. **使用示例**:点击"加载示例"查看正确的代码结构 - -### 运行时错误 - -1. **检查 ES5 兼容性**:某些高级特性可能无法完全转换 -2. **验证 API 使用**:确保正确使用 shareLinkInfo、http、logger 等对象 -3. **查看日志**:使用 logger 对象输出调试信息 - -## 未来计划 - -- [ ] 显示编译后的 ES5 代码预览 -- [ ] 添加专用的编译错误面板 -- [ ] 支持更多 TypeScript 配置选项 -- [ ] 提供完整的类型定义文件(.d.ts) -- [ ] 支持代码自动补全和智能提示 -- [ ] 添加 TypeScript 代码片段库 - -## 反馈与支持 - -如遇到问题或有建议,请在 GitHub Issues 中提出: -https://github.com/qaiu/netdisk-fast-download/issues diff --git a/web-service/src/main/java/cn/qaiu/lz/web/controller/PlaygroundApi.java b/web-service/src/main/java/cn/qaiu/lz/web/controller/PlaygroundApi.java index 51c8e8e..0a774f7 100644 --- a/web-service/src/main/java/cn/qaiu/lz/web/controller/PlaygroundApi.java +++ b/web-service/src/main/java/cn/qaiu/lz/web/controller/PlaygroundApi.java @@ -5,7 +5,6 @@ import cn.qaiu.lz.web.config.PlaygroundConfig; import cn.qaiu.lz.web.model.PlaygroundTestResp; import cn.qaiu.lz.web.service.DbService; import cn.qaiu.parser.ParserCreate; -import cn.qaiu.parser.custom.CustomParserConfig; import cn.qaiu.parser.custom.CustomParserRegistry; import cn.qaiu.parser.customjs.JsPlaygroundExecutor; import cn.qaiu.parser.customjs.JsPlaygroundLogger; @@ -636,146 +635,6 @@ public class PlaygroundApi { return dbService.getPlaygroundParserById(id); } - /** - * 保存TypeScript代码及其编译结果 - */ - @RouteMapping(value = "/typescript", method = RouteMethod.POST) - public Future saveTypeScriptCode(RoutingContext ctx) { - // 权限检查 - if (!checkAuth(ctx)) { - return Future.succeededFuture(JsonResult.error("未授权访问").toJsonObject()); - } - - Promise promise = Promise.promise(); - - try { - JsonObject body = ctx.body().asJsonObject(); - Long parserId = body.getLong("parserId"); - String tsCode = body.getString("tsCode"); - String es5Code = body.getString("es5Code"); - String compileErrors = body.getString("compileErrors"); - String compilerVersion = body.getString("compilerVersion"); - String compileOptions = body.getString("compileOptions"); - Boolean isValid = body.getBoolean("isValid", true); - - if (parserId == null) { - promise.complete(JsonResult.error("解析器ID不能为空").toJsonObject()); - return promise.future(); - } - - if (StringUtils.isBlank(tsCode)) { - promise.complete(JsonResult.error("TypeScript代码不能为空").toJsonObject()); - return promise.future(); - } - - if (StringUtils.isBlank(es5Code)) { - promise.complete(JsonResult.error("编译后的ES5代码不能为空").toJsonObject()); - return promise.future(); - } - - // 代码长度验证 - if (tsCode.length() > MAX_CODE_LENGTH || es5Code.length() > MAX_CODE_LENGTH) { - promise.complete(JsonResult.error("代码长度超过限制(最大128KB)").toJsonObject()); - return promise.future(); - } - - JsonObject tsCodeInfo = new JsonObject(); - tsCodeInfo.put("parserId", parserId); - tsCodeInfo.put("tsCode", tsCode); - tsCodeInfo.put("es5Code", es5Code); - tsCodeInfo.put("compileErrors", compileErrors); - tsCodeInfo.put("compilerVersion", compilerVersion); - tsCodeInfo.put("compileOptions", compileOptions); - tsCodeInfo.put("isValid", isValid); - tsCodeInfo.put("ip", getClientIp(ctx.request())); - - dbService.saveTypeScriptCode(tsCodeInfo).onSuccess(result -> { - promise.complete(result); - }).onFailure(e -> { - log.error("保存TypeScript代码失败", e); - promise.complete(JsonResult.error("保存失败: " + e.getMessage()).toJsonObject()); - }); - - } catch (Exception e) { - log.error("解析请求参数失败", e); - promise.complete(JsonResult.error("解析请求参数失败: " + e.getMessage()).toJsonObject()); - } - - return promise.future(); - } - - /** - * 根据parserId获取TypeScript代码 - */ - @RouteMapping(value = "/typescript/:parserId", method = RouteMethod.GET) - public Future getTypeScriptCode(RoutingContext ctx, Long parserId) { - // 权限检查 - if (!checkAuth(ctx)) { - return Future.succeededFuture(JsonResult.error("未授权访问").toJsonObject()); - } - return dbService.getTypeScriptCodeByParserId(parserId); - } - - /** - * 更新TypeScript代码 - */ - @RouteMapping(value = "/typescript/:parserId", method = RouteMethod.PUT) - public Future updateTypeScriptCode(RoutingContext ctx, Long parserId) { - // 权限检查 - if (!checkAuth(ctx)) { - return Future.succeededFuture(JsonResult.error("未授权访问").toJsonObject()); - } - - Promise promise = Promise.promise(); - - try { - JsonObject body = ctx.body().asJsonObject(); - String tsCode = body.getString("tsCode"); - String es5Code = body.getString("es5Code"); - String compileErrors = body.getString("compileErrors"); - String compilerVersion = body.getString("compilerVersion"); - String compileOptions = body.getString("compileOptions"); - Boolean isValid = body.getBoolean("isValid", true); - - if (StringUtils.isBlank(tsCode)) { - promise.complete(JsonResult.error("TypeScript代码不能为空").toJsonObject()); - return promise.future(); - } - - if (StringUtils.isBlank(es5Code)) { - promise.complete(JsonResult.error("编译后的ES5代码不能为空").toJsonObject()); - return promise.future(); - } - - // 代码长度验证 - if (tsCode.length() > MAX_CODE_LENGTH || es5Code.length() > MAX_CODE_LENGTH) { - promise.complete(JsonResult.error("代码长度超过限制(最大128KB)").toJsonObject()); - return promise.future(); - } - - JsonObject tsCodeInfo = new JsonObject(); - tsCodeInfo.put("tsCode", tsCode); - tsCodeInfo.put("es5Code", es5Code); - tsCodeInfo.put("compileErrors", compileErrors); - tsCodeInfo.put("compilerVersion", compilerVersion); - tsCodeInfo.put("compileOptions", compileOptions); - tsCodeInfo.put("isValid", isValid); - - dbService.updateTypeScriptCode(parserId, tsCodeInfo).onSuccess(result -> { - promise.complete(result); - }).onFailure(e -> { - log.error("更新TypeScript代码失败", e); - promise.complete(JsonResult.error("更新失败: " + e.getMessage()).toJsonObject()); - }); - - } catch (Exception e) { - log.error("解析请求参数失败", e); - promise.complete(JsonResult.error("解析请求参数失败: " + e.getMessage()).toJsonObject()); - } - - return promise.future(); - } - /** * 获取客户端IP */ diff --git a/web-service/src/main/java/cn/qaiu/lz/web/model/PlaygroundTypeScriptCode.java b/web-service/src/main/java/cn/qaiu/lz/web/model/PlaygroundTypeScriptCode.java deleted file mode 100644 index 43af4ad..0000000 --- a/web-service/src/main/java/cn/qaiu/lz/web/model/PlaygroundTypeScriptCode.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.qaiu.lz.web.model; - -import cn.qaiu.db.ddl.Constraint; -import cn.qaiu.db.ddl.Length; -import cn.qaiu.db.ddl.Table; -import com.fasterxml.jackson.annotation.JsonFormat; -import lombok.Data; - -import java.util.Date; - -/** - * 演练场TypeScript代码实体 - * 用于保存用户编写的TypeScript源代码 - * 与PlaygroundParser关联,存储原始TS代码和编译后的ES5代码 - */ -@Data -@Table("playground_typescript_code") -public class PlaygroundTypeScriptCode { - - private static final long serialVersionUID = 1L; - - @Constraint(autoIncrement = true, notNull = true) - private Long id; - - @Constraint(notNull = true) - private Long parserId; // 关联的解析器ID(外键) - - @Length(varcharSize = 65535) - @Constraint(notNull = true) - private String tsCode; // TypeScript原始代码 - - @Length(varcharSize = 65535) - @Constraint(notNull = true) - private String es5Code; // 编译后的ES5代码 - - @Length(varcharSize = 2000) - private String compileErrors; // 编译错误信息(如果有) - - @Length(varcharSize = 32) - private String compilerVersion; // 编译器版本 - - @Length(varcharSize = 1000) - private String compileOptions; // 编译选项(JSON格式) - - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") - private Date createTime = new Date(); // 创建时间 - - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") - private Date updateTime; // 更新时间 - - private Boolean isValid = true; // 编译是否成功 - - @Length(varcharSize = 64) - private String ip; // 创建者IP -} diff --git a/web-service/src/main/java/cn/qaiu/lz/web/service/DbService.java b/web-service/src/main/java/cn/qaiu/lz/web/service/DbService.java index 7c8716e..0256d2e 100644 --- a/web-service/src/main/java/cn/qaiu/lz/web/service/DbService.java +++ b/web-service/src/main/java/cn/qaiu/lz/web/service/DbService.java @@ -50,19 +50,4 @@ public interface DbService extends BaseAsyncService { */ Future getPlaygroundParserById(Long id); - /** - * 保存TypeScript代码及其编译结果 - */ - Future saveTypeScriptCode(JsonObject tsCodeInfo); - - /** - * 根据parserId获取TypeScript代码 - */ - Future getTypeScriptCodeByParserId(Long parserId); - - /** - * 更新TypeScript代码 - */ - Future updateTypeScriptCode(Long parserId, JsonObject tsCodeInfo); - } diff --git a/web-service/src/main/java/cn/qaiu/lz/web/service/impl/DbServiceImpl.java b/web-service/src/main/java/cn/qaiu/lz/web/service/impl/DbServiceImpl.java index 845abb8..87aa228 100644 --- a/web-service/src/main/java/cn/qaiu/lz/web/service/impl/DbServiceImpl.java +++ b/web-service/src/main/java/cn/qaiu/lz/web/service/impl/DbServiceImpl.java @@ -265,114 +265,4 @@ public class DbServiceImpl implements DbService { return promise.future(); } - - @Override - public Future saveTypeScriptCode(JsonObject tsCodeInfo) { - JDBCPool client = JDBCPoolInit.instance().getPool(); - Promise promise = Promise.promise(); - - String sql = """ - INSERT INTO playground_typescript_code - (parser_id, ts_code, es5_code, compile_errors, compiler_version, - compile_options, create_time, is_valid, ip) - VALUES (?, ?, ?, ?, ?, ?, NOW(), ?, ?) - """; - - client.preparedQuery(sql) - .execute(Tuple.of( - tsCodeInfo.getLong("parserId"), - tsCodeInfo.getString("tsCode"), - tsCodeInfo.getString("es5Code"), - tsCodeInfo.getString("compileErrors"), - tsCodeInfo.getString("compilerVersion"), - tsCodeInfo.getString("compileOptions"), - tsCodeInfo.getBoolean("isValid", true), - tsCodeInfo.getString("ip") - )) - .onSuccess(res -> { - promise.complete(JsonResult.success("保存TypeScript代码成功").toJsonObject()); - }) - .onFailure(e -> { - log.error("saveTypeScriptCode failed", e); - promise.fail(e); - }); - - return promise.future(); - } - - @Override - public Future getTypeScriptCodeByParserId(Long parserId) { - JDBCPool client = JDBCPoolInit.instance().getPool(); - Promise promise = Promise.promise(); - - String sql = "SELECT * FROM playground_typescript_code WHERE parser_id = ? ORDER BY create_time DESC LIMIT 1"; - - client.preparedQuery(sql) - .execute(Tuple.of(parserId)) - .onSuccess(rows -> { - if (rows.size() > 0) { - Row row = rows.iterator().next(); - JsonObject tsCode = new JsonObject(); - tsCode.put("id", row.getLong("id")); - tsCode.put("parserId", row.getLong("parser_id")); - tsCode.put("tsCode", row.getString("ts_code")); - tsCode.put("es5Code", row.getString("es5_code")); - tsCode.put("compileErrors", row.getString("compile_errors")); - tsCode.put("compilerVersion", row.getString("compiler_version")); - tsCode.put("compileOptions", row.getString("compile_options")); - var createTime = row.getLocalDateTime("create_time"); - if (createTime != null) { - tsCode.put("createTime", createTime.toString().replace("T", " ")); - } - var updateTime = row.getLocalDateTime("update_time"); - if (updateTime != null) { - tsCode.put("updateTime", updateTime.toString().replace("T", " ")); - } - tsCode.put("isValid", row.getBoolean("is_valid")); - tsCode.put("ip", row.getString("ip")); - promise.complete(JsonResult.data(tsCode).toJsonObject()); - } else { - promise.complete(JsonResult.data(null).toJsonObject()); - } - }) - .onFailure(e -> { - log.error("getTypeScriptCodeByParserId failed", e); - promise.fail(e); - }); - - return promise.future(); - } - - @Override - public Future updateTypeScriptCode(Long parserId, JsonObject tsCodeInfo) { - JDBCPool client = JDBCPoolInit.instance().getPool(); - Promise promise = Promise.promise(); - - String sql = """ - UPDATE playground_typescript_code - SET ts_code = ?, es5_code = ?, compile_errors = ?, compiler_version = ?, - compile_options = ?, update_time = NOW(), is_valid = ? - WHERE parser_id = ? - """; - - client.preparedQuery(sql) - .execute(Tuple.of( - tsCodeInfo.getString("tsCode"), - tsCodeInfo.getString("es5Code"), - tsCodeInfo.getString("compileErrors"), - tsCodeInfo.getString("compilerVersion"), - tsCodeInfo.getString("compileOptions"), - tsCodeInfo.getBoolean("isValid", true), - parserId - )) - .onSuccess(res -> { - promise.complete(JsonResult.success("更新TypeScript代码成功").toJsonObject()); - }) - .onFailure(e -> { - log.error("updateTypeScriptCode failed", e); - promise.fail(e); - }); - - return promise.future(); - } }