| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <title>NGS · 控制台</title>
- <link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css" />
- <link rel="stylesheet" href="/assets/bootstrap-icons/bootstrap-icons.css" />
- <link rel="stylesheet" href="/assets/app.css" />
- <script src="/assets/chartjs/chart.umd.min.js"></script>
- </head>
- <body>
- <div class="app d-flex">
- <aside class="rail flex-shrink-0">
- <div class="brand d-flex align-items-center gap-2 px-3 py-2">
- <div class="brand-mark">N</div>
- <div class="brand-text flex-grow-1">
- <div class="fw-bold text-white lh-1">NGS</div>
- <div class="small text-white-50">管理面板</div>
- </div>
- <button type="button" class="btn btn-sm btn-outline-light position-relative" id="task-badge" title="任务队列">
- <i class="bi bi-list-task"></i>
- <span class="badge text-bg-warning position-absolute top-0 start-100 translate-middle rounded-pill" id="task-badge-count">0</span>
- </button>
- </div>
- <nav class="nav flex-column px-2 gap-1 flex-grow-1">
- <button type="button" class="nav-link text-start nav-item is-active" data-view="console"><i class="bi bi-speedometer2 me-2"></i>控制台</button>
- <button type="button" class="nav-link text-start nav-item" data-view="sites"><i class="bi bi-globe2 me-2"></i>网站</button>
- <button type="button" class="nav-link text-start nav-item" data-view="files"><i class="bi bi-folder2-open me-2"></i>文件</button>
- <button type="button" class="nav-link text-start nav-item" data-view="mysql"><i class="bi bi-database me-2"></i>数据库</button>
- <button type="button" class="nav-link text-start nav-item" data-view="processes"><i class="bi bi-cpu me-2"></i>进程</button>
- <button type="button" class="nav-link text-start nav-item" data-view="software"><i class="bi bi-box-seam me-2"></i>软件</button>
- </nav>
- <div class="rail-foot px-2 pb-2">
- <button type="button" class="nav-link text-start nav-item nav-logout w-100" id="btn-logout">
- <i class="bi bi-box-arrow-right me-2"></i>退出
- </button>
- <div class="live small d-flex align-items-center gap-2 px-2 pt-1" id="live-dot">
- <span class="pulse"></span>
- <span id="live-label">检测中</span>
- </div>
- </div>
- </aside>
- <main class="main flex-grow-1">
- <header class="top d-flex justify-content-between align-items-center mb-3">
- <div>
- <div class="text-secondary small" id="page-eyebrow">Console</div>
- <h1 class="h4 mb-0" id="page-title">控制台</h1>
- </div>
- <div class="text-secondary small" id="top-meta"></div>
- </header>
- <section class="view is-active" id="view-console" data-view-panel="console">
- <div class="card mb-3">
- <div class="card-header py-2 d-flex justify-content-between align-items-center gap-2">
- <strong>实时资源</strong>
- <div class="d-flex align-items-center gap-2">
- <span class="small text-secondary" id="overview-sites">—</span>
- <select class="form-select form-select-sm metrics-range" id="metrics-range" title="时间范围">
- <option value="1m">1分</option>
- <option value="10m" selected>10分</option>
- <option value="30m">30分</option>
- <option value="1h">1小时</option>
- <option value="6h">6小时</option>
- <option value="1d">1天</option>
- <option value="7d">7天</option>
- <option value="30d">30天</option>
- </select>
- </div>
- </div>
- <div class="card-body py-3 d-flex flex-column min-h-0">
- <div class="row g-3 monitor-grid flex-grow-1">
- <div class="col-md-6">
- <div class="monitor-card">
- <div class="d-flex justify-content-between align-items-center small mb-2">
- <span class="fw-semibold">CPU</span>
- <span class="text-secondary" id="cpu-badge">—</span>
- </div>
- <div class="monitor-chart"><canvas id="chart-cpu"></canvas></div>
- </div>
- </div>
- <div class="col-md-6">
- <div class="monitor-card">
- <div class="d-flex justify-content-between align-items-center small mb-2">
- <span class="fw-semibold">内存</span>
- <span class="text-secondary" id="mem-badge">—</span>
- </div>
- <div class="monitor-chart"><canvas id="chart-mem"></canvas></div>
- </div>
- </div>
- <div class="col-md-6">
- <div class="monitor-card">
- <div class="d-flex justify-content-between align-items-center small mb-2">
- <span class="fw-semibold">网络</span>
- <span class="text-secondary" id="net-badge">—</span>
- </div>
- <div class="monitor-chart"><canvas id="chart-net"></canvas></div>
- </div>
- </div>
- <div class="col-md-6">
- <div class="monitor-card">
- <div class="d-flex justify-content-between align-items-center small mb-2">
- <span class="fw-semibold">磁盘 /</span>
- <span class="text-secondary" id="disk-badge">—</span>
- </div>
- <div class="monitor-chart"><canvas id="chart-disk"></canvas></div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </section>
- <section class="view" id="view-sites" data-view-panel="sites">
- <div class="card">
- <div class="card-header py-2 d-flex justify-content-between align-items-center">
- <div>
- <strong>网站</strong>
- <span class="small text-secondary ms-2">创建 / 启停 / 配置 / 删除</span>
- </div>
- <button type="button" class="btn btn-sm btn-primary" id="btn-site-create">新建网站</button>
- </div>
- <div class="table-responsive">
- <table class="table table-sm table-hover align-middle mb-0" id="sites-table">
- <thead>
- <tr>
- <th>名称</th>
- <th>类型</th>
- <th>域名</th>
- <th>目录</th>
- <th>证书到期</th>
- <th>状态</th>
- <th class="text-end">操作</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
- </div>
- </section>
- <section class="view" id="view-files" data-view-panel="files">
- <div class="card">
- <div class="card-header py-2">
- <div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
- <div>
- <strong>文件</strong>
- </div>
- <div class="d-flex flex-wrap align-items-center gap-2">
- <div class="btn-group btn-group-sm" id="files-bulk-actions" hidden>
- <span class="btn btn-outline-secondary disabled" id="files-selected-count">已选 0</span>
- <button type="button" class="btn btn-outline-danger" id="btn-files-delete-selected">删除选中</button>
- <button type="button" class="btn btn-outline-secondary" id="btn-files-clear-selection">取消选择</button>
- </div>
- <div class="btn-group btn-group-sm">
- <button type="button" class="btn btn-outline-secondary" id="btn-files-up">上级</button>
- <button type="button" class="btn btn-outline-secondary" id="btn-files-mkdir">新建目录</button>
- <button type="button" class="btn btn-outline-secondary" id="btn-files-newfile">新建文件</button>
- <button type="button" class="btn btn-primary" id="btn-files-upload">上传</button>
- </div>
- </div>
- </div>
- <div class="crumbs small mt-2" id="files-crumbs"></div>
- </div>
- <div class="table-responsive">
- <table class="table table-sm table-hover align-middle mb-0" id="files-table">
- <thead>
- <tr>
- <th class="files-check-col">
- <input class="form-check-input" type="checkbox" id="files-select-all" title="全选" aria-label="全选" />
- </th>
- <th>名称</th>
- <th>类型</th>
- <th>大小</th>
- <th>修改时间</th>
- <th class="text-end">操作</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
- </div>
- </section>
- <section class="view" id="view-mysql" data-view-panel="mysql">
- <div class="card">
- <div class="card-header py-2 d-flex justify-content-between align-items-center">
- <div>
- <strong>数据库</strong>
- <span class="small text-secondary ms-2">库 / 账号 / 权限</span>
- </div>
- <div class="btn-group btn-group-sm">
- <button type="button" class="btn btn-outline-secondary" id="btn-mysql-password">改 root 密码</button>
- <button type="button" class="btn btn-primary" id="btn-db-create">新建数据库</button>
- </div>
- </div>
- <div class="table-responsive">
- <table class="table table-sm table-hover align-middle mb-0" id="db-table">
- <thead>
- <tr>
- <th>库名</th>
- <th>用户</th>
- <th>密码</th>
- <th>权限</th>
- <th>Host</th>
- <th class="text-end">操作</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
- </div>
- </section>
- <section class="view" id="view-processes" data-view-panel="processes">
- <div class="card">
- <div class="card-header py-2">
- <div class="d-flex justify-content-between align-items-center gap-2 proc-toolbar">
- <div class="proc-toolbar-title text-nowrap">
- <strong>进程</strong>
- <span class="small text-secondary ms-2" id="proc-count">—</span>
- </div>
- <div class="d-flex flex-nowrap align-items-center gap-2 proc-toolbar-actions">
- <input class="form-control form-control-sm" id="proc-search" type="search" placeholder="搜索 PID / 用户 / 名称 / 命令" />
- <div class="form-check form-switch mb-0 text-nowrap">
- <input class="form-check-input" type="checkbox" id="proc-auto" />
- <label class="form-check-label small" for="proc-auto">自动刷新</label>
- </div>
- <button type="button" class="btn btn-sm btn-outline-secondary text-nowrap" id="btn-proc-refresh">刷新</button>
- </div>
- </div>
- </div>
- <div class="table-responsive proc-table-wrap">
- <table class="table table-sm table-hover align-middle mb-0" id="proc-table">
- <thead>
- <tr>
- <th data-proc-sort="pid" class="proc-sort">PID</th>
- <th data-proc-sort="user" class="proc-sort">用户</th>
- <th data-proc-sort="cpu_percent" class="proc-sort is-active">CPU%</th>
- <th data-proc-sort="mem_percent" class="proc-sort">内存%</th>
- <th data-proc-sort="rss" class="proc-sort">RSS</th>
- <th>状态</th>
- <th data-proc-sort="name" class="proc-sort">名称</th>
- <th>命令行</th>
- <th class="text-end">操作</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
- </div>
- </section>
- <section class="view" id="view-software" data-view-panel="software">
- <div class="row g-2" id="software-grid"></div>
- </section>
- </main>
- </div>
- <div class="toast-host position-fixed bottom-0 end-0 p-3" id="toast-host" aria-live="polite"></div>
- <div class="task-panel" id="task-panel" hidden>
- <div class="task-panel-backdrop" id="task-panel-backdrop"></div>
- <div class="task-panel-dialog" role="dialog" aria-modal="true" aria-labelledby="task-panel-title">
- <header class="task-panel-head d-flex justify-content-between align-items-start gap-2">
- <div>
- <h2 class="h5 mb-1" id="task-panel-title">任务队列</h2>
- <p class="small text-secondary mb-0">默认显示进行中任务,可切换查看历史。</p>
- </div>
- <button type="button" class="btn btn-sm btn-outline-light" id="task-panel-close" aria-label="关闭">×</button>
- </header>
- <div class="task-panel-body">
- <div class="task-side">
- <div class="task-side-tabs btn-group btn-group-sm w-100 px-2 pt-2">
- <button type="button" class="btn btn-warning" id="task-tab-active" data-task-tab="active">进行中</button>
- <button type="button" class="btn btn-outline-light" id="task-tab-history" data-task-tab="history">历史任务</button>
- </div>
- <div class="task-list" id="task-list"></div>
- <div class="task-side-foot px-2 pb-2">
- <button type="button" class="btn btn-sm btn-outline-light w-100 d-none" id="task-clear-finished">清理历史</button>
- </div>
- </div>
- <div class="task-log-wrap">
- <div class="task-log-head d-flex justify-content-between align-items-center">
- <strong id="task-log-title">日志</strong>
- <span class="badge text-bg-secondary" id="task-log-status">—</span>
- </div>
- <pre class="task-log" id="task-log"></pre>
- </div>
- </div>
- <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
- </div>
- </div>
- <div class="modal fade" id="modal" tabindex="-1" aria-hidden="true">
- <div class="modal-dialog modal-dialog-scrollable">
- <form class="modal-content" id="modal-form">
- <div class="modal-header py-2">
- <h5 class="modal-title" id="modal-title">操作</h5>
- <button type="button" class="btn-close" id="modal-close" aria-label="关闭"></button>
- </div>
- <div class="modal-body" id="modal-body"></div>
- <div class="modal-footer py-2">
- <button type="button" class="btn btn-sm btn-outline-secondary" id="modal-cancel">取消</button>
- <button type="submit" class="btn btn-sm btn-primary" id="modal-ok">确定</button>
- </div>
- <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
- </form>
- </div>
- </div>
- <div class="modal fade" id="site-settings-modal" tabindex="-1" aria-hidden="true">
- <div class="modal-dialog modal-xl modal-dialog-scrollable">
- <div class="modal-content">
- <div class="modal-header py-2">
- <div>
- <h5 class="modal-title mb-0">网站配置 · <span id="site-settings-name"></span></h5>
- <div class="small text-secondary" id="site-settings-sub"></div>
- </div>
- <button type="button" class="btn-close" id="site-settings-close" aria-label="关闭"></button>
- </div>
- <div class="modal-body site-settings-body p-0">
- <div class="site-settings-layout">
- <aside class="site-settings-nav">
- <button type="button" class="site-settings-tab is-active" data-site-tab="domain">域名</button>
- <button type="button" class="site-settings-tab d-none" data-site-tab="proxy" id="site-tab-proxy">反向代理</button>
- <button type="button" class="site-settings-tab" data-site-tab="cert">证书</button>
- <button type="button" class="site-settings-tab" data-site-tab="nginx">配置 (NGINX)</button>
- <button type="button" class="site-settings-tab" data-site-tab="logs">日志 (NGINX)</button>
- <button type="button" class="site-settings-tab d-none" data-site-tab="modules" id="site-tab-modules">模块</button>
- </aside>
- <div class="site-settings-panels">
- <div class="site-settings-panel is-active" data-site-panel="domain">
- <h6 class="mb-3">域名</h6>
- <div class="mb-3">
- <label class="form-label">域名(支持多个,每行一个)</label>
- <textarea class="form-control form-control-sm" id="site-set-domain" rows="4" placeholder="example.com www.example.com:8080"></textarea>
- <div class="form-text">可写 <code>域名</code> 或 <code>域名:端口</code>;不写端口默认 <code>80</code>。第一行作为主域名。</div>
- </div>
- <button type="button" class="btn btn-sm btn-primary" id="site-set-domain-save">保存并重载</button>
- </div>
- <div class="site-settings-panel" data-site-panel="proxy">
- <div class="d-flex justify-content-between align-items-center mb-3">
- <h6 class="mb-0">反向代理</h6>
- <button type="button" class="btn btn-sm btn-primary" id="site-proxy-add">添加 URL 代理</button>
- </div>
- <div class="table-responsive">
- <table class="table table-sm table-hover align-middle mb-0" id="site-proxy-table">
- <thead>
- <tr>
- <th>代理目录</th>
- <th>目标</th>
- <th>发送域名</th>
- <th>备注</th>
- <th class="text-end">操作</th>
- </tr>
- </thead>
- <tbody id="site-proxy-tbody">
- <tr><td colspan="5" class="text-secondary text-center py-3">加载中…</td></tr>
- </tbody>
- </table>
- </div>
- </div>
- <div class="site-settings-panel" data-site-panel="cert">
- <div class="btn-group btn-group-sm mb-3" role="group" aria-label="证书子页">
- <button type="button" class="btn btn-outline-primary active" id="site-cert-tab-pem" data-cert-tab="pem">证书内容</button>
- <button type="button" class="btn btn-outline-primary" id="site-cert-tab-apply" data-cert-tab="apply">申请 SSL</button>
- </div>
- <div id="site-cert-pane-pem">
- <div class="form-check form-switch mb-3">
- <input class="form-check-input" type="checkbox" id="site-set-ssl-enable" />
- <label class="form-check-label" for="site-set-ssl-enable">启用 HTTPS</label>
- </div>
- <div class="small mb-3" id="site-set-ssl-expire">证书有效期:—</div>
- <div class="mb-3">
- <label class="form-label">HTTPS 端口</label>
- <input class="form-control form-control-sm" id="site-set-ssl-port" type="number" min="1" max="65535" value="443" />
- </div>
- <div class="mb-3">
- <label class="form-label">证书 PEM <span class="text-secondary fw-normal">(fullchain)</span></label>
- <textarea class="form-control font-monospace site-ssl-pem" id="site-set-ssl-pem" rows="8" spellcheck="false" placeholder="-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----"></textarea>
- </div>
- <div class="mb-3">
- <label class="form-label">私钥 KEY</label>
- <textarea class="form-control font-monospace site-ssl-pem" id="site-set-ssl-key-pem" rows="8" spellcheck="false" placeholder="-----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----"></textarea>
- </div>
- <p class="small text-secondary mb-3">保存后写入 <code>/ngs/conf/ssl/<域名>/fullchain.pem</code> 与 <code>privkey.pem</code> 并重载 Nginx。</p>
- <button type="button" class="btn btn-sm btn-primary" id="site-set-cert-save">保存并重载</button>
- </div>
- <div id="site-cert-pane-apply" hidden>
- <div class="fw-semibold mb-2">Let's Encrypt 免费证书</div>
- <p class="small text-secondary mb-2">勾选域名后合并签发到一张证书,成功后自动写入 <code>/ngs/conf/ssl/<主域名>/</code> 并启用 HTTPS。域名需已解析到本机,且公网可访问 80 端口。</p>
- <div class="mb-2">
- <div class="d-flex justify-content-between align-items-center mb-1">
- <label class="form-label mb-0">申请域名</label>
- <div class="btn-group btn-group-sm">
- <button type="button" class="btn btn-outline-secondary" id="site-ssl-domains-all">全选</button>
- <button type="button" class="btn btn-outline-secondary" id="site-ssl-domains-none">清空</button>
- </div>
- </div>
- <div class="site-ssl-domain-list border rounded bg-white p-2" id="site-ssl-domain-list"></div>
- </div>
- <button type="button" class="btn btn-sm btn-success" id="site-set-ssl-apply">申请 / 续签证书</button>
- </div>
- </div>
- <div class="site-settings-panel" data-site-panel="nginx">
- <div class="d-flex justify-content-between align-items-center mb-2 site-nginx-toolbar">
- <h6 class="mb-0">Nginx 站点配置</h6>
- <code class="small text-secondary text-truncate" id="site-set-conf-path"></code>
- </div>
- <div id="site-nginx-monaco" class="site-nginx-monaco" aria-label="Nginx 配置编辑器"></div>
- <div class="mt-2 d-flex gap-2 site-nginx-actions">
- <button type="button" class="btn btn-sm btn-primary" id="site-set-nginx-save">保存并重载</button>
- <button type="button" class="btn btn-sm btn-outline-secondary" id="site-set-nginx-reload">重新加载文件</button>
- </div>
- </div>
- <div class="site-settings-panel" data-site-panel="logs">
- <div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-2 site-log-toolbar">
- <div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1">
- <div class="btn-group btn-group-sm" role="group">
- <button type="button" class="btn btn-outline-primary active" id="site-log-tab-access" data-log-kind="access">访问日志</button>
- <button type="button" class="btn btn-outline-primary" id="site-log-tab-error" data-log-kind="error">error.log</button>
- </div>
- <input type="search" class="form-control form-control-sm site-log-search" id="site-log-search"
- placeholder="搜索 IP / URI / 状态 / UA…" autocomplete="off" />
- <span class="badge text-bg-light border site-log-ip-filter" id="site-log-ip-filter" hidden>
- IP:<code id="site-log-ip-value"></code>
- <button type="button" class="btn-close btn-close-sm ms-1" id="site-log-ip-clear" aria-label="清除 IP 筛选"></button>
- </span>
- </div>
- <button type="button" class="btn btn-sm btn-outline-secondary" id="site-log-refresh">刷新</button>
- </div>
- <div class="site-log-view" id="site-log-view">加载中…</div>
- </div>
- <div class="site-settings-panel" data-site-panel="modules">
- <div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3">
- <div>
- <h6 class="mb-1">Fastweb 扩展模块</h6>
- <div class="small text-secondary">列表来自 <code>download.fwlua.com/module/module.json</code>,通过 <code>fastweb instmod</code> 安装到当前站点。</div>
- </div>
- <button type="button" class="btn btn-sm btn-outline-secondary" id="site-modules-refresh">刷新</button>
- </div>
- <div class="btn-group btn-group-sm mb-3" role="group" id="site-modules-filter">
- <button type="button" class="btn btn-outline-primary active" data-mod-filter="all">全部</button>
- <button type="button" class="btn btn-outline-primary" data-mod-filter="fastweb">FastWeb</button>
- <button type="button" class="btn btn-outline-primary" data-mod-filter="lua">Lua</button>
- </div>
- <div id="site-modules-list" class="site-modules-list">加载中…</div>
- </div>
- </div>
- </div>
- </div>
- <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
- </div>
- </div>
- </div>
- <div class="modal fade" id="upload-modal" tabindex="-1" aria-hidden="true">
- <div class="modal-dialog modal-lg modal-dialog-scrollable">
- <div class="modal-content">
- <div class="modal-header py-2">
- <div>
- <h5 class="modal-title mb-0">上传到当前目录</h5>
- <code class="small text-secondary" id="upload-target-path">/</code>
- </div>
- <button type="button" class="btn-close" id="upload-modal-close" aria-label="关闭"></button>
- </div>
- <div class="modal-body">
- <div class="upload-dropzone" id="upload-dropzone" tabindex="0">
- <div class="upload-dropzone-inner">
- <i class="bi bi-cloud-arrow-up upload-drop-icon"></i>
- <p class="mb-1">拖拽文件或文件夹到此处</p>
- <p class="small text-secondary mb-3">也可使用下方按钮选择,选完后统一上传</p>
- <div class="btn-group btn-group-sm">
- <button type="button" class="btn btn-outline-primary" id="upload-pick-files">选择文件</button>
- <button type="button" class="btn btn-outline-primary" id="upload-pick-dir">选择目录</button>
- </div>
- </div>
- <input type="file" id="upload-input-files" hidden multiple />
- <input type="file" id="upload-input-dir" hidden webkitdirectory multiple />
- </div>
- <div class="d-flex justify-content-between align-items-center mt-3 mb-2">
- <span class="small text-secondary" id="upload-queue-summary">未选择文件</span>
- <button type="button" class="btn btn-sm btn-link text-decoration-none p-0" id="upload-clear-queue" hidden>清空列表</button>
- </div>
- <div class="upload-queue" id="upload-queue"></div>
- <div class="upload-progress mt-3" id="upload-progress" hidden>
- <div class="d-flex justify-content-between small mb-1">
- <span id="upload-progress-label">准备上传…</span>
- <span id="upload-progress-pct">0%</span>
- </div>
- <div class="progress" style="height: 8px;">
- <div class="progress-bar bg-teal" id="upload-progress-bar" role="progressbar" style="width: 0%"></div>
- </div>
- </div>
- </div>
- <div class="modal-footer py-2">
- <button type="button" class="btn btn-sm btn-outline-secondary" id="upload-modal-cancel">取消</button>
- <button type="button" class="btn btn-sm btn-primary" id="upload-start" disabled>开始上传</button>
- </div>
- <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
- </div>
- </div>
- </div>
- <div class="modal fade" id="site-analytics-modal" tabindex="-1" aria-hidden="true">
- <div class="modal-dialog modal-xl modal-dialog-scrollable">
- <div class="modal-content">
- <div class="modal-header py-2">
- <div class="d-flex align-items-center gap-2 flex-wrap">
- <h5 class="modal-title mb-0">日志分析 · <span id="analytics-site-name">—</span></h5>
- <select class="form-select form-select-sm" id="analytics-range" style="width:auto">
- <option value="1h">近 1 小时</option>
- <option value="6h">近 6 小时</option>
- <option value="24h" selected>近 24 小时</option>
- <option value="7d">近 7 天</option>
- <option value="30d">近 30 天</option>
- <option value="all">全部</option>
- </select>
- </div>
- <button type="button" class="btn-close" id="analytics-close" aria-label="关闭"></button>
- </div>
- <div class="modal-body" id="analytics-body">
- <div class="text-secondary small">加载中…</div>
- </div>
- <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
- </div>
- </div>
- </div>
- <div class="code-editor" id="code-editor" hidden>
- <div class="code-editor-backdrop" id="code-editor-backdrop"></div>
- <div class="code-editor-window">
- <header class="code-editor-bar">
- <div class="code-editor-meta">
- <strong id="code-editor-mode">编辑器</strong>
- <span id="code-editor-path"></span>
- </div>
- <button type="button" class="code-editor-x" id="code-editor-cancel" title="关闭" aria-label="关闭">×</button>
- </header>
- <div class="code-editor-main">
- <aside class="code-editor-tree">
- <div class="code-editor-tree-head">
- <button type="button" class="btn btn-sm btn-outline-light" id="editor-tree-up" title="上级">↑</button>
- <span class="code-editor-tree-path" id="editor-tree-path">/</span>
- <button type="button" class="btn btn-sm btn-outline-light" id="editor-tree-refresh" title="刷新">↻</button>
- </div>
- <div class="code-editor-tree-body" id="editor-tree"></div>
- </aside>
- <div class="code-editor-workspace">
- <div class="code-editor-tabs" id="editor-tabs"></div>
- <div id="monaco-host" class="monaco-host"></div>
- </div>
- </div>
- <div class="dialog-resize" data-dialog-resize title="拖动调整大小" aria-hidden="true"></div>
- </div>
- </div>
- <script src="/assets/bootstrap/js/bootstrap.bundle.min.js"></script>
- <script src="/assets/monaco/vs/loader.js"></script>
- <script src="/assets/app.js"></script>
- </body>
- </html>
|