index.html 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1" />
  6. <title>NGS · 控制台</title>
  7. <link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css" />
  8. <link rel="stylesheet" href="/assets/bootstrap-icons/bootstrap-icons.css" />
  9. <link rel="stylesheet" href="/assets/xterm/xterm.css" />
  10. <link rel="stylesheet" href="/assets/app.css" />
  11. <script src="/assets/chartjs/chart.umd.min.js"></script>
  12. <script src="/assets/xterm/xterm.js"></script>
  13. <script src="/assets/xterm/xterm-addon-fit.js"></script>
  14. </head>
  15. <body>
  16. <div class="app d-flex">
  17. <aside class="rail flex-shrink-0">
  18. <div class="brand d-flex align-items-center gap-2 px-3 py-2">
  19. <div class="brand-mark" id="brand-mark">N</div>
  20. <div class="brand-text flex-grow-1 min-w-0">
  21. <div class="fw-bold text-white lh-1 text-truncate" id="brand-name">NGS</div>
  22. <div class="small text-white-50">管理面板</div>
  23. </div>
  24. <button type="button" class="btn btn-sm btn-outline-light position-relative" id="task-badge" title="任务队列">
  25. <i class="bi bi-list-task"></i>
  26. <span class="badge text-bg-warning position-absolute top-0 start-100 translate-middle rounded-pill" id="task-badge-count">0</span>
  27. </button>
  28. </div>
  29. <nav class="nav flex-column px-2 gap-1 flex-grow-1">
  30. <button type="button" class="nav-link text-start nav-item is-active" data-view="console"><i class="bi bi-speedometer2 me-2"></i>控制台</button>
  31. <button type="button" class="nav-link text-start nav-item" data-view="sites"><i class="bi bi-globe2 me-2"></i>网站</button>
  32. <button type="button" class="nav-link text-start nav-item" data-view="files"><i class="bi bi-folder2-open me-2"></i>文件</button>
  33. <div class="nav-group" data-nav-group="database">
  34. <button type="button" class="nav-link text-start nav-item nav-group-toggle" data-nav-toggle="database" aria-expanded="false">
  35. <i class="bi bi-database me-2"></i><span>数据库</span>
  36. <i class="bi bi-chevron-down nav-group-caret ms-auto"></i>
  37. </button>
  38. <div class="nav-sub">
  39. <button type="button" class="nav-link text-start nav-item nav-sub-item" data-view="mysql">MySQL</button>
  40. <button type="button" class="nav-link text-start nav-item nav-sub-item" data-view="redis">Redis</button>
  41. </div>
  42. </div>
  43. <button type="button" class="nav-link text-start nav-item" data-view="processes"><i class="bi bi-cpu me-2"></i>进程</button>
  44. <button type="button" class="nav-link text-start nav-item" data-view="shell"><i class="bi bi-terminal me-2"></i>终端</button>
  45. <button type="button" class="nav-link text-start nav-item" data-view="software"><i class="bi bi-box-seam me-2"></i>软件</button>
  46. <button type="button" class="nav-link text-start nav-item" data-view="plans"><i class="bi bi-calendar2-check me-2"></i>计划</button>
  47. <button type="button" class="nav-link text-start nav-item" data-view="settings"><i class="bi bi-gear me-2"></i>设置</button>
  48. </nav>
  49. <div class="rail-foot px-2 pb-2">
  50. <div class="self-update px-1 pb-2" id="self-update-box">
  51. <div class="small text-white-50 text-truncate" id="self-version" title="当前版本">版本 —</div>
  52. <div class="d-flex gap-1 mt-1">
  53. <button type="button" class="btn btn-sm btn-outline-light flex-grow-1" id="btn-self-check">检查更新</button>
  54. <button type="button" class="btn btn-sm btn-warning d-none" id="btn-self-update">更新</button>
  55. </div>
  56. </div>
  57. <button type="button" class="nav-link text-start nav-item nav-logout w-100" id="btn-logout">
  58. <i class="bi bi-box-arrow-right me-2"></i>退出
  59. </button>
  60. <div class="live small d-flex align-items-center gap-2 px-2 pt-1" id="live-dot">
  61. <span class="pulse"></span>
  62. <span id="live-label">检测中</span>
  63. </div>
  64. </div>
  65. </aside>
  66. <main class="main flex-grow-1">
  67. <header class="top d-flex justify-content-between align-items-center mb-3">
  68. <div>
  69. <div class="text-secondary small" id="page-eyebrow">Console</div>
  70. <h1 class="h4 mb-0" id="page-title">控制台</h1>
  71. </div>
  72. <div class="text-secondary small" id="top-meta"></div>
  73. </header>
  74. <section class="view is-active" id="view-console" data-view-panel="console">
  75. <div class="card mb-3">
  76. <div class="card-header py-2 d-flex justify-content-between align-items-center gap-2">
  77. <strong>实时资源</strong>
  78. <div class="d-flex align-items-center gap-2">
  79. <span class="small text-secondary" id="overview-sites">—</span>
  80. <select class="form-select form-select-sm metrics-range" id="metrics-range" title="时间范围">
  81. <option value="1m">1分</option>
  82. <option value="10m" selected>10分</option>
  83. <option value="30m">30分</option>
  84. <option value="1h">1小时</option>
  85. <option value="6h">6小时</option>
  86. <option value="1d">1天</option>
  87. <option value="7d">7天</option>
  88. <option value="30d">30天</option>
  89. </select>
  90. </div>
  91. </div>
  92. <div class="card-body py-3 d-flex flex-column min-h-0">
  93. <div class="row g-3 monitor-grid flex-grow-1">
  94. <div class="col-md-6">
  95. <div class="monitor-card">
  96. <div class="d-flex justify-content-between align-items-center small mb-2">
  97. <span class="fw-semibold">CPU</span>
  98. <span class="text-secondary" id="cpu-badge">—</span>
  99. </div>
  100. <div class="monitor-chart"><canvas id="chart-cpu"></canvas></div>
  101. </div>
  102. </div>
  103. <div class="col-md-6">
  104. <div class="monitor-card">
  105. <div class="d-flex justify-content-between align-items-center small mb-2">
  106. <span class="fw-semibold">内存</span>
  107. <span class="text-secondary" id="mem-badge">—</span>
  108. </div>
  109. <div class="monitor-chart"><canvas id="chart-mem"></canvas></div>
  110. </div>
  111. </div>
  112. <div class="col-md-6">
  113. <div class="monitor-card">
  114. <div class="d-flex justify-content-between align-items-center small mb-2">
  115. <span class="fw-semibold">网络</span>
  116. <span class="text-secondary text-end monitor-badge-stack" id="net-badge">—</span>
  117. </div>
  118. <div class="monitor-chart"><canvas id="chart-net"></canvas></div>
  119. </div>
  120. </div>
  121. <div class="col-md-6">
  122. <div class="monitor-card">
  123. <div class="d-flex justify-content-between align-items-center small mb-2">
  124. <span class="fw-semibold">磁盘</span>
  125. <span class="text-secondary" id="disk-badge">—</span>
  126. </div>
  127. <div class="monitor-chart"><canvas id="chart-disk"></canvas></div>
  128. <div class="resource-list" id="disk-list"></div>
  129. </div>
  130. </div>
  131. <div class="col-md-6" id="gpu-wrap" hidden>
  132. <div class="monitor-card">
  133. <div class="d-flex justify-content-between align-items-center small mb-2">
  134. <span class="fw-semibold">GPU</span>
  135. <span class="text-secondary" id="gpu-name-badge">—</span>
  136. </div>
  137. <div class="gpu-split">
  138. <div class="gpu-pane">
  139. <div class="d-flex justify-content-between align-items-center small mb-1">
  140. <span class="fw-semibold">占用</span>
  141. <span class="text-secondary" id="gpu-badge">—</span>
  142. </div>
  143. <div class="monitor-chart"><canvas id="chart-gpu"></canvas></div>
  144. </div>
  145. <div class="gpu-pane">
  146. <div class="d-flex justify-content-between align-items-center small mb-1">
  147. <span class="fw-semibold">显存</span>
  148. <span class="text-secondary" id="gpu-mem-badge">—</span>
  149. </div>
  150. <div class="monitor-chart"><canvas id="chart-gpu-mem"></canvas></div>
  151. </div>
  152. </div>
  153. </div>
  154. </div>
  155. </div>
  156. </div>
  157. </div>
  158. </section>
  159. <section class="view" id="view-sites" data-view-panel="sites">
  160. <div class="card">
  161. <div class="card-header py-2 d-flex flex-wrap justify-content-between align-items-center gap-2">
  162. <div>
  163. <strong>网站</strong>
  164. <span class="small text-secondary ms-2">创建 / 启停 / 配置 / 删除</span>
  165. </div>
  166. <button type="button" class="btn btn-sm btn-primary" id="btn-site-create">新建网站</button>
  167. </div>
  168. <div class="px-3 pt-2" id="sites-group-bar"></div>
  169. <div class="table-responsive">
  170. <table class="table table-sm table-hover align-middle mb-0" id="sites-table">
  171. <thead>
  172. <tr>
  173. <th style="width:4.5rem">排序</th>
  174. <th>名称</th>
  175. <th>分组</th>
  176. <th>类型</th>
  177. <th>域名</th>
  178. <th>目录</th>
  179. <th>证书到期</th>
  180. <th>日志</th>
  181. <th>状态</th>
  182. <th class="text-end">操作</th>
  183. </tr>
  184. </thead>
  185. <tbody></tbody>
  186. </table>
  187. </div>
  188. </div>
  189. </section>
  190. <section class="view" id="view-files" data-view-panel="files">
  191. <div class="card">
  192. <div class="card-header py-2">
  193. <div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
  194. <div>
  195. <strong>文件</strong>
  196. </div>
  197. <div class="d-flex flex-wrap align-items-center gap-2">
  198. <div class="btn-group btn-group-sm" id="files-bulk-actions" hidden>
  199. <span class="btn btn-outline-secondary disabled" id="files-selected-count">已选 0</span>
  200. <button type="button" class="btn btn-outline-danger" id="btn-files-delete-selected">删除选中</button>
  201. <button type="button" class="btn btn-outline-secondary" id="btn-files-clear-selection">取消选择</button>
  202. </div>
  203. <div class="btn-group btn-group-sm">
  204. <button type="button" class="btn btn-outline-secondary" id="btn-files-up">上级</button>
  205. <button type="button" class="btn btn-outline-secondary" id="btn-files-mkdir">新建目录</button>
  206. <button type="button" class="btn btn-outline-secondary" id="btn-files-newfile">新建文件</button>
  207. <button type="button" class="btn btn-primary" id="btn-files-upload">上传</button>
  208. </div>
  209. </div>
  210. </div>
  211. <div class="crumbs small mt-2" id="files-crumbs"></div>
  212. </div>
  213. <div class="table-responsive">
  214. <table class="table table-sm table-hover align-middle mb-0" id="files-table">
  215. <thead>
  216. <tr>
  217. <th class="files-check-col">
  218. <input class="form-check-input" type="checkbox" id="files-select-all" title="全选" aria-label="全选" />
  219. </th>
  220. <th>名称</th>
  221. <th>类型</th>
  222. <th>大小</th>
  223. <th>修改时间</th>
  224. <th class="text-end">操作</th>
  225. </tr>
  226. </thead>
  227. <tbody></tbody>
  228. </table>
  229. </div>
  230. </div>
  231. </section>
  232. <section class="view" id="view-mysql" data-view-panel="mysql">
  233. <div class="card">
  234. <div class="card-header py-2 d-flex justify-content-between align-items-center">
  235. <div>
  236. <strong>MySQL</strong>
  237. <span class="small text-secondary ms-2">库 / 账号 / 权限</span>
  238. </div>
  239. <div class="btn-group btn-group-sm">
  240. <button type="button" class="btn btn-outline-secondary" id="btn-mysql-password">改 root 密码</button>
  241. <button type="button" class="btn btn-primary" id="btn-db-create">新建数据库</button>
  242. </div>
  243. </div>
  244. <div class="table-responsive">
  245. <table class="table table-sm table-hover align-middle mb-0" id="db-table">
  246. <thead>
  247. <tr>
  248. <th>库名</th>
  249. <th>用户</th>
  250. <th>密码</th>
  251. <th>权限</th>
  252. <th>Host</th>
  253. <th class="text-end">操作</th>
  254. </tr>
  255. </thead>
  256. <tbody></tbody>
  257. </table>
  258. </div>
  259. </div>
  260. <div class="card mt-3">
  261. <div class="card-header py-2 d-flex justify-content-between align-items-center flex-wrap gap-2">
  262. <div>
  263. <strong>执行 SQL</strong>
  264. <span class="small text-secondary ms-2">以 root 运行,最多返回 500 行</span>
  265. </div>
  266. <div class="d-flex align-items-center gap-2 flex-wrap">
  267. <select class="form-select form-select-sm" id="sql-db" style="width:auto;min-width:10rem">
  268. <option value="">(不选库)</option>
  269. </select>
  270. <button type="button" class="btn btn-sm btn-primary" id="btn-sql-exec">执行</button>
  271. </div>
  272. </div>
  273. <div class="card-body py-2">
  274. <textarea class="form-control sql-editor" id="sql-input" rows="6" spellcheck="false"
  275. placeholder="SHOW DATABASES;&#10;SELECT * FROM table_name LIMIT 100;"></textarea>
  276. <div class="form-text">Ctrl+Enter 执行。DROP / DELETE 等语句请谨慎使用。</div>
  277. <div id="sql-meta" class="small text-secondary mt-2"></div>
  278. <div class="table-responsive mt-2 sql-result-wrap">
  279. <table class="table table-sm table-hover align-middle mb-0" id="sql-result">
  280. <thead></thead>
  281. <tbody></tbody>
  282. </table>
  283. </div>
  284. </div>
  285. </div>
  286. </section>
  287. <section class="view" id="view-redis" data-view-panel="redis">
  288. <div class="card">
  289. <div class="card-header py-2 d-flex justify-content-between align-items-center flex-wrap gap-2">
  290. <div>
  291. <strong>Redis</strong>
  292. <span class="small text-secondary ms-2" id="redis-status-summary">状态</span>
  293. </div>
  294. <div class="btn-group btn-group-sm" id="redis-actions"></div>
  295. </div>
  296. <div class="card-body py-3">
  297. <div class="row g-3" id="redis-info-grid">
  298. <div class="col-12 text-secondary text-center py-3">加载中…</div>
  299. </div>
  300. </div>
  301. </div>
  302. <div class="card mt-3">
  303. <div class="card-header py-2 d-flex justify-content-between align-items-center flex-wrap gap-2">
  304. <div>
  305. <strong>执行命令</strong>
  306. <span class="small text-secondary ms-2">经 redis-cli 执行,最多返回 64KB</span>
  307. </div>
  308. <button type="button" class="btn btn-sm btn-primary" id="btn-redis-exec">执行</button>
  309. </div>
  310. <div class="card-body py-2">
  311. <textarea class="form-control sql-editor" id="redis-cmd-input" rows="4" spellcheck="false"
  312. placeholder="INFO&#10;GET key&#10;SET key value&#10;KEYS *"></textarea>
  313. <div class="form-text">Ctrl+Enter 执行。FLUSHALL / SHUTDOWN 等请谨慎使用。</div>
  314. <div id="redis-cmd-meta" class="small text-secondary mt-2"></div>
  315. <pre class="redis-cmd-output mt-2 mb-0" id="redis-cmd-output" hidden></pre>
  316. </div>
  317. </div>
  318. </section>
  319. <section class="view" id="view-processes" data-view-panel="processes">
  320. <div class="card">
  321. <div class="card-header py-2">
  322. <div class="d-flex justify-content-between align-items-center gap-2 proc-toolbar">
  323. <div class="proc-toolbar-title text-nowrap">
  324. <strong>进程</strong>
  325. <span class="small text-secondary ms-2" id="proc-count">—</span>
  326. </div>
  327. <div class="d-flex flex-nowrap align-items-center gap-2 proc-toolbar-actions">
  328. <input class="form-control form-control-sm" id="proc-search" type="search" placeholder="搜索 PID / 用户 / 名称 / 目录 / 命令" />
  329. <div class="form-check form-switch mb-0 text-nowrap">
  330. <input class="form-check-input" type="checkbox" id="proc-auto" />
  331. <label class="form-check-label small" for="proc-auto">自动刷新</label>
  332. </div>
  333. <button type="button" class="btn btn-sm btn-outline-secondary text-nowrap" id="btn-proc-refresh">刷新</button>
  334. </div>
  335. </div>
  336. </div>
  337. <div class="table-responsive proc-table-wrap">
  338. <table class="table table-sm table-hover align-middle mb-0" id="proc-table">
  339. <thead>
  340. <tr>
  341. <th data-proc-sort="pid" class="proc-sort">PID</th>
  342. <th data-proc-sort="user" class="proc-sort">用户</th>
  343. <th data-proc-sort="cpu_percent" class="proc-sort is-active">CPU%</th>
  344. <th data-proc-sort="mem_percent" class="proc-sort">内存%</th>
  345. <th data-proc-sort="rss" class="proc-sort">RSS</th>
  346. <th>状态</th>
  347. <th data-proc-sort="name" class="proc-sort">名称</th>
  348. <th data-proc-sort="cwd" class="proc-sort">运行目录</th>
  349. <th>命令行</th>
  350. <th class="text-end">操作</th>
  351. </tr>
  352. </thead>
  353. <tbody></tbody>
  354. </table>
  355. </div>
  356. </div>
  357. </section>
  358. <section class="view" id="view-shell" data-view-panel="shell">
  359. <div class="card shell-card">
  360. <div class="card-header py-2">
  361. <div class="d-flex justify-content-between align-items-center gap-2 flex-wrap">
  362. <div class="text-nowrap">
  363. <strong>终端</strong>
  364. <span class="small text-secondary ms-2" id="shell-status">未连接</span>
  365. </div>
  366. <div class="d-flex flex-nowrap align-items-center gap-2">
  367. <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-shell-reconnect">重新连接</button>
  368. <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-shell-clear">清屏</button>
  369. </div>
  370. </div>
  371. </div>
  372. <div class="card-body p-0 shell-body">
  373. <div id="shell-terminal" class="shell-terminal"></div>
  374. </div>
  375. </div>
  376. </section>
  377. <section class="view" id="view-software" data-view-panel="software">
  378. <div class="row g-2" id="software-grid"></div>
  379. </section>
  380. <section class="view" id="view-plans" data-view-panel="plans">
  381. <div class="card">
  382. <div class="card-header py-2">
  383. <div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
  384. <div class="btn-group btn-group-sm" role="group" id="plans-tabs">
  385. <button type="button" class="btn btn-primary" data-plans-tab="cron">定时任务</button>
  386. <button type="button" class="btn btn-outline-primary" data-plans-tab="more" disabled title="即将推出">更多</button>
  387. </div>
  388. <div class="d-flex gap-2" id="plans-cron-actions">
  389. <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-schedule-refresh">刷新</button>
  390. <button type="button" class="btn btn-sm btn-primary" id="btn-schedule-add">新建任务</button>
  391. </div>
  392. </div>
  393. </div>
  394. <div class="card-body p-0">
  395. <div class="plans-pane is-active" data-plans-pane="cron">
  396. <div class="row g-0 plans-cron-layout">
  397. <div class="col-lg-7 border-end">
  398. <div class="table-responsive">
  399. <table class="table table-sm table-hover align-middle mb-0" id="schedule-table">
  400. <thead>
  401. <tr>
  402. <th>名称</th>
  403. <th>间隔</th>
  404. <th>URL</th>
  405. <th>状态</th>
  406. <th class="text-end">操作</th>
  407. </tr>
  408. </thead>
  409. <tbody></tbody>
  410. </table>
  411. </div>
  412. </div>
  413. <div class="col-lg-5">
  414. <div class="p-2 border-bottom d-flex flex-wrap justify-content-between align-items-center gap-2">
  415. <strong class="small mb-0">执行日志</strong>
  416. <div class="d-flex gap-2">
  417. <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-schedule-log-refresh">刷新日志</button>
  418. <button type="button" class="btn btn-sm btn-outline-danger" id="btn-schedule-log-clear">清空</button>
  419. </div>
  420. </div>
  421. <div class="schedule-log-meta small text-secondary px-2 py-1" id="schedule-log-meta">选择左侧任务查看日志</div>
  422. <pre class="schedule-log-view mb-0" id="schedule-log-view"></pre>
  423. </div>
  424. </div>
  425. </div>
  426. <div class="plans-pane p-4 text-secondary" data-plans-pane="more" hidden>
  427. 更多计划能力即将推出。
  428. </div>
  429. </div>
  430. </div>
  431. </section>
  432. <section class="view" id="view-settings" data-view-panel="settings">
  433. <div class="card">
  434. <div class="card-header py-2">
  435. <strong>面板设置</strong>
  436. <span class="small text-secondary ms-2">名称会显示在侧栏、浏览器标题和登录页</span>
  437. </div>
  438. <div class="card-body">
  439. <form id="settings-form" class="col-lg-6 col-xl-5">
  440. <div class="mb-3">
  441. <label class="form-label" for="setting-panel-name">网站名称</label>
  442. <input class="form-control" id="setting-panel-name" name="panel_name" maxlength="64"
  443. required placeholder="NGS" autocomplete="off" />
  444. </div>
  445. <button type="submit" class="btn btn-primary btn-sm" id="btn-settings-save">保存</button>
  446. </form>
  447. </div>
  448. </div>
  449. </section>
  450. </main>
  451. </div>
  452. <div class="toast-host position-fixed bottom-0 end-0 p-3" id="toast-host" aria-live="polite"></div>
  453. <div class="task-panel" id="task-panel" hidden>
  454. <div class="task-panel-backdrop" id="task-panel-backdrop"></div>
  455. <div class="task-panel-dialog" role="dialog" aria-modal="true" aria-labelledby="task-panel-title">
  456. <header class="task-panel-head d-flex justify-content-between align-items-start gap-2">
  457. <div>
  458. <h2 class="h5 mb-1" id="task-panel-title">任务队列</h2>
  459. <p class="small text-secondary mb-0">默认显示进行中任务,可切换查看历史。</p>
  460. </div>
  461. <button type="button" class="btn btn-sm btn-outline-light" id="task-panel-close" aria-label="关闭">×</button>
  462. </header>
  463. <div class="task-panel-body">
  464. <div class="task-side">
  465. <div class="task-side-tabs btn-group btn-group-sm w-100 px-2 pt-2">
  466. <button type="button" class="btn btn-warning" id="task-tab-active" data-task-tab="active">进行中</button>
  467. <button type="button" class="btn btn-outline-light" id="task-tab-history" data-task-tab="history">历史任务</button>
  468. </div>
  469. <div class="task-list" id="task-list"></div>
  470. <div class="task-side-foot px-2 pb-2">
  471. <button type="button" class="btn btn-sm btn-outline-light w-100 d-none" id="task-clear-finished">清理历史</button>
  472. </div>
  473. </div>
  474. <div class="task-log-wrap">
  475. <div class="task-log-head d-flex justify-content-between align-items-center">
  476. <strong id="task-log-title">日志</strong>
  477. <span class="badge text-bg-secondary" id="task-log-status">—</span>
  478. </div>
  479. <pre class="task-log" id="task-log"></pre>
  480. </div>
  481. </div>
  482. <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
  483. </div>
  484. </div>
  485. <div class="modal fade" id="modal" tabindex="-1" aria-hidden="true">
  486. <div class="modal-dialog modal-dialog-scrollable">
  487. <form class="modal-content" id="modal-form">
  488. <div class="modal-header py-2">
  489. <h5 class="modal-title" id="modal-title">操作</h5>
  490. <button type="button" class="btn-close" id="modal-close" aria-label="关闭"></button>
  491. </div>
  492. <div class="modal-body" id="modal-body"></div>
  493. <div class="modal-footer py-2">
  494. <button type="button" class="btn btn-sm btn-outline-secondary" id="modal-cancel">取消</button>
  495. <button type="submit" class="btn btn-sm btn-primary" id="modal-ok">确定</button>
  496. </div>
  497. <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
  498. </form>
  499. </div>
  500. </div>
  501. <div class="modal fade" id="redis-config-modal" tabindex="-1" aria-hidden="true">
  502. <div class="modal-dialog modal-lg modal-dialog-scrollable">
  503. <div class="modal-content">
  504. <div class="modal-header py-2">
  505. <h5 class="modal-title mb-0">Redis 配置</h5>
  506. <button type="button" class="btn-close" id="redis-config-close" aria-label="关闭"></button>
  507. </div>
  508. <div class="modal-body">
  509. <div class="btn-group btn-group-sm mb-3" role="group">
  510. <button type="button" class="btn btn-outline-primary active" id="redis-tab-quick" data-redis-tab="quick">基础配置</button>
  511. <button type="button" class="btn btn-outline-primary" id="redis-tab-file" data-redis-tab="file">配置文件</button>
  512. </div>
  513. <div id="redis-pane-quick">
  514. <p class="small text-secondary mb-3">快捷修改常用项;保存后若 Redis 正在运行会自动重启生效。</p>
  515. <div class="mb-2">
  516. <label class="form-label">监听地址 <span class="text-secondary fw-normal">bind</span></label>
  517. <input class="form-control form-control-sm" id="redis-cfg-bind" placeholder="127.0.0.1" autocomplete="off" />
  518. <div class="form-text">多个地址用空格分隔,例如 <code>127.0.0.1 0.0.0.0</code></div>
  519. </div>
  520. <div class="mb-2">
  521. <label class="form-label">端口 <span class="text-secondary fw-normal">port</span></label>
  522. <input class="form-control form-control-sm" id="redis-cfg-port" type="number" min="1" max="65535" />
  523. </div>
  524. <div class="mb-3">
  525. <label class="form-label">密码 <span class="text-secondary fw-normal">requirepass</span></label>
  526. <input class="form-control form-control-sm" id="redis-cfg-pass" type="password" placeholder="留空表示不设密码" autocomplete="new-password" />
  527. </div>
  528. <button type="button" class="btn btn-sm btn-primary" id="redis-cfg-save">保存并应用</button>
  529. </div>
  530. <div id="redis-pane-file" hidden>
  531. <div class="d-flex justify-content-between align-items-center mb-2 gap-2">
  532. <code class="small text-secondary text-truncate" id="redis-cfg-path"></code>
  533. <button type="button" class="btn btn-sm btn-outline-secondary" id="redis-cfg-reload">重新加载</button>
  534. </div>
  535. <div id="redis-cfg-monaco" class="soft-cfg-monaco" aria-label="Redis 配置编辑器"></div>
  536. <div class="mt-2 d-flex gap-2">
  537. <button type="button" class="btn btn-sm btn-primary" id="redis-cfg-file-save">保存文件</button>
  538. <span class="small text-secondary align-self-center">仅写入文件;如需生效请自行重启 Redis。</span>
  539. </div>
  540. </div>
  541. </div>
  542. </div>
  543. </div>
  544. </div>
  545. <div class="modal fade" id="nginx-config-modal" tabindex="-1" aria-hidden="true">
  546. <div class="modal-dialog modal-lg modal-dialog-scrollable">
  547. <div class="modal-content">
  548. <div class="modal-header py-2">
  549. <h5 class="modal-title mb-0">Nginx 配置</h5>
  550. <button type="button" class="btn-close" id="nginx-config-close" aria-label="关闭"></button>
  551. </div>
  552. <div class="modal-body">
  553. <div class="btn-group btn-group-sm mb-3" role="group">
  554. <button type="button" class="btn btn-outline-primary active" id="nginx-tab-quick" data-nginx-tab="quick">基础配置</button>
  555. <button type="button" class="btn btn-outline-primary" id="nginx-tab-file" data-nginx-tab="file">配置文件</button>
  556. </div>
  557. <div id="nginx-pane-quick">
  558. <p class="small text-secondary mb-3">快捷修改常用项;保存后会检测配置,若 Nginx 正在运行则自动重载生效。</p>
  559. <div class="mb-2">
  560. <label class="form-label">工作进程数 <span class="text-secondary fw-normal">worker_processes</span></label>
  561. <input class="form-control form-control-sm" id="nginx-cfg-workers" placeholder="auto" autocomplete="off" />
  562. <div class="form-text">填 <code>auto</code> 或正整数</div>
  563. </div>
  564. <div class="mb-2">
  565. <label class="form-label">最大连接数 <span class="text-secondary fw-normal">worker_connections</span></label>
  566. <input class="form-control form-control-sm" id="nginx-cfg-connections" type="number" min="1" max="1048576" />
  567. </div>
  568. <div class="mb-2">
  569. <label class="form-label">保持连接超时 <span class="text-secondary fw-normal">keepalive_timeout</span></label>
  570. <input class="form-control form-control-sm" id="nginx-cfg-keepalive" placeholder="65" autocomplete="off" />
  571. </div>
  572. <div class="mb-2">
  573. <label class="form-label">上传大小限制 <span class="text-secondary fw-normal">client_max_body_size</span></label>
  574. <input class="form-control form-control-sm" id="nginx-cfg-body" placeholder="50m" autocomplete="off" />
  575. <div class="form-text">例如 <code>1m</code>、<code>50m</code>、<code>1g</code></div>
  576. </div>
  577. <div class="mb-3 form-check">
  578. <input class="form-check-input" type="checkbox" id="nginx-cfg-gzip" />
  579. <label class="form-check-label" for="nginx-cfg-gzip">启用压缩 <span class="text-secondary fw-normal">gzip</span></label>
  580. </div>
  581. <button type="button" class="btn btn-sm btn-primary" id="nginx-cfg-save">保存并应用</button>
  582. </div>
  583. <div id="nginx-pane-file" hidden>
  584. <div class="d-flex justify-content-between align-items-center mb-2 gap-2">
  585. <code class="small text-secondary text-truncate" id="nginx-cfg-path"></code>
  586. <button type="button" class="btn btn-sm btn-outline-secondary" id="nginx-cfg-reload">重新加载</button>
  587. </div>
  588. <div id="nginx-cfg-monaco" class="soft-cfg-monaco" aria-label="Nginx 配置编辑器"></div>
  589. <div class="mt-2 d-flex gap-2">
  590. <button type="button" class="btn btn-sm btn-primary" id="nginx-cfg-file-save">保存文件</button>
  591. <span class="small text-secondary align-self-center">仅写入文件;如需生效请自行重载 Nginx。</span>
  592. </div>
  593. </div>
  594. </div>
  595. </div>
  596. </div>
  597. </div>
  598. <div class="modal fade" id="site-settings-modal" tabindex="-1" aria-hidden="true">
  599. <div class="modal-dialog modal-xl modal-dialog-scrollable">
  600. <div class="modal-content">
  601. <div class="modal-header py-2">
  602. <div>
  603. <h5 class="modal-title mb-0">网站配置 · <span id="site-settings-name"></span></h5>
  604. <div class="small text-secondary" id="site-settings-sub"></div>
  605. </div>
  606. <button type="button" class="btn-close" id="site-settings-close" aria-label="关闭"></button>
  607. </div>
  608. <div class="modal-body site-settings-body p-0">
  609. <div class="site-settings-layout">
  610. <aside class="site-settings-nav">
  611. <button type="button" class="site-settings-tab is-active" data-site-tab="domain">域名/分组</button>
  612. <button type="button" class="site-settings-tab d-none" data-site-tab="proxy" id="site-tab-proxy">反向代理</button>
  613. <button type="button" class="site-settings-tab" data-site-tab="cert">证书</button>
  614. <button type="button" class="site-settings-tab" data-site-tab="nginx">配置 (NGINX)</button>
  615. <button type="button" class="site-settings-tab" data-site-tab="custom">自定义配置</button>
  616. <button type="button" class="site-settings-tab" data-site-tab="logs">日志 (NGINX)</button>
  617. <button type="button" class="site-settings-tab d-none" data-site-tab="modules" id="site-tab-modules">模块</button>
  618. </aside>
  619. <div class="site-settings-panels">
  620. <div class="site-settings-panel is-active" data-site-panel="domain">
  621. <h6 class="mb-3">域名 / 分组</h6>
  622. <div class="mb-3">
  623. <label class="form-label">分组</label>
  624. <input class="form-control form-control-sm" id="site-set-group" list="site-group-datalist" placeholder="留空为未分组" autocomplete="off" />
  625. <datalist id="site-group-datalist"></datalist>
  626. <div class="form-text">同名分组合并显示;可在网站列表中筛选与排序。</div>
  627. </div>
  628. <div class="mb-3">
  629. <label class="form-label">域名(支持多个,每行一个)</label>
  630. <textarea class="form-control form-control-sm" id="site-set-domain" rows="4" placeholder="example.com&#10;www.example.com:8080"></textarea>
  631. <div class="form-text">可写 <code>域名</code> 或 <code>域名:端口</code>;不写端口默认 <code>80</code>。第一行作为主域名。</div>
  632. </div>
  633. <button type="button" class="btn btn-sm btn-primary" id="site-set-domain-save">保存并重载</button>
  634. </div>
  635. <div class="site-settings-panel" data-site-panel="proxy">
  636. <div class="d-flex justify-content-between align-items-center mb-3">
  637. <h6 class="mb-0">反向代理</h6>
  638. <button type="button" class="btn btn-sm btn-primary" id="site-proxy-add">添加 URL 代理</button>
  639. </div>
  640. <div class="table-responsive">
  641. <table class="table table-sm table-hover align-middle mb-0" id="site-proxy-table">
  642. <thead>
  643. <tr>
  644. <th>代理目录</th>
  645. <th>目标</th>
  646. <th>发送域名</th>
  647. <th>备注</th>
  648. <th class="text-end">操作</th>
  649. </tr>
  650. </thead>
  651. <tbody id="site-proxy-tbody">
  652. <tr><td colspan="5" class="text-secondary text-center py-3">加载中…</td></tr>
  653. </tbody>
  654. </table>
  655. </div>
  656. </div>
  657. <div class="site-settings-panel" data-site-panel="cert">
  658. <div class="btn-group btn-group-sm mb-3" role="group" aria-label="证书子页">
  659. <button type="button" class="btn btn-outline-primary active" id="site-cert-tab-pem" data-cert-tab="pem">证书内容</button>
  660. <button type="button" class="btn btn-outline-primary" id="site-cert-tab-apply" data-cert-tab="apply">申请 SSL</button>
  661. </div>
  662. <div id="site-cert-pane-pem">
  663. <div class="form-check form-switch mb-3">
  664. <input class="form-check-input" type="checkbox" id="site-set-ssl-enable" />
  665. <label class="form-check-label" for="site-set-ssl-enable">启用 HTTPS</label>
  666. </div>
  667. <div class="small mb-3" id="site-set-ssl-expire">证书有效期:—</div>
  668. <div class="mb-3">
  669. <label class="form-label">HTTPS 端口</label>
  670. <input class="form-control form-control-sm" id="site-set-ssl-port" type="number" min="1" max="65535" value="443" />
  671. </div>
  672. <div class="mb-3">
  673. <label class="form-label">证书 PEM <span class="text-secondary fw-normal">(fullchain)</span></label>
  674. <textarea class="form-control font-monospace site-ssl-pem" id="site-set-ssl-pem" rows="8" spellcheck="false" placeholder="-----BEGIN CERTIFICATE-----&#10;...&#10;-----END CERTIFICATE-----"></textarea>
  675. </div>
  676. <div class="mb-3">
  677. <label class="form-label">私钥 KEY</label>
  678. <textarea class="form-control font-monospace site-ssl-pem" id="site-set-ssl-key-pem" rows="8" spellcheck="false" placeholder="-----BEGIN PRIVATE KEY-----&#10;...&#10;-----END PRIVATE KEY-----"></textarea>
  679. </div>
  680. <p class="small text-secondary mb-3">保存后写入 <code>/ngs/conf/ssl/&lt;域名&gt;/fullchain.pem</code> 与 <code>privkey.pem</code> 并重载 Nginx。</p>
  681. <button type="button" class="btn btn-sm btn-primary" id="site-set-cert-save">保存并重载</button>
  682. </div>
  683. <div id="site-cert-pane-apply" hidden>
  684. <div class="fw-semibold mb-2">Let's Encrypt 免费证书</div>
  685. <p class="small text-secondary mb-2">勾选域名后合并签发到一张证书,成功后自动写入 <code>/ngs/conf/ssl/&lt;主域名&gt;/</code> 并启用 HTTPS。域名需已解析到本机,且公网可访问 80 端口。</p>
  686. <div class="mb-2">
  687. <div class="d-flex justify-content-between align-items-center mb-1">
  688. <label class="form-label mb-0">申请域名</label>
  689. <div class="btn-group btn-group-sm">
  690. <button type="button" class="btn btn-outline-secondary" id="site-ssl-domains-all">全选</button>
  691. <button type="button" class="btn btn-outline-secondary" id="site-ssl-domains-none">清空</button>
  692. </div>
  693. </div>
  694. <div class="site-ssl-domain-list border rounded bg-white p-2" id="site-ssl-domain-list"></div>
  695. </div>
  696. <button type="button" class="btn btn-sm btn-success" id="site-set-ssl-apply">申请 / 续签证书</button>
  697. </div>
  698. </div>
  699. <div class="site-settings-panel" data-site-panel="nginx">
  700. <div class="d-flex justify-content-between align-items-center mb-2 site-nginx-toolbar">
  701. <h6 class="mb-0">Nginx 站点配置</h6>
  702. <code class="small text-secondary text-truncate" id="site-set-conf-path"></code>
  703. </div>
  704. <div id="site-nginx-monaco" class="site-nginx-monaco" aria-label="Nginx 配置编辑器"></div>
  705. <div class="mt-2 d-flex gap-2 site-nginx-actions">
  706. <button type="button" class="btn btn-sm btn-primary" id="site-set-nginx-save">保存并重载</button>
  707. <button type="button" class="btn btn-sm btn-outline-secondary" id="site-set-nginx-reload">重新加载文件</button>
  708. </div>
  709. </div>
  710. <div class="site-settings-panel" data-site-panel="custom">
  711. <h6 class="mb-2">自定义配置</h6>
  712. <p class="small text-secondary mb-2">
  713. 手动添加的 Nginx 指令会写入站点 vhost,并在系统重新生成配置时保留。
  714. 保存后会自动检测并重载 Nginx。若在此填写
  715. <code>location /</code>(如 SPA 回退 / 伪静态),系统将<strong>不再生成</strong>默认的
  716. <code>location /</code>,避免 duplicate location。
  717. 若站点已配置「/」反向代理,则不要再写 <code>location /</code>。
  718. </p>
  719. <pre class="small bg-light border rounded p-2 mb-2 text-secondary"># SPA / 伪静态(会替换系统默认 location /)
  720. location / {
  721. try_files $uri $uri/ /index.html;
  722. }
  723. # 静态资源类型
  724. location ~ \.mjs$ {
  725. default_type application/javascript;
  726. }</pre>
  727. <textarea class="form-control font-monospace site-custom-nginx" id="site-set-custom"
  728. rows="14" spellcheck="false" placeholder="# 在此输入 server 块内的配置项"></textarea>
  729. <div class="mt-2 d-flex gap-2">
  730. <button type="button" class="btn btn-sm btn-primary" id="site-set-custom-save">保存并重载</button>
  731. <button type="button" class="btn btn-sm btn-outline-secondary" id="site-set-custom-reload">重新加载</button>
  732. </div>
  733. </div>
  734. <div class="site-settings-panel" data-site-panel="logs">
  735. <div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-2 site-log-toolbar">
  736. <div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
  737. <div class="btn-group btn-group-sm" role="group">
  738. <button type="button" class="btn btn-outline-primary active" id="site-log-tab-access" data-log-kind="access">访问日志</button>
  739. <button type="button" class="btn btn-outline-primary" id="site-log-tab-error" data-log-kind="error">error.log</button>
  740. </div>
  741. <input type="search" class="form-control form-control-sm site-log-search" id="site-log-search"
  742. placeholder="搜索 IP / URI / 状态 / UA…" autocomplete="off" />
  743. <span class="badge text-bg-light border site-log-ip-filter" id="site-log-ip-filter" hidden>
  744. IP:<code id="site-log-ip-value"></code>
  745. <button type="button" class="btn-close btn-close-sm ms-1" id="site-log-ip-clear" aria-label="清除 IP 筛选"></button>
  746. </span>
  747. </div>
  748. <button type="button" class="btn btn-sm btn-outline-secondary" id="site-log-refresh">刷新</button>
  749. </div>
  750. <div class="site-log-view" id="site-log-view">加载中…</div>
  751. </div>
  752. <div class="site-settings-panel" data-site-panel="modules">
  753. <div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3">
  754. <div>
  755. <h6 class="mb-1">Fastweb 扩展模块</h6>
  756. <div class="small text-secondary">列表来自 <code>download.fwlua.com/module/module.json</code>,通过 <code>fastweb instmod</code> 安装到当前站点。</div>
  757. </div>
  758. <button type="button" class="btn btn-sm btn-outline-secondary" id="site-modules-refresh">刷新</button>
  759. </div>
  760. <div class="btn-group btn-group-sm mb-3" role="group" id="site-modules-filter">
  761. <button type="button" class="btn btn-outline-primary active" data-mod-filter="all">全部</button>
  762. <button type="button" class="btn btn-outline-primary" data-mod-filter="fastweb">FastWeb</button>
  763. <button type="button" class="btn btn-outline-primary" data-mod-filter="lua">Lua</button>
  764. </div>
  765. <div id="site-modules-list" class="site-modules-list">加载中…</div>
  766. </div>
  767. </div>
  768. </div>
  769. </div>
  770. <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
  771. </div>
  772. </div>
  773. </div>
  774. <div class="modal fade" id="upload-modal" tabindex="-1" aria-hidden="true">
  775. <div class="modal-dialog modal-lg modal-dialog-scrollable">
  776. <div class="modal-content">
  777. <div class="modal-header py-2">
  778. <div>
  779. <h5 class="modal-title mb-0">上传到当前目录</h5>
  780. <code class="small text-secondary" id="upload-target-path">/</code>
  781. </div>
  782. <button type="button" class="btn-close" id="upload-modal-close" aria-label="关闭"></button>
  783. </div>
  784. <div class="modal-body">
  785. <div class="upload-dropzone" id="upload-dropzone" tabindex="0">
  786. <div class="upload-dropzone-inner">
  787. <i class="bi bi-cloud-arrow-up upload-drop-icon"></i>
  788. <p class="mb-1">拖拽文件或文件夹到此处</p>
  789. <p class="small text-secondary mb-3">支持大文件分片上传。也可使用下方按钮选择,选完后统一上传</p>
  790. <div class="btn-group btn-group-sm">
  791. <button type="button" class="btn btn-outline-primary" id="upload-pick-files">选择文件</button>
  792. <button type="button" class="btn btn-outline-primary" id="upload-pick-dir">选择目录</button>
  793. </div>
  794. </div>
  795. <input type="file" id="upload-input-files" hidden multiple />
  796. <input type="file" id="upload-input-dir" hidden webkitdirectory multiple />
  797. </div>
  798. <div class="d-flex justify-content-between align-items-center mt-3 mb-2">
  799. <span class="small text-secondary" id="upload-queue-summary">未选择文件</span>
  800. <button type="button" class="btn btn-sm btn-link text-decoration-none p-0" id="upload-clear-queue" hidden>清空列表</button>
  801. </div>
  802. <div class="upload-queue" id="upload-queue"></div>
  803. <div class="upload-progress mt-3" id="upload-progress" hidden>
  804. <div class="d-flex justify-content-between small mb-1">
  805. <span id="upload-progress-label">准备上传…</span>
  806. <span id="upload-progress-pct">0%</span>
  807. </div>
  808. <div class="progress" style="height: 8px;">
  809. <div class="progress-bar bg-teal" id="upload-progress-bar" role="progressbar" style="width: 0%"></div>
  810. </div>
  811. </div>
  812. </div>
  813. <div class="modal-footer py-2">
  814. <button type="button" class="btn btn-sm btn-outline-secondary" id="upload-modal-cancel">取消</button>
  815. <button type="button" class="btn btn-sm btn-primary" id="upload-start" disabled>开始上传</button>
  816. </div>
  817. <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
  818. </div>
  819. </div>
  820. </div>
  821. <div class="modal fade" id="site-analytics-modal" tabindex="-1" aria-hidden="true">
  822. <div class="modal-dialog modal-xl modal-dialog-scrollable">
  823. <div class="modal-content">
  824. <div class="modal-header py-2">
  825. <div class="d-flex align-items-center gap-2 flex-wrap">
  826. <h5 class="modal-title mb-0">日志分析 · <span id="analytics-site-name">—</span></h5>
  827. <select class="form-select form-select-sm" id="analytics-range" style="width:auto">
  828. <option value="1h">近 1 小时</option>
  829. <option value="6h">近 6 小时</option>
  830. <option value="24h" selected>近 24 小时</option>
  831. <option value="7d">近 7 天</option>
  832. <option value="30d">近 30 天</option>
  833. <option value="all">全部</option>
  834. </select>
  835. </div>
  836. <button type="button" class="btn-close" id="analytics-close" aria-label="关闭"></button>
  837. </div>
  838. <div class="modal-body" id="analytics-body">
  839. <div class="text-secondary small">加载中…</div>
  840. </div>
  841. <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
  842. </div>
  843. </div>
  844. </div>
  845. <div class="code-editor" id="code-editor" hidden>
  846. <div class="code-editor-backdrop" id="code-editor-backdrop"></div>
  847. <div class="code-editor-window">
  848. <header class="code-editor-bar">
  849. <div class="code-editor-meta">
  850. <strong id="code-editor-mode">编辑器</strong>
  851. <span id="code-editor-path"></span>
  852. </div>
  853. <button type="button" class="code-editor-x" id="code-editor-cancel" title="关闭" aria-label="关闭">×</button>
  854. </header>
  855. <div class="code-editor-main">
  856. <aside class="code-editor-tree">
  857. <div class="code-editor-tree-head">
  858. <button type="button" class="btn btn-sm btn-outline-light" id="editor-tree-up" title="上级">↑</button>
  859. <span class="code-editor-tree-path" id="editor-tree-path">/</span>
  860. <button type="button" class="btn btn-sm btn-outline-light" id="editor-tree-refresh" title="刷新">↻</button>
  861. </div>
  862. <div class="code-editor-tree-body" id="editor-tree"></div>
  863. </aside>
  864. <div class="code-editor-workspace">
  865. <div class="code-editor-tabs" id="editor-tabs"></div>
  866. <div id="monaco-host" class="monaco-host"></div>
  867. </div>
  868. </div>
  869. <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
  870. </div>
  871. </div>
  872. <script src="/assets/bootstrap/js/bootstrap.bundle.min.js"></script>
  873. <script src="/assets/monaco/vs/loader.js"></script>
  874. <script src="/assets/app.js"></script>
  875. </body>
  876. </html>