fix: workaround not-found problem by caching static files https://github.com/rejetto/hfs/issues/66
Massimo Melina committed
Aug 3, 2022 at 18:35 UTC
b1151e14a8db942a291bc6dccf56a6b79aad7cbe
2 files changed
+23
-17
server/src/serveFile.ts
+3
-3
@@ -41,7 +41,7 @@ export function serveFileNode(node: VfsNode) : Koa.Middleware {
41
42
const mimeCfg = defineConfig<Record<string,string>>('mime', { '*.jpg|*.png|*.mp3|*.txt': 'auto' })
43
44
-export function serveFile(source:string, mime?:string, modifier?:(s:string)=>string) : Koa.Middleware {
44
+export function serveFile(source:string, mime?:string, content?: string | Buffer) : Koa.Middleware {
45
return async (ctx) => {
46
if (!source)
47
return
@@ -69,8 +69,8 @@ export function serveFile(source:string, mime?:string, modifier?:(s:string)=>str
69
const conn = ctx.state.connection
70
if (conn)
71
updateConnection(conn, { ctx }) // fileSource is affecting connection's outputted data, so we request an update
72
- if (modifier)
73
- return ctx.body = modifier(String(await fs.readFile(source)))
72
+ if (content !== undefined)
73
+ return ctx.body = content
74
const range = getRange(ctx, stats.size)
75
ctx.body = createReadStream(source, range)
76
}
server/src/serveGuiFiles.ts
+20
-14
@@ -8,16 +8,15 @@ import { mapPlugins } from './plugins'
8
import { refresh_session } from './api.auth'
9
import { ApiError } from './apiMiddleware'
10
import path from 'path'
11
+import { getOrSet } from './misc'
12
13
// in case of dev env we have our static files within the 'dist' folder'
14
const DEV_STATIC = process.env.DEV ? '../dist/' : ''
15
16
function serveStatic(uri: string): Koa.Middleware {
17
const folder = uri.slice(2,-1) // we know folder is very similar to uri
18
+ const cache: Record<string, Promise<string>> = {}
19
return async (ctx, next) => {
18
- const loginRequired = ctx.status === UNAUTHORIZED
19
- const serveApp = ctx.path.endsWith('/') || loginRequired
20
- const fullPath = path.join(__dirname, '..', DEV_STATIC, folder, serveApp? '/index.html': ctx.path)
20
if(ctx.method === 'OPTIONS') {
21
ctx.status = NO_CONTENT
22
ctx.set({ Allow: 'OPTIONS, GET' })
@@ -25,19 +24,28 @@ function serveStatic(uri: string): Koa.Middleware {
24
}
25
if (ctx.method !== 'GET')
26
return ctx.status = METHOD_NOT_ALLOWED
27
+ const loginRequired = ctx.status === UNAUTHORIZED
28
+ const serveApp = ctx.path.endsWith('/') || loginRequired
29
+ const fullPath = path.join(__dirname, '..', DEV_STATIC, folder, serveApp? '/index.html': ctx.path)
30
+ const content = await getOrSet(cache, ctx.path, async () => {
31
+ const data = await fs.readFile(fullPath).catch(() => null)
32
+ return serveApp || !data ? data : adjustWebpackLinks(ctx.path, uri, data)
33
+ })
34
+ if (content === null)
35
+ return ctx.status = 404
36
if (!serveApp)
29
- return serveFile(fullPath, 'auto', getModifier(ctx.path, uri))(ctx, next)
37
+ return serveFile(fullPath, 'auto', content)(ctx, next)
38
// we don't cache the index as it's small and may prevent plugins change to apply
31
- ctx.body = await treatIndex(ctx, String(await fs.readFile(fullPath)), uri)
39
+ ctx.body = await treatIndex(ctx, String(content), uri)
40
ctx.type = 'html'
41
ctx.set('Cache-Control', 'no-store, no-cache, must-revalidate')
42
}
43
}
44
37
-function getModifier(path: string, uri: string) {
38
- return path.startsWith('/static/js') ? // webpack
39
- (s: string) => s.replace(/(")(static\/)/g, '$1' + uri.substring(1) + '$2')
40
- : undefined
45
+function adjustWebpackLinks(path: string, uri: string, data: string | Buffer) {
46
+ return path.startsWith('/static/js') // webpack
47
+ ? String(data).replace(/(")(static\/)/g, '$1' + uri.substring(1) + '$2')
48
+ : data
49
}
50
51
async function treatIndex(ctx: Koa.Context, body: string, filesUri: string) {
@@ -50,7 +58,7 @@ async function treatIndex(ctx: Koa.Context, body: string, filesUri: string) {
58
.replace('_HFS_PLUGINS_', pluginsInjection)
59
}
60
53
-function serveProxied(port: string | undefined, uri: string) { // used for development
61
+function serveProxied(port: string | undefined, uri: string) { // used for development only
62
if (!port)
63
return
64
console.debug('proxied on port', port)
@@ -59,10 +67,8 @@ function serveProxied(port: string | undefined, uri: string) { // used for devel
67
proxy = lib.default('127.0.0.1:'+port, {
68
proxyReqPathResolver: (ctx) => ctx.path.endsWith('/') ? '/' : ctx.path,
69
userResDecorator(res, data, ctx) {
62
- if (ctx.path.endsWith('/'))
63
- return treatIndex(ctx, String(data), uri)
64
- const mod = getModifier(ctx.path, uri)
65
- return mod ? mod(String(data)) : data
70
+ return ctx.path.endsWith('/') ? treatIndex(ctx, String(data), uri)
71
+ : adjustWebpackLinks(ctx.path, uri, String(data))
72
}
73
}) )
74
return function() { //@ts-ignore