70 lines
2.7 KiB
Cheetah
70 lines
2.7 KiB
Cheetah
package servicecontext
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
{{if ne .Storage "none"}}"net/http"
|
|
"git.yuntuo.hk/k2safe/go-omni/storage"{{end}}
|
|
{{if eq .Database "mysql"}}"time"
|
|
"git.yuntuo.hk/k2safe/go-omni/database"
|
|
"github.com/go-sql-driver/mysql"{{end}}
|
|
"git.yuntuo.hk/k2safe/go-omni/health"
|
|
"git.yuntuo.hk/k2safe/go-omni/httpx"
|
|
"git.yuntuo.hk/k2safe/go-omni/logx"
|
|
"{{.Module}}/internal/settings"
|
|
"{{.Module}}/internal/middleware"
|
|
)
|
|
// Context owns infrastructure, while Logger is borrowed from main.
|
|
// Add business repositories here; HTTP contracts do not define database tables.
|
|
type Context struct {
|
|
Logger *logx.Logger
|
|
Health *health.Checker
|
|
Middlewares map[string]httpx.Middleware
|
|
{{if eq .Database "mysql"}}DB *database.DB{{end}}
|
|
{{if ne .Storage "none"}}
|
|
Files storage.Store
|
|
Uploads *storage.Uploader
|
|
Downloads storage.Presigner
|
|
UploadLinks storage.UploadPresigner
|
|
FileHTTP http.Handler
|
|
storageClose func()error
|
|
{{end}}
|
|
closeMu sync.Mutex
|
|
closed bool
|
|
closeOnce sync.Once
|
|
closeErr error
|
|
}
|
|
// Pass the settings loaded by main. The optional form supports lightweight
|
|
// tests without infrastructure; selected adapters still require valid settings.
|
|
func New(ctx context.Context,logger *logx.Logger,configs ...settings.Config)(result *Context,err error){
|
|
if ctx==nil{ctx=context.Background()}
|
|
if err=ctx.Err();err!=nil{return nil,err}
|
|
if len(configs)>1{return nil,errors.New("expected at most one configuration")}
|
|
cfg:=settings.Default();if len(configs)==1{cfg=configs[0]}
|
|
_=cfg
|
|
checker,err:=health.New();if err!=nil{return nil,err}
|
|
c:=&Context{Logger:logger,Health:checker,Middlewares:middleware.Defaults()}
|
|
defer func(){if err!=nil{err=errors.Join(err,c.Close())}}()
|
|
{{if eq .Database "mysql"}}
|
|
if err=cfg.Validate();err!=nil{return nil,err}
|
|
dsn,parseErr:=mysql.ParseDSN(cfg.Database.DSN)
|
|
if parseErr!=nil || dsn.DBName=="" {return nil,errors.New("GO_OMNI_DB_DSN must contain a valid MySQL database name")}
|
|
dsn.ParseTime=true;dsn.Loc=time.UTC
|
|
cfg.Database.DSN=dsn.FormatDSN()
|
|
c.DB,err=database.Open(ctx,cfg.Database);if err!=nil{return nil,err}
|
|
if err=checker.Add(health.Probe{Name:"database",Timeout:cfg.Database.PingTimeout,Check:c.DB.Health});err!=nil{return nil,err}
|
|
{{end}}
|
|
{{if ne .Storage "none"}}if err=c.InitStorage(ctx,cfg.Storage);err!=nil{return nil,err}{{end}}
|
|
return c,nil
|
|
}
|
|
// Close is idempotent, including startup failure and graceful shutdown.
|
|
func(c *Context)Close()error{
|
|
if c==nil{return nil}
|
|
c.closeMu.Lock();defer c.closeMu.Unlock();c.closed=true
|
|
c.closeOnce.Do(func(){
|
|
{{if ne .Storage "none"}}if c.storageClose!=nil{c.closeErr=errors.Join(c.closeErr,c.storageClose())}{{end}}
|
|
{{if eq .Database "mysql"}}if c.DB!=nil{c.closeErr=errors.Join(c.closeErr,c.DB.Close())}{{end}}
|
|
})
|
|
return c.closeErr
|
|
}
|