title: $:/plugins/local/files-api/get-files.js type: application/javascript module-type: route /*\ title: $:/plugins/local/files-api/get-files.js type: application/javascript module-type: route GET /api/files \*/ "use strict"; var fs = require("fs"), path = require("path"); exports.methods = ["GET"]; exports.path = /^\/api\/files\/?$/; exports.info = { priority: 100 }; function encodeFilePath(filename) { return filename .split(path.sep) .map(encodeURIComponent) .join("/"); } async function scanDirectory(rootDirectory,directory,prefix) { var entries, files = []; try { entries = await fs.promises.readdir(directory,{ withFileTypes: true }); } catch(error) { if(error.code === "ENOENT") { return []; } throw error; } for(var index = 0; index < entries.length; index++) { var entry = entries[index]; // 不公开隐藏文件。 if(entry.name.charAt(0) === ".") { continue; } var absolutePath = path.join(directory,entry.name); // 忽略符号链接,避免扫描到 files/ 目录之外。 if(entry.isDirectory()) { var childFiles = await scanDirectory( rootDirectory, absolutePath, path.join(prefix,entry.name) ); files.push.apply(files,childFiles); continue; } if(!entry.isFile()) { continue; } var relativePath = path.join(prefix,entry.name), extension = path.extname(entry.name).toLowerCase(), extensionInfo = $tw.config.fileExtensionInfo[extension], stats; try { stats = await fs.promises.stat(absolutePath); } catch(error) { // 文件可能在扫描过程中被删除。 continue; } files.push({ name: entry.name, path: relativePath.split(path.sep).join("/"), url: (prefix === "" ? "" : "") + encodeFilePath(relativePath), type: extensionInfo ? extensionInfo.type : "application/octet-stream", size: stats.size, modified: stats.mtime.toISOString() }); } return files; } exports.handler = function(request,response,state) { var rootDirectory = path.resolve(state.boot.wikiPath,"files"), pathPrefix = state.pathPrefix || ""; scanDirectory(rootDirectory,rootDirectory,"") .then(function(files) { files.sort(function(left,right) { return left.path.localeCompare(right.path); }); files.forEach(function(file) { file.url = pathPrefix + "/files/" + file.url; }); state.sendResponse( 200, {"Content-Type": "application/json; charset=utf-8"}, JSON.stringify(files), "utf8" ); }) .catch(function(error) { console.error("Unable to list files directory:",error); state.sendResponse( 500, {"Content-Type": "application/json; charset=utf-8"}, JSON.stringify({ error: "Unable to list files directory" }), "utf8" ); }); };