Files
b2b/app/delivery/web/init.go
2026-03-11 09:33:36 +01:00

185 lines
4.5 KiB
Go

package web
import (
"context"
"log"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"git.ma-al.com/goc_marek/timetracker/app/config"
"git.ma-al.com/goc_marek/timetracker/app/delivery/middleware"
"git.ma-al.com/goc_marek/timetracker/app/delivery/web/api"
"git.ma-al.com/goc_marek/timetracker/app/delivery/web/api/public"
"git.ma-al.com/goc_marek/timetracker/app/delivery/web/api/restricted"
"git.ma-al.com/goc_marek/timetracker/app/delivery/web/general"
// "github.com/gofiber/fiber/v2/middleware/filesystem"
"github.com/gofiber/fiber/v3"
// "github.com/gofiber/fiber/v3/middleware/filesystem"
"github.com/gofiber/fiber/v3/middleware/logger"
"github.com/gofiber/fiber/v3/middleware/recover"
)
// Server represents the web server
type Server struct {
app *fiber.App
cfg *config.Config
api fiber.Router
public fiber.Router
restricted fiber.Router
}
// App returns the fiber app
func (s *Server) App() *fiber.App {
return s.app
}
// Cfg returns the config
func (s *Server) Cfg() *config.Config {
return s.cfg
}
// New creates a new server instance
func New() *Server {
return &Server{
app: fiber.New(fiber.Config{
ErrorHandler: customErrorHandler,
}),
cfg: config.Get(),
}
}
// Setup configures the server with routes and middleware
func (s *Server) Setup() error {
// Global middleware
s.app.Use(recover.New())
s.app.Use(logger.New())
// CORS middleware
s.app.Use(middleware.CORSMiddleware())
// Language middleware - discovers client's language and stores in context
s.app.Use(middleware.LanguageMiddleware())
// initialize healthcheck
general.InitHealth(s.App(), s.Cfg())
// serve favicon
general.Favicon(s.app, s.cfg)
// initialize swagger endpoints
general.InitSwagger(s.App())
// API routes
s.api = s.app.Group("/api/v1")
s.public = s.api.Group("/public")
s.restricted = s.api.Group("/restricted")
s.restricted.Use(middleware.AuthMiddleware())
// initialize language endpoints (general)
api.NewLangHandler().InitLanguage(s.api, s.cfg)
// Settings endpoint (general)
api.NewSettingsHandler().InitSettings(s.api, s.cfg)
// Auth routes (public)
auth := s.public.Group("/auth")
public.AuthHandlerRoutes(auth)
// Repo routes (restricted)
repo := s.restricted.Group("/repo")
restricted.RepoHandlerRoutes(repo)
// // Restricted routes example
// restricted := s.api.Group("/restricted")
// restricted.Use(middleware.AuthMiddleware())
// restricted.Get("/dashboard", func(c fiber.Ctx) error {
// user := middleware.GetUser(c)
// return c.JSON(fiber.Map{
// "message": "Welcome to the protected area",
// "user": user,
// })
// })
// // Admin routes example
// admin := s.api.Group("/admin")
// admin.Use(middleware.AuthMiddleware())
// admin.Use(middleware.RequireAdmin())
// admin.Get("/users", func(c fiber.Ctx) error {
// return c.JSON(fiber.Map{
// "message": "Admin area - user management",
// })
// })
// keep this at the end because its wilderange
general.InitBo(s.App())
return nil
}
// Run starts the server
func (s *Server) Run() error {
// Run database migrations
// if err := db.RunMigrations(); err != nil {
// log.Printf("⚠️ Database migrations failed: %v", err)
// } else {
// log.Println("✓ Database migrations completed")
// }
// // Seed admin user
// if err := db.SeedAdminUser("admin@example.com", "admin123"); err != nil {
// log.Printf("⚠️ Admin user seeding failed: %v", err)
// }
addr := s.cfg.Server.Host + ":" + strconv.Itoa(s.cfg.Server.Port)
log.Printf("Starting server on %s", addr)
log.Printf("Swagger UI available at http://%s/swagger/index.html", addr)
log.Printf("OpenAPI JSON available at http://%s/openapi.json", addr)
go func() {
if err := s.app.Listen(":3000"); err != nil {
log.Println("Server stopped:", err)
}
}()
// Wait for shutdown signal
quit := make(chan os.Signal, 1)
signal.Notify(
quit,
syscall.SIGINT, // Ctrl+C
syscall.SIGTERM, // docker stop
)
<-quit
log.Println("Shutting down server...")
// graceful shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := s.app.ShutdownWithContext(ctx); err != nil {
log.Fatal("Shutdown error:", err)
}
log.Println("Server exited cleanly")
return nil
}
// customErrorHandler handles errors
func customErrorHandler(c fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
return c.Status(code).JSON(fiber.Map{
"error": err.Error(),
})
}