209 lines
6.3 KiB
Go
209 lines
6.3 KiB
Go
package di
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"cloudnest/internal/application/auth"
|
|
"cloudnest/internal/application/captcha"
|
|
"cloudnest/internal/application/email"
|
|
"cloudnest/internal/application/file"
|
|
"cloudnest/internal/config"
|
|
"cloudnest/internal/domain/auth/repository"
|
|
fileRepo "cloudnest/internal/domain/file/repository"
|
|
"cloudnest/internal/infrastructure/database"
|
|
infrastructureRepo "cloudnest/internal/infrastructure/repository"
|
|
"cloudnest/internal/infrastructure/storage"
|
|
"cloudnest/internal/interface/http/handler"
|
|
"cloudnest/internal/interface/http/routes"
|
|
"cloudnest/internal/middleware"
|
|
"cloudnest/internal/pkg/logger"
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/dig"
|
|
)
|
|
|
|
type Container struct {
|
|
*dig.Container
|
|
}
|
|
|
|
func New() *Container {
|
|
c := dig.New()
|
|
|
|
c.Provide(func() *config.Config {
|
|
return config.Load()
|
|
})
|
|
|
|
c.Provide(func(cfg *config.Config) (*database.MySQLClient, error) {
|
|
return database.NewMySQLClient(cfg.Database.DSN())
|
|
})
|
|
|
|
c.Provide(func(cfg *config.Config) (*database.RedisClient, error) {
|
|
return database.NewRedisClient(cfg.Cache.Addr(), cfg.Cache.Password, cfg.Cache.DB)
|
|
})
|
|
|
|
c.Provide(func(cfg *config.Config) (*storage.MinioClient, error) {
|
|
return storage.NewMinioClient(
|
|
cfg.Storage.Endpoint,
|
|
cfg.Storage.AccessKeyID,
|
|
cfg.Storage.SecretAccessKey,
|
|
cfg.Storage.UseSSL,
|
|
)
|
|
})
|
|
|
|
c.Provide(func(mysql *database.MySQLClient) repository.UserRepository {
|
|
return infrastructureRepo.NewUserRepository(mysql)
|
|
})
|
|
|
|
c.Provide(func(minio *storage.MinioClient) fileRepo.FileRepository {
|
|
return infrastructureRepo.NewFileRepository(minio)
|
|
})
|
|
|
|
c.Provide(func(mysql *database.MySQLClient) *infrastructureRepo.FileMetaRepository {
|
|
return infrastructureRepo.NewFileMetaRepository(mysql)
|
|
})
|
|
|
|
c.Provide(func(userRepo repository.UserRepository, emailService *email.EmailService, cfg *config.Config) *auth.AuthService {
|
|
return auth.NewAuthService(userRepo, emailService, cfg.JWT.Secret, cfg.JWT.ExpiresIn)
|
|
})
|
|
|
|
c.Provide(func(fRepo fileRepo.FileRepository, fMetaRepo *infrastructureRepo.FileMetaRepository, cfg *config.Config) *file.FileService {
|
|
return file.NewFileService(fRepo, fMetaRepo, cfg.Storage.BucketName)
|
|
})
|
|
|
|
c.Provide(func(redis *database.RedisClient) *captcha.CaptchaService {
|
|
return captcha.NewCaptchaService(redis)
|
|
})
|
|
|
|
c.Provide(func(cfg *config.Config, redis *database.RedisClient) *email.EmailService {
|
|
return email.NewEmailService(&cfg.Email, redis)
|
|
})
|
|
|
|
c.Provide(func(service *auth.AuthService, captchaService *captcha.CaptchaService, emailService *email.EmailService) *handler.AuthHandler {
|
|
return handler.NewAuthHandler(service, captchaService, emailService)
|
|
})
|
|
|
|
c.Provide(func(emailService *email.EmailService, captchaService *captcha.CaptchaService) *handler.EmailHandler {
|
|
return handler.NewEmailHandler(emailService, captchaService)
|
|
})
|
|
|
|
c.Provide(func(service *file.FileService, authService *auth.AuthService) *handler.FileHandler {
|
|
return handler.NewFileHandler(service, authService)
|
|
})
|
|
|
|
c.Provide(func(service *captcha.CaptchaService) *handler.CaptchaHandler {
|
|
return handler.NewCaptchaHandler(service)
|
|
})
|
|
|
|
return &Container{c}
|
|
}
|
|
|
|
// BuildEngine builds the Gin engine with routes based on service type.
|
|
// BuildEngine 根据服务类型构建 Gin 引擎并注册路由。
|
|
//
|
|
// Parameters:
|
|
// - serviceType: The type of service (auth or file).
|
|
//
|
|
// Returns:
|
|
// - *gin.Engine: The configured Gin engine.
|
|
// - error: Any error that occurred during setup.
|
|
//
|
|
// 参数:
|
|
// - serviceType: 服务类型 (auth 或 file)。
|
|
//
|
|
// 返回值:
|
|
// - *gin.Engine: 配置好的 Gin 引擎。
|
|
// - error: 设置过程中发生的任何错误。
|
|
func (c *Container) BuildEngine(serviceType routes.ServiceType) (*gin.Engine, error) {
|
|
var engine *gin.Engine
|
|
err := c.Invoke(func(cfg *config.Config, authHandler *handler.AuthHandler, fileHandler *handler.FileHandler, captchaHandler *handler.CaptchaHandler, emailHandler *handler.EmailHandler) {
|
|
engine = gin.New()
|
|
|
|
engine.RedirectTrailingSlash = false
|
|
engine.RedirectFixedPath = false
|
|
|
|
engine.Use(gin.Recovery())
|
|
engine.Use(middleware.CORS())
|
|
engine.Use(middleware.ErrorHandler())
|
|
|
|
engine.GET("/healthz", func(ctx *gin.Context) {
|
|
ctx.JSON(http.StatusOK, gin.H{"status": "ok"})
|
|
})
|
|
|
|
routes.SetupRoutes(engine, serviceType, authHandler, fileHandler, captchaHandler, emailHandler, cfg.JWT.Secret)
|
|
|
|
logger.Info("engine built successfully")
|
|
})
|
|
|
|
return engine, err
|
|
}
|
|
|
|
// BuildAuthEngine builds the Gin engine for auth service.
|
|
// BuildAuthEngine 为认证服务构建 Gin 引擎。
|
|
//
|
|
// This method only registers authentication routes.
|
|
//
|
|
// 此方法只注册认证路由。
|
|
func (c *Container) BuildAuthEngine() (*gin.Engine, error) {
|
|
return c.BuildEngine(routes.ServiceTypeAuth)
|
|
}
|
|
|
|
// BuildFileEngine builds the Gin engine for file service.
|
|
// BuildFileEngine 为文件服务构建 Gin 引擎。
|
|
//
|
|
// This method only registers file management routes.
|
|
//
|
|
// 此方法只注册文件管理路由。
|
|
func (c *Container) BuildFileEngine() (*gin.Engine, error) {
|
|
return c.BuildEngine(routes.ServiceTypeFile)
|
|
}
|
|
|
|
func (c *Container) InitializeServices() error {
|
|
return c.Invoke(func(mysql *database.MySQLClient, redis *database.RedisClient, minio *storage.MinioClient, cfg *config.Config) error {
|
|
if err := mysql.Migrate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := minio.EnsureBucket(cfg.Storage.BucketName); err != nil {
|
|
return err
|
|
}
|
|
|
|
logger.Info("all services initialized")
|
|
|
|
go startRecycleBinCleaner(cfg, mysql, minio)
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func startRecycleBinCleaner(cfg *config.Config, mysql *database.MySQLClient, minio *storage.MinioClient) {
|
|
expireDays := cfg.Storage.RecycleBinExpireDays
|
|
if expireDays <= 0 {
|
|
expireDays = 7
|
|
}
|
|
|
|
fMetaRepo := infrastructureRepo.NewFileMetaRepository(mysql)
|
|
fRepo := infrastructureRepo.NewFileRepository(minio)
|
|
|
|
ticker := time.NewTicker(24 * time.Hour)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
logger.Info("starting recycle bin cleanup", "expire_days", expireDays)
|
|
|
|
expiredFiles, err := fMetaRepo.CleanExpiredRecycleBin(expireDays)
|
|
if err != nil {
|
|
logger.Error("failed to clean recycle bin", "error", err)
|
|
continue
|
|
}
|
|
|
|
for _, df := range expiredFiles {
|
|
if err := fRepo.Delete(df.UserCode, df.FileKey); err != nil {
|
|
logger.Error("failed to delete file from storage", "file_key", df.FileKey, "error", err)
|
|
}
|
|
}
|
|
|
|
logger.Info("recycle bin cleanup completed", "files_cleaned", len(expiredFiles))
|
|
|
|
<-ticker.C
|
|
}
|
|
} |