├── .editorconfig ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── admin └── src │ ├── Components │ └── LeftMenuItem │ │ ├── Wrapper.js │ │ └── index.js │ ├── assets │ └── images │ │ └── logo.svg │ ├── containers │ ├── App │ │ └── index.js │ ├── DataView │ │ ├── Wrapper.js │ │ └── index.js │ ├── HomePage │ │ ├── GlobalStyles.js │ │ └── index.js │ ├── Initializer │ │ └── index.js │ └── LeftMenu │ │ ├── Wrapper.js │ │ └── index.js │ ├── index.js │ ├── lifecycles.js │ ├── pluginId.js │ ├── translations │ ├── ar.json │ ├── cs.json │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── index.js │ ├── it.json │ ├── ko.json │ ├── ms.json │ ├── nl.json │ ├── pl.json │ ├── pt-BR.json │ ├── pt.json │ ├── ru.json │ ├── sk.json │ ├── th.json │ ├── tr.json │ ├── uk.json │ ├── vi.json │ ├── zh-Hans.json │ └── zh.json │ └── utils │ └── getTrad.js ├── config ├── constants.js ├── functions │ └── bootstrap.js └── routes.json ├── controllers └── elasticsearch.js ├── docs └── CONFIG.md ├── middlewares └── elastic │ ├── defaults.json │ └── index.js ├── package.json ├── services ├── functions.js ├── helper.js ├── index.js ├── logger.js └── middleware.js ├── yarn-error.log └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = false 6 | indent_style = space 7 | indent_size = 2 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes 2 | 3 | # Handle line endings automatically for files detected as text 4 | # and leave all files detected as binary untouched. 5 | * text=auto 6 | 7 | # 8 | # The above will handle all files NOT found below 9 | # 10 | 11 | # 12 | ## These files are text and should be normalized (Convert crlf => lf) 13 | # 14 | 15 | # source code 16 | *.php text 17 | *.css text 18 | *.sass text 19 | *.scss text 20 | *.less text 21 | *.styl text 22 | *.js text eol=lf 23 | *.coffee text 24 | *.json text 25 | *.htm text 26 | *.html text 27 | *.xml text 28 | *.svg text 29 | *.txt text 30 | *.ini text 31 | *.inc text 32 | *.pl text 33 | *.rb text 34 | *.py text 35 | *.scm text 36 | *.sql text 37 | *.sh text 38 | *.bat text 39 | 40 | # templates 41 | *.ejs text 42 | *.hbt text 43 | *.jade text 44 | *.haml text 45 | *.hbs text 46 | *.dot text 47 | *.tmpl text 48 | *.phtml text 49 | 50 | # git config 51 | .gitattributes text 52 | .gitignore text 53 | .gitconfig text 54 | 55 | # code analysis config 56 | .jshintrc text 57 | .jscsrc text 58 | .jshintignore text 59 | .csslintrc text 60 | 61 | # misc config 62 | *.yaml text 63 | *.yml text 64 | .editorconfig text 65 | 66 | # build config 67 | *.npmignore text 68 | *.bowerrc text 69 | 70 | # Heroku 71 | Procfile text 72 | .slugignore text 73 | 74 | # Documentation 75 | *.md text 76 | LICENSE text 77 | AUTHORS text 78 | 79 | 80 | # 81 | ## These files are binary and should be left untouched 82 | # 83 | 84 | # (binary is a macro for -text -diff) 85 | *.png binary 86 | *.jpg binary 87 | *.jpeg binary 88 | *.gif binary 89 | *.ico binary 90 | *.mov binary 91 | *.mp4 binary 92 | *.mp3 binary 93 | *.flv binary 94 | *.fla binary 95 | *.swf binary 96 | *.gz binary 97 | *.zip binary 98 | *.7z binary 99 | *.ttf binary 100 | *.eot binary 101 | *.woff binary 102 | *.pyc binary 103 | *.pdf binary 104 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't check auto-generated stuff into git 2 | coverage 3 | node_modules 4 | stats.json 5 | package-lock.json 6 | 7 | # Cruft 8 | .DS_Store 9 | npm-debug.log 10 | .idea 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 marefati110 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Project logo 4 |
5 |
6 | plugin 7 |

