allow local code (plugins) to read SendList errors https://github.com/rejetto/hfs/issues/54

Massimo Melina committed Jun 16, 2022 at 23:42 UTC 9463ec4a6047d9ac038809a5dbd5aa988275aaf8
4 files changed +48 -45
server/src/api.file_list.ts
+5 -4
@@ -1,7 +1,7 @@
1 // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import { cantReadStatusCode, getNodeName, hasPermission, nodeIsDirectory, urlToNode, VfsNode, walkNode } from './vfs'
4 -import { ApiError, ApiHandler, sendList } from './apiMiddleware'
4 +import { ApiError, ApiHandler, SendListReadable } from './apiMiddleware'
5 import { stat } from 'fs/promises'
6 import { mapPlugins } from './plugins'
7 import { asyncGeneratorToArray, dirTraversal, pattern2filter } from './misc'
@@ -9,7 +9,7 @@ import _ from 'lodash'
9
10 export const file_list: ApiHandler = async ({ path, offset, limit, search, omit, sse }, ctx) => {
11 let node = await urlToNode(path || '/', ctx)
12 - const list = sendList()
12 + const list = new SendListReadable()
13 if (!node)
14 return fail(404)
15 if (!hasPermission(node,'can_read',ctx))
@@ -32,13 +32,14 @@ export const file_list: ApiHandler = async ({ path, offset, limit, search, omit,
32 list.add(entry)
33 list.end()
34 })
35 - return list.return
35 + return list
36
37 function fail(code: any) {
38 if (!sse)
39 return new ApiError(code)
40 list.error(code)
41 - return list.return
41 + list.destroy(code)
42 + return list
43 }
44
45 async function* produceEntries() {
server/src/api.monitor.ts
+2 -2
@@ -1,7 +1,7 @@
1 import _ from 'lodash'
2 import { Connection, getConnections } from './connections'
3 import { pendingPromise } from './misc'
4 -import { ApiHandlers, sendList } from './apiMiddleware'
4 +import { ApiHandlers, SendListReadable } from './apiMiddleware'
5 import Koa from 'koa'
6
7 const apis: ApiHandlers = {
@@ -17,7 +17,7 @@ const apis: ApiHandlers = {
17 },
18
19 get_connections({}, ctx) {
20 - const list = sendList( getConnections().map(c => serializeConnection(c)) )
20 + const list = new SendListReadable( getConnections().map(c => serializeConnection(c)) )
21 return list.events(ctx, {
22 connection: conn => list.add(serializeConnection(conn)),
23 connectionClosed(conn: Connection) {
server/src/api.plugins.ts
+6 -6
@@ -10,7 +10,7 @@ import {
10 import _ from 'lodash'
11 import assert from 'assert'
12 import { objSameKeys, onOff, wait } from './misc'
13 -import { ApiHandlers, sendList } from './apiMiddleware'
13 +import { ApiHandlers, SendListReadable } from './apiMiddleware'
14 import events from './events'
15 import { rm } from 'fs/promises'
16 import { downloadPlugin, getFolder2repo, getRepoInfo, readOnlinePlugin, searchPlugins } from './github'
@@ -18,7 +18,7 @@ import { downloadPlugin, getFolder2repo, getRepoInfo, readOnlinePlugin, searchPl
18 const apis: ApiHandlers = {
19
20 get_plugins({}, ctx) {
21 - const list = sendList([ ...mapPlugins(serialize), ...getAvailablePlugins() ])
21 + const list = new SendListReadable([ ...mapPlugins(serialize), ...getAvailablePlugins() ])
22 return list.events(ctx, {
23 pluginInstalled: p => list.add(serialize(p)),
24 'pluginStarted pluginStopped pluginUpdated': p => {
@@ -35,7 +35,7 @@ const apis: ApiHandlers = {
35 },
36
37 async get_plugin_updates() {
38 - const list = sendList()
38 + const list = new SendListReadable()
39 setTimeout(async () => {
40 for (const [folder, repo] of Object.entries(getFolder2repo()))
41 try {
@@ -51,7 +51,7 @@ const apis: ApiHandlers = {
51 }
52 list.end()
53 })
54 - return list.return
54 + return list
55 },
56
57 async set_plugin({ id, enabled, config }) {
@@ -74,7 +74,7 @@ const apis: ApiHandlers = {
74 },
75
76 search_online_plugins({ text }, ctx) {
77 - const list = sendList()
77 + const list = new SendListReadable()
78 setTimeout(async () => {
79 try {
80 const folder2repo = getFolder2repo()
@@ -103,7 +103,7 @@ const apis: ApiHandlers = {
103 }
104 list.end()
105 })
106 - return list.return
106 + return list
107 },
108
109 async download_plugin(pl) {
server/src/apiMiddleware.ts
+35 -33
@@ -67,40 +67,42 @@ async function getJsonFromReq(req: IncomingMessage): Promise<any> {
67 })
68 }
69
70 -// offer an api for a generic dynamic list
71 -export function sendList<T>(addAtStart?: T[]) {
72 - const stream = new Readable({ objectMode: true, read(){} })
73 - const ret = {
74 - return: stream,
75 - add(rec: T) {
76 - stream.push({ add: rec })
77 - },
78 - remove(key: Partial<T>) {
79 - stream.push({ remove: [key] })
80 - },
81 - update(search: Partial<T>, change: Partial<T>) {
82 - stream.push({ update:[{ search, change }] })
83 - },
84 - end() {
85 - stream.push(null)
86 - },
87 - error(msg: string | number) {
88 - stream.push({ error: msg })
89 - },
90 - custom(data: any) {
91 - stream.push(data)
92 - },
93 - events(ctx: Koa.Context, eventMap: Parameters<typeof onOff>[1]) {
94 - const off = onOff(events, eventMap)
95 - ctx.res.once('close', off)
96 - return stream
70 +// offer an api for a generic dynamic list. Suitable to be the result of an api.
71 +export class SendListReadable<T> extends Readable {
72 + protected lastError: string | number | undefined
73 + constructor(addAtStart?: T[]) {
74 + super({ objectMode: true, read(){} })
75 + if (addAtStart) {
76 + for (const x of addAtStart)
77 + this.add(x)
78 + this.push('init')
79 }
80 }
99 - if (addAtStart) {
100 - for (const x of addAtStart)
101 - ret.add(x)
102 - stream.push('init')
81 + add(rec: T) {
82 + this.push({ add: rec })
83 + }
84 + remove(key: Partial<T>) {
85 + this.push({ remove: [key] })
86 + }
87 + update(search: Partial<T>, change: Partial<T>) {
88 + this.push({ update:[{ search, change }] })
89 + }
90 + end() {
91 + this.push(null)
92 + }
93 + error(msg: NonNullable<typeof this.lastError>) {
94 + this.push({ error: msg })
95 + this.lastError = msg
96 + }
97 + getLastError() {
98 + return this.lastError
99 + }
100 + custom(data: any) {
101 + this.push(data)
102 + }
103 + events(ctx: Koa.Context, eventMap: Parameters<typeof onOff>[1]) {
104 + const off = onOff(events, eventMap)
105 + ctx.res.once('close', off)
106 + return this
107 }
104 - return ret
108 }
106 -