You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							401 lines
						
					
					
						
							20 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							401 lines
						
					
					
						
							20 KiB
						
					
					
				| package layout | |
| 
 | |
| import ( | |
|     "fmt" | |
|     "strings" | |
|     "time" | |
|     "github.com/gin-gonic/gin" | |
|     "github.com/seaweedfs/seaweedfs/weed/util/version" | |
| ) | |
| 
 | |
| templ Layout(c *gin.Context, content templ.Component) { | |
| {{ | |
|   username := c.GetString("username") | |
|   if username == "" { | |
|     username = "admin" | |
|   } | |
|    | |
|   // Detect if we're on a configuration page to keep submenu expanded | |
|   currentPath := c.Request.URL.Path | |
|   isConfigPage := strings.HasPrefix(currentPath, "/maintenance/config") || currentPath == "/config" | |
|    | |
|   // Detect if we're on a message queue page to keep submenu expanded | |
|   isMQPage := strings.HasPrefix(currentPath, "/mq/") | |
| }} | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
|     <meta charset="UTF-8"> | |
|     <title>SeaweedFS Admin</title> | |
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | |
|     <link rel="icon" href="/static/favicon.ico" type="image/x-icon"> | |
|      | |
|     <!-- Bootstrap CSS --> | |
|     <link href="/static/css/bootstrap.min.css" rel="stylesheet"> | |
|     <!-- Font Awesome CSS --> | |
|     <link href="/static/css/fontawesome.min.css" rel="stylesheet"> | |
|     <!-- HTMX --> | |
|     <script src="/static/js/htmx.min.js"></script> | |
|     <!-- Custom CSS --> | |
|     <link rel="stylesheet" href="/static/css/admin.css"> | |
| </head> | |
| <body> | |
|     <div class="container-fluid"> | |
|         <!-- Header --> | |
|         <header class="navbar navbar-expand-lg navbar-dark bg-primary sticky-top"> | |
|             <div class="container-fluid"> | |
|                 <a class="navbar-brand fw-bold" href="/admin"> | |
|                     <i class="fas fa-server me-2"></i> | |
|                     SeaweedFS Admin | |
|                     <span class="badge bg-warning text-dark ms-2">ALPHA</span> | |
|                 </a> | |
|                  | |
|                 <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"> | |
|                     <span class="navbar-toggler-icon"></span> | |
|                 </button> | |
|                  | |
|                 <div class="collapse navbar-collapse" id="navbarNav"> | |
|                     <ul class="navbar-nav ms-auto"> | |
|                         <li class="nav-item dropdown"> | |
|                             <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"> | |
|                                 <i class="fas fa-user me-1"></i>{username} | |
|                             </a> | |
|                             <ul class="dropdown-menu"> | |
|                                 <li><a class="dropdown-item" href="/logout"> | |
|                                     <i class="fas fa-sign-out-alt me-2"></i>Logout | |
|                                 </a></li> | |
|                             </ul> | |
|                         </li> | |
|                     </ul> | |
|                 </div> | |
|             </div> | |
|         </header> | |
| 
 | |
|         <div class="row g-0"> | |
|             <!-- Sidebar --> | |
|             <div class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse"> | |
|                 <div class="position-sticky pt-3"> | |
|                     <h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted"> | |
|                         <span>MAIN</span> | |
|                     </h6> | |
|                     <ul class="nav flex-column"> | |
|                         <li class="nav-item"> | |
|                             <a class="nav-link" href="/admin"> | |
|                                 <i class="fas fa-tachometer-alt me-2"></i>Dashboard | |
|                             </a> | |
|                         </li> | |
|                         <li class="nav-item"> | |
|                             <a class="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#clusterSubmenu" aria-expanded="false" aria-controls="clusterSubmenu"> | |
|                                 <i class="fas fa-sitemap me-2"></i>Cluster | |
|                                 <i class="fas fa-chevron-down ms-auto"></i> | |
|                             </a> | |
|                             <div class="collapse" id="clusterSubmenu"> | |
|                                 <ul class="nav flex-column ms-3"> | |
|                                     <li class="nav-item"> | |
|                                         <a class="nav-link py-2" href="/cluster/masters"> | |
|                                             <i class="fas fa-crown me-2"></i>Masters | |
|                                         </a> | |
|                                     </li> | |
|                                     <li class="nav-item"> | |
|                                         <a class="nav-link py-2" href="/cluster/volume-servers"> | |
|                                             <i class="fas fa-server me-2"></i>Volume Servers | |
|                                         </a> | |
|                                     </li> | |
|                                     <li class="nav-item"> | |
|                                         <a class="nav-link py-2" href="/cluster/filers"> | |
|                                             <i class="fas fa-folder-open me-2"></i>Filers | |
|                             </a> | |
|                         </li> | |
|                         <li class="nav-item"> | |
|                                         <a class="nav-link py-2" href="/cluster/volumes"> | |
|                                 <i class="fas fa-database me-2"></i>Volumes | |
|                             </a> | |
|                                     </li> | |
|                                     <li class="nav-item"> | |
|                                         <a class="nav-link py-2" href="/cluster/collections"> | |
|                                             <i class="fas fa-layer-group me-2"></i>Collections | |
|                                         </a> | |
|                                     </li> | |
|                                 </ul> | |
|                             </div> | |
|                         </li> | |
|                     </ul> | |
|                      | |
|                     <h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted"> | |
|                         <span>MANAGEMENT</span> | |
|                     </h6> | |
|                     <ul class="nav flex-column"> | |
|                         <li class="nav-item"> | |
|                             <a class="nav-link" href="/files"> | |
|                                 <i class="fas fa-folder me-2"></i>File Browser | |
|                             </a> | |
|                         </li> | |
|                         <li class="nav-item"> | |
|                             <a class="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#objectStoreSubmenu" aria-expanded="false" aria-controls="objectStoreSubmenu"> | |
|                                 <i class="fas fa-cloud me-2"></i>Object Store | |
|                                 <i class="fas fa-chevron-down ms-auto"></i> | |
|                             </a> | |
|                             <div class="collapse" id="objectStoreSubmenu"> | |
|                                 <ul class="nav flex-column ms-3"> | |
|                                     <li class="nav-item"> | |
|                                         <a class="nav-link py-2" href="/object-store/buckets"> | |
|                                             <i class="fas fa-cube me-2"></i>Buckets | |
|                                         </a> | |
|                                     </li> | |
|                                     <li class="nav-item"> | |
|                                         <a class="nav-link py-2" href="/object-store/users"> | |
|                                             <i class="fas fa-users me-2"></i>Users | |
|                                         </a> | |
|                                     </li> | |
|                                     <li class="nav-item"> | |
|                                         <a class="nav-link py-2" href="/object-store/policies"> | |
|                                             <i class="fas fa-shield-alt me-2"></i>Policies | |
|                                         </a> | |
|                                     </li> | |
|                                 </ul> | |
|                             </div> | |
|                         </li> | |
|                         <li class="nav-item"> | |
|                             if isMQPage { | |
|                                 <a class="nav-link" href="#" data-bs-toggle="collapse" data-bs-target="#messageQueueSubmenu" aria-expanded="true" aria-controls="messageQueueSubmenu"> | |
|                                     <i class="fas fa-comments me-2"></i>Message Queue | |
|                                     <i class="fas fa-chevron-down ms-auto"></i> | |
|                                 </a> | |
|                             } else { | |
|                                 <a class="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#messageQueueSubmenu" aria-expanded="false" aria-controls="messageQueueSubmenu"> | |
|                                     <i class="fas fa-comments me-2"></i>Message Queue | |
|                                     <i class="fas fa-chevron-down ms-auto"></i> | |
|                                 </a> | |
|                             } | |
|                             if isMQPage { | |
|                                 <div class="collapse show" id="messageQueueSubmenu"> | |
|                                     <ul class="nav flex-column ms-3"> | |
|                                         <li class="nav-item"> | |
|                                             if currentPath == "/mq/brokers" { | |
|                                                 <a class="nav-link py-2 active" href="/mq/brokers"> | |
|                                                     <i class="fas fa-server me-2"></i>Brokers | |
|                                                 </a> | |
|                                             } else { | |
|                                                 <a class="nav-link py-2" href="/mq/brokers"> | |
|                                                     <i class="fas fa-server me-2"></i>Brokers | |
|                                                 </a> | |
|                                             } | |
|                                         </li> | |
|                                         <li class="nav-item"> | |
|                                             if currentPath == "/mq/topics" { | |
|                                                 <a class="nav-link py-2 active" href="/mq/topics"> | |
|                                                     <i class="fas fa-list-alt me-2"></i>Topics | |
|                                                 </a> | |
|                                             } else { | |
|                                                 <a class="nav-link py-2" href="/mq/topics"> | |
|                                                     <i class="fas fa-list-alt me-2"></i>Topics | |
|                                                 </a> | |
|                                             } | |
|                                         </li> | |
| 
 | |
|                                     </ul> | |
|                                 </div> | |
|                             } else { | |
|                                 <div class="collapse" id="messageQueueSubmenu"> | |
|                                     <ul class="nav flex-column ms-3"> | |
|                                         <li class="nav-item"> | |
|                                             <a class="nav-link py-2" href="/mq/brokers"> | |
|                                                 <i class="fas fa-server me-2"></i>Brokers | |
|                                             </a> | |
|                                         </li> | |
|                                         <li class="nav-item"> | |
|                                             <a class="nav-link py-2" href="/mq/topics"> | |
|                                                 <i class="fas fa-list-alt me-2"></i>Topics | |
|                                             </a> | |
|                                         </li> | |
| 
 | |
|                                     </ul> | |
|                                 </div> | |
|                             } | |
|                         </li> | |
|                         <li class="nav-item"> | |
|                             <a class="nav-link" href="/metrics"> | |
|                                 <i class="fas fa-chart-line me-2"></i>Metrics | |
|                             </a> | |
|                         </li> | |
|                         <li class="nav-item"> | |
|                             <a class="nav-link" href="/logs"> | |
|                                 <i class="fas fa-file-alt me-2"></i>Logs | |
|                             </a> | |
|                         </li> | |
|                     </ul> | |
| 
 | |
|                     <h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted"> | |
|                         <span>SYSTEM</span> | |
|                     </h6> | |
|                     <ul class="nav flex-column"> | |
|                         <li class="nav-item"> | |
|                             if isConfigPage { | |
|                                 <a class="nav-link" href="#" data-bs-toggle="collapse" data-bs-target="#configurationSubmenu" aria-expanded="true" aria-controls="configurationSubmenu"> | |
|                                     <i class="fas fa-cogs me-2"></i>Configuration | |
|                                     <i class="fas fa-chevron-down ms-auto"></i> | |
|                                 </a> | |
|                             } else { | |
|                                 <a class="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#configurationSubmenu" aria-expanded="false" aria-controls="configurationSubmenu"> | |
|                                     <i class="fas fa-cogs me-2"></i>Configuration | |
|                                     <i class="fas fa-chevron-right ms-auto"></i> | |
|                                 </a> | |
|                             } | |
|                             if isConfigPage { | |
|                                 <div class="collapse show" id="configurationSubmenu"> | |
|                                     <ul class="nav flex-column ms-3"> | |
|                                         for _, menuItem := range GetConfigurationMenuItems() { | |
|                                             {{ | |
|                                                 isActiveItem := currentPath == menuItem.URL | |
|                                             }} | |
|                                             <li class="nav-item"> | |
|                                                 if isActiveItem { | |
|                                                     <a class="nav-link py-2 active" href={templ.SafeURL(menuItem.URL)}> | |
|                                                         <i class={menuItem.Icon + " me-2"}></i>{menuItem.Name} | |
|                                                     </a> | |
|                                                 } else { | |
|                                                     <a class="nav-link py-2" href={templ.SafeURL(menuItem.URL)}> | |
|                                                         <i class={menuItem.Icon + " me-2"}></i>{menuItem.Name} | |
|                                                     </a> | |
|                                                 } | |
|                                             </li> | |
|                                         } | |
|                                     </ul> | |
|                                 </div> | |
|                             } else { | |
|                                 <div class="collapse" id="configurationSubmenu"> | |
|                                     <ul class="nav flex-column ms-3"> | |
|                                         for _, menuItem := range GetConfigurationMenuItems() { | |
|                                             <li class="nav-item"> | |
|                                                 <a class="nav-link py-2" href={templ.SafeURL(menuItem.URL)}> | |
|                                                     <i class={menuItem.Icon + " me-2"}></i>{menuItem.Name} | |
|                                                 </a> | |
|                                             </li> | |
|                                         } | |
|                                     </ul> | |
|                                 </div> | |
|                             } | |
|                         </li> | |
|                         <li class="nav-item"> | |
|                             if currentPath == "/maintenance" { | |
|                                 <a class="nav-link active" href="/maintenance"> | |
|                                     <i class="fas fa-list me-2"></i>Maintenance Queue | |
|                                 </a> | |
|                             } else { | |
|                                 <a class="nav-link" href="/maintenance"> | |
|                                     <i class="fas fa-list me-2"></i>Maintenance Queue | |
|                                 </a> | |
|                             } | |
|                         </li> | |
|                         <li class="nav-item"> | |
|                             if currentPath == "/maintenance/workers" { | |
|                                 <a class="nav-link active" href="/maintenance/workers"> | |
|                                     <i class="fas fa-user-cog me-2"></i>Maintenance Workers | |
|                                 </a> | |
|                             } else { | |
|                                 <a class="nav-link" href="/maintenance/workers"> | |
|                                     <i class="fas fa-user-cog me-2"></i>Maintenance Workers | |
|                                 </a> | |
|                             } | |
|                         </li> | |
|                     </ul> | |
|                 </div> | |
|             </div> | |
| 
 | |
|             <!-- Main content --> | |
|             <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4"> | |
|                 <div class="pt-3"> | |
|                     @content | |
|                 </div> | |
|             </main> | |
|         </div> | |
|     </div> | |
| 
 | |
|     <!-- Footer --> | |
|     <footer class="footer mt-auto py-3 bg-light"> | |
|         <div class="container-fluid text-center"> | |
|             <small class="text-muted"> | |
|                 © {fmt.Sprintf("%d", time.Now().Year())} SeaweedFS Admin v{version.VERSION_NUMBER} | |
|                 if !strings.Contains(version.VERSION, "enterprise") { | |
|                     <span class="mx-2">•</span> | |
|                     <a href="https://seaweedfs.com" target="_blank" class="text-decoration-none"> | |
|                         <i class="fas fa-star me-1"></i>Enterprise Version Available | |
|                     </a> | |
|                 } | |
|             </small> | |
|         </div> | |
|     </footer> | |
| 
 | |
|     <!-- Bootstrap JS --> | |
|     <script src="/static/js/bootstrap.bundle.min.js"></script> | |
|     <!-- Custom JS --> | |
|     <script src="/static/js/admin.js"></script> | |
| </body> | |
| </html> | |
| } | |
| 
 | |
