thread-master/apps/backend/internal/svc/service_context.go

150 lines
4.4 KiB
Go
Raw Normal View History

2026-07-10 12:54:45 +00:00
package svc
import (
"strings"
"apps/backend/internal/config"
"apps/backend/internal/middleware"
fsDomain "apps/backend/internal/module/filestorage/domain"
"apps/backend/internal/module/filestorage/noop"
"apps/backend/internal/module/filestorage/s3store"
memberDomain "apps/backend/internal/module/member/domain"
memberRepo "apps/backend/internal/module/member/repository"
memberUC "apps/backend/internal/module/member/usecase"
notifDomain "apps/backend/internal/module/notification/domain"
notifUC "apps/backend/internal/module/notification/usecase"
tokenUC "apps/backend/internal/module/token/usecase"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest"
)
// ServiceContext wires goctl middleware + modules.
//
// 持久化(關機不丟):
// - members / settings / refresh_tokens / auth_codes → Mongo
// Redis可丟、可重建:
// - monc 實體快取 onlyCacheRedis
type ServiceContext struct {
Config config.Config
AuthJWT rest.Middleware
AdminAuth rest.Middleware
Members memberDomain.Repository
Auth *memberUC.AuthService
Token *tokenUC.JWTIssuer
Notification notifDomain.UseCase
Storage fsDomain.Storage
}
func NewServiceContext(c config.Config) *ServiceContext {
if len(c.CacheRedis) == 0 {
logx.Must(errString("CacheRedis is required for monc entity cache"))
}
repo := memberRepo.NewMoncStore(c.Mongo.URI, c.Mongo.Database, c.CacheRedis)
logx.Infof("mongo monc ready db=%s redis_cache=%s", c.Mongo.Database, c.CacheRedis[0].Host)
issuer := tokenUC.NewJWTIssuer(
c.Auth.AccessSecret,
c.Auth.RefreshSecret,
c.Auth.AccessExpire,
c.Auth.RefreshExpire,
)
cost := c.Bcrypt.Cost
if cost <= 0 {
cost = 10
}
auth := memberUC.NewAccountService(repo, issuer, cost)
auth.GoogleClientID = c.OAuth.GoogleClientID
auth.LineClientID = c.OAuth.LineClientID
auth.LineClientSecret = c.OAuth.LineClientSecret
auth.LineRedirectURI = c.OAuth.LineRedirectURI
notify := newNotification(c)
store := newStorage(c)
return &ServiceContext{
Config: c,
Members: repo,
Auth: auth,
Token: issuer,
Notification: notify,
Storage: store,
AuthJWT: middleware.NewAuthJWTMiddleware(issuer, repo).Handle,
AdminAuth: middleware.NewAdminAuthMiddleware().Handle,
}
}
// newStorage 只接 MinIO/S3S3 API不落本機磁碟。
func newStorage(c config.Config) fsDomain.Storage {
os := c.ObjectStorage
if strings.TrimSpace(os.Endpoint) == "" {
logx.Error("object storage: Endpoint empty — uploads disabled (MinIO/S3 required)")
return noop.New()
}
s, err := s3store.New(s3store.Config{
Endpoint: os.Endpoint,
Region: os.Region,
Bucket: os.Bucket,
AccessKey: os.AccessKey,
SecretKey: os.SecretKey,
UsePathStyle: os.UsePathStyle,
PublicBaseURL: os.PublicBaseURL,
})
if err != nil {
logx.Errorf("object storage MinIO/S3 init failed: %v — uploads disabled", err)
return noop.New()
}
logx.Infof("object storage: minio/s3 endpoint=%s bucket=%s", os.Endpoint, os.Bucket)
return s
}
func newNotification(c config.Config) notifDomain.UseCase {
base := strings.TrimRight(c.PublicWebBase, "/")
if base == "" {
base = "http://127.0.0.1:5173"
}
brand := notifDomain.Brand{
Name: c.Brand.Name,
Link: base,
LogoURL: c.Brand.LogoURL,
Copyright: c.Brand.Copyright,
}
if brand.Name == "" {
brand.Name = "Harbor Desk"
}
if brand.LogoURL == "" {
// apps/web/public/brand-mark.jpg — 郵件客戶端需絕對 URL
brand.LogoURL = base + "/brand-mark.jpg"
}
if brand.Copyright == "" {
brand.Copyright = "© Harbor Desk"
}
cfg := notifUC.Config{
Sender: c.Mail.Sender,
DevExposeCode: c.Mail.DevExposeCode,
Brand: brand,
}
if c.Mail.SMTP.Host != "" {
cfg.Providers = []notifDomain.EmailDelivery{
notifUC.NewSMTPDelivery(notifUC.SMTPConfig{
Host: c.Mail.SMTP.Host,
Port: c.Mail.SMTP.Port,
User: c.Mail.SMTP.User,
Password: c.Mail.SMTP.Password,
}),
}
logx.Infof("notification mail: smtp host=%s port=%d logo=%s expose_code=%v",
c.Mail.SMTP.Host, c.Mail.SMTP.Port, brand.LogoURL, c.Mail.DevExposeCode)
} else {
cfg.Providers = []notifDomain.EmailDelivery{notifUC.NewLogDelivery()}
logx.Info("notification mail: log-only (set Mail.SMTP.Host to send real email)")
}
return notifUC.NewService(cfg)
}
type errString string
func (e errString) Error() string { return string(e) }