beta
This commit is contained in:
parent
a45ddf13d5
commit
bcd986e3f7
46 changed files with 6166 additions and 454 deletions
172
server/server.go
Normal file
172
server/server.go
Normal file
|
@ -0,0 +1,172 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"otpm/config"
|
||||
"otpm/middleware"
|
||||
)
|
||||
|
||||
// Server represents the HTTP server
|
||||
type Server struct {
|
||||
server *http.Server
|
||||
router *http.ServeMux
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
// New creates a new server
|
||||
func New(cfg *config.Config) *Server {
|
||||
router := http.NewServeMux()
|
||||
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", cfg.Server.Port),
|
||||
Handler: router,
|
||||
ReadTimeout: cfg.Server.ReadTimeout,
|
||||
WriteTimeout: cfg.Server.WriteTimeout,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
}
|
||||
|
||||
return &Server{
|
||||
server: server,
|
||||
router: router,
|
||||
config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the server
|
||||
func (s *Server) Start() error {
|
||||
// Apply global middleware in correct order with enhanced error handling
|
||||
var handler http.Handler = s.router
|
||||
|
||||
// Logger should be first to capture all request details
|
||||
handler = middleware.Logger(handler)
|
||||
|
||||
// CORS next to handle pre-flight requests
|
||||
handler = middleware.CORS(handler)
|
||||
|
||||
// Then Timeout to enforce request deadlines
|
||||
handler = middleware.Timeout(s.config.Server.Timeout)(handler)
|
||||
|
||||
// Recover should be outermost to catch any panics
|
||||
handler = middleware.Recover(handler)
|
||||
|
||||
s.server.Handler = handler
|
||||
|
||||
// Log server configuration at startup
|
||||
log.Printf("Server configuration:\n"+
|
||||
"Address: %s\n"+
|
||||
"Read Timeout: %v\n"+
|
||||
"Write Timeout: %v\n"+
|
||||
"Idle Timeout: %v\n"+
|
||||
"Request Timeout: %v",
|
||||
s.server.Addr,
|
||||
s.server.ReadTimeout,
|
||||
s.server.WriteTimeout,
|
||||
s.server.IdleTimeout,
|
||||
s.config.Server.Timeout,
|
||||
)
|
||||
|
||||
// Start server in a goroutine
|
||||
serverErr := make(chan error, 1)
|
||||
go func() {
|
||||
log.Printf("Server starting on %s", s.server.Addr)
|
||||
if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
serverErr <- fmt.Errorf("server error: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for interrupt signal or server error
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case err := <-serverErr:
|
||||
return err
|
||||
case <-quit:
|
||||
return s.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown gracefully stops the server
|
||||
func (s *Server) Shutdown() error {
|
||||
log.Println("Shutting down server...")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), s.config.Server.ShutdownTimeout)
|
||||
defer cancel()
|
||||
|
||||
if err := s.server.Shutdown(ctx); err != nil {
|
||||
return fmt.Errorf("graceful shutdown failed: %w", err)
|
||||
}
|
||||
|
||||
log.Println("Server stopped gracefully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Router returns the router
|
||||
func (s *Server) Router() *http.ServeMux {
|
||||
return s.router
|
||||
}
|
||||
|
||||
// RegisterRoutes registers all routes
|
||||
func (s *Server) RegisterRoutes(routes map[string]http.Handler) {
|
||||
for pattern, handler := range routes {
|
||||
s.router.Handle(pattern, handler)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterAuthRoutes registers routes that require authentication
|
||||
func (s *Server) RegisterAuthRoutes(routes map[string]http.Handler) {
|
||||
for pattern, handler := range routes {
|
||||
// Apply authentication middleware
|
||||
authHandler := middleware.Auth(s.config.JWT.Secret)(handler)
|
||||
s.router.Handle(pattern, authHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterHealthCheck registers an enhanced health check endpoint
|
||||
func (s *Server) RegisterHealthCheck() {
|
||||
s.router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
response := map[string]interface{}{
|
||||
"status": "ok",
|
||||
"timestamp": time.Now().Format(time.RFC3339),
|
||||
"version": "1.0.0", // Hardcoded version instead of from config
|
||||
"system": map[string]interface{}{
|
||||
"goroutines": runtime.NumGoroutine(),
|
||||
"memory": getMemoryUsage(),
|
||||
},
|
||||
}
|
||||
|
||||
// Add database status if configured
|
||||
if s.config.Database.DSN != "" { // Changed from URL to DSN to match config
|
||||
dbStatus := "ok"
|
||||
// Removed DB ping check since we don't have DB instance in config
|
||||
response["database"] = dbStatus
|
||||
}
|
||||
|
||||
middleware.SuccessResponse(w, response)
|
||||
})
|
||||
}
|
||||
|
||||
// getMemoryUsage returns current memory usage in MB
|
||||
func getMemoryUsage() map[string]interface{} {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
return map[string]interface{}{
|
||||
"alloc_mb": bToMb(m.Alloc),
|
||||
"total_alloc_mb": bToMb(m.TotalAlloc),
|
||||
"sys_mb": bToMb(m.Sys),
|
||||
"num_gc": m.NumGC,
|
||||
}
|
||||
}
|
||||
|
||||
func bToMb(b uint64) float64 {
|
||||
return float64(b) / 1024 / 1024
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue