index.html 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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/app.css" />
  10. <script src="/assets/chartjs/chart.umd.min.js"></script>
  11. </head>
  12. <body>
  13. <div class="app d-flex">
  14. <aside class="rail flex-shrink-0">
  15. <div class="brand d-flex align-items-center gap-2 px-3 py-2">
  16. <div class="brand-mark">N</div>
  17. <div class="brand-text flex-grow-1">
  18. <div class="fw-bold text-white lh-1">NGS</div>
  19. <div class="small text-white-50">管理面板</div>
  20. </div>
  21. <button type="button" class="btn btn-sm btn-outline-light position-relative" id="task-badge" title="任务队列">
  22. <i class="bi bi-list-task"></i>
  23. <span class="badge text-bg-warning position-absolute top-0 start-100 translate-middle rounded-pill" id="task-badge-count">0</span>
  24. </button>
  25. <button type="button" class="btn btn-sm btn-outline-light" id="btn-logout" title="退出登录">
  26. <i class="bi bi-box-arrow-right"></i>
  27. </button>
  28. </div>
  29. <nav class="nav flex-column px-2 gap-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. <button type="button" class="nav-link text-start nav-item" data-view="mysql"><i class="bi bi-database me-2"></i>数据库</button>
  34. <button type="button" class="nav-link text-start nav-item" data-view="software"><i class="bi bi-box-seam me-2"></i>软件</button>
  35. </nav>
  36. <div class="rail-foot mt-auto px-3 py-2">
  37. <div class="live small d-flex align-items-center gap-2" id="live-dot">
  38. <span class="pulse"></span>
  39. <span id="live-label">检测中</span>
  40. </div>
  41. </div>
  42. </aside>
  43. <main class="main flex-grow-1">
  44. <header class="top d-flex justify-content-between align-items-center mb-3">
  45. <div>
  46. <div class="text-secondary small" id="page-eyebrow">Console</div>
  47. <h1 class="h4 mb-0" id="page-title">控制台</h1>
  48. </div>
  49. <div class="text-secondary small" id="top-meta"></div>
  50. </header>
  51. <section class="view is-active" id="view-console" data-view-panel="console">
  52. <div class="card mb-3">
  53. <div class="card-header py-2 d-flex justify-content-between align-items-center gap-2">
  54. <strong>实时资源</strong>
  55. <div class="d-flex align-items-center gap-2">
  56. <span class="small text-secondary" id="overview-sites">—</span>
  57. <select class="form-select form-select-sm metrics-range" id="metrics-range" title="时间范围">
  58. <option value="1m">1分</option>
  59. <option value="10m" selected>10分</option>
  60. <option value="30m">30分</option>
  61. <option value="1h">1小时</option>
  62. <option value="6h">6小时</option>
  63. <option value="1d">1天</option>
  64. <option value="7d">7天</option>
  65. <option value="30d">30天</option>
  66. </select>
  67. </div>
  68. </div>
  69. <div class="card-body py-3 d-flex flex-column min-h-0">
  70. <div class="row g-3 monitor-grid flex-grow-1">
  71. <div class="col-md-6">
  72. <div class="monitor-card">
  73. <div class="d-flex justify-content-between align-items-center small mb-2">
  74. <span class="fw-semibold">CPU</span>
  75. <span class="text-secondary" id="cpu-badge">—</span>
  76. </div>
  77. <div class="monitor-chart"><canvas id="chart-cpu"></canvas></div>
  78. </div>
  79. </div>
  80. <div class="col-md-6">
  81. <div class="monitor-card">
  82. <div class="d-flex justify-content-between align-items-center small mb-2">
  83. <span class="fw-semibold">内存</span>
  84. <span class="text-secondary" id="mem-badge">—</span>
  85. </div>
  86. <div class="monitor-chart"><canvas id="chart-mem"></canvas></div>
  87. </div>
  88. </div>
  89. <div class="col-md-6">
  90. <div class="monitor-card">
  91. <div class="d-flex justify-content-between align-items-center small mb-2">
  92. <span class="fw-semibold">网络</span>
  93. <span class="text-secondary" id="net-badge">—</span>
  94. </div>
  95. <div class="monitor-chart"><canvas id="chart-net"></canvas></div>
  96. </div>
  97. </div>
  98. <div class="col-md-6">
  99. <div class="monitor-card">
  100. <div class="d-flex justify-content-between align-items-center small mb-2">
  101. <span class="fw-semibold">磁盘 /</span>
  102. <span class="text-secondary" id="disk-badge">—</span>
  103. </div>
  104. <div class="monitor-chart"><canvas id="chart-disk"></canvas></div>
  105. </div>
  106. </div>
  107. </div>
  108. </div>
  109. </div>
  110. </section>
  111. <section class="view" id="view-sites" data-view-panel="sites">
  112. <div class="card">
  113. <div class="card-header py-2 d-flex justify-content-between align-items-center">
  114. <div>
  115. <strong>网站</strong>
  116. <span class="small text-secondary ms-2">创建 / 启停 / 配置 / 删除</span>
  117. </div>
  118. <button type="button" class="btn btn-sm btn-primary" id="btn-site-create">新建网站</button>
  119. </div>
  120. <div class="table-responsive">
  121. <table class="table table-sm table-hover align-middle mb-0" id="sites-table">
  122. <thead>
  123. <tr>
  124. <th>名称</th>
  125. <th>类型</th>
  126. <th>域名</th>
  127. <th>目录</th>
  128. <th>上游</th>
  129. <th>状态</th>
  130. <th class="text-end">操作</th>
  131. </tr>
  132. </thead>
  133. <tbody></tbody>
  134. </table>
  135. </div>
  136. </div>
  137. </section>
  138. <section class="view" id="view-files" data-view-panel="files">
  139. <div class="card">
  140. <div class="card-header py-2">
  141. <div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
  142. <div>
  143. <strong>文件</strong>
  144. <code class="small ms-2" id="files-path-label">/</code>
  145. </div>
  146. <div class="btn-group btn-group-sm">
  147. <button type="button" class="btn btn-outline-secondary" id="btn-files-up">上级</button>
  148. <button type="button" class="btn btn-outline-secondary" id="btn-files-mkdir">新建目录</button>
  149. <button type="button" class="btn btn-outline-secondary" id="btn-files-newfile">新建文件</button>
  150. <button type="button" class="btn btn-primary" id="btn-files-upload">上传</button>
  151. </div>
  152. </div>
  153. <div class="crumbs small mt-2" id="files-crumbs"></div>
  154. </div>
  155. <div class="table-responsive">
  156. <table class="table table-sm table-hover align-middle mb-0" id="files-table">
  157. <thead>
  158. <tr>
  159. <th>名称</th>
  160. <th>类型</th>
  161. <th>大小</th>
  162. <th>修改时间</th>
  163. <th class="text-end">操作</th>
  164. </tr>
  165. </thead>
  166. <tbody></tbody>
  167. </table>
  168. </div>
  169. </div>
  170. </section>
  171. <section class="view" id="view-mysql" data-view-panel="mysql">
  172. <div class="card">
  173. <div class="card-header py-2 d-flex justify-content-between align-items-center">
  174. <div>
  175. <strong>数据库</strong>
  176. <span class="small text-secondary ms-2">库 / 账号 / 权限</span>
  177. </div>
  178. <div class="btn-group btn-group-sm">
  179. <button type="button" class="btn btn-outline-secondary" id="btn-mysql-password">改 root 密码</button>
  180. <button type="button" class="btn btn-primary" id="btn-db-create">新建数据库</button>
  181. </div>
  182. </div>
  183. <div class="table-responsive">
  184. <table class="table table-sm table-hover align-middle mb-0" id="db-table">
  185. <thead>
  186. <tr>
  187. <th>库名</th>
  188. <th>用户</th>
  189. <th>密码</th>
  190. <th>权限</th>
  191. <th>Host</th>
  192. <th class="text-end">操作</th>
  193. </tr>
  194. </thead>
  195. <tbody></tbody>
  196. </table>
  197. </div>
  198. </div>
  199. </section>
  200. <section class="view" id="view-software" data-view-panel="software">
  201. <div class="row g-2" id="software-grid"></div>
  202. </section>
  203. </main>
  204. </div>
  205. <div class="toast-host position-fixed bottom-0 end-0 p-3" id="toast-host" aria-live="polite"></div>
  206. <div class="task-panel" id="task-panel" hidden>
  207. <div class="task-panel-backdrop" id="task-panel-backdrop"></div>
  208. <aside class="task-panel-sheet">
  209. <header class="task-panel-head d-flex justify-content-between align-items-start gap-2">
  210. <div>
  211. <h2 class="h5 mb-1">任务队列</h2>
  212. <p class="small text-secondary mb-0">默认显示进行中任务,可切换查看历史。</p>
  213. </div>
  214. <button type="button" class="btn btn-sm btn-outline-light" id="task-panel-close" aria-label="关闭">×</button>
  215. </header>
  216. <div class="task-panel-body">
  217. <div class="task-side">
  218. <div class="task-side-tabs btn-group btn-group-sm w-100 px-2 pt-2">
  219. <button type="button" class="btn btn-warning" id="task-tab-active" data-task-tab="active">进行中</button>
  220. <button type="button" class="btn btn-outline-light" id="task-tab-history" data-task-tab="history">历史任务</button>
  221. </div>
  222. <div class="task-list" id="task-list"></div>
  223. <div class="task-side-foot px-2 pb-2">
  224. <button type="button" class="btn btn-sm btn-outline-light w-100 d-none" id="task-clear-finished">清理历史</button>
  225. </div>
  226. </div>
  227. <div class="task-log-wrap">
  228. <div class="task-log-head d-flex justify-content-between align-items-center">
  229. <strong id="task-log-title">日志</strong>
  230. <span class="badge text-bg-secondary" id="task-log-status">—</span>
  231. </div>
  232. <pre class="task-log" id="task-log"></pre>
  233. </div>
  234. </div>
  235. </aside>
  236. </div>
  237. <div class="modal fade" id="modal" tabindex="-1" aria-hidden="true">
  238. <div class="modal-dialog modal-dialog-scrollable">
  239. <form class="modal-content" id="modal-form">
  240. <div class="modal-header py-2">
  241. <h5 class="modal-title" id="modal-title">操作</h5>
  242. <button type="button" class="btn-close" id="modal-close" aria-label="关闭"></button>
  243. </div>
  244. <div class="modal-body" id="modal-body"></div>
  245. <div class="modal-footer py-2">
  246. <button type="button" class="btn btn-sm btn-outline-secondary" id="modal-cancel">取消</button>
  247. <button type="submit" class="btn btn-sm btn-primary" id="modal-ok">确定</button>
  248. </div>
  249. </form>
  250. </div>
  251. </div>
  252. <div class="modal fade" id="site-settings-modal" tabindex="-1" aria-hidden="true">
  253. <div class="modal-dialog modal-xl modal-dialog-scrollable">
  254. <div class="modal-content">
  255. <div class="modal-header py-2">
  256. <div>
  257. <h5 class="modal-title mb-0">网站配置 · <span id="site-settings-name"></span></h5>
  258. <div class="small text-secondary" id="site-settings-sub"></div>
  259. </div>
  260. <button type="button" class="btn-close" id="site-settings-close" aria-label="关闭"></button>
  261. </div>
  262. <div class="modal-body site-settings-body p-0">
  263. <div class="site-settings-layout">
  264. <aside class="site-settings-nav">
  265. <button type="button" class="site-settings-tab is-active" data-site-tab="domain">域名</button>
  266. <button type="button" class="site-settings-tab" data-site-tab="cert">证书</button>
  267. <button type="button" class="site-settings-tab" data-site-tab="nginx">配置 (NGINX)</button>
  268. <button type="button" class="site-settings-tab" data-site-tab="logs">日志 (NGINX)</button>
  269. </aside>
  270. <div class="site-settings-panels">
  271. <div class="site-settings-panel is-active" data-site-panel="domain">
  272. <h6 class="mb-3">域名</h6>
  273. <div class="mb-3">
  274. <label class="form-label">域名(支持多个,每行一个)</label>
  275. <textarea class="form-control form-control-sm" id="site-set-domain" rows="4" placeholder="example.com&#10;www.example.com:8080"></textarea>
  276. <div class="form-text">可写 <code>域名</code> 或 <code>域名:端口</code>;不写端口默认 <code>80</code>。第一行作为主域名。</div>
  277. </div>
  278. <button type="button" class="btn btn-sm btn-primary" id="site-set-domain-save">保存并重载</button>
  279. </div>
  280. <div class="site-settings-panel" data-site-panel="cert">
  281. <h6 class="mb-3">SSL 证书</h6>
  282. <div class="border rounded p-3 mb-3 bg-light">
  283. <div class="fw-semibold mb-2">Let's Encrypt 免费证书</div>
  284. <p class="small text-secondary mb-2">勾选要申请的域名,将合并签发到<strong>一张</strong>证书。域名需已解析到本机,且公网可访问 80 端口。</p>
  285. <div class="mb-2">
  286. <div class="d-flex justify-content-between align-items-center mb-1">
  287. <label class="form-label mb-0">申请域名</label>
  288. <div class="btn-group btn-group-sm">
  289. <button type="button" class="btn btn-outline-secondary" id="site-ssl-domains-all">全选</button>
  290. <button type="button" class="btn btn-outline-secondary" id="site-ssl-domains-none">清空</button>
  291. </div>
  292. </div>
  293. <div class="site-ssl-domain-list border rounded bg-white p-2" id="site-ssl-domain-list"></div>
  294. </div>
  295. <button type="button" class="btn btn-sm btn-success" id="site-set-ssl-apply">申请 / 续签证书</button>
  296. </div>
  297. <div class="form-check form-switch mb-3">
  298. <input class="form-check-input" type="checkbox" id="site-set-ssl-enable" />
  299. <label class="form-check-label" for="site-set-ssl-enable">启用 HTTPS</label>
  300. </div>
  301. <div class="mb-3">
  302. <label class="form-label">HTTPS 端口</label>
  303. <input class="form-control form-control-sm" id="site-set-ssl-port" type="number" min="1" max="65535" value="443" />
  304. </div>
  305. <div class="mb-3">
  306. <label class="form-label">证书文件 (.crt / .pem)</label>
  307. <div class="input-group input-group-sm">
  308. <input class="form-control" id="site-set-ssl-cert" placeholder="/path/to/fullchain.pem" />
  309. <button type="button" class="btn btn-outline-secondary" id="site-set-ssl-cert-upload">上传</button>
  310. </div>
  311. </div>
  312. <div class="mb-3">
  313. <label class="form-label">私钥文件 (.key)</label>
  314. <div class="input-group input-group-sm">
  315. <input class="form-control" id="site-set-ssl-key" placeholder="/path/to/privkey.pem" />
  316. <button type="button" class="btn btn-outline-secondary" id="site-set-ssl-key-upload">上传</button>
  317. </div>
  318. </div>
  319. <input type="file" id="site-ssl-file-input" hidden accept=".pem,.crt,.cer,.key" />
  320. <p class="small text-secondary mb-3">也可手动上传证书到网站目录 <code>ssl/</code> 后保存启用。</p>
  321. <button type="button" class="btn btn-sm btn-primary" id="site-set-cert-save">保存并重载</button>
  322. </div>
  323. <div class="site-settings-panel" data-site-panel="nginx">
  324. <div class="d-flex justify-content-between align-items-center mb-2">
  325. <h6 class="mb-0">Nginx 站点配置</h6>
  326. <code class="small text-secondary" id="site-set-conf-path"></code>
  327. </div>
  328. <textarea class="form-control font-monospace site-conf-editor" id="site-set-nginx-conf" rows="16" spellcheck="false"></textarea>
  329. <div class="mt-2 d-flex gap-2">
  330. <button type="button" class="btn btn-sm btn-primary" id="site-set-nginx-save">保存并重载</button>
  331. <button type="button" class="btn btn-sm btn-outline-secondary" id="site-set-nginx-reload">重新加载文件</button>
  332. </div>
  333. </div>
  334. <div class="site-settings-panel" data-site-panel="logs">
  335. <div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-2">
  336. <div class="btn-group btn-group-sm" role="group">
  337. <button type="button" class="btn btn-outline-primary active" id="site-log-tab-access" data-log-kind="access">访问日志</button>
  338. <button type="button" class="btn btn-outline-primary" id="site-log-tab-error" data-log-kind="error">error.log</button>
  339. </div>
  340. <div class="btn-group btn-group-sm">
  341. <button type="button" class="btn btn-outline-secondary" id="site-log-refresh">刷新</button>
  342. <button type="button" class="btn btn-outline-secondary" id="site-log-open-dir">打开日志目录</button>
  343. </div>
  344. </div>
  345. <div class="site-log-view" id="site-log-view">加载中…</div>
  346. </div>
  347. </div>
  348. </div>
  349. </div>
  350. </div>
  351. </div>
  352. </div>
  353. <div class="modal fade" id="upload-modal" tabindex="-1" aria-hidden="true">
  354. <div class="modal-dialog modal-lg modal-dialog-scrollable">
  355. <div class="modal-content">
  356. <div class="modal-header py-2">
  357. <div>
  358. <h5 class="modal-title mb-0">上传到当前目录</h5>
  359. <code class="small text-secondary" id="upload-target-path">/</code>
  360. </div>
  361. <button type="button" class="btn-close" id="upload-modal-close" aria-label="关闭"></button>
  362. </div>
  363. <div class="modal-body">
  364. <div class="upload-dropzone" id="upload-dropzone" tabindex="0">
  365. <div class="upload-dropzone-inner">
  366. <i class="bi bi-cloud-arrow-up upload-drop-icon"></i>
  367. <p class="mb-1">拖拽文件或文件夹到此处</p>
  368. <p class="small text-secondary mb-3">也可使用下方按钮选择,选完后统一上传</p>
  369. <div class="btn-group btn-group-sm">
  370. <button type="button" class="btn btn-outline-primary" id="upload-pick-files">选择文件</button>
  371. <button type="button" class="btn btn-outline-primary" id="upload-pick-dir">选择目录</button>
  372. </div>
  373. </div>
  374. <input type="file" id="upload-input-files" hidden multiple />
  375. <input type="file" id="upload-input-dir" hidden webkitdirectory multiple />
  376. </div>
  377. <div class="d-flex justify-content-between align-items-center mt-3 mb-2">
  378. <span class="small text-secondary" id="upload-queue-summary">未选择文件</span>
  379. <button type="button" class="btn btn-sm btn-link text-decoration-none p-0" id="upload-clear-queue" hidden>清空列表</button>
  380. </div>
  381. <div class="upload-queue" id="upload-queue"></div>
  382. <div class="upload-progress mt-3" id="upload-progress" hidden>
  383. <div class="d-flex justify-content-between small mb-1">
  384. <span id="upload-progress-label">准备上传…</span>
  385. <span id="upload-progress-pct">0%</span>
  386. </div>
  387. <div class="progress" style="height: 8px;">
  388. <div class="progress-bar bg-teal" id="upload-progress-bar" role="progressbar" style="width: 0%"></div>
  389. </div>
  390. </div>
  391. </div>
  392. <div class="modal-footer py-2">
  393. <button type="button" class="btn btn-sm btn-outline-secondary" id="upload-modal-cancel">取消</button>
  394. <button type="button" class="btn btn-sm btn-primary" id="upload-start" disabled>开始上传</button>
  395. </div>
  396. </div>
  397. </div>
  398. </div>
  399. <div class="modal fade" id="site-analytics-modal" tabindex="-1" aria-hidden="true">
  400. <div class="modal-dialog modal-xl modal-dialog-scrollable">
  401. <div class="modal-content">
  402. <div class="modal-header py-2">
  403. <div class="d-flex align-items-center gap-2 flex-wrap">
  404. <h5 class="modal-title mb-0">日志分析 · <span id="analytics-site-name">—</span></h5>
  405. <select class="form-select form-select-sm" id="analytics-range" style="width:auto">
  406. <option value="1h">近 1 小时</option>
  407. <option value="6h">近 6 小时</option>
  408. <option value="24h" selected>近 24 小时</option>
  409. <option value="7d">近 7 天</option>
  410. <option value="30d">近 30 天</option>
  411. <option value="all">全部</option>
  412. </select>
  413. </div>
  414. <button type="button" class="btn-close" id="analytics-close" aria-label="关闭"></button>
  415. </div>
  416. <div class="modal-body" id="analytics-body">
  417. <div class="text-secondary small">加载中…</div>
  418. </div>
  419. </div>
  420. </div>
  421. </div>
  422. <div class="code-editor" id="code-editor" hidden>
  423. <div class="code-editor-backdrop" id="code-editor-backdrop"></div>
  424. <div class="code-editor-window">
  425. <header class="code-editor-bar">
  426. <div class="code-editor-meta">
  427. <strong id="code-editor-mode">编辑器</strong>
  428. <span id="code-editor-path"></span>
  429. </div>
  430. <button type="button" class="code-editor-x" id="code-editor-cancel" title="关闭" aria-label="关闭">×</button>
  431. </header>
  432. <div class="code-editor-main">
  433. <aside class="code-editor-tree">
  434. <div class="code-editor-tree-head">
  435. <button type="button" class="btn btn-sm btn-outline-light" id="editor-tree-up" title="上级">↑</button>
  436. <span class="code-editor-tree-path" id="editor-tree-path">/</span>
  437. <button type="button" class="btn btn-sm btn-outline-light" id="editor-tree-refresh" title="刷新">↻</button>
  438. </div>
  439. <div class="code-editor-tree-body" id="editor-tree"></div>
  440. </aside>
  441. <div class="code-editor-workspace">
  442. <div class="code-editor-tabs" id="editor-tabs"></div>
  443. <div id="monaco-host" class="monaco-host"></div>
  444. </div>
  445. </div>
  446. </div>
  447. </div>
  448. <script src="/assets/bootstrap/js/bootstrap.bundle.min.js"></script>
  449. <script src="/assets/monaco/vs/loader.js"></script>
  450. <script src="/assets/app.js"></script>
  451. </body>
  452. </html>