better code

Massimo Melina committed Mar 16, 2023 at 14:59 UTC 98fa62f18b230907a773c2665f499ece1aceb432
6 files changed +23 -24
langs/hfs-lang-en.json
+2 -3
@@ -89,6 +89,7 @@
89 "Don't": "Don't",
90 "Warning": "Warning",
91 "Error": "Error",
92 + "Info": "Info",
93
94 "Unauthorized": "Unauthorized",
95 "Forbidden": "Forbidden",
@@ -100,8 +101,6 @@
101 "upload_finished": "{n} finished ({size})",
102 "upload_errors": "{n} failed",
103
103 - "download counter": "download counter",
104 -
105 - "Info": "Info"
104 + "download counter": "download counter"
105 }
106 }
src/middlewares.ts
+6 -11
@@ -102,7 +102,9 @@ export const serveGuiAndSharedFiles: Koa.Middleware = async (ctx, next) => {
102 }
103 if (ctx.originalUrl === '/favicon.ico' && favicon.get()) // originalUrl to not be subject to changes (vhosting plugin)
104 return serveFile(ctx, favicon.get())
105 - const node = await urlToNode(path, ctx)
105 + let node = await urlToNode(path, ctx)
106 + if (node?.default && (path.endsWith('/') || !node.default.match(/\.html?$/i))) // final/ needed on browser to make resource urls correctly
107 + node = await urlToNode(node.default, ctx, node)
108 if (!node)
109 return ctx.status = HTTP_NOT_FOUND
110 if (ctx.method === 'POST') { // curl -F upload=@file url/
@@ -121,7 +123,7 @@ export const serveGuiAndSharedFiles: Koa.Middleware = async (ctx, next) => {
123 || statusCodeForMissingPerm(node, 'can_read', ctx)
124 || serveFileNode(ctx, node)
125 if (!path.endsWith('/'))
124 - return ctx.redirect(ctx.state.revProxyPath + ctx.originalUrl + '/')
126 + return ctx.redirect(ctx.state.revProxyPath + ctx.originalUrl.replace(/(\?|$)/, '/$1')) // keep query-string, if any
127 if (statusCodeForMissingPerm(node, 'can_list', ctx)) {
128 if (ctx.status === HTTP_FORBIDDEN)
129 return
@@ -132,15 +134,8 @@ export const serveGuiAndSharedFiles: Koa.Middleware = async (ctx, next) => {
134 return serveFrontendFiles(ctx, next)
135 }
136 ctx.set({ server:'HFS '+BUILD_TIMESTAMP })
135 - const { get } = ctx.query
136 - if (get === 'zip')
137 - return await zipStreamFromFolder(node, ctx)
138 - if (!node.default)
139 - return serveFrontendFiles(ctx, next)
140 - const defNode = await urlToNode(path + node.default, ctx)
141 - if (defNode)
142 - statusCodeForMissingPerm(defNode, 'can_read', ctx) || serveFileNode(ctx, defNode)
143 - await next()
137 + return ctx.query.get === 'zip' ? zipStreamFromFolder(node, ctx)
138 + : serveFrontendFiles(ctx, next)
139 }
140
141 let proxyDetected = false
src/misc.ts
+10 -5
@@ -36,22 +36,27 @@ export function setHidden<T, ADD>(dest: T, src: ADD) {
36 }))) as T & ADD
37 }
38
39 -export function newObj<S extends object,VR=any>(
39 +export function newObj<S extends (object | undefined | null),VR=any>(
40 src: S,
41 - newValue: (value:Truthy<S[keyof S]>, key: Exclude<keyof S, symbol>, setK:(newK?: string)=>true)=>any
41 + returnNewValue: (value:Truthy<S[keyof S]>, key: Exclude<keyof S, symbol>, setK:(newK?: string)=>true, depth: number) => any,
42 + recur: boolean | number=false
43 ) {
44 if (!src)
45 return {}
46 const pairs = Object.entries(src).map( ([k,v]) => {
47 if (typeof k === 'symbol') return
48 let _k: undefined | typeof k = k
48 - const newV = newValue(v, k as Exclude<keyof S, symbol>, (newK) => {
49 + const curDepth = typeof recur === 'number' ? recur : 0
50 + let newV = returnNewValue(v, k as Exclude<keyof S, symbol>, (newK) => {
51 _k = newK
52 return true // for convenient expression concatenation
51 - })
53 + }, curDepth)
54 + if ((recur !== false || returnNewValue.length === 4) // if callback is using depth parameter, then it wants recursion
55 + && _.isPlainObject(newV)) // is it recurrable?
56 + newV = newObj(newV, returnNewValue, curDepth + 1)
57 return _k !== undefined && [_k, newV]
58 })
54 - return Object.fromEntries(onlyTruthy(pairs)) as { [K in keyof S]:VR }
59 + return Object.fromEntries(onlyTruthy(pairs)) as S extends undefined | null ? S : { [K in keyof S]:VR }
60 }
61
62 export function wait(ms: number) {
src/serveFile.ts
+1 -1
@@ -49,7 +49,7 @@ export async function serveFile(ctx: Koa.Context, source:string, mime?:string, c
49 if (!source)
50 return
51 const fn = path.basename(source)
52 - if (ctx.params.dl !== undefined) // please, download
52 + if ('dl' in ctx.params) // please, download
53 ctx.attachment(fn)
54 mime = mime ?? _.find(mimeCfg.get(), (v,k) => k>'' && isMatch(fn, k)) // isMatch throws on an empty string
55 if (mime === MIME_AUTO)
src/serveGuiFiles.ts
+1 -1
@@ -30,7 +30,7 @@ function serveStatic(uri: string): Koa.Middleware {
30 const folder = uri.slice(2,-1) // we know folder is very similar to uri
31 let cache: Record<string, Promise<string>> = {}
32 subscribe(customHtmlState, () => cache = {}) // reset cache at every change
33 - return async (ctx, next) => {
33 + return async (ctx) => {
34 if(ctx.method === 'OPTIONS') {
35 ctx.status = HTTP_NO_CONTENT
36 ctx.set({ Allow: 'OPTIONS, GET' })
src/vfs.ts
+3 -3
@@ -265,9 +265,9 @@ function renameUnderPath(rename:undefined | Record<string,string>, path: string)
265 function matchWho(who: Who, ctx: Koa.Context) {
266 return who === WHO_ANYONE
267 || who === WHO_ANY_ACCOUNT && Boolean(ctx.state.account)
268 - || Array.isArray(who) && (() => // check if I or any ancestor match `who`, but cache ancestors' usernames inside context state
269 - getOrSet(ctx.state, 'usernames', () => getCurrentUsernameExpanded(ctx)).some((u:string) =>
270 - who.includes(u) ))()
268 + || Array.isArray(who) // check if I or any ancestor match `who`, but cache ancestors' usernames inside context state
269 + && getOrSet(ctx.state, 'usernames', () => getCurrentUsernameExpanded(ctx)).some((u:string) =>
270 + who.includes(u) )
271 }
272
273 events.on('accountRenamed', (from, to) => {