From 2e2e804a499547d1b5b1a57820a176ad79bbfca3 Mon Sep 17 00:00:00 2001 From: xuans Date: Sun, 27 Sep 2026 15:22:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=89=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=20SSH=20=E5=AF=86=E9=92=A5=E4=B8=8E=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 SSH 密钥主页面改为按部署服务器展示,增加密钥部署状态、指纹、测试时间、错误提示和服务器专属 SSH 命令,并优化响应式布局。 --- frontend/src/views/Dashboard/Dashboard.css | 144 +++++++++++++++++++++ frontend/src/views/Dashboard/Dashboard.js | 48 +++++++ frontend/src/views/Dashboard/Dashboard.vue | 105 +++++++++++---- 3 files changed, 274 insertions(+), 23 deletions(-) diff --git a/frontend/src/views/Dashboard/Dashboard.css b/frontend/src/views/Dashboard/Dashboard.css index 622d4b0..a7141e4 100644 --- a/frontend/src/views/Dashboard/Dashboard.css +++ b/frontend/src/views/Dashboard/Dashboard.css @@ -429,6 +429,150 @@ code { background: #fff0f0; color: #ba5555 !important; } +.tag.neutral { + background: #eef2f7; + color: #63748c !important; +} + +.ssh-server-list { + display: grid; + gap: 14px; +} + +.ssh-server-card { + display: grid; + gap: 14px; + padding: 18px; + border: 1px solid #dfe7f1; + border-radius: 16px; + background: linear-gradient(135deg, #ffffff, #f8fbff); +} + +.ssh-server-card-heading, +.ssh-orphaned-heading, +.ssh-server-command { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +.ssh-server-meta { + display: flex; + flex-wrap: wrap; + gap: 8px 18px; + color: #718198; + font-size: 12px; +} + +.ssh-server-details { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; +} + +.ssh-server-detail { + display: grid; + gap: 5px; + min-width: 0; + padding: 12px; + border: 1px solid #e4ebf4; + border-radius: 12px; + background: #fff; + color: #718198; + font-size: 12px; + line-height: 1.5; +} + +.ssh-server-detail strong { + color: #30435f; + font-size: 13px; +} + +.detail-label { + color: #8a98ab; + font-size: 11px; + letter-spacing: 0.04em; +} + +.ssh-server-detail code, +.ssh-server-command code { + overflow-wrap: anywhere; + color: #345f94; + font-family: ui-monospace, SFMono-Regular, Consolas, monospace; + font-size: 12px; +} + +.inline-error { + margin: 0; +} + +.ssh-server-command { + padding: 10px 12px; + border: 1px solid #d8e5f4; + border-radius: 12px; + background: #f3f8ff; +} + +.ssh-server-command > div { + display: grid; + gap: 5px; + min-width: 0; +} + +.ssh-orphaned-keys { + display: grid; + gap: 10px; + margin-top: 18px; + padding-top: 16px; + border-top: 1px solid #e7edf5; +} + +.ssh-orphaned-heading > div { + display: grid; + gap: 3px; +} + +.ssh-orphaned-list { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.ssh-orphaned-item { + display: inline-flex !important; + align-items: center; + gap: 8px; + padding: 7px 10px; + border: 1px solid #e1e8f1; + border-radius: 9px; + background: #fbfcfe; + color: #6f7f94; + font-size: 12px; +} + +@media (max-width: 900px) { + .ssh-server-details { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 640px) { + .ssh-server-card-heading, + .ssh-orphaned-heading, + .ssh-server-command { + align-items: stretch; + flex-direction: column; + } + + .ssh-server-details { + grid-template-columns: minmax(0, 1fr); + } + + .ssh-server-command .inline-button { + align-self: flex-start; + } +} .modal-backdrop { position: fixed; inset: 0; diff --git a/frontend/src/views/Dashboard/Dashboard.js b/frontend/src/views/Dashboard/Dashboard.js index 89b1327..6d6fb0f 100644 --- a/frontend/src/views/Dashboard/Dashboard.js +++ b/frontend/src/views/Dashboard/Dashboard.js @@ -516,6 +516,49 @@ export function useDashboard() { return labels[value] || (value ? "未知" : "—"); } + function serverSSHKey(server) { + if (!server?.private_key_ref) return null; + return sshKeys.value.find((item) => item.reference === server.private_key_ref) || null; + } + + function serverSSHKeyStatusLabel(server) { + const labels = { + not_configured: "未配置", + pending: "部署中", + deployed: "已部署并验证", + manual: "已配置,待验证", + failed: "部署失败,保留密码回退", + }; + return labels[server?.ssh_key_status] || "未配置"; + } + + function serverSSHKeyStatusClass(server) { + if (server?.ssh_key_status === "deployed") return "success"; + if (server?.ssh_key_status === "failed") return "danger"; + if (server?.ssh_key_status === "pending") return "warning"; + return "neutral"; + } + + function serverSSHCommand(server) { + if (!server?.address || !server?.username) return "—"; + const port = Number(server.port || 22); + const portOption = port === 22 ? "" : ` -p ${port}`; + return `ssh${portOption} ${server.username}@${server.address}`; + } + + function serverSSHKeyHint(server) { + if (server?.ssh_key_status === "deployed") { + return "Scert 已自动部署公钥并验证密钥登录,后续部署默认使用托管私钥。"; + } + if (server?.ssh_key_status === "failed") { + return server.ssh_key_error || "公钥部署或密钥登录验证失败,当前仍使用密码回退。"; + } + if (server?.auth_method === "private_key") { + return "当前使用已配置的私钥连接,尚未完成自动部署验证。"; + } + return "保存服务器后,Scert 会先用密码部署公钥,再自动验证密钥登录。"; + } + function formatDate(value) { if (!value) return "尚未签发"; return new Intl.DateTimeFormat("zh-CN", { @@ -1318,6 +1361,11 @@ export function useDashboard() { environmentLabels, webServerLabels, serverAuthLabel, + serverSSHKey, + serverSSHKeyStatusLabel, + serverSSHKeyStatusClass, + serverSSHCommand, + serverSSHKeyHint, isEditing, formTitle, form, diff --git a/frontend/src/views/Dashboard/Dashboard.vue b/frontend/src/views/Dashboard/Dashboard.vue index 26f7f33..e285612 100644 --- a/frontend/src/views/Dashboard/Dashboard.vue +++ b/frontend/src/views/Dashboard/Dashboard.vue @@ -58,6 +58,11 @@ const { dnsProviders, environmentLabels, webServerLabels, + serverSSHKey, + serverSSHKeyStatusLabel, + serverSSHKeyStatusClass, + serverSSHCommand, + serverSSHKeyHint, isEditing, formTitle, form, @@ -415,41 +420,92 @@ const { activeSection, activeLabel, sections, selectSection } = useDashboardNavi

安全凭据

-

SSH 私钥

+

服务器 SSH 密钥

- 共 {{ sshKeys.length }} 个加密引用 -
-
- 还没有 SSH 私钥私钥会加密保存,页面和接口只显示算法、指纹与安全引用。
-
-
-
+
+ 还没有部署服务器 + 添加服务器后,Scert 会自动生成密钥、部署公钥并验证密钥登录。 + +
+
+
+
- {{ item.name }}{{ item.algorithm }} + {{ item.name }} + {{ displayLabel(webServerLabels, item.web_server) }} + + {{ serverSSHKeyStatusLabel(item) }} +
-
- 公钥指纹:{{ item.fingerprint }}更新时间:{{ formatDate(item.updated_at) }} +
+ +
-
+
+ {{ item.username }}@{{ item.address }}:{{ item.port }} + 主机指纹:{{ item.host_key_fingerprint || "未填写" }} + 认证:{{ serverAuthLabel(item) }} +
+
+
+ 托管私钥 + {{ serverSSHKey(item)?.name || "尚未生成" }} + {{ serverSSHKey(item)?.fingerprint || item.ssh_key_fingerprint || "—" }} +
+
+ 公钥部署 + {{ serverSSHKeyStatusLabel(item) }} + {{ serverSSHKeyHint(item) }} +
+
+ 最近部署 / 测试 + {{ formatDate(item.ssh_key_deployed_at) }} / {{ formatDate(item.ssh_key_tested_at) }} + {{ item.ssh_key_error }} +
+
+
+
+ 服务器 SSH 命令 + {{ serverSSHCommand(item) }} +
+
+
+
+ 兼容入口 + 未关联服务器的私钥 +
+ +
+
+ + {{ item.name }} · {{ item.fingerprint }} + + +
+
@@ -473,11 +529,14 @@ const { activeSection, activeLabel, sections, selectSection } = useDashboardNavi
{{ item.name }}{{ displayLabel(webServerLabels, item.web_server) }} + > + {{ serverSSHKeyStatusLabel(item) }} +
{{ item.username }}@{{ item.address }}:{{ item.port }}私钥引用已配置{{ serverAuthLabel(item) }}{{ serverAuthLabel(item) }}托管私钥:{{ serverSSHKey(item)?.name || "尚未生成" }}指纹:{{ item.host_key_fingerprint }}