ElasticsearchManagement.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import React, { useEffect, useState, useCallback } from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import { toastSuccess, toastError } from '~/client/util/apiNotification';
  4. import { apiv3Get, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
  5. import { SocketEventName } from '~/interfaces/websocket';
  6. import { useIsSearchServiceReachable } from '~/stores/context';
  7. import { useAdminSocket } from '~/stores/socket-io';
  8. import NormalizeIndicesControls from './NormalizeIndicesControls';
  9. import RebuildIndexControls from './RebuildIndexControls';
  10. import ReconnectControls from './ReconnectControls';
  11. import StatusTable from './StatusTable';
  12. const ElasticsearchManagement = () => {
  13. const { t } = useTranslation('admin');
  14. const { data: isSearchServiceReachable } = useIsSearchServiceReachable();
  15. const { data: socket } = useAdminSocket();
  16. const [isInitialized, setIsInitialized] = useState(false);
  17. const [isConnected, setIsConnected] = useState(false);
  18. const [isConfigured, setIsConfigured] = useState(false);
  19. const [isReconnectingProcessing, setIsReconnectingProcessing] = useState(false);
  20. const [isRebuildingProcessing, setIsRebuildingProcessing] = useState(false);
  21. const [isRebuildingCompleted, setIsRebuildingCompleted] = useState(false);
  22. const [isNormalized, setIsNormalized] = useState(false);
  23. const [indicesData, setIndicesData] = useState(null);
  24. const [aliasesData, setAliasesData] = useState(null);
  25. const retrieveIndicesStatus = useCallback(async() => {
  26. try {
  27. const { data } = await apiv3Get('/search/indices');
  28. const { info } = data;
  29. setIsConnected(true);
  30. setIsConfigured(true);
  31. setIndicesData(info.indices);
  32. setAliasesData(info.aliases);
  33. setIsNormalized(info.isNormalized);
  34. }
  35. catch (errors: unknown) {
  36. setIsConnected(false);
  37. // evaluate whether configured or not
  38. if (Array.isArray(errors)) {
  39. for (const error of errors) {
  40. if (error.code === 'search-service-unconfigured') {
  41. setIsConfigured(false);
  42. }
  43. }
  44. }
  45. toastError(errors);
  46. }
  47. finally {
  48. setIsInitialized(true);
  49. }
  50. }, []);
  51. useEffect(() => {
  52. const fetchIndicesStatusData = async() => {
  53. await retrieveIndicesStatus();
  54. };
  55. fetchIndicesStatusData();
  56. }, [retrieveIndicesStatus]);
  57. useEffect(() => {
  58. if (socket == null) {
  59. return;
  60. }
  61. socket.on(SocketEventName.AddPageProgress, (data) => {
  62. setIsRebuildingProcessing(true);
  63. });
  64. socket.on(SocketEventName.FinishAddPage, async(data) => {
  65. await retrieveIndicesStatus();
  66. setIsRebuildingProcessing(false);
  67. setIsRebuildingCompleted(true);
  68. });
  69. socket.on(SocketEventName.RebuildingFailed, (data) => {
  70. toastError(new Error(data.error), 'Rebuilding Index has failed.');
  71. });
  72. return () => {
  73. socket.off(SocketEventName.AddPageProgress);
  74. socket.off(SocketEventName.FinishAddPage);
  75. socket.off(SocketEventName.RebuildingFailed);
  76. };
  77. }, [socket]);
  78. const reconnect = async() => {
  79. setIsReconnectingProcessing(true);
  80. try {
  81. await apiv3Post('/search/connection');
  82. }
  83. catch (e) {
  84. toastError(e);
  85. return;
  86. }
  87. // reload
  88. window.location.reload();
  89. };
  90. const normalizeIndices = async() => {
  91. try {
  92. await apiv3Put('/search/indices', { operation: 'normalize' });
  93. }
  94. catch (e) {
  95. toastError(e);
  96. }
  97. await retrieveIndicesStatus();
  98. toastSuccess('Normalizing has succeeded');
  99. };
  100. const rebuildIndices = async() => {
  101. setIsRebuildingProcessing(true);
  102. try {
  103. await apiv3Put('/search/indices', { operation: 'rebuild' });
  104. toastSuccess('Rebuilding is requested');
  105. }
  106. catch (e) {
  107. toastError(e);
  108. }
  109. await retrieveIndicesStatus();
  110. };
  111. const isErrorOccuredOnSearchService = !isSearchServiceReachable;
  112. const isReconnectBtnEnabled = !isReconnectingProcessing && (!isInitialized || !isConnected || isErrorOccuredOnSearchService);
  113. return (
  114. <>
  115. <div className="row">
  116. <div className="col-md-12">
  117. <StatusTable
  118. isInitialized={isInitialized}
  119. isErrorOccuredOnSearchService={isErrorOccuredOnSearchService}
  120. isConnected={isConnected}
  121. isConfigured={isConfigured}
  122. isNormalized={isNormalized}
  123. indicesData={indicesData}
  124. aliasesData={aliasesData}
  125. />
  126. </div>
  127. </div>
  128. <hr />
  129. {/* Controls */}
  130. <div className="row">
  131. <label className="col-md-3 col-form-label text-left text-md-right">{ t('full_text_search_management.reconnect') }</label>
  132. <div className="col-md-6">
  133. <ReconnectControls
  134. isEnabled={isReconnectBtnEnabled}
  135. isProcessing={isReconnectingProcessing}
  136. onReconnectingRequested={reconnect}
  137. />
  138. </div>
  139. </div>
  140. <hr />
  141. <div className="row">
  142. <label className="col-md-3 col-form-label text-left text-md-right">{ t('full_text_search_management.normalize') }</label>
  143. <div className="col-md-6">
  144. <NormalizeIndicesControls
  145. isRebuildingProcessing={isRebuildingProcessing}
  146. isNormalized={isNormalized}
  147. onNormalizingRequested={normalizeIndices}
  148. />
  149. </div>
  150. </div>
  151. <hr />
  152. <div className="row">
  153. <label className="col-md-3 col-form-label text-left text-md-right">{ t('full_text_search_management.rebuild') }</label>
  154. <div className="col-md-6">
  155. <RebuildIndexControls
  156. isRebuildingProcessing={isRebuildingProcessing}
  157. isRebuildingCompleted={isRebuildingCompleted}
  158. isNormalized={isNormalized}
  159. onRebuildingRequested={rebuildIndices}
  160. />
  161. </div>
  162. </div>
  163. </>
  164. );
  165. };
  166. ElasticsearchManagement.propTypes = {
  167. };
  168. export default ElasticsearchManagement;