execute.ui.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. $(function()
  2. {
  3. let timer = null; // 轮询计时器
  4. let versionIndex = 0; // 当前正在处理的版本索引
  5. let shouldAbortAll = false; // 标记是否应该中止所有轮询和升级操作
  6. let currentPollVersion = null; // 标记当前应该处理轮询响应的版本
  7. function abortUpgrade()
  8. {
  9. shouldAbortAll = true;
  10. stopTimer();
  11. }
  12. /**
  13. * 停止计时器
  14. */
  15. function stopTimer()
  16. {
  17. if(timer)
  18. {
  19. clearTimeout(timer);
  20. timer = null;
  21. }
  22. }
  23. /**
  24. * 轮询进度:一直运行直到达到 100%
  25. */
  26. function fetchProgress(currentVersion)
  27. {
  28. return new Promise((resolve) =>
  29. {
  30. currentPollVersion = currentVersion;
  31. const poll = () =>
  32. {
  33. if(shouldAbortAll) return;
  34. if(currentPollVersion !== currentVersion) return;
  35. $.getJSON($.createLink('upgrade', 'ajaxGetExecutedChanges'))
  36. .done(function(response)
  37. {
  38. if(shouldAbortAll) return;
  39. if(currentPollVersion !== currentVersion) return;
  40. const executedKeys = response.executedKeys;
  41. $('#changesBox .change-item').not('.executed').each(function()
  42. {
  43. const $item = $(this);
  44. const key = $item.data('key');
  45. if(executedKeys.includes(key))
  46. {
  47. $item.addClass('executed');
  48. const $label = $item.find('.label');
  49. $label.text($label.data('text'));
  50. $label.removeClass('gray-pale text-gray-400').addClass('primary-pale text-primary');
  51. }
  52. });
  53. const $executedItems = $('#changesBox .change-item.executed');
  54. const $lastExecuted = $executedItems.last();
  55. if($lastExecuted.length)
  56. {
  57. const $nextItem = $lastExecuted.next('.change-item');
  58. const scrollItem = $nextItem.length ? $nextItem[0] : $lastExecuted[0];
  59. scrollItem.scrollIntoView({behavior: 'smooth', block: 'nearest'});
  60. }
  61. $('#executedCount').text($executedItems.length);
  62. const keyLength = executedKeys.length;
  63. if(keyLength && executedKeys[keyLength - 1] != keyLength - 1)
  64. {
  65. console.log(response);
  66. console.warn('Detected missing executed change keys. Aborting upgrade to prevent inconsistencies.');
  67. abortUpgrade();
  68. return;
  69. }
  70. if(response.allChangesExecuted)
  71. {
  72. stopTimer();
  73. /* 延迟 0.5 秒便于用户看清最后的进度变化。*/
  74. setTimeout(() =>
  75. {
  76. if(currentPollVersion === currentVersion) resolve();
  77. }, 500);
  78. return;
  79. }
  80. stopTimer();
  81. if(currentPollVersion === currentVersion) timer = setTimeout(poll, 500);
  82. })
  83. .fail(function()
  84. {
  85. console.warn('Failed to fetch upgrade progress. Retrying...');
  86. stopTimer();
  87. if(currentPollVersion === currentVersion) timer = setTimeout(poll, 500);
  88. });
  89. };
  90. poll(); // 立即开始轮询
  91. });
  92. }
  93. /**
  94. * 执行升级操作
  95. */
  96. async function runUpgrade()
  97. {
  98. const totalVersions = upgradeVersions.length;
  99. while(versionIndex < totalVersions)
  100. {
  101. currentPollVersion = null; // 清理上个版本的标记
  102. const currentVersion = upgradeVersions[versionIndex];
  103. const isLastVersion = versionIndex === totalVersions - 1;
  104. let upgradeResult = null;
  105. /* 发起请求执行升级操作 */
  106. const upgradePromise = new Promise((resolve, reject) =>
  107. {
  108. $.getJSON($.createLink('upgrade', 'ajaxExecute', 'fromVersion=' + fromVersion + '&toVersion=' + currentVersion))
  109. .done(resolve)
  110. .fail(reject);
  111. });
  112. /* 更新版本列表状态为正在升级 */
  113. const $versionItem = $('#versionsBox .version-item[data-version="' + currentVersion + '"]');
  114. $versionItem[0].scrollIntoView({behavior: 'smooth', block: 'nearest'});
  115. $versionItem.find('.icon-clock').replaceWith('<i class="icon icon-spinner-indicator text-gray-400 w-4 h-4"></i>');
  116. /* 启动轮询获取升级进度 */
  117. const pollPromise = fetchProgress(currentVersion);
  118. /* 等待升级请求完成 */
  119. try
  120. {
  121. upgradeResult = await upgradePromise;
  122. }
  123. catch(error)
  124. {
  125. /* 升级操作执行失败(网络或 HTTP 错误)*/
  126. abortUpgrade();
  127. console.error('Upgrade failed:', error);
  128. console.warn('An error occurred during the upgrade process. Please try again.');
  129. return;
  130. }
  131. /* 升级请求失败跳转到错误页面 */
  132. if(upgradeResult.result === 'fail')
  133. {
  134. abortUpgrade();
  135. const url = $.createLink('upgrade', 'execute', 'fromVersion=' + fromVersion);
  136. const data = new FormData();
  137. data.append('errors', upgradeResult.message);
  138. postAndLoadPage(url, data);
  139. return;
  140. }
  141. /* 即使升级请求完成也要等待轮询进度达到 100% */
  142. await pollPromise;
  143. /* 更新版本列表状态为升级完成 */
  144. $versionItem.find('.icon-spinner-indicator').replaceWith('<span class="bg-success rounded-full w-4 h-4"></span>');
  145. /* 更新版本升级进度条 */
  146. const executedVersions = versionIndex + 1;
  147. const progressPercent = Math.round(((executedVersions) / totalVersions) * 100);
  148. $('#versionsProgressText').text(executedVersions);
  149. $('#versionsProgressBar .progress-bar').css('width', progressPercent + '%');
  150. /* 如果是最后一个版本并且返回了跳转链接则跳转到新页面 */
  151. if(isLastVersion && upgradeResult.load)
  152. {
  153. abortUpgrade();
  154. $('#continueBtn').removeClass('disabled').attr('href', upgradeResult.load);
  155. return;
  156. }
  157. /* 局部刷新加载下一个版本的变更列表 */
  158. versionIndex++;
  159. if(versionIndex < totalVersions)
  160. {
  161. await new Promise((resolve) =>
  162. {
  163. loadCurrentPage(
  164. {
  165. selector: '#changesBlock',
  166. complete: () =>
  167. {
  168. const firstChange = $('#changesBox .change-item').first();
  169. if(firstChange.length)
  170. {
  171. firstChange[0].scrollIntoView({behavior: 'smooth', block: 'nearest'});
  172. }
  173. resolve();
  174. }
  175. });
  176. });
  177. }
  178. }
  179. stopTimer();
  180. }
  181. if(upgradeVersions.length > 0)
  182. {
  183. runUpgrade();
  184. }
  185. });
  186. window.showSQL = function(sql)
  187. {
  188. zui.Modal.alert({size: 'lg', title: 'SQL', content: {html: sql, className: 'leading-6'}});
  189. }