Skip to content

Commit

Permalink
Add directory index formats with json and rss
Browse files Browse the repository at this point in the history
  • Loading branch information
Arlen22 committed Nov 21, 2021
1 parent d5e5a75 commit 49f0734
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 23 deletions.
Empty file modified src/server.ts
100755 → 100644
Empty file.
24 changes: 24 additions & 0 deletions src/server/generate-directory-rss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { extname } from "path";
import { pathToFileURL } from "url";
import { DirectoryIndexListing, DirectoryIndexOptions } from "./server-types";
import { expandNodeTree, toXML } from "./utils-xml";
import { mime } from "send";

export function generateDirectoryRSS(def: DirectoryIndexListing, opts: DirectoryIndexOptions) {
let rss = {
title: def.path,
description: def.path,
link: def.path,
item: def.entries.map(e => ({
title: e.name,
link:"/" + e.path,
description: e.type === "file" ? mime.lookup(e.name, "") : ("index/" + e.type),
pubDate: new Date(e.modified).toISOString(),
guid: "/" + e.path + "@" + e.modified
}))
}
return '<?xml version="1.0" encoding="UTF-8" ?>\n'
+ '<rss version="2.0">\n'
+ toXML(expandNodeTree(rss, "channel")[0])
+ '\n</rss>\n';
}
21 changes: 14 additions & 7 deletions src/server/server-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,8 @@ export function sendResponse(
}
}

import { generateDirectoryListing } from "./generate-directory-listing.js";
import { generateDirectoryListing } from "./generate-directory-listing";
import { generateDirectoryRSS } from "./generate-directory-rss";
import { RequestEvent } from "./request-event";

