Refactor start.go to move server logic to internal server.go
This commit is contained in:
parent
4d29a58f94
commit
e5e9efd9b3
95
cmd/start.go
95
cmd/start.go
@ -3,14 +3,9 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.coopcloud.tech/wiki-cafe/member-console/internal/auth"
|
||||
"git.coopcloud.tech/wiki-cafe/member-console/internal/logging"
|
||||
"git.coopcloud.tech/wiki-cafe/member-console/internal/middleware"
|
||||
"github.com/rs/cors"
|
||||
"git.coopcloud.tech/wiki-cafe/member-console/internal/server"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -30,6 +25,7 @@ var startCmd = &cobra.Command{
|
||||
// Retrieve the configuration values from Viper
|
||||
port := viper.GetString("port")
|
||||
env := viper.GetString("env")
|
||||
csrfSecret := viper.GetString("csrf-secret")
|
||||
|
||||
// Set up structured logging
|
||||
logger := logging.SetupLogger(env)
|
||||
@ -37,86 +33,17 @@ var startCmd = &cobra.Command{
|
||||
// Store logger in context
|
||||
ctx = logging.WithContext(ctx, logger)
|
||||
|
||||
// Create a new HTTP request router
|
||||
httpRequestRouter := http.NewServeMux()
|
||||
|
||||
// Set up authentication
|
||||
authConfig, err := auth.Setup()
|
||||
if err != nil {
|
||||
logger.Error("failed to set up authentication", slog.Any("error", err))
|
||||
return
|
||||
// Create server config
|
||||
serverConfig := server.Config{
|
||||
Port: port,
|
||||
Env: env,
|
||||
CSRFSecret: csrfSecret,
|
||||
Logger: logger,
|
||||
}
|
||||
|
||||
// Register auth handlers
|
||||
authConfig.RegisterHandlers(httpRequestRouter)
|
||||
|
||||
// Create CORS configuration with default options
|
||||
corsOptions := cors.Options{
|
||||
// Define minimal defaults - GET method is required
|
||||
AllowedMethods: []string{"GET"},
|
||||
}
|
||||
|
||||
// Create empty CSRF configuration with default values
|
||||
var csrfConfig middleware.CSRFConfig
|
||||
|
||||
// Get and validate CSRF secret from config
|
||||
csrfSecret := viper.GetString("csrf-secret")
|
||||
csrfKey, err := middleware.ParseCSRFKey(csrfSecret)
|
||||
if err != nil {
|
||||
logger.Error("invalid csrf-secret",
|
||||
slog.String("error", err.Error()),
|
||||
slog.String("hint", "must be exactly 32 bytes and persist across restarts"))
|
||||
return
|
||||
}
|
||||
|
||||
csrfConfig.Secret = csrfKey
|
||||
|
||||
// Only override specific settings when needed
|
||||
if env == "development" {
|
||||
// In development, cookies often need to work without HTTPS
|
||||
csrfConfig.Cookie.Secure = false
|
||||
}
|
||||
|
||||
// Create middleware stack
|
||||
stack := middleware.CreateStack(
|
||||
middleware.RequestID(), // Generate a unique request ID
|
||||
middleware.Logging(), // Log requests with structured logging
|
||||
middleware.Recovery(), // Catch all panics
|
||||
middleware.Timeout(32*time.Second), // Set request timeout
|
||||
middleware.MaxBodySize(1024*1024), // 1MB size limit
|
||||
middleware.SecureHeaders(), // Set secure headers
|
||||
middleware.CORS(corsOptions), // CORS configuration
|
||||
middleware.CSRF(csrfConfig), // CSRF protection
|
||||
middleware.Compress(), // Response compression
|
||||
authConfig.Middleware(), // OIDC authentication middleware
|
||||
)
|
||||
|
||||
// Create HTTP server
|
||||
server := http.Server{
|
||||
Addr: ":" + port,
|
||||
Handler: stack(httpRequestRouter),
|
||||
ReadTimeout: 4 * time.Second,
|
||||
WriteTimeout: 8 * time.Second,
|
||||
IdleTimeout: 16 * time.Second,
|
||||
MaxHeaderBytes: 1024 * 1024, // 1MB
|
||||
BaseContext: func(_ net.Listener) context.Context { return ctx }, // Pass base context to all requests
|
||||
}
|
||||
|
||||
// Serve the templates and static files
|
||||
// Serve templates from the "templates" directory
|
||||
httpRequestRouter.Handle("/", http.FileServer(http.Dir("./templates")))
|
||||
// Serve static files from the "static" directory
|
||||
httpRequestRouter.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
|
||||
|
||||
// Log server startup with structured logging
|
||||
logger.Info("starting server",
|
||||
slog.String("port", port),
|
||||
slog.String("environment", env),
|
||||
slog.String("address", "http://localhost:"+port))
|
||||
|
||||
// Start server and log any errors
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
logger.Error("server error", slog.Any("error", err))
|
||||
// Start the server
|
||||
if err := server.Start(ctx, serverConfig); err != nil {
|
||||
logger.Error("server failed to start", slog.Any("error", err))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
107
internal/server/server.go
Normal file
107
internal/server/server.go
Normal file
@ -0,0 +1,107 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.coopcloud.tech/wiki-cafe/member-console/internal/auth"
|
||||
"git.coopcloud.tech/wiki-cafe/member-console/internal/middleware"
|
||||
"github.com/rs/cors"
|
||||
)
|
||||
|
||||
// Config holds the configuration for the server.
|
||||
type Config struct {
|
||||
Port string
|
||||
Env string
|
||||
CSRFSecret string
|
||||
Logger *slog.Logger
|
||||
}
|
||||
|
||||
// Start initializes and starts the HTTP server.
|
||||
func Start(ctx context.Context, cfg Config) error {
|
||||
// Create a new HTTP request router
|
||||
httpRequestRouter := http.NewServeMux()
|
||||
|
||||
// Set up authentication
|
||||
authConfig, err := auth.Setup()
|
||||
if err != nil {
|
||||
cfg.Logger.Error("failed to set up authentication", slog.Any("error", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Register auth handlers
|
||||
authConfig.RegisterHandlers(httpRequestRouter)
|
||||
|
||||
// Create CORS configuration with default options
|
||||
corsOptions := cors.Options{
|
||||
// Define minimal defaults - GET method is required
|
||||
AllowedMethods: []string{"GET"},
|
||||
}
|
||||
|
||||
// Create empty CSRF configuration with default values
|
||||
var csrfConfig middleware.CSRFConfig
|
||||
|
||||
// Get and validate CSRF secret from config
|
||||
csrfKey, err := middleware.ParseCSRFKey(cfg.CSRFSecret)
|
||||
if err != nil {
|
||||
cfg.Logger.Error("invalid csrf-secret",
|
||||
slog.String("error", err.Error()),
|
||||
slog.String("hint", "must be exactly 32 bytes and persist across restarts"))
|
||||
return err
|
||||
}
|
||||
|
||||
csrfConfig.Secret = csrfKey
|
||||
|
||||
// Only override specific settings when needed
|
||||
if cfg.Env == "development" {
|
||||
// In development, cookies often need to work without HTTPS
|
||||
csrfConfig.Cookie.Secure = false
|
||||
}
|
||||
|
||||
// Create middleware stack
|
||||
stack := middleware.CreateStack(
|
||||
middleware.RequestID(), // Generate a unique request ID
|
||||
middleware.Logging(), // Log requests with structured logging
|
||||
middleware.Recovery(), // Catch all panics
|
||||
middleware.Timeout(32*time.Second), // Set request timeout
|
||||
middleware.MaxBodySize(1024*1024), // 1MB size limit
|
||||
middleware.SecureHeaders(), // Set secure headers
|
||||
middleware.CORS(corsOptions), // CORS configuration
|
||||
middleware.CSRF(csrfConfig), // CSRF protection
|
||||
middleware.Compress(), // Response compression
|
||||
authConfig.Middleware(), // OIDC authentication middleware
|
||||
)
|
||||
|
||||
// Create HTTP server
|
||||
server := http.Server{
|
||||
Addr: ":" + cfg.Port,
|
||||
Handler: stack(httpRequestRouter),
|
||||
ReadTimeout: 4 * time.Second,
|
||||
WriteTimeout: 8 * time.Second,
|
||||
IdleTimeout: 16 * time.Second,
|
||||
MaxHeaderBytes: 1024 * 1024, // 1MB
|
||||
BaseContext: func(_ net.Listener) context.Context { return ctx }, // Pass base context to all requests
|
||||
}
|
||||
|
||||
// Serve the templates and static files
|
||||
// Serve templates from the "templates" directory
|
||||
httpRequestRouter.Handle("/", http.FileServer(http.Dir("./templates")))
|
||||
// Serve static files from the "static" directory
|
||||
httpRequestRouter.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
|
||||
|
||||
// Log server startup with structured logging
|
||||
cfg.Logger.Info("starting server",
|
||||
slog.String("port", cfg.Port),
|
||||
slog.String("environment", cfg.Env),
|
||||
slog.String("address", "http://localhost:"+cfg.Port))
|
||||
|
||||
// Start server and log any errors
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
cfg.Logger.Error("server error", slog.Any("error", err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user