strapi-cache 1.6.2 → 1.8.1
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 +7 -2
- package/dist/_chunks/de-C_gFyhHL.mjs +18 -0
- package/dist/_chunks/de-l6MDDAXu.js +18 -0
- package/dist/_chunks/en-D9w03LyX.js +18 -0
- package/dist/_chunks/en-bK2OKwtN.mjs +18 -0
- package/dist/_chunks/index-B_MAAg0W.js +52 -0
- package/dist/_chunks/index-BwuX8jJl.mjs +396 -0
- package/dist/_chunks/index-CkLhx2ik.mjs +52 -0
- package/dist/_chunks/index-D_ssKKxu.js +395 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/components/PurgeCacheButton/index.d.ts +1 -1
- package/dist/admin/src/components/PurgeModal/index.d.ts +2 -1
- package/dist/admin/src/hooks/useCacheConfig.d.ts +1 -0
- package/dist/admin/src/hooks/useCacheOperations.d.ts +1 -0
- package/dist/server/index.js +999 -0
- package/dist/server/index.mjs +997 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +28 -0
- package/dist/server/src/content-types/index.d.ts +2 -0
- package/dist/server/src/controllers/controller.d.ts +10 -0
- package/dist/server/src/controllers/index.d.ts +11 -0
- package/dist/server/src/index.d.ts +76 -0
- package/dist/server/src/middlewares/cache.d.ts +3 -0
- package/dist/server/src/middlewares/graphql.d.ts +2 -0
- package/dist/server/src/middlewares/index.d.ts +6 -0
- package/dist/server/src/permissions.d.ts +6 -0
- package/dist/server/src/policies/index.d.ts +2 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/index.d.ts +19 -0
- package/dist/server/src/routes/purge.d.ts +14 -0
- package/dist/server/src/services/index.d.ts +8 -0
- package/dist/server/src/services/memory/provider.d.ts +17 -0
- package/dist/server/src/services/memory/service.d.ts +6 -0
- package/dist/server/src/services/redis/provider.d.ts +23 -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 +18 -0
- package/dist/server/src/utils/body.d.ts +7 -0
- package/dist/server/src/utils/graphql.d.ts +6 -0
- package/dist/server/src/utils/header.d.ts +10 -0
- package/dist/server/src/utils/invalidateCache.d.ts +8 -0
- package/dist/server/src/utils/key.d.ts +5 -0
- package/dist/server/src/utils/log.d.ts +5 -0
- package/dist/server/src/utils/withTimeout.d.ts +1 -0
- package/package.json +16 -2
package/README.md
CHANGED
|
@@ -50,6 +50,7 @@ In your Strapi project, navigate to `config/plugins.js` and add the following co
|
|
|
50
50
|
size: 1024 * 1024 * 1024, // Maximum size of the cache (1 GB) (only for memory cache)
|
|
51
51
|
allowStale: false, // Allow stale cache items (only for memory cache)
|
|
52
52
|
cacheableRoutes: ['/api/products', '/api/categories'], // Caches routes which start with these paths (if empty array, all '/api' routes are cached)
|
|
53
|
+
// cacheableEntities: ['products', 'categories'], // (Optional) Specify which entities to cache. When set, only these entities will be cached (ignores cacheableRoutes). If not set (undefined), cacheableRoutes logic is used
|
|
53
54
|
excludeRoutes: ['/api/products/private'], // (NEW) Exclude routes which start with these paths from being cached (takes precedence over cacheableRoutes). **Note:** `excludeRoutes` takes precedence over `cacheableRoutes`.
|
|
54
55
|
provider: 'memory', // Cache provider ('memory' or 'redis')
|
|
55
56
|
redisConfig: env('REDIS_URL', 'redis://localhost:6379'), // Redis config takes either a string or an object see https://github.com/redis/ioredis for references to what object is available, the object or string is passed directly to ioredis client (if using Redis)
|
|
@@ -61,8 +62,10 @@ In your Strapi project, navigate to `config/plugins.js` and add the following co
|
|
|
61
62
|
cacheAuthorizedRequests: false, // Cache requests with authorization headers (set to true if you want to cache authorized requests)
|
|
62
63
|
cacheGetTimeoutInMs: 1000, // Timeout for getting cached data in milliseconds (default is 1 second)
|
|
63
64
|
autoPurgeCache: true, // Automatically purge cache on content CRUD operations
|
|
65
|
+
autoPurgeGraphQL: true, // Automatically purge GraphQL cache on content CRUD operations
|
|
64
66
|
autoPurgeCacheOnStart: true, // Automatically purge cache on Strapi startup
|
|
65
67
|
disableAdminPopups: false, // Disable popups in the admin panel
|
|
68
|
+
disableAdminButtons: false, // Disable the purge cache buttons in the admin panel (list view and edit view)
|
|
66
69
|
},
|
|
67
70
|
},
|
|
68
71
|
```
|
|
@@ -84,17 +87,19 @@ All of these routes are protected by the policies `admin::isAuthenticatedAdmin`
|
|
|
84
87
|
- **Packages**: Uses [lru-cache](https://github.com/isaacs/node-lru-cache) for in-memory cache. Uses [ioredis](https://github.com/redis/ioredis) for Redis caching.
|
|
85
88
|
- **Automatic Invalidation**: Cache is cleared automatically when content is updated, deleted, or created. (GraphQL cache clears on any content update.)
|
|
86
89
|
- **`no-cache` Header Support**: Respects the `no-cache` header, letting you skip the cache by setting `Cache-Control: no-cache` in your request.
|
|
87
|
-
- **Default Cached Requests**: By default, caches all GET requests to `/api` (or whatever prefix you defined) and POST requests to `/graphql`. You can customize which
|
|
90
|
+
- **Default Cached Requests**: By default, caches all GET requests to `/api` (or whatever prefix you defined) and POST requests to `/graphql`. You can customize which routes or entities to cache using `cacheableRoutes` or `cacheableEntities` config options.
|
|
88
91
|
|
|
89
92
|
## 🔮 Planned Features
|
|
90
93
|
|
|
91
94
|
- [x] **Cache Invalidation**: Automatically invalidate cache on content updates, deletions, or creations.
|
|
92
95
|
- [x] **GraphQL Caching**: Cache GraphQL queries.
|
|
93
96
|
- [x] **Purge Cache Button**: Add a UI option in the Strapi admin panel to manually purge the cache for content-types.
|
|
94
|
-
- [
|
|
97
|
+
- [x] **Purge Whole Cache Button**: Add a UI option in the Strapi admin settings panel to purge the whole cache.
|
|
95
98
|
- [x] **Route/Content-Type Specific Caching**: Allow users to define which routes should be cached based.
|
|
96
99
|
- [x] **Switchable Cache Providers**: Explore support for other caching providers like Redis for distributed caching.
|
|
97
100
|
|
|
101
|
+
If you have any feature requests or suggestions, please open a dedicated issue.
|
|
102
|
+
|
|
98
103
|
## 🛑 Problems
|
|
99
104
|
|
|
100
105
|
If you encounter any issues, please feel free to open an issue on the [GitHub repo](https://github.com/TupiC/strapi-cache/issues/new).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const de = {
|
|
2
|
+
"strapi-cache.name": "Strapi-Cache",
|
|
3
|
+
"strapi-cache.cache.purge": "Cache leeren",
|
|
4
|
+
"strapi-cache.cache.purge.entity": "Entity-Cache leeren",
|
|
5
|
+
"strapi-cache.cache.cancel": "Nein, abbrechen",
|
|
6
|
+
"strapi-cache.cache.confirm": "Ja, leeren",
|
|
7
|
+
"strapi-cache.cache.purge.confirmation": "Dies löscht alle Schlüssel, bei denen {key} Teil des Schlüssels ist. Sind Sie sicher, dass Sie den Cache leeren möchten?",
|
|
8
|
+
"strapi-cache.cache.purge.confirmation-all": "Dies löscht alle Schlüssel aus dem Cache. Sind Sie sicher, dass Sie den gesamten Cache leeren möchten?",
|
|
9
|
+
"strapi-cache.cache.purge.success": "Cache erfolgreich für {key} geleert",
|
|
10
|
+
"strapi-cache.cache.purge.error": "Fehler beim Leeren des Caches für {key}",
|
|
11
|
+
"strapi-cache.cache.purge.no-content-type": "Kein Inhaltstyp gefunden",
|
|
12
|
+
"strapi-cache.cache.routes.fetch-error": "Cacheable-Routen konnten nicht abgerufen werden. Die Cache-Löschung funktioniert möglicherweise nicht korrekt.",
|
|
13
|
+
"strapi-cache.settings.description": "Geben Sie unten einen Cache-Schlüssel ein und klicken Sie auf 'Cache leeren', um bestimmte gecachte Inhalte zu löschen.",
|
|
14
|
+
"strapi-cache.settings.key-placeholder": "Cache-Schlüssel zum Leeren eingeben"
|
|
15
|
+
};
|
|
16
|
+
export {
|
|
17
|
+
de as default
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const de = {
|
|
4
|
+
"strapi-cache.name": "Strapi-Cache",
|
|
5
|
+
"strapi-cache.cache.purge": "Cache leeren",
|
|
6
|
+
"strapi-cache.cache.purge.entity": "Entity-Cache leeren",
|
|
7
|
+
"strapi-cache.cache.cancel": "Nein, abbrechen",
|
|
8
|
+
"strapi-cache.cache.confirm": "Ja, leeren",
|
|
9
|
+
"strapi-cache.cache.purge.confirmation": "Dies löscht alle Schlüssel, bei denen {key} Teil des Schlüssels ist. Sind Sie sicher, dass Sie den Cache leeren möchten?",
|
|
10
|
+
"strapi-cache.cache.purge.confirmation-all": "Dies löscht alle Schlüssel aus dem Cache. Sind Sie sicher, dass Sie den gesamten Cache leeren möchten?",
|
|
11
|
+
"strapi-cache.cache.purge.success": "Cache erfolgreich für {key} geleert",
|
|
12
|
+
"strapi-cache.cache.purge.error": "Fehler beim Leeren des Caches für {key}",
|
|
13
|
+
"strapi-cache.cache.purge.no-content-type": "Kein Inhaltstyp gefunden",
|
|
14
|
+
"strapi-cache.cache.routes.fetch-error": "Cacheable-Routen konnten nicht abgerufen werden. Die Cache-Löschung funktioniert möglicherweise nicht korrekt.",
|
|
15
|
+
"strapi-cache.settings.description": "Geben Sie unten einen Cache-Schlüssel ein und klicken Sie auf 'Cache leeren', um bestimmte gecachte Inhalte zu löschen.",
|
|
16
|
+
"strapi-cache.settings.key-placeholder": "Cache-Schlüssel zum Leeren eingeben"
|
|
17
|
+
};
|
|
18
|
+
exports.default = de;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const en = {
|
|
4
|
+
"strapi-cache.name": "Strapi-Cache",
|
|
5
|
+
"strapi-cache.cache.purge": "Purge Cache",
|
|
6
|
+
"strapi-cache.cache.purge.entity": "Purge Entity Cache",
|
|
7
|
+
"strapi-cache.cache.cancel": "No, cancel",
|
|
8
|
+
"strapi-cache.cache.confirm": "Yes, purge",
|
|
9
|
+
"strapi-cache.cache.purge.confirmation": "This purges all keys where {key} is part of the key. Are you sure you want to purge the cache?",
|
|
10
|
+
"strapi-cache.cache.purge.confirmation-all": "This purges all items in the cache. Are you sure you want to purge the cache?",
|
|
11
|
+
"strapi-cache.cache.purge.success": "Cache purged successfully for {key}",
|
|
12
|
+
"strapi-cache.cache.purge.error": "Error purging cache for {key}",
|
|
13
|
+
"strapi-cache.cache.purge.no-content-type": "No content type found",
|
|
14
|
+
"strapi-cache.cache.routes.fetch-error": "Unable to fetch cacheable routes. Cache purge may not work correctly.",
|
|
15
|
+
"strapi-cache.settings.description": "Enter a cache key below and click 'Purge Cache' to clear specific cached content.",
|
|
16
|
+
"strapi-cache.settings.key-placeholder": "Enter cache key to purge"
|
|
17
|
+
};
|
|
18
|
+
exports.default = en;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const en = {
|
|
2
|
+
"strapi-cache.name": "Strapi-Cache",
|
|
3
|
+
"strapi-cache.cache.purge": "Purge Cache",
|
|
4
|
+
"strapi-cache.cache.purge.entity": "Purge Entity Cache",
|
|
5
|
+
"strapi-cache.cache.cancel": "No, cancel",
|
|
6
|
+
"strapi-cache.cache.confirm": "Yes, purge",
|
|
7
|
+
"strapi-cache.cache.purge.confirmation": "This purges all keys where {key} is part of the key. Are you sure you want to purge the cache?",
|
|
8
|
+
"strapi-cache.cache.purge.confirmation-all": "This purges all items in the cache. Are you sure you want to purge the cache?",
|
|
9
|
+
"strapi-cache.cache.purge.success": "Cache purged successfully for {key}",
|
|
10
|
+
"strapi-cache.cache.purge.error": "Error purging cache for {key}",
|
|
11
|
+
"strapi-cache.cache.purge.no-content-type": "No content type found",
|
|
12
|
+
"strapi-cache.cache.routes.fetch-error": "Unable to fetch cacheable routes. Cache purge may not work correctly.",
|
|
13
|
+
"strapi-cache.settings.description": "Enter a cache key below and click 'Purge Cache' to clear specific cached content.",
|
|
14
|
+
"strapi-cache.settings.key-placeholder": "Enter cache key to purge"
|
|
15
|
+
};
|
|
16
|
+
export {
|
|
17
|
+
en as default
|
|
18
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const designSystem = require("@strapi/design-system");
|
|
5
|
+
const reactIntl = require("react-intl");
|
|
6
|
+
const index = require("./index-D_ssKKxu.js");
|
|
7
|
+
const react = require("react");
|
|
8
|
+
const SettingsPage = () => {
|
|
9
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
10
|
+
const [keyToUse, setKeyToUse] = react.useState("");
|
|
11
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "20px" }, children: [
|
|
12
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", as: "h1", children: formatMessage({
|
|
13
|
+
id: "strapi-cache.name",
|
|
14
|
+
defaultMessage: "Strapi Cache Settings"
|
|
15
|
+
}) }),
|
|
16
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: "16px", marginBottom: "16px" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: formatMessage({
|
|
17
|
+
id: "strapi-cache.settings.description",
|
|
18
|
+
defaultMessage: 'Enter a cache key below and click "Purge Cache" to clear specific cached content.'
|
|
19
|
+
}) }) }),
|
|
20
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
21
|
+
"div",
|
|
22
|
+
{
|
|
23
|
+
style: {
|
|
24
|
+
display: "flex",
|
|
25
|
+
justifyContent: "center",
|
|
26
|
+
alignItems: "center",
|
|
27
|
+
gap: "12px",
|
|
28
|
+
maxWidth: "400px",
|
|
29
|
+
marginBottom: "16px"
|
|
30
|
+
},
|
|
31
|
+
children: [
|
|
32
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
33
|
+
designSystem.TextInput,
|
|
34
|
+
{
|
|
35
|
+
placeholder: formatMessage({
|
|
36
|
+
id: "strapi-cache.settings.key-placeholder",
|
|
37
|
+
defaultMessage: "Enter cache key to purge"
|
|
38
|
+
}),
|
|
39
|
+
size: "M",
|
|
40
|
+
type: "text",
|
|
41
|
+
value: keyToUse,
|
|
42
|
+
onChange: (e) => setKeyToUse(e.target.value)
|
|
43
|
+
}
|
|
44
|
+
) }),
|
|
45
|
+
/* @__PURE__ */ jsxRuntime.jsx(index.PurgeModal, { buttonText: "Purge Cache", keyToUse, isSettingsPage: true })
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
),
|
|
49
|
+
/* @__PURE__ */ jsxRuntime.jsx(index.PurgeModal, { buttonText: "Purge All", isPurgeAll: true, isSettingsPage: true })
|
|
50
|
+
] });
|
|
51
|
+
};
|
|
52
|
+
exports.default = SettingsPage;
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { useRef, useEffect, useState } from "react";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useFetchClient, useRBAC, useNotification, unstable_useContentManagerContext } from "@strapi/strapi/admin";
|
|
4
|
+
import { useIntl } from "react-intl";
|
|
5
|
+
import { Archive } from "@strapi/icons";
|
|
6
|
+
import { Modal, Button, Typography } from "@strapi/design-system";
|
|
7
|
+
const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
|
|
8
|
+
const v = glob[path];
|
|
9
|
+
if (v) {
|
|
10
|
+
return typeof v === "function" ? v() : Promise.resolve(v);
|
|
11
|
+
}
|
|
12
|
+
return new Promise((_, reject) => {
|
|
13
|
+
(typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
|
|
14
|
+
reject.bind(
|
|
15
|
+
null,
|
|
16
|
+
new Error(
|
|
17
|
+
"Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
|
|
18
|
+
)
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
const PLUGIN_ID = "strapi-cache";
|
|
24
|
+
const Initializer = ({ setPlugin }) => {
|
|
25
|
+
const ref = useRef(setPlugin);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
ref.current(PLUGIN_ID);
|
|
28
|
+
}, []);
|
|
29
|
+
return null;
|
|
30
|
+
};
|
|
31
|
+
const useCacheConfig = (enabled = true) => {
|
|
32
|
+
const [config, setConfig] = useState();
|
|
33
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
34
|
+
const [error, setError] = useState(null);
|
|
35
|
+
const { get } = useFetchClient();
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!enabled) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const fetchConfig = async () => {
|
|
41
|
+
setIsLoading(true);
|
|
42
|
+
setError(null);
|
|
43
|
+
try {
|
|
44
|
+
const { data } = await get("/strapi-cache/config");
|
|
45
|
+
setConfig(data);
|
|
46
|
+
} catch (error2) {
|
|
47
|
+
setError(error2);
|
|
48
|
+
} finally {
|
|
49
|
+
setIsLoading(false);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
fetchConfig();
|
|
53
|
+
}, [enabled, get]);
|
|
54
|
+
return {
|
|
55
|
+
config,
|
|
56
|
+
isLoading,
|
|
57
|
+
error,
|
|
58
|
+
refetch: () => {
|
|
59
|
+
const fetchConfig = async () => {
|
|
60
|
+
setIsLoading(true);
|
|
61
|
+
setError(null);
|
|
62
|
+
try {
|
|
63
|
+
const { data } = await get("/strapi-cache/config");
|
|
64
|
+
setConfig(data);
|
|
65
|
+
} catch (error2) {
|
|
66
|
+
setError(error2);
|
|
67
|
+
} finally {
|
|
68
|
+
setIsLoading(false);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
fetchConfig();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
const pluginPermissions = {
|
|
76
|
+
purge: [{ action: "plugin::strapi-cache.purge-cache", subject: null }]
|
|
77
|
+
};
|
|
78
|
+
const useCachePermissions = () => {
|
|
79
|
+
const { allowedActions } = useRBAC(pluginPermissions);
|
|
80
|
+
return {
|
|
81
|
+
canPurgeCache: allowedActions.canPurgeCache,
|
|
82
|
+
allowedActions
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
const useCacheOperations = () => {
|
|
86
|
+
const { post } = useFetchClient();
|
|
87
|
+
const isCacheableRoute = (keyToUse, contentTypeName, config) => {
|
|
88
|
+
if (!keyToUse || !config) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
const { cacheableRoutes } = config;
|
|
92
|
+
return cacheableRoutes.length === 0 || cacheableRoutes.some((route) => {
|
|
93
|
+
return route.includes(keyToUse) || contentTypeName && route.includes(contentTypeName);
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
const clearCache = async (keyToUse) => {
|
|
97
|
+
if (!keyToUse) {
|
|
98
|
+
return {
|
|
99
|
+
success: false,
|
|
100
|
+
message: "No content type found"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
await post(
|
|
105
|
+
`/strapi-cache/purge-cache/key`,
|
|
106
|
+
{ key: keyToUse },
|
|
107
|
+
{
|
|
108
|
+
headers: {
|
|
109
|
+
"Content-Type": "application/json"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
return {
|
|
114
|
+
success: true,
|
|
115
|
+
message: `Cache purged successfully for key: "${keyToUse}"`
|
|
116
|
+
};
|
|
117
|
+
} catch (error) {
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
message: `Error purging cache for key: "${keyToUse}"`,
|
|
121
|
+
error
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const clearAllCache = async () => {
|
|
126
|
+
try {
|
|
127
|
+
await post(
|
|
128
|
+
`/strapi-cache/purge-cache`,
|
|
129
|
+
{},
|
|
130
|
+
{
|
|
131
|
+
headers: {
|
|
132
|
+
"Content-Type": "application/json"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
return {
|
|
137
|
+
success: true,
|
|
138
|
+
message: "All cache purged successfully"
|
|
139
|
+
};
|
|
140
|
+
} catch (error) {
|
|
141
|
+
return {
|
|
142
|
+
success: false,
|
|
143
|
+
message: "Error purging all cache",
|
|
144
|
+
error
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
return {
|
|
149
|
+
isCacheableRoute,
|
|
150
|
+
clearCache,
|
|
151
|
+
clearAllCache
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
const useCacheNotifications = (config) => {
|
|
155
|
+
const formatMessage = useIntl().formatMessage;
|
|
156
|
+
const { toggleNotification } = useNotification();
|
|
157
|
+
const showConfigFetchError = (error) => {
|
|
158
|
+
const isPermissionError = error?.response?.status === 403 || error?.response?.status === 401;
|
|
159
|
+
if (!isPermissionError && !config?.disableAdminPopups) {
|
|
160
|
+
toggleNotification({
|
|
161
|
+
type: "warning",
|
|
162
|
+
message: formatMessage({
|
|
163
|
+
id: "strapi-cache.cache.routes.fetch-error",
|
|
164
|
+
defaultMessage: "Unable to fetch cache config. Cache purge may not work correctly."
|
|
165
|
+
})
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const showPurgeSuccess = (key) => {
|
|
170
|
+
if (!config?.disableAdminPopups) {
|
|
171
|
+
toggleNotification({
|
|
172
|
+
type: "success",
|
|
173
|
+
message: formatMessage(
|
|
174
|
+
{
|
|
175
|
+
id: "strapi-cache.cache.purge.success",
|
|
176
|
+
defaultMessage: "Cache purged successfully"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
key: `"${key}"`
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const showPurgeError = (key) => {
|
|
186
|
+
if (!config?.disableAdminPopups) {
|
|
187
|
+
toggleNotification({
|
|
188
|
+
type: "danger",
|
|
189
|
+
message: formatMessage(
|
|
190
|
+
{
|
|
191
|
+
id: "strapi-cache.cache.purge.error",
|
|
192
|
+
defaultMessage: "Error purging cache"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
key: `"${key}"`
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
const showNoContentTypeWarning = () => {
|
|
202
|
+
if (!config?.disableAdminPopups) {
|
|
203
|
+
toggleNotification({
|
|
204
|
+
type: "warning",
|
|
205
|
+
message: formatMessage({
|
|
206
|
+
id: "strapi-cache.cache.purge.no-content-type",
|
|
207
|
+
defaultMessage: "No content type found"
|
|
208
|
+
})
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
showConfigFetchError,
|
|
214
|
+
showPurgeSuccess,
|
|
215
|
+
showPurgeError,
|
|
216
|
+
showNoContentTypeWarning
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
function PurgeModal({
|
|
220
|
+
buttonText,
|
|
221
|
+
keyToUse,
|
|
222
|
+
buttonWidth,
|
|
223
|
+
contentTypeName,
|
|
224
|
+
isSettingsPage,
|
|
225
|
+
isPurgeAll
|
|
226
|
+
}) {
|
|
227
|
+
const { canPurgeCache } = useCachePermissions();
|
|
228
|
+
const { config, error: configError } = useCacheConfig(canPurgeCache);
|
|
229
|
+
const { isCacheableRoute, clearCache, clearAllCache } = useCacheOperations();
|
|
230
|
+
const { showConfigFetchError, showPurgeSuccess, showPurgeError, showNoContentTypeWarning } = useCacheNotifications(config);
|
|
231
|
+
const formatMessage = useIntl().formatMessage;
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
if (configError) {
|
|
234
|
+
showConfigFetchError(configError);
|
|
235
|
+
}
|
|
236
|
+
}, [configError, showConfigFetchError]);
|
|
237
|
+
const handleClearCache = async () => {
|
|
238
|
+
if (isPurgeAll) {
|
|
239
|
+
const result2 = await clearAllCache();
|
|
240
|
+
if (result2.success) {
|
|
241
|
+
showPurgeSuccess("all keys");
|
|
242
|
+
} else {
|
|
243
|
+
showPurgeError("all keys");
|
|
244
|
+
}
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (!keyToUse) {
|
|
248
|
+
showNoContentTypeWarning();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const result = await clearCache(keyToUse);
|
|
252
|
+
if (result.success) {
|
|
253
|
+
showPurgeSuccess(keyToUse);
|
|
254
|
+
} else {
|
|
255
|
+
showPurgeError(keyToUse);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
if (!canPurgeCache || !isPurgeAll && !isSettingsPage && !isCacheableRoute(keyToUse, contentTypeName, config)) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
return /* @__PURE__ */ jsxs(Modal.Root, { children: [
|
|
262
|
+
/* @__PURE__ */ jsx(Modal.Trigger, { children: /* @__PURE__ */ jsx(
|
|
263
|
+
Button,
|
|
264
|
+
{
|
|
265
|
+
width: buttonWidth,
|
|
266
|
+
disabled: !isPurgeAll && !keyToUse,
|
|
267
|
+
startIcon: /* @__PURE__ */ jsx(Archive, {}),
|
|
268
|
+
variant: "danger",
|
|
269
|
+
children: buttonText
|
|
270
|
+
}
|
|
271
|
+
) }),
|
|
272
|
+
/* @__PURE__ */ jsxs(Modal.Content, { children: [
|
|
273
|
+
/* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: buttonText }) }),
|
|
274
|
+
/* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: isPurgeAll ? formatMessage({
|
|
275
|
+
id: "strapi-cache.cache.purge.confirmation-all",
|
|
276
|
+
defaultMessage: "This purges all items in the cache. Are you sure you want to purge the cache?"
|
|
277
|
+
}) : formatMessage(
|
|
278
|
+
{
|
|
279
|
+
id: "strapi-cache.cache.purge.confirmation",
|
|
280
|
+
defaultMessage: "Are you sure you want to purge the cache?"
|
|
281
|
+
},
|
|
282
|
+
{ key: `"${keyToUse}"` }
|
|
283
|
+
) }) }),
|
|
284
|
+
/* @__PURE__ */ jsxs(Modal.Footer, { children: [
|
|
285
|
+
/* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
|
|
286
|
+
id: "strapi-cache.cache.cancel",
|
|
287
|
+
defaultMessage: "No, cancel"
|
|
288
|
+
}) }) }),
|
|
289
|
+
/* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { onClick: handleClearCache, children: formatMessage({
|
|
290
|
+
id: "strapi-cache.cache.confirm",
|
|
291
|
+
defaultMessage: "Yes, confirm"
|
|
292
|
+
}) }) })
|
|
293
|
+
] })
|
|
294
|
+
] })
|
|
295
|
+
] });
|
|
296
|
+
}
|
|
297
|
+
function PurgeCacheButton() {
|
|
298
|
+
const { contentType } = unstable_useContentManagerContext();
|
|
299
|
+
const { formatMessage } = useIntl();
|
|
300
|
+
const { config } = useCacheConfig();
|
|
301
|
+
const keyToUse = contentType?.info.pluralName;
|
|
302
|
+
if (config?.disableAdminButtons) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
return /* @__PURE__ */ jsx(
|
|
306
|
+
PurgeModal,
|
|
307
|
+
{
|
|
308
|
+
buttonText: formatMessage({
|
|
309
|
+
id: "strapi-cache.cache.purge",
|
|
310
|
+
defaultMessage: "Purge Cache"
|
|
311
|
+
}),
|
|
312
|
+
keyToUse
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
function PurgeEntityButton() {
|
|
317
|
+
const { formatMessage } = useIntl();
|
|
318
|
+
const { id, isSingleType, contentType } = unstable_useContentManagerContext();
|
|
319
|
+
const { config } = useCacheConfig();
|
|
320
|
+
const apiPath = isSingleType ? contentType?.info.singularName : id;
|
|
321
|
+
if (!apiPath) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
if (config?.disableAdminButtons) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
const keyToUse = encodeURIComponent(apiPath);
|
|
328
|
+
const contentTypeName = isSingleType ? contentType?.info.singularName : contentType?.info.pluralName;
|
|
329
|
+
return /* @__PURE__ */ jsx(
|
|
330
|
+
PurgeModal,
|
|
331
|
+
{
|
|
332
|
+
buttonWidth: "100%",
|
|
333
|
+
buttonText: formatMessage({
|
|
334
|
+
id: "strapi-cache.cache.purge.entity",
|
|
335
|
+
defaultMessage: "Purge Entity Cache"
|
|
336
|
+
}),
|
|
337
|
+
keyToUse,
|
|
338
|
+
contentTypeName
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
const index = {
|
|
343
|
+
register(app) {
|
|
344
|
+
app.registerPlugin({
|
|
345
|
+
id: PLUGIN_ID,
|
|
346
|
+
initializer: Initializer,
|
|
347
|
+
isReady: false,
|
|
348
|
+
name: PLUGIN_ID
|
|
349
|
+
});
|
|
350
|
+
app.getPlugin("content-manager").injectComponent("listView", "actions", {
|
|
351
|
+
name: PurgeCacheButton,
|
|
352
|
+
Component: PurgeCacheButton
|
|
353
|
+
});
|
|
354
|
+
app.getPlugin("content-manager").injectComponent("editView", "right-links", {
|
|
355
|
+
name: PurgeEntityButton,
|
|
356
|
+
Component: PurgeEntityButton
|
|
357
|
+
});
|
|
358
|
+
app.createSettingSection(
|
|
359
|
+
{
|
|
360
|
+
id: PLUGIN_ID,
|
|
361
|
+
intlLabel: {
|
|
362
|
+
id: "strapi-cache.settings.link",
|
|
363
|
+
defaultMessage: "Strapi Cache"
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
[
|
|
367
|
+
{
|
|
368
|
+
intlLabel: {
|
|
369
|
+
id: "strapi-cache.settings.link",
|
|
370
|
+
defaultMessage: "Strapi Cache"
|
|
371
|
+
},
|
|
372
|
+
id: "settings",
|
|
373
|
+
to: `${PLUGIN_ID}/settings`,
|
|
374
|
+
Component: () => import("./index-CkLhx2ik.mjs"),
|
|
375
|
+
permissions: []
|
|
376
|
+
}
|
|
377
|
+
]
|
|
378
|
+
);
|
|
379
|
+
},
|
|
380
|
+
async registerTrads({ locales }) {
|
|
381
|
+
return Promise.all(
|
|
382
|
+
locales.map(async (locale) => {
|
|
383
|
+
try {
|
|
384
|
+
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/de.json": () => import("./de-C_gFyhHL.mjs"), "./translations/en.json": () => import("./en-bK2OKwtN.mjs") }), `./translations/${locale}.json`, 3);
|
|
385
|
+
return { data, locale };
|
|
386
|
+
} catch {
|
|
387
|
+
return { data: {}, locale };
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
export {
|
|
394
|
+
PurgeModal as P,
|
|
395
|
+
index as i
|
|
396
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Typography, TextInput } from "@strapi/design-system";
|
|
3
|
+
import { useIntl } from "react-intl";
|
|
4
|
+
import { P as PurgeModal } from "./index-BwuX8jJl.mjs";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
const SettingsPage = () => {
|
|
7
|
+
const { formatMessage } = useIntl();
|
|
8
|
+
const [keyToUse, setKeyToUse] = useState("");
|
|
9
|
+
return /* @__PURE__ */ jsxs("div", { style: { padding: "20px" }, children: [
|
|
10
|
+
/* @__PURE__ */ jsx(Typography, { variant: "alpha", as: "h1", children: formatMessage({
|
|
11
|
+
id: "strapi-cache.name",
|
|
12
|
+
defaultMessage: "Strapi Cache Settings"
|
|
13
|
+
}) }),
|
|
14
|
+
/* @__PURE__ */ jsx("div", { style: { marginTop: "16px", marginBottom: "16px" }, children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: formatMessage({
|
|
15
|
+
id: "strapi-cache.settings.description",
|
|
16
|
+
defaultMessage: 'Enter a cache key below and click "Purge Cache" to clear specific cached content.'
|
|
17
|
+
}) }) }),
|
|
18
|
+
/* @__PURE__ */ jsxs(
|
|
19
|
+
"div",
|
|
20
|
+
{
|
|
21
|
+
style: {
|
|
22
|
+
display: "flex",
|
|
23
|
+
justifyContent: "center",
|
|
24
|
+
alignItems: "center",
|
|
25
|
+
gap: "12px",
|
|
26
|
+
maxWidth: "400px",
|
|
27
|
+
marginBottom: "16px"
|
|
28
|
+
},
|
|
29
|
+
children: [
|
|
30
|
+
/* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
|
|
31
|
+
TextInput,
|
|
32
|
+
{
|
|
33
|
+
placeholder: formatMessage({
|
|
34
|
+
id: "strapi-cache.settings.key-placeholder",
|
|
35
|
+
defaultMessage: "Enter cache key to purge"
|
|
36
|
+
}),
|
|
37
|
+
size: "M",
|
|
38
|
+
type: "text",
|
|
39
|
+
value: keyToUse,
|
|
40
|
+
onChange: (e) => setKeyToUse(e.target.value)
|
|
41
|
+
}
|
|
42
|
+
) }),
|
|
43
|
+
/* @__PURE__ */ jsx(PurgeModal, { buttonText: "Purge Cache", keyToUse, isSettingsPage: true })
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
),
|
|
47
|
+
/* @__PURE__ */ jsx(PurgeModal, { buttonText: "Purge All", isPurgeAll: true, isSettingsPage: true })
|
|
48
|
+
] });
|
|
49
|
+
};
|
|
50
|
+
export {
|
|
51
|
+
SettingsPage as default
|
|
52
|
+
};
|