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) => {