first
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"log/slog"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"backend/internal/modules/auth"
|
||||
"backend/internal/modules/categories"
|
||||
"backend/internal/modules/media"
|
||||
"backend/internal/modules/orders"
|
||||
"backend/internal/modules/pricing"
|
||||
"backend/internal/modules/products"
|
||||
"backend/internal/modules/site"
|
||||
"backend/internal/modules/telegram"
|
||||
"backend/internal/modules/units"
|
||||
"backend/internal/modules/users"
|
||||
"backend/internal/platform/config"
|
||||
"backend/internal/platform/db"
|
||||
"backend/internal/platform/logger"
|
||||
appmiddleware "backend/internal/platform/middleware"
|
||||
"backend/internal/platform/redis"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
log.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
appLogger := logger.New(cfg.Server.GinMode == "debug")
|
||||
slog.SetDefault(appLogger)
|
||||
|
||||
database, err := db.Connect(cfg.Database.URL, cfg.Server.GinMode == "debug")
|
||||
if err != nil {
|
||||
log.Fatalf("connect database: %v", err)
|
||||
}
|
||||
|
||||
redisClient, err := redis.Connect(cfg.Redis.URL)
|
||||
if err != nil {
|
||||
log.Fatalf("connect redis: %v", err)
|
||||
}
|
||||
|
||||
// --- Users module ---
|
||||
usersRepo := users.NewRepository(database)
|
||||
usersService := users.NewService(usersRepo)
|
||||
usersHandler := users.NewHandler(usersService)
|
||||
|
||||
// --- Auth module (admin space mounted; customer space wired but not mounted) ---
|
||||
refreshStore := auth.NewRefreshStore(redisClient)
|
||||
authService := auth.NewService(usersService, refreshStore, cfg.JWT.Secret, cfg.JWT.AccessTTL, cfg.JWT.RefreshTTL)
|
||||
adminAuthHandler := auth.NewAdminHandler(authService, cfg.Server.CookieSecure)
|
||||
|
||||
// --- Site module ---
|
||||
siteRepo := site.NewRepository(database)
|
||||
siteService := site.NewService(siteRepo)
|
||||
siteHandler := site.NewHandler(siteService)
|
||||
|
||||
// --- Media module (pluggable local/S3 storage) ---
|
||||
mediaStorage, err := buildMediaStorage(cfg.Media)
|
||||
if err != nil {
|
||||
log.Fatalf("init media storage: %v", err)
|
||||
}
|
||||
mediaRepo := media.NewRepository(database)
|
||||
mediaService := media.NewService(mediaRepo, mediaStorage, cfg.Media.MaxUploadMB*1024*1024)
|
||||
mediaHandler := media.NewHandler(mediaService)
|
||||
|
||||
// --- Categories module ---
|
||||
categoriesRepo := categories.NewRepository(database)
|
||||
categoriesService := categories.NewService(categoriesRepo)
|
||||
categoriesHandler := categories.NewHandler(categoriesService)
|
||||
|
||||
// --- Units module ---
|
||||
unitsRepo := units.NewRepository(database)
|
||||
unitsService := units.NewService(unitsRepo)
|
||||
unitsHandler := units.NewHandler(unitsService)
|
||||
|
||||
// --- Products module ---
|
||||
productsRepo := products.NewRepository(database)
|
||||
productsService := products.NewService(productsRepo)
|
||||
productsHandler := products.NewHandler(productsService)
|
||||
|
||||
// --- Pricing module (quantity-based tiers, spec section 16) ---
|
||||
pricingRepo := pricing.NewRepository(database)
|
||||
pricingService := pricing.NewService(pricingRepo)
|
||||
pricingHandler := pricing.NewHandler(pricingService)
|
||||
|
||||
// --- Telegram module (notifications, spec section 20) ---
|
||||
telegramRepo := telegram.NewRepository(database)
|
||||
telegramService := telegram.NewService(telegramRepo, telegram.NewHTTPSender(), appLogger)
|
||||
telegramHandler := telegram.NewHandler(telegramService)
|
||||
|
||||
// --- Orders module (depends on products/units/pricing for authoritative
|
||||
// pricing, and on telegram as its pluggable OrderNotifier) ---
|
||||
ordersRepo := orders.NewRepository(database)
|
||||
ordersService := orders.NewService(ordersRepo, productsService, unitsService, pricingService, telegramService)
|
||||
ordersHandler := orders.NewHandler(ordersService)
|
||||
|
||||
requireAdmin := appmiddleware.RequireAdmin(cfg.JWT.Secret)
|
||||
loginRateLimit := appmiddleware.NewPerIPRateLimiter(5, 5).Middleware()
|
||||
orderRateLimit := appmiddleware.NewPerIPRateLimiter(10, 10).Middleware()
|
||||
|
||||
gin.SetMode(cfg.Server.GinMode)
|
||||
router := gin.New()
|
||||
router.Use(gin.Recovery())
|
||||
router.Use(gin.Logger())
|
||||
router.Use(appmiddleware.CORS(cfg.Server.CORSOrigin))
|
||||
|
||||
if cfg.Media.Driver == "local" {
|
||||
router.Static("/uploads", cfg.Media.LocalDir)
|
||||
}
|
||||
|
||||
router.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"status": "ok"})
|
||||
})
|
||||
|
||||
api := router.Group("/api")
|
||||
|
||||
auth.RegisterAdminRoutes(api, adminAuthHandler, requireAdmin, loginRateLimit)
|
||||
users.RegisterAdminRoutes(api, usersHandler, requireAdmin)
|
||||
site.RegisterRoutes(api, siteHandler, requireAdmin)
|
||||
media.RegisterAdminRoutes(api, mediaHandler, requireAdmin)
|
||||
|
||||
categories.RegisterAdminRoutes(api, categoriesHandler, requireAdmin)
|
||||
categories.RegisterPublicRoutes(api, categoriesHandler)
|
||||
|
||||
units.RegisterAdminRoutes(api, unitsHandler, requireAdmin)
|
||||
|
||||
products.RegisterAdminRoutes(api, productsHandler, requireAdmin)
|
||||
products.RegisterPublicRoutes(api, productsHandler)
|
||||
|
||||
pricing.RegisterAdminRoutes(api, pricingHandler, requireAdmin)
|
||||
pricing.RegisterPublicRoutes(api, pricingHandler)
|
||||
|
||||
telegram.RegisterAdminRoutes(api, telegramHandler, requireAdmin)
|
||||
|
||||
orders.RegisterPublicRoutes(api, ordersHandler, orderRateLimit)
|
||||
orders.RegisterAdminRoutes(api, ordersHandler, requireAdmin)
|
||||
|
||||
appLogger.Info("starting server", "port", cfg.Server.Port)
|
||||
if err := router.Run(":" + cfg.Server.Port); err != nil {
|
||||
log.Fatalf("server error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func buildMediaStorage(cfg config.MediaConfig) (media.Storage, error) {
|
||||
if cfg.Driver == "s3" {
|
||||
return media.NewS3Storage(context.Background(), media.S3Config{
|
||||
Bucket: cfg.S3Bucket,
|
||||
Region: cfg.S3Region,
|
||||
Endpoint: cfg.S3Endpoint,
|
||||
AccessKeyID: cfg.S3AccessKeyID,
|
||||
SecretKey: cfg.S3SecretKey,
|
||||
UsePathStyle: cfg.S3UsePathStyle,
|
||||
PublicBaseURL: cfg.S3PublicBaseURL,
|
||||
})
|
||||
}
|
||||
return media.NewLocalStorage(cfg.LocalDir, cfg.LocalBaseURL)
|
||||
}
|
||||
Reference in New Issue
Block a user