diff options
| author | Chris Lu <chrislusf@users.noreply.github.com> | 2025-07-01 01:28:09 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-01 01:28:09 -0700 |
| commit | 1defee3d682d86c7e0cbc7db7ebdb9cae872a471 (patch) | |
| tree | dae266bee79c36a74214a47d3d9e9274b322d49d /weed/admin/view/app/admin.templ | |
| parent | e5adc3872a79e062826a387e1e2bb68196f14014 (diff) | |
| download | seaweedfs-1defee3d682d86c7e0cbc7db7ebdb9cae872a471.tar.xz seaweedfs-1defee3d682d86c7e0cbc7db7ebdb9cae872a471.zip | |
Add admin component (#6928)
* init version
* relocate
* add s3 bucket link
* refactor handlers into weed/admin folder
* fix login logout
* adding favicon
* remove fall back to http get topology
* grpc dial option, disk total capacity
* show filer count
* fix each volume disk usage
* add filers to dashboard
* adding hosts, volumes, collections
* refactor code and menu
* remove "refresh" button
* fix data for collections
* rename cluster hosts into volume servers
* add masters, filers
* reorder
* adding file browser
* create folder and upload files
* add filer version, created at time
* remove mock data
* remove fields
* fix submenu item highlighting
* fix bucket creation
* purge files
* delete multiple
* fix bucket creation
* remove region from buckets
* add object store with buckets and users
* rendering permission
* refactor
* get bucket objects and size
* link to file browser
* add file size and count for collections page
* paginate the volumes
* fix possible SSRF
https://github.com/seaweedfs/seaweedfs/pull/6928/checks?check_run_id=45108469801
* Update weed/command/admin.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update weed/command/admin.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix build
* import
* remove filer CLI option
* remove filer option
* remove CLI options
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Diffstat (limited to 'weed/admin/view/app/admin.templ')
| -rw-r--r-- | weed/admin/view/app/admin.templ | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/weed/admin/view/app/admin.templ b/weed/admin/view/app/admin.templ new file mode 100644 index 000000000..ceb11b0f2 --- /dev/null +++ b/weed/admin/view/app/admin.templ @@ -0,0 +1,351 @@ +package app + +import ( + "fmt" + "github.com/seaweedfs/seaweedfs/weed/admin/dash" +) + +templ Admin(data dash.AdminData) { + <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> + <h1 class="h2"> + <i class="fas fa-tachometer-alt me-2"></i>Dashboard + </h1> + <div class="btn-toolbar mb-2 mb-md-0"> + <div class="btn-group me-2"> + <a href="/s3/buckets" class="btn btn-sm btn-primary"> + <i class="fas fa-cube me-1"></i>S3 Buckets + </a> + + </div> + </div> + </div> + + <div id="dashboard-content"> + <!-- Status Cards --> + <div class="row mb-4"> + <div class="col-xl-3 col-md-6 mb-4"> + <div class="card border-left-primary shadow h-100 py-2"> + <div class="card-body"> + <div class="row no-gutters align-items-center"> + <div class="col mr-2"> + <div class="text-xs font-weight-bold text-primary text-uppercase mb-1"> + Cluster Status + </div> + <div class="h5 mb-0 font-weight-bold text-gray-800"> + <span class={fmt.Sprintf("badge bg-%s", getStatusColor(data.ClusterStatus))}> + {data.ClusterStatus} + </span> + </div> + </div> + <div class="col-auto"> + <i class="fas fa-heartbeat fa-2x text-gray-300"></i> + </div> + </div> + </div> + </div> + </div> + + <div class="col-xl-3 col-md-6 mb-4"> + <div class="card border-left-success shadow h-100 py-2"> + <div class="card-body"> + <div class="row no-gutters align-items-center"> + <div class="col mr-2"> + <div class="text-xs font-weight-bold text-success text-uppercase mb-1"> + Total Volumes + </div> + <div class="h5 mb-0 font-weight-bold text-gray-800"> + {fmt.Sprintf("%d", data.TotalVolumes)} + </div> + </div> + <div class="col-auto"> + <i class="fas fa-database fa-2x text-gray-300"></i> + </div> + </div> + </div> + </div> + </div> + + <div class="col-xl-3 col-md-6 mb-4"> + <div class="card border-left-info shadow h-100 py-2"> + <div class="card-body"> + <div class="row no-gutters align-items-center"> + <div class="col mr-2"> + <div class="text-xs font-weight-bold text-info text-uppercase mb-1"> + Total Files + </div> + <div class="h5 mb-0 font-weight-bold text-gray-800"> + {formatNumber(data.TotalFiles)} + </div> + </div> + <div class="col-auto"> + <i class="fas fa-file fa-2x text-gray-300"></i> + </div> + </div> + </div> + </div> + </div> + + <div class="col-xl-3 col-md-6 mb-4"> + <div class="card border-left-warning shadow h-100 py-2"> + <div class="card-body"> + <div class="row no-gutters align-items-center"> + <div class="col mr-2"> + <div class="text-xs font-weight-bold text-warning text-uppercase mb-1"> + Total Size + </div> + <div class="h5 mb-0 font-weight-bold text-gray-800"> + {formatBytes(data.TotalSize)} + </div> + </div> + <div class="col-auto"> + <i class="fas fa-hdd fa-2x text-gray-300"></i> + </div> + </div> + </div> + </div> + </div> + </div> + + <!-- Master Nodes Status --> + <div class="row mb-4"> + <div class="col-lg-6"> + <div class="card shadow mb-4"> + <div class="card-header py-3"> + <h6 class="m-0 font-weight-bold text-primary"> + <i class="fas fa-server me-2"></i>Master Nodes + </h6> + </div> + <div class="card-body"> + <div class="table-responsive"> + <table class="table table-bordered" width="100%" cellspacing="0"> + <thead> + <tr> + <th>Address</th> + <th>Role</th> + <th>Status</th> + </tr> + </thead> + <tbody> + for _, master := range data.MasterNodes { + <tr> + <td>{master.Address}</td> + <td> + if master.IsLeader { + <span class="badge bg-primary">Leader</span> + } else { + <span class="badge bg-secondary">Follower</span> + } + </td> + <td> + <span class={fmt.Sprintf("badge bg-%s", getStatusColor(master.Status))}> + {master.Status} + </span> + </td> + </tr> + } + </tbody> + </table> + </div> + </div> + </div> + </div> + + <!-- System Health --> + <div class="col-lg-6"> + <div class="card shadow mb-4"> + <div class="card-header py-3"> + <h6 class="m-0 font-weight-bold text-primary"> + <i class="fas fa-chart-pie me-2"></i>System Health + </h6> + </div> + <div class="card-body text-center"> + <div class="mb-3"> + <h3 class={fmt.Sprintf("text-%s", getHealthColor(data.SystemHealth))}> + {data.SystemHealth} + </h3> + </div> + <div class="row"> + <div class="col-4"> + <div class="card bg-light"> + <div class="card-body"> + <h5>{fmt.Sprintf("%d", len(data.MasterNodes))}</h5> + <small class="text-muted">Masters</small> + </div> + </div> + </div> + <div class="col-4"> + <div class="card bg-light"> + <div class="card-body"> + <h5>{fmt.Sprintf("%d", len(data.VolumeServers))}</h5> + <small class="text-muted">Volume Servers</small> + </div> + </div> + </div> + <div class="col-4"> + <div class="card bg-light"> + <div class="card-body"> + <h5>{fmt.Sprintf("%d", len(data.FilerNodes))}</h5> + <small class="text-muted">Filers</small> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + + <!-- Volume Servers --> + <div class="row"> + <div class="col-12"> + <div class="card shadow mb-4"> + <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between"> + <h6 class="m-0 font-weight-bold text-primary"> + <i class="fas fa-database me-2"></i>Volume Servers + </h6> + <div class="dropdown no-arrow"> + <a class="dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"> + <i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i> + </a> + <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"> + <div class="dropdown-header">Actions:</div> + <a class="dropdown-item" href="/volumes">View Details</a> + <a class="dropdown-item" href="/cluster">Topology View</a> + </div> + </div> + </div> + <div class="card-body"> + <div class="table-responsive"> + <table class="table table-hover" width="100%" cellspacing="0"> + <thead> + <tr> + <th>ID</th> + <th>Address</th> + <th>Data Center</th> + <th>Rack</th> + <th>Volumes</th> + <th>Capacity</th> + <th>Status</th> + </tr> + </thead> + <tbody> + for _, vs := range data.VolumeServers { + <tr> + <td>{vs.ID}</td> + <td> + <a href={templ.SafeURL(fmt.Sprintf("http://%s", vs.PublicURL))} target="_blank"> + {vs.Address} + <i class="fas fa-external-link-alt ms-1 text-muted"></i> + </a> + </td> + <td>{vs.DataCenter}</td> + <td>{vs.Rack}</td> + <td> + <div class="progress" style="height: 20px;"> + <div class="progress-bar" role="progressbar" + style={fmt.Sprintf("width: %d%%", calculatePercent(vs.Volumes, vs.MaxVolumes))}> + {fmt.Sprintf("%d/%d", vs.Volumes, vs.MaxVolumes)} + </div> + </div> + </td> + <td>{formatBytes(vs.DiskUsage)} / {formatBytes(vs.DiskCapacity)}</td> + <td> + <span class={fmt.Sprintf("badge bg-%s", getStatusColor(vs.Status))}> + {vs.Status} + </span> + </td> + </tr> + } + if len(data.VolumeServers) == 0 { + <tr> + <td colspan="7" class="text-center text-muted py-4"> + <i class="fas fa-info-circle me-2"></i> + No volume servers found + </td> + </tr> + } + </tbody> + </table> + </div> + </div> + </div> + </div> + </div> + + <!-- Filer Nodes --> + <div class="row mb-4"> + <div class="col-12"> + <div class="card shadow mb-4"> + <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between"> + <h6 class="m-0 font-weight-bold text-primary"> + <i class="fas fa-folder me-2"></i>Filer Nodes + </h6> + <div class="dropdown no-arrow"> + <a class="dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"> + <i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i> + </a> + <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"> + <div class="dropdown-header">Actions:</div> + <a class="dropdown-item" href="/filer">File Browser</a> + <a class="dropdown-item" href="/cluster">Topology View</a> + </div> + </div> + </div> + <div class="card-body"> + <div class="table-responsive"> + <table class="table table-hover" width="100%" cellspacing="0"> + <thead> + <tr> + <th>Address</th> + <th>Data Center</th> + <th>Rack</th> + <th>Status</th> + <th>Last Updated</th> + </tr> + </thead> + <tbody> + for _, filer := range data.FilerNodes { + <tr> + <td> + <a href={templ.SafeURL(fmt.Sprintf("http://%s", filer.Address))} target="_blank"> + {filer.Address} + <i class="fas fa-external-link-alt ms-1 text-muted"></i> + </a> + </td> + <td>{filer.DataCenter}</td> + <td>{filer.Rack}</td> + <td> + <span class={fmt.Sprintf("badge bg-%s", getStatusColor(filer.Status))}> + {filer.Status} + </span> + </td> + <td>{filer.LastUpdated.Format("2006-01-02 15:04:05")}</td> + </tr> + } + if len(data.FilerNodes) == 0 { + <tr> + <td colspan="5" class="text-center text-muted py-4"> + <i class="fas fa-info-circle me-2"></i> + No filer nodes found + </td> + </tr> + } + </tbody> + </table> + </div> + </div> + </div> + </div> + </div> + + <!-- Last Updated --> + <div class="row"> + <div class="col-12"> + <small class="text-muted"> + <i class="fas fa-clock me-1"></i> + Last updated: {data.LastUpdated.Format("2006-01-02 15:04:05")} + </small> + </div> + </div> + </div> +}
\ No newline at end of file |
