From 95520e28ddea14d880bc7470fcb35b17c13b9c33 Mon Sep 17 00:00:00 2001 From: NaitLee Date: Tue, 14 Jun 2022 23:27:19 +0800 Subject: [PATCH] Follow newest HFS change; adjust handler behavior And a 404 page in `bare.tpl` --- .gitignore | 1 + dist/bare.tpl | 16 +++++++++++ dist/tradit-handler.js | 56 +++++++++++++++++++++++--------------- dist/tradit-macros.js | 14 ++++++---- tradit-handler.ts | 62 ++++++++++++++++++++++++++++-------------- tradit-interpreter.ts | 10 +++---- tradit-macros.ts | 13 +++++---- types.d.ts | 13 +++++++-- 8 files changed, 122 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index 534da3a..d03b780 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.js.map !bare.tpl #dist +debug .directory thumbs.db diff --git a/dist/bare.tpl b/dist/bare.tpl index 20b3f2c..75e4837 100644 --- a/dist/bare.tpl +++ b/dist/bare.tpl @@ -245,3 +245,19 @@ document.addEventListener('DOMContentLoaded', () => { + +[not found] + + + + + + Not Found + + + +

Not Found

+

< Go Back

+ + + diff --git a/dist/tradit-handler.js b/dist/tradit-handler.js index 8f13ac9..8523300 100644 --- a/dist/tradit-handler.js +++ b/dist/tradit-handler.js @@ -15,6 +15,7 @@ class ReadableForMacros extends stream_1.Readable { this.ctx = options.ctx; } async put(item) { + // Note: this is useless. Will find a better solution if (!this.push(item)) { await (0, events_1.once)(this, 'readable'); } @@ -45,7 +46,7 @@ class Handler { return; // let subscriber work } else { - tradit_globals_1.API.log('Please select a template in plugin configuration'); + tradit_globals_1.API.log('Please manually select a template in plugin configuration'); return; } } @@ -77,37 +78,48 @@ class Handler { return; let section_name = ctx.path.startsWith(tradit_constants_1.SECTION_URI) ? ctx.path.slice(2) : ''; let id = this.interpreter.getSectionIndex(section_name); - let entry_generator = null; - entry_generator = - this.interpreter.template.params[id].no_list || section_name + let allow_private_section = false; + let readable_list = null; + readable_list = + (this.interpreter.template.params[id].no_list || section_name !== '') ? null : await tradit_globals_1.HFS.file_list({ path: ctx.path, omit: 'c', sse: true }, ctx); - if (entry_generator instanceof Error) { - switch (ctx.status = entry_generator.status) { - case 404: - section_name = 'not found'; - break; - case 401: + do { // for good code by early break + if (readable_list === null) + break; + await (0, events_1.once)(readable_list, 'readable'); + let possible_error = readable_list.read(); + if (possible_error === null) + break; // TODO: is this possible? + if (possible_error.error === undefined) { + readable_list.unshift(possible_error); // not an error + break; + } + switch (ctx.status = possible_error.error) { + case tradit_globals_1.API.const.UNAUTHORIZED: section_name = 'unauth'; break; - case 403: + case tradit_globals_1.API.const.FORBIDDEN: section_name = 'forbidden'; break; case 400: - // section_name = 'bad request'; - section_name = 'not found'; - break; - case 418: + // bad request return; + case 418: + // potential attack, or tea pot + return true; + default: + section_name = 'not found'; } - entry_generator = null; - } - if (!this.interpreter.hasSection(section_name)) { + readable_list = null; + allow_private_section = true; + } while (false); + if (!this.interpreter.hasSection(section_name, allow_private_section)) { ctx.status = 404; section_name = 'not found'; } - if (!this.interpreter.hasSection(section_name)) - return void (ctx.body = 'not found'); + if (!this.interpreter.hasSection(section_name, allow_private_section)) + return; let generator; const step = 64; let readable = new ReadableForMacros({ @@ -121,9 +133,9 @@ class Handler { }, ctx: ctx }); - generator = this.interpreter.getSectionGenerator(section_name, readable, entry_generator); + generator = this.interpreter.getSectionGenerator(section_name, readable, readable_list, allow_private_section); if (!generator) - return void (ctx.body = 'not found'); + return; ctx.status = 200; ctx.type = mimetype(ctx.path); ctx.body = readable; diff --git a/dist/tradit-macros.js b/dist/tradit-macros.js index 5a0dfc4..3f3a0b5 100644 --- a/dist/tradit-macros.js +++ b/dist/tradit-macros.js @@ -267,14 +267,16 @@ new Macro('list', c => { t_folder = await d.i.getSectionAsText('folder', d, true); t_link = await d.i.getSectionAsText('link', d, true); let value, result = ''; - for await (let { entry } of d.l) { + for await (let { add } of d.l) { if (d.c.aborted) break; - value = (entry.s === undefined ? t_folder : t_file) - .replace('%item-url%', d.c.path + entry.n) - .replace('%item-name%', entry.n) - .replace('%item-modified%', entry.m?.toLocaleString() ?? '') - .replace('%item-size%', smartSize(entry.s ?? 0)); + if (add === undefined) + continue; + value = (add.s === undefined ? t_folder : t_file) + .replace('%item-url%', d.c.path + add.n) + .replace('%item-name%', add.n) + .replace('%item-modified%', add.m?.toLocaleString() ?? '') + .replace('%item-size%', smartSize(add.s ?? 0)); pass ? await d.p.put(value) : result += value; } if (!pass) diff --git a/tradit-handler.ts b/tradit-handler.ts index be25145..bca016c 100644 --- a/tradit-handler.ts +++ b/tradit-handler.ts @@ -8,6 +8,11 @@ import { Interpreter } from './tradit-interpreter'; import { serialize } from './tradit-serializer'; import { makePathConsistent } from './tradit-misc'; +export interface SendListReadable extends Readable { + [Symbol.asyncIterator](): AsyncIterableIterator>; + read(size?: number): SendListEntry; +} + export class ReadableForMacros extends Readable { ctx: KoaContext; constructor(options: ReadableOptions & { ctx: KoaContext }) { @@ -15,6 +20,7 @@ export class ReadableForMacros extends Readable { this.ctx = options.ctx; } async put(item: any) { + // Note: this is useless. Will find a better solution if (!this.push(item)) { await once(this, 'readable'); } @@ -79,39 +85,48 @@ export class Handler { ) return; let section_name = ctx.path.startsWith(SECTION_URI) ? ctx.path.slice(2) : ''; let id = this.interpreter.getSectionIndex(section_name); - let entry_generator: FileEntryGenerator | APIError | null = null; - entry_generator = - this.interpreter.template.params[id].no_list || section_name + let allow_private_section = false; + let readable_list: SendListReadable | null = null; + readable_list = + (this.interpreter.template.params[id].no_list || section_name !== '') ? null - : await HFS.file_list( + : await HFS.file_list( { path: ctx.path, omit: 'c', sse: true }, ctx ); - if (entry_generator instanceof Error) { - switch (ctx.status = entry_generator.status) { - case 404: - section_name = 'not found'; - break; - case 401: + do { // for good code by early break + if (readable_list === null) break; + await once(readable_list, 'readable'); + let possible_error = readable_list.read(); + if (possible_error === null) break; // TODO: is this possible? + if (possible_error.error === undefined) { + readable_list.unshift(possible_error); // not an error + break; + } + switch (ctx.status = possible_error.error) { + case API.const.UNAUTHORIZED: section_name = 'unauth'; break; - case 403: + case API.const.FORBIDDEN: section_name = 'forbidden'; break; case 400: - // section_name = 'bad request'; - section_name = 'not found'; - break; - case 418: + // bad request return; + case 418: + // potential attack, or tea pot + return true; + default: + section_name = 'not found'; } - entry_generator = null; - } - if (!this.interpreter.hasSection(section_name)) { + readable_list = null; + allow_private_section = true; + } while (false); + if (!this.interpreter.hasSection(section_name, allow_private_section)) { ctx.status = 404; section_name = 'not found'; } - if (!this.interpreter.hasSection(section_name)) return void(ctx.body = 'not found'); + if (!this.interpreter.hasSection(section_name, allow_private_section)) return; let generator: AsyncGenerator; const step: number = 64; let readable = new ReadableForMacros({ @@ -125,8 +140,13 @@ export class Handler { }, ctx: ctx }); - generator = this.interpreter.getSectionGenerator(section_name, readable, entry_generator) as AsyncGenerator; - if (!generator) return void(ctx.body = 'not found'); + generator = this.interpreter.getSectionGenerator( + section_name, + readable, + readable_list, + allow_private_section + ) as AsyncGenerator; + if (!generator) return; ctx.status = 200; ctx.type = mimetype(ctx.path); ctx.body = readable; diff --git a/tradit-interpreter.ts b/tradit-interpreter.ts index 00f1131..1850b34 100644 --- a/tradit-interpreter.ts +++ b/tradit-interpreter.ts @@ -1,7 +1,7 @@ import { Macro, Macros } from "./tradit-macros"; import { ItemRole as ItemRole, NULL_INT, NULL_NUMBER, NULL_STRING, StackType } from "./tradit-constants"; -import { ReadableForMacros } from "./tradit-handler"; +import { ReadableForMacros, SendListReadable } from "./tradit-handler"; import { groupToString } from "./tradit-misc"; // import { Worker, isMainThread, parentPort, workerData } from "worker_threads"; @@ -61,7 +61,7 @@ export interface MacroRoutineContext { /** interpreter instance */ i: Interpreter; /** file entry generator, if available */ - l: FileEntryGenerator | null; + l: SendListReadable | null; /** koa context */ c: KoaContext; /** pass-through readable body */ @@ -428,7 +428,7 @@ export class Interpreter { r: false, t: this.template, i: this }; } - newRoutineContext(readable: ReadableForMacros, entry_generator: FileEntryGenerator | null): MacroRoutineContext { + newRoutineContext(readable: ReadableForMacros, entry_generator: SendListReadable | null): MacroRoutineContext { return { s: new MacroStack(), l: entry_generator, vn: Object.setPrototypeOf({}, this.globalVariables.n), @@ -483,13 +483,13 @@ export class Interpreter { ctx.s = old_stack; return result; } - getSectionGenerator(name: string, readable: ReadableForMacros, entry_generator: FileEntryGenerator | null, allow_private = false) { + getSectionGenerator(name: string, readable: ReadableForMacros, entry_generator: SendListReadable | null, allow_private = false) { let index = this.getSectionIndex(name, allow_private); if (index === -1) return null; let ctx = this.newRoutineContext(readable, entry_generator); return this.getGroupGenerator(index, ctx); } - getSection(name: string, readable: ReadableForMacros, entry_generator: FileEntryGenerator | null, allow_private = false) { + getSection(name: string, readable: ReadableForMacros, entry_generator: SendListReadable | null, allow_private = false) { let index = this.getSectionIndex(name, allow_private); if (index === -1) return null; let ctx = this.newRoutineContext(readable, entry_generator); diff --git a/tradit-macros.ts b/tradit-macros.ts index c12a20c..026d1c3 100644 --- a/tradit-macros.ts +++ b/tradit-macros.ts @@ -286,13 +286,14 @@ new Macro('list', c => { t_folder = await d.i.getSectionAsText('folder', d, true); t_link = await d.i.getSectionAsText('link', d, true); let value: string, result: string = ''; - for await (let { entry } of d.l) { + for await (let { add } of d.l) { if (d.c.aborted) break; - value = (entry.s === undefined ? t_folder : t_file) - .replace('%item-url%', d.c.path + entry.n) - .replace('%item-name%', entry.n) - .replace('%item-modified%', entry.m?.toLocaleString() ?? '') - .replace('%item-size%', smartSize(entry.s ?? 0)); + if (add === undefined) continue; + value = (add.s === undefined ? t_folder : t_file) + .replace('%item-url%', d.c.path + add.n) + .replace('%item-name%', add.n) + .replace('%item-modified%', add.m?.toLocaleString() ?? '') + .replace('%item-size%', smartSize(add.s ?? 0)); pass ? await d.p.put(value) : result += value; } if (!pass) d.s.pushString(result); diff --git a/types.d.ts b/types.d.ts index ddfdaab..67f4a45 100644 --- a/types.d.ts +++ b/types.d.ts @@ -40,14 +40,14 @@ type AssemblizedTemplate = { * HFS internal API */ interface HFS { - file_list( + file_list( args: { path: string; omit: 'c'; sse: boolean; }, ctx: KoaContext - ): Promise; + ): Promise; fs: TypeofFS; auth?: { refresh_session({}, ctx: KoaContext): Promise<{ @@ -125,8 +125,15 @@ interface FileEntry { s?: number | undefined; } +interface SendListEntry { + add?: T; + remove?: T; + update?: T; + error?: number; // string | number; +} + type FileEntryGenerator = AsyncGenerator<{ entry: FileEntry }>; -type FileEntryList = { list: FileEntry[] }; +type FileEntryList = { list: FileEntry[] } | APIError; interface APIError extends Error { status: number;