├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── FUNDING.yml └── workflows │ ├── docs.yml │ ├── gh-release.yml │ └── tests.yml ├── .gitignore ├── .markdownlint.json ├── .nvmrc ├── .prettierrc.js ├── .vscode ├── launch.json └── settings.json ├── BENCHMARKS.md ├── CHANGELOG.md ├── LICENSE ├── README.md ├── data ├── data.json └── uploads │ ├── a-bug-is-becoming-a-meme-on-the-internet.jpg │ ├── beautiful-picture.jpg │ ├── daviddoe@strapi.io.jpg │ ├── default-image.png │ ├── favicon.png │ ├── sarahbaker@strapi.io.jpg │ ├── the-internet-s-own-boy.jpg │ ├── this-shrimp-is-awesome.jpg │ ├── we-love-pizza.jpg │ └── what-s-inside-a-black-hole.jpg ├── docs ├── .vitepress │ ├── config.js │ └── theme │ │ ├── Layout.vue │ │ ├── custom.css │ │ └── index.js ├── guide │ ├── api │ │ ├── admin-routes.md │ │ └── index.md │ ├── index.md │ ├── installation.md │ ├── provider │ │ ├── couchbase.md │ │ ├── custom-provider.md │ │ ├── index.md │ │ ├── memory.md │ │ └── redis.md │ └── strategy │ │ ├── cache-content-type.md │ │ ├── cache-custom-routes.md │ │ ├── cache-keys.md │ │ ├── debug.md │ │ └── index.md ├── index.md └── public │ ├── fluent-emoji-cloud-with-lightning.svg │ ├── icon.png │ ├── logo-rest-cache-dark.svg │ └── logo-rest-cache-light.svg ├── jest.config.js ├── lerna.json ├── package.json ├── packages ├── strapi-plugin-rest-cache │ ├── .prettierrc │ ├── CHANGELOG.md │ ├── README.md │ ├── admin │ │ └── src │ │ │ ├── .eslintrc │ │ │ ├── components │ │ │ ├── EditViewInfoInjectedComponent │ │ │ │ └── index.js │ │ │ ├── EditViewInjectedComponent │ │ │ │ └── index.js │ │ │ ├── EntityCacheInformation │ │ │ │ └── index.js │ │ │ ├── Initializer │ │ │ │ └── index.js │ │ │ ├── ListViewInjectedComponent │ │ │ │ └── index.js │ │ │ ├── PluginIcon │ │ │ │ └── index.js │ │ │ └── PurgeCacheButton │ │ │ │ └── index.js │ │ │ ├── hooks │ │ │ ├── index.js │ │ │ └── useCacheStrategy │ │ │ │ ├── index.js │ │ │ │ ├── init.js │ │ │ │ └── reducer.js │ │ │ ├── index.js │ │ │ ├── pages │ │ │ ├── App │ │ │ │ └── index.js │ │ │ └── HomePage │ │ │ │ └── index.js │ │ │ ├── permissions.js │ │ │ ├── pluginId.js │ │ │ └── translations │ │ │ ├── en.json │ │ │ └── fr.json │ ├── package.json │ ├── server │ │ ├── bootstrap.js │ │ ├── config │ │ │ └── index.js │ │ ├── controllers │ │ │ ├── config.js │ │ │ ├── index.js │ │ │ └── purge.js │ │ ├── index.js │ │ ├── middlewares │ │ │ ├── index.js │ │ │ ├── purge.js │ │ │ ├── purgeAdmin.js │ │ │ └── recv.js │ │ ├── permissions-actions.js │ │ ├── pluginId.js │ │ ├── routes │ │ │ ├── admin │ │ │ │ ├── config.js │ │ │ │ ├── index.js │ │ │ │ └── purge.js │ │ │ └── index.js │ │ ├── services │ │ │ ├── cacheConfig.js │ │ │ ├── cacheStore.js │ │ │ └── index.js │ │ ├── types │ │ │ ├── CacheContentTypeConfig.js │ │ │ ├── CacheKeysConfig.js │ │ │ ├── CachePluginStrategy.js │ │ │ ├── CacheProvider.js │ │ │ ├── CacheRouteConfig.js │ │ │ └── index.js │ │ └── utils │ │ │ ├── config │ │ │ ├── deepFreeze.js │ │ │ ├── getRelatedModelsUid.js │ │ │ ├── getRouteRegExp.js │ │ │ ├── resolveUserStrategy.js │ │ │ └── routeExists.js │ │ │ ├── etags │ │ │ ├── etagGenerate.js │ │ │ ├── etagLookup.js │ │ │ └── etagMatch.js │ │ │ ├── keys │ │ │ ├── generateCacheKey.js │ │ │ ├── generateHeadersKey.js │ │ │ └── generateQueryParamsKey.js │ │ │ ├── middlewares │ │ │ ├── createRouter.js │ │ │ └── shouldLookup.js │ │ │ └── store │ │ │ ├── deserialize.js │ │ │ ├── serialize.js │ │ │ └── withTimeout.js │ ├── strapi-admin.js │ └── strapi-server.js ├── strapi-provider-rest-cache-couchbase │ ├── CHANGELOG.md │ ├── lib │ │ ├── CouchbaseCacheProvider.js │ │ └── index.js │ └── package.json ├── strapi-provider-rest-cache-memory │ ├── CHANGELOG.md │ ├── lib │ │ ├── MemoryCacheProvider.js │ │ └── index.js │ └── package.json └── strapi-provider-rest-cache-redis │ ├── CHANGELOG.md │ ├── lib │ ├── RedisCacheProvider.js │ └── index.js │ └── package.json ├── playgrounds ├── couchbase │ ├── .editorconfig │ ├── .env.example │ ├── .eslintignore │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── config │ │ └── plugins.js │ ├── docker-compose.yml │ ├── favicon.ico │ ├── package.json │ ├── public │ │ ├── robots.txt │ │ └── uploads │ │ │ └── .gitkeep │ ├── src │ │ └── .gitkeep │ ├── tests │ │ └── .gitkeep │ └── yarn.lock ├── memory │ ├── .editorconfig │ ├── .env.example │ ├── .eslintignore │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── config │ │ └── plugins.js │ ├── favicon.ico │ ├── package.json │ ├── public │ │ ├── robots.txt │ │ └── uploads │ │ │ └── .gitkeep │ └── yarn.lock └── redis │ ├── .editorconfig │ ├── .env.example │ ├── .eslintignore │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── config │ └── plugins.js │ ├── docker-compose.yml │ ├── favicon.ico │ ├── package.json │ ├── public │ ├── robots.txt │ └── uploads │ │ └── .gitkeep │ └── yarn.lock ├── shared ├── config │ ├── admin.js │ ├── api.js │ ├── cache-strategy.js │ ├── database.js │ ├── env │ │ └── test │ │ │ └── database.js │ ├── logger.js │ ├── middlewares.js │ └── server.js ├── src │ ├── admin │ │ ├── app.example.js │ │ └── webpack.config.example.js │ ├── api │ │ ├── .gitkeep │ │ ├── article │ │ │ ├── content-types │ │ │ │ └── article │ │ │ │ │ └── schema.json │ │ │ ├── controllers │ │ │ │ └── article.js │ │ │ ├── routes │ │ │ │ └── article.js │ │ │ └── services │ │ │ │ └── article.js │ │ ├── category │ │ │ ├── content-types │ │ │ │ └── category │ │ │ │ │ └── schema.json │ │ │ ├── controllers │ │ │ │ └── category.js │ │ │ ├── routes │ │ │ │ ├── category.js │ │ │ │ └── custom-category.js │ │ │ └── services │ │ │ │ └── category.js │ │ ├── global │ │ │ ├── content-types │ │ │ │ └── global │ │ │ │ │ └── schema.json │ │ │ ├── controllers │ │ │ │ └── global.js │ │ │ ├── routes │ │ │ │ └── global.js │ │ │ └── services │ │ │ │ └── global.js │ │ ├── homepage │ │ │ ├── content-types │ │ │ │ └── homepage │ │ │ │ │ └── schema.json │ │ │ ├── controllers │ │ │ │ └── homepage.js │ │ │ ├── routes │ │ │ │ └── homepage.js │ │ │ └── services │ │ │ │ └── homepage.js │ │ └── writer │ │ │ ├── content-types │ │ │ └── writer │ │ │ │ └── schema.json │ │ │ ├── controllers │ │ │ └── writer.js │ │ │ ├── routes │ │ │ └── writer.js │ │ │ └── services │ │ │ └── writer.js │ ├── bootstrap.js │ ├── components │ │ ├── sections │ │ │ ├── hero.json │ │ │ └── highlight.json │ │ └── shared │ │ │ └── seo.json │ ├── extensions │ │ └── .gitkeep │ └── index.js ├── start-profiler.js └── tests │ ├── .eslintrc │ ├── helpers │ └── strapi.js │ └── single-type-default.test.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | types 2 | tests 3 | **/node_modules/** 4 | CHANGELOG.md 5 | **/admin/** 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | commonjs: true, 4 | es2021: true, 5 | node: true, 6 | }, 7 | extends: ["eslint:recommended", "plugin:import/recommended", "prettier"], 8 | overrides: [ 9 | { 10 | env: { 11 | node: true, 12 | }, 13 | files: [".eslintrc.{js,cjs}"], 14 | parserOptions: { 15 | sourceType: "script", 16 | }, 17 | }, 18 | ], 19 | parserOptions: { 20 | ecmaVersion: "latest", 21 | }, 22 | rules: { 23 | "no-undef": "off", 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: strapi 2 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | # trigger deployment on push to main branch and if docs/ is updated 5 | push: 6 | branches: [main] 7 | paths: 8 | - "docs/**" 9 | # trigger deployment manually 10 | workflow_dispatch: 11 | 12 | jobs: 13 | docs: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | # fetch all commits to get last updated time or other git log info 20 | fetch-depth: 0 21 | 22 | - name: Setup Node.js 23 | uses: actions/setup-node@v1 24 | with: 25 | # choose node.js version to use 26 | node-version: "14" 27 | 28 | # cache node_modules 29 | - name: Cache dependencies 30 | uses: actions/cache@v2 31 | id: yarn-cache 32 | with: 33 | path: | 34 | **/node_modules 35 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 36 | restore-keys: | 37 | ${{ runner.os }}-yarn- 38 | 39 | # install dependencies if the cache did not hit 40 | - name: Install dependencies 41 | if: steps.yarn-cache.outputs.cache-hit != 'true' 42 | run: yarn --frozen-lockfile 43 | 44 | # run build script 45 | - name: Build VitePress site 46 | run: yarn docs:build 47 | 48 | # please check out the docs of the workflow for more details 49 | # @see https://github.com/crazy-max/ghaction-github-pages 50 | - name: Deploy to GitHub Pages 51 | uses: crazy-max/ghaction-github-pages@v2 52 | with: 53 | # deploy to gh-pages branch 54 | target_branch: gh-pages 55 | # deploy the default output dir of VitePress 56 | build_dir: docs/.vitepress/dist 57 | env: 58 | # @see https://docs.github.com/en/actions/reference/authentication-in-a-workflow#about-the-github_token-secret 59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 60 | -------------------------------------------------------------------------------- /.github/workflows/gh-release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | # trigger release on every tag push 5 | push: 6 | tags: 7 | - 'v*.*.*' 8 | 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | # extract tag from the github ref (e.g. refs/tags/v1.2.3) 15 | - name: Set up tag meta 16 | id: meta 17 | run: | 18 | echo ::set-output name=tag::${GITHUB_REF#refs/tags/} 19 | 20 | # create a new release on github with discussion 21 | - name: Create release 22 | uses: softprops/action-gh-release@v1 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | with: 26 | tag_name: ${{ steps.meta.outputs.tag }} 27 | name: Release ${{ steps.meta.outputs.tag }} 28 | body: View [CHANGELOG.md](https://github.com/strapi-community/strapi-plugin-rest-cache/blob/main/CHANGELOG.md) for details 29 | draft: false 30 | prerelease: false 31 | discussion_category_name: announcements -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | workflow_call: 5 | push: 6 | branches: [main] 7 | paths: 8 | - ".github/**" 9 | - "data/**" 10 | - "packages/**" 11 | - "playgrounds/**" 12 | - "shared/**" 13 | pull_request: 14 | branches: [main] 15 | paths: 16 | - ".github/**" 17 | - "data/**" 18 | - "packages/**" 19 | - "playgrounds/**" 20 | - "shared/**" 21 | 22 | jobs: 23 | linters: 24 | runs-on: ubuntu-latest 25 | 26 | steps: 27 | - uses: actions/checkout@v2 28 | - uses: actions/setup-node@v1 29 | with: 30 | node-version: 18.x 31 | 32 | # cache node_modules 33 | - name: Cache dependencies 34 | uses: actions/cache@v2 35 | id: yarn-cache 36 | with: 37 | path: | 38 | **/node_modules 39 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-yarn- 42 | 43 | # install dependencies if the cache did not hit 44 | - name: Install dependencies 45 | if: steps.yarn-cache.outputs.cache-hit != 'true' 46 | run: yarn --frozen-lockfile 47 | 48 | - run: yarn test:lint 49 | 50 | e2e_memory: 51 | runs-on: ubuntu-latest 52 | needs: [linters] 53 | 54 | steps: 55 | - uses: actions/checkout@v2 56 | - uses: actions/setup-node@v1 57 | with: 58 | node-version: 18.x 59 | 60 | # cache node_modules 61 | - name: Cache dependencies 62 | uses: actions/cache@v2 63 | id: yarn-cache 64 | with: 65 | path: | 66 | **/node_modules 67 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 68 | restore-keys: | 69 | ${{ runner.os }}-yarn- 70 | 71 | # install dependencies if the cache did not hit 72 | - name: Install dependencies 73 | if: steps.yarn-cache.outputs.cache-hit != 'true' 74 | run: yarn --frozen-lockfile 75 | 76 | - run: yarn postinstall:memory 77 | 78 | - name: Run Memory e2e tests 79 | working-directory: playgrounds/memory 80 | run: yarn test:e2e 81 | 82 | e2e_redis: 83 | runs-on: ubuntu-latest 84 | needs: [linters] 85 | 86 | services: 87 | redis: 88 | image: bitnami/redis:latest 89 | env: 90 | ALLOW_EMPTY_PASSWORD: yes 91 | ports: 92 | # Opens tcp port 6379 on the host and service container 93 | - 6379:6379 94 | 95 | steps: 96 | - uses: actions/checkout@v2 97 | - uses: actions/setup-node@v1 98 | with: 99 | node-version: 18.x 100 | 101 | # cache node_modules 102 | - name: Cache dependencies 103 | uses: actions/cache@v2 104 | id: yarn-cache 105 | with: 106 | path: | 107 | **/node_modules 108 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 109 | restore-keys: | 110 | ${{ runner.os }}-yarn- 111 | 112 | # install dependencies if the cache did not hit 113 | - name: Install dependencies 114 | if: steps.yarn-cache.outputs.cache-hit != 'true' 115 | run: yarn --frozen-lockfile 116 | 117 | - run: yarn postinstall:redis 118 | 119 | - name: Run Redis e2e tests 120 | working-directory: playgrounds/redis 121 | run: yarn test:e2e 122 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # vuepress 107 | .temp 108 | .profile 109 | cache 110 | 111 | # Strapi 112 | .strapi-updater.json 113 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "MD024": false, 4 | "MD013": false, 5 | "MD034": false 6 | } 7 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v14.19.1 2 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | endOfLine: 'lf', 3 | semi: true, 4 | singleQuote: true, 5 | tabWidth: 2, 6 | trailingComma: 'es5', 7 | printWidth: 100, 8 | }; -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Run mocha", 9 | "type": "node", 10 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 11 | "stopOnEntry": false, 12 | "args": ["./tests/*.test.js", "--no-timeouts", "--exit"], 13 | "cwd": "${workspaceFolder}", 14 | "runtimeExecutable": null, 15 | "request": "launch" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /BENCHMARKS.md: -------------------------------------------------------------------------------- 1 | # Benchmarks 2 | 3 | ## Context 4 | 5 | - Rest cache version: `4.2.4` 6 | - Strapi version: `4.1.5` 7 | - Node version: `16.13.2` 8 | - Endpoint used: `/api/homepage?populate=*` from `shared/api` 9 | 10 | ## Runs 11 | 12 | ### Cache disabled (reference) 13 | 14 | ```sh 15 | $ ENABLE_CACHE=false yarn profile:memory 16 | ``` 17 | 18 | | Stat | 2.5% | 50% | 97.5% | 99% | Avg | Stdev | Max | 19 | | ----------- | ------- | ------- | ------- | ------- | ---------- | --------- | ------- | 20 | | **Latency** | 2424 ms | 2555 ms | 2921 ms | 3012 ms | 2565.12 ms | 133.23 | 3401 ms | 21 | 22 | | Stat | 1% | 2.5% | 50% | 97.5% | Avg | Stdev | Min | 23 | | ------------- | --- | ---- | ------ | ------- | ------ | ------ | ------ | 24 | | **Req/Sec** | 0 | 0 | 386 | 1000 | 383.34 | 382.38 | 39 | 25 | | **Bytes/Sec** | 0 B | 0 B | 453 kB | 1.17 MB | 450 kB | 449 kB | 45.7 kB | 26 | 27 | ### Cache enabled (without etag) 28 | 29 | ```sh 30 | $ ENABLE_ETAG=false yarn profile:memory 31 | ``` 32 | 33 | | Stat | 2.5% | 50% | 97.5% | 99% | Avg | Stdev | Max | 34 | | ----------- | ------ | ------ | ------ | ------ | --------- | ------- | ------ | 35 | | **Latency** | 113 ms | 116 ms | 166 ms | 175 ms | 120.04 ms | 12.86 ms | 275 ms | 36 | 37 | | Stat | 1% | 2.5% | 50% | 97.5% | Avg | Stdev | Min | 38 | | ------------- | ------- | ------- | ------- | ------- | ------- | ------ | ------- | 39 | | **Req/Sec** | 7451 | 7523 | 8287 | 8687 | 8293.49 | 306.35 | 6381 | 40 | | **Bytes/Sec** | 8.85 MB | 8.93 MB | 9.84 MB | 10.3 MB | 9.84 MB | 364 kB | 7.57 MB | 41 | 42 | ### Cache enabled (with etag) 43 | 44 | ```sh 45 | $ yarn profile:memory 46 | ``` 47 | 48 | | Stat | 2.5% | 50% | 97.5% | 99% | Avg | Stdev | Max | 49 | | ----------- | ------ | ------ | ------ | ------ | --------- | -------- | ------ | 50 | | **Latency** | 119 ms | 125 ms | 185 ms | 197 ms | 131.05 ms | 16.87 ms | 307 ms | 51 | 52 | | Stat | 1% | 2.5% | 50% | 97.5% | Avg | Stdev | Min | 53 | | ------------- | ------- | ------- | ------- | ------- | ------- | ------ | ------- | 54 | | **Req/Sec** | 6551 | 6559 | 7651 | 8231 | 7599.39 | 472.94 | 6100 | 55 | | **Bytes/Sec** | 8.05 MB | 8.07 MB | 9.4 MB | 10.1 MB | 9.34 MB | 581 kB | 7.5 MB | 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Strapi Community. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Strapi REST Cache Plugin

3 | 4 |

Speed-up HTTP requests with LRU cache.

5 | 6 |

7 | 8 | NPM Version 9 | 10 | 11 | Monthly download on NPM 12 | 13 |

