raid.html 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <div class="ts-content">
  2. <div class="ts-container is-padded">
  3. <div class="ts-grid mobile:is-stacked">
  4. <div class="column is-6-wide">
  5. <div id="raid_array_list" class="ts-menu is-start-icon is-separated">
  6. <a class="item">
  7. <span class="ts-icon is-user-icon"></span> 使用者
  8. </a>
  9. <a class="item is-active">
  10. <span class="ts-icon is-house-icon"></span> 首頁
  11. </a>
  12. <a class="item">
  13. <span class="ts-icon is-newspaper-icon"></span> 新聞
  14. </a>
  15. </div>
  16. <div class="ts-divider has-top-spaced-small"></div>
  17. <div class="ts-content is-center-aligned">
  18. <button class="ts-button is-start-icon is-positive is-circular">
  19. <span class="ts-icon is-circle-plus-icon" style="color: var(--ts-positive-500);"></span>
  20. <span i18n>Create RAID
  21. // 新增陣列
  22. </span>
  23. </button>
  24. <button class="ts-button is-start-icon is-positive is-circular">
  25. <span class="ts-icon is-rotate-icon" style="color: var(--ts-primary-500);"></span>
  26. <span i18n>Assemble
  27. // 重組陣列
  28. </span>
  29. </button>
  30. </div>
  31. </div>
  32. <div class="column is-fluid">
  33. <div id="raid_details">
  34. </div>
  35. </div>
  36. </div>
  37. </div>
  38. </div>
  39. <script>
  40. function initRAIDDeviceList(){
  41. // Utility function to convert bytes to human-readable format
  42. function bytesToHumanReadable(bytes) {
  43. const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
  44. if (bytes === 0) return '0 B';
  45. const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
  46. return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
  47. }
  48. $.ajax({
  49. url: './api/raid/list',
  50. type: 'GET',
  51. dataType: 'json',
  52. success: function(data) {
  53. $('#raid_array_list').html("");
  54. if (data.error != undefined){
  55. // Handle error response
  56. console.error('Error fetching RAID devices:', data.error);
  57. $('#raid_array_list').append('<div class="ts-text is-error">Error: ' + data.error + '</div>');
  58. }else{
  59. let isSyncing = false;
  60. data.forEach((raid, index) => {
  61. // Add a new menu item for each RAID array
  62. let icon = '';
  63. if (raid.State.includes('clean') && !raid.State.includes('sync')) {
  64. icon = '<span class="ts-icon is-check-icon" style="color: var(--ts-positive-500);"></span>';
  65. } else if (raid.State.includes('sync')) {
  66. isSyncing = true;
  67. icon = '<span class="ts-icon is-spinning is-rotate-icon" style="color: var(--ts-positive-500);"></span>';
  68. } else if (raid.State.includes('degraded')) {
  69. icon = '<span class="ts-icon is-triangle-exclamation-icon" style="color: var(--ts-warning-600);"></span>';
  70. } else if (raid.State.includes('fail')) {
  71. icon = '<span class="ts-icon is-circle-xmark-icon" style="color: var(--ts-negative-500);"></span>';
  72. } else {
  73. icon = '<span class="ts-icon is-question-icon" style="color: var(--ts-gray-500);"></span>';
  74. }
  75. const menuItem = `
  76. <a class="raid-array item ${index==0?'is-active':''}" id="raid_menu_${index}" onclick="showRAIDDetails(${index})">
  77. ${icon}
  78. <div class="ts-content is-dense">
  79. <div>
  80. <span class="ts-text is-heavy">${raid.DevicePath}</span> | ${raid.RaidLevel.toUpperCase()}
  81. </div>
  82. <div class="ts-text is-tiny has-top-spaced-small">
  83. ${raid.Name}
  84. </div>
  85. </div>
  86. </a>
  87. `;
  88. $('#raid_array_list').append(menuItem);
  89. // Add a hidden div for each RAID array's details
  90. const raidDetails = `
  91. <div id="raid_details_${index}" class="raid-details" style="display: none ;">
  92. <div class="ts-box">
  93. <div class="ts-content is-padded">
  94. <div class="ts-header is-start-icon">
  95. ${icon}
  96. ${raid.DevicePath} | ${raid.RaidLevel.toUpperCase()}
  97. </div>
  98. <div class="ts-text is-description">
  99. ${raid.UUID}<br>
  100. ${raid.Name}
  101. </div>
  102. <div class="ts-text">
  103. <span i18n> State
  104. // 狀態
  105. </span>: ${raid.State}<br>
  106. <div class="sync-progress has-top-spaced-small ${isSyncing?'need-update-raid-sync-progress':''}" devname="${raid.DevicePath}" style="display: ${isSyncing?"auto":"none"};">
  107. <div class="ts-progress is-processing">
  108. <div class="bar" style="--value: 0">
  109. <div class="text">0%</div>
  110. </div>
  111. </div>
  112. <div class="ts-text is-description has-top-spaced-small">
  113. <span i18n> Synchronized
  114. // 已處理</span>
  115. <span class="processed_blocks"></span>
  116. <span>/</span>
  117. <span class="total_blocks"></span>
  118. <span i18n> blocks
  119. // 個區塊
  120. </span><br>
  121. <!-- <span i18n> Speed
  122. // 速度
  123. </span>: <span class="speed"></span><br>
  124. <span i18n> Expected Time
  125. // 預估時間
  126. </span>: <span class="expected_time"></span>
  127. -->
  128. </div>
  129. </div>
  130. <span i18n> Array Size
  131. // 陣列大小
  132. </span>: ${bytesToHumanReadable(raid.ArraySize * 1024)}<br>
  133. <span i18n> Created
  134. // 建立時間
  135. </span>: <span>${new Date(raid.CreationTime).toLocaleString()}</span><br>
  136. </div>
  137. <table class="ts-table is-single-line has-top-spaced-large">
  138. <thead>
  139. <tr>
  140. <th i18n>Disk Status
  141. // 磁碟狀態
  142. </th>
  143. <th i18n>Counts
  144. // 數量
  145. </th>
  146. </tr>
  147. </thead>
  148. <tbody>
  149. <tr>
  150. <td i18n> Active Devices
  151. // 啟用的磁碟
  152. </td>
  153. <td>${raid.ActiveDevices}</td>
  154. </tr>
  155. <tr>
  156. <td i18n> Working Devices
  157. // 工作中的磁碟
  158. </td>
  159. <td>${raid.WorkingDevices}</td>
  160. </tr>
  161. <tr>
  162. <td i18n> Failed Devices
  163. // 故障的磁碟
  164. </td>
  165. <td>${raid.FailedDevices}</td>
  166. </tr>
  167. <tr>
  168. <td i18n> Spare Devices
  169. // 備用磁碟
  170. </td>
  171. <td>${raid.SpareDevices}</td>
  172. </tr>
  173. </tbody>
  174. </table>
  175. </div>
  176. </div>
  177. <div class="ts-box is-padded has-top-spaced-small">
  178. <div class="ts-content">
  179. </div>
  180. </div>
  181. </div>
  182. `;
  183. $('#raid_details').append(raidDetails);
  184. });
  185. }
  186. relocale(); // Recalculate layout
  187. syncProgressTicker(); // Start the sync progress ticker
  188. },
  189. error: function(xhr, status, error) {
  190. console.error('Error fetching RAID devices:', error);
  191. }
  192. });
  193. }
  194. initRAIDDeviceList();
  195. //Create a ticker to check for RAID sync progress
  196. function syncProgressTicker(){
  197. let syncProgressTracker = $(".need-update-raid-sync-progress");
  198. if (syncProgressTracker.length > 0){
  199. syncProgressTracker.each(function(){
  200. let devname = $(this).attr("devname");
  201. $.ajax({
  202. url: './api/raid/sync?dev=' + devname,
  203. type: 'GET',
  204. dataType: 'json',
  205. data: { devname: devname },
  206. success: function(data) {
  207. if (data.error != undefined){
  208. // Handle error response
  209. console.error('Error fetching RAID sync progress:', data.error);
  210. //Refresh the RAID list
  211. initRAIDDeviceList();
  212. }else{
  213. let progress = parseFloat(data.ResyncPercent);
  214. let total_blocks = parseInt(data.TotalBlocks);
  215. let processed_blocks = parseInt(data.CompletedBlocks);
  216. let expected_time = data.ExpectedTime;
  217. let speed = data.Speed;
  218. $(`.sync-progress[devname="${devname}"] .bar`).css('--value', progress);
  219. $(`.sync-progress[devname="${devname}"] .bar .text`).text(`${progress.toFixed(1)}%`);
  220. $(`.sync-progress[devname="${devname}"] .processed_blocks`).text(processed_blocks);
  221. $(`.sync-progress[devname="${devname}"] .total_blocks`).text(total_blocks);
  222. //$(`.sync-progress[devname="${devname}"] .ts-text.is-description .speed`).text(speed);
  223. //$(`.sync-progress[devname="${devname}"] .ts-text.is-description .expected_time`).text(expected_time);
  224. }
  225. },
  226. error: function(xhr, status, error) {
  227. console.error('Error fetching RAID sync progress:', error);
  228. }
  229. });
  230. });
  231. }
  232. }
  233. setInterval(syncProgressTicker, 5000); // Check every 5 seconds
  234. function showRAIDDetails(index) {
  235. $('.raid-details').hide(); // Hide all RAID details
  236. $(`#raid_details_${index}`).show(); // Show the selected RAID details
  237. $('.raid-array.is-active').removeClass('is-active'); // Remove active class from all menu items
  238. $(`#raid_menu_${index}`).addClass('is-active'); // Add active class to the selected menu item
  239. relocale(); // Recalculate layout
  240. }
  241. </script>