diff --git a/src/app.ts b/src/app.ts index 099db06..3f5804f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -134,7 +134,7 @@ app.get('/users/:username', actorController); app.get('/nodeinfo/:version', nodeInfoSchemaController); -app.get('/api/v1/instance', cache({ cacheName: 'web', expires: Time.minutes(5) }), instanceController); +app.get('/api/v1/instance', cache({ expires: Time.seconds(3) }), instanceController); app.get('/api/v1/apps/verify_credentials', appCredentialsController); app.post('/api/v1/apps', createAppController); @@ -185,10 +185,10 @@ 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', cache({ expires: Time.minutes(5) }), frontendConfigController); -app.get('/api/v1/trends/tags', cache({ cacheName: 'web', expires: Time.minutes(15) }), trendingTagsController); -app.get('/api/v1/trends', cache({ cacheName: 'web', expires: Time.minutes(15) }), trendingTagsController); +app.get('/api/v1/trends/tags', cache({ expires: Time.minutes(15) }), trendingTagsController); +app.get('/api/v1/trends', cache({ expires: Time.minutes(15) }), trendingTagsController); app.get('/api/v1/notifications', requirePubkey, notificationsController); app.get('/api/v1/favourites', requirePubkey, favouritesController); diff --git a/src/middleware/cache.ts b/src/middleware/cache.ts index 87de611..c897645 100644 --- a/src/middleware/cache.ts +++ b/src/middleware/cache.ts @@ -1,26 +1,30 @@ import { Debug, type MiddlewareHandler } from '@/deps.ts'; -import ExpiringCache from '@/utils/expiring-cache.ts'; const debug = Debug('ditto:middleware:cache'); -export const cache = (options: { - cacheName: string; - expires?: number; -}): MiddlewareHandler => { +interface CacheOpts { + expires: number; +} + +/** In-memory cache middleware. */ +export const cache = (opts: CacheOpts): MiddlewareHandler => { + let response: Response | undefined; + let expires = Date.now() + opts.expires; + return async (c, next) => { - const key = c.req.url.replace('http://', 'https://'); - const cache = new ExpiringCache(await caches.open(options.cacheName)); - const response = await cache.match(key); - if (!response) { + if (!response || (Date.now() > expires)) { debug('Building cache for page', c.req.url); + expires = Date.now() + opts.expires; + await next(); - const response = c.res.clone(); - if (response.status < 500) { - await cache.putExpiring(key, response, options.expires ?? 0); + + const res = c.res.clone(); + if (res.status < 500) { + response = res; } } else { debug('Serving page from cache', c.req.url); - return response; + return response.clone(); } }; }; diff --git a/src/utils/expiring-cache.test.ts b/src/utils/expiring-cache.test.ts deleted file mode 100644 index 9827de8..0000000 --- a/src/utils/expiring-cache.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { assert } from '@/deps-test.ts'; - -import ExpiringCache from './expiring-cache.ts'; - -Deno.test('ExpiringCache', async () => { - const cache = new ExpiringCache(await caches.open('test')); - - await cache.putExpiring('http://mostr.local/1', new Response('hello world'), 300); - await cache.putExpiring('http://mostr.local/2', new Response('hello world'), -1); - - // const resp1 = await cache.match('http://mostr.local/1'); - const resp2 = await cache.match('http://mostr.local/2'); - - // assert(resp1!.headers.get('Expires')); - assert(!resp2); - - // await resp1!.text(); -}); diff --git a/src/utils/expiring-cache.ts b/src/utils/expiring-cache.ts deleted file mode 100644 index ebb5d2e..0000000 --- a/src/utils/expiring-cache.ts +++ /dev/null @@ -1,68 +0,0 @@ -class ExpiringCache implements Cache { - #cache: Cache; - - constructor(cache: Cache) { - this.#cache = cache; - } - - add(request: RequestInfo | URL): Promise { - return this.#cache.add(request); - } - - addAll(requests: RequestInfo[]): Promise { - return this.#cache.addAll(requests); - } - - keys(request?: RequestInfo | URL | undefined, options?: CacheQueryOptions | undefined): Promise { - return this.#cache.keys(request, options); - } - - matchAll( - request?: RequestInfo | URL | undefined, - options?: CacheQueryOptions | undefined, - ): Promise { - return this.#cache.matchAll(request, options); - } - - put(request: RequestInfo | URL, response: Response): Promise { - return this.#cache.put(request, response); - } - - putExpiring(request: RequestInfo | URL, response: Response, expiresIn: number): Promise { - const expires = Date.now() + expiresIn; - - const clone = new Response(response.body, { - status: response.status, - headers: { - expires: new Date(expires).toUTCString(), - ...Object.fromEntries(response.headers.entries()), - }, - }); - - return this.#cache.put(request, clone); - } - - async match(request: RequestInfo | URL, options?: CacheQueryOptions | undefined): Promise { - const response = await this.#cache.match(request, options); - const expires = response?.headers.get('Expires'); - - if (response && expires) { - if (new Date(expires).getTime() > Date.now()) { - return response; - } else { - await Promise.all([ - this.delete(request), - response.text(), // Prevent memory leaks - ]); - } - } else if (response) { - return response; - } - } - - delete(request: RequestInfo | URL, options?: CacheQueryOptions | undefined): Promise { - return this.#cache.delete(request, options); - } -} - -export default ExpiringCache;