purge unused
This commit is contained in:
@@ -1,247 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -51,78 +51,3 @@ func (s *AdminServer) HandleLogout(c *gin.Context) {
|
|||||||
session.Save()
|
session.Save()
|
||||||
c.Redirect(http.StatusSeeOther, "/login")
|
c.Redirect(http.StatusSeeOther, "/login")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional methods for admin functionality
|
|
||||||
func (s *AdminServer) GetClusterTopologyHandler(c *gin.Context) {
|
|
||||||
topology, err := s.GetClusterTopology()
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, topology)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) GetMasters(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusOK, gin.H{"masters": []string{s.masterAddress}})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) GetVolumeServers(c *gin.Context) {
|
|
||||||
topology, err := s.GetClusterTopology()
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, gin.H{"volume_servers": topology.VolumeServers})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) AssignVolume(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Volume assignment not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) ListVolumes(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Volume listing not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) CreateVolume(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Volume creation not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) DeleteVolume(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Volume deletion not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) ReplicateVolume(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Volume replication not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) BrowseFiles(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "File browsing not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) UploadFile(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "File upload not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) DeleteFile(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "File deletion not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) ShowMetrics(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Metrics display not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) GetMetricsData(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Metrics data not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) TriggerGC(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Garbage collection not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) CompactVolumes(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Volume compaction not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AdminServer) GetMaintenanceStatus(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotImplemented, gin.H{"message": "Maintenance status not yet implemented"})
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user