aboutsummaryrefslogtreecommitdiffstats
path: root/tiddlers/plugin-files-api-route.tid
diff options
context:
space:
mode:
Diffstat (limited to 'tiddlers/plugin-files-api-route.tid')
-rw-r--r--tiddlers/plugin-files-api-route.tid132
1 files changed, 132 insertions, 0 deletions
diff --git a/tiddlers/plugin-files-api-route.tid b/tiddlers/plugin-files-api-route.tid
new file mode 100644
index 0000000..6236926
--- /dev/null
+++ b/tiddlers/plugin-files-api-route.tid
@@ -0,0 +1,132 @@
+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"
+ );
+ });
+};