48 lines
1.8 KiB
TypeScript
48 lines
1.8 KiB
TypeScript
import type { Response, NextFunction } from 'express'
|
|
|
|
import type { ExtendedRequest } from '@/types'
|
|
|
|
// This middleware rewrites the URL of requests that contain the
|
|
// portion of `/cb-\d+/`.
|
|
// "cb" stands for "cache bust".
|
|
// There's a Markdown plugin that rewrites all <img src> values
|
|
// from `<img src="/assets/foo/bar.png">` to
|
|
// `<img src="/assets/cb-123467/foo/bar.png">` for example.
|
|
// We're doing this so that we can set a much more aggressive
|
|
// Cache-Control for assets and a CDN surrogate-key that doesn't
|
|
// soft-purge on every deployment.
|
|
|
|
const regex = /\/cb-\d+\//
|
|
|
|
export default function assetPreprocessing(
|
|
req: ExtendedRequest,
|
|
res: Response,
|
|
next: NextFunction,
|
|
) {
|
|
if (req.path.startsWith('/assets/')) {
|
|
// We didn't use to have a rule about all image assets must be
|
|
// lower case. So we've exposed things like:
|
|
// <img src="/assets/images/Foobar.png"> which means they could
|
|
// get a 404 if the file is actually named `foobar.png`.
|
|
if (req.url !== req.url.toLowerCase()) {
|
|
// The reason for doing a redirect instead rewriting the
|
|
// `req.url` attribute is that we don't want encourage this.
|
|
// By forcing this to be a redirect, it means we only serve
|
|
// 1 single file. All other requests will be redirects.
|
|
// Otherwise someone might trigger too much bypassing of the CDN.
|
|
return res.redirect(req.url.toLowerCase())
|
|
}
|
|
|
|
// We're only confident enough to set the *manual* surrogate key if the
|
|
// asset contains the cache-busting piece.
|
|
if (regex.test(req.url)) {
|
|
// We remove it so that when `express.static()` runs, it can
|
|
// find the file on disk by its original name.
|
|
req.url = req.url.replace(regex, '/')
|
|
// The Cache-Control is managed by the configuration
|
|
// for express.static() later in the middleware.
|
|
}
|
|
}
|
|
return next()
|
|
}
|