Просмотр исходного кода

GC-1316: Build ES indices from admin with async processing

Yuki Takei 7 лет назад
Родитель
Сommit
0e409a1cc2

+ 20 - 17
src/client/js/components/Admin/AdminRebuildSearch.jsx

@@ -33,25 +33,28 @@ export default class AdminRebuildSearch extends React.Component {
       return null;
     }
 
-    if (isCompleted) {
-      return (
-        <div className="progress">
-          <div className="progress-bar progress-bar-striped" style={{ width: '100%' }} />
-        </div>
-      );
-    }
+    const progressBarLabel = isCompleted ? 'Completed' : `Processing.. ${current}/${total} (${skip} skips)`;
+    const progressBarWidth = isCompleted ? '100%' : `${(current / total) * 100}%`;
+    const progressBarClassNames = isCompleted
+      ? 'progress-bar progress-bar-success'
+      : 'progress-bar progress-bar-striped progress-bar-animated active';
 
     return (
-      <div className="progress">
-        <div
-          className="progress-bar progress-bar-striped progress-bar-animated active"
-          role="progressbar"
-          aria-valuemin="0"
-          aria-valuenow={current}
-          aria-valuemax={total}
-          style={{ width: `${(current / total) * 100}%` }}
-        >
-          {current}/{total} ({skip} skips)
+      <div>
+        <h5>
+          {progressBarLabel}
+          <span className="pull-right">{progressBarWidth}</span>
+        </h5>
+        <div className="progress progress-sm">
+          <div
+            className={progressBarClassNames}
+            role="progressbar"
+            aria-valuemin="0"
+            aria-valuenow={current}
+            aria-valuemax={total}
+            style={{ width: progressBarWidth }}
+          >
+          </div>
         </div>
       </div>
     );

+ 1 - 4
src/client/styles/agile-admin/inverse/widgets.scss

@@ -808,7 +808,6 @@ border-radius:$radius;
 */
 
 /*Progressbars*/
-/*
 .progress {
 -webkit-box-shadow: none !important;
 background-color: $border;
@@ -894,10 +893,9 @@ animation-duration: 5s;
 animation-name: myanimation;
 transition: 5s all;
 }
-*/
+
 
 /* Progressbar Animated */
