// GoTunnel File Manager Plugin v2.0.0 // 提供完整的 Web 文件管理界面 function metadata() { return { name: "file-manager", version: "2.0.0", description: "Web 文件管理器,提供远程文件浏览、上传、下载和管理功能", author: "GoTunnel Official", type: "app", run_at: "client" }; } var rootPath = "."; function start() { rootPath = config("root_path") || "."; log("File Manager started, root: " + rootPath); } function stop() { log("File Manager stopped"); } function handleConn(conn) { http.serve(conn, handleRequest); } // ============================================================================ // 请求路由 // ============================================================================ function handleRequest(req) { var path = req.path; var method = req.method; // 静态页面 if (path === "/" || path === "/index.html") { return { status: 200, contentType: "text/html", body: getIndexHTML() }; } // API 路由 if (path === "/api/list" && method === "GET") { return apiList(req); } if (path === "/api/stat" && method === "GET") { return apiStat(req); } if (path === "/api/read" && method === "POST") { return apiRead(req); } if (path === "/api/write" && method === "POST") { return apiWrite(req); } if (path === "/api/mkdir" && method === "POST") { return apiMkdir(req); } if (path === "/api/delete" && method === "POST") { return apiDelete(req); } if (path === "/api/rename" && method === "POST") { return apiRename(req); } if (path === "/api/download" && method === "GET") { return apiDownload(req); } return jsonError(404, "Not Found"); } // ============================================================================ // API 处理函数 // ============================================================================ function apiList(req) { var dir = getQueryParam(req.path, "path") || "/"; var fullPath = resolvePath(dir); if (!fullPath) { return jsonError(403, "Access denied"); } var result = fs.readDir(fullPath); if (result.error) { return jsonError(500, result.error); } var entries = []; for (var i = 0; i < result.entries.length; i++) { var e = result.entries[i]; entries.push({ name: e.name, isDir: e.isDir, size: e.size || 0 }); } // 排序:文件夹在前,然后按名称 entries.sort(function (a, b) { if (a.isDir !== b.isDir) return a.isDir ? -1 : 1; return a.name.localeCompare(b.name); }); return jsonSuccess({ path: dir, entries: entries }); } function apiStat(req) { var path = getQueryParam(req.path, "path") || "/"; var fullPath = resolvePath(path); if (!fullPath) { return jsonError(403, "Access denied"); } var result = fs.stat(fullPath); if (result.error) { return jsonError(404, result.error); } return jsonSuccess({ name: result.name, size: result.size, isDir: result.isDir, modTime: result.modTime }); } function apiRead(req) { var body = parseBody(req.body); var path = body.path; if (!path) { return jsonError(400, "path required"); } var fullPath = resolvePath(path); if (!fullPath) { return jsonError(403, "Access denied"); } var result = fs.readFile(fullPath); if (result.error) { return jsonError(500, result.error); } return jsonSuccess({ content: result.data }); } function apiWrite(req) { var body = parseBody(req.body); var path = body.path; var content = body.content; if (!path) { return jsonError(400, "path required"); } var fullPath = resolvePath(path); if (!fullPath) { return jsonError(403, "Access denied"); } var result = fs.writeFile(fullPath, content || ""); if (!result.ok) { return jsonError(500, result.error); } return jsonSuccess({ message: "File saved" }); } function apiMkdir(req) { var body = parseBody(req.body); var path = body.path; if (!path) { return jsonError(400, "path required"); } var fullPath = resolvePath(path); if (!fullPath) { return jsonError(403, "Access denied"); } var result = fs.mkdir(fullPath); if (!result.ok) { return jsonError(500, result.error); } return jsonSuccess({ message: "Directory created" }); } function apiDelete(req) { var body = parseBody(req.body); var path = body.path; if (!path) { return jsonError(400, "path required"); } var fullPath = resolvePath(path); if (!fullPath) { return jsonError(403, "Access denied"); } var result = fs.remove(fullPath); if (!result.ok) { return jsonError(500, result.error); } return jsonSuccess({ message: "Deleted" }); } function apiRename(req) { var body = parseBody(req.body); var oldPath = body.path; var newPath = body.newPath; if (!oldPath || !newPath) { return jsonError(400, "path and newPath required"); } var fullOldPath = resolvePath(oldPath); var fullNewPath = resolvePath(newPath); if (!fullOldPath || !fullNewPath) { return jsonError(403, "Access denied"); } // 读取旧文件 var stat = fs.stat(fullOldPath); if (stat.error) { return jsonError(404, stat.error); } if (stat.isDir) { return jsonError(400, "Cannot rename directories"); } var content = fs.readFile(fullOldPath); if (content.error) { return jsonError(500, content.error); } // 写入新位置 var writeResult = fs.writeFile(fullNewPath, content.data); if (!writeResult.ok) { return jsonError(500, writeResult.error); } // 删除旧文件 fs.remove(fullOldPath); return jsonSuccess({ message: "Renamed" }); } function apiDownload(req) { var path = getQueryParam(req.path, "path"); if (!path) { return jsonError(400, "path required"); } var fullPath = resolvePath(path); if (!fullPath) { return jsonError(403, "Access denied"); } var result = fs.readFile(fullPath); if (result.error) { return jsonError(404, result.error); } var filename = path.split("/").pop() || "download"; return { status: 200, contentType: "application/octet-stream", body: result.data }; } // ============================================================================ // 工具函数 // ============================================================================ function resolvePath(path) { // 规范化路径 path = path || "/"; // 移除开头的斜杠 while (path.charAt(0) === "/") { path = path.substring(1); } // 检查路径穿越攻击 if (path.indexOf("..") !== -1) { return null; } // 拼接根路径 if (path === "") { return rootPath; } return rootPath + "/" + path; } function getQueryParam(path, key) { var idx = path.indexOf("?"); if (idx === -1) return ""; var query = path.substring(idx + 1); var pairs = query.split("&"); for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].split("="); if (pair[0] === key) { return decodeURIComponent(pair[1] || ""); } } return ""; } function parseBody(body) { try { return JSON.parse(body || "{}"); } catch (e) { return {}; } } function jsonSuccess(data) { return { status: 200, contentType: "application/json", body: http.json({ success: true, data: data }) }; } function jsonError(status, message) { return { status: status, contentType: "application/json", body: http.json({ success: false, error: message }) }; } // ============================================================================ // Web UI - HTML/CSS/JavaScript // ============================================================================ function getIndexHTML() { return '\n\ \n\ \n\ \n\ \n\ File Manager\n\ \n\ \n\ \n\
\n\
\n\

File Manager

\n\ \n\
\n\
\n\ \n\ \n\ \n\
\n\
\n\
加载中...
\n\
\n\
\n\
\n\ \n\ \n\ '; }