30 lines
1.8 KiB
TypeScript
30 lines
1.8 KiB
TypeScript
import { createReadStream, statSync } from 'node:fs';
|
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
import { pipeline } from 'node:stream';
|
|
import { BUILDINGS } from '../src/constants/buildings.ts';
|
|
|
|
// Only serves the precomputed building buffer; other assets use Vite's defaults.
|
|
export function buildingCompressionMiddleware(publicDirectory: URL) {
|
|
const path = new URL(BUILDINGS.assetBufferUrl.slice(1) + '.gz', publicDirectory);
|
|
return (request: IncomingMessage, response: ServerResponse, next: () => void) => {
|
|
if (request.url?.split('?')[0] !== BUILDINGS.assetBufferUrl || !['GET', 'HEAD'].includes(request.method ?? '')) return next();
|
|
const vary = response.getHeader('Vary');
|
|
response.setHeader('Vary', vary ? `${vary}, Accept-Encoding` : 'Accept-Encoding');
|
|
const encodings = (request.headers['accept-encoding'] ?? '').toLowerCase().split(',').map(entry => {
|
|
const [name, ...parameters] = entry.trim().split(';');
|
|
const quality = parameters.find(value => value.trim().startsWith('q='));
|
|
return { name, quality: quality ? Number(quality.trim().slice(2)) : 1 };
|
|
});
|
|
const accepted = encodings.find(entry => entry.name === 'gzip') ?? encodings.find(entry => entry.name === '*');
|
|
if (!accepted || !(accepted.quality > 0) || request.headers.range) return next();
|
|
let size: number;
|
|
try { size = statSync(path).size; } catch { return next(); }
|
|
response.setHeader('Content-Encoding', 'gzip');
|
|
response.setHeader('Content-Type', 'application/octet-stream');
|
|
response.setHeader('Content-Length', size);
|
|
response.setHeader('Cache-Control', 'no-cache');
|
|
if (request.method === 'HEAD') { response.end(); return; }
|
|
pipeline(createReadStream(path), response, error => { if (error) response.destroy(error); });
|
|
};
|
|
}
|