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