8 | 9 |
10 | 11 | [![GitHub issues](https://img.shields.io/github/issues/marefati110/strapi-plugin-elasticsearch?style=flat-square)](https://github.com/marefati110/strapi-plugin-elasticsearch/issues) 12 | [![GitHub forks](https://img.shields.io/github/forks/marefati110/strapi-plugin-elasticsearch)](https://github.com/marefati110/strapi-plugin-elasticsearch/network) 13 | [![GitHub stars](https://img.shields.io/github/stars/marefati110/strapi-plugin-elasticsearch)](https://github.com/marefati110/strapi-plugin-elasticsearch/stargazers) 14 | [![GitHub license](https://img.shields.io/github/license/marefati110/strapi-plugin-elasticsearch)](https://github.com/marefati110/strapi-plugin-elasticsearch) 15 | 16 |
17 |
18 |

19 | tested on strapi v3.x 20 | 21 | latest test: v3.4.0 22 | 23 |

24 | 25 |

26 | This plugin has not been tested on mongodb 27 |

28 |
29 |
30 |

31 | The purpose of developing this plugin is to use the elastic search engine in Strapi to help the application development process 32 |

33 | 34 | ## 📝 Table of Contents 35 | 36 | - [Prerequisites](#prerequisites) 37 | - [Getting Started](#getting_started) 38 | - [How plugin work](#how_work) 39 | - [Usage](#usage) 40 | - [scenario 1](#scenario-1) 41 | - [scenario 2](#scenario-2) 42 | - [scenario 3](#scenario-3) 43 | - [scenario 4](#scenario-4) 44 | - [Functions](#functions) 45 | - [Api](#api) 46 | - [Example](#example) 47 | - [Logging](#logging) 48 | - [Authors](#authors) 49 | 50 | ## Prerequisites 51 | 52 |
53 | 54 | Install Elasticsearch - https://www.elastic.co/downloads/elasticsearch 55 | 56 | Install plugin 57 | 58 | - Go to the project path 59 | 60 | - `cd PROJECT/plugins` 61 | 62 | - Clone the project 63 | 64 | - `git submodule add https://github.com/marefati110/strapi-plugin-elasticsearch.git ./elastic` 65 | 66 | - Install dependencies 67 | 68 | - `yarn install` 69 | 70 | # 🏁 Getting Started 71 | 72 | ## How plugin works? 73 | 74 | After the first run of the project, it creates a config file at `PROJECT/config/elasticsearch.js` 75 | 76 | **config file should look like the image** 77 | 78 |

79 | code3 80 |

81 | 82 | By default, syncing occurs in two ways 83 | 84 | The answer of any request that makes a change in the model is stored in Elasticsearch this is especially true for the Strap panel admin 85 | 86 | Or in response to any request, search for the pk of model the model in which the change was made, and after retrieving the information from the database, stores it in the elasticsearch 87 | 88 | In the following, solutions are prepared for more complex scenarios. 89 | 90 | After installing the plugin and running it, it creates an config file in the `PROJECT/config/elasticsearch.js` 91 | 92 | In the connections section, the settings related to the connection to the elasticsearch are listed, there is also a help link 93 | 94 | In the setting section, there are the initial settings related to the elastic plugin. 95 | 96 | In the models section for all models in the `Project/api/**` path there is a model being built and you can change the initial settings 97 | 98 |
99 | 100 | # 🎈 Usage 101 | 102 | ### Scenario 1 103 | 104 | For example, we want to make changes to the article model and then see the changes in the Elasticsearch. 105 | 106 | The first step is to activate in the settings related to this model 107 | 108 | After saving and restarting the plugin, it creates an index for this model in the elasticsearch. 109 | 110 | Note that the name selected for the index can be changed in the settings of the model. 111 | 112 | At the end of the settings should be as follows 113 | 114 | ```js 115 | { 116 | model: 'article', 117 | pk: 'id', 118 | plugin: null, // changed to true 119 | enabled: true, 120 | index: 'article', 121 | relations: [], 122 | conditions: {}, 123 | supportAdminPanel: true, 124 | fillByResponse: true, 125 | migration: false, 126 | urls: [], 127 | }, 128 | ``` 129 | 130 | Now in the strapi admin panel, by making an creating , deleting or updating , you can see the changes in Elasticsearch. 131 | 132 | ### Scenario 2 133 | 134 | In this scenario, we want to make a change in the model using the rest api and see the result in Elasticsearch. 135 | 136 | After sending a post request to `/articles`, changes will be applied and we will receive a response to this 137 | 138 | ```json 139 | { 140 | "id": 1, 141 | "title": "title", 142 | "content": "content" 143 | } 144 | ``` 145 | 146 | and model config should change to 147 | 148 | ```js 149 | { 150 | model: 'article', 151 | pk: 'id', 152 | plugin: null, 153 | enabled: true, 154 | index: 'article', 155 | relations: [], 156 | conditions: {}, 157 | supportAdminPanel: true, 158 | fillByResponse: true, // default value 159 | migration: false, 160 | urls: ['/articles'], //changed 161 | }, 162 | ``` 163 | 164 | If the `fillByResponse` settings are enabled for the model, the same data will be stored in Elasticsearch, otherwise the data will be retrieved from the database using pk and stored in Elasticsearch. 165 | 166 | ### Scenario 3 167 | 168 | This scenario is quite similar to the previous scenario with these differences being the response 169 | 170 | ```json 171 | { 172 | "metaData": null, 173 | "data": { 174 | "articleID": 1, 175 | "title": "title", 176 | "content": "content" 177 | } 178 | } 179 | ``` 180 | 181 | By default, the plugin looks for pk in the response or `ctx.body.id` 182 | 183 | We can rewrite these settings for a specific url 184 | 185 | config model should change to 186 | 187 | ```js 188 | { 189 | model: 'article', 190 | pk: 'id', 191 | plugin: null, 192 | enabled: true, 193 | index: 'article', 194 | relations: [], 195 | conditions: {}, 196 | supportAdminPanel: true, 197 | fillByResponse: true, 198 | migration: false, 199 | urls: [ 200 | { 201 | '/articles':{ 202 | pk: 'data.articleID', // over write 203 | relations: [], // over write 204 | conditions: {}, // over write 205 | } 206 | } 207 | ], 208 | }, 209 | ``` 210 | 211 | ### Scenario 4 212 | 213 | In this scenario, no pk may be found in the request response 214 | 215 | ```json 216 | { 217 | "success": true 218 | } 219 | ``` 220 | 221 | In this case, the synchronization operation can be performed on the controller 222 | 223 | there is some functions for help 224 | 225 | ```js 226 | const articleData = { title: 'title', content: 'content' }; 227 | const article = await strapi.query('article').create(articleData); 228 | 229 | strapi.elastic.createOrUpdate('article', { data: article, id: article.id }); 230 | // or 231 | strapi.elastic.migrateById('article', { id: article.id }); // execute new query 232 | ``` 233 | 234 | and for delete data 235 | 236 | ```js 237 | const articleId = 1; 238 | const article = await strapi.query('article').delete(articleData); 239 | 240 | strapi.elastic.destroy('article', { id: articleID }); 241 | ``` 242 | 243 | # Functions 244 | 245 | | Command | Description | example | 246 | | :------------------------------ | :----------------------------- | :--------------------------: | 247 | | `strapi.elastic` | official elasticsearch package | [example](#elastic) | 248 | | `strapi.elastic.createOrUpdate` | Create to update data | [example](#create_or_update) | 249 | | `strapi.elastic.findOne` | Find specific data by id | [example](#findOne) | 250 | | `strapi.elastic.destroy` | delete data | [example](#destroy) | 251 | | `strapi.elastic.migrateById` | migrate data | [example](#migrateById) | 252 | | `strapi.elastic.migrateModel` | migrate specific data | [example](#migrateModel) | 253 | | `strapi.elastic.models` | migrate all enabled models | [example](#models) | 254 | | `strapi.log` | log data to elasticsearch | [example](#logging) | 255 | 256 | # Api 257 | 258 | | Url | Method | Description | body | 259 | | :-------------- | :----: | :------------------------- | ---------------------- | 260 | | /migrate-models | POST | Migrate all enabled Models | | 261 | | /migrate-Model | POST | Migrate specific model | `{model:'MODEL_NAME'}` | 262 | 263 | # Examples 264 | 265 | ### elastic 266 | 267 | For use official Elasticsearch package we can use `strapi.elastic`, and can access builtin function 268 | [elasticsearch reference api](https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html) 269 | 270 | ```js 271 | const count = strapi.elastic.count({ index: 'article' }); // https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#_count 272 | 273 | const article = strapi.elastic.get({ index: 'article', id: 1 }); // https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#_get 274 | ``` 275 | 276 | ### CreateOrUpdate 277 | 278 | ```js 279 | const result = strapi.elastic.createOrUpdate('article', { 280 | id: 1, 281 | data: { title: 'title', content: 'content' }, 282 | }); 283 | ``` 284 | 285 | ### findOne 286 | 287 | ```js 288 | const result = strapi.elastic.findOne('article', { id: 1 }); 289 | ``` 290 | 291 | ### destroy 292 | 293 | ```js 294 | const result_one = strapi.elastic.destroy('article', { id: 1 }); 295 | // or 296 | const result_two = strapi.elastic.destroy('article', { id_in: [1, 2, 3] }); 297 | ``` 298 | 299 | ### migrateById 300 | 301 | ```js 302 | const result_one = strapi.elastic.migrateById('article', { id: 1 }); 303 | 304 | const result_two = strapi.elastic.migrateById('article', { id_in: [1, 2, 3] }); 305 | ``` 306 | 307 | ### migrateModel 308 | 309 | ```js 310 | const result = strapi.elastic.migrateModel('article', { 311 | conditions, // optional 312 | }); 313 | ``` 314 | 315 | ### migrateModels 316 | 317 | ```js 318 | const result = strapi.elastic.migrateModels({ 319 | conditions, // optional (the conditions apply on all models) 320 | }); 321 | ``` 322 | 323 | # Logging 324 | 325 | strapi use Pino to logging but can store logs or send it to elasticsearch 326 | 327 | at now wen can send logs to elasticsearch by `strapi.elastic.log` there is no difference between `strapi.elastic.log` with `strapi.log` to call functions. 328 | 329 | ```js 330 | strapi.log.info('log message in console'); 331 | strapi.elastic.log.info('log message console and store it to elasticsearch'); 332 | 333 | strapi.log.debug('log message'); 334 | strapi.elastic.log.debug('log message console and store it to elasticsearch'); 335 | 336 | strapi.log.warn('log message'); 337 | strapi.elastic.log.warn('log message console and store it to elasticsearch'); 338 | 339 | strapi.log.error('log message'); 340 | strapi.elastic.log.error('log message console and store it to elasticsearch'); 341 | 342 | strapi.log.fatal('log message'); 343 | strapi.elastic.log.fatal('log message console and store it to elasticsearch'); 344 | ``` 345 | 346 | Also there is some more options 347 | 348 | ```js 349 | // just send log to elastic and avoid to display in console 350 | strapi.elastic.log.info('some message', { setting: { show: false } }); 351 | 352 | // just display relations, // optional ni console and avoid to save it to elastic search 353 | strapi.elastic.log.info('some message', { setting: { saveToElastic: false } }); 354 | 355 | // send more data to elasticsearch 356 | const logData = { description: 'description' }; 357 | strapi.elastic.log.info('some message', logData); 358 | ``` 359 | 360 | **By default `strapi.log` send some metaData to elasticsearch such as `free memory`, `cpu load avg`, `current time`, `hostname` ,...** 361 | 362 | # Tricks 363 | 364 | to avoid config plugin for all model or write a lot of code we can create cron job for migration 365 | 366 | ```js 367 | const moment = require('moment'); 368 | module.exports = { 369 | '*/10 * * * *': async () => { 370 | const updateTime = moment() 371 | .subtract(10, 'minutes') 372 | .format('YYYY-MM-DD HH:mm:ss'); 373 | 374 | // currentTime 375 | await strapi.elastic.migrateModels({ 376 | conditions: { 377 | updated_at_gt: updateTime, 378 | /* to utilise Draft/Publish feature & migrate only published entities 379 | you can add following in conditions 380 | */ 381 | _publicationState: 'live' 382 | }, 383 | }); 384 | }, 385 | }; 386 | ``` 387 | 388 | ### ✍️ Authors 389 | 390 | - [@marefati110](https://github.com/marefati110) 391 | -------------------------------------------------------------------------------- /admin/src/Components/LeftMenuItem/Wrapper.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Wrapper = styled.div` 4 | white-space: nowrap; 5 | overflow: hidden; 6 | text-overflow: ellipsis; 7 | cursor: pointer; 8 | display: flex; 9 | align-items: center; 10 | width: 100%; 11 | height: 35px; 12 | padding-left: 15px; 13 | background-color: ${({ active }) => (active ? '#e9eaeb' : 'transparent')}; 14 | opacity: ${({ enable }) => (enable ? '1' : '0.4')}; 15 | 16 | svg { 17 | color: rgb(145, 155, 174); 18 | width: 5px; 19 | height: 5px; 20 | } 21 | `; 22 | 23 | export default Wrapper; 24 | -------------------------------------------------------------------------------- /admin/src/Components/LeftMenuItem/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Wrapper from './Wrapper'; 4 | 5 | const LeftMenu = ({ label, onClick, active, enable }) => { 6 | return ( 7 | { 9 | if (enable) onClick(); 10 | }} 11 | active={active} 12 | enable={enable} 13 | > 14 | 29 | 30 | {label} 31 | 32 | ); 33 | }; 34 | 35 | LeftMenu.propTypes = { 36 | label: PropTypes.string.isRequired, 37 | onClick: PropTypes.func.isRequired, 38 | active: PropTypes.bool.isRequired, 39 | }; 40 | 41 | export default LeftMenu; 42 | -------------------------------------------------------------------------------- /admin/src/assets/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | Elastic Search Color Lockup 11 | 13 | 15 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /admin/src/containers/App/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * This component is the skeleton around the actual pages, and should only 4 | * contain code that should be seen on all pages. (e.g. navigation bar) 5 | * 6 | */ 7 | 8 | import React from 'react'; 9 | import { Switch, Route } from 'react-router-dom'; 10 | import { NotFound } from 'strapi-helper-plugin'; 11 | // Utils 12 | import pluginId from '../../pluginId'; 13 | // Containers 14 | import HomePage from '../HomePage'; 15 | 16 | const App = () => { 17 | return ( 18 |
19 | 20 | 21 | 22 | 23 |
24 | ); 25 | }; 26 | 27 | export default App; 28 | -------------------------------------------------------------------------------- /admin/src/containers/DataView/Wrapper.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Wrapper = styled.div` 4 | margin-left: 25px; 5 | margin-top: 15px; 6 | width: 76%; 7 | h2 { 8 | margin-bottom: 20px; 9 | } 10 | `; 11 | 12 | export default Wrapper; 13 | -------------------------------------------------------------------------------- /admin/src/containers/DataView/index.js: -------------------------------------------------------------------------------- 1 | import React, { memo, useMemo, useState } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { useHistory } from 'react-router-dom'; 4 | import { GlobalPagination, request } from 'strapi-helper-plugin'; 5 | import { isObject } from 'lodash'; 6 | import { Table, Button, Select } from '@buffetjs/core'; 7 | import { LoadingBar } from '@buffetjs/styles'; 8 | import Wrapper from './Wrapper'; 9 | 10 | const LIMIT_OPTIONS = ['10', '20', '50', '100']; 11 | 12 | const DataView = ({ 13 | data = [], 14 | activeModel = '', 15 | loading, 16 | page, 17 | limit, 18 | totalCount, 19 | onChangeParams, 20 | isMigrateActive, 21 | isDeleted, 22 | isCreated, 23 | refreshData, 24 | }) => { 25 | const history = useHistory(); 26 | const tableHeaders = useMemo( 27 | () => 28 | data && data.length 29 | ? Object.keys(data[0]).map((d) => ({ name: d, value: d })) 30 | : [], 31 | [data] 32 | ); 33 | 34 | const tableData = useMemo( 35 | () => 36 | data && data.length 37 | ? data.map((dataObject) => { 38 | let newObj = {}; 39 | if (!dataObject) return newObj; 40 | 41 | for (let key in dataObject) { 42 | if (isObject(dataObject[key])) { 43 | newObj[key] = JSON.stringify(dataObject[key], null, 2); 44 | } else { 45 | newObj[key] = dataObject[key]; 46 | } 47 | } 48 | 49 | return newObj; 50 | }) 51 | : [], 52 | [data] 53 | ); 54 | 55 | const [isMigrating, setIsMigrating] = useState(false); 56 | const [isCreating, setIsCreating] = useState(false); 57 | const [isDeleting, setIsDeleting] = useState(false); 58 | 59 | const migrate = (model) => { 60 | setIsMigrating(true); 61 | request(`/elastic/migrate-model`, { 62 | method: 'POST', 63 | body: { model }, 64 | }) 65 | .then((res) => { 66 | if (res.success) { 67 | strapi.notification.success(`${model} model migrated successfully`); 68 | refreshData(); 69 | } else strapi.notification.error(`migration failed`); 70 | }) 71 | .catch(() => strapi.notification.error(`migration failed`)) 72 | .finally(() => setIsMigrating(false)); 73 | }; 74 | 75 | const deleteIndex = (model) => { 76 | setIsDeleting(true); 77 | request(`/elastic/delete-index`, { 78 | method: 'POST', 79 | body: { model }, 80 | }) 81 | .then((res) => { 82 | if (res.success) { 83 | refreshData(); 84 | strapi.notification.success(`${model} index deleted`); 85 | } else { 86 | strapi.notification.error(`cannot delete ${model} index`); 87 | } 88 | }) 89 | .catch(() => { 90 | strapi.notification.error(`cannot delete ${model} index`); 91 | }) 92 | .finally(() => setIsDeleting(false)); 93 | }; 94 | 95 | const createIndex = (model) => { 96 | setIsCreating(true); 97 | request(`/elastic/create-index`, { 98 | method: 'POST', 99 | body: { model }, 100 | }) 101 | .then((res) => { 102 | refreshData(); 103 | if (res.success) { 104 | strapi.notification.success(`${model} index created`); 105 | } else { 106 | strapi.notification.error(`cannot create ${model} index`); 107 | } 108 | }) 109 | .catch(() => strapi.notification.error(`cannot create ${model} index`)) 110 | .finally(() => setIsCreating(false)); 111 | }; 112 | 113 | return ( 114 | 115 |
116 |

{activeModel?.index?.toUpperCase()}

117 | 128 | 139 | 150 |
151 |
152 | {loading ? ( 153 | new Array(10).fill(0).map(() => ( 154 | <> 155 | 156 |
157 | 158 | )) 159 | ) : ( 160 | <> 161 | 165 | history.push( 166 | `/plugins/content-manager/collectionType/${ 167 | activeModel?.plugin 168 | ? `plugins::${activeModel?.plugin}.${activeModel?.model}` 169 | : `application::${activeModel?.model}.${activeModel?.model}` 170 | }/${data.id}` 171 | ) 172 | } 173 | /> 174 |
175 |