strapi-cache 1.2.1 → 1.4.0
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 +61 -16
- package/dist/_chunks/en-BPx_Feq_.mjs +17 -0
- package/dist/_chunks/en-BPx_Feq_.mjs.map +1 -0
- package/dist/_chunks/en-BdzOe-Jv.mjs +16 -0
- package/dist/_chunks/en-BdzOe-Jv.mjs.map +1 -0
- package/dist/_chunks/en-Bev8UGB9.js +14 -0
- package/dist/_chunks/en-Bev8UGB9.js.map +1 -0
- package/dist/_chunks/en-C0JeH0S_.js +17 -0
- package/dist/_chunks/en-C0JeH0S_.js.map +1 -0
- package/dist/_chunks/en-C0qeO9FB.mjs +15 -0
- package/dist/_chunks/en-C0qeO9FB.mjs.map +1 -0
- package/dist/_chunks/en-CVjURV1W.js +11 -0
- package/dist/_chunks/en-CVjURV1W.js.map +1 -0
- package/dist/_chunks/en-C_x7qEgd.mjs +11 -0
- package/dist/_chunks/en-C_x7qEgd.mjs.map +1 -0
- package/dist/_chunks/en-DZzRFINn.js +16 -0
- package/dist/_chunks/en-DZzRFINn.js.map +1 -0
- package/dist/_chunks/en-D_Fd-4QS.js +15 -0
- package/dist/_chunks/en-D_Fd-4QS.js.map +1 -0
- package/dist/_chunks/en-DdVULHeC.mjs +14 -0
- package/dist/_chunks/en-DdVULHeC.mjs.map +1 -0
- package/dist/admin/index.js +83 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +83 -1
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/src/components/PurgeCacheButton/index.d.ts +2 -0
- package/dist/admin/src/permission.d.ts +6 -0
- package/dist/server/index.js +275 -167
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +274 -167
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/config/index.d.ts +3 -1
- package/dist/server/src/controllers/controller.d.ts +2 -1
- package/dist/server/src/controllers/index.d.ts +2 -1
- package/dist/server/src/index.d.ts +14 -13
- package/dist/server/src/permissions.d.ts +6 -0
- package/dist/server/src/routes/index.d.ts +7 -2
- package/dist/server/src/routes/purge.d.ts +14 -0
- package/dist/server/src/services/index.d.ts +4 -2
- package/dist/server/src/services/memory/provider.d.ts +16 -0
- package/dist/server/src/services/memory/service.d.ts +6 -0
- package/dist/server/src/services/redis/provider.d.ts +16 -0
- package/dist/server/src/services/redis/service.d.ts +6 -0
- package/dist/server/src/services/resolver.d.ts +3 -0
- package/dist/server/src/types/cache.types.d.ts +1 -1
- package/package.json +4 -3
package/dist/server/index.js
CHANGED
|
@@ -3,8 +3,10 @@ const crypto = require("crypto");
|
|
|
3
3
|
const rawBody = require("raw-body");
|
|
4
4
|
const stream = require("stream");
|
|
5
5
|
const lruCache = require("lru-cache");
|
|
6
|
+
const Redis = require("ioredis");
|
|
6
7
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
7
8
|
const rawBody__default = /* @__PURE__ */ _interopDefault(rawBody);
|
|
9
|
+
const Redis__default = /* @__PURE__ */ _interopDefault(Redis);
|
|
8
10
|
const loggy = {
|
|
9
11
|
info: (msg) => {
|
|
10
12
|
const shouldDebug = strapi.plugin("strapi-cache").config("debug") ?? false;
|
|
@@ -57,11 +59,19 @@ async function invalidateGraphqlCache(event, cacheStore, strapi2) {
|
|
|
57
59
|
loggy.error(error);
|
|
58
60
|
}
|
|
59
61
|
}
|
|
62
|
+
const actions = [
|
|
63
|
+
{
|
|
64
|
+
section: "plugins",
|
|
65
|
+
displayName: "Purge Cache",
|
|
66
|
+
uid: "purge-cache",
|
|
67
|
+
pluginName: "strapi-cache"
|
|
68
|
+
}
|
|
69
|
+
];
|
|
60
70
|
const bootstrap = ({ strapi: strapi2 }) => {
|
|
61
71
|
loggy.info("Initializing");
|
|
62
72
|
try {
|
|
63
73
|
const cacheService = strapi2.plugin("strapi-cache").services.service;
|
|
64
|
-
const cacheStore = cacheService.
|
|
74
|
+
const cacheStore = cacheService.getCacheInstance();
|
|
65
75
|
cacheStore.init();
|
|
66
76
|
strapi2.db.lifecycles.subscribe({
|
|
67
77
|
async afterCreate(event) {
|
|
@@ -86,8 +96,7 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
86
96
|
return;
|
|
87
97
|
}
|
|
88
98
|
loggy.info("Plugin initialized");
|
|
89
|
-
|
|
90
|
-
const destroy = ({ strapi: strapi2 }) => {
|
|
99
|
+
strapi2.admin.services.permission.actionProvider.registerMany(actions);
|
|
91
100
|
};
|
|
92
101
|
const generateCacheKey = (context) => {
|
|
93
102
|
const { url } = context.request;
|
|
@@ -101,7 +110,7 @@ const generateGraphqlCacheKey = (payload) => {
|
|
|
101
110
|
const middleware$1 = async (ctx, next) => {
|
|
102
111
|
const cacheService = strapi.plugin("strapi-cache").services.service;
|
|
103
112
|
const cacheableRoutes = strapi.plugin("strapi-cache").config("cacheableRoutes");
|
|
104
|
-
const cacheStore = cacheService.
|
|
113
|
+
const cacheStore = cacheService.getCacheInstance();
|
|
105
114
|
const { url } = ctx.request;
|
|
106
115
|
const key = generateCacheKey(ctx);
|
|
107
116
|
const cacheEntry = await cacheStore.get(key);
|
|
@@ -123,7 +132,7 @@ const middleware$1 = async (ctx, next) => {
|
|
|
123
132
|
};
|
|
124
133
|
const middleware = async (ctx, next) => {
|
|
125
134
|
const cacheService = strapi.plugin("strapi-cache").services.service;
|
|
126
|
-
const cacheStore = cacheService.
|
|
135
|
+
const cacheStore = cacheService.getCacheInstance();
|
|
127
136
|
const { url } = ctx.request;
|
|
128
137
|
const originalReq = ctx.req;
|
|
129
138
|
const bodyBuffer = await rawBody__default.default(originalReq);
|
|
@@ -140,6 +149,12 @@ const middleware = async (ctx, next) => {
|
|
|
140
149
|
ctx.req = clonedReq;
|
|
141
150
|
ctx.request.req = clonedReq;
|
|
142
151
|
const key = generateGraphqlCacheKey(body);
|
|
152
|
+
const isIntrospectionQuery = body.includes("IntrospectionQuery");
|
|
153
|
+
if (isIntrospectionQuery) {
|
|
154
|
+
loggy.info("Skipping cache for introspection query");
|
|
155
|
+
await next();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
143
158
|
const cacheEntry = await cacheStore.get(key);
|
|
144
159
|
const cacheControlHeader = ctx.request.headers["cache-control"];
|
|
145
160
|
const noCache = cacheControlHeader && cacheControlHeader.includes("no-cache");
|
|
@@ -174,7 +189,9 @@ const config = {
|
|
|
174
189
|
ttl: 1e3 * 60 * 60,
|
|
175
190
|
size: 1024 * 1014 * 10,
|
|
176
191
|
allowStale: false,
|
|
177
|
-
|
|
192
|
+
cacheableRoutes: [],
|
|
193
|
+
provider: "memory",
|
|
194
|
+
redisUrl: env("REDIS_URL")
|
|
178
195
|
}),
|
|
179
196
|
validator: (config2) => {
|
|
180
197
|
if (typeof config2.debug !== "boolean") {
|
|
@@ -195,33 +212,78 @@ const config = {
|
|
|
195
212
|
if (!Array.isArray(config2.cacheableRoutes) || config2.cacheableRoutes.some((item) => typeof item !== "string")) {
|
|
196
213
|
throw new Error(`Invalid config: cacheableRoutes must be an string array`);
|
|
197
214
|
}
|
|
215
|
+
if (typeof config2.provider !== "string") {
|
|
216
|
+
throw new Error(`Invalid config: provider must be a string`);
|
|
217
|
+
}
|
|
218
|
+
if (config2.provider !== "memory" && config2.provider !== "redis") {
|
|
219
|
+
throw new Error(`Invalid config: provider must be 'memory' or 'redis'`);
|
|
220
|
+
}
|
|
221
|
+
if (config2.provider === "redis" && !config2.redisUrl && typeof config2.redisUrl !== "string") {
|
|
222
|
+
throw new Error(`Invalid config: redisUrl must be set when using redis provider`);
|
|
223
|
+
}
|
|
198
224
|
}
|
|
199
225
|
};
|
|
200
226
|
const contentTypes = {};
|
|
201
227
|
const controller = ({ strapi: strapi2 }) => ({
|
|
202
|
-
|
|
203
|
-
|
|
228
|
+
async purgeCache(ctx) {
|
|
229
|
+
const service2 = strapi2.plugin("strapi-cache").service("service");
|
|
230
|
+
await service2.getCacheInstance().reset();
|
|
231
|
+
ctx.body = {
|
|
232
|
+
message: "Cache purged successfully"
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
async purgeCacheByKey(ctx) {
|
|
236
|
+
const { key } = ctx.params;
|
|
237
|
+
const service2 = strapi2.plugin("strapi-cache").service("service");
|
|
238
|
+
const regex = new RegExp(`(^|/)${key}(/|\\?|$)`);
|
|
239
|
+
await service2.getCacheInstance().clearByRegexp([regex]);
|
|
240
|
+
ctx.body = {
|
|
241
|
+
message: `Cache purged successfully for key: ${key}`
|
|
242
|
+
};
|
|
204
243
|
}
|
|
205
244
|
});
|
|
206
245
|
const controllers = {
|
|
207
246
|
controller
|
|
208
247
|
};
|
|
209
248
|
const policies = {};
|
|
210
|
-
const
|
|
249
|
+
const purgeRoute = [
|
|
211
250
|
{
|
|
212
|
-
method: "
|
|
213
|
-
path: "/",
|
|
214
|
-
|
|
215
|
-
handler: "controller.index",
|
|
251
|
+
method: "POST",
|
|
252
|
+
path: "/purge-cache",
|
|
253
|
+
handler: "controller.purgeCache",
|
|
216
254
|
config: {
|
|
217
|
-
policies: [
|
|
255
|
+
policies: [
|
|
256
|
+
"admin::isAuthenticatedAdmin",
|
|
257
|
+
{
|
|
258
|
+
name: "plugin::content-manager.hasPermissions",
|
|
259
|
+
config: {
|
|
260
|
+
actions: ["plugin::strapi-cache.purge-cache"]
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
method: "POST",
|
|
268
|
+
path: "/purge-cache/:key",
|
|
269
|
+
handler: "controller.purgeCacheByKey",
|
|
270
|
+
config: {
|
|
271
|
+
policies: [
|
|
272
|
+
"admin::isAuthenticatedAdmin",
|
|
273
|
+
{
|
|
274
|
+
name: "plugin::content-manager.hasPermissions",
|
|
275
|
+
config: {
|
|
276
|
+
actions: ["plugin::strapi-cache.purge-cache"]
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
]
|
|
218
280
|
}
|
|
219
281
|
}
|
|
220
282
|
];
|
|
221
283
|
const routes = {
|
|
222
|
-
"
|
|
223
|
-
type: "
|
|
224
|
-
routes:
|
|
284
|
+
"purge-route": {
|
|
285
|
+
type: "admin",
|
|
286
|
+
routes: purgeRoute
|
|
225
287
|
}
|
|
226
288
|
};
|
|
227
289
|
const withTimeout = (callback, ms) => {
|
|
@@ -240,158 +302,203 @@ const withTimeout = (callback, ms) => {
|
|
|
240
302
|
})
|
|
241
303
|
]);
|
|
242
304
|
};
|
|
305
|
+
class InMemoryCacheProvider {
|
|
306
|
+
constructor(strapi2) {
|
|
307
|
+
this.strapi = strapi2;
|
|
308
|
+
this.initialized = false;
|
|
309
|
+
}
|
|
310
|
+
init() {
|
|
311
|
+
if (this.initialized) {
|
|
312
|
+
loggy.error("Provider already initialized");
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
this.initialized = true;
|
|
316
|
+
const max = Number(this.strapi.plugin("strapi-cache").config("max"));
|
|
317
|
+
const ttl = Number(this.strapi.plugin("strapi-cache").config("ttl"));
|
|
318
|
+
const size = Number(this.strapi.plugin("strapi-cache").config("size"));
|
|
319
|
+
const allowStale = Boolean(this.strapi.plugin("strapi-cache").config("allowStale"));
|
|
320
|
+
this.provider = new lruCache.LRUCache({
|
|
321
|
+
max,
|
|
322
|
+
ttl,
|
|
323
|
+
size,
|
|
324
|
+
allowStale
|
|
325
|
+
});
|
|
326
|
+
loggy.info("Provider initialized");
|
|
327
|
+
}
|
|
328
|
+
get ready() {
|
|
329
|
+
if (!this.initialized) {
|
|
330
|
+
loggy.info("Provider not initialized");
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
async get(key) {
|
|
336
|
+
if (!this.ready) return null;
|
|
337
|
+
const timeout = 1e3;
|
|
338
|
+
return withTimeout(
|
|
339
|
+
() => new Promise((resolve) => {
|
|
340
|
+
resolve(this.provider.get(key));
|
|
341
|
+
}),
|
|
342
|
+
timeout
|
|
343
|
+
).catch((error) => {
|
|
344
|
+
loggy.error(`Error during get: ${error?.message || error}`);
|
|
345
|
+
return null;
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
async set(key, val) {
|
|
349
|
+
if (!this.ready) return null;
|
|
350
|
+
try {
|
|
351
|
+
return this.provider.set(key, val);
|
|
352
|
+
} catch (error) {
|
|
353
|
+
loggy.error(`Error during set: ${error}`);
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async del(key) {
|
|
358
|
+
if (!this.ready) return null;
|
|
359
|
+
try {
|
|
360
|
+
loggy.info(`PURGING KEY: ${key}`);
|
|
361
|
+
return this.provider.delete(key);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
loggy.error(`Error during delete: ${error}`);
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async keys() {
|
|
368
|
+
if (!this.ready) return null;
|
|
369
|
+
try {
|
|
370
|
+
return Array.from(this.provider.keys());
|
|
371
|
+
} catch (error) {
|
|
372
|
+
loggy.error(`Error fetching keys: ${error}`);
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async reset() {
|
|
377
|
+
if (!this.ready) return null;
|
|
378
|
+
try {
|
|
379
|
+
const allKeys = await this.keys();
|
|
380
|
+
if (!allKeys) return null;
|
|
381
|
+
loggy.info(`PURGING ALL KEYS: ${allKeys.length}`);
|
|
382
|
+
await Promise.all(allKeys.map((key) => this.del(key)));
|
|
383
|
+
return true;
|
|
384
|
+
} catch (error) {
|
|
385
|
+
loggy.error(`Error during reset: ${error}`);
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
async clearByRegexp(regExps) {
|
|
390
|
+
const keys = await this.keys() || [];
|
|
391
|
+
const matches = keys.filter((key) => regExps.some((re) => re.test(key)));
|
|
392
|
+
await Promise.all(matches.map((key) => this.del(key)));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
class RedisCacheProvider {
|
|
396
|
+
constructor(strapi2) {
|
|
397
|
+
this.strapi = strapi2;
|
|
398
|
+
this.initialized = false;
|
|
399
|
+
}
|
|
400
|
+
init() {
|
|
401
|
+
if (this.initialized) {
|
|
402
|
+
loggy.error("Redis provider already initialized");
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const redisUrl = this.strapi.plugin("strapi-cache").config("redisUrl") || "redis://localhost:6379";
|
|
406
|
+
this.client = new Redis__default.default(redisUrl);
|
|
407
|
+
this.initialized = true;
|
|
408
|
+
loggy.info("Redis provider initialized");
|
|
409
|
+
}
|
|
410
|
+
get ready() {
|
|
411
|
+
if (!this.initialized) {
|
|
412
|
+
loggy.info("Redis provider not initialized");
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
async get(key) {
|
|
418
|
+
if (!this.ready) return null;
|
|
419
|
+
const timeout = 1e3;
|
|
420
|
+
return withTimeout(() => this.client.get(key), timeout).then((data) => data ? JSON.parse(data) : null).catch((error) => {
|
|
421
|
+
loggy.error(`Redis get error: ${error?.message || error}`);
|
|
422
|
+
return null;
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
async set(key, val) {
|
|
426
|
+
if (!this.ready) return null;
|
|
427
|
+
try {
|
|
428
|
+
const ttl = Number(this.strapi.plugin("strapi-cache").config("ttl"));
|
|
429
|
+
const serialized = JSON.stringify(val);
|
|
430
|
+
if (ttl > 0) {
|
|
431
|
+
await this.client.set(key, serialized, "EX", ttl);
|
|
432
|
+
} else {
|
|
433
|
+
await this.client.set(key, serialized);
|
|
434
|
+
}
|
|
435
|
+
return val;
|
|
436
|
+
} catch (error) {
|
|
437
|
+
loggy.error(`Redis set error: ${error}`);
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
async del(key) {
|
|
442
|
+
if (!this.ready) return null;
|
|
443
|
+
try {
|
|
444
|
+
loggy.info(`Redis PURGING KEY: ${key}`);
|
|
445
|
+
await this.client.del(key);
|
|
446
|
+
return true;
|
|
447
|
+
} catch (error) {
|
|
448
|
+
loggy.error(`Redis del error: ${error}`);
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
async keys() {
|
|
453
|
+
if (!this.ready) return null;
|
|
454
|
+
try {
|
|
455
|
+
const keys = await this.client.keys("*");
|
|
456
|
+
return keys;
|
|
457
|
+
} catch (error) {
|
|
458
|
+
loggy.error(`Redis keys error: ${error}`);
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
async reset() {
|
|
463
|
+
if (!this.ready) return null;
|
|
464
|
+
try {
|
|
465
|
+
loggy.info(`Redis FLUSHING ALL KEYS`);
|
|
466
|
+
await this.client.flushdb();
|
|
467
|
+
return true;
|
|
468
|
+
} catch (error) {
|
|
469
|
+
loggy.error(`Redis reset error: ${error}`);
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
async clearByRegexp(regExps) {
|
|
474
|
+
const keys = await this.keys();
|
|
475
|
+
if (!keys) return;
|
|
476
|
+
const toDelete = keys.filter((key) => regExps.some((re) => re.test(key)));
|
|
477
|
+
await Promise.all(toDelete.map((key) => this.del(key)));
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
const resolveCacheProvider = (strapi2) => {
|
|
481
|
+
const providerType = strapi2.plugin("strapi-cache").config("provider") || "memory";
|
|
482
|
+
loggy.info(`Selected cache provider: ${providerType}`);
|
|
483
|
+
let instance;
|
|
484
|
+
switch (providerType) {
|
|
485
|
+
case "redis":
|
|
486
|
+
instance = new RedisCacheProvider(strapi2);
|
|
487
|
+
break;
|
|
488
|
+
default:
|
|
489
|
+
instance = new InMemoryCacheProvider(strapi2);
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
return instance;
|
|
493
|
+
};
|
|
243
494
|
const service = ({ strapi: strapi2 }) => {
|
|
244
|
-
let
|
|
495
|
+
let instance = null;
|
|
245
496
|
return {
|
|
246
|
-
|
|
247
|
-
if (
|
|
248
|
-
|
|
497
|
+
getCacheInstance() {
|
|
498
|
+
if (!instance) {
|
|
499
|
+
loggy.info("Initializing cache service from provider config...");
|
|
500
|
+
instance = resolveCacheProvider(strapi2);
|
|
249
501
|
}
|
|
250
|
-
let initialized = false;
|
|
251
|
-
let provider;
|
|
252
|
-
loggy.info("Creating provider");
|
|
253
|
-
const instance = {
|
|
254
|
-
init() {
|
|
255
|
-
if (initialized) {
|
|
256
|
-
loggy.error("Provider already initialized");
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
initialized = true;
|
|
260
|
-
const max = strapi2.plugin("strapi-cache").config("max");
|
|
261
|
-
const ttl = strapi2.plugin("strapi-cache").config("ttl");
|
|
262
|
-
const size = strapi2.plugin("strapi-cache").config("size");
|
|
263
|
-
const allowStale = strapi2.plugin("strapi-cache").config("allowStale");
|
|
264
|
-
provider = new lruCache.LRUCache({
|
|
265
|
-
max: Number(max),
|
|
266
|
-
ttl: Number(ttl),
|
|
267
|
-
size: Number(size),
|
|
268
|
-
allowStale: allowStale ? true : false
|
|
269
|
-
});
|
|
270
|
-
loggy.info("Provider initialized");
|
|
271
|
-
},
|
|
272
|
-
/**
|
|
273
|
-
* @param {string} key
|
|
274
|
-
*/
|
|
275
|
-
async get(key) {
|
|
276
|
-
if (!initialized) {
|
|
277
|
-
loggy.error("Provider not initialized");
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
if (!this.ready) {
|
|
281
|
-
loggy.error("Provider not ready");
|
|
282
|
-
return null;
|
|
283
|
-
}
|
|
284
|
-
const getTimeout = 1e3;
|
|
285
|
-
return withTimeout(async () => await provider.get(key), getTimeout).catch((error) => {
|
|
286
|
-
if (error?.message === "timeout") {
|
|
287
|
-
loggy.error(`Provider timed-out after ${getTimeout}ms.`);
|
|
288
|
-
} else {
|
|
289
|
-
loggy.error(`Provider errored:`);
|
|
290
|
-
loggy.error(error);
|
|
291
|
-
}
|
|
292
|
-
return null;
|
|
293
|
-
});
|
|
294
|
-
},
|
|
295
|
-
/**
|
|
296
|
-
* @param {string} key
|
|
297
|
-
* @param {any} val
|
|
298
|
-
*/
|
|
299
|
-
async set(key, val) {
|
|
300
|
-
if (!initialized) {
|
|
301
|
-
loggy.error("Provider not initialized");
|
|
302
|
-
return null;
|
|
303
|
-
}
|
|
304
|
-
if (!this.ready) {
|
|
305
|
-
loggy.error("Provider not ready");
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
try {
|
|
309
|
-
const size = provider.size;
|
|
310
|
-
return provider.set(key, val);
|
|
311
|
-
} catch (error) {
|
|
312
|
-
loggy.error(`Provider errored:`);
|
|
313
|
-
loggy.error(error);
|
|
314
|
-
return null;
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
|
-
/**
|
|
318
|
-
* @param {string} key
|
|
319
|
-
*/
|
|
320
|
-
async del(key) {
|
|
321
|
-
if (!initialized) {
|
|
322
|
-
loggy.error("Provider not initialized");
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
if (!this.ready) {
|
|
326
|
-
loggy.error("Provider not ready");
|
|
327
|
-
return null;
|
|
328
|
-
}
|
|
329
|
-
try {
|
|
330
|
-
loggy.info(`PURGING KEY: ${key}`);
|
|
331
|
-
return provider.delete(key);
|
|
332
|
-
} catch (error) {
|
|
333
|
-
loggy.error(`Provider errored:`);
|
|
334
|
-
loggy.error(error);
|
|
335
|
-
return null;
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
async keys() {
|
|
339
|
-
if (!initialized) {
|
|
340
|
-
loggy.error("Provider not initialized");
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
if (!this.ready) {
|
|
344
|
-
loggy.error("Provider not ready");
|
|
345
|
-
return null;
|
|
346
|
-
}
|
|
347
|
-
try {
|
|
348
|
-
return Array.from(provider.keys());
|
|
349
|
-
} catch (error) {
|
|
350
|
-
loggy.error(`Provider errored:`);
|
|
351
|
-
loggy.error(error);
|
|
352
|
-
return null;
|
|
353
|
-
}
|
|
354
|
-
},
|
|
355
|
-
async reset() {
|
|
356
|
-
if (!initialized) {
|
|
357
|
-
loggy.error("Provider not initialized");
|
|
358
|
-
return null;
|
|
359
|
-
}
|
|
360
|
-
if (!this.ready) {
|
|
361
|
-
loggy.error("Provider not ready");
|
|
362
|
-
return null;
|
|
363
|
-
}
|
|
364
|
-
try {
|
|
365
|
-
const allKeys = await this.keys();
|
|
366
|
-
if (!allKeys) {
|
|
367
|
-
loggy.error("Provider not ready");
|
|
368
|
-
return null;
|
|
369
|
-
}
|
|
370
|
-
loggy.info(`PURGING ALL KEYS: ${allKeys.length}`);
|
|
371
|
-
return this.keys().then((keys) => Promise.all(allKeys.map((key) => this.del(key))));
|
|
372
|
-
} catch (error) {
|
|
373
|
-
loggy.error(`Provider errored:`);
|
|
374
|
-
loggy.error(error);
|
|
375
|
-
return null;
|
|
376
|
-
}
|
|
377
|
-
},
|
|
378
|
-
get ready() {
|
|
379
|
-
if (!initialized) {
|
|
380
|
-
loggy.info("Provider not initialized");
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
return true;
|
|
384
|
-
},
|
|
385
|
-
/**
|
|
386
|
-
* @param {RegExp[]} regExps
|
|
387
|
-
*/
|
|
388
|
-
async clearByRegexp(regExps = []) {
|
|
389
|
-
const keys = await this.keys() || [];
|
|
390
|
-
const toDelete = keys.filter((key) => regExps.some((re) => re.test(key)));
|
|
391
|
-
await Promise.all(toDelete.map((key) => this.del(key)));
|
|
392
|
-
}
|
|
393
|
-
};
|
|
394
|
-
cacheInstance = instance;
|
|
395
502
|
return instance;
|
|
396
503
|
}
|
|
397
504
|
};
|
|
@@ -402,7 +509,8 @@ const services = {
|
|
|
402
509
|
const index = {
|
|
403
510
|
register,
|
|
404
511
|
bootstrap,
|
|
405
|
-
destroy
|
|
512
|
+
destroy() {
|
|
513
|
+
},
|
|
406
514
|
config,
|
|
407
515
|
controllers,
|
|
408
516
|
routes,
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../server/src/utils/log.ts","../../server/src/utils/invalidateCache.ts","../../server/src/bootstrap.ts","../../server/src/destroy.ts","../../server/src/utils/key.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/content-api.ts","../../server/src/routes/index.ts","../../server/src/utils/withTimeout.ts","../../server/src/services/service.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","import type { Core } from '@strapi/strapi';\nimport { invalidateCache, invalidateGraphqlCache } from './utils/invalidateCache';\nimport { CacheService } from './types/cache.types';\nimport { loggy } from './utils/log';\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 cacheStore = cacheService.createCache();\n cacheStore.init();\n\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 if (!cacheStore) {\n loggy.error('Plugin could not be initialized');\n return;\n }\n } catch (error) {\n loggy.error('Plugin could not be initialized');\n return;\n }\n loggy.info('Plugin initialized');\n};\n\nexport default bootstrap;\n","import type { Core } from '@strapi/strapi';\n\nconst destroy = ({ strapi }: { strapi: Core.Strapi }) => {\n // destroy phase\n};\n\nexport default destroy;\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 { Context } from 'koa';\nimport { generateCacheKey } from '../utils/key';\nimport { CacheService } from 'src/types/cache.types';\nimport { loggy } from '../utils/log';\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 cacheStore = cacheService.createCache();\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\n if (cacheEntry && !noCache) {\n loggy.info(`HIT with key: ${key}`);\n ctx.status = 200;\n ctx.body = cacheEntry;\n ctx.set({ 'Access-Control-Allow-Origin': '*' });\n return;\n }\n\n await next();\n\n if (\n ctx.body &&\n ctx.method === 'GET' &&\n ctx.status >= 200 &&\n ctx.status < 300 &&\n routeIsCachable\n ) {\n loggy.info(`MISS with key: ${key}`);\n await cacheStore.set(key, ctx.body);\n }\n};\n\nexport default middleware;\n","import rawBody from 'raw-body';\nimport { generateGraphqlCacheKey } from '../utils/key';\nimport { Readable } from 'stream';\nimport { loggy } from '../utils/log';\n\nconst middleware = async (ctx: any, next: any) => {\n const cacheService = strapi.plugin('strapi-cache').services.service;\n const cacheStore = cacheService.createCache();\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 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\n if (cacheEntry && !noCache) {\n loggy.info(`HIT with key: ${key}`);\n ctx.status = 200;\n ctx.body = cacheEntry.body;\n ctx.set(cacheEntry.headers);\n return;\n }\n\n await next();\n\n if (\n ctx.body &&\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 await cacheStore.set(key, {\n body: ctx.body,\n headers: ctx.response.headers,\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 * 1014 * 10,\n allowStale: false,\n contentTypes: [],\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 },\n};\n","export default {};\n","import type { Core } from '@strapi/strapi';\nimport { Context } from 'koa';\n\nconst controller = ({ strapi }: { strapi: Core.Strapi }) => ({\n index(ctx: Context) {\n ctx.body = strapi\n .plugin('strapi-cache')\n // the name of the service file & the method.\n .service('service')\n .getWelcomeMessage();\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: 'GET',\n path: '/',\n // name of the controller file & the method.\n handler: 'controller.index',\n config: {\n policies: [],\n },\n },\n];\n","import contentAPIRoutes from './content-api';\n\nconst routes = {\n 'content-api': {\n type: 'content-api',\n routes: contentAPIRoutes,\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 '../../src/utils/withTimeout';\nimport { CacheProvider, CacheService } from '../types/cache.types';\nimport { loggy } from '../utils/log';\n\nconst service = ({ strapi }: { strapi: Core.Strapi }): CacheService => {\n let cacheInstance: CacheProvider | null = null;\n return {\n createCache() {\n if (cacheInstance) {\n return cacheInstance;\n }\n\n let initialized = false;\n let provider: LRUCache<string, any>;\n\n loggy.info('Creating provider');\n\n const instance: CacheProvider = {\n init() {\n if (initialized) {\n loggy.error('Provider already initialized');\n return;\n }\n\n initialized = true;\n const max = strapi.plugin('strapi-cache').config('max');\n const ttl = strapi.plugin('strapi-cache').config('ttl');\n const size = strapi.plugin('strapi-cache').config('size');\n const allowStale = strapi.plugin('strapi-cache').config('allowStale');\n\n provider = new LRUCache({\n max: Number(max),\n ttl: Number(ttl),\n size: Number(size),\n allowStale: allowStale ? true : false,\n });\n loggy.info('Provider initialized');\n },\n\n /**\n * @param {string} key\n */\n async get(key: string) {\n if (!initialized) {\n loggy.error('Provider not initialized');\n return null;\n }\n\n if (!this.ready) {\n loggy.error('Provider not ready');\n return null;\n }\n\n const getTimeout = 1000;\n return withTimeout(async () => await provider.get(key), getTimeout).catch((error) => {\n if (error?.message === 'timeout') {\n loggy.error(`Provider timed-out after ${getTimeout}ms.`);\n } else {\n loggy.error(`Provider errored:`);\n loggy.error(error);\n }\n return null;\n });\n },\n\n /**\n * @param {string} key\n * @param {any} val\n */\n async set(key: string, val: any) {\n if (!initialized) {\n loggy.error('Provider not initialized');\n return null;\n }\n\n if (!this.ready) {\n loggy.error('Provider not ready');\n return null;\n }\n\n try {\n const size = provider.size;\n return provider.set(key, val);\n } catch (error) {\n loggy.error(`Provider errored:`);\n loggy.error(error);\n return null;\n }\n },\n\n /**\n * @param {string} key\n */\n async del(key: string) {\n if (!initialized) {\n loggy.error('Provider not initialized');\n return null;\n }\n\n if (!this.ready) {\n loggy.error('Provider not ready');\n return null;\n }\n\n try {\n loggy.info(`PURGING KEY: ${key}`);\n return provider.delete(key);\n } catch (error) {\n loggy.error(`Provider errored:`);\n loggy.error(error);\n return null;\n }\n },\n\n async keys() {\n if (!initialized) {\n loggy.error('Provider not initialized');\n return null;\n }\n\n if (!this.ready) {\n loggy.error('Provider not ready');\n return null;\n }\n\n try {\n return Array.from(provider.keys());\n } catch (error) {\n loggy.error(`Provider errored:`);\n loggy.error(error);\n return null;\n }\n },\n\n async reset() {\n if (!initialized) {\n loggy.error('Provider not initialized');\n return null;\n }\n\n if (!this.ready) {\n loggy.error('Provider not ready');\n return null;\n }\n\n try {\n const allKeys = await this.keys();\n if (!allKeys) {\n loggy.error('Provider not ready');\n return null;\n }\n loggy.info(`PURGING ALL KEYS: ${allKeys.length}`);\n return this.keys().then((keys) => Promise.all(allKeys.map((key) => this.del(key))));\n } catch (error) {\n loggy.error(`Provider errored:`);\n loggy.error(error);\n return null;\n }\n },\n\n get ready() {\n if (!initialized) {\n loggy.info('Provider not initialized');\n return false;\n }\n\n return true;\n },\n\n /**\n * @param {RegExp[]} regExps\n */\n async clearByRegexp(regExps: RegExp[] = []) {\n const keys = (await this.keys()) || [];\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 cacheInstance = instance;\n return instance;\n },\n };\n};\n\nexport default service;\n","import service from './service';\n\nexport default {\n service,\n};\n","/**\n * Application methods\n */\nimport bootstrap from './bootstrap';\nimport destroy from './destroy';\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","middleware","rawBody","Readable","graphql","cache","config","LRUCache"],"mappings":"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"}
|
|
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/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 cacheStore = cacheService.getCacheInstance();\n cacheStore.init();\n\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 if (!cacheStore) {\n loggy.error('Plugin could not be initialized');\n return;\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 { Context } from 'koa';\nimport { generateCacheKey } from '../utils/key';\nimport { CacheService } from 'src/types/cache.types';\nimport { loggy } from '../utils/log';\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 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\n if (cacheEntry && !noCache) {\n loggy.info(`HIT with key: ${key}`);\n ctx.status = 200;\n ctx.body = cacheEntry;\n ctx.set({ 'Access-Control-Allow-Origin': '*' });\n return;\n }\n\n await next();\n\n if (\n ctx.body &&\n ctx.method === 'GET' &&\n ctx.status >= 200 &&\n ctx.status < 300 &&\n routeIsCachable\n ) {\n loggy.info(`MISS with key: ${key}`);\n await cacheStore.set(key, ctx.body);\n }\n};\n\nexport default middleware;\n","import rawBody from 'raw-body';\nimport { generateGraphqlCacheKey } from '../utils/key';\nimport { Readable } from 'stream';\nimport { loggy } from '../utils/log';\nimport { CacheService } from 'src/types/cache.types';\n\nconst middleware = async (ctx: any, next: any) => {\n const cacheService = strapi.plugin('strapi-cache').services.service as CacheService;\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 key = generateGraphqlCacheKey(body);\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 cacheEntry = await cacheStore.get(key);\n const cacheControlHeader = ctx.request.headers['cache-control'];\n const noCache = cacheControlHeader && cacheControlHeader.includes('no-cache');\n\n if (cacheEntry && !noCache) {\n loggy.info(`HIT with key: ${key}`);\n ctx.status = 200;\n ctx.body = cacheEntry.body;\n ctx.set(cacheEntry.headers);\n return;\n }\n\n await next();\n\n if (\n ctx.body &&\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 await cacheStore.set(key, {\n body: ctx.body,\n headers: ctx.response.headers,\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 * 1014 * 10,\n allowStale: false,\n cacheableRoutes: [],\n provider: 'memory',\n redisUrl: env('REDIS_URL'),\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' && !config.redisUrl && typeof config.redisUrl !== 'string') {\n throw new Error(`Invalid config: redisUrl must be set when using redis provider`);\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});\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","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\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 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 const timeout = 1000;\n return withTimeout(\n () =>\n new Promise((resolve) => {\n resolve(this.provider.get(key));\n }),\n timeout\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 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;\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\n const redisUrl =\n this.strapi.plugin('strapi-cache').config('redisUrl') || 'redis://localhost:6379';\n\n this.client = new Redis(redisUrl);\n this.initialized = true;\n\n loggy.info('Redis provider initialized');\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 const timeout = 1000;\n return withTimeout(() => this.client.get(key), timeout)\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 const ttl = Number(this.strapi.plugin('strapi-cache').config('ttl'));\n const serialized = JSON.stringify(val);\n if (ttl > 0) {\n await this.client.set(key, serialized, 'EX', ttl);\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","middleware","rawBody","Readable","graphql","cache","config","service","LRUCache","Redis"],"mappings":"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"}
|