feat: redesign system page - service status with refresh, separate backup/restore cards
This commit is contained in:
parent
80955c95a0
commit
3ddffdc8f4
@ -1,36 +1,33 @@
|
|||||||
{{define "system"}}
|
{{define "system"}}
|
||||||
<div class="card">
|
|
||||||
<div class="crumb">系统管理 / 系统状态</div>
|
|
||||||
<div class="muted small" style="margin-top:8px">系统状态页负责平台健康检查和数据备份恢复。</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="detail-grid">
|
<div class="detail-grid">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2 class="title-with-icon">{{icon "heartbeat"}}<span>后台健康</span></h2>
|
<h2 class="title-with-icon">{{icon "heartbeat"}}<span>服务状态</span></h2>
|
||||||
<div class="info-list compact-list">
|
<div class="info-list compact-list">
|
||||||
<div><span>健康检查</span><strong class="mono">/health</strong></div>
|
<div><span>管理后台</span><strong id="svc-status">检测中…</strong></div>
|
||||||
<div><span>接口文档</span><strong class="mono">/openapi.json</strong></div>
|
<div><span>接口文档</span><strong id="api-status">检测中…</strong></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions" style="margin-top:12px">
|
<div class="actions" style="margin-top:12px">
|
||||||
<a class="btn ghost" href="/health">健康检查</a>
|
<button class="btn ghost" type="button" id="btn-refresh">刷新</button>
|
||||||
<a class="btn ghost" href="/openapi.json">OpenAPI</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="detail-grid">
|
<div class="detail-grid">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2 class="title-with-icon">{{icon "audit"}}<span>数据备份 / 恢复</span></h2>
|
<h2 class="title-with-icon">{{icon "audit"}}<span>数据备份</span></h2>
|
||||||
<div class="info-list compact-list">
|
<div class="info-list compact-list">
|
||||||
<div><span>数据库文件</span><strong class="mono">{{if .DBPath}}{{.DBPath}}{{else}}未配置{{end}}</strong></div>
|
<div><span>数据库文件</span><strong class="mono">{{if .DBPath}}{{.DBPath}}{{else}}未配置{{end}}</strong></div>
|
||||||
<div><span>备份方式</span><strong>另存为数据库文件</strong></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="actions" style="margin-top:12px">
|
<div class="actions" style="margin-top:12px">
|
||||||
<button type="button" class="btn ghost js-export-db" data-export-url="/ui/system/db-backup" data-default-filename="app.db">备份数据库</button>
|
<button type="button" class="btn ghost js-export-db" data-export-url="/ui/system/db-backup" data-default-filename="app.db">备份数据库</button>
|
||||||
</div>
|
</div>
|
||||||
<form method="post" action="/ui/system/db-restore" enctype="multipart/form-data" style="margin-top:12px">
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h2 class="title-with-icon">{{icon "apply"}}<span>数据恢复</span></h2>
|
||||||
|
<form method="post" action="/ui/system/db-restore" enctype="multipart/form-data">
|
||||||
<div class="field-grid">
|
<div class="field-grid">
|
||||||
<label class="full"><span>恢复数据库</span><input type="file" name="file" accept=".db,application/octet-stream" required /></label>
|
<label class="full"><span>选择备份文件</span><input type="file" name="file" accept=".db,application/octet-stream" required /></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions" style="margin-top:12px">
|
<div class="actions" style="margin-top:12px">
|
||||||
<button type="submit" class="secondary">恢复数据库</button>
|
<button type="submit" class="secondary">恢复数据库</button>
|
||||||
@ -38,4 +35,52 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var svcEl = document.getElementById("svc-status");
|
||||||
|
var apiEl = document.getElementById("api-status");
|
||||||
|
|
||||||
|
function check() {
|
||||||
|
svcEl.textContent = "检测中…";
|
||||||
|
svcEl.className = "";
|
||||||
|
apiEl.textContent = "检测中…";
|
||||||
|
apiEl.className = "";
|
||||||
|
|
||||||
|
fetch("/health")
|
||||||
|
.then(function(r) { return r.text(); })
|
||||||
|
.then(function(t) {
|
||||||
|
if (t.indexOf("ok") !== -1) {
|
||||||
|
svcEl.textContent = "运行正常";
|
||||||
|
svcEl.className = "ok";
|
||||||
|
} else {
|
||||||
|
svcEl.textContent = "异常";
|
||||||
|
svcEl.className = "bad";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
svcEl.textContent = "无法连接";
|
||||||
|
svcEl.className = "bad";
|
||||||
|
});
|
||||||
|
|
||||||
|
fetch("/openapi.json")
|
||||||
|
.then(function(r) {
|
||||||
|
if (r.ok) {
|
||||||
|
apiEl.textContent = "正常";
|
||||||
|
apiEl.className = "ok";
|
||||||
|
} else {
|
||||||
|
apiEl.textContent = "异常 (" + r.status + ")";
|
||||||
|
apiEl.className = "bad";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
apiEl.textContent = "无法连接";
|
||||||
|
apiEl.className = "bad";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("btn-refresh").addEventListener("click", check);
|
||||||
|
check();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@ -3414,7 +3414,7 @@ func TestUI_AuditAndSystemPagesDefineNewScopes(t *testing.T) {
|
|||||||
|
|
||||||
rrSystem := httptest.NewRecorder()
|
rrSystem := httptest.NewRecorder()
|
||||||
ui.pageSystem(rrSystem, httptest.NewRequest(http.MethodGet, "/ui/system", nil))
|
ui.pageSystem(rrSystem, httptest.NewRequest(http.MethodGet, "/ui/system", nil))
|
||||||
for _, want := range []string{"系统管理 / 系统状态", "后台健康", "数据备份 / 恢复", "/health", "/openapi.json"} {
|
for _, want := range []string{"服务状态", "数据备份", "数据恢复", "/health", "/openapi.json"} {
|
||||||
if !strings.Contains(rrSystem.Body.String(), want) {
|
if !strings.Contains(rrSystem.Body.String(), want) {
|
||||||
t.Fatalf("expected system HTML to contain %q", want)
|
t.Fatalf("expected system HTML to contain %q", want)
|
||||||
}
|
}
|
||||||
@ -3774,7 +3774,7 @@ func TestUI_SystemManagementPagesUseSystemManagementCrumb(t *testing.T) {
|
|||||||
path string
|
path string
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{path: "/ui/system", want: "系统管理 / 系统状态"},
|
{path: "/ui/system", want: "服务状态"},
|
||||||
{path: "/ui/audit", want: "系统管理 / 日志审计 / 审计记录"},
|
{path: "/ui/audit", want: "系统管理 / 日志审计 / 审计记录"},
|
||||||
{path: "/ui/api", want: "系统管理 / 日志审计 / 高级调试"},
|
{path: "/ui/api", want: "系统管理 / 日志审计 / 高级调试"},
|
||||||
} {
|
} {
|
||||||
@ -4480,7 +4480,7 @@ func TestUI_SystemPageShowsDatabaseBackupAction(t *testing.T) {
|
|||||||
|
|
||||||
html := renderPage(t, ui, "/ui/system")
|
html := renderPage(t, ui, "/ui/system")
|
||||||
for _, want := range []string{
|
for _, want := range []string{
|
||||||
"数据备份 / 恢复",
|
"数据备份",
|
||||||
"恢复数据库",
|
"恢复数据库",
|
||||||
`class="btn ghost js-export-db"`,
|
`class="btn ghost js-export-db"`,
|
||||||
`data-export-url="/ui/system/db-backup"`,
|
`data-export-url="/ui/system/db-backup"`,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user