@vegan-friendly/strapi-plugin-elasticsearch 0.3.3 → 0.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/dist/admin/src/components/SubNavigation/index.js +6 -0
- package/dist/admin/src/pages/App/index.js +3 -0
- package/dist/admin/src/pages/VirtualCollections/index.d.ts +3 -0
- package/dist/admin/src/pages/VirtualCollections/index.js +194 -0
- package/dist/admin/src/utils/apiUrls.d.ts +3 -0
- package/dist/admin/src/utils/apiUrls.js +7 -1
- package/dist/package.json +2 -2
- package/dist/server/controllers/index.d.ts +28 -0
- package/dist/server/controllers/index.js +2 -0
- package/dist/server/controllers/virtual-collections.d.ts +29 -0
- package/dist/server/controllers/virtual-collections.js +164 -0
- package/dist/server/index.d.ts +39 -0
- package/dist/server/routes/index.d.ts +11 -0
- package/dist/server/routes/index.js +2 -0
- package/dist/server/routes/virtual-collections.d.ts +12 -0
- package/dist/server/routes/virtual-collections.js +26 -0
- package/dist/server/services/es-interface.js +44 -0
- package/dist/server/services/schedule-indexing.js +1 -1
- package/dist/server/types/esInterface.type.d.ts +18 -0
- package/package.json +2 -2
@@ -26,6 +26,12 @@ const SubNavigation = ({ activeUrl = null }) => {
|
|
26
26
|
},
|
27
27
|
{
|
28
28
|
id: 3,
|
29
|
+
label: 'Virtual Collections',
|
30
|
+
icon: icons_1.Connector,
|
31
|
+
to: `/plugins/${pluginId_1.default}/virtual-collections`,
|
32
|
+
},
|
33
|
+
{
|
34
|
+
id: 4,
|
29
35
|
label: 'Indexing Run Logs',
|
30
36
|
icon: icons_1.Connector,
|
31
37
|
to: `/plugins/${pluginId_1.default}/view-indexing-logs`,
|
@@ -16,13 +16,16 @@ const pluginId_1 = __importDefault(require("../../pluginId"));
|
|
16
16
|
const ConfigureCollectionList_1 = __importDefault(require("../ConfigureCollectionList"));
|
17
17
|
const ConfigureCollection_1 = __importDefault(require("../ConfigureCollection"));
|
18
18
|
const ViewIndexingRunLog_1 = __importDefault(require("../ViewIndexingRunLog"));
|
19
|
+
const VirtualCollections_1 = __importDefault(require("../VirtualCollections"));
|
19
20
|
const Homepage_1 = __importDefault(require("../Homepage"));
|
20
21
|
const App = () => {
|
22
|
+
console.log('elasticsearch plugin App component rendering');
|
21
23
|
return (react_1.default.createElement(react_router_dom_1.Switch, null,
|
22
24
|
react_1.default.createElement(react_router_dom_1.Route, { path: `/plugins/${pluginId_1.default}`, render: () => (react_1.default.createElement(react_router_dom_1.Redirect, { to: `/plugins/${pluginId_1.default}/home` })), exact: true }),
|
23
25
|
react_1.default.createElement(react_router_dom_1.Route, { path: `/plugins/${pluginId_1.default}/home`, component: Homepage_1.default, exact: true }),
|
24
26
|
react_1.default.createElement(react_router_dom_1.Route, { path: `/plugins/${pluginId_1.default}/configure-collections`, component: ConfigureCollectionList_1.default, exact: true }),
|
25
27
|
react_1.default.createElement(react_router_dom_1.Route, { path: `/plugins/${pluginId_1.default}/configure-collections/:collectionName`, component: ConfigureCollection_1.default, exact: true }),
|
28
|
+
react_1.default.createElement(react_router_dom_1.Route, { path: `/plugins/${pluginId_1.default}/virtual-collections`, component: VirtualCollections_1.default, exact: true }),
|
26
29
|
react_1.default.createElement(react_router_dom_1.Route, { path: `/plugins/${pluginId_1.default}/view-indexing-logs`, component: ViewIndexingRunLog_1.default }),
|
27
30
|
react_1.default.createElement(react_router_dom_1.Route, { component: helper_plugin_1.AnErrorOccurred })));
|
28
31
|
};
|
@@ -0,0 +1,194 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
19
|
+
var ownKeys = function(o) {
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
21
|
+
var ar = [];
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
23
|
+
return ar;
|
24
|
+
};
|
25
|
+
return ownKeys(o);
|
26
|
+
};
|
27
|
+
return function (mod) {
|
28
|
+
if (mod && mod.__esModule) return mod;
|
29
|
+
var result = {};
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
31
|
+
__setModuleDefault(result, mod);
|
32
|
+
return result;
|
33
|
+
};
|
34
|
+
})();
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
37
|
+
};
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
39
|
+
const react_1 = __importStar(require("react"));
|
40
|
+
const SubNavigation_1 = require("../../components/SubNavigation");
|
41
|
+
const design_system_1 = require("@strapi/design-system");
|
42
|
+
const apiUrls_1 = require("../../utils/apiUrls");
|
43
|
+
const axiosInstance_1 = __importDefault(require("../../utils/axiosInstance"));
|
44
|
+
const helper_plugin_1 = require("@strapi/helper-plugin");
|
45
|
+
const icons_1 = require("@strapi/icons");
|
46
|
+
const pluginId_1 = __importDefault(require("../../pluginId"));
|
47
|
+
const loadVirtualCollections = async () => {
|
48
|
+
const resp = await axiosInstance_1.default.get(apiUrls_1.apiGetVirtualCollections);
|
49
|
+
return resp.data;
|
50
|
+
};
|
51
|
+
const VirtualCollections = () => {
|
52
|
+
const [virtualCollections, setVirtualCollections] = (0, react_1.useState)(null);
|
53
|
+
const [isInProgress, setIsInProgress] = (0, react_1.useState)(false);
|
54
|
+
const [reindexingCollection, setReindexingCollection] = (0, react_1.useState)(null);
|
55
|
+
const [refreshingCollection, setRefreshingCollection] = (0, react_1.useState)(null);
|
56
|
+
const toggleNotification = (0, helper_plugin_1.useNotification)();
|
57
|
+
console.log('VirtualCollections component rendering');
|
58
|
+
const reloadVirtualCollections = async ({ showNotification }) => {
|
59
|
+
console.log('VirtualCollections component reloading');
|
60
|
+
setIsInProgress(true);
|
61
|
+
try {
|
62
|
+
const collections = await loadVirtualCollections();
|
63
|
+
setVirtualCollections(collections);
|
64
|
+
console.log('VirtualCollections component reloaded.');
|
65
|
+
if (showNotification) {
|
66
|
+
toggleNotification({
|
67
|
+
type: 'success',
|
68
|
+
message: 'Virtual collections information reloaded.',
|
69
|
+
timeout: 5000,
|
70
|
+
});
|
71
|
+
}
|
72
|
+
}
|
73
|
+
catch (err) {
|
74
|
+
console.error('Error loading virtual collections:', err);
|
75
|
+
setVirtualCollections([]); // Set empty array on error
|
76
|
+
toggleNotification({
|
77
|
+
type: 'warning',
|
78
|
+
message: 'An error was encountered while loading virtual collections.',
|
79
|
+
timeout: 5000,
|
80
|
+
});
|
81
|
+
}
|
82
|
+
finally {
|
83
|
+
setIsInProgress(false);
|
84
|
+
}
|
85
|
+
};
|
86
|
+
const refreshCollectionInfo = async (collectionName) => {
|
87
|
+
setRefreshingCollection(collectionName);
|
88
|
+
try {
|
89
|
+
const updatedInfo = await axiosInstance_1.default.get((0, apiUrls_1.apiGetVirtualCollectionInfo)(collectionName));
|
90
|
+
// Update the specific collection in the list
|
91
|
+
setVirtualCollections((prevCollections) => {
|
92
|
+
if (!prevCollections)
|
93
|
+
return prevCollections;
|
94
|
+
return prevCollections.map((collection) => (collection.collectionName === collectionName ? { ...collection, ...updatedInfo.data } : collection));
|
95
|
+
});
|
96
|
+
toggleNotification({
|
97
|
+
type: 'success',
|
98
|
+
message: `Virtual collection "${collectionName}" information refreshed.`,
|
99
|
+
timeout: 3000,
|
100
|
+
});
|
101
|
+
}
|
102
|
+
catch (err) {
|
103
|
+
console.error('Error refreshing collection info:', err);
|
104
|
+
toggleNotification({
|
105
|
+
type: 'warning',
|
106
|
+
message: `Failed to refresh virtual collection "${collectionName}" information.`,
|
107
|
+
timeout: 5000,
|
108
|
+
});
|
109
|
+
}
|
110
|
+
finally {
|
111
|
+
setRefreshingCollection(null);
|
112
|
+
}
|
113
|
+
};
|
114
|
+
const reindexVirtualCollection = async (collectionName) => {
|
115
|
+
setReindexingCollection(collectionName);
|
116
|
+
try {
|
117
|
+
await axiosInstance_1.default.post((0, apiUrls_1.apiReindexVirtualCollection)(collectionName));
|
118
|
+
toggleNotification({
|
119
|
+
type: 'success',
|
120
|
+
message: `Virtual collection "${collectionName}" reindexing started successfully.`,
|
121
|
+
timeout: 5000,
|
122
|
+
});
|
123
|
+
}
|
124
|
+
catch (err) {
|
125
|
+
toggleNotification({
|
126
|
+
type: 'warning',
|
127
|
+
message: `Failed to reindex virtual collection "${collectionName}".`,
|
128
|
+
timeout: 5000,
|
129
|
+
});
|
130
|
+
console.error(err);
|
131
|
+
}
|
132
|
+
finally {
|
133
|
+
setReindexingCollection(null);
|
134
|
+
}
|
135
|
+
};
|
136
|
+
(0, react_1.useEffect)(() => {
|
137
|
+
const loadData = async () => {
|
138
|
+
await reloadVirtualCollections({ showNotification: false });
|
139
|
+
};
|
140
|
+
loadData();
|
141
|
+
}, []);
|
142
|
+
if (virtualCollections === null)
|
143
|
+
return react_1.default.createElement(helper_plugin_1.LoadingIndicatorPage, null);
|
144
|
+
else
|
145
|
+
return (react_1.default.createElement(design_system_1.Flex, { alignItems: "stretch", gap: 4 },
|
146
|
+
react_1.default.createElement(SubNavigation_1.SubNavigation, { activeUrl: `/plugins/${pluginId_1.default}/virtual-collections` }),
|
147
|
+
react_1.default.createElement(design_system_1.Box, { padding: 8, background: "neutral100", width: "100%" },
|
148
|
+
react_1.default.createElement(design_system_1.Box, { paddingBottom: 4 },
|
149
|
+
react_1.default.createElement(design_system_1.Flex, { justifyContent: "space-between", alignItems: "center" },
|
150
|
+
react_1.default.createElement(design_system_1.Typography, { variant: "alpha" }, "Virtual Collections"),
|
151
|
+
react_1.default.createElement(design_system_1.IconButton, { disabled: isInProgress, onClick: () => reloadVirtualCollections({
|
152
|
+
showNotification: true,
|
153
|
+
}), label: "Refresh", icon: react_1.default.createElement(icons_1.Refresh, null) }))),
|
154
|
+
react_1.default.createElement(design_system_1.Box, { width: "100%", paddingBottom: 4 },
|
155
|
+
' ',
|
156
|
+
virtualCollections && virtualCollections.length > 0 ? (react_1.default.createElement(design_system_1.Table, { colCount: 7, rowCount: virtualCollections.length },
|
157
|
+
react_1.default.createElement(design_system_1.Thead, null,
|
158
|
+
react_1.default.createElement(design_system_1.Tr, null,
|
159
|
+
react_1.default.createElement(design_system_1.Th, null,
|
160
|
+
react_1.default.createElement(design_system_1.Typography, { variant: "sigma" }, "Collection Name")),
|
161
|
+
react_1.default.createElement(design_system_1.Th, null,
|
162
|
+
react_1.default.createElement(design_system_1.Typography, { variant: "sigma" }, "Index Alias")),
|
163
|
+
react_1.default.createElement(design_system_1.Th, null,
|
164
|
+
react_1.default.createElement(design_system_1.Typography, { variant: "sigma" }, "Current Index")),
|
165
|
+
react_1.default.createElement(design_system_1.Th, null,
|
166
|
+
react_1.default.createElement(design_system_1.Typography, { variant: "sigma" }, "Indices Count")),
|
167
|
+
react_1.default.createElement(design_system_1.Th, null,
|
168
|
+
react_1.default.createElement(design_system_1.Typography, { variant: "sigma" }, "Documents")),
|
169
|
+
react_1.default.createElement(design_system_1.Th, null,
|
170
|
+
react_1.default.createElement(design_system_1.Typography, { variant: "sigma" }, "Trigger Collections")),
|
171
|
+
react_1.default.createElement(design_system_1.Th, null,
|
172
|
+
react_1.default.createElement(design_system_1.Typography, { variant: "sigma" }, "Actions")))),
|
173
|
+
react_1.default.createElement(design_system_1.Tbody, null, virtualCollections.map((collection, index) => (react_1.default.createElement(design_system_1.Tr, { key: index },
|
174
|
+
react_1.default.createElement(design_system_1.Td, null,
|
175
|
+
react_1.default.createElement(design_system_1.Typography, { textColor: "neutral600", fontWeight: "bold" }, collection.collectionName)),
|
176
|
+
react_1.default.createElement(design_system_1.Td, null,
|
177
|
+
react_1.default.createElement(design_system_1.Typography, { textColor: "neutral600" }, collection.indexAlias || 'Default Index')),
|
178
|
+
react_1.default.createElement(design_system_1.Td, null,
|
179
|
+
react_1.default.createElement(design_system_1.Typography, { textColor: "neutral600", fontSize: 1 }, collection.error ? react_1.default.createElement("span", { style: { color: 'red' } }, "Error") : collection.currentIndex || 'N/A')),
|
180
|
+
react_1.default.createElement(design_system_1.Td, null,
|
181
|
+
react_1.default.createElement(design_system_1.Typography, { textColor: "neutral600", fontSize: 1 }, collection.error ? '-' : collection.matchingIndicesCount || 0)),
|
182
|
+
react_1.default.createElement(design_system_1.Td, null,
|
183
|
+
react_1.default.createElement(design_system_1.Typography, { textColor: "neutral600", fontSize: 1 }, collection.error ? '-' : collection.documentCount?.toLocaleString() || '0')),
|
184
|
+
react_1.default.createElement(design_system_1.Td, null,
|
185
|
+
react_1.default.createElement(design_system_1.Box, null, collection.triggerCollections && collection.triggerCollections.length > 0 ? (collection.triggerCollections.map((triggerCollection, idx) => (react_1.default.createElement(design_system_1.Box, { key: idx, paddingBottom: 1 },
|
186
|
+
react_1.default.createElement(design_system_1.Typography, { textColor: "neutral600", fontSize: 1 }, triggerCollection))))) : (react_1.default.createElement(design_system_1.Typography, { textColor: "neutral400", fontSize: 1 }, "No triggers")))),
|
187
|
+
react_1.default.createElement(design_system_1.Td, null,
|
188
|
+
react_1.default.createElement(design_system_1.Flex, { gap: 2 },
|
189
|
+
react_1.default.createElement(design_system_1.IconButton, { label: "Refresh Info", loading: refreshingCollection === collection.collectionName, disabled: isInProgress || refreshingCollection !== null, onClick: () => refreshCollectionInfo(collection.collectionName), icon: react_1.default.createElement(icons_1.Refresh, null) }),
|
190
|
+
react_1.default.createElement(design_system_1.Button, { variant: "secondary", size: "S", loading: reindexingCollection === collection.collectionName, disabled: isInProgress || reindexingCollection !== null || refreshingCollection !== null, onClick: () => reindexVirtualCollection(collection.collectionName) }, "Reindex"))))))))) : (react_1.default.createElement(design_system_1.Box, { padding: 8, background: "neutral0", hasRadius: true },
|
191
|
+
react_1.default.createElement(design_system_1.Typography, { textAlign: "center", textColor: "neutral600" }, "No virtual collections configured."),
|
192
|
+
react_1.default.createElement(design_system_1.Typography, { textAlign: "center", textColor: "neutral500", fontSize: 1, paddingTop: 2 }, "Virtual collections are configured in your Strapi application code.")))))));
|
193
|
+
};
|
194
|
+
exports.default = VirtualCollections;
|
@@ -8,3 +8,6 @@ export declare const apiRequestCollectionIndexing: (collectionName: any) => stri
|
|
8
8
|
export declare const apiTriggerIndexing: string;
|
9
9
|
export declare const apiExportContentConfig: string;
|
10
10
|
export declare const apiImportContentConfig: string;
|
11
|
+
export declare const apiGetVirtualCollections: string;
|
12
|
+
export declare const apiGetVirtualCollectionInfo: (collectionName: any) => string;
|
13
|
+
export declare const apiReindexVirtualCollection: (collectionName: any) => string;
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.apiImportContentConfig = exports.apiExportContentConfig = exports.apiTriggerIndexing = exports.apiRequestCollectionIndexing = exports.apiRequestReIndexing = exports.apiFetchRecentIndexingRunLog = exports.apiGetElasticsearchSetupInfo = exports.apiSaveCollectionConfig = exports.apiGetCollectionConfig = exports.apiGetContentConfig = void 0;
|
6
|
+
exports.apiReindexVirtualCollection = exports.apiGetVirtualCollectionInfo = exports.apiGetVirtualCollections = exports.apiImportContentConfig = exports.apiExportContentConfig = exports.apiTriggerIndexing = exports.apiRequestCollectionIndexing = exports.apiRequestReIndexing = exports.apiFetchRecentIndexingRunLog = exports.apiGetElasticsearchSetupInfo = exports.apiSaveCollectionConfig = exports.apiGetCollectionConfig = exports.apiGetContentConfig = void 0;
|
7
7
|
// import { collectionName } from "../../../server/content-types/tasks";
|
8
8
|
const pluginId_1 = __importDefault(require("../pluginId"));
|
9
9
|
exports.apiGetContentConfig = `/${pluginId_1.default}/content-config/`;
|
@@ -19,3 +19,9 @@ exports.apiRequestCollectionIndexing = apiRequestCollectionIndexing;
|
|
19
19
|
exports.apiTriggerIndexing = `/${pluginId_1.default}/trigger-indexing/`;
|
20
20
|
exports.apiExportContentConfig = `/${pluginId_1.default}/export-content-config/`;
|
21
21
|
exports.apiImportContentConfig = `/${pluginId_1.default}/import-content-config/`;
|
22
|
+
// Virtual Collections APIs
|
23
|
+
exports.apiGetVirtualCollections = `/${pluginId_1.default}/virtual-collections`;
|
24
|
+
const apiGetVirtualCollectionInfo = (collectionName) => `/${pluginId_1.default}/virtual-collections/${collectionName}/info`;
|
25
|
+
exports.apiGetVirtualCollectionInfo = apiGetVirtualCollectionInfo;
|
26
|
+
const apiReindexVirtualCollection = (collectionName) => `/${pluginId_1.default}/virtual-collections/${collectionName}/reindex`;
|
27
|
+
exports.apiReindexVirtualCollection = apiReindexVirtualCollection;
|
package/dist/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vegan-friendly/strapi-plugin-elasticsearch",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.4.0",
|
4
4
|
"description": "A Strapi plugin to enable using Elasticsearch with Strapi CMS.",
|
5
5
|
"homepage": "https://github.com/vegan-friendly/strapi-plugin-elasticsearch",
|
6
6
|
"strapi": {
|
@@ -34,7 +34,7 @@
|
|
34
34
|
],
|
35
35
|
"scripts": {
|
36
36
|
"build": "tsc",
|
37
|
-
"
|
37
|
+
"watch": "tsc --watch"
|
38
38
|
},
|
39
39
|
"dependencies": {
|
40
40
|
"@elastic/elasticsearch": "^8.9.0",
|
@@ -29,5 +29,33 @@ declare const _default: {
|
|
29
29
|
indexCollection: (ctx: any) => Promise<any>;
|
30
30
|
triggerIndexingTask: (ctx: any) => Promise<any>;
|
31
31
|
};
|
32
|
+
virtualCollections: ({ strapi }: {
|
33
|
+
strapi: any;
|
34
|
+
}) => {
|
35
|
+
getAll: (ctx: any) => Promise<any[] | undefined>;
|
36
|
+
getCollectionInfo: (ctx: any) => Promise<{
|
37
|
+
collectionName: any;
|
38
|
+
indexAlias: any;
|
39
|
+
triggersCount: any;
|
40
|
+
triggerCollections: any;
|
41
|
+
currentIndex: string | null;
|
42
|
+
matchingIndicesCount: any;
|
43
|
+
documentCount: number;
|
44
|
+
error: null;
|
45
|
+
} | {
|
46
|
+
collectionName: any;
|
47
|
+
indexAlias: any;
|
48
|
+
triggersCount: any;
|
49
|
+
triggerCollections: any;
|
50
|
+
currentIndex: null;
|
51
|
+
matchingIndicesCount: number;
|
52
|
+
documentCount: number;
|
53
|
+
error: any;
|
54
|
+
} | undefined>;
|
55
|
+
reindexCollection: (ctx: any) => Promise<{
|
56
|
+
success: boolean;
|
57
|
+
message: string;
|
58
|
+
} | undefined>;
|
59
|
+
};
|
32
60
|
};
|
33
61
|
export default _default;
|
@@ -8,10 +8,12 @@ const perform_search_1 = __importDefault(require("./perform-search"));
|
|
8
8
|
const log_indexing_1 = __importDefault(require("./log-indexing"));
|
9
9
|
const setup_info_1 = __importDefault(require("./setup-info"));
|
10
10
|
const perform_indexing_1 = __importDefault(require("./perform-indexing"));
|
11
|
+
const virtual_collections_1 = __importDefault(require("./virtual-collections"));
|
11
12
|
exports.default = {
|
12
13
|
configureIndexing: configure_indexing_1.default,
|
13
14
|
performSearch: perform_search_1.default,
|
14
15
|
logIndexing: log_indexing_1.default,
|
15
16
|
setupInfo: setup_info_1.default,
|
16
17
|
performIndexing: perform_indexing_1.default,
|
18
|
+
virtualCollections: virtual_collections_1.default,
|
17
19
|
};
|
@@ -0,0 +1,29 @@
|
|
1
|
+
declare const _default: ({ strapi }: {
|
2
|
+
strapi: any;
|
3
|
+
}) => {
|
4
|
+
getAll: (ctx: any) => Promise<any[] | undefined>;
|
5
|
+
getCollectionInfo: (ctx: any) => Promise<{
|
6
|
+
collectionName: any;
|
7
|
+
indexAlias: any;
|
8
|
+
triggersCount: any;
|
9
|
+
triggerCollections: any;
|
10
|
+
currentIndex: string | null;
|
11
|
+
matchingIndicesCount: any;
|
12
|
+
documentCount: number;
|
13
|
+
error: null;
|
14
|
+
} | {
|
15
|
+
collectionName: any;
|
16
|
+
indexAlias: any;
|
17
|
+
triggersCount: any;
|
18
|
+
triggerCollections: any;
|
19
|
+
currentIndex: null;
|
20
|
+
matchingIndicesCount: number;
|
21
|
+
documentCount: number;
|
22
|
+
error: any;
|
23
|
+
} | undefined>;
|
24
|
+
reindexCollection: (ctx: any) => Promise<{
|
25
|
+
success: boolean;
|
26
|
+
message: string;
|
27
|
+
} | undefined>;
|
28
|
+
};
|
29
|
+
export default _default;
|
@@ -0,0 +1,164 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.default = ({ strapi }) => {
|
4
|
+
const getVirtualCollectionsIndexer = () => {
|
5
|
+
try {
|
6
|
+
return strapi.service('plugin::elasticsearch.virtualCollectionsIndexer');
|
7
|
+
}
|
8
|
+
catch (err) {
|
9
|
+
strapi.log.warn('Virtual collections indexer service not available:', err?.message || err);
|
10
|
+
return null;
|
11
|
+
}
|
12
|
+
};
|
13
|
+
const getVirtualCollectionsRegistry = () => {
|
14
|
+
try {
|
15
|
+
return strapi.service('plugin::elasticsearch.virtualCollectionsRegistry');
|
16
|
+
}
|
17
|
+
catch (err) {
|
18
|
+
strapi.log.warn('Virtual collections registry service not available:', err?.message || err);
|
19
|
+
return null;
|
20
|
+
}
|
21
|
+
};
|
22
|
+
const getEsInterface = () => {
|
23
|
+
try {
|
24
|
+
return strapi.service('plugin::elasticsearch.esInterface');
|
25
|
+
}
|
26
|
+
catch (err) {
|
27
|
+
strapi.log.warn('ElasticSearch interface service not available:', err?.message || err);
|
28
|
+
return null;
|
29
|
+
}
|
30
|
+
};
|
31
|
+
const getHelper = () => {
|
32
|
+
try {
|
33
|
+
return strapi.plugins['elasticsearch'].services.helper;
|
34
|
+
}
|
35
|
+
catch (err) {
|
36
|
+
strapi.log.warn('Helper service not available:', err?.message || err);
|
37
|
+
return null;
|
38
|
+
}
|
39
|
+
};
|
40
|
+
const getVirtualCollectionInfo = async (collection) => {
|
41
|
+
const esInterface = getEsInterface();
|
42
|
+
const helper = getHelper();
|
43
|
+
if (!esInterface || !helper) {
|
44
|
+
return {
|
45
|
+
collectionName: collection.collectionName,
|
46
|
+
indexAlias: collection.indexAlias,
|
47
|
+
triggersCount: collection.triggers.length,
|
48
|
+
triggerCollections: collection.triggers.map((t) => t.collection),
|
49
|
+
currentIndex: null,
|
50
|
+
matchingIndicesCount: 0,
|
51
|
+
documentCount: 0,
|
52
|
+
error: 'ElasticSearch services not available',
|
53
|
+
};
|
54
|
+
}
|
55
|
+
try {
|
56
|
+
const pattern = collection.indexAlias ? `${collection.indexAlias}_*` : 'strapi-plugin-elasticsearch-index_*';
|
57
|
+
// Get current index from alias
|
58
|
+
let currentIndex = null;
|
59
|
+
let documentCount = 0;
|
60
|
+
if (collection.indexAlias) {
|
61
|
+
const aliasInfo = await esInterface.getAliasInfo(collection.indexAlias);
|
62
|
+
if (aliasInfo) {
|
63
|
+
const indices = Object.keys(aliasInfo);
|
64
|
+
currentIndex = indices.length > 0 ? indices[0] : null;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
else {
|
68
|
+
currentIndex = await helper.getCurrentIndexName();
|
69
|
+
}
|
70
|
+
// Get matching indices count
|
71
|
+
const matchingIndices = await esInterface.listIndicesByPattern(pattern);
|
72
|
+
const matchingIndicesCount = matchingIndices.length;
|
73
|
+
// Get document count for current index
|
74
|
+
if (currentIndex) {
|
75
|
+
documentCount = await esInterface.getIndexDocumentCount(currentIndex);
|
76
|
+
}
|
77
|
+
return {
|
78
|
+
collectionName: collection.collectionName,
|
79
|
+
indexAlias: collection.indexAlias,
|
80
|
+
triggersCount: collection.triggers.length,
|
81
|
+
triggerCollections: collection.triggers.map((t) => t.collection),
|
82
|
+
currentIndex,
|
83
|
+
matchingIndicesCount,
|
84
|
+
documentCount,
|
85
|
+
error: null,
|
86
|
+
};
|
87
|
+
}
|
88
|
+
catch (err) {
|
89
|
+
strapi.log.error(`Error getting info for virtual collection ${collection.collectionName}:`, err);
|
90
|
+
return {
|
91
|
+
collectionName: collection.collectionName,
|
92
|
+
indexAlias: collection.indexAlias,
|
93
|
+
triggersCount: collection.triggers.length,
|
94
|
+
triggerCollections: collection.triggers.map((t) => t.collection),
|
95
|
+
currentIndex: null,
|
96
|
+
matchingIndicesCount: 0,
|
97
|
+
documentCount: 0,
|
98
|
+
error: err?.message || 'Unknown error',
|
99
|
+
};
|
100
|
+
}
|
101
|
+
};
|
102
|
+
const getAll = async (ctx) => {
|
103
|
+
try {
|
104
|
+
const virtualCollectionsRegistry = getVirtualCollectionsRegistry();
|
105
|
+
if (!virtualCollectionsRegistry) {
|
106
|
+
return [];
|
107
|
+
}
|
108
|
+
const collections = virtualCollectionsRegistry.getAll();
|
109
|
+
const result = await Promise.all(collections.map((collection) => getVirtualCollectionInfo(collection)));
|
110
|
+
return result;
|
111
|
+
}
|
112
|
+
catch (err) {
|
113
|
+
strapi.log.error('Error getting virtual collections:', err);
|
114
|
+
ctx.throw(500, err);
|
115
|
+
}
|
116
|
+
};
|
117
|
+
const getCollectionInfo = async (ctx) => {
|
118
|
+
try {
|
119
|
+
const { collectionName } = ctx.params;
|
120
|
+
const virtualCollectionsRegistry = getVirtualCollectionsRegistry();
|
121
|
+
if (!virtualCollectionsRegistry) {
|
122
|
+
ctx.throw(503, 'Virtual collections registry service not available');
|
123
|
+
}
|
124
|
+
const collection = virtualCollectionsRegistry.get(collectionName);
|
125
|
+
if (!collection) {
|
126
|
+
ctx.throw(404, `Virtual collection not found: ${collectionName}`);
|
127
|
+
}
|
128
|
+
const result = await getVirtualCollectionInfo(collection);
|
129
|
+
return result;
|
130
|
+
}
|
131
|
+
catch (err) {
|
132
|
+
strapi.log.error(`Error getting virtual collection info ${ctx.params.collectionName}:`, err);
|
133
|
+
ctx.throw(500, err);
|
134
|
+
}
|
135
|
+
};
|
136
|
+
const reindexCollection = async (ctx) => {
|
137
|
+
try {
|
138
|
+
const { collectionName } = ctx.params;
|
139
|
+
const virtualCollectionsRegistry = getVirtualCollectionsRegistry();
|
140
|
+
const virtualCollectionsIndexer = getVirtualCollectionsIndexer();
|
141
|
+
if (!virtualCollectionsRegistry || !virtualCollectionsIndexer) {
|
142
|
+
ctx.throw(503, 'Virtual collections services not available');
|
143
|
+
}
|
144
|
+
const collection = virtualCollectionsRegistry.get(collectionName);
|
145
|
+
if (!collection) {
|
146
|
+
ctx.throw(404, `Virtual collection not found: ${collectionName}`);
|
147
|
+
}
|
148
|
+
await virtualCollectionsIndexer.reindex(collection);
|
149
|
+
return {
|
150
|
+
success: true,
|
151
|
+
message: `Successfully reindexed virtual collection: ${collectionName}`,
|
152
|
+
};
|
153
|
+
}
|
154
|
+
catch (err) {
|
155
|
+
strapi.log.error(`Error reindexing virtual collection ${ctx.params.collectionName}:`, err);
|
156
|
+
ctx.throw(500, err);
|
157
|
+
}
|
158
|
+
};
|
159
|
+
return {
|
160
|
+
getAll,
|
161
|
+
getCollectionInfo,
|
162
|
+
reindexCollection,
|
163
|
+
};
|
164
|
+
};
|
package/dist/server/index.d.ts
CHANGED
@@ -44,6 +44,34 @@ declare const _default: {
|
|
44
44
|
indexCollection: (ctx: any) => Promise<any>;
|
45
45
|
triggerIndexingTask: (ctx: any) => Promise<any>;
|
46
46
|
};
|
47
|
+
virtualCollections: ({ strapi }: {
|
48
|
+
strapi: any;
|
49
|
+
}) => {
|
50
|
+
getAll: (ctx: any) => Promise<any[] | undefined>;
|
51
|
+
getCollectionInfo: (ctx: any) => Promise<{
|
52
|
+
collectionName: any;
|
53
|
+
indexAlias: any;
|
54
|
+
triggersCount: any;
|
55
|
+
triggerCollections: any;
|
56
|
+
currentIndex: string | null;
|
57
|
+
matchingIndicesCount: any;
|
58
|
+
documentCount: number;
|
59
|
+
error: null;
|
60
|
+
} | {
|
61
|
+
collectionName: any;
|
62
|
+
indexAlias: any;
|
63
|
+
triggersCount: any;
|
64
|
+
triggerCollections: any;
|
65
|
+
currentIndex: null;
|
66
|
+
matchingIndicesCount: number;
|
67
|
+
documentCount: number;
|
68
|
+
error: any;
|
69
|
+
} | undefined>;
|
70
|
+
reindexCollection: (ctx: any) => Promise<{
|
71
|
+
success: boolean;
|
72
|
+
message: string;
|
73
|
+
} | undefined>;
|
74
|
+
};
|
47
75
|
};
|
48
76
|
routes: {
|
49
77
|
config: {
|
@@ -101,6 +129,17 @@ declare const _default: {
|
|
101
129
|
};
|
102
130
|
}[];
|
103
131
|
};
|
132
|
+
virtualCollections: {
|
133
|
+
type: string;
|
134
|
+
routes: {
|
135
|
+
method: string;
|
136
|
+
path: string;
|
137
|
+
handler: string;
|
138
|
+
config: {
|
139
|
+
policies: never[];
|
140
|
+
};
|
141
|
+
}[];
|
142
|
+
};
|
104
143
|
};
|
105
144
|
services: {
|
106
145
|
configureIndexing: ({ strapi }: {
|
@@ -54,5 +54,16 @@ declare const _default: {
|
|
54
54
|
};
|
55
55
|
}[];
|
56
56
|
};
|
57
|
+
virtualCollections: {
|
58
|
+
type: string;
|
59
|
+
routes: {
|
60
|
+
method: string;
|
61
|
+
path: string;
|
62
|
+
handler: string;
|
63
|
+
config: {
|
64
|
+
policies: never[];
|
65
|
+
};
|
66
|
+
}[];
|
67
|
+
};
|
57
68
|
};
|
58
69
|
export default _default;
|
@@ -8,10 +8,12 @@ const perform_search_1 = __importDefault(require("./perform-search"));
|
|
8
8
|
const run_log_1 = __importDefault(require("./run-log"));
|
9
9
|
const setup_info_1 = __importDefault(require("./setup-info"));
|
10
10
|
const perform_indexing_1 = __importDefault(require("./perform-indexing"));
|
11
|
+
const virtual_collections_1 = __importDefault(require("./virtual-collections"));
|
11
12
|
exports.default = {
|
12
13
|
config: configure_indexing_1.default,
|
13
14
|
search: perform_search_1.default,
|
14
15
|
runLog: run_log_1.default,
|
15
16
|
setupInfo: setup_info_1.default,
|
16
17
|
performIndexing: perform_indexing_1.default,
|
18
|
+
virtualCollections: virtual_collections_1.default,
|
17
19
|
};
|
@@ -0,0 +1,26 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.default = {
|
4
|
+
// accessible only from admin UI
|
5
|
+
type: 'admin',
|
6
|
+
routes: [
|
7
|
+
{
|
8
|
+
method: 'GET',
|
9
|
+
path: '/virtual-collections',
|
10
|
+
handler: 'virtualCollections.getAll',
|
11
|
+
config: { policies: [] },
|
12
|
+
},
|
13
|
+
{
|
14
|
+
method: 'GET',
|
15
|
+
path: '/virtual-collections/:collectionName/info',
|
16
|
+
handler: 'virtualCollections.getCollectionInfo',
|
17
|
+
config: { policies: [] },
|
18
|
+
},
|
19
|
+
{
|
20
|
+
method: 'POST',
|
21
|
+
path: '/virtual-collections/:collectionName/reindex',
|
22
|
+
handler: 'virtualCollections.reindexCollection',
|
23
|
+
config: { policies: [] },
|
24
|
+
},
|
25
|
+
],
|
26
|
+
};
|
@@ -170,4 +170,48 @@ exports.default = ({ strapi }) => ({
|
|
170
170
|
throw err;
|
171
171
|
}
|
172
172
|
},
|
173
|
+
async getAliasInfo(aliasName) {
|
174
|
+
try {
|
175
|
+
const result = await client.indices.getAlias({
|
176
|
+
name: aliasName,
|
177
|
+
});
|
178
|
+
return result;
|
179
|
+
}
|
180
|
+
catch (err) {
|
181
|
+
if (err?.meta?.statusCode === 404) {
|
182
|
+
return null;
|
183
|
+
}
|
184
|
+
throw err;
|
185
|
+
}
|
186
|
+
},
|
187
|
+
async getIndexDocumentCount(indexName) {
|
188
|
+
try {
|
189
|
+
const result = await client.count({
|
190
|
+
index: indexName,
|
191
|
+
});
|
192
|
+
return result.count;
|
193
|
+
}
|
194
|
+
catch (err) {
|
195
|
+
if (err?.meta?.statusCode === 404) {
|
196
|
+
return 0;
|
197
|
+
}
|
198
|
+
throw err;
|
199
|
+
}
|
200
|
+
},
|
201
|
+
async getIndicesInfo(pattern) {
|
202
|
+
try {
|
203
|
+
const results = await client.cat.indices({
|
204
|
+
index: pattern,
|
205
|
+
format: 'json',
|
206
|
+
h: 'index,docs.count,store.size',
|
207
|
+
});
|
208
|
+
return results;
|
209
|
+
}
|
210
|
+
catch (err) {
|
211
|
+
if (err?.message?.includes('index_not_found_exception')) {
|
212
|
+
return [];
|
213
|
+
}
|
214
|
+
throw err;
|
215
|
+
}
|
216
|
+
},
|
173
217
|
});
|
@@ -72,4 +72,22 @@ export interface EsInterfaceService {
|
|
72
72
|
*/
|
73
73
|
searchData(searchQuery: any): Promise<any>;
|
74
74
|
listIndicesByPattern(pattern: string): Promise<string[]>;
|
75
|
+
/**
|
76
|
+
* Gets information about an alias, including which indexes it points to.
|
77
|
+
* @param aliasName - The name of the alias.
|
78
|
+
* @returns A promise that resolves with alias information or null if not found.
|
79
|
+
*/
|
80
|
+
getAliasInfo(aliasName: string): Promise<any>;
|
81
|
+
/**
|
82
|
+
* Gets the document count for a specific index.
|
83
|
+
* @param indexName - The name of the index.
|
84
|
+
* @returns A promise that resolves with the document count.
|
85
|
+
*/
|
86
|
+
getIndexDocumentCount(indexName: string): Promise<number>;
|
87
|
+
/**
|
88
|
+
* Gets detailed information about indices matching a pattern.
|
89
|
+
* @param pattern - The index pattern to match.
|
90
|
+
* @returns A promise that resolves with index information.
|
91
|
+
*/
|
92
|
+
getIndicesInfo(pattern: string): Promise<any[]>;
|
75
93
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vegan-friendly/strapi-plugin-elasticsearch",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.4.0",
|
4
4
|
"description": "A Strapi plugin to enable using Elasticsearch with Strapi CMS.",
|
5
5
|
"homepage": "https://github.com/vegan-friendly/strapi-plugin-elasticsearch",
|
6
6
|
"strapi": {
|
@@ -34,7 +34,7 @@
|
|
34
34
|
],
|
35
35
|
"scripts": {
|
36
36
|
"build": "tsc",
|
37
|
-
"
|
37
|
+
"watch": "tsc --watch"
|
38
38
|
},
|
39
39
|
"dependencies": {
|
40
40
|
"@elastic/elasticsearch": "^8.9.0",
|