//const generateDirectoryListing: (...args: any[]) => string = require('./generate-directory-listing').generateDirectoryListing;
Expand All @@ -300,16 +301,17 @@ export type DirectoryIndexListing = {
icon: string;
type: "error" | "folder" | "datafolder" | "file" | "group";
size: string;
modified: number;
}[]
type: "group" | "folder" | 403 | 404
}
export type DirectoryIndexOptions = {
upload: boolean;
mkdir: boolean;
format: "json" | "html";
format: "json" | "html" | "rss";
mixFolders: boolean;
isLoggedIn: string | false;
extTypes: { [ext_mime: string]: string };
extIcons: { [ext_mime: string]: string };
};
export async function sendDirectoryIndex(_r: DirectoryIndexData, options: DirectoryIndexOptions) {
let { keys, paths, dirpath, type } = _r;
Expand All @@ -319,21 +321,26 @@ export async function sendDirectoryIndex(_r: DirectoryIndexData, options: Direct
let statpath = paths[i];
let stat = statpath === true ? undefined : await statPath(statpath, false);
const nameparts = key.indexOf(".") !== -1 ? key.split(".").pop() : "";
return {
const list: DirectoryIndexListing["entries"][number] = {
name: key,
path: key + (!stat || stat.itemtype === "folder" ? "/" : ""),
type: !stat ? "group" : stat.itemtype,
icon: !stat ? "group.png" : (stat.itemtype === "file")
? (nameparts && options.extTypes[nameparts] || "other.png")
? (nameparts && options.extIcons[nameparts] || "other.png")
: (stat.itemtype as string + ".png"),
size: stat && stat.stat ? getHumanSize(stat.stat.size) : "",
} as DirectoryIndexListing["entries"][number];
modified: stat?.stat?.mtimeMs || 0
};
return list;
})
);
if (options.format === "json") {
return JSON.stringify({ path: dirpath, entries, type, options }, null, 2);
} else if (options.format === "rss") {
let def: DirectoryIndexListing = { path: dirpath, entries, type };
return generateDirectoryRSS(def, options);
} else {
let def = { path: dirpath, entries, type };
let def: DirectoryIndexListing = { path: dirpath, entries, type };
return generateDirectoryListing(def, options);
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
ServerConfigSchema,
ServerConfig_AccessOptions,
} from "./server-types";
import {loadSettings} from "./utils-config";
import { loadSettings } from "./utils-config";
import { keys } from "./utils-functions";
import { StateObject } from "./state-object";
import { handleTreeRoute } from "./tiddlyserver";
Expand Down Expand Up @@ -215,12 +215,12 @@ export class MainServer {
bindAddress is ${JSON.stringify(bindAddress, null, 2)}
`;
}
stateError = state => {
stateError = (state: StateObject) => {
if (state.doneMessage.length > 0)
StateObject.DebugLogger("STA-ERR").call(state, 2, state.doneMessage.join("\n"));
debugger;
};
stateDebug = state => {
stateDebug = (state: StateObject) => {
if (state.doneMessage.length > 0)
StateObject.DebugLogger("STA-DBG").call(state, -2, state.doneMessage.join("\n"));
};
Expand Down
19 changes: 12 additions & 7 deletions src/server/tiddlyserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Stats } from "fs";
import * as path from "path";
import { promisify } from "util";
import * as zlib from "zlib";

import { mime } from "send";
import * as formidable from "formidable";
import { handleDataFolderRequest, handleWebsocketConnection } from "./data-folder";
import { OptionsConfig } from "./server-config";
Expand All @@ -31,6 +31,7 @@ import { StateObject } from "./state-object";
import { RequestEvent } from "./request-event";
import { parse } from "url";
import { generateDirectoryListing } from './generate-directory-listing';
import { contains, first, keys } from "./utils-functions";


// it isn't pretty but I can't find a way to improve it - 2020/04/10
Expand All @@ -46,7 +47,7 @@ function generateErrorPage(type: 403 | 404, path: string, state: {
mixFolders: state.settings.directoryIndex.mixFolders,
isLoggedIn: state.username ? state.username + " (group " + state.authAccountKey + ")" : (false as false),
format: "html",
extTypes: state.settings.directoryIndex.types
extIcons: state.settings.directoryIndex.types
});
}

Expand All @@ -62,7 +63,7 @@ export async function handleTreeRoute(event: RequestEvent, eventer: ServerEventE

let { authList, authError } = treeOptions.auth;


if (Array.isArray(authList) && authList.indexOf(event.authAccountKey) === -1)
return event.close(403, authAccessDenied(
authError,
Expand Down Expand Up @@ -252,17 +253,21 @@ export class TreeStateObject<STATPATH extends StatPathResult = StatPathResult> e
}

//generate the index using generateDirectoryListing.js
const format = state.treeOptions.index.defaultType as "html" | "json";
const options = this.getDirectoryIndexOptions(isFolder);
let contentType = {
let contentType: { [K in DirectoryIndexOptions["format"]]: string } = {
html: "text/html",
json: "application/json",
rss: "application/rss"
};
const format = first(state.url.query.format);
if (contains(keys(contentType), format)) {
options.format = format;
}
let e = await state.getTreePathFiles();
let res = await sendDirectoryIndex(e, options);
state
.respond(200, "", {
"Content-Type": contentType[format],
"Content-Type": options.format,
"Content-Encoding": "utf-8",
})
// @ts-ignore
Expand Down Expand Up @@ -298,7 +303,7 @@ export class TreeStateObject<STATPATH extends StatPathResult = StatPathResult> e
? this.username + " (group " + this.authAccountKey + ")"
: (false as false),
format: this.treeOptions.index.defaultType as "html" | "json",
extTypes: this.settings.directoryIndex.types
extIcons: this.settings.directoryIndex.types
};
}

Expand Down
7 changes: 7 additions & 0 deletions src/server/utils-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,10 @@ export function safeJSON(key, value) {
if (key === "__proto__" || key === "constructor") return undefined;
else return value;
}

export function first<T>(item: T | T[]): T{
return Array.isArray(item) ? item[0] : item;
}
export function contains<T extends string>(arr: T[], test: string): test is T {
return arr.indexOf(test as any) !== -1;
}
46 changes: 40 additions & 6 deletions src/server/utils-xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,21 @@ function convertToXMLinner(e: any) {
}
}
}
// export interface xmlTree extends xmlBase
// export interface xmlBase {
// [k: string]: string;
// }
export type xmlTree = {
$element: string;
$children: (xmlTree | string)[];
} & {
[k: string]: unknown;
}

export function toXML(input: any) {
export function toXML(input: xmlTree, declaration: boolean = true) {
let inter = JSON.stringify({
declaration: { "$attrs": { "version": "1.0" } },
$children: [
convertToXMLinner(input)
]
// declaration: { "$attrs": { "version": "1.0" } },
$children: [convertToXMLinner(input)]
});
return json2xml(inter, { compact: false, ...keys, });
}
Expand All @@ -78,4 +86,30 @@ export function fromXML(docStr: string, root: string[]) {
} else {
return false;
}
}
}
type ret = { $element: string, $children: (ret | string)[] };
/**
* Convert an object into a JSON document, or an array if the key is specified
* @param obj The basic JS object to convert to an XML document, arrays require the key param
* @param key The node name, depending on the type of obj param
* @returns An array of $children or an array with a root element
*/
export function expandNodeTree(obj: any): (string | xmlTree)[];
export function expandNodeTree(obj: any, key: string): xmlTree[];
export function expandNodeTree(obj: any, key?: string): (string | xmlTree)[] {
if (!obj) return [];
if (Array.isArray(obj)) {
if (!key) throw new Error("key is required in expandNodeTree array branch");
return obj.map(e => ({
$element: key,
$children: expandNodeTree(e)
}));
} else if (typeof obj === "object") {
let $children: ret[] = Object.keys(obj).reduce((n, e) =>
[...n, ...expandNodeTree(obj[e], e)], [] as ret[]);
return key ? [{ $element: key, $children } as ret] : $children;
} else {
let $children = [obj.toString()];
return key ? [{ $element: key, $children }] : $children;
}
}

0 comments on commit 49f0734

Please sign in to comment.