14 |
15 | 16 | ## Table of Contents 17 | 18 | - [🚦 Current Status](#-current-status) 19 | - [✨ Features](#-features) 20 | - [🤔 Motivation](#-motivation) 21 | - [🖐 Requirements](#-requirements) 22 | - [🚚 Getting Started](#-getting-started) 23 | - [Contributing](#contributing) 24 | - [License](#license) 25 | 26 | ## 🚦 Current Status 27 | 28 | This package is currently under development and should be consider **ALPHA** in terms of state. I/We are currently accepting contributions and/or dedicated contributors to help develop and maintain this package. 29 | 30 | ## ✨ Features 31 | 32 | This plugin provide a way to cache **HTTP requests** in order to **improve performance**. It's get inspired by varnish cache which is a popular caching solution. 33 | 34 | The cache content is stored by a **provider**, which can be either an in-memory provider, a redis connection, a file system, or any other custom provider. 35 | You can set a **strategy** to tell what to cache and how much time responses should be cached. The cache will be invalidated when the related Content-Type is updated, so you **never have to worry about stale data**. 36 | 37 | ## 🖐 Requirements 38 | 39 | Supported Strapi Versions: 40 | 41 | - Strapi v4.0.x (recently tested as of January 2022) 42 | - Strapi v4.1.x (recently tested as of March 2022) 43 | - Strapi v4.x.x (Assumed, but possibly not tested) 44 | 45 | **If you are looking for a plugin for Strapi v3.x, please check the [strapi-middleware-cache](https://github.com/patrixr/strapi-middleware-cache/).** 46 | 47 | ## 🚚 Getting Started 48 | 49 | [Read the Docs to Learn More.](https://strapi-community.github.io/strapi-plugin-rest-cache/) 50 | 51 | ## Contributing 52 | 53 | I/We are actively looking for contributors, maintainers, and others to help shape this package. As this plugins sole purpose within the Strapi community is to be used by other developers and plugin maintainers to get fast responses time. 54 | 55 | If interested please feel free to email the lead maintainer Sacha at: sacha@digisquad.io or ping `stf#3254` on Discord. 56 | 57 | ## License 58 | 59 | See the [LICENSE](./LICENSE.md) file for licensing information. 60 | -------------------------------------------------------------------------------- /data/uploads/a-bug-is-becoming-a-meme-on-the-internet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/data/uploads/a-bug-is-becoming-a-meme-on-the-internet.jpg -------------------------------------------------------------------------------- /data/uploads/beautiful-picture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/data/uploads/beautiful-picture.jpg -------------------------------------------------------------------------------- /data/uploads/daviddoe@strapi.io.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/data/uploads/daviddoe@strapi.io.jpg -------------------------------------------------------------------------------- /data/uploads/default-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/data/uploads/default-image.png -------------------------------------------------------------------------------- /data/uploads/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/data/uploads/favicon.png -------------------------------------------------------------------------------- /data/uploads/sarahbaker@strapi.io.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/data/uploads/sarahbaker@strapi.io.jpg -------------------------------------------------------------------------------- /data/uploads/the-internet-s-own-boy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/data/uploads/the-internet-s-own-boy.jpg -------------------------------------------------------------------------------- /data/uploads/this-shrimp-is-awesome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/data/uploads/this-shrimp-is-awesome.jpg -------------------------------------------------------------------------------- /data/uploads/we-love-pizza.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/data/uploads/we-love-pizza.jpg -------------------------------------------------------------------------------- /data/uploads/what-s-inside-a-black-hole.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/data/uploads/what-s-inside-a-black-hole.jpg -------------------------------------------------------------------------------- /docs/.vitepress/config.js: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'module' 2 | import { defineConfig } from 'vitepress' 3 | 4 | const require = createRequire(import.meta.url) 5 | const pkg = require('strapi-plugin-rest-cache/package.json') 6 | 7 | export default defineConfig({ 8 | title: "REST Cache", 9 | description: "Speed-up HTTP requests with LRU cache", 10 | base: "/strapi-plugin-rest-cache/", 11 | lastUpdated: true, 12 | themeConfig: { 13 | socialLinks: [ 14 | { icon: 'github', link: 'https://github.com/strapi-community/strapi-plugin-rest-cache' }, 15 | ], 16 | editLink: { 17 | pattern: 'https://github.com/strapi-community/strapi-plugin-rest-cache/edit/main/docs/:path', 18 | text: 'Edit this page on GitHub' 19 | }, 20 | logo: { 21 | src: "/icon.png", 22 | }, 23 | outline: [2,3], 24 | footer: { 25 | message: 'Made with ❤️ by Strapi Community' 26 | }, 27 | nav: [ 28 | { 29 | text: "Guide", 30 | link: "/guide/", 31 | activeMatch: '/guide/', 32 | }, 33 | { 34 | text: pkg.version, 35 | items: [ 36 | { 37 | text: 'Changelog', 38 | link: 'https://github.com/strapi-community/strapi-plugin-rest-cache/blob/main/CHANGELOG.md' 39 | }, 40 | { 41 | text: 'Strapi Community', 42 | link: 'https://github.com/strapi-community' 43 | } 44 | ] 45 | } 46 | ], 47 | sidebar: { 48 | '/guide/': [ 49 | { 50 | text: 'Guide', 51 | items: [ 52 | { text: 'Introduction', link: '/guide/' }, 53 | { text: 'Installation', link: '/guide/installation' }, 54 | ] 55 | }, 56 | { 57 | text: 'Provider', 58 | collapsible: true, 59 | items: [ 60 | { text: 'Provider configuration', link: '/guide/provider/' }, 61 | { text: 'Provider: Memory', link: '/guide/provider/memory' }, 62 | { text: 'Provider: Redis', link: '/guide/provider/redis' }, 63 | { text: 'Provider: Couchbase', link: '/guide/provider/couchbase' }, 64 | { text: 'Custom provider', link: '/guide/provider/custom-provider' }, 65 | ] 66 | }, 67 | { 68 | text: 'Strategy', 69 | collapsible: true, 70 | items: [ 71 | { text: 'Strategy configuration', link: '/guide/strategy/' }, 72 | { text: 'Cache content type', link: '/guide/strategy/cache-content-type' }, 73 | { text: 'Cache custom routes', link: '/guide/strategy/cache-custom-routes' }, 74 | { text: 'Cache keys', link: '/guide/strategy/cache-keys' }, 75 | { text: 'Debug mode', link: '/guide/strategy/debug' }, 76 | ] 77 | }, 78 | { 79 | text: 'API', 80 | collapsible: true, 81 | items: [ 82 | { text: 'Services', link: '/guide/api/' }, 83 | { text: 'Admin Routes', link: '/guide/api/admin-routes' }, 84 | ] 85 | } 86 | ], 87 | '/reference/': [ 88 | { 89 | text: 'Reference', 90 | collapsible: true, 91 | items: [ 92 | { text: 'Configuration', link: '/reference/configuration' }, 93 | { text: 'Routes', link: '/reference/routes' }, 94 | { text: 'Services', link: '/reference/services' }, 95 | ] 96 | } 97 | ] 98 | } 99 | } 100 | }) 101 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/Layout.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 32 | 33 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/custom.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --vp-c-brand: #8e75ff; 3 | --vp-c-brand-light: #a091ed; 4 | --vp-c-brand-lighter: #a091ed; 5 | --vp-c-brand-dark: #8e75ff; 6 | --vp-c-brand-darker: #8e75ff; 7 | } -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.js: -------------------------------------------------------------------------------- 1 | import DefaultTheme from 'vitepress/theme' 2 | import Layout from './Layout.vue' 3 | import './custom.css' 4 | 5 | export default { 6 | ...DefaultTheme, 7 | // override the Layout with a wrapper component that 8 | // injects the slots 9 | Layout 10 | } -------------------------------------------------------------------------------- /docs/guide/api/admin-routes.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Admin Routes 3 | --- 4 | 5 | # Admin Routes 6 | 7 | This plugin exposes a few admin routes that can be used to interact with the cache from Strapi admin panel. 8 | 9 | ## [GET] /config/strategy 10 | 11 | ## [GET] /config/provider 12 | 13 | ## [POST] /purge 14 | -------------------------------------------------------------------------------- /docs/guide/api/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Services 3 | --- 4 | 5 | # Services 6 | 7 | This plugin exposes a few services that can be used to interact with the cache. 8 | 9 | ## cacheConfig 10 | 11 | This service is used to interact with the plugin configuration. 12 | 13 | ### `getUids()` 14 | 15 | Get all uid of cached contentTypes 16 | 17 | ```js 18 | const uids = strapi.plugins["rest-cache"].services.cacheConfig.getUids(); 19 | ``` 20 | 21 | ### `getRelatedCachedUid(uid)` 22 | 23 | Return the intersection of cached contentTypes and the related contentTypes of a given contentType uid 24 | 25 | ```js 26 | const relatedUids = strapi.plugins[ 27 | "rest-cache" 28 | ].services.cacheConfig.getRelatedCachedUid("api::article.article"); 29 | ``` 30 | 31 | ### `get(uid)` 32 | 33 | Get related `CacheContentTypeConfig` with an uid 34 | 35 | ```js 36 | const contentTypeConfig = strapi.plugins["rest-cache"].services.cacheConfig.get( 37 | "api::article.article" 38 | ); 39 | ``` 40 | 41 | ### `getCacheKeysRegexp(uid, params, wildcard)` 42 | 43 | Get regexs to match all `CacheContentTypeConfig` keys with given params 44 | 45 | ```js 46 | const regExps = strapi.plugins[ 47 | "rest-cache" 48 | ].services.cacheConfig.getCacheKeysRegexp("api::article.article", { 49 | lang: "en", 50 | }); 51 | ``` 52 | 53 | ### `isCached()` 54 | 55 | Check if a cache configuration exists for a contentType uid 56 | 57 | ```js 58 | const isContentTypeCached = strapi.plugins[ 59 | "rest-cache" 60 | ].services.cacheConfig.isCached("api::article.article"); 61 | ``` 62 | 63 | ## cacheStore 64 | 65 | This service is used to interact with the cache store. 66 | 67 | ### `get(key)` 68 | 69 | ### `set(key, val, maxAge)` 70 | 71 | ### `del(key)` 72 | 73 | ### `keys()` 74 | 75 | ### `reset()` 76 | 77 | ### `init()` 78 | 79 | ### `ready` 80 | 81 | ### `clearByRegexp(regExps)` 82 | 83 | ### `clearByUid(uid, params, wildcard)` 84 | -------------------------------------------------------------------------------- /docs/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | --- 4 | 5 | # {{ $frontmatter.title }} 6 | 7 | This plugin inject a middleware that caches incoming `GET` requests on the strapi API, based on query params and Content-Type UID. 8 | The cache is automatically busted everytime a `PUT`, `PATCH`, `POST`, or `DELETE` request comes in or when an entity is updated through the admin panel. It can also be programmatically cleared via exposed services or admin routes. 9 | 10 | The cache content is stored by a [**provider**](./provider/index.md), which can be either an in-memory provider, a redis connection, a file system, or any other custom provider. 11 | 12 | You can set a [**strategy**](./strategy/index.md) to tell what to cache and how much time responses should be cached. The cache will be invalidated when the related Content-Type is updated, so you **never have to worry about stale data**. 13 | 14 | In addition, you can interact with the plugin through the admin panel, api admin routes or programmatically using internal services. 15 | -------------------------------------------------------------------------------- /docs/guide/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | --- 4 | 5 | # {{ $frontmatter.title }} 6 | 7 | 1. Add required dependencies 8 | 9 | :::: code-group 10 | 11 | ```bash [memory (default)] 12 | yarn add strapi-plugin-rest-cache 13 | ``` 14 | 15 | ```bash [redis] 16 | yarn add \ 17 | strapi-plugin-rest-cache \ 18 | strapi-plugin-redis \ 19 | strapi-provider-rest-cache-redis 20 | ``` 21 | 22 | ```bash [couchbase] 23 | yarn add \ 24 | strapi-plugin-rest-cache \ 25 | strapi-provider-rest-cache-couchbase 26 | ``` 27 | 28 | :::: 29 | 30 | ::: info 31 | This plugin is only compatible with Strapi v4.0.0 and above. 32 | If you are looking for a plugin for Strapi v3.x, please check the [strapi-middleware-cache](https://github.com/patrixr/strapi-middleware-cache/). 33 | ::: 34 | 35 | 1. Enable the plugin in `./config/plugins.js` 36 | 37 | :::: code-group 38 | 39 | ```js [memory (default)] 40 | module.exports = { 41 | "rest-cache": { 42 | config: { 43 | provider: { 44 | name: "memory", 45 | options: { 46 | max: 32767, 47 | maxAge: 3600, 48 | }, 49 | }, 50 | strategy: { 51 | contentTypes: [ 52 | // list of Content-Types UID to cache 53 | "api::category.category", 54 | "api::article.article", 55 | "api::global.global", 56 | "api::homepage.homepage", 57 | ], 58 | }, 59 | }, 60 | }, 61 | }; 62 | ``` 63 | 64 | ```js [redis] 65 | module.exports = { 66 | // Step 1: Configure the redis connection 67 | // @see https://github.com/strapi-community/strapi-plugin-redis 68 | redis: { 69 | // ... 70 | }, 71 | // Step 2: Configure the redis cache plugin 72 | "rest-cache": { 73 | config: { 74 | provider: { 75 | name: "redis", 76 | options: { 77 | max: 32767, 78 | connection: "default", 79 | }, 80 | }, 81 | strategy: { 82 | // if you are using keyPrefix for your Redis, please add 83 | keysPrefix: "", 84 | contentTypes: [ 85 | // list of Content-Types UID to cache 86 | "api::category.category", 87 | "api::article.article", 88 | "api::global.global", 89 | "api::homepage.homepage", 90 | ], 91 | }, 92 | }, 93 | }, 94 | }; 95 | ``` 96 | 97 | ```js [couchbase] 98 | module.exports = { 99 | "rest-cache": { 100 | config: { 101 | provider: { 102 | name: "couchbase", 103 | max: 32767, 104 | options: { 105 | connectionString: "couchbase://127.0.0.1:8091", 106 | connectionOptions: { 107 | username: "Administrator", 108 | password: "couchbase", 109 | }, 110 | bucket: "test-bucket", 111 | ttl: 2, 112 | }, 113 | }, 114 | strategy: { 115 | contentTypes: [ 116 | // list of Content-Types UID to cache 117 | "api::category.category", 118 | "api::article.article", 119 | "api::global.global", 120 | "api::homepage.homepage", 121 | ], 122 | }, 123 | }, 124 | }, 125 | }; 126 | ``` 127 | 128 | :::: 129 | -------------------------------------------------------------------------------- /docs/guide/provider/couchbase.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Couchbase provider 3 | --- 4 | 5 | # Couchbase provider 6 | 7 | ## Installation 8 | 9 | ```bash 10 | yarn add \ 11 | strapi-plugin-rest-cache \ 12 | strapi-provider-rest-cache-couchbase 13 | ``` 14 | 15 | ## Configuration 16 | 17 | ```js 18 | module.exports = { 19 | "rest-cache": { 20 | config: { 21 | provider: { 22 | name: "couchbase", 23 | max: 32767, 24 | options: { 25 | connectionString: "couchbase://127.0.0.1:8091", 26 | connectionOptions: { 27 | username: "Administrator", 28 | password: "couchbase", 29 | }, 30 | bucket: "test-bucket", 31 | ttl: 2, 32 | }, 33 | }, 34 | strategy: { 35 | contentTypes: [ 36 | // list of Content-Types UID to cache 37 | "api::category.category", 38 | "api::article.article", 39 | "api::global.global", 40 | "api::homepage.homepage", 41 | ], 42 | }, 43 | }, 44 | }, 45 | }; 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/guide/provider/custom-provider.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Custom provider 3 | --- 4 | 5 | # Create a custom provider 6 | 7 | ## Extends the `CacheProvider` class 8 | 9 | ```js 10 | // file: /custom-rest-cache-provider/MyCacheProvider.js 11 | const { CacheProvider } = require("strapi-plugin-rest-cache/server/types"); 12 | 13 | class MyCacheProvider extends CacheProvider { 14 | // implement your custom provider here 15 | } 16 | 17 | module.exports = { 18 | MyCacheProvider, 19 | }; 20 | ``` 21 | 22 | ::: details View abstract CacheProvider class 23 | 24 | ```js 25 | /** 26 | * Abstract Class CacheProvider. 27 | * 28 | * @class CacheProvider 29 | */ 30 | class CacheProvider { 31 | constructor() { 32 | if (this.constructor === CacheProvider) { 33 | throw new Error("CacheProvider class can't be instantiated."); 34 | } 35 | } 36 | 37 | /** 38 | * @param {string} key 39 | */ 40 | async get(key) { 41 | throw new Error("Method 'get()' must be implemented."); 42 | } 43 | 44 | /** 45 | * @param {string} key 46 | * @param {any} val 47 | * @param {number=} maxAge 48 | */ 49 | async set(key, val, maxAge = 3600) { 50 | throw new Error("Method 'set()' must be implemented."); 51 | } 52 | 53 | /** 54 | * @param {string|string[]} key 55 | */ 56 | async del(key) { 57 | throw new Error("Method 'del()' must be implemented."); 58 | } 59 | 60 | async keys() { 61 | throw new Error("Method 'keys()' must be implemented."); 62 | } 63 | 64 | get ready() { 65 | throw new Error("getter 'ready' must be implemented."); 66 | } 67 | } 68 | ``` 69 | 70 | ::: 71 | 72 | ## Export the provider 73 | 74 | ```js 75 | // file: /custom-rest-cache-provider/index.js 76 | const couchbase = require("couchbase"); 77 | 78 | const { MyCacheProvider } = require("./MyCacheProvider"); 79 | 80 | module.exports = { 81 | provider: "custom", 82 | name: "Custom provider", 83 | 84 | async init(options, { strapi }) { 85 | // here you can initialize your provider connection 86 | const client = await couchbase.connect(options.clients); 87 | 88 | // then return your provider instance 89 | return new MyCacheProvider(client); 90 | }, 91 | }; 92 | ``` 93 | 94 | ## Use your custom cache provider 95 | 96 | ```js 97 | // file: /config/plugins.js 98 | 99 | module.exports = { 100 | "rest-cache": { 101 | config: { 102 | provider: { 103 | name: "../custom-rest-cache-provider/index.js", 104 | // your provider options 105 | // will be passed to the provider init function 106 | options: {}, 107 | }, 108 | strategy: { 109 | // ... 110 | }, 111 | }, 112 | }, 113 | }; 114 | ``` 115 | -------------------------------------------------------------------------------- /docs/guide/provider/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cache provider configuration 3 | --- 4 | 5 | # Cache provider configuration 6 | 7 | By default, the **strapi-plugin-rest-cache** use **strapi-provider-rest-cache-memory** which is an in-memory provider. It's not persisted and will be lost when the server restarts. 8 | 9 | Alternatively, you can use: 10 | 11 | - **strapi-provider-rest-cache-redis** which is a bridge between the cache plugin and the [strapi-plugin-redis](https://github.com/strapi-community/strapi-plugin-redis) 12 | - **strapi-provider-rest-cache-couchbase** which connect to a couchbase cluster and store the cache in a bucket 13 | - Your custom provider 14 | 15 | You have to set the provider name in the plugin configuration so it will be initialized once the plugin is bootstrapped. At this time only one provider can be used at a time. 16 | 17 | You can also set the provider `getTimeout` which is the time in milliseconds to wait for the provider to respond, **if the provider is not responding, the cache will be considered as a miss**. 18 | 19 | ```js {6-17} 20 | // file: /config/plugins.js 21 | 22 | module.exports = ({ env }) => ({ 23 | 'rest-cache': { 24 | config: { 25 | provider: /* @type {Provider} */ { 26 | // name can be an alias: 27 | name: "my-provider", // try to require 'strapi-provider-rest-cache-my-provider' 28 | // a full package name: 29 | name: "@org/my-cache-provider", // try to require '@org/my-cache-provider' 30 | // or a relative path: 31 | name: "../path/to/my-provider", 32 | 33 | // provider options 34 | getTimeout: 500, // in milliseconds (default: 500) 35 | options: {}, 36 | }, 37 | strategy: { 38 | // ... 39 | }, 40 | }, 41 | }, 42 | }); 43 | ``` 44 | 45 | Note that each provider has its own configuration, so you will have to refer to the provider documentation to know how to configure it. 46 | 47 | ::: tip 48 | Check the [memory](./memory.md), [redis](./redis.md) or [couchbase](./couchbase.md) documentation for more details for advanced provider configuration. 49 | ::: 50 | 51 | ## `Provider` reference 52 | 53 | ### `name` 54 | 55 | The name of the provider. 56 | Will try to load the package with `strapi-rest-cache-provider-` and fallback with ``, so you can either use a package name or an absolute path. 57 | 58 | - **Type:** `string` 59 | - **Default:** `'memory'` 60 | 61 | ### `getTimeout` 62 | 63 | Time in milliseconds to wait for the provider to respond on cache lookup requests, if the provider is not responding, the cache will be considered as a miss 64 | 65 | - **Type:** `number` 66 | - **Default:** `500` 67 | 68 | ### `options` 69 | 70 | Object passed to the provider constructor. 71 | 72 | - **Type:** `any` 73 | - **Default:** `{}` 74 | -------------------------------------------------------------------------------- /docs/guide/provider/memory.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Memory provider 3 | --- 4 | 5 | # Memory provider 6 | 7 | The memory provider allow you to store cached content in memory. It use a simple key-value store with LRU algorithm provided by the default provider of [`node-cache-manager`](https://github.com/BryanDonovan/node-cache-manager) module which uses [`lru-node`](https://github.com/isaacs/node-lru-cache/tree/v6.0.0). 8 | 9 | ## Installation 10 | 11 | ::: info 12 | This provider is already installed with the plugin. 13 | ::: 14 | 15 | ## Configuration 16 | 17 | ```js {6-16} 18 | // file: /config/plugins.js 19 | 20 | module.exports = ({ env }) => ({ 21 | 'rest-cache': { 22 | config: { 23 | provider: { 24 | name: 'memory', 25 | getTimeout: 500, 26 | options: { 27 | // The maximum size of the cache 28 | max: 32767, 29 | // Update to the current time whenever it is retrieved from cache, causing it to not expire 30 | updateAgeOnGet: false, 31 | // ... 32 | }, 33 | }, 34 | strategy: { 35 | // ... 36 | }, 37 | }, 38 | }, 39 | }); 40 | ``` 41 | 42 | ::: tip 43 | View full options available on [`lru-cache`](https://github.com/isaacs/node-lru-cache/tree/v6.0.0#options) documentation. 44 | ::: 45 | -------------------------------------------------------------------------------- /docs/guide/provider/redis.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Redis provider 3 | --- 4 | 5 | # Redis provider 6 | 7 | ## Installation 8 | 9 | ```bash 10 | yarn add \ 11 | strapi-plugin-rest-cache \ 12 | strapi-plugin-redis \ 13 | strapi-provider-rest-cache-redis 14 | ``` 15 | 16 | ## Configuration 17 | 18 | ```js 19 | module.exports = { 20 | // Step 1: Configure the redis connection 21 | // @see https://github.com/strapi-community/strapi-plugin-redis 22 | redis: { 23 | // ... 24 | }, 25 | // Step 2: Configure the redis cache plugin 26 | "rest-cache": { 27 | config: { 28 | provider: { 29 | name: "redis", 30 | options: { 31 | max: 32767, 32 | connection: "default", 33 | }, 34 | }, 35 | strategy: { 36 | // if you are using keyPrefix for your Redis, please add 37 | keysPrefix: "", 38 | contentTypes: [ 39 | // list of Content-Types UID to cache 40 | "api::category.category", 41 | "api::article.article", 42 | "api::global.global", 43 | "api::homepage.homepage", 44 | ], 45 | }, 46 | }, 47 | }, 48 | }; 49 | ``` 50 | 51 | ::: warning 52 | Ensure `redis` plugin configuration come before `strapi-plugin-rest-cache` 53 | ::: 54 | -------------------------------------------------------------------------------- /docs/guide/strategy/cache-content-type.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Content types 3 | --- 4 | 5 | # Content types 6 | 7 | The plugin will **only inject cache middleware to Content-Types which have been explicitely enabled**. This can be done by setting the `config.strategy.contentTypes` configuration. 8 | 9 | It accept either a string or an object, so we can configure differently each Content-Type. 10 | 11 | ```js {10-19} 12 | // file: /config/plugins.js 13 | 14 | module.exports = ({ env }) => ({ 15 | 'rest-cache': { 16 | config: {, 17 | provider: { 18 | // ... 19 | }, 20 | strategy: { 21 | contentTypes: /* @type {(string|CacheContentTypeConfig)[]} */ [ 22 | // can be a string (the Content-Type UID) 23 | "api::article.article", 24 | 25 | // or a custom CacheContentTypeConfig object 26 | { 27 | contentType: "api::pages.pages", 28 | // ... 29 | }, 30 | ], 31 | }, 32 | }, 33 | }, 34 | }); 35 | ``` 36 | 37 | ## `CacheContentTypeConfig` reference 38 | 39 | ### `injectDefaultRoutes` 40 | 41 | When enabled, inject [default routes](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest-api.html#api-endpoints) for each content type. 42 | 43 | - **Type:** `boolean` 44 | - **Default:** `true` 45 | 46 | ### `hitpass` 47 | 48 | When true, the cache plugin will not lookup for cache and serve fresh response from backend instead. Also, the response is not stored in the cache. 49 | 50 | - **Type:** `(ctx: Context) => boolean | boolean` 51 | - **Default:** _(inherit from `CachePluginStrategy` if set)_ 52 | 53 | ### `routes` 54 | 55 | Additionnal routes to register for this content type. 56 | 57 | - **Type:** [`CacheRouteConfig[]`](./cache-custom-routes.md#cacherouteconfig-reference) 58 | - **Default:** `[]` 59 | 60 | ### `contentType` 61 | 62 | Content-Type UID to cache (e.g. `api::article.article`). 63 | 64 | - **Type:** `string` 65 | - **Default:** `''` 66 | 67 | ### `keys` 68 | 69 | Options used to generate the cache keys. 70 | 71 | - **Type:** [`CacheKeysConfig`](./cache-keys.md#cachekeysconfig-reference) 72 | - **Default:** _(inherit from `CachePluginStrategy` if set)_ 73 | 74 | ### `maxAge` 75 | 76 | Default max age for cached entries. 77 | 78 | - **Type:** `number` 79 | - **Default:** _(inherit from `CachePluginStrategy` if set)_ 80 | -------------------------------------------------------------------------------- /docs/guide/strategy/cache-custom-routes.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cache custom routes 3 | --- 4 | 5 | # {{ $frontmatter.title }} 6 | 7 | By default the plugin registers a middleware to intercept all [predefined routes](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest-api.html#api-endpoints), but you can also enable it on custom routes. 8 | 9 | ```js {13-18} 10 | // file: /config/plugins.js 11 | 12 | module.exports = ({ env }) => ({ 13 | 'rest-cache': { 14 | config: {, 15 | provider: { 16 | // ... 17 | }, 18 | strategy: { 19 | contentTypes: [ 20 | { 21 | contentType: "api::pages.pages", 22 | routes: /* @type {CacheRouteConfig[]} */ [ 23 | { 24 | path: '/api/pages/slug/:slug+', // note that we set the /api prefix here 25 | method: 'GET', // can be omitted, defaults to GET 26 | }, 27 | ], 28 | }, 29 | ], 30 | }, 31 | }, 32 | }, 33 | }); 34 | ``` 35 | 36 | ::: warning 37 | At this time a custom route can only be registered within a single Content-Type. 38 | ::: 39 | 40 | ## `CacheRouteConfig` reference 41 | 42 | ### `path` 43 | 44 | Refer to an [existing route path in strapi](https://docs.strapi.io/developer-docs/latest/development/backend-customization/routes.html) on which the cache middleware will be registered. 45 | A warning will be displayed if the path does not exist. 46 | 47 | - **Type:** `string` 48 | - **Default:** `GET` 49 | 50 | ### `method` 51 | 52 | Refer to an [existing route method in strapi](https://docs.strapi.io/developer-docs/latest/development/backend-customization/routes.html) on which the cache middleware will be registered. 53 | A warning will be displayed if the path does not exist. 54 | 55 | - **Type:** `'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'` 56 | - **Default:** `GET` 57 | 58 | ::: tip 59 | Cache lookup is performed only on `GET` requests, and cache invalidation is performed on all other requests. 60 | ::: 61 | 62 | ### `hitpass` 63 | 64 | When true, the cache plugin will not lookup for cache and serve fresh response from backend instead. Also, the response is not stored in the cache. 65 | 66 | - **Type:** `(ctx: Context) => boolean | boolean` 67 | - **Default:** _(inherit from `CacheContentTypeConfig` if set)_ 68 | 69 | ### `keys` 70 | 71 | Options used to generate the cache keys. 72 | 73 | - **Type:** [`CacheKeysConfig`](./cache-keys.md#cachekeysconfig-reference) 74 | - **Default:** _(inherit from `CacheContentTypeConfig` if set)_ 75 | 76 | ### `maxAge` 77 | 78 | Default max age for cached entries. 79 | 80 | - **Type:** `number` 81 | - **Default:** _(inherit from `CacheContentTypeConfig` if set)_ 82 | -------------------------------------------------------------------------------- /docs/guide/strategy/cache-keys.md: -------------------------------------------------------------------------------- 1 | # Cache keys configuration 2 | 3 | By default, the cache plugin will compute a cache key based on the request method, the request path and the request query parameters. 4 | 5 | You can customize this behavior by providing a `keys` option in either the `CachePluginStrategy` to customize global behavior, or on each route in `CacheRouteConfig` so that you can customize the behavior for each route. 6 | 7 | ::::code-group 8 | 9 | ```js {13-16} [public cache] 10 | // file: /config/plugins.js 11 | 12 | module.exports = ({ env }) => ({ 13 | 'rest-cache': { 14 | config: {, 15 | provider: { 16 | // ... 17 | }, 18 | strategy: { 19 | contentTypes: [ 20 | { 21 | contentType: 'api::homepage.homepage', 22 | hitpass: false, // never check if we should bypass the cache 23 | keys: { 24 | useQueryParams: false, // disable query parameters in cache keys 25 | }, 26 | }, 27 | ], 28 | }, 29 | }, 30 | }, 31 | }); 32 | ``` 33 | 34 | ```js {13-16} [user cache] 35 | // file: /config/plugins.js 36 | 37 | module.exports = ({ env }) => ({ 38 | 'rest-cache': { 39 | config: {, 40 | provider: { 41 | // ... 42 | }, 43 | strategy: { 44 | contentTypes: [ 45 | { 46 | contentType: 'api::orders.orders', 47 | hitpass: false, // never check if we should bypass the cache 48 | keys: { 49 | useHeaders: ['authorization'], // use the authorization header value in cache keys 50 | }, 51 | }, 52 | ], 53 | }, 54 | }, 55 | }, 56 | }); 57 | ``` 58 | 59 | :::: 60 | 61 | ::: warning 62 | When using `authorization` header in cache keys, you will not be able to clear the cache for a specific user. 63 | ::: 64 | 65 | ## `CacheKeysConfig` reference 66 | 67 | ### `useQueryParams` 68 | 69 | When set to `true`, all query parameters will be used to generate the cache key. 70 | If an array is provided, only the query parameters specified in the array will be used. 71 | You can totally disable query parameters by setting this option to `false`. 72 | 73 | - **Type:** `boolean|string[]` 74 | - **Default:** `true` 75 | 76 | ### `useHeaders` 77 | 78 | Headers to use to generate the cache key. 79 | 80 | - **Type:** `string[]` 81 | - **Default:** `[]` 82 | -------------------------------------------------------------------------------- /docs/guide/strategy/debug.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Debug mode 3 | --- 4 | 5 | # Debug mode 6 | 7 | This plugins use [debug](https://www.npmjs.com/package/debug) module to log messages that can help during development. 8 | You can enable debug mode by setting the environment variable `DEBUG=strapi:strapi-plugin-rest-cache` before starting strapi. 9 | 10 | eg. `DEBUG=strapi:strapi-plugin-rest-cache yarn strapi develop` 11 | 12 | You can also enable debug mode by setting the [`config.strategy.debug`](./index.md#debug) configuration option to `true`. 13 | -------------------------------------------------------------------------------- /docs/guide/strategy/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cache strategy configuration 3 | --- 4 | 5 | # Cache strategy configuration 6 | 7 | The plugin will **only inject cache middleware to Content-Types which have been explicitely enabled**. This can be done by setting the `config.strategy.contentTypes` configuration. 8 | 9 | It accept either a string or an object, so we can configure differently each Content-Type. 10 | 11 | ```js {9-14} 12 | // file: /config/plugins.js 13 | 14 | module.exports = ({ env }) => ({ 15 | 'rest-cache': { 16 | config: {, 17 | provider: { 18 | // ... 19 | }, 20 | strategy: /* @type {CachePluginStrategy} */ { 21 | keysPrefix: 'project-name', 22 | maxAge: 3600000, 23 | debug: true, 24 | contentTypes: [ /** ... */ ], 25 | }, 26 | }, 27 | }, 28 | }); 29 | ``` 30 | 31 | In addition to the **contentType** configuration, you can also set the default **maxAge**, **hitpass** and **keys** configuration, enables **ETag** and **X-Cache** headers or tune how the plugin will work for each route. 32 | 33 | ::: warning 34 | The plugin will not cache any request if the `Authorization` header is present on the request or if the `Cookie` header is present on the request. This is to prevent caching of private data. 35 | 36 | You can change this behavior by setting the `config.strategy.hitpass` configuration. 37 | ::: 38 | 39 | ## `CachePluginStrategy` reference 40 | 41 | ### `debug` 42 | 43 | Enable extra log with [debug](https://www.npmjs.com/package/debug) package. This is usefull only when configuring the plugin. 44 | 45 | - **Type:** `boolean` 46 | - **Default:** `false` 47 | 48 | ### `enableEtag` 49 | 50 | Enable etag generation for response. Also enable etag lookup when `If-None-Match` header is present on requests. 51 | This add extra CPU overhead due to the etag computation but save bandwidth by sending `304 Not Modified` response. 52 | 53 | - **Type:** `boolean` 54 | - **Default:** `false` 55 | 56 | ### `enableXCacheHeaders` 57 | 58 | Add extra `X-Cache` headers to responses. This is usefull when configuring the plugin or when using behind a reverse proxy. 59 | 60 | - **Type:** `boolean` 61 | - **Default:** `false` 62 | 63 | ### `enableAdminCTBMiddleware` 64 | 65 | Register a middleware to handle cache invalidation requests performed using the admin UI. 66 | 67 | - **Type:** `boolean` 68 | - **Default:** `true` 69 | 70 | ### `resetOnStartup` 71 | 72 | Delete all cache entries from the provider on startup. This is usefull when performing a migration using an external cache provider. 73 | 74 | - **Type:** `boolean` 75 | - **Default:** `false` 76 | 77 | ### `clearRelatedCache` 78 | 79 | Try to delete all cache entries related to the deleted entry. 80 | 81 | - **Type:** `boolean` 82 | - **Default:** `true` 83 | 84 | ### `keysPrefix` 85 | 86 | Prefix added to the cache keys. 87 | 88 | - **Type:** `string` 89 | - **Default:** `''` 90 | 91 | ### `hitpass` 92 | 93 | When true, the cache plugin will not lookup for cache and serve fresh response from backend instead. Also, the response is not stored in the cache. 94 | 95 | - **Type:** `(ctx: Context) => boolean` 96 | - **Default:** 97 | 98 | ```js 99 | function hitpass(ctx) { 100 | // ignore cache when authorization or cookie headers are present 101 | return Boolean( 102 | ctx.request.headers.authorization || ctx.request.headers.cookie 103 | ); 104 | } 105 | ``` 106 | 107 | ### `keys` 108 | 109 | Options used to generate the cache keys. 110 | 111 | - **Type:** [`CacheKeysConfig`](#cachekeysconfig) 112 | 113 | ### `maxAge` 114 | 115 | Default max age for cached entries. 116 | 117 | - **Type:** `number` 118 | - **Default:** `3600000` (1 hour) 119 | 120 | ### `contentTypes` 121 | 122 | Specify each content types that should be cached. If a string is provided, default configuration from [`CacheContentTypeConfig`](#cachecontenttypeconfig) will be used 123 | 124 | - **Type:** [`(string|CacheContentTypeConfig)[]`](#cachecontenttypeconfig) 125 | - **Default:** `[]` 126 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: REST Cache 6 | text: API cache for Strapi 7 | tagline: Speed-up HTTP requests with LRU cache 8 | image: 9 | src: /fluent-emoji-cloud-with-lightning.svg 10 | alt: "" 11 | actions: 12 | - theme: brand 13 | text: Guide 14 | link: /guide/ 15 | - theme: alt 16 | text: View on GitHub 17 | link: https://github.com/strapi-community/strapi-plugin-rest-cache 18 | features: 19 | - icon: ⚡ 20 | title: Respond in ~1ms 21 | details: Give your Strapi blazing fast response times with this plugin. 22 | - icon: 🔀 23 | title: Cache storage providers 24 | details: Choose between in-memory, redis or couchbase to store your cached content. 25 | - icon: 🛠️ 26 | title: Customizable 27 | details: Want more? Use the internal API or add your own custom cache storage engine. 28 | --- 29 | -------------------------------------------------------------------------------- /docs/public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/docs/public/icon.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = async () => ({ 4 | verbose: true, 5 | }); 6 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/*", "playgrounds/*"], 3 | "version": "4.2.9", 4 | "npmClient": "yarn", 5 | "useWorkspaces": true 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@strapi-plugin-rest-cache/monorepo", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Speed-up HTTP requests with LRU cache", 6 | "workspaces": [ 7 | "./packages/*", 8 | "./playgrounds/*" 9 | ], 10 | "scripts": { 11 | "postinstall": "run-s postinstall:*", 12 | "postinstall:memory": "cpy . ../playgrounds/memory/ --cwd=shared", 13 | "postinstall:redis": "cpy . ../playgrounds/redis/ --cwd=shared", 14 | "postinstall:couchbase": "cpy . ../playgrounds/couchbase/ --cwd=shared", 15 | "docs:dev": "vitepress dev docs", 16 | "docs:build": "vitepress build docs", 17 | "docs:serve": "vitepress serve docs", 18 | "dev:memory": "yarn workspace @strapi-plugin-rest-cache/playground-memory develop", 19 | "dev:redis": "yarn workspace @strapi-plugin-rest-cache/playground-redis develop", 20 | "dev:couchbase": "yarn workspace @strapi-plugin-rest-cache/playground-couchbase develop", 21 | "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", 22 | "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", 23 | "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", 24 | "lint": "lerna run lint --stream", 25 | "test:lint": "lerna run test:lint --stream", 26 | "test:e2e": "run-s test:e2e:*", 27 | "test:e2e:memory": "yarn workspace @strapi-plugin-rest-cache/playground-memory test:e2e", 28 | "test:e2e:redis": "yarn workspace @strapi-plugin-rest-cache/playground-redis test:e2e", 29 | "test:e2e:couchbase": "yarn workspace @strapi-plugin-rest-cache/playground-couchbase test:e2e" 30 | }, 31 | "license": "MIT", 32 | "resolutions": { 33 | "**/colors": "1.4.0", 34 | "semver": "7.5.4", 35 | "vuepress-vite": "2.0.0-beta.66", 36 | "d3-color": "3.1.0" 37 | }, 38 | "devDependencies": { 39 | "0x": "^5.5.0", 40 | "@giscus/vue": "^2.2.6", 41 | "@vuepress/plugin-register-components": "^2.0.0-beta.36", 42 | "@vuepress/plugin-search": "^2.0.0-beta.36", 43 | "autocannon": "^7.7.1", 44 | "copyfiles": "^2.4.1", 45 | "cpy-cli": "^4.1.0", 46 | "eslint": "^8.11.0", 47 | "eslint-config-prettier": "^8.8.0", 48 | "eslint-plugin-import": "^2.27.5", 49 | "jest": "^29.6.1", 50 | "lerna": "^5.0.0", 51 | "lint-staged": "^12.3.7", 52 | "mermaid": "^9.4.0", 53 | "mime-types": "^2.1.35", 54 | "npm-run-all": "^4.1.5", 55 | "prettier": "^2.6.0", 56 | "supertest": "^6.3.3", 57 | "vitepress": "^1.0.0-alpha.33", 58 | "vue": "^3.2.45", 59 | "vuepress": "^2.0.0-beta.36", 60 | "yorkie": "^2.0.0" 61 | }, 62 | "engines": { 63 | "node": ">=14.0.0", 64 | "npm": ">=6.0.0" 65 | }, 66 | "lint-staged": { 67 | "{packages,playgrounds}/**/*.{ts,js}": [ 68 | "prettier --write" 69 | ], 70 | "docs/**/*.md": [ 71 | "prettier --write" 72 | ] 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/README.md: -------------------------------------------------------------------------------- 1 |
2 |

