app_manage.html 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. {% extends 'base.html' %}
  2. {% block title %}{{ title }}{% endblock %}
  3. {% block content %}
  4. <div class="container mt-5">
  5. <h1 class="mb-4">应用管理</h1>
  6. <!-- 重启应用服务 -->
  7. <div class="card mb-4">
  8. <div class="card-header">
  9. <h2>重启应用服务</h2>
  10. </div>
  11. <div class="card-body">
  12. <p>点击下方按钮重启应用服务。注意:重启过程中服务会暂时不可用。</p>
  13. <button id="restart-btn" class="btn btn-danger" style="width:100px">重启应用</button>
  14. <div id="restart-result" class="mt-3"></div>
  15. </div>
  16. </div>
  17. <!-- 更新应用服务 -->
  18. <div class="card mb-4">
  19. <div class="card-header">
  20. <h2>更新应用服务</h2>
  21. </div>
  22. <div class="card-body">
  23. <form id="update-form" enctype="multipart/form-data">
  24. <div class="form-group">
  25. <label for="app-file">选择应用服务文件</label>
  26. <input type="file" class="form-control-file" id="app-file" name="app_file" required>
  27. <small class="form-text text-muted">请上传只包含Python文件(.py)、HTML文件(.html)或压缩包(.zip)的文件</small>
  28. </div>
  29. <button type="submit" class="btn btn-primary">上传更新</button>
  30. </form>
  31. <div id="update-result" class="mt-3"></div>
  32. </div>
  33. </div>
  34. <!-- 应用日志查看 -->
  35. <div class="card">
  36. <div class="card-header">
  37. <h2>应用日志查看</h2>
  38. </div>
  39. <div class="card-body">
  40. <div class="form-group log-controls">
  41. <button id="refresh-logs" class="btn btn-primary" style="width: 120px;">刷新日志</button>
  42. <div class="form-check form-check-inline">
  43. <input type="checkbox" id="auto-refresh" class="form-check-input">
  44. <label for="auto-refresh" class="form-check-label">自动刷新</label>
  45. </div>
  46. <div class="form-group form-inline">
  47. <label for="log-lines" class="mr-2">显示行数</label>
  48. <select id="log-lines" class="form-control" style="width:100px;">
  49. <option value="10">10行</option>
  50. <option value="50" selected>50行</option>
  51. <option value="100">100行</option>
  52. <option value="all">全部</option>
  53. </select>
  54. </div>
  55. </div>
  56. <div class="mt-3">
  57. <pre id="app-logs" class="bg-dark text-white p-3 overflow-auto log-container">加载中...</pre>
  58. </div>
  59. </div>
  60. </div>
  61. </div>
  62. <style>
  63. .log-controls {
  64. display: flex;
  65. align-items: center;
  66. gap: 15px;
  67. flex-wrap: wrap;
  68. }
  69. .form-check {
  70. display: flex;
  71. align-items: center;
  72. }
  73. .form-check-input {
  74. height: 20px;
  75. width: 20px;
  76. margin-right: 5px;
  77. }
  78. .form-check-label {
  79. margin: 0;
  80. white-space: nowrap;
  81. }
  82. .form-inline {
  83. display: flex;
  84. align-items: center;
  85. }
  86. </style>
  87. {% endblock %}
  88. {% block scripts %}
  89. <script>
  90. // 重启应用服务
  91. document.getElementById('restart-btn').addEventListener('click', function() {
  92. if (confirm('确定要重启应用服务吗?')) {
  93. const resultDiv = document.getElementById('restart-result');
  94. resultDiv.innerHTML = '<div class="alert alert-info">正在发送重启请求...</div>';
  95. fetch('/api/restart_app', {
  96. method: 'POST'
  97. })
  98. .then(response => {
  99. if (!response.ok) {
  100. throw new Error('网络响应错误');
  101. }
  102. return response.json();
  103. })
  104. .then(data => {
  105. resultDiv.innerHTML = '<div class="alert alert-info">' + data.message + '</div>';
  106. // 开始轮询检查服务器是否重启完成
  107. let pollCount = 0;
  108. const maxPollCount = 30; // 最多检查30次
  109. const pollInterval = 2000; // 每2秒检查一次
  110. const pollServer = () => {
  111. if (pollCount >= maxPollCount) {
  112. resultDiv.innerHTML = '<div class="alert alert-warning">重启超时,请手动检查服务状态。</div>';
  113. return;
  114. }
  115. pollCount++;
  116. // 尝试连接服务器
  117. fetch('/api/ping', {
  118. method: 'GET',
  119. timeout: 1000
  120. })
  121. .then(response => {
  122. if (response.ok) {
  123. return response.json();
  124. }
  125. throw new Error('服务器未就绪');
  126. })
  127. .then(pingData => {
  128. if (pingData.status === 'ok') {
  129. resultDiv.innerHTML = '<div class="alert alert-success">应用重启成功!</div>';
  130. // 可选:刷新页面
  131. /*
  132. setTimeout(() => {
  133. location.reload();
  134. }, 2000);
  135. */
  136. return response.json();
  137. } else {
  138. setTimeout(pollServer, pollInterval);
  139. }
  140. })
  141. .catch(error => {
  142. setTimeout(pollServer, pollInterval);
  143. });
  144. };
  145. // 启动轮询
  146. setTimeout(pollServer, 3000); // 延迟3秒后开始轮询,给服务器重启时间
  147. })
  148. .catch(error => {
  149. resultDiv.innerHTML = '<div class="alert alert-danger">重启请求发送失败: ' + error.message + '</div>';
  150. });
  151. }
  152. });
  153. // 更新应用服务
  154. document.getElementById('update-form').addEventListener('submit', function(e) {
  155. e.preventDefault();
  156. const formData = new FormData(this);
  157. fetch('/api/update_app', {
  158. method: 'POST',
  159. body: formData
  160. })
  161. .then(response => response.json())
  162. .then(data => {
  163. const resultDiv = document.getElementById('update-result');
  164. if (data.success) {
  165. resultDiv.innerHTML = '<div class="alert alert-success">' + data.message + '</div>';
  166. } else {
  167. resultDiv.innerHTML = '<div class="alert alert-danger">' + data.message + '</div>';
  168. }
  169. })
  170. .catch(error => {
  171. document.getElementById('update-result').innerHTML = '<div class="alert alert-danger">更新失败: ' + error + '</div>';
  172. });
  173. });
  174. // 查看应用日志
  175. function loadLogs() {
  176. const lines = document.getElementById('log-lines').value;
  177. fetch(`/api/get_logs?lines=${lines}`)
  178. .then(response => response.text())
  179. .then(data => {
  180. document.getElementById('app-logs').textContent = data;
  181. })
  182. .catch(error => {
  183. document.getElementById('app-logs').textContent = '加载日志失败: ' + error;
  184. });
  185. }
  186. document.getElementById('refresh-logs').addEventListener('click', loadLogs);
  187. // 页面加载时加载日志
  188. window.addEventListener('load', loadLogs);
  189. // 自动刷新相关代码
  190. let refreshInterval;
  191. const autoRefreshCheckbox = document.getElementById('auto-refresh');
  192. // 检查复选框状态并设置定时器
  193. function updateAutoRefresh() {
  194. if (autoRefreshCheckbox.checked) {
  195. if (!refreshInterval) {
  196. refreshInterval = setInterval(loadLogs, 1000);
  197. }
  198. } else {
  199. if (refreshInterval) {
  200. clearInterval(refreshInterval);
  201. refreshInterval = null;
  202. }
  203. }
  204. }
  205. // 监听复选框状态变化
  206. autoRefreshCheckbox.addEventListener('change', updateAutoRefresh);
  207. // 初始检查状态
  208. updateAutoRefresh();
  209. </script>
  210. {% endblock %}