const API = require("../../lib/API") const AccessControl = require("../../lib/AccessControl") const { BaseStdResponse } = require("../../BaseStdResponse") const db = require("../../plugin/DataBase/db") const axios = require('axios') const { core_url } = require('../../config.json') const path = require('path') const { isBinaryFileSync } = require('isbinaryfile') class SummaryFile extends API { constructor() { super() this.setMethod("GET") this.setPath("/AI/SummaryFile") } // 定义支持的语言后缀 languageExtensions = { "JavaScript": [".js", ".jsx", ".mjs", ".cjs"], "TypeScript": [".ts", ".tsx"], "Python": [".py"], "Java": [".java"], "C": [".c"], "C++": [".cpp", ".cc", ".cxx", ".h", ".hpp"], "C#": [".cs"], "Go": [".go"], "PHP": [".php"], "HTML": [".html", ".htm"], "CSS": [".css"], "SCSS": [".scss", ".sass"], "JSON": [".json"], "YAML": [".yml", ".yaml"], "Markdown": [".md"], "Shell": [".sh"], "Bash": [".bash"], "PowerShell": [".ps1"], "Ruby": [".rb"], "Vue": [".vue"], // Vue文件 "LESS": [".less"], // LESS样式文件 "Sass": [".sass"], // Sass文件 "Haml": [".haml"], // Haml文件 "Elixir": [".ex", ".exs"], // Elixir语言文件 "Erlang": [".erl", ".hrl"], // Erlang语言文件 "Swift": [".swift"], // Swift语言文件 "Kotlin": [".kt", ".kts"], // Kotlin语言文件 "Rust": [".rs"], // Rust语言文件 "Dart": [".dart"], // Dart语言文件 "Lua": [".lua"], // Lua语言文件 "R": [".r"], // R语言文件 "Perl": [".pl"], // Perl语言文件 "SQL": [".sql"], // SQL文件 "Dockerfile": [".dockerfile", "Dockerfile"], // Dockerfile "GraphQL": [".graphql", ".gql"], // GraphQL文件 "JSON5": [".json5"], // JSON5文件 "TOML": [".toml"], // TOML文件 "Text": [".txt", ".text"], "Assembly": [".asm", ".s"], "Objective-C": [".m", ".mm"], "F#": [".fs", ".fsi", ".fsx", ".fsscript"], "Haskell": [".hs", ".lhs"], "OCaml": [".ml", ".mli"], "Nim": [".nim", ".nims"], "Julia": [".jl"], "Fortran": [".f", ".for", ".f90", ".f95"], "COBOL": [".cbl", ".cob"], "VHDL": [".vhd", ".vhdl"], "Verilog": [".v", ".vh"], "Ada": [".adb", ".ads"], "Matlab": [".m"], // 注意 Objective-C 也使用 .m,可根据上下文判断 "Scala": [".scala"], "VB.NET": [".vb"], "Groovy": [".groovy", ".gvy", ".gy", ".gsh"], "Makefile": ["Makefile", "makefile", ".mk"], "Clojure": [".clj", ".cljs", ".cljc", ".edn"], "Common Lisp": [".lisp", ".lsp"], "Scheme": [".scm", ".ss"], "Prolog": [".pl", ".pro", ".P"], "Smalltalk": [".st", ".squeak"], "Tcl": [".tcl"], "Crystal": [".cr"], "Solidity": [".sol"], "Vim script": [".vim"], "ReScript": [".res", ".resi"], "ReasonML": [".re", ".rei"], "Pug": [".pug", ".jade"], "Handlebars": [".hbs", ".handlebars"], "XML": [".xml"], "INI": [".ini", ".cfg", ".conf"], "Log": [".log"], "ENV": [".env"], "ReStructuredText": [".rst"], "AsciiDoc": [".adoc", ".asciidoc"], "Racket": [".rkt"], "Zig": [".zig"], "Haxe": [".hx"], "Dotenv": [".env"], "Config": [".config"], "PlantUML": [".puml", ".plantuml"], "Visual Basic": [".bas", ".vbs"], "Batch": [".bat", ".cmd"], "BibTeX": [".bib"], "TeX/LaTeX": [".tex", ".ltx", ".sty", ".cls"], "Apache": [".htaccess", "httpd.conf"], "NGINX": ["nginx.conf"], "Terraform": [".tf", ".tfvars"], "HCL": [".hcl"], "QML": [".qml"], "Cue": [".cue"], "GDScript": [".gd"], // 用于 Godot 引擎 "ANTLR": [".g4"], "Pascal": [".pas"], "Logtalk": [".lgt"], "Awk": [".awk"], "Sed": [".sed"], "ConfigScript": [".conf", ".cfg"], "YANG": [".yang"], "NetLogo": [".nlogo"] } isBinaryFile(filePath) { try { return isBinaryFileSync(filePath) } catch (err) { return false } } async onRequest(req, res) { let { uuid, session, id, hash, filePath } = req.query if ([uuid, session, id, hash, filePath].some(value => value === '' || value === null || value === undefined)) return res.json({ ...BaseStdResponse.MISSING_PARAMETER }) // 检查 session if (!await AccessControl.checkSession(uuid, session)) return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED }) const ext = path.extname(filePath).toLowerCase() const language = Object.keys(this.languageExtensions).find(lang => this.languageExtensions[lang].includes(ext) ) if (!language) return res.json({ ...BaseStdResponse.ERR, msg: '该文件类型不支持AI分析!' }) let sql = 'SELECT state, path, url FROM repos WHERE create_user = ? AND id = ?' let rows = await db.query(sql, [uuid, id]) if (!rows || rows.length === 0) return res.json({ ...BaseStdResponse.ERR, msg: '未找到仓库' }) if (rows[0].state !== 1 || !rows[0].path) return res.json({ ...BaseStdResponse.ERR, msg: '仓库未成功克隆!' }) try { filePath = path.join(rows[0].path, filePath) if (this.isBinaryFile(filePath)) return res.json({ ...BaseStdResponse.ERR, msg: '该文件类型不支持AI总结!' }) const time = new Date().getTime() sql = 'INSERT INTO file_summary_tasks (repo_id, create_time, create_user, repo_hash, filepath) VALUES (?, ?, ?, ?, ?)' let r = await db.query(sql, [id, time, uuid, hash, filePath]) if (!r || r.affectedRows !== 1) return res.json({ ...BaseStdResponse.ERR, msg: '扫描任务添加失败!' }) res.json({ ...BaseStdResponse.OK }) let endpoint = core_url + '/ai/summaryFile' await axios.post(endpoint, { uuid, repo_url: rows[0].url, task_id: String(r.insertId), file_path: filePath, repo_id: id }) } catch (error) { this.logger.error('添加AI分析任务失败!' + error.stack) return res.json({ ...BaseStdResponse.ERR, msg: '添加AI分析任务失败!' }) } } } module.exports.SummaryFile = SummaryFile