From d35c5e90c994302c445f562d4dc188650fb4ebf4 Mon Sep 17 00:00:00 2001 From: mediabot-pt <295750538+mediabot-pt@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:43:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=84=9A=E6=9C=AC=E4=B8=8B=E8=BD=BD/?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E6=8E=A5=E5=8F=A3=E3=80=81=E5=89=8D=E7=AB=AF?= =?UTF-8?q?Groovy=E7=BC=96=E8=BE=91=E5=99=A8=E3=80=81Groovy=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E8=A1=8C=E5=8F=B7=E6=8F=90=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../par/api/controller/AdminController.java | 46 +++++++++++++++ .../par/api/controller/ConfigController.java | 23 ++++++++ .../par/core/adapter/GroovyScriptEngine.java | 21 ++++++- web/src/api/index.js | 2 + web/src/views/AccessResultCheck.vue | 56 +++++++++++++++---- 5 files changed, 135 insertions(+), 13 deletions(-) diff --git a/par-api/src/main/java/com/par/api/controller/AdminController.java b/par-api/src/main/java/com/par/api/controller/AdminController.java index 5ef89d8..d662698 100644 --- a/par-api/src/main/java/com/par/api/controller/AdminController.java +++ b/par-api/src/main/java/com/par/api/controller/AdminController.java @@ -229,6 +229,52 @@ public class AdminController { return ApiResponse.success(configService.toDTO(config)); } + /** + * 查看指定配置的 Groovy 脚本 + */ + @GetMapping("/configs/{configId}/script") + public ApiResponse> getConfigScript( + @PathVariable Long configId, HttpServletRequest request) { + checkAdmin(request); + SiteConfig config = siteConfigMapper.selectById(configId); + if (config == null) return ApiResponse.error(404, "Config not found"); + String script = null; + if (config.getScriptStoragePath() != null) { + try { script = storageService.read(config.getScriptStoragePath()); } catch (Exception ignored) {} + } + return ApiResponse.success(Map.of("id", config.getId(), "script", script != null ? script : "")); + } + + /** + * 编辑待审核配置的 Groovy 脚本 + */ + @PutMapping("/configs/{configId}/script") + public ApiResponse updateConfigScript( + @PathVariable Long configId, + @RequestBody Map payload, + HttpServletRequest request) { + checkAdmin(request); + SiteConfig config = siteConfigMapper.selectById(configId); + if (config == null) return ApiResponse.error(404, "Config not found"); + if (config.getStatus() != ConfigStatus.PENDING) { + return ApiResponse.error(400, "只能编辑待审核的配置"); + } + + String script = payload.get("script"); + if (script == null) return ApiResponse.error(400, "script is required"); + + String scriptPath = config.getScriptStoragePath(); + if (scriptPath == null || scriptPath.isBlank()) { + scriptPath = String.format("configs/%s/pending/%s.groovy", config.getSiteId(), config.getConfigHash().substring(0, 8)); + config.setScriptStoragePath(scriptPath); + } + storageService.store(scriptPath, script); + siteConfigMapper.updateById(config); + + log.info("Config script updated: id={}, siteId={}", configId, config.getSiteId()); + return ApiResponse.success(configService.toDTO(config)); + } + /** * 设置用户信任等级(禁止修改管理员、禁止提升为管理员) */ diff --git a/par-api/src/main/java/com/par/api/controller/ConfigController.java b/par-api/src/main/java/com/par/api/controller/ConfigController.java index 4e6ea86..21b4073 100644 --- a/par-api/src/main/java/com/par/api/controller/ConfigController.java +++ b/par-api/src/main/java/com/par/api/controller/ConfigController.java @@ -117,6 +117,29 @@ public class ConfigController { .body(content); } + /** + * 下载指定版本 Groovy 脚本(供 mediabot 使用) + */ + @GetMapping("/{siteId}/{version}/script") + public ResponseEntity downloadScript( + @PathVariable("siteId") String siteId, + @PathVariable("version") String version) { + + SiteConfig config = configService.getConfig(siteId, version); + if (config == null || config.getScriptStoragePath() == null) { + return ResponseEntity.notFound().build(); + } + + String script; + try { script = storageService.read(config.getScriptStoragePath()); } + catch (Exception e) { return ResponseEntity.status(500).body("Failed to read script"); } + if (script == null) return ResponseEntity.notFound().build(); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_TYPE, "text/plain;charset=UTF-8") + .body(script); + } + /** * 提交新配置(需 HMAC 鉴权) */ diff --git a/par-core/src/main/java/com/par/core/adapter/GroovyScriptEngine.java b/par-core/src/main/java/com/par/core/adapter/GroovyScriptEngine.java index a8fef11..7045af0 100644 --- a/par-core/src/main/java/com/par/core/adapter/GroovyScriptEngine.java +++ b/par-core/src/main/java/com/par/core/adapter/GroovyScriptEngine.java @@ -41,7 +41,7 @@ public class GroovyScriptEngine { return buildResult(result, scriptResult); } catch (Exception e) { result.put("matched", false); - result.put("message", "Groovy 脚本执行异常: " + e.getMessage()); + result.put("message", extractGroovyError(e)); log.error("Groovy error for url={}", url, e); } return result; @@ -91,6 +91,25 @@ public class GroovyScriptEngine { return result; } + /** 从 Groovy 异常中提取含行号的错误信息 */ + private String extractGroovyError(Exception e) { + StringBuilder sb = new StringBuilder(); + sb.append(e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()); + for (StackTraceElement ste : e.getStackTrace()) { + String cn = ste.getClassName(); + // Groovy 编译后的类名通常是 Script1, Script2 或用户脚本类 + if (cn.startsWith("Script") || cn.contains("$_run_closure") || cn.contains("$__execute")) { + sb.append("\n at line ").append(ste.getLineNumber()); + if (ste.getFileName() != null) sb.append(" (").append(ste.getFileName()).append(")"); + break; + } + } + if (e.getCause() != null && e.getCause() != e) { + sb.append("\nCaused by: ").append(e.getCause().getMessage()); + } + return sb.toString(); + } + public static class JsoupHelper { public Object parse(String html) { return Jsoup.parse(html); } public Object parse(String html, String baseUri) { return Jsoup.parse(html, baseUri); } diff --git a/web/src/api/index.js b/web/src/api/index.js index 0955916..3bc35d3 100644 --- a/web/src/api/index.js +++ b/web/src/api/index.js @@ -79,6 +79,8 @@ export default { pendingBySite: (siteId) => api.get(`/admin/configs/pending/${siteId}`), getContent: (configId) => api.get(`/admin/configs/${configId}/content`), updateContent: (configId, content) => api.put(`/admin/configs/${configId}/content`, { content }), + getScript: (configId) => api.get(`/admin/configs/${configId}/script`), + updateScript: (configId, script) => api.put(`/admin/configs/${configId}/script`, { script }), approve: (configId) => api.post(`/admin/reviews/${configId}/approve`), reject: (configId, comment) => api.post(`/admin/reviews/${configId}/reject`, null, { params: { comment: comment || '' } }), }, diff --git a/web/src/views/AccessResultCheck.vue b/web/src/views/AccessResultCheck.vue index b7e7e77..4d51701 100644 --- a/web/src/views/AccessResultCheck.vue +++ b/web/src/views/AccessResultCheck.vue @@ -48,20 +48,33 @@ - - - -

+    
+    
+      
+        
+          
+          

+        
+ +
加载中...
+ +
+