ElasticsearchManagement.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import React, { useEffect, useState, useCallback } from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import { apiv3Get, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
  4. import { toastSuccess, toastError } from '~/client/util/toastr';
  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. toastError(errors as Error[]);
  45. }
  46. else {
  47. toastError(errors as Error);
  48. }
  49. }
  50. finally {
  51. setIsInitialized(true);
  52. }
  53. }, []);
  54. useEffect(() => {
  55. const fetchIndicesStatusData = async() => {
  56. await retrieveIndicesStatus();
  57. };
  58. fetchIndicesStatusData();
  59. }, [retrieveIndicesStatus]);
  60. useEffect(() => {
  61. if (socket == null) {
  62. return;
  63. }
  64. socket.on(SocketEventName.AddPageProgress, (data) => {
  65. setIsRebuildingProcessing(true);
  66. });
  67. socket.on(SocketEventName.FinishAddPage, async(data) => {
  68. await retrieveIndicesStatus();
  69. setIsRebuildingProcessing(false);
  70. setIsRebuildingCompleted(true);
  71. });
  72. socket.on(SocketEventName.RebuildingFailed, (data) => {
  73. toastError(new Error(data.error));
  74. });
  75. return () => {
  76. socket.off(SocketEventName.AddPageProgress);
  77. socket.off(SocketEventName.FinishAddPage);
  78. socket.off(SocketEventName.RebuildingFailed);
  79. };
  80. }, [socket]);
  81. const reconnect = async() => {
  82. setIsReconnectingProcessing(true);
  83. try {
  84. await apiv3Post('/search/connection');
  85. }
  86. catch (e) {
  87. toastError(e);
  88. return;
  89. }
  90. // reload
  91. window.location.reload();
  92. };
  93. const normalizeIndices = async() => {
  94. try {
  95. await apiv3Put('/search/indices', { operation: 'normalize' });
  96. }
  97. catch (e) {
  98. toastError(e);
  99. }
  100. await retrieveIndicesStatus();
  101. toastSuccess('Normalizing has succeeded');
  102. };
  103. const rebuildIndices = async() => {
  104. setIsRebuildingProcessing(true);
  105. try {
  106. await apiv3Put('/search/indices', { operation: 'rebuild' });
  107. toastSuccess('Rebuilding is requested');
  108. }
  109. catch (e) {
  110. toastError(e);
  111. }
  112. await retrieveIndicesStatus();
  113. };
  114. const isErrorOccuredOnSearchService = !isSearchServiceReachable;
  115. const isReconnectBtnEnabled = !isReconnectingProcessing && (!isInitialized || !isConnected || isErrorOccuredOnSearchService);
  116. return (
  117. <>
  118. <div data-testid="admin-full-text-search" className="row">
  119. <div className="col-md-12">
  120. <StatusTable
  121. isInitialized={isInitialized}
  122. isErrorOccuredOnSearchService={isErrorOccuredOnSearchService}
  123. isConnected={isConnected}
  124. isConfigured={isConfigured}
  125. isNormalized={isNormalized}
  126. indicesData={indicesData}
  127. aliasesData={aliasesData}
  128. />
  129. </div>
  130. </div>
  131. <hr />
  132. {/* Controls */}
  133. <div className="row">
  134. <label className="col-md-3 col-form-label text-left text-md-right">{ t('full_text_search_management.reconnect') }</label>
  135. <div className="col-md-6">
  136. <ReconnectControls
  137. isEnabled={isReconnectBtnEnabled}
  138. isProcessing={isReconnectingProcessing}
  139. onReconnectingRequested={reconnect}
  140. />
  141. </div>
  142. </div>
  143. <hr />
  144. <div className="row">
  145. <label className="col-md-3 col-form-label text-left text-md-right">{ t('full_text_search_management.normalize') }</label>
  146. <div className="col-md-6">
  147. <NormalizeIndicesControls
  148. isRebuildingProcessing={isRebuildingProcessing}
  149. isNormalized={isNormalized}
  150. onNormalizingRequested={normalizeIndices}
  151. />
  152. </div>
  153. </div>
  154. <hr />
  155. <div className="row">
  156. <label className="col-md-3 col-form-label text-left text-md-right">{ t('full_text_search_management.rebuild') }</label>
  157. <div className="col-md-6">
  158. <RebuildIndexControls
  159. isRebuildingProcessing={isRebuildingProcessing}
  160. isRebuildingCompleted={isRebuildingCompleted}
  161. isNormalized={isNormalized}
  162. onRebuildingRequested={rebuildIndices}
  163. />
  164. </div>
  165. </div>
  166. </>
  167. );
  168. };
  169. ElasticsearchManagement.propTypes = {
  170. };
  171. export default ElasticsearchManagement;