strapi-cache 1.5.5 → 1.5.7-no-cache-header
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -28
- package/dist/_chunks/de-Bw-yMVQV.js +0 -1
- package/dist/_chunks/de-OlWEMza0.mjs +0 -1
- package/dist/_chunks/en-3qeM6Pow.js +0 -1
- package/dist/_chunks/en-CFA1lEXn.mjs +0 -1
- package/dist/admin/index.js +5 -2
- package/dist/admin/index.mjs +5 -2
- package/dist/admin/src/components/PurgeEntityButton/index.d.ts +1 -1
- package/dist/server/index.js +71 -12
- package/dist/server/index.mjs +71 -12
- package/dist/server/src/config/index.d.ts +1 -0
- package/dist/server/src/index.d.ts +1 -0
- package/dist/server/src/services/redis/provider.d.ts +1 -0
- package/package.json +1 -1
- package/dist/_chunks/de-Bw-yMVQV.js.map +0 -1
- package/dist/_chunks/de-OlWEMza0.mjs.map +0 -1
- package/dist/_chunks/en-3qeM6Pow.js.map +0 -1
- package/dist/_chunks/en-CFA1lEXn.mjs.map +0 -1
- package/dist/admin/index.js.map +0 -1
- package/dist/admin/index.mjs.map +0 -1
- package/dist/server/index.js.map +0 -1
- package/dist/server/index.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -60,6 +60,7 @@ In your Strapi project, navigate to `config/plugins.js` and add the following co
|
|
|
60
60
|
cacheAuthorizedRequests: false, // Cache requests with authorization headers (set to true if you want to cache authorized requests)
|
|
61
61
|
cacheGetTimeoutInMs: 1000, // Timeout for getting cached data in milliseconds (default is 1 second)
|
|
62
62
|
autoPurgeCache: true, // Automatically purge cache on content CRUD operations
|
|
63
|
+
autoPurgeCacheOnStart: true, // Automatically purge cache on Strapi startup
|
|
63
64
|
},
|
|
64
65
|
},
|
|
65
66
|
```
|
|
@@ -95,34 +96,6 @@ All of these routes are protected by the policies `admin::isAuthenticatedAdmin`
|
|
|
95
96
|
|
|
96
97
|
If you encounter any issues, please feel free to open an issue on the [GitHub repo](https://github.com/TupiC/strapi-cache/issues/new).
|
|
97
98
|
|
|
98
|
-
## 🛠️ Troubleshooting
|
|
99
|
-
|
|
100
|
-
If you encounter an error like:
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
Access to fetch at 'http://your-backend.com' from origin 'http://your-origin.com' has been blocked by CORS policy:
|
|
104
|
-
Request header field cache-control is not allowed by Access-Control-Allow-Headers in preflight response.
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
You might need to adjust your CORS middleware settings in Strapi:
|
|
108
|
-
|
|
109
|
-
```javascript
|
|
110
|
-
// config/middlewares.{js,ts}
|
|
111
|
-
'strapi::cors';
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
with:
|
|
115
|
-
|
|
116
|
-
```javascript
|
|
117
|
-
// config/middlewares.{js,ts}
|
|
118
|
-
{
|
|
119
|
-
name: "strapi::cors",
|
|
120
|
-
config: {
|
|
121
|
-
headers: ["Content-Type", "Authorization", "Cache-Control"], // Add 'Cache-Control' to the allowed headers
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
```
|
|
125
|
-
|
|
126
99
|
## 🛠️ Contributing
|
|
127
100
|
|
|
128
101
|
Contributions are welcome! If you have suggestions or improvements, please open an issue or submit a pull request.
|
package/dist/admin/index.js
CHANGED
|
@@ -161,7 +161,11 @@ function PurgeCacheButton() {
|
|
|
161
161
|
function PurgeEntityButton() {
|
|
162
162
|
const { formatMessage } = reactIntl.useIntl();
|
|
163
163
|
const { id, isSingleType, contentType } = admin.unstable_useContentManagerContext();
|
|
164
|
-
const
|
|
164
|
+
const apiPath = isSingleType ? contentType?.info.singularName : id;
|
|
165
|
+
if (!apiPath) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const keyToUse = encodeURIComponent(apiPath);
|
|
165
169
|
const contentTypeName = isSingleType ? contentType?.info.singularName : contentType?.info.pluralName;
|
|
166
170
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
167
171
|
PurgeModal,
|
|
@@ -207,4 +211,3 @@ const index = {
|
|
|
207
211
|
}
|
|
208
212
|
};
|
|
209
213
|
module.exports = index;
|
|
210
|
-
//# sourceMappingURL=index.js.map
|
package/dist/admin/index.mjs
CHANGED
|
@@ -160,7 +160,11 @@ function PurgeCacheButton() {
|
|
|
160
160
|
function PurgeEntityButton() {
|
|
161
161
|
const { formatMessage } = useIntl();
|
|
162
162
|
const { id, isSingleType, contentType } = unstable_useContentManagerContext();
|
|
163
|
-
const
|
|
163
|
+
const apiPath = isSingleType ? contentType?.info.singularName : id;
|
|
164
|
+
if (!apiPath) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const keyToUse = encodeURIComponent(apiPath);
|
|
164
168
|
const contentTypeName = isSingleType ? contentType?.info.singularName : contentType?.info.pluralName;
|
|
165
169
|
return /* @__PURE__ */ jsx(
|
|
166
170
|
PurgeModal,
|
|
@@ -208,4 +212,3 @@ const index = {
|
|
|
208
212
|
export {
|
|
209
213
|
index as default
|
|
210
214
|
};
|
|
211
|
-
//# sourceMappingURL=index.mjs.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare function PurgeEntityButton(): import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
declare function PurgeEntityButton(): import("react/jsx-runtime").JSX.Element | null;
|
|
2
2
|
export default PurgeEntityButton;
|
package/dist/server/index.js
CHANGED
|
@@ -73,6 +73,7 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
73
73
|
try {
|
|
74
74
|
const cacheService = strapi2.plugin("strapi-cache").services.service;
|
|
75
75
|
const autoPurgeCache = strapi2.plugin("strapi-cache").config("autoPurgeCache");
|
|
76
|
+
const autoPurgeCacheOnStart = strapi2.plugin("strapi-cache").config("autoPurgeCacheOnStart");
|
|
76
77
|
const cacheStore = cacheService.getCacheInstance();
|
|
77
78
|
if (!cacheStore) {
|
|
78
79
|
loggy.error("Plugin could not be initialized");
|
|
@@ -95,6 +96,13 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
95
96
|
}
|
|
96
97
|
});
|
|
97
98
|
}
|
|
99
|
+
if (autoPurgeCacheOnStart) {
|
|
100
|
+
cacheStore.reset().then(() => {
|
|
101
|
+
loggy.info("Cache purged successfully");
|
|
102
|
+
}).catch((error) => {
|
|
103
|
+
loggy.error(`Error purging cache on start: ${error.message}`);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
98
106
|
} catch (error) {
|
|
99
107
|
loggy.error("Plugin could not be initialized");
|
|
100
108
|
return;
|
|
@@ -184,8 +192,6 @@ const middleware$1 = async (ctx, next) => {
|
|
|
184
192
|
const { url } = ctx.request;
|
|
185
193
|
const key = generateCacheKey(ctx);
|
|
186
194
|
const cacheEntry = await cacheStore.get(key);
|
|
187
|
-
const cacheControlHeader = ctx.request.headers["cache-control"];
|
|
188
|
-
const noCache = cacheControlHeader && cacheControlHeader.includes("no-cache");
|
|
189
195
|
const routeIsCachable = cacheableRoutes.some((route) => url.startsWith(route)) || cacheableRoutes.length === 0 && url.startsWith("/api");
|
|
190
196
|
const authorizationHeader = ctx.request.headers["authorization"];
|
|
191
197
|
if (authorizationHeader && !cacheAuthorizedRequests) {
|
|
@@ -193,13 +199,34 @@ const middleware$1 = async (ctx, next) => {
|
|
|
193
199
|
await next();
|
|
194
200
|
return;
|
|
195
201
|
}
|
|
196
|
-
if (cacheEntry
|
|
202
|
+
if (cacheEntry) {
|
|
197
203
|
loggy.info(`HIT with key: ${key}`);
|
|
198
204
|
ctx.status = 200;
|
|
199
205
|
ctx.body = cacheEntry.body;
|
|
200
206
|
if (cacheHeaders) {
|
|
201
207
|
ctx.set(cacheEntry.headers);
|
|
202
208
|
}
|
|
209
|
+
const middlewaresConfig = strapi.config.get("middlewares");
|
|
210
|
+
const corsMiddleware = middlewaresConfig.find((mw) => mw.name === "strapi::cors");
|
|
211
|
+
if (corsMiddleware) {
|
|
212
|
+
loggy.info("CORS middleware is set, checking allowed origins");
|
|
213
|
+
const corsConfig = corsMiddleware?.config;
|
|
214
|
+
const origin = ctx?.request?.headers?.origin;
|
|
215
|
+
let allowedOrigins = corsConfig?.origin ?? "*";
|
|
216
|
+
if (typeof allowedOrigins === "string") {
|
|
217
|
+
allowedOrigins = [allowedOrigins];
|
|
218
|
+
}
|
|
219
|
+
if (allowedOrigins.includes(origin)) {
|
|
220
|
+
loggy.info(`Setting Access-Control-Allow-Origin to ${origin}`);
|
|
221
|
+
ctx.set("Access-Control-Allow-Origin", origin);
|
|
222
|
+
} else if (typeof origin === "undefined" || allowedOrigins.includes("*")) {
|
|
223
|
+
loggy.info("No origin header or * in allowed origins, setting to *");
|
|
224
|
+
ctx.set("Access-Control-Allow-Origin", "*");
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
loggy.info("No CORS middleware set, setting to request origin or *");
|
|
228
|
+
ctx.set("Access-Control-Allow-Origin", ctx.request.headers.origin || "*");
|
|
229
|
+
}
|
|
203
230
|
return;
|
|
204
231
|
}
|
|
205
232
|
await next();
|
|
@@ -250,21 +277,40 @@ const middleware = async (ctx, next) => {
|
|
|
250
277
|
}
|
|
251
278
|
const key = generateGraphqlCacheKey(body);
|
|
252
279
|
const cacheEntry = await cacheStore.get(key);
|
|
253
|
-
const cacheControlHeader = ctx.request.headers["cache-control"];
|
|
254
|
-
const noCache = cacheControlHeader && cacheControlHeader.includes("no-cache");
|
|
255
280
|
const authorizationHeader = ctx.request.headers["authorization"];
|
|
256
281
|
if (authorizationHeader && !cacheAuthorizedRequests) {
|
|
257
282
|
loggy.info(`Authorized request bypassing cache: ${key}`);
|
|
258
283
|
await next();
|
|
259
284
|
return;
|
|
260
285
|
}
|
|
261
|
-
if (cacheEntry
|
|
286
|
+
if (cacheEntry) {
|
|
262
287
|
loggy.info(`HIT with key: ${key}`);
|
|
263
288
|
ctx.status = 200;
|
|
264
289
|
ctx.body = cacheEntry.body;
|
|
265
290
|
if (cacheHeaders) {
|
|
266
291
|
ctx.set(cacheEntry.headers);
|
|
267
292
|
}
|
|
293
|
+
const middlewaresConfig = strapi.config.get("middlewares");
|
|
294
|
+
const corsMiddleware = middlewaresConfig.find((mw) => mw.name === "strapi::cors");
|
|
295
|
+
if (corsMiddleware) {
|
|
296
|
+
loggy.info("CORS middleware is set, checking allowed origins");
|
|
297
|
+
const corsConfig = corsMiddleware?.config;
|
|
298
|
+
const origin = ctx?.request?.headers?.origin;
|
|
299
|
+
let allowedOrigins = corsConfig?.origin ?? "*";
|
|
300
|
+
if (typeof allowedOrigins === "string") {
|
|
301
|
+
allowedOrigins = [allowedOrigins];
|
|
302
|
+
}
|
|
303
|
+
if (allowedOrigins.includes(origin)) {
|
|
304
|
+
loggy.info(`Setting Access-Control-Allow-Origin to ${origin}`);
|
|
305
|
+
ctx.set("Access-Control-Allow-Origin", origin);
|
|
306
|
+
} else if (typeof origin === "undefined" || allowedOrigins.includes("*")) {
|
|
307
|
+
loggy.info("No origin header or * in allowed origins, setting to *");
|
|
308
|
+
ctx.set("Access-Control-Allow-Origin", "*");
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
loggy.info("No CORS middleware set, setting to request origin or *");
|
|
312
|
+
ctx.set("Access-Control-Allow-Origin", ctx.request.headers.origin || "*");
|
|
313
|
+
}
|
|
268
314
|
return;
|
|
269
315
|
}
|
|
270
316
|
await next();
|
|
@@ -319,7 +365,8 @@ const config = {
|
|
|
319
365
|
cacheHeadersAllowList: [],
|
|
320
366
|
cacheAuthorizedRequests: false,
|
|
321
367
|
cacheGetTimeoutInMs: 1e3,
|
|
322
|
-
autoPurgeCache: true
|
|
368
|
+
autoPurgeCache: true,
|
|
369
|
+
autoPurgeCacheOnStart: true
|
|
323
370
|
}),
|
|
324
371
|
validator: (config2) => {
|
|
325
372
|
if (typeof config2.debug !== "boolean") {
|
|
@@ -380,6 +427,9 @@ const config = {
|
|
|
380
427
|
if (typeof config2.autoPurgeCache !== "boolean") {
|
|
381
428
|
throw new Error(`Invalid config: autoPurgeCache must be a boolean`);
|
|
382
429
|
}
|
|
430
|
+
if (typeof config2.autoPurgeCacheOnStart !== "boolean") {
|
|
431
|
+
throw new Error(`Invalid config: autoPurgeCacheOnStart must be a boolean`);
|
|
432
|
+
}
|
|
383
433
|
}
|
|
384
434
|
};
|
|
385
435
|
const contentTypes = {};
|
|
@@ -394,7 +444,7 @@ const controller = ({ strapi: strapi2 }) => ({
|
|
|
394
444
|
async purgeCacheByKey(ctx) {
|
|
395
445
|
const { key } = ctx.params;
|
|
396
446
|
const service2 = strapi2.plugin("strapi-cache").service("service");
|
|
397
|
-
const regex = new RegExp(`(^|/)
|
|
447
|
+
const regex = new RegExp(`(^|/)?${key}(/|\\?|$)`);
|
|
398
448
|
await service2.getCacheInstance().clearByRegexp([regex]);
|
|
399
449
|
ctx.body = {
|
|
400
450
|
message: `Cache purged successfully for key: ${key}`
|
|
@@ -589,6 +639,7 @@ class RedisCacheProvider {
|
|
|
589
639
|
this.cacheGetTimeoutInMs = Number(
|
|
590
640
|
this.strapi.plugin("strapi-cache").config("cacheGetTimeoutInMs")
|
|
591
641
|
);
|
|
642
|
+
this.keyPrefix = this.strapi.plugin("strapi-cache").config("redisConfig")?.["keyPrefix"] ?? "";
|
|
592
643
|
if (redisClusterNodes.length) {
|
|
593
644
|
const redisClusterOptions = this.strapi.plugin("strapi-cache").config("redisClusterOptions");
|
|
594
645
|
if (!redisClusterOptions["redisOptions"]) {
|
|
@@ -638,8 +689,9 @@ class RedisCacheProvider {
|
|
|
638
689
|
async del(key) {
|
|
639
690
|
if (!this.ready) return null;
|
|
640
691
|
try {
|
|
641
|
-
|
|
642
|
-
|
|
692
|
+
const relativeKey = key.slice(this.keyPrefix.length);
|
|
693
|
+
loggy.info(`Redis PURGING KEY: ${relativeKey}`);
|
|
694
|
+
await this.client.del(relativeKey);
|
|
643
695
|
return true;
|
|
644
696
|
} catch (error) {
|
|
645
697
|
loggy.error(`Redis del error: ${error}`);
|
|
@@ -649,7 +701,7 @@ class RedisCacheProvider {
|
|
|
649
701
|
async keys() {
|
|
650
702
|
if (!this.ready) return null;
|
|
651
703
|
try {
|
|
652
|
-
const keys = await this.client.keys(
|
|
704
|
+
const keys = await this.client.keys(`${this.keyPrefix}*`);
|
|
653
705
|
return keys;
|
|
654
706
|
} catch (error) {
|
|
655
707
|
loggy.error(`Redis keys error: ${error}`);
|
|
@@ -659,6 +711,14 @@ class RedisCacheProvider {
|
|
|
659
711
|
async reset() {
|
|
660
712
|
if (!this.ready) return null;
|
|
661
713
|
try {
|
|
714
|
+
if (this.keyPrefix) {
|
|
715
|
+
loggy.info(`Redis FLUSHING NAMESPACE: ${this.keyPrefix}`);
|
|
716
|
+
const keys = await this.keys();
|
|
717
|
+
if (!keys) return null;
|
|
718
|
+
const toDelete = keys.filter((key) => key.startsWith(this.keyPrefix));
|
|
719
|
+
await Promise.all(toDelete.map((key) => this.del(key)));
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
662
722
|
loggy.info(`Redis FLUSHING ALL KEYS`);
|
|
663
723
|
await this.client.flushdb();
|
|
664
724
|
return true;
|
|
@@ -717,4 +777,3 @@ const index = {
|
|
|
717
777
|
middlewares
|
|
718
778
|
};
|
|
719
779
|
module.exports = index;
|
|
720
|
-
//# sourceMappingURL=index.js.map
|
package/dist/server/index.mjs
CHANGED
|
@@ -69,6 +69,7 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
69
69
|
try {
|
|
70
70
|
const cacheService = strapi2.plugin("strapi-cache").services.service;
|
|
71
71
|
const autoPurgeCache = strapi2.plugin("strapi-cache").config("autoPurgeCache");
|
|
72
|
+
const autoPurgeCacheOnStart = strapi2.plugin("strapi-cache").config("autoPurgeCacheOnStart");
|
|
72
73
|
const cacheStore = cacheService.getCacheInstance();
|
|
73
74
|
if (!cacheStore) {
|
|
74
75
|
loggy.error("Plugin could not be initialized");
|
|
@@ -91,6 +92,13 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
91
92
|
}
|
|
92
93
|
});
|
|
93
94
|
}
|
|
95
|
+
if (autoPurgeCacheOnStart) {
|
|
96
|
+
cacheStore.reset().then(() => {
|
|
97
|
+
loggy.info("Cache purged successfully");
|
|
98
|
+
}).catch((error) => {
|
|
99
|
+
loggy.error(`Error purging cache on start: ${error.message}`);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
94
102
|
} catch (error) {
|
|
95
103
|
loggy.error("Plugin could not be initialized");
|
|
96
104
|
return;
|
|
@@ -180,8 +188,6 @@ const middleware$1 = async (ctx, next) => {
|
|
|
180
188
|
const { url } = ctx.request;
|
|
181
189
|
const key = generateCacheKey(ctx);
|
|
182
190
|
const cacheEntry = await cacheStore.get(key);
|
|
183
|
-
const cacheControlHeader = ctx.request.headers["cache-control"];
|
|
184
|
-
const noCache = cacheControlHeader && cacheControlHeader.includes("no-cache");
|
|
185
191
|
const routeIsCachable = cacheableRoutes.some((route) => url.startsWith(route)) || cacheableRoutes.length === 0 && url.startsWith("/api");
|
|
186
192
|
const authorizationHeader = ctx.request.headers["authorization"];
|
|
187
193
|
if (authorizationHeader && !cacheAuthorizedRequests) {
|
|
@@ -189,13 +195,34 @@ const middleware$1 = async (ctx, next) => {
|
|
|
189
195
|
await next();
|
|
190
196
|
return;
|
|
191
197
|
}
|
|
192
|
-
if (cacheEntry
|
|
198
|
+
if (cacheEntry) {
|
|
193
199
|
loggy.info(`HIT with key: ${key}`);
|
|
194
200
|
ctx.status = 200;
|
|
195
201
|
ctx.body = cacheEntry.body;
|
|
196
202
|
if (cacheHeaders) {
|
|
197
203
|
ctx.set(cacheEntry.headers);
|
|
198
204
|
}
|
|
205
|
+
const middlewaresConfig = strapi.config.get("middlewares");
|
|
206
|
+
const corsMiddleware = middlewaresConfig.find((mw) => mw.name === "strapi::cors");
|
|
207
|
+
if (corsMiddleware) {
|
|
208
|
+
loggy.info("CORS middleware is set, checking allowed origins");
|
|
209
|
+
const corsConfig = corsMiddleware?.config;
|
|
210
|
+
const origin = ctx?.request?.headers?.origin;
|
|
211
|
+
let allowedOrigins = corsConfig?.origin ?? "*";
|
|
212
|
+
if (typeof allowedOrigins === "string") {
|
|
213
|
+
allowedOrigins = [allowedOrigins];
|
|
214
|
+
}
|
|
215
|
+
if (allowedOrigins.includes(origin)) {
|
|
216
|
+
loggy.info(`Setting Access-Control-Allow-Origin to ${origin}`);
|
|
217
|
+
ctx.set("Access-Control-Allow-Origin", origin);
|
|
218
|
+
} else if (typeof origin === "undefined" || allowedOrigins.includes("*")) {
|
|
219
|
+
loggy.info("No origin header or * in allowed origins, setting to *");
|
|
220
|
+
ctx.set("Access-Control-Allow-Origin", "*");
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
loggy.info("No CORS middleware set, setting to request origin or *");
|
|
224
|
+
ctx.set("Access-Control-Allow-Origin", ctx.request.headers.origin || "*");
|
|
225
|
+
}
|
|
199
226
|
return;
|
|
200
227
|
}
|
|
201
228
|
await next();
|
|
@@ -246,21 +273,40 @@ const middleware = async (ctx, next) => {
|
|
|
246
273
|
}
|
|
247
274
|
const key = generateGraphqlCacheKey(body);
|
|
248
275
|
const cacheEntry = await cacheStore.get(key);
|
|
249
|
-
const cacheControlHeader = ctx.request.headers["cache-control"];
|
|
250
|
-
const noCache = cacheControlHeader && cacheControlHeader.includes("no-cache");
|
|
251
276
|
const authorizationHeader = ctx.request.headers["authorization"];
|
|
252
277
|
if (authorizationHeader && !cacheAuthorizedRequests) {
|
|
253
278
|
loggy.info(`Authorized request bypassing cache: ${key}`);
|
|
254
279
|
await next();
|
|
255
280
|
return;
|
|
256
281
|
}
|
|
257
|
-
if (cacheEntry
|
|
282
|
+
if (cacheEntry) {
|
|
258
283
|
loggy.info(`HIT with key: ${key}`);
|
|
259
284
|
ctx.status = 200;
|
|
260
285
|
ctx.body = cacheEntry.body;
|
|
261
286
|
if (cacheHeaders) {
|
|
262
287
|
ctx.set(cacheEntry.headers);
|
|
263
288
|
}
|
|
289
|
+
const middlewaresConfig = strapi.config.get("middlewares");
|
|
290
|
+
const corsMiddleware = middlewaresConfig.find((mw) => mw.name === "strapi::cors");
|
|
291
|
+
if (corsMiddleware) {
|
|
292
|
+
loggy.info("CORS middleware is set, checking allowed origins");
|
|
293
|
+
const corsConfig = corsMiddleware?.config;
|
|
294
|
+
const origin = ctx?.request?.headers?.origin;
|
|
295
|
+
let allowedOrigins = corsConfig?.origin ?? "*";
|
|
296
|
+
if (typeof allowedOrigins === "string") {
|
|
297
|
+
allowedOrigins = [allowedOrigins];
|
|
298
|
+
}
|
|
299
|
+
if (allowedOrigins.includes(origin)) {
|
|
300
|
+
loggy.info(`Setting Access-Control-Allow-Origin to ${origin}`);
|
|
301
|
+
ctx.set("Access-Control-Allow-Origin", origin);
|
|
302
|
+
} else if (typeof origin === "undefined" || allowedOrigins.includes("*")) {
|
|
303
|
+
loggy.info("No origin header or * in allowed origins, setting to *");
|
|
304
|
+
ctx.set("Access-Control-Allow-Origin", "*");
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
loggy.info("No CORS middleware set, setting to request origin or *");
|
|
308
|
+
ctx.set("Access-Control-Allow-Origin", ctx.request.headers.origin || "*");
|
|
309
|
+
}
|
|
264
310
|
return;
|
|
265
311
|
}
|
|
266
312
|
await next();
|
|
@@ -315,7 +361,8 @@ const config = {
|
|
|
315
361
|
cacheHeadersAllowList: [],
|
|
316
362
|
cacheAuthorizedRequests: false,
|
|
317
363
|
cacheGetTimeoutInMs: 1e3,
|
|
318
|
-
autoPurgeCache: true
|
|
364
|
+
autoPurgeCache: true,
|
|
365
|
+
autoPurgeCacheOnStart: true
|
|
319
366
|
}),
|
|
320
367
|
validator: (config2) => {
|
|
321
368
|
if (typeof config2.debug !== "boolean") {
|
|
@@ -376,6 +423,9 @@ const config = {
|
|
|
376
423
|
if (typeof config2.autoPurgeCache !== "boolean") {
|
|
377
424
|
throw new Error(`Invalid config: autoPurgeCache must be a boolean`);
|
|
378
425
|
}
|
|
426
|
+
if (typeof config2.autoPurgeCacheOnStart !== "boolean") {
|
|
427
|
+
throw new Error(`Invalid config: autoPurgeCacheOnStart must be a boolean`);
|
|
428
|
+
}
|
|
379
429
|
}
|
|
380
430
|
};
|
|
381
431
|
const contentTypes = {};
|
|
@@ -390,7 +440,7 @@ const controller = ({ strapi: strapi2 }) => ({
|
|
|
390
440
|
async purgeCacheByKey(ctx) {
|
|
391
441
|
const { key } = ctx.params;
|
|
392
442
|
const service2 = strapi2.plugin("strapi-cache").service("service");
|
|
393
|
-
const regex = new RegExp(`(^|/)
|
|
443
|
+
const regex = new RegExp(`(^|/)?${key}(/|\\?|$)`);
|
|
394
444
|
await service2.getCacheInstance().clearByRegexp([regex]);
|
|
395
445
|
ctx.body = {
|
|
396
446
|
message: `Cache purged successfully for key: ${key}`
|
|
@@ -585,6 +635,7 @@ class RedisCacheProvider {
|
|
|
585
635
|
this.cacheGetTimeoutInMs = Number(
|
|
586
636
|
this.strapi.plugin("strapi-cache").config("cacheGetTimeoutInMs")
|
|
587
637
|
);
|
|
638
|
+
this.keyPrefix = this.strapi.plugin("strapi-cache").config("redisConfig")?.["keyPrefix"] ?? "";
|
|
588
639
|
if (redisClusterNodes.length) {
|
|
589
640
|
const redisClusterOptions = this.strapi.plugin("strapi-cache").config("redisClusterOptions");
|
|
590
641
|
if (!redisClusterOptions["redisOptions"]) {
|
|
@@ -634,8 +685,9 @@ class RedisCacheProvider {
|
|
|
634
685
|
async del(key) {
|
|
635
686
|
if (!this.ready) return null;
|
|
636
687
|
try {
|
|
637
|
-
|
|
638
|
-
|
|
688
|
+
const relativeKey = key.slice(this.keyPrefix.length);
|
|
689
|
+
loggy.info(`Redis PURGING KEY: ${relativeKey}`);
|
|
690
|
+
await this.client.del(relativeKey);
|
|
639
691
|
return true;
|
|
640
692
|
} catch (error) {
|
|
641
693
|
loggy.error(`Redis del error: ${error}`);
|
|
@@ -645,7 +697,7 @@ class RedisCacheProvider {
|
|
|
645
697
|
async keys() {
|
|
646
698
|
if (!this.ready) return null;
|
|
647
699
|
try {
|
|
648
|
-
const keys = await this.client.keys(
|
|
700
|
+
const keys = await this.client.keys(`${this.keyPrefix}*`);
|
|
649
701
|
return keys;
|
|
650
702
|
} catch (error) {
|
|
651
703
|
loggy.error(`Redis keys error: ${error}`);
|
|
@@ -655,6 +707,14 @@ class RedisCacheProvider {
|
|
|
655
707
|
async reset() {
|
|
656
708
|
if (!this.ready) return null;
|
|
657
709
|
try {
|
|
710
|
+
if (this.keyPrefix) {
|
|
711
|
+
loggy.info(`Redis FLUSHING NAMESPACE: ${this.keyPrefix}`);
|
|
712
|
+
const keys = await this.keys();
|
|
713
|
+
if (!keys) return null;
|
|
714
|
+
const toDelete = keys.filter((key) => key.startsWith(this.keyPrefix));
|
|
715
|
+
await Promise.all(toDelete.map((key) => this.del(key)));
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
658
718
|
loggy.info(`Redis FLUSHING ALL KEYS`);
|
|
659
719
|
await this.client.flushdb();
|
|
660
720
|
return true;
|
|
@@ -715,4 +775,3 @@ const index = {
|
|
|
715
775
|
export {
|
|
716
776
|
index as default
|
|
717
777
|
};
|
|
718
|
-
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"de-Bw-yMVQV.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"de-OlWEMza0.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"en-3qeM6Pow.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"en-CFA1lEXn.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;"}
|
package/dist/admin/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../admin/src/pluginId.ts","../../admin/src/components/Initializer.tsx","../../admin/src/permission.ts","../../admin/src/components/PurgeModal/PurgeButton/index.tsx","../../admin/src/components/PurgeModal/index.tsx","../../admin/src/components/PurgeCacheButton/index.tsx","../../admin/src/components/PurgeEntityButton/index.tsx","../../admin/src/index.ts"],"sourcesContent":["export const PLUGIN_ID = 'strapi-cache';\n","import { useEffect, useRef } from 'react';\n\nimport { PLUGIN_ID } from '../pluginId';\n\ntype InitializerProps = {\n setPlugin: (id: string) => void;\n};\n\nconst Initializer = ({ setPlugin }: InitializerProps) => {\n const ref = useRef(setPlugin);\n\n useEffect(() => {\n ref.current(PLUGIN_ID);\n }, []);\n\n return null;\n};\n\nexport { Initializer };\n","export const pluginPermissions = {\n purge: [{ action: 'plugin::strapi-cache.purge-cache', subject: null }],\n};\n","import { Button } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nfunction PurgeButton({ onClick }: { onClick: () => void }) {\n const formatMessage = useIntl().formatMessage;\n\n return (\n <Button onClick={onClick}>\n {formatMessage({\n id: 'strapi-cache.cache.confirm',\n defaultMessage: 'Yes, confirm',\n })}\n </Button>\n );\n}\n\nexport default PurgeButton;\n","import { useIntl } from 'react-intl';\nimport { Archive } from '@strapi/icons';\nimport { Button, Modal } from '@strapi/design-system';\nimport { useNotification, useRBAC } from '@strapi/strapi/admin';\nimport { pluginPermissions } from '../../permission';\nimport { Typography } from '@strapi/design-system';\nimport { useFetchClient } from '@strapi/strapi/admin';\nimport { useEffect, useState } from 'react';\nimport PurgeButton from './PurgeButton';\n\nexport type PurgeProps = {\n buttonText: string;\n buttonWidth?: string;\n keyToUse?: string;\n contentTypeName?: string;\n};\n\nfunction PurgeModal({ buttonText, keyToUse, buttonWidth, contentTypeName }: PurgeProps) {\n const { allowedActions } = useRBAC(pluginPermissions);\n const formatMessage = useIntl().formatMessage;\n const { post, get } = useFetchClient();\n const { toggleNotification } = useNotification();\n const [cacheableRoutes, setCacheableRoutes] = useState<string[]>();\n\n useEffect(() => {\n if (!allowedActions.canPurgeCache) {\n return;\n }\n const fetchCacheableRoutes = async () => {\n try {\n const { data } = await get('/strapi-cache/cacheable-routes');\n return data;\n } catch (error) {\n toggleNotification({\n type: 'warning',\n message: formatMessage({\n id: 'strapi-cache.cache.routes.fetch-error',\n defaultMessage: 'Unable to fetch cacheable routes. Cache purge may not work correctly.',\n }),\n });\n return undefined;\n }\n };\n fetchCacheableRoutes().then((data) => {\n setCacheableRoutes(data);\n });\n }, [allowedActions.canPurgeCache]);\n\n const isCacheableRoute = () => {\n if (!keyToUse || !cacheableRoutes) {\n return false;\n }\n\n return (\n cacheableRoutes.length === 0 ||\n cacheableRoutes.some((route) => {\n return route.includes(keyToUse) || (contentTypeName && route.includes(contentTypeName));\n })\n );\n };\n\n const clearCache = () => {\n if (!keyToUse) {\n toggleNotification({\n type: 'warning',\n message: formatMessage({\n id: 'strapi-cache.cache.purge.no-content-type',\n defaultMessage: 'No content type found',\n }),\n });\n return;\n }\n\n post(`/strapi-cache/purge-cache/${keyToUse}`, undefined, {\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n .then(() => {\n toggleNotification({\n type: 'success',\n message: formatMessage(\n {\n id: 'strapi-cache.cache.purge.success',\n defaultMessage: 'Cache purged successfully',\n },\n {\n key: `\"${keyToUse}\"`,\n }\n ),\n });\n })\n .catch(() => {\n toggleNotification({\n type: 'danger',\n message: formatMessage(\n {\n id: 'strapi-cache.cache.purge.error',\n defaultMessage: 'Error purging cache',\n },\n {\n key: `\"${keyToUse}\"`,\n }\n ),\n });\n });\n };\n\n if (!allowedActions.canPurgeCache || !isCacheableRoute()) {\n return null;\n }\n\n return (\n <Modal.Root>\n <Modal.Trigger>\n <Button width={buttonWidth} startIcon={<Archive />} variant=\"danger\">\n {buttonText}\n </Button>\n </Modal.Trigger>\n <Modal.Content>\n <Modal.Header>\n <Modal.Title>{buttonText}</Modal.Title>\n </Modal.Header>\n <Modal.Body>\n <Typography variant=\"omega\">\n {formatMessage(\n {\n id: 'strapi-cache.cache.purge.confirmation',\n defaultMessage: 'Are you sure you want to purge the cache?',\n },\n { key: `\"${keyToUse}\"` }\n )}\n </Typography>\n </Modal.Body>\n <Modal.Footer>\n <Modal.Close>\n <Button variant=\"tertiary\">\n {formatMessage({\n id: 'strapi-cache.cache.cancel',\n defaultMessage: 'No, cancel',\n })}\n </Button>\n </Modal.Close>\n <Modal.Close>\n <PurgeButton onClick={clearCache}></PurgeButton>\n </Modal.Close>\n </Modal.Footer>\n </Modal.Content>\n </Modal.Root>\n );\n}\n\nexport default PurgeModal;\n","import { unstable_useContentManagerContext as useContentManagerContext } from '@strapi/strapi/admin';\nimport PurgeModal from '../PurgeModal';\nimport { useIntl } from 'react-intl';\n\nfunction PurgeCacheButton() {\n const { contentType } = useContentManagerContext();\n const { formatMessage } = useIntl();\n const keyToUse = contentType?.info.pluralName;\n\n return (\n <PurgeModal\n buttonText={formatMessage({\n id: 'strapi-cache.cache.purge',\n defaultMessage: 'Purge Cache',\n })}\n keyToUse={keyToUse}\n ></PurgeModal>\n );\n}\n\nexport default PurgeCacheButton;\n","import { unstable_useContentManagerContext as useContentManagerContext } from '@strapi/strapi/admin';\nimport PurgeModal from '../PurgeModal';\nimport { useIntl } from 'react-intl';\n\nfunction PurgeEntityButton() {\n const { formatMessage } = useIntl();\n const { id, isSingleType, contentType } = useContentManagerContext();\n const keyToUse = isSingleType ? contentType?.info.singularName : id;\n const contentTypeName = isSingleType\n ? contentType?.info.singularName\n : contentType?.info.pluralName;\n\n return (\n <PurgeModal\n buttonWidth=\"100%\"\n buttonText={formatMessage({\n id: 'strapi-cache.cache.purge.entity',\n defaultMessage: 'Purge Entity Cache',\n })}\n keyToUse={keyToUse}\n contentTypeName={contentTypeName}\n ></PurgeModal>\n );\n}\n\nexport default PurgeEntityButton;\n","import { PLUGIN_ID } from './pluginId';\nimport { Initializer } from './components/Initializer';\nimport PurgeCacheButton from './components/PurgeCacheButton';\nimport PurgeEntityButton from './components/PurgeEntityButton';\n\nexport default {\n register(app: any) {\n app.registerPlugin({\n id: PLUGIN_ID,\n initializer: Initializer,\n isReady: false,\n name: PLUGIN_ID,\n });\n\n app.getPlugin('content-manager').injectComponent('listView', 'actions', {\n name: PurgeCacheButton,\n Component: PurgeCacheButton,\n });\n\n app.getPlugin('content-manager').injectComponent('editView', 'right-links', {\n name: PurgeEntityButton,\n Component: PurgeEntityButton,\n });\n },\n\n async registerTrads({ locales }: { locales: string[] }) {\n return Promise.all(\n locales.map(async (locale) => {\n try {\n const { default: data } = await import(`./translations/${locale}.json`);\n\n return { data, locale };\n } catch {\n return { data: {}, locale };\n }\n })\n );\n },\n};\n"],"names":["useRef","useEffect","useIntl","jsx","Button","useRBAC","useFetchClient","useNotification","useState","jsxs","Modal","Archive","Typography","useContentManagerContext"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAO,MAAM,YAAY;ACQzB,MAAM,cAAc,CAAC,EAAE,gBAAkC;AACjD,QAAA,MAAMA,aAAO,SAAS;AAE5BC,QAAAA,UAAU,MAAM;AACd,QAAI,QAAQ,SAAS;AAAA,EACvB,GAAG,EAAE;AAEE,SAAA;AACT;AChBO,MAAM,oBAAoB;AAAA,EAC/B,OAAO,CAAC,EAAE,QAAQ,oCAAoC,SAAS,KAAM,CAAA;AACvE;ACCA,SAAS,YAAY,EAAE,WAAoC;AACnD,QAAA,gBAAgBC,oBAAU;AAG9B,SAAAC,2BAAA,IAACC,aAAO,QAAA,EAAA,SACL,UAAc,cAAA;AAAA,IACb,IAAI;AAAA,IACJ,gBAAgB;AAAA,EACjB,CAAA,GACH;AAEJ;ACGA,SAAS,WAAW,EAAE,YAAY,UAAU,aAAa,mBAA+B;AACtF,QAAM,EAAE,eAAA,IAAmBC,MAAA,QAAQ,iBAAiB;AAC9C,QAAA,gBAAgBH,oBAAU;AAChC,QAAM,EAAE,MAAM,IAAI,IAAII,qBAAe;AAC/B,QAAA,EAAE,mBAAmB,IAAIC,sBAAgB;AAC/C,QAAM,CAAC,iBAAiB,kBAAkB,IAAIC,eAAmB;AAEjEP,QAAAA,UAAU,MAAM;AACV,QAAA,CAAC,eAAe,eAAe;AACjC;AAAA,IAAA;AAEF,UAAM,uBAAuB,YAAY;AACnC,UAAA;AACF,cAAM,EAAE,KAAA,IAAS,MAAM,IAAI,gCAAgC;AACpD,eAAA;AAAA,eACA,OAAO;AACK,2BAAA;AAAA,UACjB,MAAM;AAAA,UACN,SAAS,cAAc;AAAA,YACrB,IAAI;AAAA,YACJ,gBAAgB;AAAA,UACjB,CAAA;AAAA,QAAA,CACF;AACM,eAAA;AAAA,MAAA;AAAA,IAEX;AACqB,yBAAA,EAAE,KAAK,CAAC,SAAS;AACpC,yBAAmB,IAAI;AAAA,IAAA,CACxB;AAAA,EAAA,GACA,CAAC,eAAe,aAAa,CAAC;AAEjC,QAAM,mBAAmB,MAAM;AACzB,QAAA,CAAC,YAAY,CAAC,iBAAiB;AAC1B,aAAA;AAAA,IAAA;AAGT,WACE,gBAAgB,WAAW,KAC3B,gBAAgB,KAAK,CAAC,UAAU;AAC9B,aAAO,MAAM,SAAS,QAAQ,KAAM,mBAAmB,MAAM,SAAS,eAAe;AAAA,IAAA,CACtF;AAAA,EAEL;AAEA,QAAM,aAAa,MAAM;AACvB,QAAI,CAAC,UAAU;AACM,yBAAA;AAAA,QACjB,MAAM;AAAA,QACN,SAAS,cAAc;AAAA,UACrB,IAAI;AAAA,UACJ,gBAAgB;AAAA,QACjB,CAAA;AAAA,MAAA,CACF;AACD;AAAA,IAAA;AAGG,SAAA,6BAA6B,QAAQ,IAAI,QAAW;AAAA,MACvD,SAAS;AAAA,QACP,gBAAgB;AAAA,MAAA;AAAA,IAClB,CACD,EACE,KAAK,MAAM;AACS,yBAAA;AAAA,QACjB,MAAM;AAAA,QACN,SAAS;AAAA,UACP;AAAA,YACE,IAAI;AAAA,YACJ,gBAAgB;AAAA,UAClB;AAAA,UACA;AAAA,YACE,KAAK,IAAI,QAAQ;AAAA,UAAA;AAAA,QACnB;AAAA,MACF,CACD;AAAA,IAAA,CACF,EACA,MAAM,MAAM;AACQ,yBAAA;AAAA,QACjB,MAAM;AAAA,QACN,SAAS;AAAA,UACP;AAAA,YACE,IAAI;AAAA,YACJ,gBAAgB;AAAA,UAClB;AAAA,UACA;AAAA,YACE,KAAK,IAAI,QAAQ;AAAA,UAAA;AAAA,QACnB;AAAA,MACF,CACD;AAAA,IAAA,CACF;AAAA,EACL;AAEA,MAAI,CAAC,eAAe,iBAAiB,CAAC,oBAAoB;AACjD,WAAA;AAAA,EAAA;AAIP,SAAAQ,gCAACC,aAAAA,MAAM,MAAN,EACC,UAAA;AAAA,IAAAP,+BAACO,aAAAA,MAAM,SAAN,EACC,UAAAP,2BAAAA,IAACC,uBAAO,OAAO,aAAa,WAAWD,+BAACQ,MAAAA,SAAQ,CAAA,CAAA,GAAI,SAAQ,UACzD,qBACH,CAAA,GACF;AAAA,IACAF,2BAAAA,KAACC,aAAM,MAAA,SAAN,EACC,UAAA;AAAA,MAACP,2BAAAA,IAAAO,aAAA,MAAM,QAAN,EACC,UAAAP,2BAAA,IAACO,mBAAM,OAAN,EAAa,sBAAW,EAC3B,CAAA;AAAA,qCACCA,aAAM,MAAA,MAAN,EACC,UAACP,2BAAAA,IAAAS,aAAA,YAAA,EAAW,SAAQ,SACjB,UAAA;AAAA,QACC;AAAA,UACE,IAAI;AAAA,UACJ,gBAAgB;AAAA,QAClB;AAAA,QACA,EAAE,KAAK,IAAI,QAAQ,IAAI;AAAA,SAE3B,EACF,CAAA;AAAA,MACAH,2BAAAA,KAACC,aAAM,MAAA,QAAN,EACC,UAAA;AAAA,QAAAP,2BAAAA,IAACO,mBAAM,OAAN,EACC,yCAACN,aAAO,QAAA,EAAA,SAAQ,YACb,UAAc,cAAA;AAAA,UACb,IAAI;AAAA,UACJ,gBAAgB;AAAA,QAAA,CACjB,GACH,EACF,CAAA;AAAA,QACAD,+BAACO,aAAAA,MAAM,OAAN,EACC,yCAAC,aAAY,EAAA,SAAS,YAAY,EACpC,CAAA;AAAA,MAAA,EACF,CAAA;AAAA,IAAA,EACF,CAAA;AAAA,EAAA,GACF;AAEJ;AClJA,SAAS,mBAAmB;AACpB,QAAA,EAAE,YAAY,IAAIG,wCAAyB;AAC3C,QAAA,EAAE,cAAc,IAAIX,kBAAQ;AAC5B,QAAA,WAAW,aAAa,KAAK;AAGjC,SAAAC,2BAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,YAAY,cAAc;AAAA,QACxB,IAAI;AAAA,QACJ,gBAAgB;AAAA,MAAA,CACjB;AAAA,MACD;AAAA,IAAA;AAAA,EACD;AAEL;ACdA,SAAS,oBAAoB;AACrB,QAAA,EAAE,cAAc,IAAID,kBAAQ;AAClC,QAAM,EAAE,IAAI,cAAc,YAAA,IAAgBW,MAAAA,kCAAyB;AACnE,QAAM,WAAW,eAAe,aAAa,KAAK,eAAe;AACjE,QAAM,kBAAkB,eACpB,aAAa,KAAK,eAClB,aAAa,KAAK;AAGpB,SAAAV,2BAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAY;AAAA,MACZ,YAAY,cAAc;AAAA,QACxB,IAAI;AAAA,QACJ,gBAAgB;AAAA,MAAA,CACjB;AAAA,MACD;AAAA,MACA;AAAA,IAAA;AAAA,EACD;AAEL;AClBA,MAAe,QAAA;AAAA,EACb,SAAS,KAAU;AACjB,QAAI,eAAe;AAAA,MACjB,IAAI;AAAA,MACJ,aAAa;AAAA,MACb,SAAS;AAAA,MACT,MAAM;AAAA,IAAA,CACP;AAED,QAAI,UAAU,iBAAiB,EAAE,gBAAgB,YAAY,WAAW;AAAA,MACtE,MAAM;AAAA,MACN,WAAW;AAAA,IAAA,CACZ;AAED,QAAI,UAAU,iBAAiB,EAAE,gBAAgB,YAAY,eAAe;AAAA,MAC1E,MAAM;AAAA,MACN,WAAW;AAAA,IAAA,CACZ;AAAA,EACH;AAAA,EAEA,MAAM,cAAc,EAAE,WAAkC;AACtD,WAAO,QAAQ;AAAA,MACb,QAAQ,IAAI,OAAO,WAAW;AACxB,YAAA;AACF,gBAAM,EAAE,SAAS,KAAA,IAAS,MAAM,qCAAA,uBAAA,OAAA,EAAA,0BAAA,MAAA,QAAA,QAAA,EAAA,KAAA,MAAA,QAAA,2BAAA,CAAA,GAAA,0BAAA,MAAA,qCAAA,2BAAA,CAAA,EAAA,CAAA,GAAA,kBAAA,MAAA,SAAA,CAAA;AAEzB,iBAAA,EAAE,MAAM,OAAO;AAAA,QAAA,QAChB;AACN,iBAAO,EAAE,MAAM,CAAC,GAAG,OAAO;AAAA,QAAA;AAAA,MAE7B,CAAA;AAAA,IACH;AAAA,EAAA;AAEJ;;"}
|
package/dist/admin/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../admin/src/pluginId.ts","../../admin/src/components/Initializer.tsx","../../admin/src/permission.ts","../../admin/src/components/PurgeModal/PurgeButton/index.tsx","../../admin/src/components/PurgeModal/index.tsx","../../admin/src/components/PurgeCacheButton/index.tsx","../../admin/src/components/PurgeEntityButton/index.tsx","../../admin/src/index.ts"],"sourcesContent":["export const PLUGIN_ID = 'strapi-cache';\n","import { useEffect, useRef } from 'react';\n\nimport { PLUGIN_ID } from '../pluginId';\n\ntype InitializerProps = {\n setPlugin: (id: string) => void;\n};\n\nconst Initializer = ({ setPlugin }: InitializerProps) => {\n const ref = useRef(setPlugin);\n\n useEffect(() => {\n ref.current(PLUGIN_ID);\n }, []);\n\n return null;\n};\n\nexport { Initializer };\n","export const pluginPermissions = {\n purge: [{ action: 'plugin::strapi-cache.purge-cache', subject: null }],\n};\n","import { Button } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\n\nfunction PurgeButton({ onClick }: { onClick: () => void }) {\n const formatMessage = useIntl().formatMessage;\n\n return (\n <Button onClick={onClick}>\n {formatMessage({\n id: 'strapi-cache.cache.confirm',\n defaultMessage: 'Yes, confirm',\n })}\n </Button>\n );\n}\n\nexport default PurgeButton;\n","import { useIntl } from 'react-intl';\nimport { Archive } from '@strapi/icons';\nimport { Button, Modal } from '@strapi/design-system';\nimport { useNotification, useRBAC } from '@strapi/strapi/admin';\nimport { pluginPermissions } from '../../permission';\nimport { Typography } from '@strapi/design-system';\nimport { useFetchClient } from '@strapi/strapi/admin';\nimport { useEffect, useState } from 'react';\nimport PurgeButton from './PurgeButton';\n\nexport type PurgeProps = {\n buttonText: string;\n buttonWidth?: string;\n keyToUse?: string;\n contentTypeName?: string;\n};\n\nfunction PurgeModal({ buttonText, keyToUse, buttonWidth, contentTypeName }: PurgeProps) {\n const { allowedActions } = useRBAC(pluginPermissions);\n const formatMessage = useIntl().formatMessage;\n const { post, get } = useFetchClient();\n const { toggleNotification } = useNotification();\n const [cacheableRoutes, setCacheableRoutes] = useState<string[]>();\n\n useEffect(() => {\n if (!allowedActions.canPurgeCache) {\n return;\n }\n const fetchCacheableRoutes = async () => {\n try {\n const { data } = await get('/strapi-cache/cacheable-routes');\n return data;\n } catch (error) {\n toggleNotification({\n type: 'warning',\n message: formatMessage({\n id: 'strapi-cache.cache.routes.fetch-error',\n defaultMessage: 'Unable to fetch cacheable routes. Cache purge may not work correctly.',\n }),\n });\n return undefined;\n }\n };\n fetchCacheableRoutes().then((data) => {\n setCacheableRoutes(data);\n });\n }, [allowedActions.canPurgeCache]);\n\n const isCacheableRoute = () => {\n if (!keyToUse || !cacheableRoutes) {\n return false;\n }\n\n return (\n cacheableRoutes.length === 0 ||\n cacheableRoutes.some((route) => {\n return route.includes(keyToUse) || (contentTypeName && route.includes(contentTypeName));\n })\n );\n };\n\n const clearCache = () => {\n if (!keyToUse) {\n toggleNotification({\n type: 'warning',\n message: formatMessage({\n id: 'strapi-cache.cache.purge.no-content-type',\n defaultMessage: 'No content type found',\n }),\n });\n return;\n }\n\n post(`/strapi-cache/purge-cache/${keyToUse}`, undefined, {\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n .then(() => {\n toggleNotification({\n type: 'success',\n message: formatMessage(\n {\n id: 'strapi-cache.cache.purge.success',\n defaultMessage: 'Cache purged successfully',\n },\n {\n key: `\"${keyToUse}\"`,\n }\n ),\n });\n })\n .catch(() => {\n toggleNotification({\n type: 'danger',\n message: formatMessage(\n {\n id: 'strapi-cache.cache.purge.error',\n defaultMessage: 'Error purging cache',\n },\n {\n key: `\"${keyToUse}\"`,\n }\n ),\n });\n });\n };\n\n if (!allowedActions.canPurgeCache || !isCacheableRoute()) {\n return null;\n }\n\n return (\n <Modal.Root>\n <Modal.Trigger>\n <Button width={buttonWidth} startIcon={<Archive />} variant=\"danger\">\n {buttonText}\n </Button>\n </Modal.Trigger>\n <Modal.Content>\n <Modal.Header>\n <Modal.Title>{buttonText}</Modal.Title>\n </Modal.Header>\n <Modal.Body>\n <Typography variant=\"omega\">\n {formatMessage(\n {\n id: 'strapi-cache.cache.purge.confirmation',\n defaultMessage: 'Are you sure you want to purge the cache?',\n },\n { key: `\"${keyToUse}\"` }\n )}\n </Typography>\n </Modal.Body>\n <Modal.Footer>\n <Modal.Close>\n <Button variant=\"tertiary\">\n {formatMessage({\n id: 'strapi-cache.cache.cancel',\n defaultMessage: 'No, cancel',\n })}\n </Button>\n </Modal.Close>\n <Modal.Close>\n <PurgeButton onClick={clearCache}></PurgeButton>\n </Modal.Close>\n </Modal.Footer>\n </Modal.Content>\n </Modal.Root>\n );\n}\n\nexport default PurgeModal;\n","import { unstable_useContentManagerContext as useContentManagerContext } from '@strapi/strapi/admin';\nimport PurgeModal from '../PurgeModal';\nimport { useIntl } from 'react-intl';\n\nfunction PurgeCacheButton() {\n const { contentType } = useContentManagerContext();\n const { formatMessage } = useIntl();\n const keyToUse = contentType?.info.pluralName;\n\n return (\n <PurgeModal\n buttonText={formatMessage({\n id: 'strapi-cache.cache.purge',\n defaultMessage: 'Purge Cache',\n })}\n keyToUse={keyToUse}\n ></PurgeModal>\n );\n}\n\nexport default PurgeCacheButton;\n","import { unstable_useContentManagerContext as useContentManagerContext } from '@strapi/strapi/admin';\nimport PurgeModal from '../PurgeModal';\nimport { useIntl } from 'react-intl';\n\nfunction PurgeEntityButton() {\n const { formatMessage } = useIntl();\n const { id, isSingleType, contentType } = useContentManagerContext();\n const keyToUse = isSingleType ? contentType?.info.singularName : id;\n const contentTypeName = isSingleType\n ? contentType?.info.singularName\n : contentType?.info.pluralName;\n\n return (\n <PurgeModal\n buttonWidth=\"100%\"\n buttonText={formatMessage({\n id: 'strapi-cache.cache.purge.entity',\n defaultMessage: 'Purge Entity Cache',\n })}\n keyToUse={keyToUse}\n contentTypeName={contentTypeName}\n ></PurgeModal>\n );\n}\n\nexport default PurgeEntityButton;\n","import { PLUGIN_ID } from './pluginId';\nimport { Initializer } from './components/Initializer';\nimport PurgeCacheButton from './components/PurgeCacheButton';\nimport PurgeEntityButton from './components/PurgeEntityButton';\n\nexport default {\n register(app: any) {\n app.registerPlugin({\n id: PLUGIN_ID,\n initializer: Initializer,\n isReady: false,\n name: PLUGIN_ID,\n });\n\n app.getPlugin('content-manager').injectComponent('listView', 'actions', {\n name: PurgeCacheButton,\n Component: PurgeCacheButton,\n });\n\n app.getPlugin('content-manager').injectComponent('editView', 'right-links', {\n name: PurgeEntityButton,\n Component: PurgeEntityButton,\n });\n },\n\n async registerTrads({ locales }: { locales: string[] }) {\n return Promise.all(\n locales.map(async (locale) => {\n try {\n const { default: data } = await import(`./translations/${locale}.json`);\n\n return { data, locale };\n } catch {\n return { data: {}, locale };\n }\n })\n );\n },\n};\n"],"names":["useContentManagerContext"],"mappings":"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"}
|
package/dist/server/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../server/src/utils/log.ts","../../server/src/utils/invalidateCache.ts","../../server/src/permissions.ts","../../server/src/bootstrap.ts","../../server/src/utils/key.ts","../../server/src/utils/body.ts","../../server/src/utils/header.ts","../../server/src/middlewares/cache.ts","../../server/src/middlewares/graphql.ts","../../server/src/middlewares/index.ts","../../server/src/register.ts","../../server/src/config/index.ts","../../server/src/content-types/index.ts","../../server/src/controllers/controller.ts","../../server/src/controllers/index.ts","../../server/src/policies/index.ts","../../server/src/routes/purge.ts","../../server/src/routes/index.ts","../../server/src/utils/withTimeout.ts","../../server/src/services/memory/provider.ts","../../server/src/services/redis/provider.ts","../../server/src/services/resolver.ts","../../server/src/services/index.ts","../../server/src/index.ts"],"sourcesContent":["export const loggy = {\n info: (msg: string) => {\n const shouldDebug = strapi.plugin('strapi-cache').config('debug') ?? false;\n if (!shouldDebug) {\n return;\n }\n strapi.log.info(`[STRAPI CACHE] ${msg}`);\n },\n error: (msg: string) => {\n const shouldDebug = strapi.plugin('strapi-cache').config('debug') ?? false;\n if (!shouldDebug) {\n return;\n }\n strapi.log.error(`[STRAPI CACHE] ${msg}`);\n },\n warn: (msg: string) => {\n const shouldDebug = strapi.plugin('strapi-cache').config('debug') ?? false;\n if (!shouldDebug) {\n return;\n }\n strapi.log.warn(`[STRAPI CACHE] ${msg}`);\n },\n};\n","import { Core } from '@strapi/strapi';\nimport { CacheProvider } from 'src/types/cache.types';\nimport { loggy } from './log';\n\nexport async function invalidateCache(event: any, cacheStore: CacheProvider, strapi: Core.Strapi) {\n const { model } = event;\n const uid = model.uid;\n\n try {\n const contentType = strapi.contentType(uid);\n\n if (!contentType || !contentType.kind) {\n loggy.info(`Content type ${uid} not found`);\n return;\n }\n\n const pluralName =\n contentType.kind === 'singleType'\n ? contentType.info.singularName\n : contentType.info.pluralName;\n const apiPath = `/api/${pluralName}`;\n const regex = new RegExp(`^.*:${apiPath}(/.*)?(\\\\?.*)?$`);\n\n await cacheStore.clearByRegexp([regex]);\n loggy.info(`Invalidated cache for ${apiPath}`);\n } catch (error) {\n loggy.error('Cache invalidation error:');\n loggy.error(error);\n }\n}\n\nexport async function invalidateGraphqlCache(\n event: any,\n cacheStore: CacheProvider,\n strapi: Core.Strapi\n) {\n try {\n const graphqlRegex = new RegExp(`^POST:\\/graphql(:.*)?$`);\n\n await cacheStore.clearByRegexp([graphqlRegex]);\n loggy.info(`Invalidated cache for ${graphqlRegex}`);\n } catch (error) {\n loggy.error('Cache invalidation error:');\n loggy.error(error);\n }\n}\n","export const actions = [\n {\n section: 'plugins',\n displayName: 'Purge Cache',\n uid: 'purge-cache',\n pluginName: 'strapi-cache',\n },\n];\n","import type { Core } from '@strapi/strapi';\nimport { invalidateCache, invalidateGraphqlCache } from './utils/invalidateCache';\nimport { CacheService } from './types/cache.types';\nimport { loggy } from './utils/log';\nimport { actions } from './permissions';\n\nconst bootstrap = ({ strapi }: { strapi: Core.Strapi }) => {\n loggy.info('Initializing');\n try {\n const cacheService = strapi.plugin('strapi-cache').services.service as CacheService;\n const autoPurgeCache = strapi.plugin('strapi-cache').config('autoPurgeCache') as boolean;\n const cacheStore = cacheService.getCacheInstance();\n\n if (!cacheStore) {\n loggy.error('Plugin could not be initialized');\n return;\n }\n\n cacheStore.init();\n\n if (autoPurgeCache) {\n strapi.db.lifecycles.subscribe({\n async afterCreate(event) {\n await invalidateCache(event, cacheStore, strapi);\n await invalidateGraphqlCache(event, cacheStore, strapi);\n },\n async afterUpdate(event) {\n await invalidateCache(event, cacheStore, strapi);\n await invalidateGraphqlCache(event, cacheStore, strapi);\n },\n async afterDelete(event) {\n await invalidateCache(event, cacheStore, strapi);\n await invalidateGraphqlCache(event, cacheStore, strapi);\n },\n });\n }\n } catch (error) {\n loggy.error('Plugin could not be initialized');\n return;\n }\n loggy.info('Plugin initialized');\n\n strapi.admin.services.permission.actionProvider.registerMany(actions);\n};\n\nexport default bootstrap;\n","import { createHash } from 'crypto';\nimport { Context } from 'koa';\n\nexport const generateCacheKey = (context: Context) => {\n const { url } = context.request;\n const { method } = context.request;\n\n return `${method}:${url}`;\n};\n\nexport const generateGraphqlCacheKey = (payload: string) => {\n const hash = createHash('sha256').update(payload).digest('base64url');\n return `POST:/graphql:${hash}`;\n};\n","import { Stream } from 'stream';\nimport { createGunzip, createBrotliDecompress, createInflate } from 'zlib';\n\nexport const streamToBuffer = (stream: Stream): Promise<Buffer> => {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n stream.on('data', (chunk) => chunks.push(chunk));\n stream.on('end', () => resolve(Buffer.concat(chunks)));\n stream.on('error', reject);\n });\n};\n\nexport const decompressBuffer = async (buffer: Buffer, encoding?: string): Promise<Buffer> => {\n return new Promise((resolve, reject) => {\n let decompressStream;\n switch (encoding) {\n case 'gzip':\n decompressStream = createGunzip();\n break;\n case 'br':\n decompressStream = createBrotliDecompress();\n break;\n case 'deflate':\n decompressStream = createInflate();\n break;\n default:\n return resolve(buffer);\n }\n\n const chunks: Buffer[] = [];\n decompressStream.on('data', (chunk) => chunks.push(chunk));\n decompressStream.on('end', () => resolve(Buffer.concat(chunks)));\n decompressStream.on('error', reject);\n\n decompressStream.end(buffer);\n });\n};\n\nexport const decodeBufferToText = (buffer: Buffer): string => {\n const decoder = new TextDecoder('utf-8');\n return decoder.decode(buffer);\n};\n","import { Context } from 'koa';\nimport { OutgoingHttpHeaders } from 'http';\n\nexport function getHeadersToStore(\n ctx: Context,\n cacheHeaders: boolean,\n cacheHeadersAllowList: string[] = [],\n cacheHeadersDenyList: string[] = []\n): OutgoingHttpHeaders | null {\n let headersToStore: OutgoingHttpHeaders | null = null;\n\n if (cacheHeaders) {\n let headers = ctx.response.headers;\n\n if (cacheHeadersAllowList.length) {\n headers = Object.fromEntries(\n Object.entries(headers).filter(([key]) => cacheHeadersAllowList.includes(key.toLowerCase()))\n );\n }\n\n if (cacheHeadersDenyList.length) {\n headers = Object.fromEntries(\n Object.entries(headers).filter(([key]) => !cacheHeadersDenyList.includes(key.toLowerCase()))\n );\n }\n\n headersToStore = headers;\n }\n\n return headersToStore;\n}\n\nexport function getCacheHeaderConfig() {\n const cacheHeaders = strapi.plugin('strapi-cache').config('cacheHeaders') as boolean;\n const cacheHeadersDenyList = strapi\n .plugin('strapi-cache')\n .config('cacheHeadersDenyList') as string[];\n const cacheHeadersAllowList = strapi\n .plugin('strapi-cache')\n .config('cacheHeadersAllowList') as string[];\n const cacheAuthorizedRequests = strapi\n .plugin('strapi-cache')\n .config('cacheAuthorizedRequests') as boolean;\n\n return {\n cacheHeaders,\n cacheHeadersDenyList,\n cacheHeadersAllowList,\n cacheAuthorizedRequests,\n };\n}\n","import { Context } from 'koa';\nimport { generateCacheKey } from '../utils/key';\nimport { CacheService } from '../../src/types/cache.types';\nimport { loggy } from '../utils/log';\nimport Stream from 'stream';\nimport { decodeBufferToText, decompressBuffer, streamToBuffer } from '../utils/body';\nimport { getCacheHeaderConfig, getHeadersToStore } from '../utils/header';\n\nconst middleware = async (ctx: Context, next: any) => {\n const cacheService = strapi.plugin('strapi-cache').services.service as CacheService;\n const cacheableRoutes = strapi.plugin('strapi-cache').config('cacheableRoutes') as string[];\n const { cacheHeaders, cacheHeadersDenyList, cacheHeadersAllowList, cacheAuthorizedRequests } =\n getCacheHeaderConfig();\n const cacheStore = cacheService.getCacheInstance();\n const { url } = ctx.request;\n const key = generateCacheKey(ctx);\n const cacheEntry = await cacheStore.get(key);\n const cacheControlHeader = ctx.request.headers['cache-control'];\n const noCache = cacheControlHeader && cacheControlHeader.includes('no-cache');\n const routeIsCachable =\n cacheableRoutes.some((route) => url.startsWith(route)) ||\n (cacheableRoutes.length === 0 && url.startsWith('/api'));\n const authorizationHeader = ctx.request.headers['authorization'];\n\n if (authorizationHeader && !cacheAuthorizedRequests) {\n loggy.info(`Authorized request bypassing cache: ${key}`);\n await next();\n return;\n }\n\n if (cacheEntry && !noCache) {\n loggy.info(`HIT with key: ${key}`);\n ctx.status = 200;\n ctx.body = cacheEntry.body;\n if (cacheHeaders) {\n ctx.set(cacheEntry.headers);\n }\n return;\n }\n\n await next();\n\n if (ctx.method === 'GET' && ctx.status >= 200 && ctx.status < 300 && routeIsCachable) {\n loggy.info(`MISS with key: ${key}`);\n const headersToStore = getHeadersToStore(\n ctx,\n cacheHeaders,\n cacheHeadersAllowList,\n cacheHeadersDenyList\n );\n\n if (ctx.body instanceof Stream) {\n const buf = await streamToBuffer(ctx.body);\n const contentEncoding = ctx.response.headers['content-encoding'];\n const decompressed = await decompressBuffer(buf, contentEncoding);\n const responseText = decodeBufferToText(decompressed);\n\n await cacheStore.set(key, { body: responseText, headers: headersToStore });\n ctx.body = buf;\n } else {\n await cacheStore.set(key, { body: ctx.body, headers: headersToStore });\n }\n }\n};\n\nexport default middleware;\n","import rawBody from 'raw-body';\nimport { generateGraphqlCacheKey } from '../utils/key';\nimport Stream, { Readable } from 'stream';\nimport { loggy } from '../utils/log';\nimport { CacheService } from '../types/cache.types';\nimport { decodeBufferToText, decompressBuffer, streamToBuffer } from '../utils/body';\nimport { getCacheHeaderConfig, getHeadersToStore } from '../utils/header';\n\nconst middleware = async (ctx: any, next: any) => {\n const cacheService = strapi.plugin('strapi-cache').services.service as CacheService;\n const { cacheHeaders, cacheHeadersDenyList, cacheHeadersAllowList, cacheAuthorizedRequests } =\n getCacheHeaderConfig();\n const cacheStore = cacheService.getCacheInstance();\n const { url } = ctx.request;\n\n const originalReq = ctx.req;\n const bodyBuffer = await rawBody(originalReq);\n const body = bodyBuffer.toString();\n\n const clonedReq = new Readable();\n clonedReq.push(bodyBuffer);\n clonedReq.push(null);\n\n (clonedReq as any).headers = { ...originalReq.headers };\n (clonedReq as any).method = originalReq.method;\n (clonedReq as any).url = originalReq.url;\n (clonedReq as any).httpVersion = originalReq.httpVersion;\n (clonedReq as any).socket = originalReq.socket;\n (clonedReq as any).connection = originalReq.connection;\n\n ctx.req = clonedReq;\n ctx.request.req = clonedReq;\n\n const isIntrospectionQuery = body.includes('IntrospectionQuery');\n if (isIntrospectionQuery) {\n loggy.info('Skipping cache for introspection query');\n await next();\n return;\n }\n\n const key = generateGraphqlCacheKey(body);\n const cacheEntry = await cacheStore.get(key);\n const cacheControlHeader = ctx.request.headers['cache-control'];\n const noCache = cacheControlHeader && cacheControlHeader.includes('no-cache');\n const authorizationHeader = ctx.request.headers['authorization'];\n\n if (authorizationHeader && !cacheAuthorizedRequests) {\n loggy.info(`Authorized request bypassing cache: ${key}`);\n await next();\n return;\n }\n\n if (cacheEntry && !noCache) {\n loggy.info(`HIT with key: ${key}`);\n ctx.status = 200;\n ctx.body = cacheEntry.body;\n if (cacheHeaders) {\n ctx.set(cacheEntry.headers);\n }\n return;\n }\n\n await next();\n\n if (\n ctx.method === 'POST' &&\n ctx.status >= 200 &&\n ctx.status < 300 &&\n url.startsWith('/graphql')\n ) {\n loggy.info(`MISS with key: ${key}`);\n const headers = ctx.request.headers;\n const authorizationHeader = headers['authorization'];\n\n if (authorizationHeader && !cacheAuthorizedRequests) {\n loggy.info(`Authorized request not caching: ${key}`);\n return;\n }\n\n const headersToStore = getHeadersToStore(\n ctx,\n cacheHeaders,\n cacheHeadersAllowList,\n cacheHeadersDenyList\n );\n\n if (ctx.body instanceof Stream) {\n const buf = await streamToBuffer(ctx.body);\n const contentEncoding = ctx.response.headers['content-encoding'];\n const decompressed = await decompressBuffer(buf, contentEncoding);\n const responseText = decodeBufferToText(decompressed);\n\n await cacheStore.set(key, { body: responseText, headers: headersToStore });\n ctx.body = buf;\n } else {\n await cacheStore.set(key, { body: ctx.body, headers: headersToStore });\n }\n }\n};\n\nexport default middleware;\n","import cache from './cache';\nimport graphql from './graphql';\n\nexport default {\n graphql,\n cache,\n};\n","import type { Core } from '@strapi/strapi';\nimport middlewares from './middlewares';\n\nconst register = ({ strapi }: { strapi: Core.Strapi }) => {\n strapi.server.use(middlewares.cache);\n strapi.server.use(middlewares.graphql);\n};\n\nexport default register;\n","export default {\n default: ({ env }) => ({\n debug: false,\n max: 1000,\n ttl: 1000 * 60 * 60,\n size: 1024 * 1024 * 10,\n allowStale: false,\n cacheableRoutes: [],\n provider: 'memory',\n redisConfig: env('REDIS_URL'),\n redisClusterNodes: [],\n redisClusterOptions: {},\n cacheHeaders: true,\n cacheHeadersDenyList: [],\n cacheHeadersAllowList: [],\n cacheAuthorizedRequests: false,\n cacheGetTimeoutInMs: 1000,\n autoPurgeCache: true,\n }),\n validator: (config) => {\n if (typeof config.debug !== 'boolean') {\n throw new Error(`Invalid config: debug must be a boolean`);\n }\n if (typeof config.max !== 'number') {\n throw new Error(`Invalid config: max must be a number`);\n }\n if (typeof config.ttl !== 'number') {\n throw new Error(`Invalid config: ttl must be a number`);\n }\n if (typeof config.size !== 'number') {\n throw new Error(`Invalid config: size must be a number`);\n }\n if (typeof config.allowStale !== 'boolean') {\n throw new Error(`Invalid config: allowStale must be a boolean`);\n }\n if (\n !Array.isArray(config.cacheableRoutes) ||\n config.cacheableRoutes.some((item) => typeof item !== 'string')\n ) {\n throw new Error(`Invalid config: cacheableRoutes must be an string array`);\n }\n if (typeof config.provider !== 'string') {\n throw new Error(`Invalid config: provider must be a string`);\n }\n if (config.provider !== 'memory' && config.provider !== 'redis') {\n throw new Error(`Invalid config: provider must be 'memory' or 'redis'`);\n }\n if (config.provider === 'redis') {\n if (!config.redisConfig) {\n throw new Error(`Invalid config: redisConfig must be set when using redis provider`);\n }\n if (typeof config.redisConfig !== 'string' && typeof config.redisConfig !== 'object') {\n throw new Error(`Invalid config: redisConfig must be a string or object when using redis provider`);\n }\n if (\n !Array.isArray(config.redisClusterNodes) ||\n config.redisClusterNodes.some((item) => !('host' in item && 'port' in item))\n ) {\n throw new Error(\n `Invalid config: redisClusterNodes must be as a list of objects with keys 'host' and 'port'`\n );\n }\n if (typeof config.redisClusterOptions !== 'object') {\n throw new Error(`Invalid config: redisClusterOptions must be an object`);\n }\n }\n if (typeof config.cacheHeaders !== 'boolean') {\n throw new Error(`Invalid config: cacheHeaders must be a boolean`);\n }\n if (\n !Array.isArray(config.cacheHeadersDenyList) ||\n config.cacheHeadersDenyList.some((item) => typeof item !== 'string')\n ) {\n throw new Error(`Invalid config: cacheHeadersDenyList must be an string array`);\n }\n if (\n !Array.isArray(config.cacheHeadersAllowList) ||\n config.cacheHeadersAllowList.some((item) => typeof item !== 'string')\n ) {\n throw new Error(`Invalid config: cacheHeadersAllowList must be an string array`);\n }\n if (typeof config.cacheAuthorizedRequests !== 'boolean') {\n throw new Error(`Invalid config: cacheAuthorizedRequests must be a boolean`);\n }\n if (typeof config.cacheGetTimeoutInMs !== 'number') {\n throw new Error(`Invalid config: cacheGetTimeoutInMs must be a number`);\n }\n if (typeof config.autoPurgeCache !== 'boolean') {\n throw new Error(`Invalid config: autoPurgeCache must be a boolean`);\n }\n },\n};\n","export default {};\n","import type { Core } from '@strapi/strapi';\nimport { Context } from 'koa';\nimport { CacheService } from 'src/types/cache.types';\n\nconst controller = ({ strapi }: { strapi: Core.Strapi }) => ({\n async purgeCache(ctx: Context) {\n const service = strapi.plugin('strapi-cache').service('service') as CacheService;\n\n await service.getCacheInstance().reset();\n\n ctx.body = {\n message: 'Cache purged successfully',\n };\n },\n async purgeCacheByKey(ctx: Context) {\n const { key } = ctx.params;\n const service = strapi.plugin('strapi-cache').service('service') as CacheService;\n const regex = new RegExp(`(^|\\/)${key}(\\/|\\\\?|$)`);\n\n await service.getCacheInstance().clearByRegexp([regex]);\n\n ctx.body = {\n message: `Cache purged successfully for key: ${key}`,\n };\n },\n async cacheableRoutes(ctx: Context) {\n const cacheableRoutes = strapi.plugin('strapi-cache').config('cacheableRoutes');\n ctx.body = cacheableRoutes;\n },\n});\n\nexport default controller;\n","import controller from './controller';\n\nexport default {\n controller,\n};\n","export default {};\n","export default [\n {\n method: 'POST',\n path: '/purge-cache',\n handler: 'controller.purgeCache',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'plugin::content-manager.hasPermissions',\n config: {\n actions: ['plugin::strapi-cache.purge-cache'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/purge-cache/:key',\n handler: 'controller.purgeCacheByKey',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'plugin::content-manager.hasPermissions',\n config: {\n actions: ['plugin::strapi-cache.purge-cache'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/cacheable-routes',\n handler: 'controller.cacheableRoutes',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'plugin::content-manager.hasPermissions',\n config: {\n actions: ['plugin::strapi-cache.purge-cache'],\n },\n },\n ],\n },\n },\n];\n","import purgeRoute from './purge';\n\nconst routes = {\n 'purge-route': {\n type: 'admin',\n routes: purgeRoute,\n },\n};\n\nexport default routes;\n","export const withTimeout = (callback: () => Promise<any>, ms: number) => {\n let timeout: NodeJS.Timeout | null = null;\n\n return Promise.race([\n callback().then((result) => {\n if (timeout) {\n clearTimeout(timeout);\n }\n return result;\n }),\n new Promise((_, reject) => {\n timeout = setTimeout(() => {\n reject(new Error('timeout'));\n }, ms);\n }),\n ]);\n};\n","import type { Core } from '@strapi/strapi';\nimport { LRUCache } from 'lru-cache';\nimport { withTimeout } from '../../utils/withTimeout';\nimport { CacheProvider, CacheService } from '../../types/cache.types';\nimport { loggy } from '../../utils/log';\n\nexport class InMemoryCacheProvider implements CacheProvider {\n private initialized = false;\n private provider!: LRUCache<string, any>;\n private cacheGetTimeoutInMs: number;\n\n constructor(private strapi: Core.Strapi) {}\n\n init(): void {\n if (this.initialized) {\n loggy.error('Provider already initialized');\n return;\n }\n\n this.initialized = true;\n\n const max = Number(this.strapi.plugin('strapi-cache').config('max'));\n const ttl = Number(this.strapi.plugin('strapi-cache').config('ttl'));\n const size = Number(this.strapi.plugin('strapi-cache').config('size'));\n const allowStale = Boolean(this.strapi.plugin('strapi-cache').config('allowStale'));\n\n this.provider = new LRUCache({\n max,\n ttl,\n size,\n allowStale,\n });\n\n this.cacheGetTimeoutInMs = Number(\n this.strapi.plugin('strapi-cache').config('cacheGetTimeoutInMs')\n );\n\n loggy.info('Provider initialized');\n }\n\n get ready(): boolean {\n if (!this.initialized) {\n loggy.info('Provider not initialized');\n return false;\n }\n\n return true;\n }\n\n async get(key: string): Promise<any | null> {\n if (!this.ready) return null;\n\n return withTimeout(\n () =>\n new Promise((resolve) => {\n resolve(this.provider.get(key));\n }),\n this.cacheGetTimeoutInMs\n ).catch((error) => {\n loggy.error(`Error during get: ${error?.message || error}`);\n return null;\n });\n }\n\n async set(key: string, val: any): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n return this.provider.set(key, val);\n } catch (error) {\n loggy.error(`Error during set: ${error}`);\n return null;\n }\n }\n\n async del(key: string): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n loggy.info(`PURGING KEY: ${key}`);\n return this.provider.delete(key);\n } catch (error) {\n loggy.error(`Error during delete: ${error}`);\n return null;\n }\n }\n\n async keys(): Promise<string[] | null> {\n if (!this.ready) return null;\n\n try {\n return Array.from(this.provider.keys());\n } catch (error) {\n loggy.error(`Error fetching keys: ${error}`);\n return null;\n }\n }\n\n async reset(): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n const allKeys = await this.keys();\n if (!allKeys) return null;\n\n loggy.info(`PURGING ALL KEYS: ${allKeys.length}`);\n await Promise.all(allKeys.map((key) => this.del(key)));\n return true;\n } catch (error) {\n loggy.error(`Error during reset: ${error}`);\n return null;\n }\n }\n\n async clearByRegexp(regExps: RegExp[]): Promise<void> {\n const keys = (await this.keys()) || [];\n const matches = keys.filter((key) => regExps.some((re) => re.test(key)));\n await Promise.all(matches.map((key) => this.del(key)));\n }\n}\n","import type { Core } from '@strapi/strapi';\nimport { Redis, Cluster, ClusterNode, ClusterOptions } from 'ioredis';\nimport { withTimeout } from '../../utils/withTimeout';\nimport { CacheProvider } from '../../types/cache.types';\nimport { loggy } from '../../utils/log';\n\nexport class RedisCacheProvider implements CacheProvider {\n private initialized = false;\n private client!: Redis | Cluster;\n private cacheGetTimeoutInMs: number;\n\n constructor(private strapi: Core.Strapi) { }\n\n init(): void {\n if (this.initialized) {\n loggy.error('Redis provider already initialized');\n return;\n }\n try {\n const redisConfig =\n this.strapi.plugin('strapi-cache').config('redisConfig') || 'redis://localhost:6379';\n const redisClusterNodes: ClusterNode[] =\n this.strapi.plugin('strapi-cache').config('redisClusterNodes');\n this.cacheGetTimeoutInMs = Number(\n this.strapi.plugin('strapi-cache').config('cacheGetTimeoutInMs')\n );\n if (redisClusterNodes.length) {\n const redisClusterOptions: ClusterOptions =\n this.strapi.plugin('strapi-cache').config('redisClusterOptions');\n if (!redisClusterOptions['redisOptions']) {\n redisClusterOptions.redisOptions = redisConfig;\n }\n this.client = new Redis.Cluster(redisClusterNodes, redisClusterOptions);\n } else {\n this.client = new Redis(redisConfig);\n }\n this.initialized = true;\n\n loggy.info('Redis provider initialized');\n } catch (error) {\n loggy.error(error);\n }\n }\n\n get ready(): boolean {\n if (!this.initialized) {\n loggy.info('Redis provider not initialized');\n return false;\n }\n\n return true;\n }\n\n async get(key: string): Promise<any | null> {\n if (!this.ready) return null;\n\n return withTimeout(() => this.client.get(key), this.cacheGetTimeoutInMs)\n .then((data) => (data ? JSON.parse(data) : null))\n .catch((error) => {\n loggy.error(`Redis get error: ${error?.message || error}`);\n return null;\n });\n }\n\n async set(key: string, val: any): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n // plugin ttl is ms, ioredis ttl is s, so we convert here\n const ttlInMs = Number(this.strapi.plugin('strapi-cache').config('ttl'));\n const ttlInS = Number((ttlInMs/1000).toFixed());\n const serialized = JSON.stringify(val);\n if (ttlInS > 0) {\n await this.client.set(key, serialized, 'EX', ttlInS);\n } else {\n await this.client.set(key, serialized);\n }\n return val;\n } catch (error) {\n loggy.error(`Redis set error: ${error}`);\n return null;\n }\n }\n\n async del(key: string): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n loggy.info(`Redis PURGING KEY: ${key}`);\n await this.client.del(key);\n return true;\n } catch (error) {\n loggy.error(`Redis del error: ${error}`);\n return null;\n }\n }\n\n async keys(): Promise<string[] | null> {\n if (!this.ready) return null;\n\n try {\n const keys = await this.client.keys('*');\n return keys;\n } catch (error) {\n loggy.error(`Redis keys error: ${error}`);\n return null;\n }\n }\n\n async reset(): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n loggy.info(`Redis FLUSHING ALL KEYS`);\n await this.client.flushdb();\n return true;\n } catch (error) {\n loggy.error(`Redis reset error: ${error}`);\n return null;\n }\n }\n\n async clearByRegexp(regExps: RegExp[]): Promise<void> {\n const keys = await this.keys();\n if (!keys) return;\n\n const toDelete = keys.filter((key) => regExps.some((re) => re.test(key)));\n await Promise.all(toDelete.map((key) => this.del(key)));\n }\n}\n","import type { Core } from '@strapi/strapi';\nimport { InMemoryCacheProvider } from './memory/provider';\nimport { RedisCacheProvider } from './redis/provider';\nimport { CacheProvider } from '../../src/types/cache.types';\nimport { loggy } from '../../src/utils/log';\n\nexport const resolveCacheProvider = (strapi: Core.Strapi): CacheProvider => {\n const providerType = strapi.plugin('strapi-cache').config('provider') || 'memory';\n\n loggy.info(`Selected cache provider: ${providerType}`);\n\n let instance: CacheProvider;\n\n switch (providerType) {\n case 'redis':\n instance = new RedisCacheProvider(strapi);\n break;\n default:\n instance = new InMemoryCacheProvider(strapi);\n break;\n }\n\n return instance;\n};\n","import type { Core } from '@strapi/strapi';\nimport { resolveCacheProvider } from './resolver';\nimport { CacheProvider, CacheService } from '../../src/types/cache.types';\nimport { loggy } from '../../src/utils/log';\n\nconst service = ({ strapi }: { strapi: Core.Strapi }): CacheService => {\n let instance: null | CacheProvider = null;\n\n return {\n getCacheInstance() {\n if (!instance) {\n loggy.info('Initializing cache service from provider config...');\n instance = resolveCacheProvider(strapi);\n }\n return instance;\n },\n };\n};\n\nexport default {\n service,\n};\n","/**\n * Application methods\n */\nimport bootstrap from './bootstrap';\nimport register from './register';\n\n/**\n * Plugin server methods\n */\nimport config from './config';\nimport contentTypes from './content-types';\nimport controllers from './controllers';\nimport middlewares from './middlewares';\nimport policies from './policies';\nimport routes from './routes';\nimport services from './services';\n\nexport default {\n register,\n bootstrap,\n destroy() {},\n config,\n controllers,\n routes,\n services,\n contentTypes,\n policies,\n middlewares,\n};\n"],"names":["strapi","createHash","createGunzip","createBrotliDecompress","createInflate","middleware","Stream","rawBody","Readable","authorizationHeader","graphql","cache","config","service","LRUCache","Redis"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../server/src/utils/log.ts","../../server/src/utils/invalidateCache.ts","../../server/src/permissions.ts","../../server/src/bootstrap.ts","../../server/src/utils/key.ts","../../server/src/utils/body.ts","../../server/src/utils/header.ts","../../server/src/middlewares/cache.ts","../../server/src/middlewares/graphql.ts","../../server/src/middlewares/index.ts","../../server/src/register.ts","../../server/src/config/index.ts","../../server/src/content-types/index.ts","../../server/src/controllers/controller.ts","../../server/src/controllers/index.ts","../../server/src/policies/index.ts","../../server/src/routes/purge.ts","../../server/src/routes/index.ts","../../server/src/utils/withTimeout.ts","../../server/src/services/memory/provider.ts","../../server/src/services/redis/provider.ts","../../server/src/services/resolver.ts","../../server/src/services/index.ts","../../server/src/index.ts"],"sourcesContent":["export const loggy = {\n info: (msg: string) => {\n const shouldDebug = strapi.plugin('strapi-cache').config('debug') ?? false;\n if (!shouldDebug) {\n return;\n }\n strapi.log.info(`[STRAPI CACHE] ${msg}`);\n },\n error: (msg: string) => {\n const shouldDebug = strapi.plugin('strapi-cache').config('debug') ?? false;\n if (!shouldDebug) {\n return;\n }\n strapi.log.error(`[STRAPI CACHE] ${msg}`);\n },\n warn: (msg: string) => {\n const shouldDebug = strapi.plugin('strapi-cache').config('debug') ?? false;\n if (!shouldDebug) {\n return;\n }\n strapi.log.warn(`[STRAPI CACHE] ${msg}`);\n },\n};\n","import { Core } from '@strapi/strapi';\nimport { CacheProvider } from 'src/types/cache.types';\nimport { loggy } from './log';\n\nexport async function invalidateCache(event: any, cacheStore: CacheProvider, strapi: Core.Strapi) {\n const { model } = event;\n const uid = model.uid;\n\n try {\n const contentType = strapi.contentType(uid);\n\n if (!contentType || !contentType.kind) {\n loggy.info(`Content type ${uid} not found`);\n return;\n }\n\n const pluralName =\n contentType.kind === 'singleType'\n ? contentType.info.singularName\n : contentType.info.pluralName;\n const apiPath = `/api/${pluralName}`;\n const regex = new RegExp(`^.*:${apiPath}(/.*)?(\\\\?.*)?$`);\n\n await cacheStore.clearByRegexp([regex]);\n loggy.info(`Invalidated cache for ${apiPath}`);\n } catch (error) {\n loggy.error('Cache invalidation error:');\n loggy.error(error);\n }\n}\n\nexport async function invalidateGraphqlCache(\n event: any,\n cacheStore: CacheProvider,\n strapi: Core.Strapi\n) {\n try {\n const graphqlRegex = new RegExp(`^POST:\\/graphql(:.*)?$`);\n\n await cacheStore.clearByRegexp([graphqlRegex]);\n loggy.info(`Invalidated cache for ${graphqlRegex}`);\n } catch (error) {\n loggy.error('Cache invalidation error:');\n loggy.error(error);\n }\n}\n","export const actions = [\n {\n section: 'plugins',\n displayName: 'Purge Cache',\n uid: 'purge-cache',\n pluginName: 'strapi-cache',\n },\n];\n","import type { Core } from '@strapi/strapi';\nimport { invalidateCache, invalidateGraphqlCache } from './utils/invalidateCache';\nimport { CacheService } from './types/cache.types';\nimport { loggy } from './utils/log';\nimport { actions } from './permissions';\n\nconst bootstrap = ({ strapi }: { strapi: Core.Strapi }) => {\n loggy.info('Initializing');\n try {\n const cacheService = strapi.plugin('strapi-cache').services.service as CacheService;\n const autoPurgeCache = strapi.plugin('strapi-cache').config('autoPurgeCache') as boolean;\n const cacheStore = cacheService.getCacheInstance();\n\n if (!cacheStore) {\n loggy.error('Plugin could not be initialized');\n return;\n }\n\n cacheStore.init();\n\n if (autoPurgeCache) {\n strapi.db.lifecycles.subscribe({\n async afterCreate(event) {\n await invalidateCache(event, cacheStore, strapi);\n await invalidateGraphqlCache(event, cacheStore, strapi);\n },\n async afterUpdate(event) {\n await invalidateCache(event, cacheStore, strapi);\n await invalidateGraphqlCache(event, cacheStore, strapi);\n },\n async afterDelete(event) {\n await invalidateCache(event, cacheStore, strapi);\n await invalidateGraphqlCache(event, cacheStore, strapi);\n },\n });\n }\n } catch (error) {\n loggy.error('Plugin could not be initialized');\n return;\n }\n loggy.info('Plugin initialized');\n\n strapi.admin.services.permission.actionProvider.registerMany(actions);\n};\n\nexport default bootstrap;\n","import { createHash } from 'crypto';\nimport { Context } from 'koa';\n\nexport const generateCacheKey = (context: Context) => {\n const { url } = context.request;\n const { method } = context.request;\n\n return `${method}:${url}`;\n};\n\nexport const generateGraphqlCacheKey = (payload: string) => {\n const hash = createHash('sha256').update(payload).digest('base64url');\n return `POST:/graphql:${hash}`;\n};\n","import { Stream } from 'stream';\nimport { createGunzip, createBrotliDecompress, createInflate } from 'zlib';\n\nexport const streamToBuffer = (stream: Stream): Promise<Buffer> => {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n stream.on('data', (chunk) => chunks.push(chunk));\n stream.on('end', () => resolve(Buffer.concat(chunks)));\n stream.on('error', reject);\n });\n};\n\nexport const decompressBuffer = async (buffer: Buffer, encoding?: string): Promise<Buffer> => {\n return new Promise((resolve, reject) => {\n let decompressStream;\n switch (encoding) {\n case 'gzip':\n decompressStream = createGunzip();\n break;\n case 'br':\n decompressStream = createBrotliDecompress();\n break;\n case 'deflate':\n decompressStream = createInflate();\n break;\n default:\n return resolve(buffer);\n }\n\n const chunks: Buffer[] = [];\n decompressStream.on('data', (chunk) => chunks.push(chunk));\n decompressStream.on('end', () => resolve(Buffer.concat(chunks)));\n decompressStream.on('error', reject);\n\n decompressStream.end(buffer);\n });\n};\n\nexport const decodeBufferToText = (buffer: Buffer): string => {\n const decoder = new TextDecoder('utf-8');\n return decoder.decode(buffer);\n};\n","import { Context } from 'koa';\nimport { OutgoingHttpHeaders } from 'http';\n\nexport function getHeadersToStore(\n ctx: Context,\n cacheHeaders: boolean,\n cacheHeadersAllowList: string[] = [],\n cacheHeadersDenyList: string[] = []\n): OutgoingHttpHeaders | null {\n let headersToStore: OutgoingHttpHeaders | null = null;\n\n if (cacheHeaders) {\n let headers = ctx.response.headers;\n\n if (cacheHeadersAllowList.length) {\n headers = Object.fromEntries(\n Object.entries(headers).filter(([key]) => cacheHeadersAllowList.includes(key.toLowerCase()))\n );\n }\n\n if (cacheHeadersDenyList.length) {\n headers = Object.fromEntries(\n Object.entries(headers).filter(([key]) => !cacheHeadersDenyList.includes(key.toLowerCase()))\n );\n }\n\n headersToStore = headers;\n }\n\n return headersToStore;\n}\n\nexport function getCacheHeaderConfig() {\n const cacheHeaders = strapi.plugin('strapi-cache').config('cacheHeaders') as boolean;\n const cacheHeadersDenyList = strapi\n .plugin('strapi-cache')\n .config('cacheHeadersDenyList') as string[];\n const cacheHeadersAllowList = strapi\n .plugin('strapi-cache')\n .config('cacheHeadersAllowList') as string[];\n const cacheAuthorizedRequests = strapi\n .plugin('strapi-cache')\n .config('cacheAuthorizedRequests') as boolean;\n\n return {\n cacheHeaders,\n cacheHeadersDenyList,\n cacheHeadersAllowList,\n cacheAuthorizedRequests,\n };\n}\n","import { Context } from 'koa';\nimport { generateCacheKey } from '../utils/key';\nimport { CacheService } from '../../src/types/cache.types';\nimport { loggy } from '../utils/log';\nimport Stream from 'stream';\nimport { decodeBufferToText, decompressBuffer, streamToBuffer } from '../utils/body';\nimport { getCacheHeaderConfig, getHeadersToStore } from '../utils/header';\n\nconst middleware = async (ctx: Context, next: any) => {\n const cacheService = strapi.plugin('strapi-cache').services.service as CacheService;\n const cacheableRoutes = strapi.plugin('strapi-cache').config('cacheableRoutes') as string[];\n const { cacheHeaders, cacheHeadersDenyList, cacheHeadersAllowList, cacheAuthorizedRequests } =\n getCacheHeaderConfig();\n const cacheStore = cacheService.getCacheInstance();\n const { url } = ctx.request;\n const key = generateCacheKey(ctx);\n const cacheEntry = await cacheStore.get(key);\n const cacheControlHeader = ctx.request.headers['cache-control'];\n const noCache = cacheControlHeader && cacheControlHeader.includes('no-cache');\n const routeIsCachable =\n cacheableRoutes.some((route) => url.startsWith(route)) ||\n (cacheableRoutes.length === 0 && url.startsWith('/api'));\n const authorizationHeader = ctx.request.headers['authorization'];\n\n if (authorizationHeader && !cacheAuthorizedRequests) {\n loggy.info(`Authorized request bypassing cache: ${key}`);\n await next();\n return;\n }\n\n if (cacheEntry && !noCache) {\n loggy.info(`HIT with key: ${key}`);\n ctx.status = 200;\n ctx.body = cacheEntry.body;\n if (cacheHeaders) {\n ctx.set(cacheEntry.headers);\n }\n return;\n }\n\n await next();\n\n if (ctx.method === 'GET' && ctx.status >= 200 && ctx.status < 300 && routeIsCachable) {\n loggy.info(`MISS with key: ${key}`);\n const headersToStore = getHeadersToStore(\n ctx,\n cacheHeaders,\n cacheHeadersAllowList,\n cacheHeadersDenyList\n );\n\n if (ctx.body instanceof Stream) {\n const buf = await streamToBuffer(ctx.body);\n const contentEncoding = ctx.response.headers['content-encoding'];\n const decompressed = await decompressBuffer(buf, contentEncoding);\n const responseText = decodeBufferToText(decompressed);\n\n await cacheStore.set(key, { body: responseText, headers: headersToStore });\n ctx.body = buf;\n } else {\n await cacheStore.set(key, { body: ctx.body, headers: headersToStore });\n }\n }\n};\n\nexport default middleware;\n","import rawBody from 'raw-body';\nimport { generateGraphqlCacheKey } from '../utils/key';\nimport Stream, { Readable } from 'stream';\nimport { loggy } from '../utils/log';\nimport { CacheService } from '../types/cache.types';\nimport { decodeBufferToText, decompressBuffer, streamToBuffer } from '../utils/body';\nimport { getCacheHeaderConfig, getHeadersToStore } from '../utils/header';\n\nconst middleware = async (ctx: any, next: any) => {\n const cacheService = strapi.plugin('strapi-cache').services.service as CacheService;\n const { cacheHeaders, cacheHeadersDenyList, cacheHeadersAllowList, cacheAuthorizedRequests } =\n getCacheHeaderConfig();\n const cacheStore = cacheService.getCacheInstance();\n const { url } = ctx.request;\n\n const originalReq = ctx.req;\n const bodyBuffer = await rawBody(originalReq);\n const body = bodyBuffer.toString();\n\n const clonedReq = new Readable();\n clonedReq.push(bodyBuffer);\n clonedReq.push(null);\n\n (clonedReq as any).headers = { ...originalReq.headers };\n (clonedReq as any).method = originalReq.method;\n (clonedReq as any).url = originalReq.url;\n (clonedReq as any).httpVersion = originalReq.httpVersion;\n (clonedReq as any).socket = originalReq.socket;\n (clonedReq as any).connection = originalReq.connection;\n\n ctx.req = clonedReq;\n ctx.request.req = clonedReq;\n\n const isIntrospectionQuery = body.includes('IntrospectionQuery');\n if (isIntrospectionQuery) {\n loggy.info('Skipping cache for introspection query');\n await next();\n return;\n }\n\n const key = generateGraphqlCacheKey(body);\n const cacheEntry = await cacheStore.get(key);\n const cacheControlHeader = ctx.request.headers['cache-control'];\n const noCache = cacheControlHeader && cacheControlHeader.includes('no-cache');\n const authorizationHeader = ctx.request.headers['authorization'];\n\n if (authorizationHeader && !cacheAuthorizedRequests) {\n loggy.info(`Authorized request bypassing cache: ${key}`);\n await next();\n return;\n }\n\n if (cacheEntry && !noCache) {\n loggy.info(`HIT with key: ${key}`);\n ctx.status = 200;\n ctx.body = cacheEntry.body;\n if (cacheHeaders) {\n ctx.set(cacheEntry.headers);\n }\n return;\n }\n\n await next();\n\n if (\n ctx.method === 'POST' &&\n ctx.status >= 200 &&\n ctx.status < 300 &&\n url.startsWith('/graphql')\n ) {\n loggy.info(`MISS with key: ${key}`);\n const headers = ctx.request.headers;\n const authorizationHeader = headers['authorization'];\n\n if (authorizationHeader && !cacheAuthorizedRequests) {\n loggy.info(`Authorized request not caching: ${key}`);\n return;\n }\n\n const headersToStore = getHeadersToStore(\n ctx,\n cacheHeaders,\n cacheHeadersAllowList,\n cacheHeadersDenyList\n );\n\n if (ctx.body instanceof Stream) {\n const buf = await streamToBuffer(ctx.body);\n const contentEncoding = ctx.response.headers['content-encoding'];\n const decompressed = await decompressBuffer(buf, contentEncoding);\n const responseText = decodeBufferToText(decompressed);\n\n await cacheStore.set(key, { body: responseText, headers: headersToStore });\n ctx.body = buf;\n } else {\n await cacheStore.set(key, { body: ctx.body, headers: headersToStore });\n }\n }\n};\n\nexport default middleware;\n","import cache from './cache';\nimport graphql from './graphql';\n\nexport default {\n graphql,\n cache,\n};\n","import type { Core } from '@strapi/strapi';\nimport middlewares from './middlewares';\n\nconst register = ({ strapi }: { strapi: Core.Strapi }) => {\n strapi.server.use(middlewares.cache);\n strapi.server.use(middlewares.graphql);\n};\n\nexport default register;\n","export default {\n default: ({ env }) => ({\n debug: false,\n max: 1000,\n ttl: 1000 * 60 * 60,\n size: 1024 * 1024 * 10,\n allowStale: false,\n cacheableRoutes: [],\n provider: 'memory',\n redisConfig: env('REDIS_URL'),\n redisClusterNodes: [],\n redisClusterOptions: {},\n cacheHeaders: true,\n cacheHeadersDenyList: [],\n cacheHeadersAllowList: [],\n cacheAuthorizedRequests: false,\n cacheGetTimeoutInMs: 1000,\n autoPurgeCache: true,\n }),\n validator: (config) => {\n if (typeof config.debug !== 'boolean') {\n throw new Error(`Invalid config: debug must be a boolean`);\n }\n if (typeof config.max !== 'number') {\n throw new Error(`Invalid config: max must be a number`);\n }\n if (typeof config.ttl !== 'number') {\n throw new Error(`Invalid config: ttl must be a number`);\n }\n if (typeof config.size !== 'number') {\n throw new Error(`Invalid config: size must be a number`);\n }\n if (typeof config.allowStale !== 'boolean') {\n throw new Error(`Invalid config: allowStale must be a boolean`);\n }\n if (\n !Array.isArray(config.cacheableRoutes) ||\n config.cacheableRoutes.some((item) => typeof item !== 'string')\n ) {\n throw new Error(`Invalid config: cacheableRoutes must be an string array`);\n }\n if (typeof config.provider !== 'string') {\n throw new Error(`Invalid config: provider must be a string`);\n }\n if (config.provider !== 'memory' && config.provider !== 'redis') {\n throw new Error(`Invalid config: provider must be 'memory' or 'redis'`);\n }\n if (config.provider === 'redis') {\n if (!config.redisConfig) {\n throw new Error(`Invalid config: redisConfig must be set when using redis provider`);\n }\n if (typeof config.redisConfig !== 'string' && typeof config.redisConfig !== 'object') {\n throw new Error(`Invalid config: redisConfig must be a string or object when using redis provider`);\n }\n if (\n !Array.isArray(config.redisClusterNodes) ||\n config.redisClusterNodes.some((item) => !('host' in item && 'port' in item))\n ) {\n throw new Error(\n `Invalid config: redisClusterNodes must be as a list of objects with keys 'host' and 'port'`\n );\n }\n if (typeof config.redisClusterOptions !== 'object') {\n throw new Error(`Invalid config: redisClusterOptions must be an object`);\n }\n }\n if (typeof config.cacheHeaders !== 'boolean') {\n throw new Error(`Invalid config: cacheHeaders must be a boolean`);\n }\n if (\n !Array.isArray(config.cacheHeadersDenyList) ||\n config.cacheHeadersDenyList.some((item) => typeof item !== 'string')\n ) {\n throw new Error(`Invalid config: cacheHeadersDenyList must be an string array`);\n }\n if (\n !Array.isArray(config.cacheHeadersAllowList) ||\n config.cacheHeadersAllowList.some((item) => typeof item !== 'string')\n ) {\n throw new Error(`Invalid config: cacheHeadersAllowList must be an string array`);\n }\n if (typeof config.cacheAuthorizedRequests !== 'boolean') {\n throw new Error(`Invalid config: cacheAuthorizedRequests must be a boolean`);\n }\n if (typeof config.cacheGetTimeoutInMs !== 'number') {\n throw new Error(`Invalid config: cacheGetTimeoutInMs must be a number`);\n }\n if (typeof config.autoPurgeCache !== 'boolean') {\n throw new Error(`Invalid config: autoPurgeCache must be a boolean`);\n }\n },\n};\n","export default {};\n","import type { Core } from '@strapi/strapi';\nimport { Context } from 'koa';\nimport { CacheService } from 'src/types/cache.types';\n\nconst controller = ({ strapi }: { strapi: Core.Strapi }) => ({\n async purgeCache(ctx: Context) {\n const service = strapi.plugin('strapi-cache').service('service') as CacheService;\n\n await service.getCacheInstance().reset();\n\n ctx.body = {\n message: 'Cache purged successfully',\n };\n },\n async purgeCacheByKey(ctx: Context) {\n const { key } = ctx.params;\n const service = strapi.plugin('strapi-cache').service('service') as CacheService;\n const regex = new RegExp(`(^|\\/)${key}(\\/|\\\\?|$)`);\n\n await service.getCacheInstance().clearByRegexp([regex]);\n\n ctx.body = {\n message: `Cache purged successfully for key: ${key}`,\n };\n },\n async cacheableRoutes(ctx: Context) {\n const cacheableRoutes = strapi.plugin('strapi-cache').config('cacheableRoutes');\n ctx.body = cacheableRoutes;\n },\n});\n\nexport default controller;\n","import controller from './controller';\n\nexport default {\n controller,\n};\n","export default {};\n","export default [\n {\n method: 'POST',\n path: '/purge-cache',\n handler: 'controller.purgeCache',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'plugin::content-manager.hasPermissions',\n config: {\n actions: ['plugin::strapi-cache.purge-cache'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/purge-cache/:key',\n handler: 'controller.purgeCacheByKey',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'plugin::content-manager.hasPermissions',\n config: {\n actions: ['plugin::strapi-cache.purge-cache'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/cacheable-routes',\n handler: 'controller.cacheableRoutes',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'plugin::content-manager.hasPermissions',\n config: {\n actions: ['plugin::strapi-cache.purge-cache'],\n },\n },\n ],\n },\n },\n];\n","import purgeRoute from './purge';\n\nconst routes = {\n 'purge-route': {\n type: 'admin',\n routes: purgeRoute,\n },\n};\n\nexport default routes;\n","export const withTimeout = (callback: () => Promise<any>, ms: number) => {\n let timeout: NodeJS.Timeout | null = null;\n\n return Promise.race([\n callback().then((result) => {\n if (timeout) {\n clearTimeout(timeout);\n }\n return result;\n }),\n new Promise((_, reject) => {\n timeout = setTimeout(() => {\n reject(new Error('timeout'));\n }, ms);\n }),\n ]);\n};\n","import type { Core } from '@strapi/strapi';\nimport { LRUCache } from 'lru-cache';\nimport { withTimeout } from '../../utils/withTimeout';\nimport { CacheProvider, CacheService } from '../../types/cache.types';\nimport { loggy } from '../../utils/log';\n\nexport class InMemoryCacheProvider implements CacheProvider {\n private initialized = false;\n private provider!: LRUCache<string, any>;\n private cacheGetTimeoutInMs: number;\n\n constructor(private strapi: Core.Strapi) {}\n\n init(): void {\n if (this.initialized) {\n loggy.error('Provider already initialized');\n return;\n }\n\n this.initialized = true;\n\n const max = Number(this.strapi.plugin('strapi-cache').config('max'));\n const ttl = Number(this.strapi.plugin('strapi-cache').config('ttl'));\n const size = Number(this.strapi.plugin('strapi-cache').config('size'));\n const allowStale = Boolean(this.strapi.plugin('strapi-cache').config('allowStale'));\n\n this.provider = new LRUCache({\n max,\n ttl,\n size,\n allowStale,\n });\n\n this.cacheGetTimeoutInMs = Number(\n this.strapi.plugin('strapi-cache').config('cacheGetTimeoutInMs')\n );\n\n loggy.info('Provider initialized');\n }\n\n get ready(): boolean {\n if (!this.initialized) {\n loggy.info('Provider not initialized');\n return false;\n }\n\n return true;\n }\n\n async get(key: string): Promise<any | null> {\n if (!this.ready) return null;\n\n return withTimeout(\n () =>\n new Promise((resolve) => {\n resolve(this.provider.get(key));\n }),\n this.cacheGetTimeoutInMs\n ).catch((error) => {\n loggy.error(`Error during get: ${error?.message || error}`);\n return null;\n });\n }\n\n async set(key: string, val: any): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n return this.provider.set(key, val);\n } catch (error) {\n loggy.error(`Error during set: ${error}`);\n return null;\n }\n }\n\n async del(key: string): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n loggy.info(`PURGING KEY: ${key}`);\n return this.provider.delete(key);\n } catch (error) {\n loggy.error(`Error during delete: ${error}`);\n return null;\n }\n }\n\n async keys(): Promise<string[] | null> {\n if (!this.ready) return null;\n\n try {\n return Array.from(this.provider.keys());\n } catch (error) {\n loggy.error(`Error fetching keys: ${error}`);\n return null;\n }\n }\n\n async reset(): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n const allKeys = await this.keys();\n if (!allKeys) return null;\n\n loggy.info(`PURGING ALL KEYS: ${allKeys.length}`);\n await Promise.all(allKeys.map((key) => this.del(key)));\n return true;\n } catch (error) {\n loggy.error(`Error during reset: ${error}`);\n return null;\n }\n }\n\n async clearByRegexp(regExps: RegExp[]): Promise<void> {\n const keys = (await this.keys()) || [];\n const matches = keys.filter((key) => regExps.some((re) => re.test(key)));\n await Promise.all(matches.map((key) => this.del(key)));\n }\n}\n","import type { Core } from '@strapi/strapi';\nimport { Redis, Cluster, ClusterNode, ClusterOptions } from 'ioredis';\nimport { withTimeout } from '../../utils/withTimeout';\nimport { CacheProvider } from '../../types/cache.types';\nimport { loggy } from '../../utils/log';\n\nexport class RedisCacheProvider implements CacheProvider {\n private initialized = false;\n private client!: Redis | Cluster;\n private cacheGetTimeoutInMs: number;\n\n constructor(private strapi: Core.Strapi) { }\n\n init(): void {\n if (this.initialized) {\n loggy.error('Redis provider already initialized');\n return;\n }\n try {\n const redisConfig =\n this.strapi.plugin('strapi-cache').config('redisConfig') || 'redis://localhost:6379';\n const redisClusterNodes: ClusterNode[] =\n this.strapi.plugin('strapi-cache').config('redisClusterNodes');\n this.cacheGetTimeoutInMs = Number(\n this.strapi.plugin('strapi-cache').config('cacheGetTimeoutInMs')\n );\n if (redisClusterNodes.length) {\n const redisClusterOptions: ClusterOptions =\n this.strapi.plugin('strapi-cache').config('redisClusterOptions');\n if (!redisClusterOptions['redisOptions']) {\n redisClusterOptions.redisOptions = redisConfig;\n }\n this.client = new Redis.Cluster(redisClusterNodes, redisClusterOptions);\n } else {\n this.client = new Redis(redisConfig);\n }\n this.initialized = true;\n\n loggy.info('Redis provider initialized');\n } catch (error) {\n loggy.error(error);\n }\n }\n\n get ready(): boolean {\n if (!this.initialized) {\n loggy.info('Redis provider not initialized');\n return false;\n }\n\n return true;\n }\n\n async get(key: string): Promise<any | null> {\n if (!this.ready) return null;\n\n return withTimeout(() => this.client.get(key), this.cacheGetTimeoutInMs)\n .then((data) => (data ? JSON.parse(data) : null))\n .catch((error) => {\n loggy.error(`Redis get error: ${error?.message || error}`);\n return null;\n });\n }\n\n async set(key: string, val: any): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n // plugin ttl is ms, ioredis ttl is s, so we convert here\n const ttlInMs = Number(this.strapi.plugin('strapi-cache').config('ttl'));\n const ttlInS = Number((ttlInMs/1000).toFixed());\n const serialized = JSON.stringify(val);\n if (ttlInS > 0) {\n await this.client.set(key, serialized, 'EX', ttlInS);\n } else {\n await this.client.set(key, serialized);\n }\n return val;\n } catch (error) {\n loggy.error(`Redis set error: ${error}`);\n return null;\n }\n }\n\n async del(key: string): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n loggy.info(`Redis PURGING KEY: ${key}`);\n await this.client.del(key);\n return true;\n } catch (error) {\n loggy.error(`Redis del error: ${error}`);\n return null;\n }\n }\n\n async keys(): Promise<string[] | null> {\n if (!this.ready) return null;\n\n try {\n const keys = await this.client.keys('*');\n return keys;\n } catch (error) {\n loggy.error(`Redis keys error: ${error}`);\n return null;\n }\n }\n\n async reset(): Promise<any | null> {\n if (!this.ready) return null;\n\n try {\n loggy.info(`Redis FLUSHING ALL KEYS`);\n await this.client.flushdb();\n return true;\n } catch (error) {\n loggy.error(`Redis reset error: ${error}`);\n return null;\n }\n }\n\n async clearByRegexp(regExps: RegExp[]): Promise<void> {\n const keys = await this.keys();\n if (!keys) return;\n\n const toDelete = keys.filter((key) => regExps.some((re) => re.test(key)));\n await Promise.all(toDelete.map((key) => this.del(key)));\n }\n}\n","import type { Core } from '@strapi/strapi';\nimport { InMemoryCacheProvider } from './memory/provider';\nimport { RedisCacheProvider } from './redis/provider';\nimport { CacheProvider } from '../../src/types/cache.types';\nimport { loggy } from '../../src/utils/log';\n\nexport const resolveCacheProvider = (strapi: Core.Strapi): CacheProvider => {\n const providerType = strapi.plugin('strapi-cache').config('provider') || 'memory';\n\n loggy.info(`Selected cache provider: ${providerType}`);\n\n let instance: CacheProvider;\n\n switch (providerType) {\n case 'redis':\n instance = new RedisCacheProvider(strapi);\n break;\n default:\n instance = new InMemoryCacheProvider(strapi);\n break;\n }\n\n return instance;\n};\n","import type { Core } from '@strapi/strapi';\nimport { resolveCacheProvider } from './resolver';\nimport { CacheProvider, CacheService } from '../../src/types/cache.types';\nimport { loggy } from '../../src/utils/log';\n\nconst service = ({ strapi }: { strapi: Core.Strapi }): CacheService => {\n let instance: null | CacheProvider = null;\n\n return {\n getCacheInstance() {\n if (!instance) {\n loggy.info('Initializing cache service from provider config...');\n instance = resolveCacheProvider(strapi);\n }\n return instance;\n },\n };\n};\n\nexport default {\n service,\n};\n","/**\n * Application methods\n */\nimport bootstrap from './bootstrap';\nimport register from './register';\n\n/**\n * Plugin server methods\n */\nimport config from './config';\nimport contentTypes from './content-types';\nimport controllers from './controllers';\nimport middlewares from './middlewares';\nimport policies from './policies';\nimport routes from './routes';\nimport services from './services';\n\nexport default {\n register,\n bootstrap,\n destroy() {},\n config,\n controllers,\n routes,\n services,\n contentTypes,\n policies,\n middlewares,\n};\n"],"names":["strapi","middleware","authorizationHeader","graphql","cache","config","service"],"mappings":"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"}
|