refactor: move cache headers logic into reusable functions (#4533)

* refactor: move cache headers logic into reusable functions

* dev

---------

Co-authored-by: Alexandr <qwerty541zxc@gmail.com>
This commit is contained in:
Alexandr GarbuzovandAlexandr authored and GitHub committed 2025-10-07 00:13:21 +03:00
1 parent ade7d53653
commit 09627ce715
10 files changed
+100 -94

No files matched your search

+8 -12
View File
@@ -5,7 +5,11 @@ import { gistWhitelist } from "../src/common/envs.js";
import { isLocaleAvailable } from "../src/translations.js"; import { isLocaleAvailable } from "../src/translations.js";
import { renderGistCard } from "../src/cards/gist.js"; import { renderGistCard } from "../src/cards/gist.js";
import { fetchGist } from "../src/fetchers/gist.js"; import { fetchGist } from "../src/fetchers/gist.js";
import { resolveCacheSeconds } from "../src/common/cache.js"; import {
resolveCacheSeconds,
setCacheHeaders,
setErrorCacheHeaders,
} from "../src/common/cache.js";
export default async (req, res) => { export default async (req, res) => {
const { const {
@@ -57,16 +61,13 @@ export default async (req, res) => {
try { try {
const gistData = await fetchGist(id); const gistData = await fetchGist(id);
const cacheSeconds = resolveCacheSeconds({ const cacheSeconds = resolveCacheSeconds({
requested: cache_seconds, requested: parseInt(cache_seconds, 10),
def: CONSTANTS.TWO_DAY, def: CONSTANTS.TWO_DAY,
min: CONSTANTS.TWO_DAY, min: CONSTANTS.TWO_DAY,
max: CONSTANTS.SIX_DAY, max: CONSTANTS.SIX_DAY,
}); });
res.setHeader( setCacheHeaders(res, cacheSeconds);
"Cache-Control",
`max-age=${cacheSeconds}, s-maxage=${cacheSeconds}`,
);
return res.send( return res.send(
renderGistCard(gistData, { renderGistCard(gistData, {
@@ -83,12 +84,7 @@ export default async (req, res) => {
}), }),
); );
} catch (err) { } catch (err) {
res.setHeader( setErrorCacheHeaders(res);
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
CONSTANTS.ERROR_CACHE_SECONDS
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); // Use lower cache period for errors.
return res.send( return res.send(
renderError(err.message, err.secondaryMessage, { renderError(err.message, err.secondaryMessage, {
title_color, title_color,
+8 -12
View File
@@ -2,7 +2,11 @@
import { renderStatsCard } from "../src/cards/stats.js"; import { renderStatsCard } from "../src/cards/stats.js";
import { blacklist } from "../src/common/blacklist.js"; import { blacklist } from "../src/common/blacklist.js";
import { resolveCacheSeconds } from "../src/common/cache.js"; import {
resolveCacheSeconds,
setCacheHeaders,
setErrorCacheHeaders,
} from "../src/common/cache.js";
import { whitelist } from "../src/common/envs.js"; import { whitelist } from "../src/common/envs.js";
import { import {
CONSTANTS, CONSTANTS,
@@ -104,16 +108,13 @@ export default async (req, res) => {
parseInt(commits_year, 10), parseInt(commits_year, 10),
); );
const cacheSeconds = resolveCacheSeconds({ const cacheSeconds = resolveCacheSeconds({
requested: cache_seconds, requested: parseInt(cache_seconds, 10),
def: CONSTANTS.CARD_CACHE_SECONDS, def: CONSTANTS.CARD_CACHE_SECONDS,
min: CONSTANTS.TWELVE_HOURS, min: CONSTANTS.TWELVE_HOURS,
max: CONSTANTS.TWO_DAY, max: CONSTANTS.TWO_DAY,
}); });
res.setHeader( setCacheHeaders(res, cacheSeconds);
"Cache-Control",
`max-age=${cacheSeconds}, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
);
return res.send( return res.send(
renderStatsCard(stats, { renderStatsCard(stats, {
@@ -144,12 +145,7 @@ export default async (req, res) => {
}), }),
); );
} catch (err) { } catch (err) {
res.setHeader( setErrorCacheHeaders(res);
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
CONSTANTS.ERROR_CACHE_SECONDS
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); // Use lower cache period for errors.
return res.send( return res.send(
renderError(err.message, err.secondaryMessage, { renderError(err.message, err.secondaryMessage, {
title_color, title_color,
+8 -12
View File
@@ -2,7 +2,11 @@
import { renderRepoCard } from "../src/cards/repo.js"; import { renderRepoCard } from "../src/cards/repo.js";
import { blacklist } from "../src/common/blacklist.js"; import { blacklist } from "../src/common/blacklist.js";
import { resolveCacheSeconds } from "../src/common/cache.js"; import {
resolveCacheSeconds,
setCacheHeaders,
setErrorCacheHeaders,
} from "../src/common/cache.js";
import { whitelist } from "../src/common/envs.js"; import { whitelist } from "../src/common/envs.js";
import { CONSTANTS, parseBoolean, renderError } from "../src/common/utils.js"; import { CONSTANTS, parseBoolean, renderError } from "../src/common/utils.js";
import { fetchRepo } from "../src/fetchers/repo.js"; import { fetchRepo } from "../src/fetchers/repo.js";
@@ -77,16 +81,13 @@ export default async (req, res) => {
try { try {
const repoData = await fetchRepo(username, repo); const repoData = await fetchRepo(username, repo);
const cacheSeconds = resolveCacheSeconds({ const cacheSeconds = resolveCacheSeconds({
requested: cache_seconds, requested: parseInt(cache_seconds, 10),
def: CONSTANTS.PIN_CARD_CACHE_SECONDS, def: CONSTANTS.PIN_CARD_CACHE_SECONDS,
min: CONSTANTS.ONE_DAY, min: CONSTANTS.ONE_DAY,
max: CONSTANTS.TEN_DAY, max: CONSTANTS.TEN_DAY,
}); });
res.setHeader( setCacheHeaders(res, cacheSeconds);
"Cache-Control",
`max-age=${cacheSeconds}, s-maxage=${cacheSeconds}`,
);
return res.send( return res.send(
renderRepoCard(repoData, { renderRepoCard(repoData, {
@@ -104,12 +105,7 @@ export default async (req, res) => {
}), }),
); );
} catch (err) { } catch (err) {
res.setHeader( setErrorCacheHeaders(res);
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
CONSTANTS.ERROR_CACHE_SECONDS
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); // Use lower cache period for errors.
return res.send( return res.send(
renderError(err.message, err.secondaryMessage, { renderError(err.message, err.secondaryMessage, {
title_color, title_color,
+8 -12
View File
@@ -2,7 +2,11 @@
import { renderTopLanguages } from "../src/cards/top-languages.js"; import { renderTopLanguages } from "../src/cards/top-languages.js";
import { blacklist } from "../src/common/blacklist.js"; import { blacklist } from "../src/common/blacklist.js";
import { resolveCacheSeconds } from "../src/common/cache.js"; import {
resolveCacheSeconds,
setCacheHeaders,
setErrorCacheHeaders,
} from "../src/common/cache.js";
import { whitelist } from "../src/common/envs.js"; import { whitelist } from "../src/common/envs.js";
import { import {
CONSTANTS, CONSTANTS,
@@ -106,16 +110,13 @@ export default async (req, res) => {
count_weight, count_weight,
); );
const cacheSeconds = resolveCacheSeconds({ const cacheSeconds = resolveCacheSeconds({
requested: cache_seconds, requested: parseInt(cache_seconds, 10),
def: CONSTANTS.TOP_LANGS_CACHE_SECONDS, def: CONSTANTS.TOP_LANGS_CACHE_SECONDS,
min: CONSTANTS.TWO_DAY, min: CONSTANTS.TWO_DAY,
max: CONSTANTS.TEN_DAY, max: CONSTANTS.TEN_DAY,
}); });
res.setHeader( setCacheHeaders(res, cacheSeconds);
"Cache-Control",
`max-age=${cacheSeconds / 2}, s-maxage=${cacheSeconds}`,
);
return res.send( return res.send(
renderTopLanguages(topLangs, { renderTopLanguages(topLangs, {
@@ -139,12 +140,7 @@ export default async (req, res) => {
}), }),
); );
} catch (err) { } catch (err) {
res.setHeader( setErrorCacheHeaders(res);
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
CONSTANTS.ERROR_CACHE_SECONDS
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); // Use lower cache period for errors.
return res.send( return res.send(
renderError(err.message, err.secondaryMessage, { renderError(err.message, err.secondaryMessage, {
title_color, title_color,
+8 -14
View File
@@ -10,7 +10,11 @@ import {
import { whitelist } from "../src/common/envs.js"; import { whitelist } from "../src/common/envs.js";
import { fetchWakatimeStats } from "../src/fetchers/wakatime.js"; import { fetchWakatimeStats } from "../src/fetchers/wakatime.js";
import { isLocaleAvailable } from "../src/translations.js"; import { isLocaleAvailable } from "../src/translations.js";
import { resolveCacheSeconds } from "../src/common/cache.js"; import {
resolveCacheSeconds,
setCacheHeaders,
setErrorCacheHeaders,
} from "../src/common/cache.js";
export default async (req, res) => { export default async (req, res) => {
const { const {
@@ -72,18 +76,13 @@ export default async (req, res) => {
try { try {
const stats = await fetchWakatimeStats({ username, api_domain }); const stats = await fetchWakatimeStats({ username, api_domain });
const cacheSeconds = resolveCacheSeconds({ const cacheSeconds = resolveCacheSeconds({
requested: cache_seconds, requested: parseInt(cache_seconds, 10),
def: CONSTANTS.CARD_CACHE_SECONDS, def: CONSTANTS.CARD_CACHE_SECONDS,
min: CONSTANTS.SIX_HOURS, min: CONSTANTS.SIX_HOURS,
max: CONSTANTS.TWO_DAY, max: CONSTANTS.TWO_DAY,
}); });
res.setHeader( setCacheHeaders(res, cacheSeconds);
"Cache-Control",
`max-age=${
cacheSeconds / 2
}, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
);
return res.send( return res.send(
renderWakatimeCard(stats, { renderWakatimeCard(stats, {
@@ -109,12 +108,7 @@ export default async (req, res) => {
}), }),
); );
} catch (err) { } catch (err) {
res.setHeader( setErrorCacheHeaders(res);
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
CONSTANTS.ERROR_CACHE_SECONDS
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); // Use lower cache period for errors.
return res.send( return res.send(
renderError(err.message, err.secondaryMessage, { renderError(err.message, err.secondaryMessage, {
title_color, title_color,
+33 -3
View File
@@ -1,4 +1,6 @@
import { clampValue } from "./utils"; // @ts-check
import { clampValue, CONSTANTS } from "./utils.js";
/** /**
* Resolves the cache seconds based on the requested, default, min, and max values. * Resolves the cache seconds based on the requested, default, min, and max values.
@@ -11,7 +13,7 @@ import { clampValue } from "./utils";
* @returns {number} The resolved cache seconds. * @returns {number} The resolved cache seconds.
*/ */
const resolveCacheSeconds = ({ requested, def, min, max }) => { const resolveCacheSeconds = ({ requested, def, min, max }) => {
let cacheSeconds = clampValue(parseInt(requested || def, 10), min, max); let cacheSeconds = clampValue(isNaN(requested) ? def : requested, min, max);
cacheSeconds = process.env.CACHE_SECONDS cacheSeconds = process.env.CACHE_SECONDS
? parseInt(process.env.CACHE_SECONDS, 10) || cacheSeconds ? parseInt(process.env.CACHE_SECONDS, 10) || cacheSeconds
@@ -20,4 +22,32 @@ const resolveCacheSeconds = ({ requested, def, min, max }) => {
return cacheSeconds; return cacheSeconds;
}; };
export { resolveCacheSeconds }; /**
* Sets the Cache-Control headers on the response object.
*
* @param {Object} res The response object.
* @param {number} cacheSeconds The cache seconds to set in the headers.
*/
const setCacheHeaders = (res, cacheSeconds) => {
res.setHeader(
"Cache-Control",
`max-age=${cacheSeconds}, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
);
};
/**
* Sets the Cache-Control headers for error responses on the response object.
*
* @param {Object} res The response object.
*/
const setErrorCacheHeaders = (res) => {
// Use lower cache period for errors.
res.setHeader(
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS}, ` +
`s-maxage=${CONSTANTS.ERROR_CACHE_SECONDS}, ` +
`stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
);
};
export { resolveCacheSeconds, setCacheHeaders, setErrorCacheHeaders };
+20 -22
View File
@@ -179,9 +179,9 @@ describe("Test /api/", () => {
["Content-Type", "image/svg+xml"], ["Content-Type", "image/svg+xml"],
[ [
"Cache-Control", "Cache-Control",
`max-age=${CONSTANTS.CARD_CACHE_SECONDS}, s-maxage=${ `max-age=${CONSTANTS.CARD_CACHE_SECONDS}, ` +
CONSTANTS.CARD_CACHE_SECONDS `s-maxage=${CONSTANTS.CARD_CACHE_SECONDS}, ` +
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`, `stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
], ],
]); ]);
}); });
@@ -195,11 +195,9 @@ describe("Test /api/", () => {
["Content-Type", "image/svg+xml"], ["Content-Type", "image/svg+xml"],
[ [
"Cache-Control", "Cache-Control",
`max-age=${ `max-age=${cache_seconds}, ` +
cache_seconds `s-maxage=${cache_seconds}, ` +
}, s-maxage=${cache_seconds}, stale-while-revalidate=${ `stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
CONSTANTS.ONE_DAY
}`,
], ],
]); ]);
}); });
@@ -212,25 +210,25 @@ describe("Test /api/", () => {
["Content-Type", "image/svg+xml"], ["Content-Type", "image/svg+xml"],
[ [
"Cache-Control", "Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${ `max-age=${CONSTANTS.ERROR_CACHE_SECONDS}, ` +
CONSTANTS.ERROR_CACHE_SECONDS `s-maxage=${CONSTANTS.ERROR_CACHE_SECONDS}, ` +
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`, `stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
], ],
]); ]);
}); });
it("should set proper cache with clamped values", async () => { it("should set proper cache with clamped values", async () => {
{ {
let { req, res } = faker({ cache_seconds: 200000 }, data_stats); let { req, res } = faker({ cache_seconds: 200_000 }, data_stats);
await api(req, res); await api(req, res);
expect(res.setHeader.mock.calls).toEqual([ expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"], ["Content-Type", "image/svg+xml"],
[ [
"Cache-Control", "Cache-Control",
`max-age=${CONSTANTS.TWO_DAY}, s-maxage=${ `max-age=${CONSTANTS.TWO_DAY}, ` +
CONSTANTS.TWO_DAY `s-maxage=${CONSTANTS.TWO_DAY}, ` +
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`, `stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
], ],
]); ]);
} }
@@ -244,24 +242,24 @@ describe("Test /api/", () => {
["Content-Type", "image/svg+xml"], ["Content-Type", "image/svg+xml"],
[ [
"Cache-Control", "Cache-Control",
`max-age=${CONSTANTS.ONE_DAY}, s-maxage=${ `max-age=${CONSTANTS.TWELVE_HOURS}, ` +
CONSTANTS.ONE_DAY `s-maxage=${CONSTANTS.TWELVE_HOURS}, ` +
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`, `stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
], ],
]); ]);
} }
{ {
let { req, res } = faker({ cache_seconds: -10000 }, data_stats); let { req, res } = faker({ cache_seconds: -10_000 }, data_stats);
await api(req, res); await api(req, res);
expect(res.setHeader.mock.calls).toEqual([ expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"], ["Content-Type", "image/svg+xml"],
[ [
"Cache-Control", "Cache-Control",
`max-age=${CONSTANTS.TWELVE_HOURS}, s-maxage=${ `max-age=${CONSTANTS.TWELVE_HOURS}, ` +
CONSTANTS.TWELVE_HOURS `s-maxage=${CONSTANTS.TWELVE_HOURS}, ` +
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`, `stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
], ],
]); ]);
} }
+1 -1
View File
@@ -188,7 +188,7 @@ describe("Test /api/gist", () => {
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml"); expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.setHeader).toBeCalledWith( expect(res.setHeader).toBeCalledWith(
"Cache-Control", "Cache-Control",
`max-age=${CONSTANTS.TWO_DAY}, s-maxage=${CONSTANTS.TWO_DAY}`, `max-age=${CONSTANTS.TWO_DAY}, s-maxage=${CONSTANTS.TWO_DAY}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); );
}); });
}); });
+3 -3
View File
@@ -224,9 +224,9 @@ describe("Test /api/pin", () => {
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml"); expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.setHeader).toBeCalledWith( expect(res.setHeader).toBeCalledWith(
"Cache-Control", "Cache-Control",
`max-age=${CONSTANTS.PIN_CARD_CACHE_SECONDS}, s-maxage=${ `max-age=${CONSTANTS.PIN_CARD_CACHE_SECONDS}, ` +
CONSTANTS.PIN_CARD_CACHE_SECONDS `s-maxage=${CONSTANTS.PIN_CARD_CACHE_SECONDS}, ` +
}`, `stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); );
}); });
}); });
+3 -3
View File
@@ -230,9 +230,9 @@ describe("Test /api/top-langs", () => {
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml"); expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.setHeader).toBeCalledWith( expect(res.setHeader).toBeCalledWith(
"Cache-Control", "Cache-Control",
`max-age=${CONSTANTS.TOP_LANGS_CACHE_SECONDS / 2}, s-maxage=${ `max-age=${CONSTANTS.TOP_LANGS_CACHE_SECONDS}, ` +
CONSTANTS.TOP_LANGS_CACHE_SECONDS `s-maxage=${CONSTANTS.TOP_LANGS_CACHE_SECONDS}, ` +
}`, `stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); );
}); });
}); });