| templ LoginForm(c *gin.Context, title string, errorMessage string) { | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
|     <meta charset="UTF-8"> | |
|     <title>{title} - Login</title> | |
|     <link rel="icon" href="/static/favicon.ico" type="image/x-icon"> | |
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | |
|     <link href="/static/css/bootstrap.min.css" rel="stylesheet"> | |
|     <link href="/static/css/fontawesome.min.css" rel="stylesheet"> | |
| </head> | |
| <body class="bg-light"> | |
|     <div class="container"> | |
|         <div class="row justify-content-center min-vh-100 align-items-center"> | |
|             <div class="col-md-6 col-lg-4"> | |
|                 <div class="card shadow"> | |
|                     <div class="card-body p-5"> | |
|                         <div class="text-center mb-4"> | |
|                             <i class="fas fa-server fa-3x text-primary mb-3"></i> | |
|                             <h4 class="card-title">{title}</h4> | |
|                             <p class="text-muted">Please sign in to continue</p> | |
|                         </div> | |
|                          | |
|                         if errorMessage != "" { | |
|                             <div class="alert alert-danger" role="alert"> | |
|                                 <i class="fas fa-exclamation-triangle me-2"></i> | |
|                                 {errorMessage} | |
|                             </div> | |
|                         } | |
|                          | |
|                         <form method="POST" action="/login"> | |
|                             <div class="mb-3"> | |
|                                 <label for="username" class="form-label">Username</label> | |
|                                 <div class="input-group"> | |
|                                     <span class="input-group-text"> | |
|                                         <i class="fas fa-user"></i> | |
|                                     </span> | |
|                                     <input type="text" class="form-control" id="username" name="username" required> | |
|                                 </div> | |
|                             </div> | |
|                              | |
|                             <div class="mb-4"> | |
|                                 <label for="password" class="form-label">Password</label> | |
|                                 <div class="input-group"> | |
|                                     <span class="input-group-text"> | |
|                                         <i class="fas fa-lock"></i> | |
|                                     </span> | |
|                                     <input type="password" class="form-control" id="password" name="password" required> | |
|                                 </div> | |
|                             </div> | |
|                              | |
|                             <button type="submit" class="btn btn-primary w-100"> | |
|                                 <i class="fas fa-sign-in-alt me-2"></i>Sign In | |
|                             </button> | |
|                         </form> | |
|                     </div> | |
|                 </div> | |
|             </div> | |
|         </div> | |
|     </div> | |
|      | |
|     <script src="/static/js/bootstrap.bundle.min.js"></script> | |
| </body> | |
| </html> | |
| }  |