-/*
 @-webkit-keyframes myanimation {
 from {
   width:0;
@@ -908,7 +906,6 @@ from {
   width:0;
 }
 }
-*/
 
 /* Progressbar Vertical */
 /*

+ 43 - 46
src/server/routes/admin.js

@@ -434,52 +434,6 @@ module.exports = function(crowi, app) {
     return res.render('admin/search', {});
   };
 
-  actions.search.buildIndex = async function(req, res) {
-    const search = crowi.getSearcher();
-    if (!search) {
-      return res.redirect('/admin');
-    }
-
-    // first, delete index
-    try {
-      await search.deleteIndex();
-      debug('Index deleted.');
-    }
-    catch (err) {
-      debug('Delete index Error, but if it is initialize, its ok.', err);
-    }
-
-    // second, create index
-    try {
-      await search.buildIndex();
-      debug('Index created.');
-    }
-    catch (err) {
-      debug('Error', err);
-      req.flash('errorMessage', 'Error while building index.');
-      return res.redirect('/admin/search');
-    }
-
-    searchEvent.on('addPageProgress', (total, current, skip) => {
-      crowi.getIo().sockets.emit('admin:addPageProgress', { total, current, skip });
-    });
-    searchEvent.on('finishAddPage', (total, current, skip) => {
-      crowi.getIo().sockets.emit('admin:finishAddPage', { total, current, skip });
-    });
-    // add all page
-    search
-      .addAllPages()
-      .then(() => {
-        debug('Data is successfully indexed. ------------------ ✧✧');
-      })
-      .catch(err => {
-        debug('Error', err);
-      });
-
-    req.flash('successMessage', 'Now re-building index ... this takes a while.');
-    return res.redirect('/admin/search');
-  };
-
   actions.user = {};
   actions.user.index = async function(req, res) {
     const activeUsers = await User.countListByStatus(User.STATUS_ACTIVE);
@@ -1428,6 +1382,49 @@ module.exports = function(crowi, app) {
     }
   };
 
+
+  actions.api.searchBuildIndex = async function(req, res) {
+    const search = crowi.getSearcher();
+    if (!search) {
+      return res.json(ApiResponse.error('ElasticSearch Integration is not set up.'));
+    }
+
+    // first, delete index
+    try {
+      await search.deleteIndex();
+    }
+    catch (err) {
+      logger.warn('Delete index Error, but if it is initialize, its ok.', err);
+    }
+
+    // second, create index
+    try {
+      await search.buildIndex();
+    }
+    catch (err) {
+      logger.error('Error', err);
+      return res.json(ApiResponse.error(err));
+    }
+
+    searchEvent.on('addPageProgress', (total, current, skip) => {
+      crowi.getIo().sockets.emit('admin:addPageProgress', { total, current, skip });
+    });
+    searchEvent.on('finishAddPage', (total, current, skip) => {
+      crowi.getIo().sockets.emit('admin:finishAddPage', { total, current, skip });
+    });
+    // add all page
+    search
+      .addAllPages()
+      .then(() => {
+        debug('Data is successfully indexed. ------------------ ✧✧');
+      })
+      .catch(err => {
+        logger.error('Error', err);
+      });
+
+    return res.json(ApiResponse.success());
+  };
+
   /**
    * save settings, update config cache, and response json
    *

+ 1 - 1
src/server/routes/index.js

@@ -102,7 +102,7 @@ module.exports = function(crowi, app) {
 
   // search admin
   app.get('/admin/search'              , loginRequired(crowi, app) , middleware.adminRequired() , admin.search.index);
-  app.post('/admin/search/build'       , loginRequired(crowi, app) , middleware.adminRequired() , csrf, admin.search.buildIndex);
+  app.post('/_api/admin/search/build'  , loginRequired(crowi, app) , middleware.adminRequired() , csrf, admin.api.searchBuildIndex);
 
   // notification admin
   app.get('/admin/notification'              , loginRequired(crowi, app) , middleware.adminRequired() , admin.notification.index);

+ 67 - 1
src/server/views/admin/search.html

@@ -45,7 +45,7 @@
         </div>
         {% endif %}
 
-        <form action="/admin/search/build" method="post" class="form-horizontal" id="appSettingForm" role="form">
+        <form action="/_api/admin/search/build" method="post" class="form-horizontal" id="buildIndexForm" role="form">
           <fieldset>
             <legend>Index Build</legend>
             <div class="form-group">
@@ -71,6 +71,72 @@
   </div>
 
 </div>
+
+<script>
+  /**
+   * show flash message
+   */
+  function showMessage(formId, msg, status) {
+    $('#' + formId + ' .alert').remove();
+
+    if (!status) {
+      status = 'success';
+    }
+    var $message = $('<p class="alert"></p>');
+    $message.addClass('alert-' + status);
+    $message.html(msg.replace('\n', '<br>'));
+    $message.insertAfter('#' + formId + ' legend');
+
+    if (status == 'success') {
+      setTimeout(function()
+      {
+        $message.fadeOut({
+          complete: function() {
+            $message.remove();
+          }
+        });
+      }, 5000);
+    }
+  }
+
+  /**
+   * Post form data and process UI
+   */
+  function postData(form, button, action) {
+    var id = form.attr('id');
+    button.attr('disabled', 'disabled');
+    var jqxhr = $.post(action, form.serialize(), function(res)
+      {
+        if (!res.ok) {
+          showMessage(id, `Error: ${res.message}`, 'danger');
+        }
+        else {
+          showMessage(id, 'Building request is successfully posted.');
+        }
+      })
+      .fail(function() {
+        showMessage(id, "エラーが発生しました", 'danger');
+      })
+      .always(function() {
+        button.prop('disabled', false);
+      });
+    return false;
+  }
+
+  /**
+   * Handle submit button esa
+   */
+  $('#buildIndexForm').each(function() {
+    var $form = $(this);
+    var $button = $("#buildIndexForm" + $(this).attr('name') + " button[type='submit']");
+    var $action = $form.attr('action');
+    var $success_msg = $button.attr('data-success-message');
+    var $error_msg = $button.attr('data-error-message');
+    $form.submit(function() { return postData($form, $button, $action, $success_msg, $error_msg) });
+  });
+
+</script>
+
 {% endblock content_main %}
 
 {% block content_footer %}