@vegan-friendly/strapi-plugin-elasticsearch 0.0.8
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/.vscode/settings.json +3 -0
- package/LICENSE +21 -0
- package/README.md +266 -0
- package/admin/src/components/Initializer/index.js +26 -0
- package/admin/src/components/PluginIcon/index.js +12 -0
- package/admin/src/components/SubNavigation/index.js +48 -0
- package/admin/src/index.js +63 -0
- package/admin/src/pages/App/index.js +29 -0
- package/admin/src/pages/ConfigureCollection/index.js +225 -0
- package/admin/src/pages/ConfigureCollectionList/index.js +266 -0
- package/admin/src/pages/Homepage/index.js +168 -0
- package/admin/src/pages/ViewIndexingRunLog/index.js +124 -0
- package/admin/src/pluginId.js +5 -0
- package/admin/src/translations/en.json +1 -0
- package/admin/src/translations/fr.json +1 -0
- package/admin/src/utils/apiUrls.js +14 -0
- package/admin/src/utils/axiosInstance.js +40 -0
- package/admin/src/utils/getTrad.js +5 -0
- package/package.json +40 -0
- package/server/bootstrap.js +142 -0
- package/server/config/index.js +6 -0
- package/server/content-types/index.js +9 -0
- package/server/content-types/indexing-logs.js +35 -0
- package/server/content-types/name-prefix.js +0 -0
- package/server/content-types/tasks.js +52 -0
- package/server/controllers/configure-indexing.js +66 -0
- package/server/controllers/index.js +15 -0
- package/server/controllers/log-indexing.js +11 -0
- package/server/controllers/perform-indexing.js +28 -0
- package/server/controllers/perform-search.js +31 -0
- package/server/controllers/setup-info.js +14 -0
- package/server/destroy.js +5 -0
- package/server/index.js +25 -0
- package/server/middlewares/index.js +3 -0
- package/server/policies/index.js +3 -0
- package/server/register.js +5 -0
- package/server/routes/configure-indexing.js +42 -0
- package/server/routes/index.js +13 -0
- package/server/routes/perform-indexing.js +24 -0
- package/server/routes/perform-search.js +14 -0
- package/server/routes/run-log.js +12 -0
- package/server/routes/setup-info.js +12 -0
- package/server/services/configure-indexing.js +184 -0
- package/server/services/es-interface.js +187 -0
- package/server/services/helper.js +305 -0
- package/server/services/index.js +19 -0
- package/server/services/log-indexing.js +26 -0
- package/server/services/perform-indexing.js +173 -0
- package/server/services/schedule-indexing.js +65 -0
- package/server/services/transform-content.js +22 -0
- package/strapi-admin.js +3 -0
- package/strapi-server.js +3 -0
@@ -0,0 +1,225 @@
|
|
1
|
+
/*
|
2
|
+
*
|
3
|
+
* HomePage
|
4
|
+
*
|
5
|
+
*/
|
6
|
+
|
7
|
+
import React, { useState, useEffect } from 'react';
|
8
|
+
import { useParams, useHistory } from 'react-router-dom';
|
9
|
+
import axiosInstance from '../../utils/axiosInstance';
|
10
|
+
import { SubNavigation } from '../../components/SubNavigation';;
|
11
|
+
import { Box, Flex } from '@strapi/design-system';
|
12
|
+
import { ToggleInput } from '@strapi/design-system';
|
13
|
+
import { Link, useTracking } from '@strapi/helper-plugin';
|
14
|
+
import pluginId from '../../pluginId';
|
15
|
+
import { apiGetCollectionConfig, apiSaveCollectionConfig } from "../../utils/apiUrls";
|
16
|
+
import { Alert } from '@strapi/design-system';
|
17
|
+
import { Button } from '@strapi/design-system';
|
18
|
+
import { ArrowLeft } from '@strapi/icons';
|
19
|
+
import { Typography } from '@strapi/design-system';
|
20
|
+
import { Loader, Textarea, TextInput } from '@strapi/design-system';
|
21
|
+
import {LoadingIndicatorPage, useNotification} from '@strapi/helper-plugin';
|
22
|
+
|
23
|
+
const loadConfigForCollection = (collectionName) => {
|
24
|
+
return axiosInstance.get(apiGetCollectionConfig(collectionName))
|
25
|
+
.then((resp) => resp.data);
|
26
|
+
}
|
27
|
+
|
28
|
+
const saveConfigForCollection = (collectionName, data) => {
|
29
|
+
return axiosInstance.post(apiSaveCollectionConfig(collectionName), {
|
30
|
+
data
|
31
|
+
})
|
32
|
+
}
|
33
|
+
|
34
|
+
const ConfigureField = ({config, index, setFieldConfig}) => {
|
35
|
+
|
36
|
+
const validateSubfieldsConfig = (conf) => {
|
37
|
+
if (conf && conf.length > 0)
|
38
|
+
{
|
39
|
+
try {
|
40
|
+
JSON.parse(conf);
|
41
|
+
return true;
|
42
|
+
} catch (e) {
|
43
|
+
return false;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
else
|
47
|
+
return true;
|
48
|
+
}
|
49
|
+
|
50
|
+
const updateIndex = (checked) => {
|
51
|
+
setFieldConfig({index, config: {...config, index: checked}})
|
52
|
+
}
|
53
|
+
|
54
|
+
const updateSubfieldConfig = (subfields) => {
|
55
|
+
const subfieldsConfigValid = validateSubfieldsConfig(subfields);
|
56
|
+
setFieldConfig({index, config: {...config, subfields, subfieldsConfigValid}})
|
57
|
+
}
|
58
|
+
|
59
|
+
const updateMappedFieldName = (mappedName) => {
|
60
|
+
setFieldConfig({index, config: {...config, searchFieldName: mappedName}})
|
61
|
+
}
|
62
|
+
|
63
|
+
return (
|
64
|
+
<Box background="neutral100" borderColor="neutral200" hasRadius index={index}
|
65
|
+
padding={4}>
|
66
|
+
<Box paddingTop={2} paddingBottom={2}>
|
67
|
+
<Typography fontWeight="bold" textColor="neutral600">{config.name}</Typography>
|
68
|
+
</Box>
|
69
|
+
<Box paddingTop={2} paddingBottom={2}>
|
70
|
+
<ToggleInput label="Index" onLabel="Yes" offLabel="No"
|
71
|
+
checked={config.index} onChange={(e) => updateIndex(e.target.checked)} />
|
72
|
+
</Box>
|
73
|
+
<Box width="50%" paddingTop={2} paddingBottom={2}>
|
74
|
+
<TextInput label="Maps to search field" placeholder="Enter field name" name="Search field" onChange={e => updateMappedFieldName(e.target.value)} value={config.searchFieldName || ""} />
|
75
|
+
</Box>
|
76
|
+
{
|
77
|
+
config.index && config.type && config.type === "dynamiczone" ? (
|
78
|
+
<Box paddingTop={2} paddingBottom={2}>
|
79
|
+
<Textarea
|
80
|
+
label="Dynamic zone fields to index"
|
81
|
+
error={config.subfieldsConfigValid === false ? 'Invalid indexing configuration' : undefined}
|
82
|
+
onChange={e => updateSubfieldConfig(e.target.value)}
|
83
|
+
>{config.subfields || ""}</Textarea>
|
84
|
+
</Box>
|
85
|
+
) : null
|
86
|
+
}
|
87
|
+
{
|
88
|
+
config.index && config.type && config.type === "component" ? (
|
89
|
+
<Box paddingTop={2} paddingBottom={2}>
|
90
|
+
<Textarea
|
91
|
+
label="Component fields to index"
|
92
|
+
error={config.subfieldsConfigValid === false ? 'Invalid indexing configuration' : undefined}
|
93
|
+
onChange={e => updateSubfieldConfig(e.target.value)}
|
94
|
+
>{config.subfields || ""}</Textarea>
|
95
|
+
</Box>
|
96
|
+
) : null
|
97
|
+
}
|
98
|
+
</Box>
|
99
|
+
)
|
100
|
+
}
|
101
|
+
|
102
|
+
const ConfigureCollection = () => {
|
103
|
+
const [isInProgress, setIsInProgress] = useState(false);
|
104
|
+
const [selectedCollection, setSelectedCollection] = useState(null);
|
105
|
+
const [collectionConfig, setCollectionConfig] = useState(null);
|
106
|
+
|
107
|
+
const params = useParams();
|
108
|
+
const toggleNotification = useNotification();
|
109
|
+
const updateCollectionsConfig = ({index, config}) => {
|
110
|
+
setCollectionConfig({
|
111
|
+
collectionName: collectionConfig.collectionName,
|
112
|
+
attributes: collectionConfig.attributes.map((e, idx) => index === idx ? config : e)
|
113
|
+
});
|
114
|
+
}
|
115
|
+
|
116
|
+
const saveCollectionConfig = () => {
|
117
|
+
if (collectionConfig && collectionConfig.collectionName)
|
118
|
+
{
|
119
|
+
const data = {}
|
120
|
+
data[collectionConfig.collectionName] = {}
|
121
|
+
for (let k=0; k<collectionConfig.attributes.length; k++)
|
122
|
+
{
|
123
|
+
const {name, ...attribs} = collectionConfig.attributes[k]
|
124
|
+
data[collectionConfig.collectionName][name] = attribs
|
125
|
+
}
|
126
|
+
setIsInProgress(true);
|
127
|
+
saveConfigForCollection(collectionConfig.collectionName, data)
|
128
|
+
.then((resp) => {
|
129
|
+
toggleNotification({
|
130
|
+
type: "success", message: "The collection configuration is saved.", timeout: 5000
|
131
|
+
});
|
132
|
+
})
|
133
|
+
.catch((err) => {
|
134
|
+
toggleNotification({
|
135
|
+
type: "warning", message: err.message || "An error was encountered.", timeout: 5000
|
136
|
+
});
|
137
|
+
console.log(err);
|
138
|
+
})
|
139
|
+
.finally(() => setIsInProgress(false));
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
useEffect(() => {
|
144
|
+
if (params && params.collectionName)
|
145
|
+
setSelectedCollection(params.collectionName)
|
146
|
+
}, [params]);
|
147
|
+
|
148
|
+
useEffect(() => {
|
149
|
+
if (selectedCollection)
|
150
|
+
{
|
151
|
+
loadConfigForCollection(selectedCollection)
|
152
|
+
.then((resp) => {
|
153
|
+
if (Object.keys(resp).length === 0)
|
154
|
+
{
|
155
|
+
toggleNotification({
|
156
|
+
type: "warning", message: 'No collection with the selected name exists.', timeout: 5000
|
157
|
+
});
|
158
|
+
}
|
159
|
+
else
|
160
|
+
{
|
161
|
+
const collectionName = Object.keys(resp)[0];
|
162
|
+
const attributeNames = Object.keys(resp[collectionName]);
|
163
|
+
const attributes = [];
|
164
|
+
for (let s = 0; s<attributeNames.length; s++)
|
165
|
+
attributes.push({name: attributeNames[s], ...resp[collectionName][attributeNames[s]]})
|
166
|
+
const item = {collectionName, attributes};
|
167
|
+
setCollectionConfig(item);
|
168
|
+
}
|
169
|
+
})
|
170
|
+
.catch((err) => {
|
171
|
+
toggleNotification({
|
172
|
+
type: "warning", message: err.message || "An error was encountered.", timeout: 5000
|
173
|
+
});
|
174
|
+
console.log(err);
|
175
|
+
});
|
176
|
+
}
|
177
|
+
}, [selectedCollection]);
|
178
|
+
|
179
|
+
if (collectionConfig === null)
|
180
|
+
return <LoadingIndicatorPage />;
|
181
|
+
else
|
182
|
+
return (
|
183
|
+
<Flex alignItems="stretch" gap={4}>
|
184
|
+
<SubNavigation />
|
185
|
+
<Box padding={8} background="neutral100" width="100%">
|
186
|
+
<Box paddingBottom={4}>
|
187
|
+
<Link startIcon={<ArrowLeft />} to={`/plugins/${pluginId}/configure-collections/`}>
|
188
|
+
Back
|
189
|
+
</Link>
|
190
|
+
</Box>
|
191
|
+
{
|
192
|
+
selectedCollection && (
|
193
|
+
<Box paddingBottom={4}>
|
194
|
+
<Typography variant="alpha">{selectedCollection}</Typography>
|
195
|
+
</Box>
|
196
|
+
)
|
197
|
+
}
|
198
|
+
{
|
199
|
+
collectionConfig && (
|
200
|
+
<>
|
201
|
+
<Flex alignItems="stretch" gap={4} width="100%">
|
202
|
+
<Box padding={8} background="neutral0" width="100%">
|
203
|
+
<Box paddingBottom={2}>
|
204
|
+
<Typography variant="beta">Attributes</Typography>
|
205
|
+
{
|
206
|
+
collectionConfig.attributes.map((a, idx) => {
|
207
|
+
return <Box paddingTop={4} paddingBottom={4}><ConfigureField index={idx} config={a}
|
208
|
+
setFieldConfig={updateCollectionsConfig} /></Box>
|
209
|
+
})
|
210
|
+
}
|
211
|
+
</Box>
|
212
|
+
</Box>
|
213
|
+
</Flex>
|
214
|
+
<Box paddingTop={4}>
|
215
|
+
<Button loading={isInProgress} variant="default" onClick={saveCollectionConfig} >Save Configuration Changes</Button>
|
216
|
+
</Box>
|
217
|
+
</>
|
218
|
+
)
|
219
|
+
}
|
220
|
+
</Box>
|
221
|
+
</Flex>
|
222
|
+
);
|
223
|
+
};
|
224
|
+
|
225
|
+
export default ConfigureCollection;
|
@@ -0,0 +1,266 @@
|
|
1
|
+
/*
|
2
|
+
*
|
3
|
+
* HomePage
|
4
|
+
*
|
5
|
+
*/
|
6
|
+
|
7
|
+
import React, { useState } from 'react';
|
8
|
+
// import PropTypes from 'prop-types';
|
9
|
+
import pluginId from '../../pluginId';
|
10
|
+
import { SubNavigation } from '../../components/SubNavigation';;
|
11
|
+
import { Box, Flex } from '@strapi/design-system';
|
12
|
+
import { useEffect } from 'react';
|
13
|
+
import { Loader } from '@strapi/design-system';
|
14
|
+
import { apiGetContentConfig, apiRequestCollectionIndexing,
|
15
|
+
apiImportContentConfig, apiExportContentConfig } from "../../utils/apiUrls";
|
16
|
+
import axiosInstance from '../../utils/axiosInstance';
|
17
|
+
import { Alert } from '@strapi/design-system';
|
18
|
+
import { Table, Thead, Tbody, Tr, Td, Th } from '@strapi/design-system';
|
19
|
+
import { Typography } from '@strapi/design-system';
|
20
|
+
import { Pencil, Server } from '@strapi/icons';
|
21
|
+
import { IconButton } from '@strapi/design-system';
|
22
|
+
import { useHistory } from "react-router-dom";
|
23
|
+
import { Button } from '@strapi/design-system';
|
24
|
+
import { ModalLayout, ModalBody, ModalHeader, ModalFooter } from '@strapi/design-system';
|
25
|
+
import { Textarea, TwoColsLayout, Divider } from '@strapi/design-system';
|
26
|
+
import {LoadingIndicatorPage, useNotification} from '@strapi/helper-plugin';
|
27
|
+
|
28
|
+
const exportContentConfig = () => {
|
29
|
+
return axiosInstance.get(apiGetContentConfig,
|
30
|
+
{
|
31
|
+
responseType: 'blob'
|
32
|
+
})
|
33
|
+
.then((response) => {
|
34
|
+
const href = URL.createObjectURL(response.data);
|
35
|
+
const link = document.createElement('a');
|
36
|
+
link.href = href;
|
37
|
+
link.setAttribute('download', 'strapi-plugin-elasticsearch-contentconfig.json');
|
38
|
+
document.body.appendChild(link);
|
39
|
+
link.click();
|
40
|
+
document.body.removeChild(link);
|
41
|
+
URL.revokeObjectURL(href);
|
42
|
+
});
|
43
|
+
}
|
44
|
+
|
45
|
+
const importContentConfig = (conf) => {
|
46
|
+
return axiosInstance.post(apiImportContentConfig, {
|
47
|
+
data : conf
|
48
|
+
});
|
49
|
+
}
|
50
|
+
|
51
|
+
const loadContentConfig = () => {
|
52
|
+
return axiosInstance.get(apiGetContentConfig)
|
53
|
+
.then((resp) => resp.data);
|
54
|
+
}
|
55
|
+
|
56
|
+
const scheduleCollectionIndexing = (collectionName) => {
|
57
|
+
return axiosInstance.get(apiRequestCollectionIndexing(collectionName))
|
58
|
+
}
|
59
|
+
|
60
|
+
const Configure = () => {
|
61
|
+
const [isInProgress, setIsInProgress] = useState(false);
|
62
|
+
const [displayImportModal, setDisplayImportModal] = useState(false);
|
63
|
+
const [isEnteredJsonValid, setIsEnteredJsonValid] = useState(true);
|
64
|
+
const [importJson, setImportJson] = useState(null);
|
65
|
+
|
66
|
+
const [config, setConfig] = useState(null);
|
67
|
+
const history = useHistory();
|
68
|
+
const toggleNotification = useNotification();
|
69
|
+
|
70
|
+
const performImport = () => {
|
71
|
+
const conf = importJson
|
72
|
+
console.log(conf && conf.length > 0)
|
73
|
+
if (conf && conf.length > 0) {
|
74
|
+
setIsInProgress(true);
|
75
|
+
importContentConfig(conf)
|
76
|
+
.then(() => {
|
77
|
+
toggleNotification({
|
78
|
+
type: "success", message: "Collections configuration imported. Please refresh this view.", timeout: 5000
|
79
|
+
});
|
80
|
+
})
|
81
|
+
.catch((err) => {
|
82
|
+
toggleNotification({
|
83
|
+
type: "warning", message: "Importing collections configuration failed. An error was encountered.", timeout: 5000
|
84
|
+
});
|
85
|
+
console.log(err);
|
86
|
+
})
|
87
|
+
.finally(() => setIsInProgress(false));
|
88
|
+
}
|
89
|
+
}
|
90
|
+
const performExport = () => {
|
91
|
+
setIsInProgress(true);
|
92
|
+
exportContentConfig()
|
93
|
+
.then(() => {
|
94
|
+
toggleNotification({
|
95
|
+
type: "success", message: "Collections configuration exported.", timeout: 5000
|
96
|
+
});
|
97
|
+
})
|
98
|
+
.catch((err) => {
|
99
|
+
toggleNotification({
|
100
|
+
type: "warning", message: "Exporting collections configuration failed. An error was encountered.", timeout: 5000
|
101
|
+
});
|
102
|
+
console.log(err);
|
103
|
+
})
|
104
|
+
.finally(() => setIsInProgress(false));
|
105
|
+
}
|
106
|
+
|
107
|
+
useEffect(() => {
|
108
|
+
if (importJson && importJson.length > 0)
|
109
|
+
{
|
110
|
+
try {
|
111
|
+
JSON.parse(importJson);
|
112
|
+
setIsEnteredJsonValid(true);
|
113
|
+
} catch (e) {
|
114
|
+
setIsEnteredJsonValid(false);
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}, [importJson]);
|
118
|
+
|
119
|
+
useEffect(() => {
|
120
|
+
setIsInProgress(true);
|
121
|
+
loadContentConfig()
|
122
|
+
.then((resp) => {
|
123
|
+
const displayConfig = [];
|
124
|
+
for (let r=0; r < Object.keys(resp).length; r++)
|
125
|
+
{
|
126
|
+
const item = {collectionName: Object.keys(resp)[r], indexed: [], notIndexed: []};
|
127
|
+
const collectionName = item.collectionName;
|
128
|
+
for (let k=0; k < Object.keys(resp[collectionName]).length; k++)
|
129
|
+
{
|
130
|
+
const attribs = resp[collectionName];
|
131
|
+
for (let s=0; s < Object.keys(attribs).length; s++)
|
132
|
+
{
|
133
|
+
const attrName = Object.keys(attribs)[s]
|
134
|
+
const attr = attribs[attrName]
|
135
|
+
if (attr.index === false && !item.notIndexed.includes(attrName))
|
136
|
+
item.notIndexed.push(attrName)
|
137
|
+
else if (attr.index && !item.indexed.includes(attrName))
|
138
|
+
item.indexed.push(attrName)
|
139
|
+
}
|
140
|
+
}
|
141
|
+
displayConfig.push(item);
|
142
|
+
}
|
143
|
+
setConfig(displayConfig);
|
144
|
+
})
|
145
|
+
.catch((err) => {
|
146
|
+
toggleNotification({
|
147
|
+
type: "warning", message: "An error was encountered while fetching the configuration.", timeout: 5000
|
148
|
+
});
|
149
|
+
console.log(err);
|
150
|
+
})
|
151
|
+
.finally (() => {
|
152
|
+
setIsInProgress(false);
|
153
|
+
})
|
154
|
+
}, []);
|
155
|
+
if (config === null)
|
156
|
+
return <LoadingIndicatorPage />;
|
157
|
+
else
|
158
|
+
{
|
159
|
+
return (
|
160
|
+
<>
|
161
|
+
<Flex alignItems="stretch" gap={4}>
|
162
|
+
<SubNavigation activeUrl={'/configure-collections/'}/>
|
163
|
+
<Box padding={8} background="neutral100">
|
164
|
+
<Box paddingBottom={4}>
|
165
|
+
<Typography variant="alpha">Configure Collections</Typography>
|
166
|
+
</Box>
|
167
|
+
{
|
168
|
+
config && (
|
169
|
+
<TwoColsLayout startCol={
|
170
|
+
<Table colCount={4} rowCount={config.length}>
|
171
|
+
<Thead>
|
172
|
+
<Tr>
|
173
|
+
<Th style={{width: "250px"}}>
|
174
|
+
<Typography variant="sigma">Collection</Typography>
|
175
|
+
</Th>
|
176
|
+
<Th style={{width: "250px"}}>
|
177
|
+
<Typography variant="sigma">Index</Typography>
|
178
|
+
</Th>
|
179
|
+
<Th style={{width: "250px"}}>
|
180
|
+
<Typography variant="sigma">Do not Index</Typography>
|
181
|
+
</Th>
|
182
|
+
<Th>
|
183
|
+
<Typography variant="sigma">Actions</Typography>
|
184
|
+
</Th>
|
185
|
+
</Tr>
|
186
|
+
</Thead>
|
187
|
+
<Tbody>
|
188
|
+
{
|
189
|
+
config.map((collection, idx) => {
|
190
|
+
return (
|
191
|
+
<Tr key={idx}>
|
192
|
+
<Td>
|
193
|
+
<Typography textColor="neutral600">{collection.collectionName}
|
194
|
+
</Typography>
|
195
|
+
</Td>
|
196
|
+
<Td>
|
197
|
+
{collection.indexed.map((i) =>
|
198
|
+
<Box paddingBottom={2}>
|
199
|
+
<Typography textColor="neutral600">{i}
|
200
|
+
</Typography></Box>
|
201
|
+
)}
|
202
|
+
</Td>
|
203
|
+
<Td>
|
204
|
+
{collection.notIndexed.map((i) =>
|
205
|
+
<Box paddingBottom={2}>
|
206
|
+
<Typography textColor="neutral600">{i}
|
207
|
+
</Typography></Box>
|
208
|
+
)}
|
209
|
+
</Td>
|
210
|
+
<Td>
|
211
|
+
<IconButton onClick={() => history.push(`/plugins/${pluginId}/configure-collections/${collection.collectionName}`)} label="Edit collection configuration" noBorder icon={<Pencil />} />
|
212
|
+
<IconButton onClick={() => scheduleCollectionIndexing(collection.collectionName)} label="Schedule indexing for all items in this collection" noBorder icon={<Server />} />
|
213
|
+
</Td>
|
214
|
+
</Tr>
|
215
|
+
);
|
216
|
+
})
|
217
|
+
}
|
218
|
+
</Tbody>
|
219
|
+
</Table>}
|
220
|
+
endCol={<>
|
221
|
+
<Box paddingLeft={2} paddingRight={2} paddingTop={4} paddingBottom={4} >
|
222
|
+
<Box paddingTop={4} paddingBottom={4}>
|
223
|
+
<Typography variant="pi" fontWeight="bold" textColor="neutral600" >CONFIG ACTIONS</Typography>
|
224
|
+
</Box>
|
225
|
+
<Divider />
|
226
|
+
<Box paddingTop={4} paddingBottom={4}>
|
227
|
+
<Box paddingTop={2} paddingBottom={2}>
|
228
|
+
<Button loading={isInProgress} fullWidth variant="secondary" onClick={performExport}>Export</Button>
|
229
|
+
</Box>
|
230
|
+
<Box paddingTop={2} paddingBottom={2}>
|
231
|
+
<Button loading={isInProgress} fullWidth variant="secondary" onClick={() => setDisplayImportModal(true)}>Import</Button>
|
232
|
+
</Box>
|
233
|
+
</Box>
|
234
|
+
</Box>
|
235
|
+
</>
|
236
|
+
} />
|
237
|
+
)
|
238
|
+
}
|
239
|
+
{displayImportModal &&
|
240
|
+
<ModalLayout onClose={() => setDisplayImportModal(false)} labelledBy="title">
|
241
|
+
<ModalHeader>
|
242
|
+
<Typography fontWeight="bold" textColor="neutral800" as="h2" id="title">
|
243
|
+
Import Search Configuration
|
244
|
+
</Typography>
|
245
|
+
</ModalHeader>
|
246
|
+
<ModalBody>
|
247
|
+
<Textarea
|
248
|
+
label="Configuration Json"
|
249
|
+
error={!isEnteredJsonValid ? 'Invalid Json' : undefined}
|
250
|
+
onChange={e => setImportJson(e.target.value)}
|
251
|
+
>{importJson}</Textarea>
|
252
|
+
</ModalBody>
|
253
|
+
<ModalFooter startActions={<Button onClick={() => setDisplayImportModal(false)} variant="tertiary">
|
254
|
+
Cancel
|
255
|
+
</Button>} endActions={<>
|
256
|
+
<Button loading={isInProgress} onClick={performImport} disabled={!isEnteredJsonValid && !importJson.length>0}>Import</Button>
|
257
|
+
</>} />
|
258
|
+
</ModalLayout>}
|
259
|
+
</Box>
|
260
|
+
</Flex>
|
261
|
+
</>
|
262
|
+
);
|
263
|
+
}
|
264
|
+
};
|
265
|
+
|
266
|
+
export default Configure;
|
@@ -0,0 +1,168 @@
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
2
|
+
import { SubNavigation } from '../../components/SubNavigation';;
|
3
|
+
import { Box, Flex, Tab } from '@strapi/design-system';
|
4
|
+
import { Typography } from '@strapi/design-system';
|
5
|
+
import { apiGetElasticsearchSetupInfo, apiRequestReIndexing,
|
6
|
+
apiTriggerIndexing } from '../../utils/apiUrls';
|
7
|
+
import axiosInstance from '../../utils/axiosInstance';
|
8
|
+
import { IconButton } from '@strapi/design-system';
|
9
|
+
import { Table, Tr, Td } from '@strapi/design-system';
|
10
|
+
import { Refresh } from '@strapi/icons';
|
11
|
+
import { TwoColsLayout, Button } from '@strapi/design-system';
|
12
|
+
import { Grid, GridItem, Divider } from '@strapi/design-system';
|
13
|
+
import {LoadingIndicatorPage, useNotification} from '@strapi/helper-plugin';
|
14
|
+
|
15
|
+
const loadElasticsearchSetupInfo = () => {
|
16
|
+
return axiosInstance.get(apiGetElasticsearchSetupInfo)
|
17
|
+
.then((resp) => resp.data)
|
18
|
+
.then((data) => {
|
19
|
+
return data;
|
20
|
+
});
|
21
|
+
}
|
22
|
+
|
23
|
+
const Homepage = () => {
|
24
|
+
const [setupInfo, setSetupInfo] = useState(null);
|
25
|
+
const [isInProgress, setIsInProgress] = useState(false);
|
26
|
+
const toggleNotification = useNotification();
|
27
|
+
|
28
|
+
const displayLabels = {'connected' : 'Connected',
|
29
|
+
'elasticCertificate' : 'Certificate',
|
30
|
+
'elasticHost' : 'Elasticsearch host',
|
31
|
+
'elasticIndexAlias' : 'Elasticsearch index Alias name',
|
32
|
+
'elasticUserName' : 'Elasticsearch username',
|
33
|
+
'indexingCronSchedule' : 'Indexing cron schedule',
|
34
|
+
'initialized' : 'Elasticsearch configuration loaded'};
|
35
|
+
|
36
|
+
const reloadElasticsearchSetupInfo = ({showNotification}) => {
|
37
|
+
setIsInProgress(true);
|
38
|
+
loadElasticsearchSetupInfo()
|
39
|
+
.then(setSetupInfo)
|
40
|
+
.then(() => {
|
41
|
+
if (showNotification)
|
42
|
+
toggleNotification({
|
43
|
+
type: "success", message: "Elasticsearch setup information reloaded.", timeout: 5000
|
44
|
+
});
|
45
|
+
})
|
46
|
+
.finally(() => setIsInProgress(false));
|
47
|
+
}
|
48
|
+
|
49
|
+
const requestFullSiteReindexing = () => {
|
50
|
+
setIsInProgress(true);
|
51
|
+
return axiosInstance.get(apiRequestReIndexing)
|
52
|
+
.then(() => {
|
53
|
+
toggleNotification({
|
54
|
+
type: "success", message: "Rebuilding the index is triggered.", timeout: 5000
|
55
|
+
});
|
56
|
+
})
|
57
|
+
.catch(() => {
|
58
|
+
toggleNotification({
|
59
|
+
type: "warning", message: "An error was encountered.", timeout: 5000
|
60
|
+
});
|
61
|
+
})
|
62
|
+
.finally(() => setIsInProgress(false));
|
63
|
+
}
|
64
|
+
|
65
|
+
const triggerIndexingRun = () => {
|
66
|
+
setIsInProgress(true);
|
67
|
+
return axiosInstance.get(apiTriggerIndexing)
|
68
|
+
.then(() => {
|
69
|
+
toggleNotification({
|
70
|
+
type: "success", message: "The indexing job to process the pending tasks is started.", timeout: 5000
|
71
|
+
});
|
72
|
+
})
|
73
|
+
.catch(() => {
|
74
|
+
toggleNotification({
|
75
|
+
type: "warning", message: "An error was encountered.", timeout: 5000
|
76
|
+
});
|
77
|
+
})
|
78
|
+
.finally(() => setIsInProgress(false));
|
79
|
+
}
|
80
|
+
useEffect(() => {
|
81
|
+
reloadElasticsearchSetupInfo({showNotification: false});
|
82
|
+
}, []);
|
83
|
+
|
84
|
+
if (setupInfo === null)
|
85
|
+
return <LoadingIndicatorPage />
|
86
|
+
else
|
87
|
+
return (
|
88
|
+
<Flex alignItems="stretch" gap={4}>
|
89
|
+
<SubNavigation />
|
90
|
+
<Box padding={8} background="neutral100" width="100%">
|
91
|
+
<Box paddingBottom={4}>
|
92
|
+
<Typography variant="alpha">Setup Information</Typography>
|
93
|
+
</Box>
|
94
|
+
<Box width="100%" paddingBottom={4}>
|
95
|
+
<TwoColsLayout startCol={
|
96
|
+
<>
|
97
|
+
<Table>
|
98
|
+
{
|
99
|
+
setupInfo && (
|
100
|
+
Object.keys(setupInfo).map((k, idx) => {
|
101
|
+
return (
|
102
|
+
<Tr key={idx}>
|
103
|
+
<Td><Box padding={2}>
|
104
|
+
<Typography textColor="neutral600">{displayLabels[k]} :</Typography>
|
105
|
+
</Box></Td>
|
106
|
+
<Td>
|
107
|
+
<Box padding={2}>
|
108
|
+
<Grid>
|
109
|
+
<GridItem padding={2}>
|
110
|
+
{
|
111
|
+
k === 'connected' && setupInfo[k] === true &&
|
112
|
+
(
|
113
|
+
<Typography fontWeight="bold" textColor="success500">Yes</Typography>
|
114
|
+
)
|
115
|
+
}
|
116
|
+
{
|
117
|
+
k === 'connected' && setupInfo[k] === false &&
|
118
|
+
(
|
119
|
+
<Typography fontWeight="bold" textColor="danger500">No</Typography>
|
120
|
+
)
|
121
|
+
}
|
122
|
+
{
|
123
|
+
k !== 'connected' &&
|
124
|
+
(
|
125
|
+
<Typography textColor="neutral600">{String(setupInfo[k])}</Typography>
|
126
|
+
)
|
127
|
+
}
|
128
|
+
</GridItem>
|
129
|
+
<GridItem padding={1}>
|
130
|
+
{
|
131
|
+
k === 'connected' ?
|
132
|
+
<IconButton disabled={isInProgress} onClick={() => reloadElasticsearchSetupInfo({showNotification: true})} label="Refresh" icon={<Refresh />} /> : null
|
133
|
+
}
|
134
|
+
</GridItem>
|
135
|
+
</Grid>
|
136
|
+
</Box>
|
137
|
+
</Td>
|
138
|
+
</Tr>
|
139
|
+
);
|
140
|
+
})
|
141
|
+
)
|
142
|
+
}
|
143
|
+
</Table></>}
|
144
|
+
endCol={<>
|
145
|
+
<Box paddingLeft={2} paddingRight={2} paddingTop={4} paddingBottom={4} >
|
146
|
+
<Box paddingTop={4} paddingBottom={4}>
|
147
|
+
<Typography variant="pi" fontWeight="bold" textColor="neutral600" >ACTIONS</Typography>
|
148
|
+
</Box>
|
149
|
+
<Divider />
|
150
|
+
<Box paddingTop={4} paddingBottom={4}>
|
151
|
+
<Box paddingTop={2} paddingBottom={2}>
|
152
|
+
<Button loading={isInProgress} fullWidth variant="secondary" onClick={requestFullSiteReindexing}>Rebuild Index</Button>
|
153
|
+
</Box>
|
154
|
+
<Box paddingTop={2} paddingBottom={2}>
|
155
|
+
<Button loading={isInProgress} fullWidth variant="secondary" onClick={triggerIndexingRun}>Trigger Indexing</Button>
|
156
|
+
</Box>
|
157
|
+
</Box>
|
158
|
+
</Box>
|
159
|
+
</>
|
160
|
+
} />
|
161
|
+
</Box>
|
162
|
+
</Box>
|
163
|
+
</Flex>
|
164
|
+
);
|
165
|
+
};
|
166
|
+
|
167
|
+
|
168
|
+
export default Homepage;
|