Strapi REST Cache Plugin

3 | 4 |

Speed-up HTTP requests with LRU cache.

5 | 6 |

7 | 8 | NPM Version 9 | 10 | 11 | Monthly download on NPM 12 | 13 |

14 |
15 | 16 | ## Table of Contents 17 | 18 | - [🚦 Current Status](#-current-status) 19 | - [✨ Features](#-features) 20 | - [🤔 Motivation](#-motivation) 21 | - [🖐 Requirements](#-requirements) 22 | - [🚚 Getting Started](#-getting-started) 23 | - [Contributing](#contributing) 24 | - [License](#license) 25 | 26 | ## 🚦 Current Status 27 | 28 | This package is currently under development and should be consider **ALPHA** in terms of state. I/We are currently accepting contributions and/or dedicated contributors to help develop and maintain this package. 29 | 30 | ## ✨ Features 31 | 32 | This plugin provide a way to cache **HTTP requests** in order to **improve performance**. It's get inspired by varnish cache which is a popular caching solution. 33 | 34 | The cache content is stored by a **provider**, which can be either an in-memory provider, a redis connection, a file system, or any other custom provider. 35 | You can set a **strategy** to tell what to cache and how much time responses should be cached. The cache will be invalidated when the related Content-Type is updated, so you **never have to worry about stale data**. 36 | 37 | ## 🖐 Requirements 38 | 39 | Supported Strapi Versions: 40 | 41 | - Strapi v4.0.x (recently tested as of January 2022) 42 | - Strapi v4.x.x (Assumed, but possibly not tested) 43 | 44 | **If you are looking for a plugin for Strapi v3.x, please check the [strapi-middleware-cache](https://github.com/patrixr/strapi-middleware-cache/).** 45 | 46 | ## 🚚 Getting Started 47 | 48 | [Read the Docs to Learn More.](https://strapi-community.github.io/strapi-plugin-rest-cache/) 49 | 50 | ## Contributing 51 | 52 | I/We are actively looking for contributors, maintainers, and others to help shape this package. As this plugins sole purpose within the Strapi community is to be used by other developers and plugin maintainers to get fast responses time. 53 | 54 | If interested please feel free to email the lead maintainer Sacha at: sacha@digisquad.io or ping `stf#3254` on Discord. 55 | 56 | ## License 57 | 58 | See the [LICENSE](./LICENSE.md) file for licensing information. 59 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@strapi-community/admin" 3 | } 4 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/components/EditViewInfoInjectedComponent/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useCMEditViewDataManager, useRBAC } from '@strapi/helper-plugin'; 3 | 4 | import cachePermissions from '../../permissions'; 5 | import EntityCacheInformation from '../EntityCacheInformation'; 6 | 7 | function EditViewInfoInjectedComponent() { 8 | const { allowedActions } = useRBAC(cachePermissions); 9 | const { slug } = useCMEditViewDataManager(); 10 | 11 | if (!allowedActions.canReadStrategy) { 12 | return null; 13 | } 14 | 15 | return ; 16 | } 17 | 18 | export default EditViewInfoInjectedComponent; 19 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/components/EditViewInjectedComponent/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useCMEditViewDataManager, useRBAC } from '@strapi/helper-plugin'; 3 | import cachePermissions from '../../permissions'; 4 | import PurgeCacheButton from '../PurgeCacheButton'; 5 | 6 | function EditViewInjectedComponent() { 7 | const { allowedActions } = useRBAC(cachePermissions); 8 | 9 | const { 10 | slug, 11 | isCreatingEntry, 12 | hasDraftAndPublish, 13 | initialData, 14 | isSingleType, 15 | } = useCMEditViewDataManager(); 16 | 17 | if (isCreatingEntry) { 18 | return null; 19 | } 20 | 21 | if (hasDraftAndPublish && initialData.publishedAt === null) { 22 | return null; 23 | } 24 | 25 | if (!allowedActions.canReadStrategy || !allowedActions.canPurge) { 26 | return null; 27 | } 28 | 29 | return ( 30 | 35 | ); 36 | } 37 | 38 | export default EditViewInjectedComponent; 39 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/components/EntityCacheInformation/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { useIntl } from 'react-intl'; 4 | import { Status } from '@strapi/design-system/Status'; 5 | import { Typography } from '@strapi/design-system/Typography'; 6 | import { Box } from '@strapi/design-system/Box'; 7 | 8 | import { useCacheStrategy } from '../../hooks'; 9 | 10 | function EntityCacheInformation({ contentType }) { 11 | const { strategy } = useCacheStrategy(); 12 | const { formatMessage } = useIntl(); 13 | 14 | if ( 15 | !strategy?.contentTypes?.find( 16 | (config) => config.contentType === contentType 17 | ) 18 | ) { 19 | return null; 20 | } 21 | 22 | return ( 23 | 24 | 25 | 26 | {formatMessage({ 27 | id: 'cache.info-box.entity-cached', 28 | defaultMessage: 'This entity is cached via REST Cache plugin', 29 | })} 30 | 31 | 32 | 33 | ); 34 | } 35 | 36 | EntityCacheInformation.propTypes = { 37 | contentType: PropTypes.string.isRequired, 38 | }; 39 | 40 | export default EntityCacheInformation; 41 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/components/Initializer/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Initializer 4 | * 5 | */ 6 | 7 | import { useEffect, useRef } from 'react'; 8 | import PropTypes from 'prop-types'; 9 | import pluginId from '../../pluginId'; 10 | 11 | function Initializer({ setPlugin }) { 12 | const ref = useRef(); 13 | ref.current = setPlugin; 14 | 15 | useEffect(() => { 16 | ref.current(pluginId); 17 | }, []); 18 | 19 | return null; 20 | } 21 | 22 | Initializer.propTypes = { 23 | setPlugin: PropTypes.func.isRequired, 24 | }; 25 | 26 | export default Initializer; 27 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/components/ListViewInjectedComponent/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // import { useIntl } from 'react-intl'; 3 | // import { Button } from '@strapi/design-system/Button'; 4 | // import Refresh from '@strapi/icons/Refresh'; 5 | import { 6 | // ConfirmDialog, 7 | // useNotification, 8 | // hasPermissions, 9 | // useRBACProvider, 10 | // useCMEditViewDataManager, 11 | useRBAC, 12 | // request, 13 | } from '@strapi/helper-plugin'; 14 | import { useRouteMatch } from 'react-router-dom'; 15 | // import cachePermissions from '../../permissions'; 16 | import cachePermissions from '../../permissions'; 17 | import PurgeCacheButton from '../PurgeCacheButton'; 18 | 19 | function ListViewInjectedComponent() { 20 | const { 21 | params: { slug }, 22 | } = useRouteMatch('/content-manager/:kind/:slug?'); 23 | const { allowedActions } = useRBAC(cachePermissions); 24 | 25 | if (!allowedActions.canReadStrategy || !allowedActions.canPurge) { 26 | return null; 27 | } 28 | 29 | return ; 30 | } 31 | 32 | export default ListViewInjectedComponent; 33 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/components/PluginIcon/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Information from '@strapi/icons/Information'; 3 | 4 | function PluginIcon() { 5 | return ; 6 | } 7 | 8 | export default PluginIcon; 9 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/components/PurgeCacheButton/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { useIntl } from 'react-intl'; 3 | import { Button } from '@strapi/design-system/Button'; 4 | import Refresh from '@strapi/icons/Refresh'; 5 | import { ConfirmDialog, useNotification, request } from '@strapi/helper-plugin'; 6 | import PropTypes from 'prop-types'; 7 | 8 | import pluginId from '../../pluginId'; 9 | import { useCacheStrategy } from '../../hooks'; 10 | 11 | function PurgeCacheButton({ contentType, params, wildcard }) { 12 | const { strategy } = useCacheStrategy(); 13 | 14 | const [showConfirmModal, setShowConfirmModal] = useState(false); 15 | const [isModalConfirmButtonLoading, setIsModalConfirmButtonLoading] = 16 | useState(false); 17 | const { formatMessage } = useIntl(); 18 | const toggleNotification = useNotification(); 19 | 20 | const abortController = new AbortController(); 21 | const { signal } = abortController; 22 | 23 | useEffect(() => { 24 | return () => { 25 | abortController.abort(); 26 | }; 27 | // eslint-disable-next-line react-hooks/exhaustive-deps 28 | }, []); 29 | 30 | const toggleConfirmModal = () => 31 | setShowConfirmModal((prevState) => !prevState); 32 | 33 | const handleConfirmDelete = async () => { 34 | try { 35 | // Show the loading state 36 | setIsModalConfirmButtonLoading(true); 37 | 38 | await request(`/${pluginId}/purge`, { 39 | method: 'POST', 40 | signal, 41 | body: { 42 | contentType, 43 | params, 44 | wildcard, 45 | }, 46 | }); 47 | 48 | toggleNotification({ 49 | type: 'success', 50 | message: { 51 | id: 'cache.purge.success', 52 | defaultMessage: 'Cache purged successfully', 53 | }, 54 | }); 55 | 56 | setIsModalConfirmButtonLoading(false); 57 | 58 | toggleConfirmModal(); 59 | } catch (err) { 60 | const errorMessage = err?.response?.payload?.error?.message; 61 | setIsModalConfirmButtonLoading(false); 62 | toggleConfirmModal(); 63 | 64 | if (errorMessage) { 65 | toggleNotification({ 66 | type: 'warning', 67 | message: { id: 'cache.purge.error', defaultMessage: errorMessage }, 68 | }); 69 | } else { 70 | toggleNotification({ 71 | type: 'warning', 72 | message: { id: 'notification.error' }, 73 | }); 74 | } 75 | } 76 | }; 77 | 78 | if ( 79 | !strategy?.contentTypes?.find( 80 | (config) => config.contentType === contentType 81 | ) 82 | ) { 83 | return null; 84 | } 85 | 86 | return ( 87 | <> 88 | 99 | } 114 | rightButtonText={{ 115 | id: 'cache.purge.confirm-modal-confirm', 116 | defaultMessage: 'Purge REST Cache', 117 | }} 118 | /> 119 | 120 | ); 121 | } 122 | 123 | PurgeCacheButton.propTypes = { 124 | contentType: PropTypes.string.isRequired, 125 | params: PropTypes.object, 126 | wildcard: PropTypes.bool, 127 | }; 128 | PurgeCacheButton.defaultProps = { 129 | params: {}, 130 | wildcard: undefined, 131 | }; 132 | 133 | export default PurgeCacheButton; 134 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/hooks/index.js: -------------------------------------------------------------------------------- 1 | export { default as useCacheStrategy } from './useCacheStrategy'; 2 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/hooks/useCacheStrategy/index.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useReducer, useRef } from 'react'; 2 | import { request, useNotification } from '@strapi/helper-plugin'; 3 | import init from './init'; 4 | import pluginId from '../../pluginId'; 5 | import reducer, { initialState } from './reducer'; 6 | 7 | const useCacheStrategy = (shouldFetchData = true) => { 8 | const [{ strategy, isLoading }, dispatch] = useReducer( 9 | reducer, 10 | initialState, 11 | () => init(initialState, shouldFetchData) 12 | ); 13 | const toggleNotification = useNotification(); 14 | 15 | const isMounted = useRef(true); 16 | const abortController = new AbortController(); 17 | const { signal } = abortController; 18 | 19 | useEffect(() => { 20 | if (shouldFetchData) { 21 | fetchCacheStrategy(); 22 | } 23 | 24 | return () => { 25 | abortController.abort(); 26 | isMounted.current = false; 27 | }; 28 | // eslint-disable-next-line react-hooks/exhaustive-deps 29 | }, [shouldFetchData]); 30 | 31 | const fetchCacheStrategy = async () => { 32 | try { 33 | dispatch({ 34 | type: 'GET_DATA', 35 | }); 36 | 37 | const { strategy } = await request(`/${pluginId}/config/strategy`, { 38 | method: 'GET', 39 | signal, 40 | }); 41 | 42 | dispatch({ 43 | type: 'GET_DATA_SUCCEEDED', 44 | data: strategy, 45 | }); 46 | } catch (err) { 47 | const message = err?.response?.payload?.message ?? 'An error occured'; 48 | 49 | if (isMounted.current) { 50 | dispatch({ 51 | type: 'GET_DATA_ERROR', 52 | }); 53 | 54 | if (message !== 'Forbidden') { 55 | toggleNotification({ 56 | type: 'warning', 57 | message, 58 | }); 59 | } 60 | } 61 | } 62 | }; 63 | 64 | return { strategy, isLoading, getData: fetchCacheStrategy }; 65 | }; 66 | 67 | export default useCacheStrategy; 68 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/hooks/useCacheStrategy/init.js: -------------------------------------------------------------------------------- 1 | const init = (initialState, shouldFetchData) => ({ 2 | ...initialState, 3 | isLoading: shouldFetchData, 4 | }); 5 | 6 | export default init; 7 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/hooks/useCacheStrategy/reducer.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable consistent-return */ 2 | import produce from 'immer'; 3 | 4 | export const initialState = { 5 | strategy: {}, 6 | isLoading: true, 7 | }; 8 | 9 | const reducer = (state, action) => 10 | produce(state, (draftState) => { 11 | switch (action.type) { 12 | case 'GET_DATA': { 13 | draftState.isLoading = true; 14 | draftState.strategy = {}; 15 | break; 16 | } 17 | case 'GET_DATA_SUCCEEDED': { 18 | draftState.strategy = action.data; 19 | draftState.isLoading = false; 20 | break; 21 | } 22 | case 'GET_DATA_ERROR': { 23 | draftState.isLoading = false; 24 | break; 25 | } 26 | default: 27 | return draftState; 28 | } 29 | }); 30 | 31 | export default reducer; 32 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/index.js: -------------------------------------------------------------------------------- 1 | import { prefixPluginTranslations } from '@strapi/helper-plugin'; 2 | import pluginPkg from '../../package.json'; 3 | import pluginId from './pluginId'; 4 | 5 | import Initializer from './components/Initializer'; 6 | import EditViewInfoInjectedComponent from './components/EditViewInfoInjectedComponent'; 7 | import EditViewInjectedComponent from './components/EditViewInjectedComponent'; 8 | import ListViewInjectedComponent from './components/ListViewInjectedComponent'; 9 | 10 | const { name } = pluginPkg.strapi; 11 | 12 | export default { 13 | register(app) { 14 | app.registerPlugin({ 15 | id: pluginId, 16 | initializer: Initializer, 17 | isReady: true, 18 | name, 19 | }); 20 | }, 21 | bootstrap(app) { 22 | app.injectContentManagerComponent('editView', 'informations', { 23 | name: 'EditViewInfoInjectedComponent', 24 | Component: EditViewInfoInjectedComponent, 25 | }); 26 | app.injectContentManagerComponent('editView', 'right-links', { 27 | name: 'EditViewInjectedComponent', 28 | Component: EditViewInjectedComponent, 29 | }); 30 | app.injectContentManagerComponent('listView', 'actions', { 31 | name: 'ListViewInjectedComponent', 32 | Component: ListViewInjectedComponent, 33 | }); 34 | }, 35 | async registerTrads({ locales }) { 36 | const importedTrads = await Promise.all( 37 | locales.map((locale) => 38 | import(`./translations/${locale}.json`) 39 | .then(({ default: data }) => ({ 40 | data: prefixPluginTranslations(data, pluginId), 41 | locale, 42 | })) 43 | .catch(() => ({ 44 | data: {}, 45 | locale, 46 | })) 47 | ) 48 | ); 49 | 50 | return Promise.resolve(importedTrads); 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/pages/App/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * This component is the skeleton around the actual pages, and should only 4 | * contain code that should be seen on all pages. (e.g. navigation bar) 5 | * 6 | */ 7 | 8 | import React from 'react'; 9 | import { Switch, Route } from 'react-router-dom'; 10 | import { NotFound } from '@strapi/helper-plugin'; 11 | import pluginId from '../../pluginId'; 12 | import HomePage from '../HomePage'; 13 | 14 | function App() { 15 | return ( 16 |
17 | 18 | 19 | 20 | 21 |
22 | ); 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/pages/HomePage/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * HomePage 4 | * 5 | */ 6 | 7 | import React, { memo } from 'react'; 8 | import PurgeCacheButton from '../../components/PurgeCacheButton'; 9 | // import PropTypes from 'prop-types'; 10 | import pluginId from '../../pluginId'; 11 | 12 | function HomePage() { 13 | return ( 14 |
15 |

