aboutsummaryrefslogtreecommitdiff
path: root/weed/admin/admin.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/admin/admin.go')
-rw-r--r--weed/admin/admin.go247
1 files changed, 247 insertions, 0 deletions
diff --git a/weed/admin/admin.go b/weed/admin/admin.go
new file mode 100644
index 000000000..dbda0c402
--- /dev/null
+++ b/weed/admin/admin.go
@@ -0,0 +1,247 @@
+package main
+
+import (
+ "context"
+ "crypto/tls"
+ "embed"
+ "flag"
+ "fmt"
+ "io/fs"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ "github.com/gin-contrib/sessions"
+ "github.com/gin-contrib/sessions/cookie"
+ "github.com/gin-gonic/gin"
+
+ "github.com/seaweedfs/seaweedfs/weed/admin/dash"
+)
+
+//go:embed static/* view/*
+var adminFS embed.FS
+
+func main() {
+ var (
+ port = flag.Int("port", 23646, "Port to run the admin server on")
+ host = flag.String("host", "localhost", "Host to bind the admin server to")
+ sessionKey = flag.String("sessionKey", "", "Session encryption key (32 bytes, random if not provided)")
+ tlsCert = flag.String("tlsCert", "", "Path to TLS certificate file")
+ tlsKey = flag.String("tlsKey", "", "Path to TLS key file")
+ master = flag.String("master", "localhost:9333", "SeaweedFS master server address")
+ authRequired = flag.Bool("auth", false, "Enable authentication")
+ username = flag.String("username", "admin", "Admin username (only used if auth is enabled)")
+ password = flag.String("password", "", "Admin password (only used if auth is enabled)")
+ help = flag.Bool("help", false, "Show help")
+ )
+
+ flag.Parse()
+
+ if *help {
+ fmt.Println("SeaweedFS Admin Server")
+ fmt.Println()
+ flag.PrintDefaults()
+ return
+ }
+
+ // Set Gin mode
+ gin.SetMode(gin.ReleaseMode)
+
+ // Create router
+ r := gin.New()
+ r.Use(gin.Logger(), gin.Recovery())
+
+ // Session store
+ var sessionKeyBytes []byte
+ if *sessionKey != "" {
+ sessionKeyBytes = []byte(*sessionKey)
+ } else {
+ // Generate a random session key
+ sessionKeyBytes = make([]byte, 32)
+ for i := range sessionKeyBytes {
+ sessionKeyBytes[i] = byte(time.Now().UnixNano() & 0xff)
+ }
+ }
+ store := cookie.NewStore(sessionKeyBytes)
+ r.Use(sessions.Sessions("admin-session", store))
+
+ // Static files
+ staticFS, err := fs.Sub(adminFS, "static")
+ if err != nil {
+ log.Fatal("Failed to create static filesystem:", err)
+ }
+ r.StaticFS("/static", http.FS(staticFS))
+
+ // Templates
+ viewFS, err := fs.Sub(adminFS, "view")
+ if err != nil {
+ log.Fatal("Failed to create view filesystem:", err)
+ }
+
+ // Create admin server
+ adminServer := dash.NewAdminServer(*master, http.FS(viewFS))
+
+ // Setup routes
+ setupRoutes(r, adminServer, *authRequired, *username, *password)
+
+ // Server configuration
+ addr := fmt.Sprintf("%s:%d", *host, *port)
+ server := &http.Server{
+ Addr: addr,
+ Handler: r,
+ }
+
+ // TLS configuration
+ if *tlsCert != "" && *tlsKey != "" {
+ server.TLSConfig = &tls.Config{
+ MinVersion: tls.VersionTLS12,
+ }
+ }
+
+ // Start server
+ go func() {
+ log.Printf("Starting SeaweedFS Admin Server on %s", addr)
+
+ var err error
+ if *tlsCert != "" && *tlsKey != "" {
+ log.Printf("Using TLS with cert: %s, key: %s", *tlsCert, *tlsKey)
+ err = server.ListenAndServeTLS(*tlsCert, *tlsKey)
+ } else {
+ err = server.ListenAndServe()
+ }
+
+ if err != nil && err != http.ErrServerClosed {
+ log.Fatal("Failed to start server:", err)
+ }
+ }()
+
+ // Wait for interrupt signal to gracefully shutdown the server
+ quit := make(chan os.Signal, 1)
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+ <-quit
+ log.Println("Shutting down admin server...")
+
+ // Give outstanding requests 30 seconds to complete
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer cancel()
+
+ if err := server.Shutdown(ctx); err != nil {
+ log.Fatal("Admin server forced to shutdown:", err)
+ }
+
+ log.Println("Admin server exited")
+}
+
+func setupRoutes(r *gin.Engine, adminServer *dash.AdminServer, authRequired bool, username, password string) {
+ // Health check (no auth required)
+ r.GET("/health", func(c *gin.Context) {
+ c.JSON(200, gin.H{"status": "ok"})
+ })
+
+ if authRequired {
+ // Auth routes
+ auth := r.Group("/")
+ auth.GET("/login", adminServer.ShowLogin)
+ auth.POST("/login", adminServer.HandleLogin(username, password))
+ auth.POST("/logout", adminServer.HandleLogout)
+
+ // Protected routes
+ protected := r.Group("/")
+ protected.Use(dash.RequireAuth())
+
+ // Admin routes
+ protected.GET("/", adminServer.ShowAdmin)
+ protected.GET("/admin", adminServer.ShowAdmin)
+ protected.GET("/overview", adminServer.ShowAdmin)
+
+ // Cluster management
+ cluster := protected.Group("/cluster")
+ {
+ cluster.GET("/topology", adminServer.GetClusterTopologyHandler)
+ cluster.GET("/masters", adminServer.GetMasters)
+ cluster.GET("/volumes", adminServer.GetVolumeServers)
+ cluster.POST("/volumes/assign", adminServer.AssignVolume)
+ }
+
+ // Volume management
+ volumes := protected.Group("/volumes")
+ {
+ volumes.GET("/", adminServer.ListVolumes)
+ volumes.POST("/create", adminServer.CreateVolume)
+ volumes.DELETE("/:id", adminServer.DeleteVolume)
+ volumes.POST("/:id/replicate", adminServer.ReplicateVolume)
+ }
+
+ // File browser
+ files := protected.Group("/filer")
+ {
+ files.GET("/*path", adminServer.BrowseFiles)
+ files.POST("/upload", adminServer.UploadFile)
+ files.DELETE("/*path", adminServer.DeleteFile)
+ }
+
+ // Metrics
+ metrics := protected.Group("/metrics")
+ {
+ metrics.GET("/", adminServer.ShowMetrics)
+ metrics.GET("/data", adminServer.GetMetricsData)
+ }
+
+ // Maintenance
+ maintenance := protected.Group("/maintenance")
+ {
+ maintenance.POST("/gc", adminServer.TriggerGC)
+ maintenance.POST("/compact", adminServer.CompactVolumes)
+ maintenance.GET("/status", adminServer.GetMaintenanceStatus)
+ }
+ } else {
+ // No auth required - all routes are public
+ r.GET("/", adminServer.ShowAdmin)
+ r.GET("/admin", adminServer.ShowAdmin)
+ r.GET("/overview", adminServer.ShowAdmin)
+
+ // Cluster management
+ cluster := r.Group("/cluster")
+ {
+ cluster.GET("/topology", adminServer.GetClusterTopologyHandler)
+ cluster.GET("/masters", adminServer.GetMasters)
+ cluster.GET("/volumes", adminServer.GetVolumeServers)
+ cluster.POST("/volumes/assign", adminServer.AssignVolume)
+ }
+
+ // Volume management
+ volumes := r.Group("/volumes")
+ {
+ volumes.GET("/", adminServer.ListVolumes)
+ volumes.POST("/create", adminServer.CreateVolume)
+ volumes.DELETE("/:id", adminServer.DeleteVolume)
+ volumes.POST("/:id/replicate", adminServer.ReplicateVolume)
+ }
+
+ // File browser
+ files := r.Group("/filer")
+ {
+ files.GET("/*path", adminServer.BrowseFiles)
+ files.POST("/upload", adminServer.UploadFile)
+ files.DELETE("/*path", adminServer.DeleteFile)
+ }
+
+ // Metrics
+ metrics := r.Group("/metrics")
+ {
+ metrics.GET("/", adminServer.ShowMetrics)
+ metrics.GET("/data", adminServer.GetMetricsData)
+ }
+
+ // Maintenance
+ maintenance := r.Group("/maintenance")
+ {
+ maintenance.POST("/gc", adminServer.TriggerGC)
+ maintenance.POST("/compact", adminServer.CompactVolumes)
+ maintenance.GET("/status", adminServer.GetMaintenanceStatus)
+ }
+ }
+}