diff --git a/backend/internal/database/database.go b/backend/internal/database/database.go index e67ca6e..f098dc6 100644 --- a/backend/internal/database/database.go +++ b/backend/internal/database/database.go @@ -203,9 +203,9 @@ func seedResources() { if n == 0 { hb := time.Now().Add(-10 * time.Second) DB.Create([]model.BuildNode{ - {Name: "build-host-01", IP: "10.0.8.11", Labels: `["docker","go","java"]`, MaxConcurrency: 4, Status: "online", CurrentJobs: 1, LastHeartbeat: &hb}, - {Name: "build-host-02", IP: "10.0.8.12", Labels: `["docker","nodejs"]`, MaxConcurrency: 2, Status: "busy", CurrentJobs: 2, LastHeartbeat: &hb}, - {Name: "build-host-03", IP: "10.0.8.13", Labels: `["python"]`, MaxConcurrency: 2, Status: "offline", LastHeartbeat: &hb}, + {Name: "build-host-01", IP: "10.0.8.11", Labels: `["docker","go","java"]`, MaxConcurrency: 4, Status: "online", CurrentJobs: 1, DiskInfo: "78G/200G", LastHeartbeat: &hb}, + {Name: "build-host-02", IP: "10.0.8.12", Labels: `["docker","nodejs"]`, MaxConcurrency: 2, Status: "busy", CurrentJobs: 2, DiskInfo: "156G/500G", LastHeartbeat: &hb}, + {Name: "build-host-03", IP: "10.0.8.13", Labels: `["python"]`, MaxConcurrency: 2, Status: "offline", DiskInfo: "45G/100G", LastHeartbeat: &hb}, }) } DB.Model(&model.WebhookEndpoint{}).Count(&n) diff --git a/backend/internal/handler/resources.go b/backend/internal/handler/resources.go index 3ab17ae..f9c1b1f 100644 --- a/backend/internal/handler/resources.go +++ b/backend/internal/handler/resources.go @@ -394,7 +394,7 @@ func UpdateBuildNode(c *gin.Context) { } var raw map[string]any _ = c.ShouldBindJSON(&raw) - for _, k := range []string{"name", "ip", "labels", "maxConcurrency", "workspacePath", "enabled"} { + for _, k := range []string{"name", "ip", "labels", "maxConcurrency", "workspacePath", "diskInfo", "enabled"} { if v, ok := raw[k]; ok { database.DB.Model(&n).Update(k, v) } diff --git a/backend/internal/model/models.go b/backend/internal/model/models.go index 1107b5b..53b9991 100644 --- a/backend/internal/model/models.go +++ b/backend/internal/model/models.go @@ -204,6 +204,7 @@ type BuildNode struct { Labels string `gorm:"type:text" json:"labels"` // JSON ["docker","go"] MaxConcurrency int `gorm:"default:2" json:"maxConcurrency"` WorkspacePath string `gorm:"default:/data/flowstack-workspace" json:"workspacePath"` + DiskInfo string `json:"diskInfo"` // 如 "45G/200G" Enabled bool `gorm:"default:true" json:"enabled"` Status string `gorm:"default:offline" json:"status"` // online|offline|busy CurrentJobs int `gorm:"default:0" json:"currentJobs"` diff --git a/frontend/index.html b/frontend/index.html index ae53457..2947087 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,6 +3,7 @@ + FlowStack · 发布系统 diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..03a1a29 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/pages/BuildNodeManager.tsx b/frontend/src/pages/BuildNodeManager.tsx index 6d399be..4be2425 100644 --- a/frontend/src/pages/BuildNodeManager.tsx +++ b/frontend/src/pages/BuildNodeManager.tsx @@ -10,6 +10,7 @@ import { Trash2, Tag, Zap, + HardDrive, } from 'lucide-react'; import { toast } from 'sonner'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; @@ -60,9 +61,10 @@ interface NodeForm { labels: string; maxConcurrency: string; workspacePath: string; + diskInfo: string; } -const emptyForm: NodeForm = { name: '', ip: '', labels: '', maxConcurrency: '2', workspacePath: '/var/lib/flowstack/workspace' }; +const emptyForm: NodeForm = { name: '', ip: '', labels: '', maxConcurrency: '2', workspacePath: '/var/lib/flowstack/workspace', diskInfo: '' }; const inputStyle = { background: 'var(--panel-bg-secondary)', @@ -92,6 +94,7 @@ export default function BuildNodeManager() { labels: parseJsonArray(node.labels).join(','), maxConcurrency: String(node.maxConcurrency ?? 2), workspacePath: node.workspacePath ?? '', + diskInfo: node.diskInfo ?? '', } : emptyForm, ); @@ -152,6 +155,7 @@ export default function BuildNodeManager() { labels: JSON.stringify(splitComma(form.labels)), maxConcurrency: Number(form.maxConcurrency) || 1, workspacePath: form.workspacePath.trim(), + diskInfo: form.diskInfo.trim(), }, }); }; @@ -292,10 +296,16 @@ export default function BuildNodeManager() { - {/* Info: 工作空间 / 心跳(后端暂无 CPU/内存/磁盘数据) */} -
+ {/* Info: 磁盘 / 工作空间 / 心跳 */} +
- 工作空间 + + + {node.diskInfo || '—'} + +
+
+ 空间 {node.workspacePath || '—'} @@ -366,6 +376,10 @@ export default function BuildNodeManager() { setForm({ ...form, workspacePath: e.target.value })} placeholder="/var/lib/flowstack/workspace" />
+
+ + setForm({ ...form, diskInfo: e.target.value })} placeholder="如 45G/200G(已用/总量)" /> +