mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
* Utils to compress/decompress data Gzip was chosen because we want smaller data but quick algorithm since this will be ran for every request * Compress system cache * Decompress system cache * Set/Get compressed cache for individual requests * Switch from gzip to snappy, use json compression too * Fix cache exp set/get * Remove unused import Co-authored-by: rijkvanzanten <rijkvanzanten@me.com>
90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
import { RequestHandler } from 'express';
|
|
import ms from 'ms';
|
|
import { getCache, setCacheValue } from '../cache';
|
|
import env from '../env';
|
|
import asyncHandler from '../utils/async-handler';
|
|
import { getCacheKey } from '../utils/get-cache-key';
|
|
import { getCacheControlHeader } from '../utils/get-cache-headers';
|
|
import logger from '../logger';
|
|
import { ExportService } from '../services';
|
|
import { getDateFormatted } from '../utils/get-date-formatted';
|
|
import { stringByteSize } from '../utils/get-string-byte-size';
|
|
import { parse as parseBytesConfiguration } from 'bytes';
|
|
|
|
export const respond: RequestHandler = asyncHandler(async (req, res) => {
|
|
const { cache } = getCache();
|
|
|
|
let exceedsMaxSize = false;
|
|
|
|
if (env.CACHE_VALUE_MAX_SIZE !== false) {
|
|
const valueSize = res.locals.payload ? stringByteSize(JSON.stringify(res.locals.payload)) : 0;
|
|
const maxSize = parseBytesConfiguration(env.CACHE_VALUE_MAX_SIZE);
|
|
exceedsMaxSize = valueSize > maxSize;
|
|
}
|
|
|
|
if (
|
|
(req.method.toLowerCase() === 'get' || req.path?.startsWith('/graphql')) &&
|
|
env.CACHE_ENABLED === true &&
|
|
cache &&
|
|
!req.sanitizedQuery.export &&
|
|
res.locals.cache !== false &&
|
|
exceedsMaxSize === false
|
|
) {
|
|
const key = getCacheKey(req);
|
|
|
|
try {
|
|
await setCacheValue(cache, key, res.locals.payload, ms(env.CACHE_TTL as string));
|
|
await setCacheValue(cache, `${key}__expires_at`, { exp: Date.now() + ms(env.CACHE_TTL as string) });
|
|
} catch (err: any) {
|
|
logger.warn(err, `[cache] Couldn't set key ${key}. ${err}`);
|
|
}
|
|
|
|
res.setHeader('Cache-Control', getCacheControlHeader(req, ms(env.CACHE_TTL as string)));
|
|
res.setHeader('Vary', 'Origin, Cache-Control');
|
|
} else {
|
|
// Don't cache anything by default
|
|
res.setHeader('Cache-Control', 'no-cache');
|
|
res.setHeader('Vary', 'Origin, Cache-Control');
|
|
}
|
|
|
|
if (req.sanitizedQuery.export) {
|
|
const exportService = new ExportService({ accountability: req.accountability, schema: req.schema });
|
|
|
|
let filename = '';
|
|
|
|
if (req.collection) {
|
|
filename += req.collection;
|
|
} else {
|
|
filename += 'Export';
|
|
}
|
|
|
|
filename += ' ' + getDateFormatted();
|
|
|
|
if (req.sanitizedQuery.export === 'json') {
|
|
res.attachment(`${filename}.json`);
|
|
res.set('Content-Type', 'application/json');
|
|
return res.status(200).send(exportService.transform(res.locals.payload?.data, 'json'));
|
|
}
|
|
|
|
if (req.sanitizedQuery.export === 'xml') {
|
|
res.attachment(`${filename}.xml`);
|
|
res.set('Content-Type', 'text/xml');
|
|
return res.status(200).send(exportService.transform(res.locals.payload?.data, 'xml'));
|
|
}
|
|
|
|
if (req.sanitizedQuery.export === 'csv') {
|
|
res.attachment(`${filename}.csv`);
|
|
res.set('Content-Type', 'text/csv');
|
|
return res.status(200).send(exportService.transform(res.locals.payload?.data, 'csv'));
|
|
}
|
|
}
|
|
|
|
if (Buffer.isBuffer(res.locals.payload)) {
|
|
return res.end(res.locals.payload);
|
|
} else if (res.locals.payload) {
|
|
return res.json(res.locals.payload);
|
|
} else {
|
|
return res.status(204).end();
|
|
}
|
|
});
|