{pluginId}'s HomePage

16 |

Happy coding

17 | 18 |
19 | ); 20 | } 21 | 22 | export default memo(HomePage); 23 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/permissions.js: -------------------------------------------------------------------------------- 1 | // import pluginId from './pluginId'; 2 | 3 | const pluginPermissions = { 4 | purge: [{ action: `plugin::rest-cache.cache.purge`, subject: null }], 5 | readStrategy: [ 6 | { 7 | action: `plugin::rest-cache.cache.read-strategy`, 8 | subject: null, 9 | }, 10 | ], 11 | readProvider: [ 12 | { 13 | action: `plugin::rest-cache.cache.read-provider`, 14 | subject: null, 15 | }, 16 | ], 17 | }; 18 | 19 | export default pluginPermissions; 20 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/pluginId.js: -------------------------------------------------------------------------------- 1 | import pluginPkg from '../../package.json'; 2 | 3 | // const pluginId = pluginPkg.name.replace(/^strapi-plugin-/i, ''); 4 | 5 | export default pluginPkg.strapi.name; 6 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/translations/en.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/admin/src/translations/fr.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strapi-plugin-rest-cache", 3 | "version": "4.2.9", 4 | "description": "Speed-up HTTP requests with LRU cache", 5 | "license": "MIT", 6 | "strapi": { 7 | "displayName": "REST Cache", 8 | "name": "rest-cache", 9 | "description": "Speed-up HTTP requests with LRU cache", 10 | "kind": "plugin" 11 | }, 12 | "keywords": [ 13 | "strapi", 14 | "plugin", 15 | "rest", 16 | "cache", 17 | "http", 18 | "lru" 19 | ], 20 | "files": [ 21 | "admin/**/*.js", 22 | "admin/**/*.json", 23 | "server/**/*.js", 24 | "strapi-admin.js", 25 | "strapi-server.js" 26 | ], 27 | "author": { 28 | "name": "Strapi Community", 29 | "url": "https://github.com/strapi-community/" 30 | }, 31 | "maintainers": [ 32 | { 33 | "name": "Sacha Stafyniak", 34 | "email": "sacha@digisquad.io", 35 | "url": "https://digisquad.io" 36 | }, 37 | { 38 | "name": "Patrick R", 39 | "url": "https://github.com/patrixr/" 40 | }, 41 | { 42 | "name": "Davide Pellegatta", 43 | "email": "davide.pellegatta@gmail.com" 44 | } 45 | ], 46 | "repository": { 47 | "type": "git", 48 | "url": "git+https://github.com/strapi-community/strapi-plugin-rest-cache.git", 49 | "directory": "packages/strapi-plugin-rest-cache" 50 | }, 51 | "bugs": { 52 | "url": "https://github.com/strapi-community/strapi-plugin-rest-cache/issues" 53 | }, 54 | "homepage": "https://strapi-community.github.io/strapi-plugin-rest-cache/", 55 | "scripts": { 56 | "lint": "run-s eslint:server:fix prettier:fix", 57 | "eslint:server": "eslint ./server", 58 | "eslint:server:fix": "eslint ./server --fix", 59 | "prettier": "prettier --check ./{admin,server}/**/*", 60 | "prettier:fix": "prettier --write ./{admin,server}/**/*", 61 | "test": "run-s test:*", 62 | "test:lint": "run-s eslint:server prettier" 63 | }, 64 | "dependencies": { 65 | "@koa/router": "^10.1.1", 66 | "chalk": "^4.1.2", 67 | "debug": "^4.3.4", 68 | "lodash": "^4.17.21", 69 | "path": "^0.12.7", 70 | "strapi-provider-rest-cache-memory": "^4.2.9" 71 | }, 72 | "devDependencies": { 73 | "@strapi/strapi": "^4.11.7", 74 | "koa": "^2.11.0", 75 | "npm-run-all": "^4.1.5", 76 | "react": "^17.0.2", 77 | "react-intl": "^5.24.7", 78 | "react-redux": "^7.2.2", 79 | "redux": "^4.0.5", 80 | "styled-components": "^5.2.3" 81 | }, 82 | "peerDependencies": { 83 | "@strapi/strapi": "^4.0.5" 84 | }, 85 | "engines": { 86 | "node": ">=14.0.0", 87 | "npm": ">=6.0.0" 88 | }, 89 | "gitHead": "80f7c4b1c3915484e282597292cd1f5c749cbe8f" 90 | } 91 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/bootstrap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('@strapi/strapi').Strapi} Strapi 5 | */ 6 | const chalk = require('chalk'); 7 | const debug = require('debug'); 8 | 9 | const { CacheProvider } = require('./types'); 10 | const { resolveUserStrategy } = require('./utils/config/resolveUserStrategy'); 11 | const { createRouter } = require('./utils/middlewares/createRouter'); 12 | 13 | const permissionsActions = require('./permissions-actions'); 14 | 15 | const createProvider = async (providerConfig, { strapi }) => { 16 | const providerName = providerConfig.name.toLowerCase(); 17 | let provider; 18 | 19 | let modulePath; 20 | try { 21 | modulePath = require.resolve(`strapi-provider-rest-cache-${providerName}`); 22 | } catch (error) { 23 | if (error.code === 'MODULE_NOT_FOUND') { 24 | modulePath = providerName; 25 | } else { 26 | throw error; 27 | } 28 | } 29 | 30 | try { 31 | // eslint-disable-next-line 32 | provider = require(modulePath); 33 | } catch (err) { 34 | throw new Error( 35 | `Could not load REST Cache provider "${providerName}". You may need to install a provider plugin "yarn add strapi-provider-rest-cache-${providerName}".` 36 | ); 37 | } 38 | 39 | const providerInstance = await provider.init(providerConfig.options, { 40 | strapi, 41 | }); 42 | 43 | if (!(providerInstance instanceof CacheProvider)) { 44 | throw new Error( 45 | `Could not load REST Cache provider "${providerName}". The package "strapi-provider-rest-cache-${providerName}" does not export a CacheProvider instance.` 46 | ); 47 | } 48 | 49 | return Object.freeze(providerInstance); 50 | }; 51 | 52 | /** 53 | * @param {{ strapi: Strapi }} strapi 54 | */ 55 | async function bootstrap({ strapi }) { 56 | // resolve user configuration, check for missing or invalid options 57 | const pluginOption = strapi.config.get('plugin.rest-cache'); 58 | const cacheStore = strapi.plugin('rest-cache').service('cacheStore'); 59 | 60 | if (pluginOption.strategy.debug === true) { 61 | debug.enable('strapi:strapi-plugin-rest-cache'); 62 | } 63 | 64 | const strategy = resolveUserStrategy(strapi, pluginOption.strategy); 65 | strapi.config.set('plugin.rest-cache', { 66 | ...pluginOption, 67 | strategy, 68 | }); 69 | 70 | debug('strapi:strapi-plugin-rest-cache')('[STRATEGY]: %O', strategy); 71 | 72 | // watch for changes in any roles -> clear all cache 73 | // need to be done before lifecycles are registered 74 | if (strapi.plugin('users-permissions')) { 75 | strapi.db.lifecycles.subscribe({ 76 | models: ['plugin::users-permissions.role'], 77 | async beforeDelete() { 78 | await cacheStore.reset(); 79 | }, 80 | }); 81 | } 82 | 83 | // register cache provider 84 | const provider = await createProvider(pluginOption.provider, { strapi }); 85 | strapi.plugin('rest-cache').service('cacheStore').init(provider); 86 | 87 | // boostrap plugin permissions 88 | await strapi.admin.services.permission.actionProvider.registerMany( 89 | permissionsActions.actions 90 | ); 91 | 92 | // boostrap cache middlewares 93 | const router = createRouter(strapi, strategy); 94 | strapi.server.router.use(router.routes()); 95 | 96 | strapi.log.info( 97 | `Using REST Cache plugin with provider "${chalk.cyan( 98 | pluginOption.provider.name 99 | )}"` 100 | ); 101 | 102 | if (strategy.resetOnStartup) { 103 | strapi.log.warn('Reset cache on startup is enabled'); 104 | await strapi.plugin('rest-cache').service('cacheStore').reset(); 105 | } 106 | } 107 | 108 | module.exports = { 109 | bootstrap, 110 | }; 111 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const config = { 4 | default: () => ({ 5 | provider: { 6 | name: 'memory', 7 | getTimeout: 500, 8 | options: {}, 9 | }, 10 | strategy: { 11 | debug: false, 12 | enableEtag: false, 13 | enableXCacheHeaders: false, 14 | enableAdminCTBMiddleware: true, 15 | resetOnStartup: false, 16 | clearRelatedCache: true, 17 | keysPrefix: '', 18 | keys: { 19 | useHeaders: [], 20 | useQueryParams: true, 21 | }, 22 | hitpass: (ctx) => 23 | Boolean( 24 | ctx.request.headers.authorization || ctx.request.headers.cookie 25 | ), 26 | maxAge: 3600000, 27 | contentTypes: [], 28 | }, 29 | }), 30 | validator() {}, 31 | }; 32 | 33 | module.exports = { 34 | config, 35 | }; 36 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/controllers/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('@strapi/strapi').Strapi} Strapi 5 | * @typedef {import('koa').Context} Context 6 | */ 7 | 8 | /** 9 | * @param {{ strapi: Strapi }} strapi 10 | */ 11 | function createConfigController({ strapi }) { 12 | return { 13 | /** 14 | * @param {Context} ctx 15 | */ 16 | async strategy(ctx) { 17 | const { strategy } = strapi.config.get('plugin.rest-cache'); 18 | ctx.body = { strategy }; 19 | }, 20 | /** 21 | * @param {Context} ctx 22 | */ 23 | async provider(ctx) { 24 | const { provider } = strapi.config.get('plugin.rest-cache'); 25 | ctx.body = { provider }; 26 | }, 27 | }; 28 | } 29 | module.exports = { 30 | createConfigController, 31 | }; 32 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/controllers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { createPurgeController: purge } = require('./purge'); 4 | const { createConfigController: config } = require('./config'); 5 | 6 | const controllers = { 7 | purge, 8 | config, 9 | }; 10 | 11 | module.exports = { 12 | controllers, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/controllers/purge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('@strapi/strapi').Strapi} Strapi 5 | * @typedef {import('koa').Context} Context 6 | */ 7 | 8 | /** 9 | * @param {{ strapi: Strapi }} strapi 10 | */ 11 | function createPurgeController({ strapi }) { 12 | return { 13 | /** 14 | * @param {Context} ctx 15 | */ 16 | async index(ctx) { 17 | const { contentType, params, wildcard } = ctx.request.body; 18 | 19 | if (!contentType) { 20 | ctx.badRequest('contentType is required'); 21 | return; 22 | } 23 | 24 | const cacheConfigService = strapi 25 | .plugin('rest-cache') 26 | .service('cacheConfig'); 27 | 28 | const cacheStoreService = strapi 29 | .plugin('rest-cache') 30 | .service('cacheStore'); 31 | 32 | if (!cacheConfigService.isCached(contentType)) { 33 | ctx.badRequest('contentType is not cached', { contentType }); 34 | return; 35 | } 36 | 37 | await cacheStoreService.clearByUid(contentType, params, wildcard); 38 | 39 | // send no-content status 40 | // ctx.status = 204; 41 | ctx.body = {}; 42 | }, 43 | }; 44 | } 45 | 46 | module.exports = { 47 | createPurgeController, 48 | }; 49 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { bootstrap } = require('./bootstrap'); 4 | const { services } = require('./services'); 5 | const { config } = require('./config'); 6 | const { controllers } = require('./controllers'); 7 | const { middlewares } = require('./middlewares'); 8 | const { routes } = require('./routes'); 9 | 10 | module.exports = { 11 | bootstrap, 12 | destroy() {}, 13 | config, 14 | controllers, 15 | routes, 16 | services, 17 | contentTypes: {}, 18 | policies: {}, 19 | middlewares, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/middlewares/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { createRecv: recv } = require('./recv'); 4 | const { createPurge: purge } = require('./purge'); 5 | const { createPurgeAdmin: purgeAdmin } = require('./purgeAdmin'); 6 | 7 | const middlewares = { 8 | recv, 9 | purge, 10 | purgeAdmin, 11 | }; 12 | 13 | module.exports = { 14 | middlewares, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/middlewares/purge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @param {{ contentType: string }} options 5 | * @param {{ strapi: import('@strapi/strapi').Strapi }} context 6 | */ 7 | 8 | function createPurge(options, { strapi }) { 9 | if (!options.contentType) { 10 | throw new Error( 11 | 'REST Cache: unable to initialize purge middleware: options.contentType is required' 12 | ); 13 | } 14 | 15 | const cacheConf = strapi.plugin('rest-cache').service('cacheConfig'); 16 | const cacheStore = strapi.plugin('rest-cache').service('cacheStore'); 17 | 18 | if (!cacheConf.isCached(options.contentType)) { 19 | throw new Error( 20 | `REST Cache: unable to initialize purge middleware: no configuration found for contentType "${options.contentType}"` 21 | ); 22 | } 23 | 24 | return async function purge(ctx, next) { 25 | await next(); 26 | 27 | if (!(ctx.status >= 200 && ctx.status <= 300)) return; 28 | 29 | await cacheStore.clearByUid(options.contentType, ctx.params); 30 | }; 31 | } 32 | 33 | module.exports = { 34 | createPurge, 35 | }; 36 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/middlewares/purgeAdmin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @param {{ }} options 5 | * @param {{ strapi: import('@strapi/strapi').Strapi }} context 6 | */ 7 | 8 | function createPurgeAdmin(options, { strapi }) { 9 | const cacheConfig = strapi.plugin('rest-cache').service('cacheConfig'); 10 | const cacheStore = strapi.plugin('rest-cache').service('cacheStore'); 11 | 12 | return async function purgeAdmin(ctx, next) { 13 | // uid: 14 | // - application::sport.sport 15 | // - plugins::users-permissions.user 16 | const { model: uid, ...params } = ctx.params; 17 | 18 | if (!uid) { 19 | await next(); 20 | return; 21 | } 22 | 23 | if (!cacheConfig.isCached(uid)) { 24 | await next(); 25 | return; 26 | } 27 | 28 | await next(); 29 | 30 | if (!(ctx.status >= 200 && ctx.status <= 300)) return; 31 | 32 | await cacheStore.clearByUid(uid, params); 33 | }; 34 | } 35 | 36 | module.exports = { 37 | createPurgeAdmin, 38 | }; 39 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/middlewares/recv.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('../types').CacheRouteConfig} CacheRouteConfig 5 | */ 6 | 7 | const chalk = require('chalk'); 8 | const debug = require('debug')('strapi:strapi-plugin-rest-cache'); 9 | 10 | const { generateCacheKey } = require('../utils/keys/generateCacheKey'); 11 | const { shouldLookup } = require('../utils/middlewares/shouldLookup'); 12 | const { etagGenerate } = require('../utils/etags/etagGenerate'); 13 | const { etagLookup } = require('../utils/etags/etagLookup'); 14 | const { etagMatch } = require('../utils/etags/etagMatch'); 15 | 16 | /** 17 | * @param {{ cacheRouteConfig: CacheRouteConfig }} options 18 | * @param {{ strapi: import('@strapi/strapi').Strapi }} context 19 | */ 20 | function createRecv(options, { strapi }) { 21 | if (!options?.cacheRouteConfig) { 22 | throw new Error( 23 | 'REST Cache: unable to initialize recv middleware: options.cacheRouteConfig is required' 24 | ); 25 | } 26 | const store = strapi.plugin('rest-cache').service('cacheStore'); 27 | const { strategy } = strapi.config.get('plugin.rest-cache'); 28 | const { cacheRouteConfig } = options; 29 | const { hitpass, maxAge, keys } = cacheRouteConfig; 30 | const { enableEtag = false, enableXCacheHeaders = false } = strategy; 31 | 32 | return async function recv(ctx, next) { 33 | // hash 34 | const cacheKey = generateCacheKey(ctx, keys); 35 | 36 | // hitpass check 37 | const lookup = shouldLookup(ctx, hitpass); 38 | 39 | // keep track of the etag 40 | let etagCached = null; 41 | 42 | if (lookup) { 43 | // lookup cached etag 44 | if (enableEtag) { 45 | etagCached = await etagLookup(cacheKey); 46 | 47 | if (etagCached && etagMatch(ctx, etagCached)) { 48 | if (enableXCacheHeaders) { 49 | ctx.set('X-Cache', 'HIT'); 50 | } 51 | 52 | // etag match -> send HTTP 304 Not Modified 53 | ctx.body = null; 54 | ctx.status = 304; 55 | return; 56 | } 57 | 58 | // etag miss 59 | } 60 | 61 | const cacheEntry = await store.get(cacheKey); 62 | 63 | // hit cache 64 | if (cacheEntry) { 65 | debug(`[RECV] GET ${cacheKey} ${chalk.green('HIT')}`); 66 | 67 | if (enableXCacheHeaders) { 68 | ctx.set('X-Cache', 'HIT'); 69 | } 70 | 71 | if (etagCached) { 72 | // send back cached etag on hit 73 | ctx.set('ETag', `"${etagCached}"`); 74 | } 75 | 76 | ctx.status = 200; 77 | ctx.body = cacheEntry; 78 | return; 79 | } 80 | } 81 | 82 | // fetch backend 83 | await next(); 84 | 85 | // fetch done 86 | if (!lookup) { 87 | debug(`[RECV] GET ${cacheKey} ${chalk.magenta('HITPASS')}`); 88 | 89 | if (enableXCacheHeaders) { 90 | ctx.set('X-Cache', 'HITPASS'); 91 | } 92 | 93 | // do not store hitpass response content 94 | return; 95 | } 96 | 97 | // deliver 98 | debug(`[RECV] GET ${cacheKey} ${chalk.yellow('MISS')}`); 99 | 100 | if (enableXCacheHeaders) { 101 | ctx.set('X-Cache', 'MISS'); 102 | } 103 | 104 | if (ctx.body && ctx.status >= 200 && ctx.status <= 300) { 105 | // @TODO check Cache-Control response header 106 | 107 | if (enableEtag) { 108 | const etag = etagGenerate(ctx, cacheKey); 109 | 110 | ctx.set('ETag', `"${etag}"`); 111 | 112 | // persist etag asynchronously 113 | store.set(`${cacheKey}_etag`, etag, maxAge).catch(() => { 114 | debug( 115 | `[RECV] GET ${cacheKey} ${chalk.yellow( 116 | 'Unable to store ETag in cache' 117 | )}` 118 | ); 119 | }); 120 | } 121 | 122 | // persist cache asynchronously 123 | store.set(cacheKey, ctx.body, maxAge).catch(() => { 124 | debug( 125 | `[RECV] GET ${cacheKey} ${chalk.yellow( 126 | 'Unable to store Content in cache' 127 | )}` 128 | ); 129 | }); 130 | } 131 | }; 132 | } 133 | 134 | module.exports = { 135 | createRecv, 136 | }; 137 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/permissions-actions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | actions: [ 5 | { 6 | section: 'plugins', 7 | displayName: 'Purge cache', 8 | uid: 'cache.purge', 9 | subCategory: 'purge', 10 | pluginName: 'rest-cache', 11 | }, 12 | { 13 | section: 'plugins', 14 | displayName: 'Read cache strategy configuration', 15 | uid: 'cache.read-strategy', 16 | subCategory: 'read', 17 | pluginName: 'rest-cache', 18 | }, 19 | { 20 | section: 'plugins', 21 | displayName: 'Read cache provider configuration', 22 | uid: 'cache.read-provider', 23 | subCategory: 'read', 24 | pluginName: 'rest-cache', 25 | }, 26 | ], 27 | }; 28 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/pluginId.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // eslint-disable-next-line import/extensions 4 | const packageJson = require('../package.json'); 5 | 6 | module.exports = packageJson.strapi.name; 7 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/routes/admin/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const pluginId = require('../../pluginId'); 4 | 5 | module.exports = [ 6 | { 7 | method: 'GET', 8 | path: '/config/strategy', 9 | handler: 'config.strategy', 10 | config: { 11 | policies: [ 12 | 'admin::isAuthenticatedAdmin', 13 | { 14 | name: 'plugin::content-manager.hasPermissions', 15 | config: { actions: [`plugin::${pluginId}.cache.read-strategy`] }, 16 | }, 17 | ], 18 | }, 19 | }, 20 | { 21 | method: 'GET', 22 | path: '/config/provider', 23 | handler: 'config.provider', 24 | config: { 25 | policies: [ 26 | 'admin::isAuthenticatedAdmin', 27 | { 28 | name: 'plugin::content-manager.hasPermissions', 29 | config: { actions: [`plugin::${pluginId}.cache.read-provider`] }, 30 | }, 31 | ], 32 | }, 33 | }, 34 | ]; 35 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/routes/admin/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const configRoutes = require('./config'); 4 | const purgeRoutes = require('./purge'); 5 | 6 | module.exports = { 7 | type: 'admin', 8 | routes: [...configRoutes, ...purgeRoutes], 9 | }; 10 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/routes/admin/purge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const pluginId = require('../../pluginId'); 4 | 5 | module.exports = [ 6 | { 7 | method: 'POST', 8 | path: '/purge', 9 | handler: 'purge.index', 10 | config: { 11 | policies: [ 12 | 'admin::isAuthenticatedAdmin', 13 | { 14 | name: 'plugin::content-manager.hasPermissions', 15 | config: { actions: [`plugin::${pluginId}.cache.purge`] }, 16 | }, 17 | ], 18 | }, 19 | }, 20 | ]; 21 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const admin = require('./admin'); 4 | 5 | const routes = { 6 | admin, 7 | }; 8 | 9 | module.exports = { 10 | routes, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/services/cacheConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('@strapi/strapi').Strapi} Strapi 5 | */ 6 | 7 | const { getRouteRegExp } = require('../utils/config/getRouteRegExp'); 8 | 9 | /** 10 | * @param {{ strapi: Strapi }} strapi 11 | */ 12 | 13 | function createCacheConfigService({ strapi }) { 14 | return { 15 | /** 16 | * Get all uid of cached contentTypes 17 | * 18 | * uid: 19 | * - api::sport.sport 20 | * - plugin::users-permissions.user 21 | * 22 | * @return {string[]} 23 | */ 24 | getUids() { 25 | const { strategy } = strapi.config.get('plugin.rest-cache'); 26 | return strategy.contentTypes.map((cacheConf) => cacheConf.contentType); 27 | }, 28 | 29 | /** 30 | * Return the intersection of cached contentTypes and the related contentTypes of a given contentType uid 31 | * 32 | * uid: 33 | * - api::sport.sport 34 | * - plugin::users-permissions.user 35 | * 36 | * @return {string[]} 37 | */ 38 | getRelatedCachedUid(uid) { 39 | const cacheConf = this.get(uid); 40 | if (!cacheConf) { 41 | return []; 42 | } 43 | 44 | const cached = this.getUids(); 45 | const related = cacheConf.relatedContentTypeUid; 46 | 47 | return related.filter((relatedUid) => cached.includes(relatedUid)); 48 | }, 49 | 50 | /** 51 | * Get related ModelCacheConfig with an uid 52 | * 53 | * uid: 54 | * - api::sport.sport 55 | * - plugin::users-permissions.user 56 | * 57 | * @param {string} uid 58 | * @return {CacheContentTypeConfig | undefined} 59 | */ 60 | get(uid) { 61 | const { strategy } = strapi.config.get('plugin.rest-cache'); 62 | return strategy.contentTypes.find( 63 | (cacheConf) => cacheConf.contentType === uid 64 | ); 65 | }, 66 | 67 | /** 68 | * Get regexs to match all ModelCacheConfig keys with given params 69 | * 70 | * @param {string} uid 71 | * @param {any} params 72 | * @param {boolean=} wildcard 73 | * @return {RegExp[]} 74 | */ 75 | getCacheKeysRegexp(uid, params, wildcard = false) { 76 | const cacheConf = this.get(uid); 77 | if (!cacheConf) { 78 | return []; 79 | } 80 | 81 | const regExps = []; 82 | 83 | const routes = cacheConf.routes.filter((route) => route.method === 'GET'); 84 | 85 | for (const route of routes) { 86 | regExps.push(...getRouteRegExp(route, params, wildcard)); 87 | } 88 | 89 | return regExps; 90 | }, 91 | 92 | /** 93 | * Check if a cache configuration exists for a contentType uid 94 | * 95 | * uid: 96 | * - api::sport.sport 97 | * - plugin::users-permissions.user 98 | * 99 | * @param {string} uid 100 | * @return {boolean} 101 | */ 102 | isCached(uid) { 103 | return !!this.get(uid); 104 | }, 105 | 106 | /** 107 | * @deprecated use strapi.plugin('rest-cache').service('cacheStore').clearByUid instead 108 | */ 109 | async clearCache(uid, params = {}, wildcard = false) { 110 | strapi.log.warn( 111 | 'REST Cache cacheConfig.clearCache is deprecated, use cacheStore.clearByUid instead' 112 | ); 113 | strapi 114 | .plugin('rest-cache') 115 | .service('cacheStore') 116 | .clearByUid(uid, params, wildcard); 117 | }, 118 | }; 119 | } 120 | 121 | module.exports = { createCacheConfigService }; 122 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/services/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { createCacheConfigService: cacheConfig } = require('./cacheConfig'); 4 | const { createCacheStoreService: cacheStore } = require('./cacheStore'); 5 | 6 | const services = { 7 | cacheConfig, 8 | cacheStore, 9 | }; 10 | 11 | module.exports = { services }; 12 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/types/CacheContentTypeConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('./CacheRouteConfig').CacheRouteConfig} CacheRouteConfig 5 | * @typedef {import('koa').Context} Context 6 | * @typedef {(ctx: Context) => boolean} CachePluginHitpass 7 | */ 8 | const { CacheKeysConfig } = require('./CacheKeysConfig'); 9 | 10 | class CacheContentTypeConfig { 11 | singleType = false; 12 | 13 | injectDefaultRoutes = true; 14 | 15 | maxAge = 3600000; 16 | 17 | /** 18 | * @type {CachePluginHitpass | boolean} 19 | */ 20 | hitpass = false; 21 | 22 | /** 23 | * @type {CacheKeysConfig} 24 | */ 25 | keys; 26 | 27 | /** 28 | * @type {string=} 29 | */ 30 | plugin; 31 | 32 | /** 33 | * @type {CacheRouteConfig[]} 34 | */ 35 | routes = []; 36 | 37 | /** 38 | * @type {string} 39 | */ 40 | contentType; 41 | 42 | /** 43 | * @type {string[]} 44 | */ 45 | relatedContentTypeUid = []; 46 | 47 | constructor(options = {}) { 48 | const { 49 | singleType = false, 50 | injectDefaultRoutes = true, 51 | maxAge = true, 52 | hitpass = false, 53 | keys = new CacheKeysConfig(), 54 | routes = [], 55 | relatedContentTypeUid = [], 56 | contentType, 57 | plugin, 58 | } = options; 59 | 60 | this.singleType = singleType; 61 | this.injectDefaultRoutes = injectDefaultRoutes; 62 | this.maxAge = maxAge; 63 | this.hitpass = hitpass; 64 | this.keys = keys; 65 | this.routes = routes; 66 | this.relatedContentTypeUid = relatedContentTypeUid; 67 | this.contentType = contentType; 68 | this.plugin = plugin; 69 | } 70 | } 71 | 72 | module.exports = { CacheContentTypeConfig }; 73 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/types/CacheKeysConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class CacheKeysConfig { 4 | /** 5 | * @type {string[]} 6 | */ 7 | useHeaders = []; 8 | 9 | /** 10 | * @type {Boolean|string[]} 11 | */ 12 | useQueryParams = true; 13 | 14 | constructor(options = {}) { 15 | const { useHeaders = [], useQueryParams = true } = options; 16 | this.useHeaders = useHeaders; 17 | this.useQueryParams = useQueryParams; 18 | 19 | this.useHeaders.sort(); 20 | } 21 | } 22 | 23 | module.exports = { CacheKeysConfig }; 24 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/types/CachePluginStrategy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('./CacheContentTypeConfig').CacheContentTypeConfig} CacheContentTypeConfig 5 | */ 6 | const { CacheKeysConfig } = require('./CacheKeysConfig'); 7 | 8 | class CachePluginStrategy { 9 | debug = false; 10 | 11 | enableEtag = false; 12 | 13 | enableXCacheHeaders = false; 14 | 15 | enableAdminCTBMiddleware = true; 16 | 17 | resetOnStartup = false; 18 | 19 | clearRelatedCache = false; 20 | 21 | maxAge = 3600000; 22 | 23 | keysPrefix = ''; 24 | 25 | /** 26 | * @type {CacheContentTypeConfig[]} 27 | */ 28 | contentTypes = []; 29 | 30 | /** 31 | * @type {CacheKeysConfig} 32 | */ 33 | keys; 34 | 35 | constructor(options = {}) { 36 | const { 37 | debug = false, 38 | enableEtag = false, 39 | enableXCacheHeaders = false, 40 | enableAdminCTBMiddleware = true, 41 | resetOnStartup = false, 42 | clearRelatedCache = true, 43 | maxAge = 3600000, 44 | keysPrefix = '', 45 | contentTypes = [], 46 | keys = new CacheKeysConfig(), 47 | } = options; 48 | 49 | this.debug = debug; 50 | this.enableEtag = enableEtag; 51 | this.enableXCacheHeaders = enableXCacheHeaders; 52 | this.enableAdminCTBMiddleware = enableAdminCTBMiddleware; 53 | this.resetOnStartup = resetOnStartup; 54 | this.clearRelatedCache = clearRelatedCache; 55 | this.maxAge = maxAge; 56 | this.keysPrefix = keysPrefix; 57 | this.contentTypes = contentTypes; 58 | this.keys = keys; 59 | } 60 | } 61 | 62 | module.exports = { CachePluginStrategy }; 63 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/types/CacheProvider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint-disable class-methods-use-this */ 4 | /* eslint-disable no-unused-vars */ 5 | 6 | /** 7 | * Abstract Class CacheProvider. 8 | * 9 | * @class CacheProvider 10 | */ 11 | class CacheProvider { 12 | constructor() { 13 | if (this.constructor === CacheProvider) { 14 | throw new Error("CacheProvider class can't be instantiated."); 15 | } 16 | } 17 | 18 | /** 19 | * @param {string} key 20 | */ 21 | async get(key) { 22 | throw new Error("Method 'get()' must be implemented."); 23 | } 24 | 25 | /** 26 | * @param {string} key 27 | * @param {any} val 28 | * @param {number=} maxAge 29 | */ 30 | async set(key, val, maxAge = 3600) { 31 | throw new Error("Method 'set()' must be implemented."); 32 | } 33 | 34 | /** 35 | * @param {string|string[]} key 36 | */ 37 | async del(key) { 38 | throw new Error("Method 'del()' must be implemented."); 39 | } 40 | 41 | async keys() { 42 | throw new Error("Method 'keys()' must be implemented."); 43 | } 44 | 45 | get ready() { 46 | throw new Error("getter 'ready' must be implemented."); 47 | } 48 | } 49 | 50 | module.exports = { 51 | CacheProvider, 52 | }; 53 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/types/CacheRouteConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {(ctx: Context) => boolean} CachePluginHitpass 5 | */ 6 | const { CacheKeysConfig } = require('./CacheKeysConfig'); 7 | 8 | class CacheRouteConfig { 9 | maxAge = 3600000; 10 | 11 | /** 12 | * @type {string} 13 | */ 14 | path; 15 | 16 | /** 17 | * @type {'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'} 18 | */ 19 | method = 'GET'; 20 | 21 | /** 22 | * @type {string[]} 23 | */ 24 | paramNames = []; 25 | 26 | /** 27 | * @type {CacheKeysConfig} 28 | */ 29 | keys; 30 | 31 | /** 32 | * @type {CachePluginHitpass | boolean} 33 | */ 34 | hitpass = false; 35 | 36 | constructor(options = {}) { 37 | const { 38 | path, 39 | method = 'GET', 40 | paramNames = [], 41 | maxAge = 3600000, 42 | hitpass = false, 43 | keys = new CacheKeysConfig(), 44 | } = options; 45 | this.path = path; 46 | this.method = method; 47 | this.paramNames = paramNames; 48 | this.maxAge = maxAge; 49 | this.hitpass = hitpass; 50 | this.keys = keys; 51 | } 52 | } 53 | 54 | module.exports = { 55 | CacheRouteConfig, 56 | }; 57 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/types/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { CachePluginStrategy } = require('./CachePluginStrategy'); 4 | const { CacheRouteConfig } = require('./CacheRouteConfig'); 5 | const { CacheProvider } = require('./CacheProvider'); 6 | const { CacheContentTypeConfig } = require('./CacheContentTypeConfig'); 7 | const { CacheKeysConfig } = require('./CacheKeysConfig'); 8 | 9 | module.exports = { 10 | CachePluginStrategy, 11 | CacheRouteConfig, 12 | CacheProvider, 13 | CacheContentTypeConfig, 14 | CacheKeysConfig, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/config/deepFreeze.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function deepFreeze(object) { 4 | // Retrieve the property names defined on object 5 | const propNames = Object.getOwnPropertyNames(object); 6 | 7 | // Freeze properties before freezing self 8 | for (const name of propNames) { 9 | const value = object[name]; 10 | 11 | if (value && typeof value === 'object') { 12 | deepFreeze(value); 13 | } 14 | } 15 | 16 | return Object.freeze(object); 17 | } 18 | 19 | module.exports = { deepFreeze }; 20 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/config/getRelatedModelsUid.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('@strapi/strapi').Strapi} Strapi 5 | */ 6 | 7 | /** 8 | * Get models uid that is related to a ModelCacheConfig 9 | * 10 | * @param {Strapi} strapi 11 | * @param {string} uid The contentType used to find related caches to purge 12 | * @return {string[]} Array of related models uid 13 | */ 14 | function getRelatedModelsUid(strapi, uid) { 15 | if (!uid) { 16 | return []; 17 | } 18 | 19 | const models = strapi.contentTypes; 20 | 21 | const { components } = strapi; 22 | const modelObj = models[uid]; 23 | const modelAttributes = modelObj?.attributes ?? {}; 24 | /** 25 | * @type {any[]} 26 | */ 27 | const relatedComponents = []; 28 | /** 29 | * @type {any} 30 | */ 31 | const relatedModels = {}; 32 | 33 | // first, look for direct relations in other contentTypes 34 | for (const key in modelAttributes) { 35 | if (!Object.hasOwnProperty.call(modelAttributes, key)) { 36 | continue; 37 | } 38 | 39 | const attr = modelAttributes[key]; 40 | 41 | if (attr.type !== 'relation') continue; 42 | if (!attr.target) continue; 43 | 44 | relatedModels[attr.target] = models[attr.target]; 45 | } 46 | 47 | // second, look for relations to current model in components 48 | for (const compKey in components) { 49 | if (!Object.hasOwnProperty.call(components, compKey)) { 50 | continue; 51 | } 52 | 53 | const compObj = components[compKey]; 54 | const attributes = compObj?.attributes ?? {}; 55 | 56 | for (const key in attributes) { 57 | if (!Object.hasOwnProperty.call(attributes, key)) { 58 | continue; 59 | } 60 | 61 | const attr = attributes[key]; 62 | 63 | if (attr.type !== 'relation') continue; 64 | if (!attr.target) continue; 65 | 66 | if (attr.target === uid) { 67 | relatedComponents.push(compKey); 68 | } 69 | } 70 | } 71 | 72 | // third, look for nested components with relations to current model 73 | for (const compKey in components) { 74 | if (!Object.hasOwnProperty.call(components, compKey)) { 75 | continue; 76 | } 77 | const compObj = components[compKey]; 78 | const attributes = compObj?.attributes ?? {}; 79 | 80 | // check if current component contains another component that contains a relation to the current model 81 | // look one level deeper 82 | for (const key in attributes) { 83 | if (!Object.hasOwnProperty.call(attributes, key)) { 84 | continue; 85 | } 86 | 87 | const attr = attributes[key]; 88 | 89 | if (attr.type !== 'component') continue; 90 | if (!attr.component) continue; 91 | if (!relatedComponents.includes(attr.component)) continue; 92 | if (relatedComponents.includes(compKey)) continue; 93 | 94 | relatedComponents.push(compKey); 95 | } 96 | } 97 | 98 | // finally locate all the models that have the related components in their models 99 | for (const modelKey in models) { 100 | if (!Object.hasOwnProperty.call(models, modelKey)) { 101 | continue; 102 | } 103 | 104 | const otherModelObj = models[modelKey]; 105 | const attributes = otherModelObj?.attributes ?? {}; 106 | 107 | for (const key in attributes) { 108 | if (!Object.hasOwnProperty.call(attributes, key)) { 109 | continue; 110 | } 111 | const attr = attributes[key]; 112 | 113 | if (attr.type !== 'component') continue; 114 | if (!attr.component) continue; 115 | if (!relatedComponents.includes(attr.component)) continue; 116 | if (relatedModels[modelKey]) continue; 117 | 118 | relatedModels[modelKey] = models[modelKey]; 119 | } 120 | } 121 | 122 | const relatedModelUid = []; 123 | 124 | Object.values(relatedModels).reduce((acc, model) => { 125 | if (model.uid) { 126 | acc.push(model.uid); 127 | } 128 | return acc; 129 | }, relatedModelUid); 130 | 131 | return relatedModelUid; 132 | } 133 | 134 | module.exports = { getRelatedModelsUid }; 135 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/config/getRouteRegExp.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('../../types').CacheRouteConfig} CacheRouteConfig 5 | * @typedef {import('koa').Context} Context 6 | */ 7 | 8 | /** 9 | * Get regexs to match CustomRoute keys with given params 10 | * 11 | * @param {CacheRouteConfig} route 12 | * @param {any} params 13 | * @param {boolean=} wildcard 14 | * @return {RegExp[]} 15 | */ 16 | function getRouteRegExp(route, params, wildcard = false) { 17 | // route not contains any params -> clear 18 | if (!route.paramNames || !route.paramNames.length) { 19 | return [new RegExp(`^${route.path}\\?`)]; 20 | } 21 | 22 | // wildcard: clear all routes 23 | if (wildcard) { 24 | let pattern = route.path; 25 | for (const paramName of route.paramNames) { 26 | pattern = pattern 27 | .replace(new RegExp(`:${paramName}([^\\/#\\?]*)`, 'g'), '([^\\/#\\?]+)') 28 | .replace('//', '/'); 29 | } 30 | 31 | return [new RegExp(`^${pattern}\\?`)]; 32 | } 33 | 34 | if (!params) { 35 | return []; 36 | } 37 | 38 | const paramNames = Object.keys(params); 39 | const regExps = []; 40 | 41 | let pattern = route.path; 42 | for (const paramName of paramNames) { 43 | pattern = pattern 44 | .replace(new RegExp(`:${paramName}([^\\/#\\?]*)`, 'g'), params[paramName]) 45 | .replace('//', '/'); 46 | } 47 | 48 | // add if pattern does not contain any unresolved params 49 | if (!pattern.includes(':')) { 50 | regExps.push(new RegExp(`^${pattern}\\?`)); 51 | } 52 | 53 | return regExps; 54 | } 55 | 56 | module.exports = { getRouteRegExp }; 57 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/config/routeExists.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('../../types').CacheRouteConfig} CacheRouteConfig 5 | */ 6 | 7 | /** 8 | * Check if a custom route is registered in strapi 9 | * 10 | * @param {Strapi} strapi 11 | * @param {CacheRouteConfig} route 12 | * @return {boolean} 13 | */ 14 | function routeExists(strapi, route) { 15 | // check api routes 16 | const allRoutes = [ 17 | ...strapi.server.listRoutes(), 18 | ...strapi.server.api('content-api').listRoutes(), 19 | ...strapi.server.api('admin').listRoutes(), 20 | ]; 21 | const match = allRoutes.find( 22 | (routeLayer) => 23 | routeLayer.methods.includes(route.method) && 24 | routeLayer.path.match(new RegExp(`^${route.path}/?`)) // match with optional leading slash 25 | ); 26 | 27 | return Boolean(match); 28 | } 29 | 30 | module.exports = { routeExists }; 31 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/etags/etagGenerate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const crypto = require('crypto'); 4 | 5 | function etagGenerate(ctx) { 6 | const type = typeof ctx.body; 7 | const etag = crypto 8 | .createHash('md5') 9 | .update(type === 'string' ? ctx.body : JSON.stringify(ctx.body)) 10 | .digest('hex'); 11 | 12 | return etag; 13 | } 14 | module.exports = { 15 | etagGenerate, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/etags/etagLookup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | async function etagLookup(cacheKey) { 4 | const store = strapi.plugin('rest-cache').service('cacheStore'); 5 | const etagCached = await store.get(`${cacheKey}_etag`); 6 | 7 | if (etagCached) { 8 | return etagCached; 9 | } 10 | 11 | return null; 12 | } 13 | 14 | module.exports = { etagLookup }; 15 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/etags/etagMatch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function etagMatch(ctx, etagCached) { 4 | const ifNoneMatch = ctx.request.headers['if-none-match']; 5 | 6 | if (!ifNoneMatch) { 7 | return false; 8 | } 9 | 10 | return ifNoneMatch.indexOf(`"${etagCached}"`) !== -1; 11 | } 12 | 13 | module.exports = { etagMatch }; 14 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/keys/generateCacheKey.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { toLower } = require('lodash/fp'); 4 | const path = require('path'); 5 | const { generateHeadersKey } = require('./generateHeadersKey'); 6 | const { generateQueryParamsKey } = require('./generateQueryParamsKey'); 7 | 8 | function generateCacheKey( 9 | ctx, 10 | keys = { 11 | useQueryParams: false, // @todo: array or boolean => can be optimized 12 | useHeaders: [], 13 | } 14 | ) { 15 | let querySuffix = ''; 16 | let headersSuffix = ''; 17 | 18 | if (keys.useQueryParams !== false) { 19 | querySuffix = generateQueryParamsKey(ctx, keys.useQueryParams); 20 | } 21 | 22 | if (keys.useHeaders.length > 0) { 23 | headersSuffix = generateHeadersKey(ctx, keys.useHeaders); 24 | } 25 | 26 | const requestPath = toLower(path.posix.normalize(ctx.request.path)).replace( 27 | /\/$/, 28 | '' 29 | ); 30 | 31 | return `${requestPath}?${querySuffix}&${headersSuffix}`; 32 | } 33 | 34 | module.exports = { generateCacheKey }; 35 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/keys/generateHeadersKey.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function generateHeadersKey(ctx, useHeaders = []) { 4 | return useHeaders 5 | .filter((k) => ctx.request.header[k.toLowerCase()] !== undefined) 6 | .map((k) => `${k.toLowerCase()}=${ctx.request.header[k.toLowerCase()]}`) // headers are key insensitive 7 | .join(','); 8 | } 9 | 10 | module.exports = { generateHeadersKey }; 11 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/keys/generateQueryParamsKey.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function generateQueryParamsKey( 4 | ctx, 5 | useQueryParams = true // @todo: array or boolean => can be optimized 6 | ) { 7 | let keys = []; 8 | 9 | if (useQueryParams === true) { 10 | keys = Object.keys(ctx.query); 11 | } else if (useQueryParams.length > 0) { 12 | keys = Object.keys(ctx.query).filter((key) => useQueryParams.includes(key)); 13 | } 14 | 15 | if (keys.length === 0) { 16 | return ''; 17 | } 18 | 19 | keys.sort(); 20 | 21 | return keys 22 | .map( 23 | (k) => 24 | `${k}=${ 25 | typeof ctx.query[k] === 'object' 26 | ? JSON.stringify(ctx.query[k]) 27 | : ctx.query[k] 28 | }` 29 | ) // query strings are key sensitive 30 | .join(','); 31 | } 32 | 33 | module.exports = { generateQueryParamsKey }; 34 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/middlewares/createRouter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Router = require('@koa/router'); 4 | const chalk = require('chalk'); 5 | const debug = require('debug')('strapi:strapi-plugin-rest-cache'); 6 | 7 | /** 8 | * @typedef {import('../../types').CachePluginStrategy} CachePluginStrategy 9 | */ 10 | 11 | // @see https://github.com/strapi/strapi/blob/master/packages/core/content-manager/server/routes/admin.js 12 | const adminRoutes = { 13 | post: [ 14 | '/content-manager/single-types/:model/actions/publish', 15 | '/content-manager/single-types/:model/actions/unpublish', 16 | '/content-manager/collection-types/:model', 17 | '/content-manager/collection-types/:model/:id/actions/publish', 18 | '/content-manager/collection-types/:model/:id/actions/unpublish', 19 | '/content-manager/collection-types/:model/actions/bulkDelete', 20 | ], 21 | put: [ 22 | '/content-manager/single-types/:model', 23 | '/content-manager/collection-types/:model/:id', 24 | ], 25 | delete: [ 26 | '/content-manager/single-types/:model', 27 | '/content-manager/collection-types/:model/:id', 28 | ], 29 | }; 30 | 31 | /** 32 | * @param {Strapi} strapi 33 | * @param {CachePluginStrategy} strategy 34 | * @return {Router} 35 | */ 36 | function createRouter(strapi, strategy) { 37 | const router = new Router(); 38 | 39 | const createRecvMiddleware = strapi.plugin('rest-cache').middleware('recv'); 40 | const createPurgeMiddleware = strapi.plugin('rest-cache').middleware('purge'); 41 | const createPurgeAdminMiddleware = strapi 42 | .plugin('rest-cache') 43 | .middleware('purgeAdmin'); 44 | const purgeAdminMiddleware = createPurgeAdminMiddleware({}, { strapi }); 45 | 46 | for (const cacheConf of strategy.contentTypes) { 47 | debug(`[REGISTER] ${chalk.cyan(cacheConf.contentType)} routes middlewares`); 48 | 49 | const purgeMiddleware = createPurgeMiddleware( 50 | { contentType: cacheConf.contentType }, 51 | { strapi } 52 | ); 53 | 54 | for (const route of cacheConf.routes) { 55 | switch (route.method) { 56 | case 'DELETE': { 57 | debug(`[REGISTER] DELETE ${route.path} ${chalk.redBright('purge')}`); 58 | router.delete(route.path, purgeMiddleware); 59 | break; 60 | } 61 | case 'PUT': { 62 | debug(`[REGISTER] PUT ${route.path} ${chalk.redBright('purge')}`); 63 | router.put(route.path, purgeMiddleware); 64 | break; 65 | } 66 | case 'PATCH': { 67 | debug(`[REGISTER] PATCH ${route.path} ${chalk.redBright('purge')}`); 68 | router.patch(route.path, purgeMiddleware); 69 | break; 70 | } 71 | case 'POST': { 72 | debug(`[REGISTER] POST ${route.path} ${chalk.redBright('purge')}`); 73 | router.post(route.path, purgeMiddleware); 74 | break; 75 | } 76 | case 'GET': { 77 | const vary = route.keys.useHeaders 78 | .map((name) => name.toLowerCase()) 79 | .join(','); 80 | 81 | debug( 82 | `[REGISTER] GET ${route.path} ${chalk.green('recv')} ${chalk.grey( 83 | `maxAge=${route.maxAge}` 84 | )}${vary && chalk.grey(` vary=${vary}`)}` 85 | ); 86 | 87 | router.get( 88 | route.path, 89 | createRecvMiddleware({ cacheRouteConfig: route }, { strapi }) 90 | ); 91 | break; 92 | } 93 | default: 94 | break; 95 | } 96 | } 97 | } 98 | 99 | // --- Admin REST endpoints 100 | if (strategy.enableAdminCTBMiddleware) { 101 | debug(`[REGISTER] ${chalk.magentaBright('admin')} routes middlewares`); 102 | 103 | for (const route of adminRoutes.post) { 104 | debug(`[REGISTER] POST ${route} ${chalk.magentaBright('purge-admin')}`); 105 | router.post(route, purgeAdminMiddleware); 106 | } 107 | for (const route of adminRoutes.put) { 108 | debug(`[REGISTER] PUT ${route} ${chalk.magentaBright('purge-admin')}`); 109 | router.put(route, purgeAdminMiddleware); 110 | } 111 | for (const route of adminRoutes.delete) { 112 | debug(`[REGISTER] DELETE ${route} ${chalk.magentaBright('purge-admin')}`); 113 | router.delete(route, purgeAdminMiddleware); 114 | } 115 | } 116 | 117 | return router; 118 | } 119 | 120 | module.exports = { createRouter }; 121 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/middlewares/shouldLookup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function shouldLookup( 4 | ctx, 5 | hitpass // @todo: function or boolean => can be optimized 6 | ) { 7 | const type = typeof hitpass; 8 | 9 | if (type === 'boolean') { 10 | return !hitpass; 11 | } 12 | 13 | if (type === 'function') { 14 | return !hitpass(ctx); 15 | } 16 | 17 | return false; 18 | } 19 | 20 | module.exports = { 21 | shouldLookup, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/store/deserialize.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @param {string} str 5 | * @return {any} 6 | */ 7 | function deserialize(str) { 8 | if (!str) { 9 | return null; 10 | } 11 | return JSON.parse(str).data; 12 | } 13 | 14 | module.exports = { deserialize }; 15 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/store/serialize.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @param {any} data 5 | * @return {string} 6 | */ 7 | function serialize(data) { 8 | return JSON.stringify({ data }); 9 | } 10 | 11 | module.exports = { serialize }; 12 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/server/utils/store/withTimeout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Reject promise after timeout 5 | * 6 | * @todo this is slow, we should find a way to do this in a faster way 7 | * @param {Promise} callback 8 | * @param {number} ms 9 | * @return {Promise} 10 | */ 11 | function withTimeout(callback, ms) { 12 | let timeout; 13 | 14 | return Promise.race([ 15 | callback().then((result) => { 16 | clearTimeout(timeout); 17 | return result; 18 | }), 19 | new Promise((_, reject) => { 20 | timeout = setTimeout(() => { 21 | reject(new Error('timeout')); 22 | }, ms); 23 | }), 24 | ]); 25 | } 26 | 27 | module.exports = { 28 | withTimeout, 29 | }; 30 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/strapi-admin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./admin/src').default; 4 | -------------------------------------------------------------------------------- /packages/strapi-plugin-rest-cache/strapi-server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./server'); 4 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-couchbase/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [4.2.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.3...v4.2.4) (2022-03-19) 7 | 8 | **Note:** Version bump only for package strapi-provider-rest-cache-couchbase 9 | 10 | 11 | 12 | 13 | 14 | ## [4.2.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.2...v4.2.3) (2022-03-18) 15 | 16 | **Note:** Version bump only for package strapi-provider-rest-cache-couchbase 17 | 18 | 19 | 20 | 21 | 22 | ## [4.2.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.1...v4.2.2) (2022-03-15) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * ensure keyprefix is not undefined ([9134f52](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/9134f52a0ea8a8399db4af59a5dc689742104739)) 28 | 29 | 30 | 31 | 32 | 33 | ## [4.2.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.0...v4.2.1) (2022-03-11) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * empty keys returned by providers ([fb5c79c](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/fb5c79c490309e8bd4458726fe8aedacbfae503b)) 39 | 40 | 41 | 42 | 43 | 44 | # [4.2.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.1.0...v4.2.0) (2022-03-09) 45 | 46 | **Note:** Version bump only for package strapi-provider-rest-cache-couchbase 47 | 48 | 49 | 50 | 51 | 52 | # [4.1.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.6...v4.1.0) (2022-03-05) 53 | 54 | **Note:** Version bump only for package strapi-provider-rest-cache-couchbase 55 | 56 | 57 | 58 | 59 | 60 | ## [4.0.6](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.5...v4.0.6) (2022-03-02) 61 | 62 | **Note:** Version bump only for package strapi-provider-rest-cache-couchbase 63 | 64 | ## [4.0.5](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.4...v4.0.5) (2022-03-02) 65 | 66 | **Note:** Version bump only for package strapi-provider-rest-cache-couchbase 67 | 68 | ## [4.0.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.3...v4.0.4) (2022-02-26) 69 | 70 | **Note:** Version bump only for package strapi-provider-rest-cache-couchbase 71 | 72 | ## [4.0.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.2...v4.0.3) (2022-02-26) 73 | 74 | **Note:** Version bump only for package strapi-provider-rest-cache-couchbase 75 | 76 | ## [4.0.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.1...v4.0.2) (2022-02-24) 77 | 78 | ### Bug Fixes 79 | 80 | - use short plugin name ([8daf416](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/8daf41643c2479c0df19a2fe137cae7ec395ec78)) 81 | 82 | ## [4.0.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0...v4.0.1) (2022-02-24) 83 | 84 | ### Bug Fixes 85 | 86 | - empty npm packages ([1fde26a](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/1fde26a1da956c854661b036bc48483c49f9f75e)) 87 | 88 | # Change Log 89 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-couchbase/lib/CouchbaseCacheProvider.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const cacheManager = require("cache-manager"); 4 | const couchbaseStore = require("cache-manager-couchbase"); 5 | const { CacheProvider } = require("strapi-plugin-rest-cache/server/types"); 6 | 7 | class CouchbaseCacheProvider extends CacheProvider { 8 | constructor(options) { 9 | super(); 10 | this.cache = cacheManager.caching({ 11 | store: couchbaseStore, 12 | ...options, 13 | }); 14 | } 15 | 16 | /** 17 | * @param {string} key 18 | */ 19 | async get(key) { 20 | return this.cache.get(key); 21 | } 22 | 23 | /** 24 | * @param {string} key 25 | * @param {any} val 26 | * @param {number=} maxAge 27 | */ 28 | async set(key, val, maxAge = 3600) { 29 | // TODO: When we upgrade the cache manager >=5.x.x, need to multiply this not divide 30 | const options = { 31 | ttl: maxAge / 1000, 32 | }; 33 | return this.cache.set(key, val, options); 34 | } 35 | 36 | /** 37 | * @param {string|string[]} key 38 | */ 39 | async del(key) { 40 | return this.cache.del(key); 41 | } 42 | 43 | async keys() { 44 | return this.cache.keys(); 45 | } 46 | 47 | get ready() { 48 | return this.cache.store.getClient() != null; 49 | } 50 | } 51 | 52 | module.exports = { 53 | CouchbaseCacheProvider, 54 | }; 55 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-couchbase/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { CouchbaseCacheProvider } = require("./CouchbaseCacheProvider"); 4 | 5 | module.exports = { 6 | provider: "couchbase", 7 | name: "Couchbase", 8 | 9 | async init(options, { strapi }) { 10 | if ( 11 | options.connectionString == null || 12 | options.connectionString === "undefined" 13 | ) { 14 | throw new Error( 15 | `Could not initialize REST Cache provider "couchbase". Missing 'connectionString'` 16 | ); 17 | } 18 | if ( 19 | options.connectionOptions == null || 20 | options.connectionOptions === "undefined" 21 | ) { 22 | throw new Error( 23 | `Could not initialize REST Cache provider "couchbase". Missing 'connectionOptions'` 24 | ); 25 | } 26 | if ( 27 | options.connectionOptions.username == null || 28 | options.connectionOptions.username === "undefined" 29 | ) { 30 | throw new Error( 31 | `Could not initialize REST Cache provider "couchbase". Missing 'connectionOptions.username'` 32 | ); 33 | } 34 | if ( 35 | options.connectionOptions.password == null || 36 | options.connectionOptions.password === "undefined" 37 | ) { 38 | throw new Error( 39 | `Could not initialize REST Cache provider "couchbase". Missing 'connectionOptions.password'` 40 | ); 41 | } 42 | if (options.bucket == null || options.bucket === "undefined") { 43 | throw new Error( 44 | `Could not initialize REST Cache provider "couchbase". Missing 'bucket'` 45 | ); 46 | } 47 | 48 | const provider = new CouchbaseCacheProvider(options); 49 | 50 | strapi.log.info( 51 | `CouchbaseCacheProvider initialised with connectionString: "${options.connectionString}", bucket: "${options.bucket}", scope: "${options.scope}", collection: "${options.collection}",` 52 | ); 53 | 54 | return provider; 55 | }, 56 | }; 57 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-couchbase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strapi-provider-rest-cache-couchbase", 3 | "version": "4.2.9", 4 | "description": "Speed-up HTTP requests with Couchbase database", 5 | "license": "MIT", 6 | "strapi": { 7 | "displayName": "REST Cache (couchbase)", 8 | "name": "strapi-provider-rest-cache-couchbase", 9 | "description": "Couchbase provider for REST Cache plugin", 10 | "kind": "plugin" 11 | }, 12 | "keywords": [ 13 | "strapi", 14 | "provider", 15 | "rest-cache", 16 | "couchbase" 17 | ], 18 | "author": { 19 | "name": "Strapi Community", 20 | "url": "https://github.com/strapi-community/" 21 | }, 22 | "maintainers": [ 23 | { 24 | "name": "Davide Pellegatta", 25 | "email": "davide.pellegatta@gmail.com", 26 | "url": "https://github.com/davidepellegatta/" 27 | }, 28 | { 29 | "name": "Patrick R", 30 | "url": "https://github.com/patrixr/" 31 | } 32 | ], 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/strapi-community/strapi-plugin-rest-cache.git", 36 | "directory": "packages/strapi-provider-rest-cache-couchbase" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/strapi-community/strapi-plugin-rest-cache/issues" 40 | }, 41 | "homepage": "https://github.com/strapi-community/strapi-plugin-rest-cache#readme", 42 | "main": "lib/index.js", 43 | "files": [ 44 | "lib/**/*.js" 45 | ], 46 | "scripts": { 47 | "lint": "run-s eslint:fix prettier:fix", 48 | "eslint": "eslint ./lib", 49 | "eslint:fix": "eslint ./lib --fix", 50 | "prettier": "prettier --check ./lib", 51 | "prettier:fix": "prettier --write ./lib", 52 | "test": "run-s test:*", 53 | "test:lint": "run-s eslint prettier" 54 | }, 55 | "dependencies": { 56 | "cache-manager": "^3.6.0", 57 | "cache-manager-couchbase": "^0.1.5" 58 | }, 59 | "devDependencies": { 60 | "@strapi/strapi": "^4.11.7", 61 | "strapi-plugin-rest-cache": "^4.2.9" 62 | }, 63 | "peerDependencies": { 64 | "@strapi/strapi": "^4.0.0", 65 | "strapi-plugin-rest-cache": "^4.0.0" 66 | }, 67 | "engines": { 68 | "node": ">=14.0.0", 69 | "npm": ">=6.0.0" 70 | }, 71 | "gitHead": "80f7c4b1c3915484e282597292cd1f5c749cbe8f" 72 | } 73 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-memory/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [4.2.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.3...v4.2.4) (2022-03-19) 7 | 8 | **Note:** Version bump only for package strapi-provider-rest-cache-memory 9 | 10 | 11 | 12 | 13 | 14 | ## [4.2.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.2...v4.2.3) (2022-03-18) 15 | 16 | **Note:** Version bump only for package strapi-provider-rest-cache-memory 17 | 18 | 19 | 20 | 21 | 22 | ## [4.2.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.1...v4.2.2) (2022-03-15) 23 | 24 | **Note:** Version bump only for package strapi-provider-rest-cache-memory 25 | 26 | 27 | 28 | 29 | 30 | ## [4.2.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.0...v4.2.1) (2022-03-11) 31 | 32 | 33 | ### Bug Fixes 34 | 35 | * empty keys returned by providers ([fb5c79c](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/fb5c79c490309e8bd4458726fe8aedacbfae503b)) 36 | 37 | 38 | 39 | 40 | 41 | # [4.2.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.1.0...v4.2.0) (2022-03-09) 42 | 43 | **Note:** Version bump only for package strapi-provider-rest-cache-memory 44 | 45 | 46 | 47 | 48 | 49 | # [4.1.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.6...v4.1.0) (2022-03-05) 50 | 51 | **Note:** Version bump only for package strapi-provider-rest-cache-memory 52 | 53 | 54 | 55 | 56 | 57 | ## [4.0.6](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.5...v4.0.6) (2022-03-02) 58 | 59 | **Note:** Version bump only for package strapi-provider-rest-cache-memory 60 | 61 | ## [4.0.5](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.4...v4.0.5) (2022-03-02) 62 | 63 | **Note:** Version bump only for package strapi-provider-rest-cache-memory 64 | 65 | ## [4.0.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.3...v4.0.4) (2022-02-26) 66 | 67 | **Note:** Version bump only for package strapi-provider-rest-cache-memory 68 | 69 | ## [4.0.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.2...v4.0.3) (2022-02-26) 70 | 71 | **Note:** Version bump only for package strapi-provider-rest-cache-memory 72 | 73 | ## [4.0.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.1...v4.0.2) (2022-02-24) 74 | 75 | ### Bug Fixes 76 | 77 | - use short plugin name ([8daf416](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/8daf41643c2479c0df19a2fe137cae7ec395ec78)) 78 | 79 | ## [4.0.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0...v4.0.1) (2022-02-24) 80 | 81 | ### Bug Fixes 82 | 83 | - empty npm packages ([1fde26a](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/1fde26a1da956c854661b036bc48483c49f9f75e)) 84 | 85 | # [4.0.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0-alpha.1...v4.0.0) (2022-01-31) 86 | 87 | ### Bug Fixes 88 | 89 | - peerDependencies fixed version ([4b5e317](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/4b5e317ae9319a91f90d7d7fb62fbcb7401d67af)) 90 | 91 | ### Features 92 | 93 | - **core:** add keysPrefix strategy option ([8ed2149](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/8ed21495fadd2d2d709c741c3bccdc48d17376bd)) 94 | 95 | # [4.0.0-alpha.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) (2022-01-31) 96 | 97 | ### Bug Fixes 98 | 99 | - peerDependencies fixed version ([f43ef96](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/f43ef96b87c274618ecd041b733ecfa22c824c74)) 100 | 101 | # [4.0.0-alpha.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v1.0.1-alpha.0...v4.0.0-alpha.0) (2022-01-31) 102 | 103 | **Note:** Version bump only for package strapi-provider-rest-cache-memory 104 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-memory/lib/MemoryCacheProvider.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /* eslint-disable class-methods-use-this */ 4 | const cacheManager = require("cache-manager"); 5 | const { CacheProvider } = require("strapi-plugin-rest-cache/server/types"); 6 | 7 | class MemoryCacheProvider extends CacheProvider { 8 | constructor(options) { 9 | super(); 10 | this.cache = cacheManager.caching({ store: "memory", ...options }); 11 | } 12 | 13 | /** 14 | * @param {string} key 15 | */ 16 | async get(key) { 17 | return this.cache.get(key); 18 | } 19 | 20 | /** 21 | * @param {string} key 22 | * @param {any} val 23 | * @param {number=} maxAge 24 | */ 25 | async set(key, val, maxAge = 3600) { 26 | // TODO: When we upgrade the cache manager >=5.x.x, need to multiply this not divide 27 | const options = { 28 | ttl: maxAge / 1000, 29 | }; 30 | return this.cache.set(key, val, options); 31 | } 32 | 33 | /** 34 | * @param {string|string[]} key 35 | */ 36 | async del(key) { 37 | return this.cache.del(key); 38 | } 39 | 40 | async keys() { 41 | return this.cache.keys(); 42 | } 43 | 44 | get ready() { 45 | return true; 46 | } 47 | } 48 | 49 | module.exports = { 50 | MemoryCacheProvider, 51 | }; 52 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-memory/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @typedef {import('@strapi/strapi').Strapi} Strapi 5 | */ 6 | 7 | const { MemoryCacheProvider } = require("./MemoryCacheProvider"); 8 | 9 | module.exports = { 10 | provider: "memory", 11 | name: "Memory", 12 | 13 | async init(options /* , { strapi } */) { 14 | return new MemoryCacheProvider(options); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-memory/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strapi-provider-rest-cache-memory", 3 | "version": "4.2.9", 4 | "description": "Speed-up HTTP requests with LRU cache", 5 | "license": "MIT", 6 | "strapi": { 7 | "displayName": "REST Cache (memory)", 8 | "name": "strapi-provider-rest-cache-memory", 9 | "description": "In-memory provider for REST Cache plugin", 10 | "kind": "plugin" 11 | }, 12 | "keywords": [ 13 | "strapi", 14 | "provider", 15 | "rest-cache", 16 | "memory" 17 | ], 18 | "author": { 19 | "name": "Strapi Community", 20 | "url": "https://github.com/strapi-community/" 21 | }, 22 | "maintainers": [ 23 | { 24 | "name": "Sacha Stafyniak", 25 | "email": "sacha@digisquad.io", 26 | "url": "https://digisquad.io" 27 | }, 28 | { 29 | "name": "Patrick R", 30 | "url": "https://github.com/patrixr/" 31 | } 32 | ], 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/strapi-community/strapi-plugin-rest-cache.git", 36 | "directory": "packages/strapi-provider-rest-cache-memory" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/strapi-community/strapi-plugin-rest-cache/issues" 40 | }, 41 | "homepage": "https://strapi-community.github.io/strapi-plugin-rest-cache/", 42 | "main": "lib/index.js", 43 | "files": [ 44 | "lib/**/*.js" 45 | ], 46 | "scripts": { 47 | "lint": "run-s eslint:fix prettier:fix", 48 | "eslint": "eslint ./lib", 49 | "eslint:fix": "eslint ./lib --fix", 50 | "prettier": "prettier --check ./lib", 51 | "prettier:fix": "prettier --write ./lib", 52 | "test": "run-s test:*", 53 | "test:lint": "run-s eslint prettier" 54 | }, 55 | "dependencies": { 56 | "cache-manager": "^3.6.0" 57 | }, 58 | "devDependencies": { 59 | "@strapi/strapi": "^4.11.7", 60 | "strapi-plugin-rest-cache": "^4.2.9" 61 | }, 62 | "peerDependencies": { 63 | "@strapi/strapi": "^4.0.0", 64 | "strapi-plugin-rest-cache": "^4.0.0" 65 | }, 66 | "engines": { 67 | "node": ">=14.0.0", 68 | "npm": ">=6.0.0" 69 | }, 70 | "gitHead": "80f7c4b1c3915484e282597292cd1f5c749cbe8f" 71 | } 72 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-redis/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [4.2.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.3...v4.2.4) (2022-03-19) 7 | 8 | **Note:** Version bump only for package strapi-provider-rest-cache-redis 9 | 10 | 11 | 12 | 13 | 14 | ## [4.2.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.2...v4.2.3) (2022-03-18) 15 | 16 | **Note:** Version bump only for package strapi-provider-rest-cache-redis 17 | 18 | 19 | 20 | 21 | 22 | ## [4.2.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.1...v4.2.2) (2022-03-15) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * ensure keyprefix is not undefined ([9134f52](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/9134f52a0ea8a8399db4af59a5dc689742104739)) 28 | 29 | 30 | 31 | 32 | 33 | ## [4.2.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.0...v4.2.1) (2022-03-11) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * empty keys returned by providers ([fb5c79c](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/fb5c79c490309e8bd4458726fe8aedacbfae503b)) 39 | 40 | 41 | 42 | 43 | 44 | # [4.2.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.1.0...v4.2.0) (2022-03-09) 45 | 46 | **Note:** Version bump only for package strapi-provider-rest-cache-redis 47 | 48 | 49 | 50 | 51 | 52 | # [4.1.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.6...v4.1.0) (2022-03-05) 53 | 54 | **Note:** Version bump only for package strapi-provider-rest-cache-redis 55 | 56 | 57 | 58 | 59 | 60 | ## [4.0.6](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.5...v4.0.6) (2022-03-02) 61 | 62 | **Note:** Version bump only for package strapi-provider-rest-cache-redis 63 | 64 | ## [4.0.5](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.4...v4.0.5) (2022-03-02) 65 | 66 | **Note:** Version bump only for package strapi-provider-rest-cache-redis 67 | 68 | ## [4.0.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.3...v4.0.4) (2022-02-26) 69 | 70 | **Note:** Version bump only for package strapi-provider-rest-cache-redis 71 | 72 | ## [4.0.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.2...v4.0.3) (2022-02-26) 73 | 74 | **Note:** Version bump only for package strapi-provider-rest-cache-redis 75 | 76 | ## [4.0.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.1...v4.0.2) (2022-02-24) 77 | 78 | ### Bug Fixes 79 | 80 | - use short plugin name ([8daf416](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/8daf41643c2479c0df19a2fe137cae7ec395ec78)) 81 | 82 | ## [4.0.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0...v4.0.1) (2022-02-24) 83 | 84 | ### Bug Fixes 85 | 86 | - empty npm packages ([1fde26a](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/1fde26a1da956c854661b036bc48483c49f9f75e)) 87 | 88 | # [4.0.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0-alpha.1...v4.0.0) (2022-01-31) 89 | 90 | ### Bug Fixes 91 | 92 | - peerDependencies fixed version ([4b5e317](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/4b5e317ae9319a91f90d7d7fb62fbcb7401d67af)) 93 | 94 | ### Features 95 | 96 | - **core:** add keysPrefix strategy option ([8ed2149](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/8ed21495fadd2d2d709c741c3bccdc48d17376bd)) 97 | 98 | # [4.0.0-alpha.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) (2022-01-31) 99 | 100 | ### Bug Fixes 101 | 102 | - peerDependencies fixed version ([f43ef96](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/f43ef96b87c274618ecd041b733ecfa22c824c74)) 103 | 104 | # [4.0.0-alpha.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v1.0.1-alpha.0...v4.0.0-alpha.0) (2022-01-31) 105 | 106 | ### Bug Fixes 107 | 108 | - **redis:** remove ready listener on error ([9a90fa2](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/9a90fa2938650a826dcf293ddda292d8d8f3a175)) 109 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-redis/lib/RedisCacheProvider.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const cacheManager = require("cache-manager"); 4 | const redisStore = require("cache-manager-ioredis"); 5 | const { CacheProvider } = require("strapi-plugin-rest-cache/server/types"); 6 | 7 | class RedisCacheProvider extends CacheProvider { 8 | constructor(client, options) { 9 | super(); 10 | this.client = client; 11 | this.cache = cacheManager.caching({ 12 | store: redisStore, 13 | redisInstance: client, 14 | ...options, 15 | }); 16 | } 17 | 18 | /** 19 | * @param {string} key 20 | */ 21 | async get(key) { 22 | return this.cache.get(key); 23 | } 24 | 25 | /** 26 | * @param {string} key 27 | * @param {any} val 28 | * @param {number=} maxAge 29 | */ 30 | async set(key, val, maxAge = 3600) { 31 | // TODO: When we upgrade the cache manager >=5.x.x, need to multiply this not divide 32 | const options = { 33 | ttl: maxAge / 1000, 34 | }; 35 | return this.cache.set(key, val, options); 36 | } 37 | 38 | /** 39 | * @param {string|string[]} key 40 | */ 41 | async del(key) { 42 | return this.cache.del(key); 43 | } 44 | 45 | async keys(prefix = "") { 46 | return this.cache.keys(`${prefix}*`); 47 | } 48 | 49 | get ready() { 50 | const client = this.cache.store.getClient(); 51 | return client.status === "ready"; 52 | } 53 | } 54 | 55 | module.exports = { 56 | RedisCacheProvider, 57 | }; 58 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-redis/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @typedef {import('@strapi/strapi').Strapi} Strapi 5 | */ 6 | const { RedisCacheProvider } = require("./RedisCacheProvider"); 7 | 8 | function waitForRedis(client) { 9 | return new Promise((resolve, reject) => { 10 | const onReady = () => { 11 | strapi.log.info('REST Cache provider "redis": connection established'); 12 | 13 | // eslint-disable-next-line no-use-before-define 14 | client.off("error", onError); 15 | resolve(); 16 | }; 17 | const onError = (error) => { 18 | client.off("ready", onReady); 19 | reject( 20 | new Error( 21 | `Could not initialize REST Cache provider "redis": ${error?.message}` 22 | ) 23 | ); 24 | }; 25 | 26 | if (client.status === "ready") { 27 | return onReady(); 28 | } 29 | 30 | client.once("ready", onReady); 31 | client.once("error", onError); 32 | }); 33 | } 34 | 35 | module.exports = { 36 | provider: "redis", 37 | name: "Redis", 38 | 39 | async init(options, { strapi }) { 40 | if (!strapi.redis) { 41 | throw new Error( 42 | `Could not initialize REST Cache provider "redis". The package "strapi-plugin-redis" is required.` 43 | ); 44 | } 45 | 46 | const connectionName = options.connection || "default"; 47 | const { client } = strapi.redis.connections[connectionName] ?? {}; 48 | 49 | if (!client) { 50 | throw new Error( 51 | `Could not initialize REST Cache provider "redis". No connection found with name "${connectionName}".` 52 | ); 53 | } 54 | 55 | return waitForRedis(client).then( 56 | () => new RedisCacheProvider(client, options) 57 | ); 58 | }, 59 | }; 60 | -------------------------------------------------------------------------------- /packages/strapi-provider-rest-cache-redis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strapi-provider-rest-cache-redis", 3 | "version": "4.2.9", 4 | "description": "Speed-up HTTP requests with LRU cache", 5 | "license": "MIT", 6 | "strapi": { 7 | "displayName": "REST Cache (redis)", 8 | "name": "strapi-provider-rest-cache-redis", 9 | "description": "Redis provider for REST Cache plugin", 10 | "kind": "plugin" 11 | }, 12 | "keywords": [ 13 | "strapi", 14 | "provider", 15 | "rest-cache", 16 | "redis" 17 | ], 18 | "author": { 19 | "name": "Strapi Community", 20 | "url": "https://github.com/strapi-community/" 21 | }, 22 | "maintainers": [ 23 | { 24 | "name": "Sacha Stafyniak", 25 | "email": "sacha@digisquad.io", 26 | "url": "https://digisquad.io" 27 | }, 28 | { 29 | "name": "Patrick R", 30 | "url": "https://github.com/patrixr/" 31 | } 32 | ], 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/strapi-community/strapi-plugin-rest-cache.git", 36 | "directory": "packages/strapi-provider-rest-cache-redis" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/strapi-community/strapi-plugin-rest-cache/issues" 40 | }, 41 | "homepage": "https://strapi-community.github.io/strapi-plugin-rest-cache/", 42 | "main": "lib/index.js", 43 | "files": [ 44 | "lib/**/*.js" 45 | ], 46 | "scripts": { 47 | "lint": "run-s eslint:fix prettier:fix", 48 | "eslint": "eslint ./lib", 49 | "eslint:fix": "eslint ./lib --fix", 50 | "prettier": "prettier --check ./lib", 51 | "prettier:fix": "prettier --write ./lib", 52 | "test": "run-s test:*", 53 | "test:lint": "run-s eslint prettier" 54 | }, 55 | "dependencies": { 56 | "cache-manager": "^3.6.0", 57 | "cache-manager-ioredis": "^2.1.0" 58 | }, 59 | "devDependencies": { 60 | "@strapi/strapi": "^4.11.7", 61 | "strapi-plugin-redis": "^1.0.1", 62 | "strapi-plugin-rest-cache": "^4.2.9" 63 | }, 64 | "peerDependencies": { 65 | "@strapi/strapi": "^4.0.0", 66 | "strapi-plugin-rest-cache": "^4.0.0" 67 | }, 68 | "engines": { 69 | "node": ">=14.0.0", 70 | "npm": ">=6.0.0" 71 | }, 72 | "gitHead": "80f7c4b1c3915484e282597292cd1f5c749cbe8f" 73 | } 74 | -------------------------------------------------------------------------------- /playgrounds/couchbase/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{package.json,*.yml}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /playgrounds/couchbase/.env.example: -------------------------------------------------------------------------------- 1 | HOST=0.0.0.0 2 | PORT=1337 3 | -------------------------------------------------------------------------------- /playgrounds/couchbase/.eslintignore: -------------------------------------------------------------------------------- 1 | .cache 2 | build 3 | **/node_modules/** 4 | -------------------------------------------------------------------------------- /playgrounds/couchbase/.gitignore: -------------------------------------------------------------------------------- 1 | ############################ 2 | # OS X 3 | ############################ 4 | 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | Icon 9 | .Spotlight-V100 10 | .Trashes 11 | ._* 12 | 13 | 14 | ############################ 15 | # Linux 16 | ############################ 17 | 18 | *~ 19 | 20 | 21 | ############################ 22 | # Windows 23 | ############################ 24 | 25 | Thumbs.db 26 | ehthumbs.db 27 | Desktop.ini 28 | $RECYCLE.BIN/ 29 | *.cab 30 | *.msi 31 | *.msm 32 | *.msp 33 | 34 | 35 | ############################ 36 | # Packages 37 | ############################ 38 | 39 | *.7z 40 | *.csv 41 | *.dat 42 | *.dmg 43 | *.gz 44 | *.iso 45 | *.jar 46 | *.rar 47 | *.tar 48 | *.zip 49 | *.com 50 | *.class 51 | *.dll 52 | *.exe 53 | *.o 54 | *.seed 55 | *.so 56 | *.swo 57 | *.swp 58 | *.swn 59 | *.swm 60 | *.out 61 | *.pid 62 | 63 | 64 | ############################ 65 | # Logs and databases 66 | ############################ 67 | 68 | .tmp 69 | *.log 70 | *.sql 71 | *.sqlite 72 | *.sqlite3 73 | 74 | 75 | ############################ 76 | # Misc. 77 | ############################ 78 | 79 | *# 80 | ssl 81 | .idea 82 | nbproject 83 | public/uploads/* 84 | !public/uploads/.gitkeep 85 | 86 | ############################ 87 | # Node.js 88 | ############################ 89 | 90 | lib-cov 91 | lcov.info 92 | pids 93 | logs 94 | results 95 | node_modules 96 | .node_history 97 | 98 | ############################ 99 | # Tests 100 | ############################ 101 | 102 | testApp 103 | coverage 104 | 105 | ############################ 106 | # Strapi 107 | ############################ 108 | 109 | .env 110 | license.txt 111 | exports 112 | *.cache 113 | build 114 | .strapi-updater.json 115 | 116 | ############################ 117 | # Shared files copied from the project root 118 | ############################ 119 | config 120 | !config/plugins.js 121 | data 122 | src 123 | !src/.gitkeep 124 | tests 125 | !tests/.gitkeep 126 | start-profiler.js -------------------------------------------------------------------------------- /playgrounds/couchbase/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [4.2.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.3...v4.2.4) (2022-03-19) 7 | 8 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-couchbase 9 | 10 | 11 | 12 | 13 | 14 | ## [4.2.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.2...v4.2.3) (2022-03-18) 15 | 16 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-couchbase 17 | 18 | 19 | 20 | 21 | 22 | ## [4.2.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.1...v4.2.2) (2022-03-15) 23 | 24 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-couchbase 25 | 26 | 27 | 28 | 29 | 30 | ## [4.2.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.0...v4.2.1) (2022-03-11) 31 | 32 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-couchbase 33 | 34 | 35 | 36 | 37 | 38 | # [4.2.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.1.0...v4.2.0) (2022-03-09) 39 | 40 | 41 | ### Features 42 | 43 | * expose new clearByUid and clearByRegexp functions in cacheStore service ([c7d67fd](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/c7d67fd532ccca66df90b3621061ba2d65b70fe1)) 44 | 45 | 46 | 47 | 48 | 49 | # [4.1.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.6...v4.1.0) (2022-03-05) 50 | 51 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-couchbase 52 | 53 | 54 | 55 | 56 | 57 | ## [4.0.6](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.5...v4.0.6) (2022-03-02) 58 | 59 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-couchbase 60 | 61 | 62 | 63 | 64 | 65 | ## [4.0.5](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.4...v4.0.5) (2022-03-02) 66 | 67 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-couchbase 68 | 69 | 70 | 71 | 72 | 73 | ## [4.0.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.3...v4.0.4) (2022-02-26) 74 | 75 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-couchbase 76 | 77 | 78 | 79 | 80 | 81 | ## [4.0.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.2...v4.0.3) (2022-02-26) 82 | 83 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-couchbase 84 | 85 | 86 | 87 | 88 | 89 | ## [4.0.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.1...v4.0.2) (2022-02-24) 90 | 91 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-couchbase 92 | 93 | 94 | 95 | 96 | 97 | ## [4.0.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0...v4.0.1) (2022-02-24) 98 | 99 | 100 | ### Bug Fixes 101 | 102 | * empty npm packages ([1fde26a](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/1fde26a1da956c854661b036bc48483c49f9f75e)) 103 | 104 | 105 | 106 | 107 | 108 | # [4.0.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0-alpha.1...v4.0.0) (2022-01-31) 109 | 110 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-memory 111 | 112 | 113 | 114 | 115 | 116 | # [4.0.0-alpha.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) (2022-01-31) 117 | 118 | 119 | ### Bug Fixes 120 | 121 | * peerDependencies fixed version ([f43ef96](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/f43ef96b87c274618ecd041b733ecfa22c824c74)) 122 | 123 | 124 | 125 | 126 | 127 | # [4.0.0-alpha.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v1.0.1-alpha.0...v4.0.0-alpha.0) (2022-01-31) 128 | 129 | 130 | * feat(core)!: add keys alterations options ([a4214f2](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/a4214f2fb90259400c1c5a9701b83221ac2fa1bb)) 131 | 132 | 133 | ### BREAKING CHANGES 134 | 135 | * move headers to keys.useHeaders 136 | add keys.useQueryParams option 137 | -------------------------------------------------------------------------------- /playgrounds/couchbase/README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Getting started with Strapi 2 | 3 | Strapi comes with a full featured [Command Line Interface](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html) (CLI) which lets you scaffold and manage your project in seconds. 4 | 5 | ### `develop` 6 | 7 | Start your Strapi application with autoReload enabled. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-develop) 8 | 9 | ``` 10 | npm run develop 11 | # or 12 | yarn develop 13 | ``` 14 | 15 | ### `start` 16 | 17 | Start your Strapi application with autoReload disabled. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-start) 18 | 19 | ``` 20 | npm run start 21 | # or 22 | yarn start 23 | ``` 24 | 25 | ### `build` 26 | 27 | Build your admin panel. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-build) 28 | 29 | ``` 30 | npm run build 31 | # or 32 | yarn build 33 | ``` 34 | 35 | ## ⚙️ Deployment 36 | 37 | Strapi gives you many possible deployment options for your project. Find the one that suits you on the [deployment section of the documentation](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/deployment.html). 38 | 39 | ## 📚 Learn more 40 | 41 | - [Resource center](https://strapi.io/resource-center) - Strapi resource center. 42 | - [Strapi documentation](https://docs.strapi.io) - Official Strapi documentation. 43 | - [Strapi tutorials](https://strapi.io/tutorials) - List of tutorials made by the core team and the community. 44 | - [Strapi blog](https://docs.strapi.io) - Official Strapi blog containing articles made by the Strapi team and the community. 45 | - [Changelog](https://strapi.io/changelog) - Find out about the Strapi product updates, new features and general improvements. 46 | 47 | Feel free to check out the [Strapi GitHub repository](https://github.com/strapi/strapi). Your feedback and contributions are welcome! 48 | 49 | ## ✨ Community 50 | 51 | - [Discord](https://discord.strapi.io) - Come chat with the Strapi community including the core team. 52 | - [Forum](https://forum.strapi.io/) - Place to discuss, ask questions and find answers, show your Strapi project and get feedback or just talk with other Community members. 53 | - [Awesome Strapi](https://github.com/strapi/awesome-strapi) - A curated list of awesome things related to Strapi. 54 | 55 | --- 56 | 57 | 🤫 Psst! [Strapi is hiring](https://strapi.io/careers). 58 | -------------------------------------------------------------------------------- /playgrounds/couchbase/config/plugins.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = ({ env }) => ({ 4 | "rest-cache": { 5 | enabled: env.bool("ENABLE_CACHE", true), 6 | config: { 7 | provider: { 8 | name: "couchbase", 9 | max: 32767, 10 | options: { 11 | connectionString: "couchbase://127.0.0.1:8091", 12 | connectionOptions: { 13 | username: "Administrator", 14 | password: "Administrator", 15 | }, 16 | bucket: "testbucket", 17 | ttl: 2, 18 | }, 19 | }, 20 | 21 | // loads shared config (from /shared folder) 22 | strategy: require("./cache-strategy")({ env }), 23 | }, 24 | }, 25 | "users-permissions": { 26 | config: { 27 | jwtSecret: env("JWT_SECRET", "b46375d2efd1c69d8efcdcb46d3acd67a"), 28 | }, 29 | }, 30 | }); 31 | -------------------------------------------------------------------------------- /playgrounds/couchbase/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | couchbase: 5 | image: 'couchbase:enterprise-6.6.3' 6 | ports: 7 | - '8091-8094:8091-8094' 8 | - '11210:11210' -------------------------------------------------------------------------------- /playgrounds/couchbase/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/playgrounds/couchbase/favicon.ico -------------------------------------------------------------------------------- /playgrounds/couchbase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@strapi-plugin-rest-cache/playground-couchbase", 3 | "private": true, 4 | "version": "4.2.9", 5 | "description": "A Strapi application", 6 | "scripts": { 7 | "develop": "strapi develop", 8 | "start": "strapi start", 9 | "build": "strapi build", 10 | "strapi": "strapi", 11 | "test": "run-s test:*", 12 | "test:e2e": "jest --detectOpenHandles --forceExit" 13 | }, 14 | "dependencies": { 15 | "@strapi/plugin-i18n": "4.11.7", 16 | "@strapi/plugin-users-permissions": "4.11.7", 17 | "@strapi/strapi": "4.11.7", 18 | "better-sqlite3": "8.0.1", 19 | "strapi-plugin-rest-cache": "^4.2.9", 20 | "strapi-provider-rest-cache-couchbase": "^4.2.9" 21 | }, 22 | "author": { 23 | "name": "A Strapi developer" 24 | }, 25 | "strapi": { 26 | "uuid": "89bd2494-df0e-4b1d-9e55-c46ec3e9952f" 27 | }, 28 | "engines": { 29 | "node": ">=12.x.x", 30 | "npm": ">=6.0.0" 31 | }, 32 | "license": "MIT" 33 | } 34 | -------------------------------------------------------------------------------- /playgrounds/couchbase/public/robots.txt: -------------------------------------------------------------------------------- 1 | # To prevent search engines from seeing the site altogether, uncomment the next two lines: 2 | # User-Agent: * 3 | # Disallow: / 4 | -------------------------------------------------------------------------------- /playgrounds/couchbase/public/uploads/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/playgrounds/couchbase/public/uploads/.gitkeep -------------------------------------------------------------------------------- /playgrounds/couchbase/src/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/playgrounds/couchbase/src/.gitkeep -------------------------------------------------------------------------------- /playgrounds/couchbase/tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/playgrounds/couchbase/tests/.gitkeep -------------------------------------------------------------------------------- /playgrounds/memory/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{package.json,*.yml}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /playgrounds/memory/.env.example: -------------------------------------------------------------------------------- 1 | HOST=0.0.0.0 2 | PORT=1337 3 | -------------------------------------------------------------------------------- /playgrounds/memory/.eslintignore: -------------------------------------------------------------------------------- 1 | .cache 2 | build 3 | **/node_modules/** 4 | -------------------------------------------------------------------------------- /playgrounds/memory/.gitignore: -------------------------------------------------------------------------------- 1 | ############################ 2 | # OS X 3 | ############################ 4 | 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | Icon 9 | .Spotlight-V100 10 | .Trashes 11 | ._* 12 | 13 | 14 | ############################ 15 | # Linux 16 | ############################ 17 | 18 | *~ 19 | 20 | 21 | ############################ 22 | # Windows 23 | ############################ 24 | 25 | Thumbs.db 26 | ehthumbs.db 27 | Desktop.ini 28 | $RECYCLE.BIN/ 29 | *.cab 30 | *.msi 31 | *.msm 32 | *.msp 33 | 34 | 35 | ############################ 36 | # Packages 37 | ############################ 38 | 39 | *.7z 40 | *.csv 41 | *.dat 42 | *.dmg 43 | *.gz 44 | *.iso 45 | *.jar 46 | *.rar 47 | *.tar 48 | *.zip 49 | *.com 50 | *.class 51 | *.dll 52 | *.exe 53 | *.o 54 | *.seed 55 | *.so 56 | *.swo 57 | *.swp 58 | *.swn 59 | *.swm 60 | *.out 61 | *.pid 62 | 63 | 64 | ############################ 65 | # Logs and databases 66 | ############################ 67 | 68 | .tmp 69 | *.log 70 | *.sql 71 | *.sqlite 72 | *.sqlite3 73 | 74 | 75 | ############################ 76 | # Misc. 77 | ############################ 78 | 79 | *# 80 | ssl 81 | .idea 82 | nbproject 83 | public/uploads/* 84 | !public/uploads/.gitkeep 85 | 86 | ############################ 87 | # Node.js 88 | ############################ 89 | 90 | lib-cov 91 | lcov.info 92 | pids 93 | logs 94 | results 95 | node_modules 96 | .node_history 97 | 98 | ############################ 99 | # Tests 100 | ############################ 101 | 102 | testApp 103 | coverage 104 | 105 | ############################ 106 | # Strapi 107 | ############################ 108 | 109 | .env 110 | license.txt 111 | exports 112 | *.cache 113 | build 114 | .strapi-updater.json 115 | 116 | ############################ 117 | # Shared files copied from the project root 118 | ############################ 119 | config 120 | !config/plugins.js 121 | data 122 | src 123 | !src/.gitkeep 124 | tests 125 | !tests/.gitkeep 126 | start-profiler.js -------------------------------------------------------------------------------- /playgrounds/memory/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [4.2.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.3...v4.2.4) (2022-03-19) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **perfs:** split keys computation into smaller functions ([5aba888](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/5aba8888cf132be241ef8a1ced7a83bfb1a626cb)) 12 | 13 | 14 | 15 | 16 | 17 | ## [4.2.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.2...v4.2.3) (2022-03-18) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * **etag:** send a 304 (Not Modified) when valid If-None-Match header contains multiple values ([ccf936a](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/ccf936a02fbbb04a13bcf8143dd6009a3d1148c5)) 23 | 24 | 25 | 26 | 27 | 28 | ## [4.2.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.1...v4.2.2) (2022-03-15) 29 | 30 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-memory 31 | 32 | 33 | 34 | 35 | 36 | ## [4.2.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.0...v4.2.1) (2022-03-11) 37 | 38 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-memory 39 | 40 | 41 | 42 | 43 | 44 | # [4.2.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.1.0...v4.2.0) (2022-03-09) 45 | 46 | 47 | ### Features 48 | 49 | * expose new clearByUid and clearByRegexp functions in cacheStore service ([c7d67fd](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/c7d67fd532ccca66df90b3621061ba2d65b70fe1)) 50 | 51 | 52 | 53 | 54 | 55 | # [4.1.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.6...v4.1.0) (2022-03-05) 56 | 57 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-memory 58 | 59 | 60 | 61 | 62 | 63 | ## [4.0.6](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.5...v4.0.6) (2022-03-02) 64 | 65 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-memory 66 | 67 | 68 | 69 | 70 | 71 | ## [4.0.5](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.4...v4.0.5) (2022-03-02) 72 | 73 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-memory 74 | 75 | 76 | 77 | 78 | 79 | ## [4.0.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.3...v4.0.4) (2022-02-26) 80 | 81 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-memory 82 | 83 | 84 | 85 | 86 | 87 | ## [4.0.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.2...v4.0.3) (2022-02-26) 88 | 89 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-memory 90 | 91 | 92 | 93 | 94 | 95 | ## [4.0.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.1...v4.0.2) (2022-02-24) 96 | 97 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-memory 98 | 99 | 100 | 101 | 102 | 103 | ## [4.0.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0...v4.0.1) (2022-02-24) 104 | 105 | 106 | ### Bug Fixes 107 | 108 | * empty npm packages ([1fde26a](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/1fde26a1da956c854661b036bc48483c49f9f75e)) 109 | 110 | 111 | 112 | 113 | 114 | # [4.0.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0-alpha.1...v4.0.0) (2022-01-31) 115 | 116 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-memory 117 | 118 | 119 | 120 | 121 | 122 | # [4.0.0-alpha.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) (2022-01-31) 123 | 124 | 125 | ### Bug Fixes 126 | 127 | * peerDependencies fixed version ([f43ef96](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/f43ef96b87c274618ecd041b733ecfa22c824c74)) 128 | 129 | 130 | 131 | 132 | 133 | # [4.0.0-alpha.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v1.0.1-alpha.0...v4.0.0-alpha.0) (2022-01-31) 134 | 135 | 136 | * feat(core)!: add keys alterations options ([a4214f2](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/a4214f2fb90259400c1c5a9701b83221ac2fa1bb)) 137 | 138 | 139 | ### BREAKING CHANGES 140 | 141 | * move headers to keys.useHeaders 142 | add keys.useQueryParams option 143 | -------------------------------------------------------------------------------- /playgrounds/memory/README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Getting started with Strapi 2 | 3 | Strapi comes with a full featured [Command Line Interface](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html) (CLI) which lets you scaffold and manage your project in seconds. 4 | 5 | ### `develop` 6 | 7 | Start your Strapi application with autoReload enabled. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-develop) 8 | 9 | ``` 10 | npm run develop 11 | # or 12 | yarn develop 13 | ``` 14 | 15 | ### `start` 16 | 17 | Start your Strapi application with autoReload disabled. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-start) 18 | 19 | ``` 20 | npm run start 21 | # or 22 | yarn start 23 | ``` 24 | 25 | ### `build` 26 | 27 | Build your admin panel. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-build) 28 | 29 | ``` 30 | npm run build 31 | # or 32 | yarn build 33 | ``` 34 | 35 | ## ⚙️ Deployment 36 | 37 | Strapi gives you many possible deployment options for your project. Find the one that suits you on the [deployment section of the documentation](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/deployment.html). 38 | 39 | ## 📚 Learn more 40 | 41 | - [Resource center](https://strapi.io/resource-center) - Strapi resource center. 42 | - [Strapi documentation](https://docs.strapi.io) - Official Strapi documentation. 43 | - [Strapi tutorials](https://strapi.io/tutorials) - List of tutorials made by the core team and the community. 44 | - [Strapi blog](https://docs.strapi.io) - Official Strapi blog containing articles made by the Strapi team and the community. 45 | - [Changelog](https://strapi.io/changelog) - Find out about the Strapi product updates, new features and general improvements. 46 | 47 | Feel free to check out the [Strapi GitHub repository](https://github.com/strapi/strapi). Your feedback and contributions are welcome! 48 | 49 | ## ✨ Community 50 | 51 | - [Discord](https://discord.strapi.io) - Come chat with the Strapi community including the core team. 52 | - [Forum](https://forum.strapi.io/) - Place to discuss, ask questions and find answers, show your Strapi project and get feedback or just talk with other Community members. 53 | - [Awesome Strapi](https://github.com/strapi/awesome-strapi) - A curated list of awesome things related to Strapi. 54 | 55 | --- 56 | 57 | 🤫 Psst! [Strapi is hiring](https://strapi.io/careers). 58 | -------------------------------------------------------------------------------- /playgrounds/memory/config/plugins.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = ({ env }) => ({ 4 | "rest-cache": { 5 | enabled: env.bool("ENABLE_CACHE", true), 6 | config: { 7 | provider: { 8 | name: "memory", 9 | max: 32767, 10 | }, 11 | // loads shared config (from /shared folder) 12 | strategy: require("./cache-strategy")({ env }), 13 | }, 14 | }, 15 | "users-permissions": { 16 | config: { 17 | jwtSecret: env("JWT_SECRET", "b46375d2efd1c69d8efcdcb46d3acd67a"), 18 | }, 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /playgrounds/memory/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/playgrounds/memory/favicon.ico -------------------------------------------------------------------------------- /playgrounds/memory/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@strapi-plugin-rest-cache/playground-memory", 3 | "private": true, 4 | "version": "4.2.9", 5 | "description": "A Strapi application", 6 | "scripts": { 7 | "develop": "strapi develop", 8 | "start": "strapi start", 9 | "build": "strapi build", 10 | "strapi": "strapi", 11 | "test": "run-s test:*", 12 | "test:e2e": "jest --detectOpenHandles --forceExit" 13 | }, 14 | "dependencies": { 15 | "@strapi/plugin-i18n": "4.11.7", 16 | "@strapi/plugin-users-permissions": "4.11.7", 17 | "@strapi/strapi": "4.11.7", 18 | "better-sqlite3": "8.0.1", 19 | "strapi-plugin-rest-cache": "^4.2.9" 20 | }, 21 | "devDependencies": { 22 | "jest": "^27.5.1", 23 | "npm-run-all": "^4.1.5", 24 | "supertest": "^6.2.2" 25 | }, 26 | "author": { 27 | "name": "A Strapi developer" 28 | }, 29 | "strapi": { 30 | "uuid": "89bd2494-df0e-4b1d-9e55-c46ec3e9952f" 31 | }, 32 | "engines": { 33 | "node": ">=12.x.x", 34 | "npm": ">=6.0.0" 35 | }, 36 | "license": "MIT" 37 | } 38 | -------------------------------------------------------------------------------- /playgrounds/memory/public/robots.txt: -------------------------------------------------------------------------------- 1 | # To prevent search engines from seeing the site altogether, uncomment the next two lines: 2 | # User-Agent: * 3 | # Disallow: / 4 | -------------------------------------------------------------------------------- /playgrounds/memory/public/uploads/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/playgrounds/memory/public/uploads/.gitkeep -------------------------------------------------------------------------------- /playgrounds/redis/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{package.json,*.yml}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /playgrounds/redis/.env.example: -------------------------------------------------------------------------------- 1 | HOST=0.0.0.0 2 | PORT=1337 3 | -------------------------------------------------------------------------------- /playgrounds/redis/.eslintignore: -------------------------------------------------------------------------------- 1 | .cache 2 | build 3 | **/node_modules/** 4 | -------------------------------------------------------------------------------- /playgrounds/redis/.gitignore: -------------------------------------------------------------------------------- 1 | ############################ 2 | # OS X 3 | ############################ 4 | 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | Icon 9 | .Spotlight-V100 10 | .Trashes 11 | ._* 12 | 13 | 14 | ############################ 15 | # Linux 16 | ############################ 17 | 18 | *~ 19 | 20 | 21 | ############################ 22 | # Windows 23 | ############################ 24 | 25 | Thumbs.db 26 | ehthumbs.db 27 | Desktop.ini 28 | $RECYCLE.BIN/ 29 | *.cab 30 | *.msi 31 | *.msm 32 | *.msp 33 | 34 | 35 | ############################ 36 | # Packages 37 | ############################ 38 | 39 | *.7z 40 | *.csv 41 | *.dat 42 | *.dmg 43 | *.gz 44 | *.iso 45 | *.jar 46 | *.rar 47 | *.tar 48 | *.zip 49 | *.com 50 | *.class 51 | *.dll 52 | *.exe 53 | *.o 54 | *.seed 55 | *.so 56 | *.swo 57 | *.swp 58 | *.swn 59 | *.swm 60 | *.out 61 | *.pid 62 | 63 | 64 | ############################ 65 | # Logs and databases 66 | ############################ 67 | 68 | .tmp 69 | *.log 70 | *.sql 71 | *.sqlite 72 | *.sqlite3 73 | 74 | 75 | ############################ 76 | # Misc. 77 | ############################ 78 | 79 | *# 80 | ssl 81 | .idea 82 | nbproject 83 | public/uploads/* 84 | !public/uploads/.gitkeep 85 | 86 | ############################ 87 | # Node.js 88 | ############################ 89 | 90 | lib-cov 91 | lcov.info 92 | pids 93 | logs 94 | results 95 | node_modules 96 | .node_history 97 | 98 | ############################ 99 | # Tests 100 | ############################ 101 | 102 | testApp 103 | coverage 104 | 105 | ############################ 106 | # Strapi 107 | ############################ 108 | 109 | .env 110 | license.txt 111 | exports 112 | *.cache 113 | build 114 | .strapi-updater.json 115 | 116 | ############################ 117 | # Shared files copied from the project root 118 | ############################ 119 | config 120 | !config/plugins.js 121 | data 122 | src 123 | !src/.gitkeep 124 | tests 125 | !tests/.gitkeep 126 | start-profiler.js -------------------------------------------------------------------------------- /playgrounds/redis/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [4.2.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.3...v4.2.4) (2022-03-19) 7 | 8 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 9 | 10 | 11 | 12 | 13 | 14 | ## [4.2.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.2...v4.2.3) (2022-03-18) 15 | 16 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 17 | 18 | 19 | 20 | 21 | 22 | ## [4.2.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.1...v4.2.2) (2022-03-15) 23 | 24 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 25 | 26 | 27 | 28 | 29 | 30 | ## [4.2.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.2.0...v4.2.1) (2022-03-11) 31 | 32 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 33 | 34 | 35 | 36 | 37 | 38 | # [4.2.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.1.0...v4.2.0) (2022-03-09) 39 | 40 | 41 | ### Features 42 | 43 | * expose new clearByUid and clearByRegexp functions in cacheStore service ([c7d67fd](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/c7d67fd532ccca66df90b3621061ba2d65b70fe1)) 44 | 45 | 46 | 47 | 48 | 49 | # [4.1.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.6...v4.1.0) (2022-03-05) 50 | 51 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 52 | 53 | 54 | 55 | 56 | 57 | ## [4.0.6](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.5...v4.0.6) (2022-03-02) 58 | 59 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 60 | 61 | 62 | 63 | 64 | 65 | ## [4.0.5](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.4...v4.0.5) (2022-03-02) 66 | 67 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 68 | 69 | 70 | 71 | 72 | 73 | ## [4.0.4](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.3...v4.0.4) (2022-02-26) 74 | 75 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 76 | 77 | 78 | 79 | 80 | 81 | ## [4.0.3](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.2...v4.0.3) (2022-02-26) 82 | 83 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 84 | 85 | 86 | 87 | 88 | 89 | ## [4.0.2](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.1...v4.0.2) (2022-02-24) 90 | 91 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 92 | 93 | 94 | 95 | 96 | 97 | ## [4.0.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0...v4.0.1) (2022-02-24) 98 | 99 | 100 | ### Bug Fixes 101 | 102 | * empty npm packages ([1fde26a](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/1fde26a1da956c854661b036bc48483c49f9f75e)) 103 | 104 | 105 | 106 | 107 | 108 | # [4.0.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0-alpha.1...v4.0.0) (2022-01-31) 109 | 110 | **Note:** Version bump only for package @strapi-plugin-rest-cache/playground-redis 111 | 112 | 113 | 114 | 115 | 116 | # [4.0.0-alpha.1](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) (2022-01-31) 117 | 118 | 119 | ### Bug Fixes 120 | 121 | * peerDependencies fixed version ([f43ef96](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/f43ef96b87c274618ecd041b733ecfa22c824c74)) 122 | 123 | 124 | 125 | 126 | 127 | # [4.0.0-alpha.0](https://github.com/strapi-community/strapi-plugin-rest-cache/compare/v1.0.1-alpha.0...v4.0.0-alpha.0) (2022-01-31) 128 | 129 | 130 | * feat(core)!: add keys alterations options ([a4214f2](https://github.com/strapi-community/strapi-plugin-rest-cache/commit/a4214f2fb90259400c1c5a9701b83221ac2fa1bb)) 131 | 132 | 133 | ### BREAKING CHANGES 134 | 135 | * move headers to keys.useHeaders 136 | add keys.useQueryParams option 137 | -------------------------------------------------------------------------------- /playgrounds/redis/README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Getting started with Strapi 2 | 3 | Strapi comes with a full featured [Command Line Interface](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html) (CLI) which lets you scaffold and manage your project in seconds. 4 | 5 | ### `develop` 6 | 7 | Start your Strapi application with autoReload enabled. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-develop) 8 | 9 | ``` 10 | npm run develop 11 | # or 12 | yarn develop 13 | ``` 14 | 15 | ### `start` 16 | 17 | Start your Strapi application with autoReload disabled. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-start) 18 | 19 | ``` 20 | npm run start 21 | # or 22 | yarn start 23 | ``` 24 | 25 | ### `build` 26 | 27 | Build your admin panel. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-build) 28 | 29 | ``` 30 | npm run build 31 | # or 32 | yarn build 33 | ``` 34 | 35 | ## ⚙️ Deployment 36 | 37 | Strapi gives you many possible deployment options for your project. Find the one that suits you on the [deployment section of the documentation](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/deployment.html). 38 | 39 | ## 📚 Learn more 40 | 41 | - [Resource center](https://strapi.io/resource-center) - Strapi resource center. 42 | - [Strapi documentation](https://docs.strapi.io) - Official Strapi documentation. 43 | - [Strapi tutorials](https://strapi.io/tutorials) - List of tutorials made by the core team and the community. 44 | - [Strapi blog](https://docs.strapi.io) - Official Strapi blog containing articles made by the Strapi team and the community. 45 | - [Changelog](https://strapi.io/changelog) - Find out about the Strapi product updates, new features and general improvements. 46 | 47 | Feel free to check out the [Strapi GitHub repository](https://github.com/strapi/strapi). Your feedback and contributions are welcome! 48 | 49 | ## ✨ Community 50 | 51 | - [Discord](https://discord.strapi.io) - Come chat with the Strapi community including the core team. 52 | - [Forum](https://forum.strapi.io/) - Place to discuss, ask questions and find answers, show your Strapi project and get feedback or just talk with other Community members. 53 | - [Awesome Strapi](https://github.com/strapi/awesome-strapi) - A curated list of awesome things related to Strapi. 54 | 55 | --- 56 | 57 | 🤫 Psst! [Strapi is hiring](https://strapi.io/careers). 58 | -------------------------------------------------------------------------------- /playgrounds/redis/config/plugins.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = ({ env }) => ({ 4 | redis: { 5 | config: { 6 | debug: true, 7 | connections: { 8 | default: { 9 | connection: { 10 | host: "127.0.0.1", 11 | port: 6379, 12 | db: 0, 13 | }, 14 | settings: { 15 | debug: false, 16 | cluster: false, 17 | }, 18 | }, 19 | }, 20 | redlock: { 21 | enabled: true, 22 | databases: ["default"], 23 | options: { 24 | driftFactor: 0.01, 25 | retryCount: 10, 26 | retryDelay: 200, 27 | retryJitter: 200, 28 | automaticExtensionThreshold: 500, 29 | }, 30 | }, 31 | }, 32 | }, 33 | "rest-cache": { 34 | enabled: env.bool("ENABLE_CACHE", true), 35 | config: { 36 | provider: { 37 | name: "redis", 38 | options: { 39 | max: 32767, 40 | connection: "default", 41 | }, 42 | }, 43 | // loads shared config (from /shared folder) 44 | strategy: require("./cache-strategy")({ env }), 45 | }, 46 | }, 47 | "users-permissions": { 48 | config: { 49 | jwtSecret: env("JWT_SECRET", "b46375d2efd1c69d8efcdcb46d3acd67a"), 50 | }, 51 | }, 52 | }); 53 | -------------------------------------------------------------------------------- /playgrounds/redis/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | redis: 5 | image: 'bitnami/redis:latest' 6 | environment: 7 | - ALLOW_EMPTY_PASSWORD=yes 8 | ports: 9 | - '6379:6379' -------------------------------------------------------------------------------- /playgrounds/redis/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/playgrounds/redis/favicon.ico -------------------------------------------------------------------------------- /playgrounds/redis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@strapi-plugin-rest-cache/playground-redis", 3 | "private": true, 4 | "version": "4.2.9", 5 | "description": "A Strapi application", 6 | "scripts": { 7 | "develop": "strapi develop", 8 | "start": "strapi start", 9 | "build": "strapi build", 10 | "strapi": "strapi", 11 | "test": "run-s test:*", 12 | "test:e2e": "jest --detectOpenHandles --forceExit" 13 | }, 14 | "dependencies": { 15 | "@strapi/plugin-i18n": "4.11.7", 16 | "@strapi/plugin-users-permissions": "4.11.7", 17 | "@strapi/strapi": "4.11.7", 18 | "better-sqlite3": "8.0.1", 19 | "strapi-plugin-redis": "^1.0.1", 20 | "strapi-plugin-rest-cache": "^4.2.9", 21 | "strapi-provider-rest-cache-redis": "^4.2.9" 22 | }, 23 | "devDependencies": { 24 | "jest": "^27.5.1", 25 | "npm-run-all": "^4.1.5", 26 | "supertest": "^6.2.2" 27 | }, 28 | "author": { 29 | "name": "A Strapi developer" 30 | }, 31 | "strapi": { 32 | "uuid": "89bd2494-df0e-4b1d-9e55-c46ec3e9952f" 33 | }, 34 | "engines": { 35 | "node": ">=12.x.x", 36 | "npm": ">=6.0.0" 37 | }, 38 | "license": "MIT" 39 | } 40 | -------------------------------------------------------------------------------- /playgrounds/redis/public/robots.txt: -------------------------------------------------------------------------------- 1 | # To prevent search engines from seeing the site altogether, uncomment the next two lines: 2 | # User-Agent: * 3 | # Disallow: / 4 | -------------------------------------------------------------------------------- /playgrounds/redis/public/uploads/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/playgrounds/redis/public/uploads/.gitkeep -------------------------------------------------------------------------------- /shared/config/admin.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = ({ env }) => ({ 4 | auth: { 5 | secret: env("ADMIN_JWT_SECRET", "b46375d2efd1c69d8efcdcb46d3acd67"), 6 | }, 7 | apiToken: { 8 | salt: env("API_TOKEN_SALT", "changeme"), 9 | }, 10 | transfer: { 11 | token: { 12 | salt: env("TRANSFER_TOKEN_SALT", "changeme"), 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /shared/config/api.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | rest: { 5 | defaultLimit: 25, 6 | maxLimit: 100, 7 | withCount: true, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /shared/config/cache-strategy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = ({ env }) => ({ 4 | debug: false, 5 | maxAge: env.int("ENABLE_MAX_AGE", 3600000), 6 | enableEtag: env.bool("ENABLE_ETAG", true), 7 | enableXCacheHeaders: env.bool("ENABLE_XCACHE_HEADERS", true), 8 | enableAdminCTBMiddleware: env.bool("ENABLE_ADMIN_CTB_MIDDLEWARE", true), 9 | resetOnStartup: env.bool("RESET_STARTUP", false), 10 | clearRelatedCache: env.bool("CREAR_RELATED_CACHE", true), 11 | keysPrefix: env.bool("KEYS_PREFIX", ''), 12 | keys: env.json("KEYS", { 13 | useHeaders: [], 14 | useQueryParams: true, 15 | }), 16 | hitpass: (ctx) => 17 | Boolean( 18 | ctx.request.headers.authorization || ctx.request.headers.cookie 19 | ), 20 | contentTypes: [ 21 | "api::article.article", 22 | "api::global.global", 23 | "api::homepage.homepage", 24 | { 25 | contentType: "api::category.category", 26 | maxAge: 3600000, 27 | hitpass: false, 28 | keys: { 29 | useQueryParams: false, 30 | useHeaders: ["accept-encoding"], 31 | }, 32 | routes: [ 33 | { 34 | path: "/api/categories/slug/:slug+", 35 | keys: { 36 | useQueryParams: ["populate", "locale"], 37 | useHeaders: [], 38 | }, 39 | maxAge: 18000, 40 | method: "GET", 41 | }, 42 | ], 43 | }, 44 | ], 45 | }); -------------------------------------------------------------------------------- /shared/config/database.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require("path"); 4 | 5 | module.exports = ({ env }) => ({ 6 | connection: { 7 | client: "sqlite", 8 | connection: { 9 | filename: path.join( 10 | __dirname, 11 | "..", 12 | env("DATABASE_FILENAME", ".tmp/data.db") 13 | ), 14 | }, 15 | useNullAsDefault: true, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /shared/config/env/test/database.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { join } = require("path"); 4 | 5 | module.exports = () => ({ 6 | connection: { 7 | client: "sqlite", 8 | connection: { 9 | filename: join(__dirname, "../../../", ".tmp/tests.db"), 10 | }, 11 | useNullAsDefault: true, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /shared/config/logger.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = ({ env }) => ({ 4 | level: env("STRAPI_LOG_LEVEL", "error"), 5 | }); -------------------------------------------------------------------------------- /shared/config/middlewares.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = [ 4 | "strapi::errors", 5 | "strapi::security", 6 | "strapi::cors", 7 | "strapi::poweredBy", 8 | // "strapi::logger", 9 | "strapi::query", 10 | "strapi::session", 11 | "strapi::body", 12 | "strapi::favicon", 13 | "strapi::public", 14 | ]; 15 | -------------------------------------------------------------------------------- /shared/config/server.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = ({ env }) => ({ 4 | host: env("HOST", "0.0.0.0"), 5 | port: env.int("PORT", 1337), 6 | app: { 7 | keys: env.array("APP_KEYS", ["testKey1", "testKey2"]), 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /shared/src/admin/app.example.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | export default { 4 | config: { 5 | locales: [ 6 | // 'ar', 7 | // 'fr', 8 | // 'cs', 9 | // 'de', 10 | // 'dk', 11 | // 'es', 12 | // 'he', 13 | // 'id', 14 | // 'it', 15 | // 'ja', 16 | // 'ko', 17 | // 'ms', 18 | // 'nl', 19 | // 'no', 20 | // 'pl', 21 | // 'pt-BR', 22 | // 'pt', 23 | // 'ru', 24 | // 'sk', 25 | // 'sv', 26 | // 'th', 27 | // 'tr', 28 | // 'uk', 29 | // 'vi', 30 | // 'zh-Hans', 31 | // 'zh', 32 | ], 33 | }, 34 | bootstrap(app) { 35 | console.log(app); 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /shared/src/admin/webpack.config.example.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /* eslint-disable no-unused-vars */ 4 | module.exports = (config, webpack) => 5 | // Note: we provide webpack above so you should not `require` it 6 | // Perform customizations to webpack config 7 | // Important: return the modified config 8 | config; 9 | -------------------------------------------------------------------------------- /shared/src/api/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/shared/src/api/.gitkeep -------------------------------------------------------------------------------- /shared/src/api/article/content-types/article/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "articles", 4 | "info": { 5 | "singularName": "article", 6 | "pluralName": "articles", 7 | "displayName": "Article", 8 | "name": "article" 9 | }, 10 | "options": { 11 | "increments": true, 12 | "timestamps": true, 13 | "draftAndPublish": true 14 | }, 15 | "pluginOptions": {}, 16 | "attributes": { 17 | "title": { 18 | "type": "string", 19 | "required": true 20 | }, 21 | "description": { 22 | "type": "text", 23 | "required": true 24 | }, 25 | "content": { 26 | "type": "richtext", 27 | "required": true 28 | }, 29 | "slug": { 30 | "type": "uid", 31 | "targetField": "title", 32 | "required": true 33 | }, 34 | "category": { 35 | "type": "relation", 36 | "relation": "manyToOne", 37 | "target": "api::category.category", 38 | "inversedBy": "articles" 39 | }, 40 | "image": { 41 | "allowedTypes": [ 42 | "images" 43 | ], 44 | "type": "media", 45 | "multiple": false 46 | }, 47 | "author": { 48 | "type": "relation", 49 | "relation": "manyToOne", 50 | "target": "api::writer.writer", 51 | "inversedBy": "articles" 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /shared/src/api/article/controllers/article.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * article controller 5 | */ 6 | 7 | const { createCoreController } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreController("api::article.article"); 10 | -------------------------------------------------------------------------------- /shared/src/api/article/routes/article.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * article router. 5 | */ 6 | 7 | const { createCoreRouter } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreRouter("api::article.article"); 10 | -------------------------------------------------------------------------------- /shared/src/api/article/services/article.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * article service. 5 | */ 6 | 7 | const { createCoreService } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreService("api::article.article"); 10 | -------------------------------------------------------------------------------- /shared/src/api/category/content-types/category/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "categories", 4 | "info": { 5 | "singularName": "category", 6 | "pluralName": "categories", 7 | "displayName": "Category", 8 | "name": "category" 9 | }, 10 | "options": { 11 | "increments": true, 12 | "timestamps": true 13 | }, 14 | "attributes": { 15 | "name": { 16 | "type": "string", 17 | "required": true 18 | }, 19 | "slug": { 20 | "type": "uid", 21 | "targetField": "name", 22 | "required": true 23 | }, 24 | "articles": { 25 | "type": "relation", 26 | "relation": "oneToMany", 27 | "target": "api::article.article", 28 | "mappedBy": "category" 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /shared/src/api/category/controllers/category.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * category controller 5 | */ 6 | 7 | const { createCoreController } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreController( 10 | "api::category.category", 11 | ({ strapi }) => ({ 12 | async findBySlug(ctx) { 13 | const { slug } = ctx.params; 14 | const { query } = ctx; 15 | 16 | let populate; 17 | if (query.populate) { 18 | if (query.populate === "*") { 19 | populate = true; 20 | } else { 21 | populate = query.populate.split(","); 22 | } 23 | } 24 | 25 | const category = await strapi.db.query("api::category.category").findOne({ 26 | where: { 27 | slug, 28 | }, 29 | populate, 30 | }); 31 | 32 | const sanitizedEntity = await this.sanitizeOutput(category, ctx); 33 | 34 | return this.transformResponse(sanitizedEntity); 35 | }, 36 | }) 37 | ); 38 | -------------------------------------------------------------------------------- /shared/src/api/category/routes/category.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * category router. 5 | */ 6 | 7 | const { createCoreRouter } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreRouter("api::category.category"); 10 | -------------------------------------------------------------------------------- /shared/src/api/category/routes/custom-category.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | routes: [ 5 | { 6 | method: "GET", 7 | path: "/categories/slug/:slug+", 8 | handler: "category.findBySlug", 9 | }, 10 | ], 11 | }; 12 | -------------------------------------------------------------------------------- /shared/src/api/category/services/category.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * category service. 5 | */ 6 | 7 | const { createCoreService } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreService("api::category.category"); 10 | -------------------------------------------------------------------------------- /shared/src/api/global/content-types/global/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "singleType", 3 | "collectionName": "globals", 4 | "info": { 5 | "singularName": "global", 6 | "pluralName": "globals", 7 | "displayName": "Global", 8 | "name": "global" 9 | }, 10 | "options": { 11 | "increments": true, 12 | "timestamps": true, 13 | "draftAndPublish": false 14 | }, 15 | "attributes": { 16 | "siteName": { 17 | "type": "string", 18 | "required": true 19 | }, 20 | "defaultSeo": { 21 | "type": "component", 22 | "repeatable": false, 23 | "component": "shared.seo", 24 | "required": true 25 | }, 26 | "favicon": { 27 | "allowedTypes": [ 28 | "images", 29 | "files", 30 | "videos" 31 | ], 32 | "type": "media", 33 | "multiple": false 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /shared/src/api/global/controllers/global.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * global controller 5 | */ 6 | 7 | const { createCoreController } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreController("api::global.global"); 10 | -------------------------------------------------------------------------------- /shared/src/api/global/routes/global.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * global router. 5 | */ 6 | 7 | const { createCoreRouter } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreRouter("api::global.global"); 10 | -------------------------------------------------------------------------------- /shared/src/api/global/services/global.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * global service. 5 | */ 6 | 7 | const { createCoreService } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreService("api::global.global"); 10 | -------------------------------------------------------------------------------- /shared/src/api/homepage/content-types/homepage/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "singleType", 3 | "collectionName": "homepages", 4 | "info": { 5 | "singularName": "homepage", 6 | "pluralName": "homepages", 7 | "displayName": "Homepage", 8 | "name": "homepage", 9 | "description": "" 10 | }, 11 | "options": { 12 | "increments": true, 13 | "timestamps": true, 14 | "draftAndPublish": false 15 | }, 16 | "pluginOptions": { 17 | "i18n": { 18 | "localized": true 19 | } 20 | }, 21 | "attributes": { 22 | "seo": { 23 | "type": "component", 24 | "repeatable": false, 25 | "component": "shared.seo", 26 | "pluginOptions": { 27 | "i18n": { 28 | "localized": true 29 | } 30 | } 31 | }, 32 | "hero": { 33 | "type": "component", 34 | "repeatable": false, 35 | "component": "sections.hero", 36 | "required": true, 37 | "pluginOptions": { 38 | "i18n": { 39 | "localized": true 40 | } 41 | } 42 | }, 43 | "highlights": { 44 | "type": "component", 45 | "repeatable": false, 46 | "component": "sections.highlight", 47 | "pluginOptions": { 48 | "i18n": { 49 | "localized": true 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /shared/src/api/homepage/controllers/homepage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * homepage controller 5 | */ 6 | 7 | const { createCoreController } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreController("api::homepage.homepage", { 10 | config: { 11 | find: { 12 | auth: false, 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /shared/src/api/homepage/routes/homepage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * homepage router. 5 | */ 6 | 7 | const { createCoreRouter } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreRouter("api::homepage.homepage"); 10 | -------------------------------------------------------------------------------- /shared/src/api/homepage/services/homepage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * homepage service. 5 | */ 6 | 7 | const { createCoreService } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreService("api::homepage.homepage"); 10 | -------------------------------------------------------------------------------- /shared/src/api/writer/content-types/writer/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "writers", 4 | "info": { 5 | "singularName": "writer", 6 | "pluralName": "writers", 7 | "displayName": "Writer", 8 | "name": "writer" 9 | }, 10 | "options": { 11 | "increments": true, 12 | "timestamps": true 13 | }, 14 | "attributes": { 15 | "name": { 16 | "type": "string" 17 | }, 18 | "picture": { 19 | "allowedTypes": [ 20 | "images", 21 | "files", 22 | "videos" 23 | ], 24 | "type": "media", 25 | "multiple": false 26 | }, 27 | "articles": { 28 | "type": "relation", 29 | "relation": "oneToMany", 30 | "target": "api::article.article", 31 | "mappedBy": "author" 32 | }, 33 | "email": { 34 | "type": "string" 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /shared/src/api/writer/controllers/writer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * writer controller 5 | */ 6 | 7 | const { createCoreController } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreController("api::writer.writer"); 10 | -------------------------------------------------------------------------------- /shared/src/api/writer/routes/writer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * writer router. 5 | */ 6 | 7 | const { createCoreRouter } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreRouter("api::writer.writer"); 10 | -------------------------------------------------------------------------------- /shared/src/api/writer/services/writer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * writer service. 5 | */ 6 | 7 | const { createCoreService } = require("@strapi/strapi").factories; 8 | 9 | module.exports = createCoreService("api::writer.writer"); 10 | -------------------------------------------------------------------------------- /shared/src/components/sections/hero.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectionName": "components_decoration_heroes", 3 | "info": { 4 | "name": "Hero", 5 | "icon": "address-card" 6 | }, 7 | "options": {}, 8 | "attributes": { 9 | "title": { 10 | "type": "string", 11 | "required": true 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /shared/src/components/sections/highlight.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectionName": "components_sections_highlights", 3 | "info": { 4 | "displayName": "Highlight", 5 | "icon": "award" 6 | }, 7 | "options": {}, 8 | "attributes": { 9 | "articles": { 10 | "type": "relation", 11 | "relation": "oneToMany", 12 | "target": "api::article.article" 13 | }, 14 | "category": { 15 | "type": "relation", 16 | "relation": "oneToOne", 17 | "target": "api::category.category" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /shared/src/components/shared/seo.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectionName": "components_shared_seos", 3 | "info": { 4 | "name": "Seo", 5 | "icon": "allergies" 6 | }, 7 | "options": {}, 8 | "attributes": { 9 | "metaTitle": { 10 | "type": "string", 11 | "required": true 12 | }, 13 | "metaDescription": { 14 | "type": "text", 15 | "required": true 16 | }, 17 | "shareImage": { 18 | "allowedTypes": [ 19 | "images" 20 | ], 21 | "type": "media", 22 | "multiple": false 23 | }, 24 | "highlights": { 25 | "type": "component", 26 | "repeatable": false, 27 | "component": "sections.highlight" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /shared/src/extensions/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi-community/plugin-rest-cache/7e08dc4715b0c231b9f6646045b5c9a8674b58fd/shared/src/extensions/.gitkeep -------------------------------------------------------------------------------- /shared/src/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { bootstrap } = require("./bootstrap"); 4 | 5 | module.exports = { 6 | /** 7 | * An asynchronous register function that runs before 8 | * your application is initialized. 9 | * 10 | * This gives you an opportunity to extend code. 11 | */ 12 | register(/* { strapi } */) {}, 13 | 14 | /** 15 | * An asynchronous bootstrap function that runs before 16 | * your application gets started. 17 | * 18 | * This gives you an opportunity to set up your data model, 19 | * run jobs, or perform some special logic. 20 | */ 21 | bootstrap 22 | }; 23 | -------------------------------------------------------------------------------- /shared/start-profiler.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | process.env.NODE_ENV = 'test'; 4 | 5 | const Strapi = require("@strapi/strapi"); 6 | 7 | async function setup() { 8 | Strapi({ 9 | dir: __dirname, 10 | }); 11 | await strapi.start(); 12 | 13 | return Promise.resolve(strapi); 14 | } 15 | 16 | // eslint-disable-next-line no-console 17 | setup().catch(console.error); 18 | -------------------------------------------------------------------------------- /shared/tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@strapi-community/tests" 3 | } 4 | -------------------------------------------------------------------------------- /shared/tests/helpers/strapi.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { existsSync, unlinkSync } = require("fs"); 4 | const Strapi = require("@strapi/strapi"); 5 | const supertest = require("supertest"); 6 | const { join } = require("path"); 7 | 8 | let adminToken; 9 | 10 | function agent() { 11 | return supertest.agent(strapi.server.httpServer); 12 | } 13 | function adminAgent() { 14 | return supertest 15 | .agent(strapi.server.httpServer) 16 | .auth(adminToken, { type: "bearer" }); 17 | } 18 | 19 | async function setup() { 20 | Strapi({ 21 | dir: join(__dirname, "../../"), 22 | }); 23 | await strapi.start(); 24 | 25 | // create first admin 26 | await agent().post("/admin/register-admin").send({ 27 | email: "admin@strapi.io", 28 | firstname: "admin", 29 | lastname: "admin", 30 | password: "Password123", 31 | }); 32 | 33 | const response = await agent().post("/admin/login").send({ 34 | email: "admin@strapi.io", 35 | password: "Password123", 36 | }); 37 | 38 | adminToken = response.body.data.token; 39 | 40 | return Promise.resolve(strapi); 41 | } 42 | 43 | async function teardown() { 44 | const dbSettings = strapi.config.get("database.connection.connection"); 45 | 46 | // close server to release the db-file 47 | await strapi.destroy(); 48 | 49 | // delete test database after all tests 50 | if (dbSettings && dbSettings.filename) { 51 | const tmpDbFile = `${dbSettings.filename}`; 52 | 53 | if (existsSync(tmpDbFile)) { 54 | unlinkSync(tmpDbFile); 55 | } 56 | } 57 | 58 | return Promise.resolve(); 59 | } 60 | 61 | module.exports = { 62 | setup, 63 | teardown, 64 | agent, 65 | adminAgent, 66 | }; 67 | --------------------------------------------------------------------------------