Files
versitygw/webui/webserver.go
T
niksis02 2b7c2e25ba feat: fiber migration v2 -> v3
Fiber has released a new major version, **v3.0.0**, introducing several breaking changes. As part of the gateway migration from Fiber v2 to v3, the `filesystem` middleware has been deprecated and replaced with the `static` middleware for serving web assets. Currently, Fiber’s `static` middleware contains a bug in how it resolves the filesystem root path. Because of this, `fs.Sub` is used to set the embedded `web` directory as the effective root of the filesystem.
2026-02-11 19:29:19 +04:00

170 lines
4.1 KiB
Go

// Copyright 2026 Versity Software
// This file is licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package webui
import (
"fmt"
"io/fs"
"strings"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/logger"
"github.com/gofiber/fiber/v3/middleware/recover"
"github.com/gofiber/fiber/v3/middleware/static"
"github.com/versity/versitygw/s3api/utils"
)
// ServerConfig holds the server configuration
type ServerConfig struct {
ListenAddr string
Gateways []string // S3 API gateways
AdminGateways []string // Admin API gateways (defaults to Gateways if empty)
Region string
CORSOrigin string
}
// Server is the main GUI server
type Server struct {
app *fiber.App
CertStorage *utils.CertStorage
config *ServerConfig
quiet bool
}
// Option sets various options for NewServer()
type Option func(*Server)
// WithQuiet silences default logging output.
func WithQuiet() Option {
return func(s *Server) { s.quiet = true }
}
// WithTLS sets TLS Credentials
func WithTLS(cs *utils.CertStorage) Option {
return func(s *Server) { s.CertStorage = cs }
}
// NewServer creates a new GUI server instance
func NewServer(cfg *ServerConfig, opts ...Option) (*Server, error) {
app := fiber.New(fiber.Config{
AppName: "versitygw",
ServerHeader: "VERSITYGW",
})
server := &Server{
app: app,
config: cfg,
}
for _, opt := range opts {
opt(server)
}
server.setupMiddleware()
err := server.setupRoutes()
if err != nil {
return nil, err
}
fmt.Printf("initializing web dashboard on %s\n", cfg.ListenAddr)
return server, nil
}
// setupMiddleware configures middleware
func (s *Server) setupMiddleware() {
// Panic recovery
s.app.Use("*", recover.New())
// Request logging
if !s.quiet {
s.app.Use("*", logger.New(logger.Config{
Format: "${time} | web | ${status} | ${latency} | ${ip} | ${method} | ${path}\n",
}))
}
}
// setupRoutes configures all routes
func (s *Server) setupRoutes() error {
// API endpoint to get configured gateways
s.app.Get("/api/gateways", s.handleGetGateways)
// create a subtree from 'web' directory
// so that routing works correctly
// currently there's a bug in fasthttp resolving fs root
// https://github.com/valyala/fasthttp/issues/2141
// TODO: remove sub fs after the bug fix
subFS, err := fs.Sub(webFS, "web")
if err != nil {
return err
}
s.app.Use("/", static.New("", static.Config{
FS: subFS,
Browse: false,
IndexNames: []string{"index.html"},
}))
return nil
}
// handleGetGateways returns the configured gateway URLs (both S3 and Admin)
func (s *Server) handleGetGateways(c fiber.Ctx) error {
adminGateways := s.config.AdminGateways
if len(adminGateways) == 0 {
// Fallback to S3 gateways if admin gateways not configured
adminGateways = s.config.Gateways
}
return c.JSON(fiber.Map{
"gateways": s.config.Gateways,
"adminGateways": adminGateways,
"defaultRegion": s.config.Region,
})
}
// Serve starts the server
func (s *Server) Serve() error {
addr := strings.TrimSpace(s.config.ListenAddr)
if addr == "" {
return fmt.Errorf("webui: listen address is required")
}
// Check if TLS is configured
if s.CertStorage != nil {
ln, err := utils.NewTLSListener(fiber.NetworkTCP, addr, s.CertStorage.GetCertificate)
if err != nil {
return err
}
return s.app.Listener(ln,
fiber.ListenConfig{
ListenerNetwork: fiber.NetworkTCP,
DisableStartupMessage: true,
})
}
return s.app.Listen(addr,
fiber.ListenConfig{
ListenerNetwork: fiber.NetworkTCP,
DisableStartupMessage: true,
})
}
// Shutdown gracefully shuts down the server
func (s *Server) Shutdown() error {
return s.app.Shutdown()
}