template-monorepo/internal/model/member/usecase/module.go

127 lines
3.9 KiB
Go
Raw Normal View History

package usecase
import (
"fmt"
2026-05-20 13:03:59 +00:00
libcrypto "gateway/internal/library/crypto"
2026-05-20 23:51:22 +00:00
libmongo "gateway/internal/library/mongo"
redislib "gateway/internal/library/redis"
memberconfig "gateway/internal/model/member/config"
domrepo "gateway/internal/model/member/domain/repository"
domusecase "gateway/internal/model/member/domain/usecase"
"gateway/internal/model/member/repository"
)
2026-05-20 13:03:59 +00:00
// Module bundles member atomic primitives. Each entry is a single-purpose
// usecase; composite flows (e.g. "send verification email then mark
// business_email verified") are assembled at the logic / driver layer and
// MUST NOT live inside another usecase.
type Module struct {
2026-05-20 23:51:22 +00:00
OTP domusecase.OTPUseCase
TOTP domusecase.TOTPUseCase
Profile domusecase.ProfileUseCase
Lifecycle domusecase.LifecycleUseCase
Provisioning domusecase.ProvisioningUseCase
Tenant domusecase.TenantUseCase
VerifyRate domusecase.VerifyRateUseCase
2026-05-20 13:03:59 +00:00
2026-05-20 23:51:22 +00:00
Members domrepo.MemberRepository
Tenants domrepo.TenantRepository
Identities domrepo.IdentityRepository
}
// ModuleParam wires member module dependencies.
type ModuleParam struct {
2026-05-20 23:51:22 +00:00
Redis *redislib.Client
MongoConf *libmongo.Conf
Config memberconfig.Config
// Optional overrides for tests.
Members domrepo.MemberRepository
Tenants domrepo.TenantRepository
Identities domrepo.IdentityRepository
2026-05-20 13:03:59 +00:00
TOTPProfile domrepo.TOTPProfileRepository
2026-05-20 23:51:22 +00:00
UIDGen domrepo.UIDGenerator
}
2026-05-20 13:03:59 +00:00
// NewModuleFromParam builds member atomic usecases.
func NewModuleFromParam(param ModuleParam) (*Module, error) {
if param.Redis == nil || param.Redis.Zero() == nil {
return nil, fmt.Errorf("member: redis is required")
}
2026-05-20 23:51:22 +00:00
cfg := param.Config.Defaults()
otpStore := repository.NewRedisOTPChallengeStore(param.Redis)
rateStore := repository.NewRedisVerifyRateStore(param.Redis)
2026-05-20 23:51:22 +00:00
members := param.Members
tenants := param.Tenants
identities := param.Identities
uidGen := param.UIDGen
totpProfile := param.TOTPProfile
if param.MongoConf != nil && param.MongoConf.Host != "" {
if members == nil {
members = repository.NewMemberRepository(repository.MemberRepositoryParam{Conf: param.MongoConf})
}
if tenants == nil {
tenants = repository.NewTenantRepository(repository.TenantRepositoryParam{Conf: param.MongoConf})
}
if identities == nil {
identities = repository.NewIdentityRepository(repository.IdentityRepositoryParam{Conf: param.MongoConf})
}
if totpProfile == nil {
totpProfile = repository.NewMongoTOTPProfileRepository(param.MongoConf)
}
}
if uidGen == nil {
uidGen = repository.NewRedisUIDGenerator(param.Redis)
}
if totpProfile == nil {
totpProfile = repository.NewMemoryTOTPProfileRepository()
}
2026-05-20 13:03:59 +00:00
mod := &Module{
OTP: MustOTPUseCase(OTPUseCaseParam{Store: otpStore, Config: cfg}),
VerifyRate: MustVerifyRateUseCase(VerifyRateUseCaseParam{Store: rateStore}),
2026-05-20 23:51:22 +00:00
Members: members,
Tenants: tenants,
Identities: identities,
}
if members != nil {
mod.Profile = MustProfileUseCase(ProfileUseCaseParam{Members: members})
}
if members != nil && tenants != nil && uidGen != nil {
mod.Lifecycle = MustLifecycleUseCase(LifecycleUseCaseParam{
Members: members,
Tenants: tenants,
UIDGen: uidGen,
})
mod.Tenant = MustTenantUseCase(TenantUseCaseParam{Tenants: tenants})
}
if members != nil && identities != nil && tenants != nil && uidGen != nil {
mod.Provisioning = MustProvisioningUseCase(ProvisioningUseCaseParam{
Members: members,
Identities: identities,
Tenants: tenants,
UIDGen: uidGen,
})
2026-05-20 13:03:59 +00:00
}
if cfg.TOTP.SecretKEK != "" {
cipher, err := libcrypto.NewAESGCMFromString(cfg.TOTP.SecretKEK)
if err != nil {
return nil, fmt.Errorf("member: totp kek: %w", err)
}
mod.TOTP = MustTOTPUseCase(TOTPUseCaseParam{
Profile: totpProfile,
Enroll: repository.NewRedisTOTPEnrollStore(param.Redis),
Replay: repository.NewRedisTOTPReplayStore(param.Redis),
Cipher: cipher,
Config: cfg,
})
}
return mod, nil
}