From 871222ee4e631ba1017a9f0e4be0fa7619ecea9a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 23 Jan 2025 14:11:10 -0600 Subject: [PATCH] Add Cache-Control headers to a bunch of routes --- src/app.ts | 98 ++++++++++++++++++++++++----- src/controllers/api/fallback.ts | 13 +++- src/controllers/frontend.ts | 1 + src/controllers/well-known/nostr.ts | 8 +-- 4 files changed, 98 insertions(+), 22 deletions(-) diff --git a/src/app.ts b/src/app.ts index 98119602..24d9afe8 100644 --- a/src/app.ts +++ b/src/app.ts @@ -199,15 +199,39 @@ app.use( app.get('/metrics', metricsController); -app.get('/.well-known/nodeinfo', nodeInfoController); +app.get( + '/.well-known/nodeinfo', + cacheControlMiddleware({ maxAge: 300, staleWhileRevalidate: 300, staleIfError: 21600, public: true }), + nodeInfoController, +); app.get('/.well-known/nostr.json', nostrController); -app.get('/nodeinfo/:version', nodeInfoSchemaController); -app.get('/manifest.webmanifest', manifestController); +app.get( + '/nodeinfo/:version', + cacheControlMiddleware({ maxAge: 300, staleWhileRevalidate: 300, staleIfError: 21600, public: true }), + nodeInfoSchemaController, +); +app.get( + '/manifest.webmanifest', + cacheControlMiddleware({ maxAge: 5, staleWhileRevalidate: 5, staleIfError: 21600, public: true }), + manifestController, +); -app.get('/api/v1/instance', instanceV1Controller); -app.get('/api/v2/instance', instanceV2Controller); -app.get('/api/v1/instance/extended_description', instanceDescriptionController); +app.get( + '/api/v1/instance', + cacheControlMiddleware({ maxAge: 5, staleWhileRevalidate: 5, staleIfError: 21600, public: true }), + instanceV1Controller, +); +app.get( + '/api/v2/instance', + cacheControlMiddleware({ maxAge: 5, staleWhileRevalidate: 5, staleIfError: 21600, public: true }), + instanceV2Controller, +); +app.get( + '/api/v1/instance/extended_description', + cacheControlMiddleware({ maxAge: 5, staleWhileRevalidate: 5, staleIfError: 21600, public: true }), + instanceDescriptionController, +); app.get('/api/v1/apps/verify_credentials', appCredentialsController); app.post('/api/v1/apps', createAppController); @@ -296,12 +320,28 @@ app.get('/api/v1/preferences', preferencesController); app.get('/api/v1/search', searchController); app.get('/api/v2/search', searchController); -app.get('/api/pleroma/frontend_configurations', frontendConfigController); +app.get( + '/api/pleroma/frontend_configurations', + cacheControlMiddleware({ maxAge: 5, staleWhileRevalidate: 5, staleIfError: 21600, public: true }), + frontendConfigController, +); app.get('/api/v1/trends/statuses', rateLimitMiddleware(8, Time.seconds(30)), trendingStatusesController); -app.get('/api/v1/trends/links', trendingLinksController); -app.get('/api/v1/trends/tags', trendingTagsController); -app.get('/api/v1/trends', trendingTagsController); +app.get( + '/api/v1/trends/links', + cacheControlMiddleware({ maxAge: 300, staleWhileRevalidate: 300, staleIfError: 21600, public: true }), + trendingLinksController, +); +app.get( + '/api/v1/trends/tags', + cacheControlMiddleware({ maxAge: 300, staleWhileRevalidate: 300, staleIfError: 21600, public: true }), + trendingTagsController, +); +app.get( + '/api/v1/trends', + cacheControlMiddleware({ maxAge: 300, staleWhileRevalidate: 300, staleIfError: 21600, public: true }), + trendingTagsController, +); app.get('/api/v1/suggestions', suggestionsV1Controller); app.get('/api/v2/suggestions', suggestionsV2Controller); @@ -345,7 +385,11 @@ app.post( captchaVerifyController, ); -app.get('/api/v1/ditto/zap_splits', getZapSplitsController); +app.get( + '/api/v1/ditto/zap_splits', + cacheControlMiddleware({ maxAge: 5, staleWhileRevalidate: 5, public: true }), + getZapSplitsController, +); app.get('/api/v1/ditto/:id{[0-9a-f]{64}}/zap_splits', statusZapSplitsController); app.put('/api/v1/admin/ditto/zap_splits', requireRole('admin'), updateZapSplitsController); @@ -409,12 +453,36 @@ app.get('/timeline/*', frontendController); // Known static file routes app.get('/sw.js', publicFiles); -app.get('/favicon.ico', publicFiles, staticFiles); -app.get('/images/*', publicFiles, staticFiles); -app.get('/instance/*', publicFiles); +app.get( + '/favicon.ico', + cacheControlMiddleware({ maxAge: 5, staleWhileRevalidate: 5, staleIfError: 21600, public: true }), + publicFiles, + staticFiles, +); +app.get( + '/images/*', + cacheControlMiddleware({ maxAge: 5, staleWhileRevalidate: 5, staleIfError: 21600, public: true }), + publicFiles, + staticFiles, +); +app.get( + '/instance/*', + cacheControlMiddleware({ maxAge: 5, staleWhileRevalidate: 5, staleIfError: 21600, public: true }), + publicFiles, +); // Packs contains immutable static files -app.get('/packs/*', cacheControlMiddleware({ maxAge: 31536000, public: true, immutable: true }), publicFiles); +app.get( + '/packs/*', + cacheControlMiddleware({ + maxAge: 31536000, + staleWhileRevalidate: 86400, + staleIfError: 21600, + public: true, + immutable: true, + }), + publicFiles, +); // Site index app.get('/', frontendController, indexController); diff --git a/src/controllers/api/fallback.ts b/src/controllers/api/fallback.ts index 0e98ac79..5794c544 100644 --- a/src/controllers/api/fallback.ts +++ b/src/controllers/api/fallback.ts @@ -1,6 +1,13 @@ -import { type Context } from '@hono/hono'; +import { Handler } from '@hono/hono'; -const emptyArrayController = (c: Context) => c.json([]); -const notImplementedController = (c: Context) => Promise.resolve(c.json({ error: 'Not implemented' }, 404)); +const emptyArrayController: Handler = (c) => { + c.header('Cache-Control', 'max-age=300, public, stale-while-revalidate=60'); + return c.json([]); +}; + +const notImplementedController: Handler = (c) => { + c.header('Cache-Control', 'max-age=300, public, stale-while-revalidate=60'); + return c.json({ error: 'Not implemented' }, 404); +}; export { emptyArrayController, notImplementedController }; diff --git a/src/controllers/frontend.ts b/src/controllers/frontend.ts index b1a3bba4..31a19b92 100644 --- a/src/controllers/frontend.ts +++ b/src/controllers/frontend.ts @@ -31,6 +31,7 @@ export const frontendController: AppMiddleware = async (c, next) => { try { const entities = await getEntities(params ?? {}); const meta = renderMetadata(c.req.url, entities); + c.header('Cache-Control', 'max-age=30, public, stale-while-revalidate=30'); return c.html(content.replace(META_PLACEHOLDER, meta)); } catch (e) { console.log(`Error building meta tags: ${e}`); diff --git a/src/controllers/well-known/nostr.ts b/src/controllers/well-known/nostr.ts index 5a2017bb..4fd366e7 100644 --- a/src/controllers/well-known/nostr.ts +++ b/src/controllers/well-known/nostr.ts @@ -14,7 +14,7 @@ const emptyResult: NostrJson = { names: {}, relays: {} }; const nostrController: AppController = async (c) => { // If there are no query parameters, this will always return an empty result. if (!Object.entries(c.req.queries()).length) { - c.header('Cache-Control', 'max-age=31536000, public, immutable'); + c.header('Cache-Control', 'max-age=31536000, public, immutable, stale-while-revalidate=86400'); return c.json(emptyResult); } @@ -26,14 +26,14 @@ const nostrController: AppController = async (c) => { if (!name || !pointer) { // Not found, cache for 5 minutes. - c.header('Cache-Control', 'max-age=300, public'); + c.header('Cache-Control', 'max-age=300, public, stale-while-revalidate=30'); return c.json(emptyResult); } const { pubkey, relays = [] } = pointer; - // It's found, so cache for 12 hours. - c.header('Cache-Control', 'max-age=43200, public'); + // It's found, so cache for 6 hours. + c.header('Cache-Control', 'max-age=21600, public, stale-while-revalidate=3600'); return c.json( {