Skip to content
Snippets Groups Projects
Commit f3f13d72 authored by julian.baeume's avatar julian.baeume :pick:
Browse files

proxy files not part of manifest.json files to all involved containers

first hit wins. This allows us to transparently use sourcemaps and other files that
are not part of the sources according to the vite manifest
parent 9b026d1b
No related branches found
No related tags found
No related merge requests found
......@@ -41,7 +41,8 @@ describe('File caching service', () => {
'/test.txt': (req, res) => res.setHeader('content-type', 'text/plain').status(200).send('this is test'),
'/index.html.js': (req, res) => res.setHeader('content-type', 'application/javascript').status(200).send('this is index.html.js'),
'/index.html': (req, res) => res.setHeader('content-type', 'text/html').status(200).send('<html><head></head><body>it\'s me</body></html>'),
'/main.css': (req, res) => res.setHeader('content-type', 'text/css').status(200).send('.foo { color: #000; }')
'/main.css': (req, res) => res.setHeader('content-type', 'text/css').status(200).send('.foo { color: #000; }'),
'/favicon.ico': 'not really a favicon, though'
})
await request(app).get('/ready')
......@@ -77,4 +78,15 @@ describe('File caching service', () => {
expect(response.headers['content-type']).toBe('text/html; charset=utf-8')
expect(response.text).toBe('<html><head></head><body>it\'s me</body></html>')
})
it('directly fetches files not referenced in manifest.json files from the upstream servers', async () => {
const response = await request(app).get('/favicon.ico')
expect(response.statusCode).toBe(200)
expect(response.body).toBe('not really a favicon, though')
})
it('returns 404 if file can not be resolved', async () => {
const response = await request(app).get('/unknown-file.txt')
expect(response.statusCode).toBe(404)
})
})
import fs from 'fs/promises'
import yaml from 'js-yaml'
class Config {
async load () {
const urlsSource = await fs.readFile('./config/manifests/urls.yaml', 'utf8')
this._urls = yaml.load(urlsSource).manifests
}
get urls () {
return this._urls || []
}
}
export const config = new Config()
export default config
......@@ -90,6 +90,14 @@ export function createApp () {
next()
})
app.use(async (req, res, next) => {
try {
const { 'content-type': contentType, content } = await fileCache.fetchAndStore(req.path)
if (content) return res.setHeader('content-type', contentType).status(200).send(content)
} catch (e) {}
next()
})
app.use(function (err, req, res, next) {
logger.error(err)
res.status(500).end()
......
import fetch from 'node-fetch'
import crypto from 'crypto'
import { config } from './config.js'
async function fetchData (path, baseUrl) {
const response = await fetch(new URL(path, baseUrl))
if (!response.ok) return null
const content = await response.buffer()
const sha256Sum = crypto.createHash('sha256').update(content).digest('base64')
return [path, {
'content-type': response.headers.get('content-type'),
sha256Sum,
content
}]
}
class FileCache {
constructor () {
this._cache = {}
......@@ -23,15 +35,7 @@ class FileCache {
console.error('could not find manifest for', file)
return null
}
const response = await fetch(new URL(file, manifest.meta.baseUrl))
if (!response.ok) return null
const content = await response.buffer()
const sha256Sum = crypto.createHash('sha256').update(content).digest('base64')
return [file, {
'content-type': response.headers.get('content-type'),
sha256Sum,
content
}]
return fetchData(file, manifest.meta.baseUrl)
} catch (e) { console.error(e) }
}))).filter(data => Array.isArray(data) && data.length === 2))
}
......@@ -40,6 +44,13 @@ class FileCache {
this._cache = cache
}
async fetchAndStore (path) {
if (config.urls.length === 0) await config.load()
const [key, value] = await Promise.race(config.urls.map(baseUrl => fetchData(path, baseUrl)))
this._cache[key] = value
return value
}
get (path) {
return this?._cache[path.slice(1)] || {}
}
......
import fs from 'fs/promises'
import yaml from 'js-yaml'
import fetch from 'node-fetch'
import path from 'path'
import { URL } from 'url'
import { fileCache } from './files.js'
import { config } from './config.js'
export const loadViteManifests = (() => {
let cache
......@@ -13,12 +12,10 @@ export const loadViteManifests = (() => {
const CACHE_TTL = parseInt(process.env.CACHE_TTL)
if (!cache || useCache === false || +new Date() > lastCacheTime + CACHE_TTL) {
const urlsSource = await fs.readFile('./config/manifests/urls.yaml', 'utf8')
const urls = yaml.load(urlsSource).manifests
await config.load()
// vite manifests contains a set of objects with the vite-manifests
// from the corresponding registered services
const viteManifests = await Promise.all(urls.map(async url => {
const viteManifests = await Promise.all(config.urls.map(async url => {
const { origin } = new URL(url)
// fetch the manifests
const result = await fetch(url)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment