From 808efd3abe4201dc9e27dbff6a797637fd068fe0 Mon Sep 17 00:00:00 2001 From: Boegie19 Date: Thu, 12 Oct 2023 22:03:45 +0200 Subject: [PATCH 1/6] inject middleware instead of using router --- package.json | 5 +- .../server/bootstrap.js | 85 +-------- .../strapi-plugin-rest-cache/server/index.js | 5 +- .../server/register.js | 91 ++++++++++ .../utils/config/resolveUserStrategy.js | 160 +++++++---------- .../server/utils/middlewares/createRouter.js | 120 ------------- .../server/utils/middlewares/flattenRoutes.js | 35 ++++ .../utils/middlewares/injectMiddlewares.js | 164 ++++++++++++++++++ 8 files changed, 360 insertions(+), 305 deletions(-) create mode 100644 packages/strapi-plugin-rest-cache/server/register.js delete mode 100644 packages/strapi-plugin-rest-cache/server/utils/middlewares/createRouter.js create mode 100644 packages/strapi-plugin-rest-cache/server/utils/middlewares/flattenRoutes.js create mode 100644 packages/strapi-plugin-rest-cache/server/utils/middlewares/injectMiddlewares.js diff --git a/package.json b/package.json index e70292c4f..4f398d940 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,9 @@ "dev:memory": "yarn workspace @strapi-plugin-rest-cache/playground-memory develop", "dev:redis": "yarn workspace @strapi-plugin-rest-cache/playground-redis develop", "dev:couchbase": "yarn workspace @strapi-plugin-rest-cache/playground-couchbase develop", + "console:memory": "yarn workspace @strapi-plugin-rest-cache/playground-memory strapi console", + "console:redis": "yarn workspace @strapi-plugin-rest-cache/playground-redis strapi console", + "console:couchbase": "yarn workspace @strapi-plugin-rest-cache/playground-couchbase strapi console", "profile:memory": "cd ./playgrounds/memory && 0x -o -D .profile -P 'autocannon --warmup [ -c 1 -d 3 ] -c 100 -p 10 -d 120 http://localhost:1337/api/homepage?populate=*' start-profiler.js", "profile:redis": "cd ./playgrounds/redis && 0x -o -D .profile -P 'autocannon --warmup [ -c 1 -d 3 ] -c 100 -p 10 -d 120 http://localhost:1337/api/homepage?populate=*' start-profiler.js", "profile:couchbase": "cd ./playgrounds/couchbase && 0x -o -D .profile -P 'autocannon --warmup [ -c 1 -d 3 ] -c 100 -p 10 -d 120 http://localhost:1337/api/homepage?populate=*' start-profiler.js", @@ -71,4 +74,4 @@ "prettier --write" ] } -} +} \ No newline at end of file diff --git a/packages/strapi-plugin-rest-cache/server/bootstrap.js b/packages/strapi-plugin-rest-cache/server/bootstrap.js index 2cf9aac94..96fdfd9ef 100644 --- a/packages/strapi-plugin-rest-cache/server/bootstrap.js +++ b/packages/strapi-plugin-rest-cache/server/bootstrap.js @@ -1,74 +1,13 @@ 'use strict'; -/** - * @typedef {import('@strapi/strapi').Strapi} Strapi - */ -const chalk = require('chalk'); -const debug = require('debug'); - -const { CacheProvider } = require('./types'); -const { resolveUserStrategy } = require('./utils/config/resolveUserStrategy'); -const { createRouter } = require('./utils/middlewares/createRouter'); - const permissionsActions = require('./permissions-actions'); -const createProvider = async (providerConfig, { strapi }) => { - const providerName = providerConfig.name.toLowerCase(); - let provider; - - let modulePath; - try { - modulePath = require.resolve(`strapi-provider-rest-cache-${providerName}`); - } catch (error) { - if (error.code === 'MODULE_NOT_FOUND') { - modulePath = providerName; - } else { - throw error; - } - } - - try { - // eslint-disable-next-line - provider = require(modulePath); - } catch (err) { - throw new Error( - `Could not load REST Cache provider "${providerName}". You may need to install a provider plugin "yarn add strapi-provider-rest-cache-${providerName}".` - ); - } - - const providerInstance = await provider.init(providerConfig.options, { - strapi, - }); - - if (!(providerInstance instanceof CacheProvider)) { - throw new Error( - `Could not load REST Cache provider "${providerName}". The package "strapi-provider-rest-cache-${providerName}" does not export a CacheProvider instance.` - ); - } - - return Object.freeze(providerInstance); -}; - /** * @param {{ strapi: Strapi }} strapi */ async function bootstrap({ strapi }) { - // resolve user configuration, check for missing or invalid options - const pluginOption = strapi.config.get('plugin.rest-cache'); - const cacheStore = strapi.plugin('rest-cache').service('cacheStore'); - - if (pluginOption.strategy.debug === true) { - debug.enable('strapi:strapi-plugin-rest-cache'); - } - - const strategy = resolveUserStrategy(strapi, pluginOption.strategy); - strapi.config.set('plugin.rest-cache', { - ...pluginOption, - strategy, - }); - - debug('strapi:strapi-plugin-rest-cache')('[STRATEGY]: %O', strategy); + const cacheStore = strapi.plugin('rest-cache').service('cacheStore'); // watch for changes in any roles -> clear all cache // need to be done before lifecycles are registered if (strapi.plugin('users-permissions')) { @@ -79,32 +18,12 @@ async function bootstrap({ strapi }) { }, }); } - - // register cache provider - const provider = await createProvider(pluginOption.provider, { strapi }); - strapi.plugin('rest-cache').service('cacheStore').init(provider); - // boostrap plugin permissions await strapi.admin.services.permission.actionProvider.registerMany( permissionsActions.actions ); - - // boostrap cache middlewares - const router = createRouter(strapi, strategy); - strapi.server.router.use(router.routes()); - - strapi.log.info( - `Using REST Cache plugin with provider "${chalk.cyan( - pluginOption.provider.name - )}"` - ); - - if (strategy.resetOnStartup) { - strapi.log.warn('Reset cache on startup is enabled'); - await strapi.plugin('rest-cache').service('cacheStore').reset(); - } } module.exports = { - bootstrap, + bootstrap, }; diff --git a/packages/strapi-plugin-rest-cache/server/index.js b/packages/strapi-plugin-rest-cache/server/index.js index bddd61252..70679e3b2 100644 --- a/packages/strapi-plugin-rest-cache/server/index.js +++ b/packages/strapi-plugin-rest-cache/server/index.js @@ -6,15 +6,14 @@ const { config } = require('./config'); const { controllers } = require('./controllers'); const { middlewares } = require('./middlewares'); const { routes } = require('./routes'); +const { register } = require('./register') module.exports = { bootstrap, - destroy() {}, + register, config, controllers, routes, services, - contentTypes: {}, - policies: {}, middlewares, }; diff --git a/packages/strapi-plugin-rest-cache/server/register.js b/packages/strapi-plugin-rest-cache/server/register.js new file mode 100644 index 000000000..1a8c55d83 --- /dev/null +++ b/packages/strapi-plugin-rest-cache/server/register.js @@ -0,0 +1,91 @@ +'use strict'; + +/** + * @typedef {import('@strapi/strapi').Strapi} Strapi + */ +const chalk = require('chalk'); +const debug = require('debug'); + +const { CacheProvider } = require('./types'); +const { resolveUserStrategy } = require('./utils/config/resolveUserStrategy'); +const { injectMiddlewares } = require('./utils/middlewares/injectMiddlewares'); + +const createProvider = async (providerConfig, { strapi }) => { + const providerName = providerConfig.name.toLowerCase(); + let provider; + + let modulePath; + try { + modulePath = require.resolve(`strapi-provider-rest-cache-${providerName}`); + } catch (error) { + if (error.code === 'MODULE_NOT_FOUND') { + modulePath = providerName; + } else { + throw error; + } + } + + try { + // eslint-disable-next-line + provider = require(modulePath); + } catch (err) { + throw new Error( + `Could not load REST Cache provider "${providerName}". You may need to install a provider plugin "yarn add strapi-provider-rest-cache-${providerName}".` + ); + } + + const providerInstance = await provider.init(providerConfig.options, { + strapi, + }); + + if (!(providerInstance instanceof CacheProvider)) { + throw new Error( + `Could not load REST Cache provider "${providerName}". The package "strapi-provider-rest-cache-${providerName}" does not export a CacheProvider instance.` + ); + } + + return Object.freeze(providerInstance); +}; + +/** + * @param {{ strapi: Strapi }} strapi + */ +async function register({ strapi }) { + // resolve user configuration, check for missing or invalid options + const pluginOption = strapi.config.get('plugin.rest-cache'); + const cacheStore = strapi.plugin('rest-cache').service('cacheStore'); + + if (pluginOption.strategy.debug === true) { + debug.enable('strapi:strapi-plugin-rest-cache'); + } + + const strategy = resolveUserStrategy(strapi, pluginOption.strategy); + strapi.config.set('plugin.rest-cache', { + ...pluginOption, + strategy, + }); + + debug('strapi:strapi-plugin-rest-cache')('[STRATEGY]: %O', strategy); + + // register cache provider + const provider = await createProvider(pluginOption.provider, { strapi }); + cacheStore.init(provider); + + // boostrap cache middlewares + injectMiddlewares(strapi, strategy); + + strapi.log.info( + `Using REST Cache plugin with provider "${chalk.cyan( + pluginOption.provider.name + )}"` + ); + + if (strategy.resetOnStartup) { + strapi.log.warn('Reset cache on startup is enabled'); + await cacheStore.reset(); + } +} + +module.exports = { + register, +}; diff --git a/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js b/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js index ae3f5e82c..207d869b3 100644 --- a/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js +++ b/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js @@ -17,6 +17,7 @@ const { } = require('../../types'); const routeParamNameRegex = /:([^/]+)/g; +const routeParams = /(?<=\/\:).*?(?=\/|$)/g; /** * @param {Strapi} strapi @@ -39,7 +40,9 @@ function resolveUserStrategy(strapi, userOptions) { maxAge: userOptions.maxAge, }; + // Creating cache Config for (const contentTypeOption of contentTypes) { + // string if (typeof contentTypeOption === 'string') { cacheConfigs.push({ ...defaultModelConfig, @@ -50,6 +53,7 @@ function resolveUserStrategy(strapi, userOptions) { continue; } + // Object /** * @type {CacheRouteConfig[]} */ @@ -71,6 +75,7 @@ function resolveUserStrategy(strapi, userOptions) { }) ); } else { + // @TODO get the route of the value maby replace route with handler. acc.push( new CacheRouteConfig({ maxAge: defaultModelConfig.maxAge, @@ -126,105 +131,64 @@ function resolveUserStrategy(strapi, userOptions) { // get strapi api prefix const apiPrefix = strapi.config.get('api.rest.prefix'); - // https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest-api.html#api-endpoints - // https://github.com/strapi/strapi/blob/master/packages/core/strapi/lib/core-api/routes/index.js - if (cacheConfig.singleType) { - const base = `${apiPrefix}/${contentType.info.singularName}`; - - // delete - cacheConfig.routes.push( - new CacheRouteConfig({ - path: base, - method: 'DELETE', - keys: new CacheKeysConfig(cacheConfig.keys), - }) - ); - // update - cacheConfig.routes.push( - new CacheRouteConfig({ - path: base, - method: 'PUT', - keys: new CacheKeysConfig(cacheConfig.keys), - }) - ); - // find - cacheConfig.routes.push( - new CacheRouteConfig({ - path: base, - method: 'GET', - keys: new CacheKeysConfig(cacheConfig.keys), - maxAge: cacheConfig.maxAge, - hitpass: cacheConfig.hitpass, - }) - ); - } else { - const base = `${apiPrefix}/${contentType.info.pluralName}`; - - // create - cacheConfig.routes.push( - new CacheRouteConfig({ - path: base, - method: 'POST', - keys: new CacheKeysConfig(cacheConfig.keys), - }) - ); - // delete - cacheConfig.routes.push( - new CacheRouteConfig({ - path: `${base}/:id`, - method: 'DELETE', - keys: new CacheKeysConfig(cacheConfig.keys), - paramNames: ['id'], - }) - ); - // update - cacheConfig.routes.push( - new CacheRouteConfig({ - path: `${base}/:id`, - method: 'PUT', - keys: new CacheKeysConfig(cacheConfig.keys), - paramNames: ['id'], - }) - ); - - // find - cacheConfig.routes.push( - new CacheRouteConfig({ - path: base, - method: 'GET', - keys: new CacheKeysConfig(cacheConfig.keys), - maxAge: cacheConfig.maxAge, - hitpass: cacheConfig.hitpass, - }) - ); - // findOne - cacheConfig.routes.push( - new CacheRouteConfig({ - path: `${base}/:id`, - method: 'GET', - paramNames: ['id'], - keys: new CacheKeysConfig(cacheConfig.keys), - maxAge: cacheConfig.maxAge, - hitpass: cacheConfig.hitpass, - }) - ); - } - } - - for (const cacheConfig of cacheConfigs) { - cacheConfig.routes = cacheConfig.routes.filter((route) => { - const exist = routeExists(strapi, route); - - if (!exist) { - debug( - '[WARNING] route "[%s] %s" not registered in strapi, ignoring...', - route.method, - route.path - ); + for (const routes of Object.values( + strapi.api[contentType.info.name].routes + )) { + for (const route of routes.routes) { + // @TODO remove path and method and use the one + if (cacheConfig.singleType === true) { + const singleTypeMethod = ['GET', 'PUT', 'DELETE']; + if ( + singleTypeMethod.includes(route.method) && + route.path === `/${contentType.info.singularName}` + ) { + cacheConfig.routes.push( + new CacheRouteConfig({ + path: `/api${route.path}`, + paramNames: route.path.match(routeParams) ?? [], + method: route.method, + keys: new CacheKeysConfig(cacheConfig.keys), + maxAge: cacheConfig.maxAge, + hitpass: cacheConfig.hitpass, + }) + ); + } + } else { + const CollectionTypeMethod = ['GET', 'POST']; + const CollectionTypeIdMethod = ['GET', 'PUT', 'DELETE']; + if ( + CollectionTypeMethod.includes(route.method) && + route.path === `/${contentType.info.pluralName}` + ) { + cacheConfig.routes.push( + new CacheRouteConfig({ + path: `/api${route.path}`, + paramNames: route.path.match(routeParams) ?? [], + method: route.method, + keys: new CacheKeysConfig(cacheConfig.keys), + maxAge: cacheConfig.maxAge, + hitpass: cacheConfig.hitpass, + }) + ); + } + if ( + CollectionTypeIdMethod.includes(route.method) && + route.path === `/${contentType.info.pluralName}/:id` + ) { + cacheConfig.routes.push( + new CacheRouteConfig({ + path: `/api${route.path}`, + paramNames: route.path.match(routeParams) ?? [], + method: route.method, + keys: new CacheKeysConfig(cacheConfig.keys), + maxAge: cacheConfig.maxAge, + hitpass: cacheConfig.hitpass, + }) + ); + } + } } - - return exist; - }); + } } return deepFreeze( diff --git a/packages/strapi-plugin-rest-cache/server/utils/middlewares/createRouter.js b/packages/strapi-plugin-rest-cache/server/utils/middlewares/createRouter.js deleted file mode 100644 index 14dd4a2f3..000000000 --- a/packages/strapi-plugin-rest-cache/server/utils/middlewares/createRouter.js +++ /dev/null @@ -1,120 +0,0 @@ -'use strict'; - -const Router = require('@koa/router'); -const chalk = require('chalk'); -const debug = require('debug')('strapi:strapi-plugin-rest-cache'); - -/** - * @typedef {import('../../types').CachePluginStrategy} CachePluginStrategy - */ - -// @see https://github.com/strapi/strapi/blob/master/packages/core/content-manager/server/routes/admin.js -const adminRoutes = { - post: [ - '/content-manager/single-types/:model/actions/publish', - '/content-manager/single-types/:model/actions/unpublish', - '/content-manager/collection-types/:model', - '/content-manager/collection-types/:model/:id/actions/publish', - '/content-manager/collection-types/:model/:id/actions/unpublish', - '/content-manager/collection-types/:model/actions/bulkDelete', - ], - put: [ - '/content-manager/single-types/:model', - '/content-manager/collection-types/:model/:id', - ], - delete: [ - '/content-manager/single-types/:model', - '/content-manager/collection-types/:model/:id', - ], -}; - -/** - * @param {Strapi} strapi - * @param {CachePluginStrategy} strategy - * @return {Router} - */ -function createRouter(strapi, strategy) { - const router = new Router(); - - const createRecvMiddleware = strapi.plugin('rest-cache').middleware('recv'); - const createPurgeMiddleware = strapi.plugin('rest-cache').middleware('purge'); - const createPurgeAdminMiddleware = strapi - .plugin('rest-cache') - .middleware('purgeAdmin'); - const purgeAdminMiddleware = createPurgeAdminMiddleware({}, { strapi }); - - for (const cacheConf of strategy.contentTypes) { - debug(`[REGISTER] ${chalk.cyan(cacheConf.contentType)} routes middlewares`); - - const purgeMiddleware = createPurgeMiddleware( - { contentType: cacheConf.contentType }, - { strapi } - ); - - for (const route of cacheConf.routes) { - switch (route.method) { - case 'DELETE': { - debug(`[REGISTER] DELETE ${route.path} ${chalk.redBright('purge')}`); - router.delete(route.path, purgeMiddleware); - break; - } - case 'PUT': { - debug(`[REGISTER] PUT ${route.path} ${chalk.redBright('purge')}`); - router.put(route.path, purgeMiddleware); - break; - } - case 'PATCH': { - debug(`[REGISTER] PATCH ${route.path} ${chalk.redBright('purge')}`); - router.patch(route.path, purgeMiddleware); - break; - } - case 'POST': { - debug(`[REGISTER] POST ${route.path} ${chalk.redBright('purge')}`); - router.post(route.path, purgeMiddleware); - break; - } - case 'GET': { - const vary = route.keys.useHeaders - .map((name) => name.toLowerCase()) - .join(','); - - debug( - `[REGISTER] GET ${route.path} ${chalk.green('recv')} ${chalk.grey( - `maxAge=${route.maxAge}` - )}${vary && chalk.grey(` vary=${vary}`)}` - ); - - router.get( - route.path, - createRecvMiddleware({ cacheRouteConfig: route }, { strapi }) - ); - break; - } - default: - break; - } - } - } - - // --- Admin REST endpoints - if (strategy.enableAdminCTBMiddleware) { - debug(`[REGISTER] ${chalk.magentaBright('admin')} routes middlewares`); - - for (const route of adminRoutes.post) { - debug(`[REGISTER] POST ${route} ${chalk.magentaBright('purge-admin')}`); - router.post(route, purgeAdminMiddleware); - } - for (const route of adminRoutes.put) { - debug(`[REGISTER] PUT ${route} ${chalk.magentaBright('purge-admin')}`); - router.put(route, purgeAdminMiddleware); - } - for (const route of adminRoutes.delete) { - debug(`[REGISTER] DELETE ${route} ${chalk.magentaBright('purge-admin')}`); - router.delete(route, purgeAdminMiddleware); - } - } - - return router; -} - -module.exports = { createRouter }; diff --git a/packages/strapi-plugin-rest-cache/server/utils/middlewares/flattenRoutes.js b/packages/strapi-plugin-rest-cache/server/utils/middlewares/flattenRoutes.js new file mode 100644 index 000000000..25fcc48bf --- /dev/null +++ b/packages/strapi-plugin-rest-cache/server/utils/middlewares/flattenRoutes.js @@ -0,0 +1,35 @@ +'use strict'; + +/** + * @param {Strapi} strapi + * @return {void} + */ +function flattenRoutes(strapi) { + let routes = []; + for (const contentTypes of Object.values(strapi.api)) { + routes = routes.concat(flatten(contentTypes)); + } + // @TODO add prefix support before doing this + for (const route of routes) { + route.globalPath = `/api${route.path}`; + } + return routes; +} + +function flatten(routes) { + let returnRoutes = []; + if (Array.isArray(routes)) { + return routes; + } + if (Array.isArray(routes.routes)) { + return routes.routes; + } + for (const route of Object.values(routes.routes)) { + returnRoutes = returnRoutes.concat(flatten(route)); + } + return returnRoutes; +} + +module.exports = { + flattenRoutes, +}; diff --git a/packages/strapi-plugin-rest-cache/server/utils/middlewares/injectMiddlewares.js b/packages/strapi-plugin-rest-cache/server/utils/middlewares/injectMiddlewares.js new file mode 100644 index 000000000..ae730116e --- /dev/null +++ b/packages/strapi-plugin-rest-cache/server/utils/middlewares/injectMiddlewares.js @@ -0,0 +1,164 @@ +'use strict'; + +const chalk = require('chalk'); +const debug = require('debug')('strapi:strapi-plugin-rest-cache'); +const { flattenRoutes } = require('./flattenRoutes'); + +const adminRoutes = { + post: [ + '/single-types/:model/actions/publish', + '/single-types/:model/actions/unpublish', + '/collection-types/:model', + '/collection-types/:model/:id/actions/publish', + '/collection-types/:model/:id/actions/unpublish', + '/collection-types/:model/actions/bulkDelete', + ], + put: ['/single-types/:model', '/collection-types/:model/:id'], + delete: ['/single-types/:model', '/collection-types/:model/:id'], +}; + +function injectMiddleware(route, pluginUUid, config = {}) { + if (typeof route.config === 'undefined') { + route.config = {}; + } + route['rest-cache-config'] = config; + if (typeof route.config.middlewares === 'undefined') { + route.config.middlewares = [ + { + name: pluginUUid, + config: config, + }, + ]; + } else { + route.config.middlewares.push({ + name: pluginUUid, + config: config, + }); + } +} + +/** + * @param {Strapi} strapi + * @param {CachePluginStrategy} strategy + * @return {void} + */ +function injectMiddlewares(strapi, strategy) { + const strapiRoutes = flattenRoutes(strapi); + + for (const cacheConf of strategy.contentTypes) { + debug(`[REGISTER] ${chalk.cyan(cacheConf.contentType)} routes middlewares`); + for (const cacheRoute of cacheConf.routes) { + const indexID = strapiRoutes.findIndex( + (route) => + // You can modify this to search for a specific route or multiple + route.method === cacheRoute.method && + //below replace removes the + at the end of the line + route.globalPath === cacheRoute.path.replace(/\+$/, '') + ); + + // If the route exists lets inject the middleware + if (indexID > -1) { + switch (strapiRoutes[indexID].method) { + case 'DELETE': + case 'PUT': + case 'PATCH': + case 'POST': + debug( + `[REGISTER] ${cacheRoute.method} ${ + cacheRoute.path + } ${chalk.redBright('purge')}` + ); + injectMiddleware( + strapiRoutes[indexID], + 'plugin::rest-cache.purge', + { + contentType: cacheConf.contentType, + } + ); + break; + case 'GET': { + const vary = cacheRoute.keys.useHeaders + .map((name) => name.toLowerCase()) + .join(','); + + debug( + `[REGISTER] GET ${cacheRoute.path} ${chalk.green( + 'recv' + )} ${chalk.grey(`maxAge=${cacheRoute.maxAge}`)}${ + vary && chalk.grey(` vary=${vary}`) + }` + ); + injectMiddleware(strapiRoutes[indexID], 'plugin::rest-cache.recv', { + cacheRouteConfig: cacheRoute, + }); + break; + } + default: + break; + } + } + } + } + + // --- Admin REST endpoints + if (strategy.enableAdminCTBMiddleware) { + debug(`[REGISTER] ${chalk.magentaBright('admin')} routes middlewares`); + let contentMangerRoutes = []; + for (const routes of Object.values( + strapi.plugins['content-manager'].routes + )) { + for (const route of routes.routes) { + contentMangerRoutes = contentMangerRoutes.concat(route); + } + } + + for (const route of adminRoutes.post) { + const indexID = contentMangerRoutes.findIndex( + (strapiRoute) => + // You can modify this to search for a specific route or multiple + strapiRoute.method === 'POST' && strapiRoute.globalPath === route + ); + if (indexID !== -1) { + debug(`[REGISTER] POST ${route} ${chalk.magentaBright('purge-admin')}`); + injectMiddleware( + contentMangerRoutes[indexID], + 'plugin::rest-cache.purgeAdmin' + ); + } + } + for (const route of adminRoutes.put) { + const indexID = contentMangerRoutes.findIndex( + (strapiRoute) => + // You can modify this to search for a specific route or multiple + strapiRoute.method === 'PUT' && strapiRoute.globalPath === route + ); + if (indexID !== -1) { + debug(`[REGISTER] PUT ${route} ${chalk.magentaBright('purge-admin')}`); + injectMiddleware( + contentMangerRoutes[indexID], + 'plugin::rest-cache.purgeAdmin' + ); + } + } + for (const route of adminRoutes.delete) { + const indexID = contentMangerRoutes.findIndex( + (strapiRoute) => + // You can modify this to search for a specific route or multiple + strapiRoute.method === 'PUT' && strapiRoute.globalPath === route + ); + if (indexID !== -1) { + debug( + `[REGISTER] DELETE ${route} ${chalk.magentaBright('purge-admin')}` + ); + injectMiddleware( + contentMangerRoutes[indexID], + 'plugin::rest-cache.purgeAdmin' + ); + } + } + } +} + +module.exports = { + injectMiddlewares, +}; From 7b32656e6ade23e46fd424e739344e203062d8b4 Mon Sep 17 00:00:00 2001 From: Boegie19 Date: Thu, 12 Oct 2023 22:09:33 +0200 Subject: [PATCH 2/6] add warning if route is not found --- .../server/utils/middlewares/injectMiddlewares.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/strapi-plugin-rest-cache/server/utils/middlewares/injectMiddlewares.js b/packages/strapi-plugin-rest-cache/server/utils/middlewares/injectMiddlewares.js index ae730116e..5e07fcf75 100644 --- a/packages/strapi-plugin-rest-cache/server/utils/middlewares/injectMiddlewares.js +++ b/packages/strapi-plugin-rest-cache/server/utils/middlewares/injectMiddlewares.js @@ -57,7 +57,13 @@ function injectMiddlewares(strapi, strategy) { ); // If the route exists lets inject the middleware - if (indexID > -1) { + if (indexID === -1) { + debug( + '[WARNING] route "[%s] %s" not registered in strapi, ignoring...', + cacheRoute.method, + cacheRoute.path + ); + } else { switch (strapiRoutes[indexID].method) { case 'DELETE': case 'PUT': From 2c55dd84c689cdc2374a3573e7fab8194364f020 Mon Sep 17 00:00:00 2001 From: Boegie19 Date: Thu, 12 Oct 2023 22:23:41 +0200 Subject: [PATCH 3/6] fixed harding api and removed not used stuff --- .../utils/config/resolveUserStrategy.js | 8 ++--- .../server/utils/config/routeExists.js | 30 ------------------- .../server/utils/middlewares/flattenRoutes.js | 6 +++- 3 files changed, 8 insertions(+), 36 deletions(-) delete mode 100644 packages/strapi-plugin-rest-cache/server/utils/config/routeExists.js diff --git a/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js b/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js index 207d869b3..444101f1a 100644 --- a/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js +++ b/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js @@ -5,10 +5,8 @@ * @typedef {import('@strapi/strapi').Strapi} Strapi */ -const debug = require('debug')('strapi:strapi-plugin-rest-cache'); const { getRelatedModelsUid } = require('./getRelatedModelsUid'); const { deepFreeze } = require('./deepFreeze'); -const { routeExists } = require('./routeExists'); const { CachePluginStrategy, CacheRouteConfig, @@ -144,7 +142,7 @@ function resolveUserStrategy(strapi, userOptions) { ) { cacheConfig.routes.push( new CacheRouteConfig({ - path: `/api${route.path}`, + path: `/${apiPrefix}${route.path}`, paramNames: route.path.match(routeParams) ?? [], method: route.method, keys: new CacheKeysConfig(cacheConfig.keys), @@ -162,7 +160,7 @@ function resolveUserStrategy(strapi, userOptions) { ) { cacheConfig.routes.push( new CacheRouteConfig({ - path: `/api${route.path}`, + path: `/${apiPrefix}${route.path}`, paramNames: route.path.match(routeParams) ?? [], method: route.method, keys: new CacheKeysConfig(cacheConfig.keys), @@ -177,7 +175,7 @@ function resolveUserStrategy(strapi, userOptions) { ) { cacheConfig.routes.push( new CacheRouteConfig({ - path: `/api${route.path}`, + path: `/${apiPrefix}${route.path}`, paramNames: route.path.match(routeParams) ?? [], method: route.method, keys: new CacheKeysConfig(cacheConfig.keys), diff --git a/packages/strapi-plugin-rest-cache/server/utils/config/routeExists.js b/packages/strapi-plugin-rest-cache/server/utils/config/routeExists.js deleted file mode 100644 index 6dd8f631a..000000000 --- a/packages/strapi-plugin-rest-cache/server/utils/config/routeExists.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../../types').CacheRouteConfig} CacheRouteConfig - */ - -/** - * Check if a custom route is registered in strapi - * - * @param {Strapi} strapi - * @param {CacheRouteConfig} route - * @return {boolean} - */ -function routeExists(strapi, route) { - // check api routes - const allRoutes = [ - ...strapi.server.listRoutes(), - ...strapi.server.api('content-api').listRoutes(), - ...strapi.server.api('admin').listRoutes(), - ]; - const match = allRoutes.find( - (routeLayer) => - routeLayer.methods.includes(route.method) && - routeLayer.path.match(new RegExp(`^${route.path}/?`)) // match with optional leading slash - ); - - return Boolean(match); -} - -module.exports = { routeExists }; diff --git a/packages/strapi-plugin-rest-cache/server/utils/middlewares/flattenRoutes.js b/packages/strapi-plugin-rest-cache/server/utils/middlewares/flattenRoutes.js index 25fcc48bf..30426a71e 100644 --- a/packages/strapi-plugin-rest-cache/server/utils/middlewares/flattenRoutes.js +++ b/packages/strapi-plugin-rest-cache/server/utils/middlewares/flattenRoutes.js @@ -4,14 +4,18 @@ * @param {Strapi} strapi * @return {void} */ + function flattenRoutes(strapi) { let routes = []; for (const contentTypes of Object.values(strapi.api)) { routes = routes.concat(flatten(contentTypes)); } // @TODO add prefix support before doing this + + const apiPrefix = strapi.config.get('api.rest.prefix'); + for (const route of routes) { - route.globalPath = `/api${route.path}`; + route.globalPath = `/${apiPrefix}${route.path}`; } return routes; } From d0729e265e67760feeed26785168dd04bda45574 Mon Sep 17 00:00:00 2001 From: Boegie19 Date: Thu, 12 Oct 2023 22:29:52 +0200 Subject: [PATCH 4/6] removed the need to eslint for e2e to run --- .github/workflows/tests.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index de2c8a64d..6374e2180 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -49,7 +49,6 @@ jobs: e2e_memory: runs-on: ubuntu-latest - needs: [linters] steps: - uses: actions/checkout@v2 @@ -81,7 +80,6 @@ jobs: e2e_redis: runs-on: ubuntu-latest - needs: [linters] services: redis: From 708a48ddecba5175096c10caa548046f0c70a40f Mon Sep 17 00:00:00 2001 From: Derrick Mehaffy Date: Sun, 12 Nov 2023 00:53:08 -0700 Subject: [PATCH 5/6] fix: set proper contentType reference name --- .../server/utils/config/resolveUserStrategy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js b/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js index 444101f1a..be6341700 100644 --- a/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js +++ b/packages/strapi-plugin-rest-cache/server/utils/config/resolveUserStrategy.js @@ -130,7 +130,7 @@ function resolveUserStrategy(strapi, userOptions) { const apiPrefix = strapi.config.get('api.rest.prefix'); for (const routes of Object.values( - strapi.api[contentType.info.name].routes + strapi.api[contentType.info.singularName].routes )) { for (const route of routes.routes) { // @TODO remove path and method and use the one From b23779dd625eee613cee31f64a97e7247da69d66 Mon Sep 17 00:00:00 2001 From: Boegie19 Date: Sun, 12 Nov 2023 19:53:01 +0100 Subject: [PATCH 6/6] move provider registration to bootstrap --- .../server/bootstrap.js | 54 ++++++++++++++++++- .../server/register.js | 51 +----------------- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/packages/strapi-plugin-rest-cache/server/bootstrap.js b/packages/strapi-plugin-rest-cache/server/bootstrap.js index 96fdfd9ef..f37ec4139 100644 --- a/packages/strapi-plugin-rest-cache/server/bootstrap.js +++ b/packages/strapi-plugin-rest-cache/server/bootstrap.js @@ -1,12 +1,52 @@ 'use strict'; +const chalk = require('chalk'); + const permissionsActions = require('./permissions-actions'); +const { CacheProvider } = require('./types'); +const createProvider = async (providerConfig, { strapi }) => { + const providerName = providerConfig.name.toLowerCase(); + let provider; + + let modulePath; + try { + modulePath = require.resolve(`strapi-provider-rest-cache-${providerName}`); + } catch (error) { + if (error.code === 'MODULE_NOT_FOUND') { + modulePath = providerName; + } else { + throw error; + } + } + + try { + // eslint-disable-next-line + provider = require(modulePath); + } catch (err) { + throw new Error( + `Could not load REST Cache provider "${providerName}". You may need to install a provider plugin "yarn add strapi-provider-rest-cache-${providerName}".` + ); + } + + const providerInstance = await provider.init(providerConfig.options, { + strapi, + }); + + if (!(providerInstance instanceof CacheProvider)) { + throw new Error( + `Could not load REST Cache provider "${providerName}". The package "strapi-provider-rest-cache-${providerName}" does not export a CacheProvider instance.` + ); + } + + return Object.freeze(providerInstance); +}; /** * @param {{ strapi: Strapi }} strapi */ async function bootstrap({ strapi }) { - + // resolve user configuration, check for missing or invalid optinos + const pluginOption = strapi.config.get('plugin.rest-cache'); const cacheStore = strapi.plugin('rest-cache').service('cacheStore'); // watch for changes in any roles -> clear all cache // need to be done before lifecycles are registered @@ -22,8 +62,18 @@ async function bootstrap({ strapi }) { await strapi.admin.services.permission.actionProvider.registerMany( permissionsActions.actions ); + + // register cache provider + const provider = await createProvider(pluginOption.provider, { strapi }); + cacheStore.init(provider); + + strapi.log.info( + `Using REST Cache plugin with provider "${chalk.cyan( + pluginOption.provider.name + )}"` + ); } module.exports = { - bootstrap, + bootstrap, }; diff --git a/packages/strapi-plugin-rest-cache/server/register.js b/packages/strapi-plugin-rest-cache/server/register.js index 1a8c55d83..9ca29dba9 100644 --- a/packages/strapi-plugin-rest-cache/server/register.js +++ b/packages/strapi-plugin-rest-cache/server/register.js @@ -3,55 +3,16 @@ /** * @typedef {import('@strapi/strapi').Strapi} Strapi */ -const chalk = require('chalk'); const debug = require('debug'); -const { CacheProvider } = require('./types'); const { resolveUserStrategy } = require('./utils/config/resolveUserStrategy'); const { injectMiddlewares } = require('./utils/middlewares/injectMiddlewares'); -const createProvider = async (providerConfig, { strapi }) => { - const providerName = providerConfig.name.toLowerCase(); - let provider; - - let modulePath; - try { - modulePath = require.resolve(`strapi-provider-rest-cache-${providerName}`); - } catch (error) { - if (error.code === 'MODULE_NOT_FOUND') { - modulePath = providerName; - } else { - throw error; - } - } - - try { - // eslint-disable-next-line - provider = require(modulePath); - } catch (err) { - throw new Error( - `Could not load REST Cache provider "${providerName}". You may need to install a provider plugin "yarn add strapi-provider-rest-cache-${providerName}".` - ); - } - - const providerInstance = await provider.init(providerConfig.options, { - strapi, - }); - - if (!(providerInstance instanceof CacheProvider)) { - throw new Error( - `Could not load REST Cache provider "${providerName}". The package "strapi-provider-rest-cache-${providerName}" does not export a CacheProvider instance.` - ); - } - - return Object.freeze(providerInstance); -}; - /** * @param {{ strapi: Strapi }} strapi */ async function register({ strapi }) { - // resolve user configuration, check for missing or invalid options + // resolve user configuration, check for missing or invalid optinos const pluginOption = strapi.config.get('plugin.rest-cache'); const cacheStore = strapi.plugin('rest-cache').service('cacheStore'); @@ -67,19 +28,9 @@ async function register({ strapi }) { debug('strapi:strapi-plugin-rest-cache')('[STRATEGY]: %O', strategy); - // register cache provider - const provider = await createProvider(pluginOption.provider, { strapi }); - cacheStore.init(provider); - // boostrap cache middlewares injectMiddlewares(strapi, strategy); - strapi.log.info( - `Using REST Cache plugin with provider "${chalk.cyan( - pluginOption.provider.name - )}"` - ); - if (strategy.resetOnStartup) { strapi.log.warn('Reset cache on startup is enabled'); await cacheStore.reset();