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.
		
		
		
		
		
			
		
			
				
					
					
						
							406 lines
						
					
					
						
							21 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							406 lines
						
					
					
						
							21 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/ec-shards">
							 | 
						|
								                                            <i class="fas fa-th-large me-2"></i>EC 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>
							 | 
						|
								} 
							 |