261 lines
8.2 KiB
Go
261 lines
8.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/labstack/echo/v4/middleware"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
|
|
"tm/internal/domain"
|
|
"tm/internal/handler"
|
|
"tm/internal/infrastructure"
|
|
"tm/internal/repository"
|
|
"tm/internal/service"
|
|
"tm/pkg/logger"
|
|
customMiddleware "tm/pkg/middleware"
|
|
"tm/pkg/response"
|
|
)
|
|
|
|
func main() {
|
|
// Load configuration
|
|
config, err := infrastructure.LoadConfig("./configs")
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Failed to load config: %v", err))
|
|
}
|
|
|
|
// Initialize logger
|
|
logger.InitLogger(&config.Logging)
|
|
log := logger.GetLogger()
|
|
|
|
log.Info("Starting Tender Management API Server", map[string]interface{}{
|
|
"version": "1.0.0",
|
|
"port": config.Server.Port,
|
|
})
|
|
|
|
// Initialize MongoDB connection
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(config.Database.MongoDB.URI))
|
|
if err != nil {
|
|
log.Fatal("Failed to connect to MongoDB", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
// Ping MongoDB to verify connection
|
|
if err = mongoClient.Ping(ctx, nil); err != nil {
|
|
log.Fatal("Failed to ping MongoDB", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
log.Info("Connected to MongoDB successfully", map[string]interface{}{
|
|
"database": config.Database.MongoDB.Name,
|
|
})
|
|
|
|
// Get MongoDB database
|
|
db := mongoClient.Database(config.Database.MongoDB.Name)
|
|
|
|
// Initialize validator
|
|
validate := validator.New()
|
|
|
|
// Initialize repositories
|
|
customerRepo := repository.NewCustomerRepository(db, log)
|
|
userRepo := repository.NewUserRepository(db, log)
|
|
companyRepo := repository.NewCompanyRepository(db, log)
|
|
|
|
// Initialize services
|
|
customerAuthService := service.NewCustomerAuthService(customerRepo, companyRepo, &config.Auth, log)
|
|
userAuthService := service.NewUserAuthService(userRepo, &config.Auth, log)
|
|
// TODO: Initialize customer service when needed for admin customer management
|
|
// customerService := service.NewCustomerService(customerRepo, log)
|
|
|
|
// Backward compatibility - create generic auth service
|
|
authService := handler.NewAuthHandler(customerAuthService, userAuthService, validate, log)
|
|
|
|
// Initialize handlers
|
|
customerAuthHandler := handler.NewCustomerAuthHandler(customerAuthService, validate, log)
|
|
userAuthHandler := handler.NewUserAuthHandler(userAuthService, validate, log)
|
|
|
|
// Initialize Echo
|
|
e := echo.New()
|
|
|
|
// Configure Echo
|
|
e.HideBanner = true
|
|
e.HidePort = true
|
|
|
|
// Add built-in middleware
|
|
e.Use(middleware.Logger())
|
|
e.Use(middleware.Recover())
|
|
e.Use(middleware.CORS())
|
|
e.Use(middleware.RequestID())
|
|
|
|
// Add custom middleware for logging
|
|
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
start := time.Now()
|
|
|
|
err := next(c)
|
|
|
|
log.Info("HTTP Request", map[string]interface{}{
|
|
"method": c.Request().Method,
|
|
"path": c.Request().URL.Path,
|
|
"status": c.Response().Status,
|
|
"duration": time.Since(start).String(),
|
|
"request_id": c.Response().Header().Get(echo.HeaderXRequestID),
|
|
})
|
|
|
|
return err
|
|
}
|
|
})
|
|
|
|
// Add authentication middleware
|
|
e.Use(customMiddleware.AuthMiddleware(customerAuthService, userAuthService, log))
|
|
|
|
// Setup routes
|
|
setupRoutes(e, authService, customerAuthHandler, userAuthHandler)
|
|
|
|
// Health check endpoint
|
|
e.GET("/health", func(c echo.Context) error {
|
|
return response.Success(c, map[string]interface{}{
|
|
"status": "healthy",
|
|
"timestamp": time.Now().UTC(),
|
|
"version": "1.0.0",
|
|
}, "Service is healthy")
|
|
})
|
|
|
|
// Start server in a goroutine
|
|
go func() {
|
|
address := fmt.Sprintf("%s:%d", config.Server.Host, config.Server.Port)
|
|
log.Info("Server starting", map[string]interface{}{
|
|
"address": address,
|
|
})
|
|
|
|
if err := e.Start(address); err != nil && err != http.ErrServerClosed {
|
|
log.Fatal("Failed to start server", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
}()
|
|
|
|
// Wait for interrupt signal to gracefully shutdown the server
|
|
quit := make(chan os.Signal, 1)
|
|
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
|
|
<-quit
|
|
|
|
log.Info("Server is shutting down...", nil)
|
|
|
|
// Create a deadline for graceful shutdown
|
|
ctx, cancel = context.WithTimeout(context.Background(), config.Server.Timeout)
|
|
defer cancel()
|
|
|
|
// Gracefully shutdown the server
|
|
if err := e.Shutdown(ctx); err != nil {
|
|
log.Fatal("Server forced to shutdown", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
// Close MongoDB connection
|
|
if err := mongoClient.Disconnect(context.Background()); err != nil {
|
|
log.Error("Failed to disconnect from MongoDB", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
log.Info("Server stopped gracefully", nil)
|
|
|
|
// Cleanup logger to flush any remaining logs
|
|
logger.Cleanup()
|
|
}
|
|
|
|
// setupRoutes configures all API routes
|
|
func setupRoutes(
|
|
e *echo.Echo,
|
|
authHandler *handler.AuthHandler,
|
|
customerAuthHandler *handler.CustomerAuthHandler,
|
|
userAuthHandler *handler.UserAuthHandler,
|
|
) {
|
|
// Legacy/Generic authentication routes (backward compatibility)
|
|
auth := e.Group("/auth")
|
|
auth.POST("/register", authHandler.Register)
|
|
auth.POST("/login", authHandler.Login)
|
|
auth.POST("/refresh", authHandler.RefreshToken)
|
|
auth.POST("/change-password", authHandler.ChangePassword)
|
|
auth.GET("/profile", authHandler.GetProfile)
|
|
auth.POST("/logout", authHandler.Logout)
|
|
|
|
// API version group
|
|
api := e.Group("/api/v1")
|
|
|
|
// Mobile app routes (customers)
|
|
mobile := api.Group("/mobile")
|
|
mobileAuth := mobile.Group("/auth")
|
|
mobileAuth.POST("/register", customerAuthHandler.Register)
|
|
mobileAuth.POST("/login", customerAuthHandler.Login)
|
|
mobileAuth.POST("/refresh", customerAuthHandler.RefreshToken)
|
|
mobileAuth.POST("/verify-email", customerAuthHandler.VerifyEmail)
|
|
mobileAuth.POST("/resend-verification", customerAuthHandler.ResendVerification)
|
|
|
|
// Protected mobile routes
|
|
mobileAuth.POST("/change-password", customerAuthHandler.ChangePassword, customMiddleware.CustomerOnlyMiddleware())
|
|
mobileAuth.GET("/profile", customerAuthHandler.GetProfile, customMiddleware.CustomerOnlyMiddleware())
|
|
mobileAuth.POST("/logout", customerAuthHandler.Logout, customMiddleware.CustomerOnlyMiddleware())
|
|
|
|
// Mobile app tenders routes
|
|
mobileTenders := mobile.Group("/tenders", customMiddleware.CustomerOnlyMiddleware())
|
|
// TODO: Add tender routes for customers
|
|
_ = mobileTenders
|
|
|
|
// Mobile app companies routes
|
|
mobileCompanies := mobile.Group("/companies", customMiddleware.CustomerOnlyMiddleware())
|
|
// TODO: Add company routes for customers
|
|
_ = mobileCompanies
|
|
|
|
// Admin panel routes (panel users)
|
|
admin := api.Group("/admin")
|
|
adminAuth := admin.Group("/auth")
|
|
adminAuth.POST("/login", userAuthHandler.Login)
|
|
adminAuth.POST("/refresh", userAuthHandler.RefreshToken)
|
|
|
|
// Protected admin auth routes
|
|
adminAuth.POST("/change-password", userAuthHandler.ChangePassword, customMiddleware.UserOnlyMiddleware())
|
|
adminAuth.GET("/profile", userAuthHandler.GetProfile, customMiddleware.UserOnlyMiddleware())
|
|
adminAuth.POST("/logout", userAuthHandler.Logout, customMiddleware.UserOnlyMiddleware())
|
|
|
|
// Admin user management routes (admin only)
|
|
adminUsers := admin.Group("/users", customMiddleware.UserRoleMiddleware(domain.UserRoleAdmin))
|
|
adminUsers.POST("", userAuthHandler.CreatePanelUser)
|
|
adminUsers.PUT("/:user_id/permissions", userAuthHandler.UpdateUserPermissions)
|
|
|
|
// Admin customer management routes
|
|
adminCustomers := admin.Group("/customers", customMiddleware.UserRoleMiddleware(domain.UserRoleAdmin, domain.UserRoleModerator))
|
|
// TODO: Add customer management routes for admins
|
|
_ = adminCustomers
|
|
|
|
// Admin tender management routes
|
|
adminTenders := admin.Group("/tenders", customMiddleware.UserPermissionMiddleware("manage_tenders"))
|
|
// TODO: Add tender management routes for admins
|
|
_ = adminTenders
|
|
|
|
// Admin company management routes
|
|
adminCompanies := admin.Group("/companies", customMiddleware.UserPermissionMiddleware("manage_companies"))
|
|
// TODO: Add company management routes for admins
|
|
_ = adminCompanies
|
|
|
|
// Admin reports routes
|
|
adminReports := admin.Group("/reports", customMiddleware.UserPermissionMiddleware("view_reports"))
|
|
// TODO: Add reporting routes for admins
|
|
_ = adminReports
|
|
}
|