105 lines
6.1 KiB
Cheetah
105 lines
6.1 KiB
Cheetah
package settings
|
|
import (
|
|
"os"
|
|
{{if and (not .API) (ne .Storage "none")}}"strings"{{end}}
|
|
"time"
|
|
"git.yuntuo.hk/k2safe/go-omni/config"
|
|
"git.yuntuo.hk/k2safe/go-omni/logx"
|
|
{{if eq .Database "mysql"}}mysqladapter "git.yuntuo.hk/k2safe/go-omni/database/mysql"{{end}}
|
|
{{if ne .Storage "none"}}"errors"
|
|
{{if ne .Storage "r2"}}"net/url"{{else}}"regexp"{{end}}
|
|
"git.yuntuo.hk/k2safe/go-omni/storage"{{end}}
|
|
)
|
|
type Config struct {
|
|
Server config.Config `json:"server"`
|
|
{{if eq .Database "mysql"}}Database mysqladapter.Config `json:"database" env:"GO_OMNI_DB"`{{end}}
|
|
{{if ne .Storage "none"}}Storage StorageConfig `json:"storage" env:"GO_OMNI_STORAGE"`{{end}}
|
|
}
|
|
func Default() Config {
|
|
server:=config.Default()
|
|
server.Address="127.0.0.1:8080"
|
|
server.EnableLogger=true
|
|
server.SlowRequestThreshold=500*time.Millisecond
|
|
server.Log.Service={{printf "%q" .Module}}
|
|
server.Log.Format={{printf "%q" .LogFormat}}
|
|
{{if eq .LogOutput "file"}}server.Log.File="./var/log/server.log"
|
|
server.Log.Rotation=logx.RotationConfig{Enabled:true,MaxSizeMB:100,MaxBackups:10,MaxAgeDays:7,Compress:true}
|
|
{{else}}server.Log.Output={{printf "%q" .LogOutput}}{{end}}
|
|
return Config{Server:server{{if and .API (eq .Database "mysql")}},Database:mysqladapter.DefaultConfig(){{end}}{{if ne .Storage "none"}},Storage:StorageConfig{
|
|
Provider:{{printf "%q" .Storage}}, Root:"./var/files", PublicURL:"http://127.0.0.1:8080/storage/object",
|
|
Secure:true, MaxBytes:32<<20, ContentTypes:[]string{"text/plain","image/png","image/jpeg","application/pdf"},
|
|
LinkTTL:5*time.Minute, RequestTimeout:30*time.Second,
|
|
{{if eq .Storage "s3"}}Region:"us-east-1",{{end}}
|
|
{{if eq .Storage "r2"}}Region:"auto",{{end}}
|
|
{{if eq .Storage "oss"}}Region:"cn-hangzhou",{{end}}
|
|
}{{end}}}
|
|
}
|
|
|
|
// Load applies defaults -> file -> typed environment -> validation.
|
|
// New fields automatically use GO_OMNI_<JSON_PATH>, or an explicit env tag.
|
|
func Load(path string)(Config,error){
|
|
cfg:=Default();var err error
|
|
cfg,err=config.ApplyDefaults(cfg);if err!=nil{return Config{},err}
|
|
if path!="" {cfg,err=config.DecodeFile(path,cfg);if err!=nil{return Config{},err}}
|
|
cfg,err=config.OverlayEnv(cfg,config.EnvOptions{Prefix:"GO_OMNI_",Empty:"ignore",Aliases:config.ServerEnvAliases("GO_OMNI_","server")});if err!=nil{return Config{},err}
|
|
output,outputSet:=os.LookupEnv("GO_OMNI_SERVER_LOG_OUTPUT");if !outputSet {output=os.Getenv("GO_OMNI_LOG_OUTPUT")}
|
|
file,fileSet:=os.LookupEnv("GO_OMNI_SERVER_LOG_FILE");if !fileSet {file=os.Getenv("GO_OMNI_LOG_FILE")}
|
|
if output!="" && file=="" {cfg.Server.Log.File="";cfg.Server.Log.Rotation=logx.RotationConfig{}}
|
|
return cfg,cfg.Validate()
|
|
}
|
|
func(c Config)Validate()error{
|
|
if err:=c.Server.Validate();err!=nil{return err}
|
|
{{if eq .Database "mysql"}}if err:=c.Database.Validate();err!=nil{return err}{{end}}
|
|
{{if ne .Storage "none"}}if err:=c.Storage.Validate();err!=nil{return err}{{end}}
|
|
return nil
|
|
}
|
|
{{if ne .Storage "none"}}
|
|
type StorageConfig struct {
|
|
Provider string `json:"provider"`
|
|
Root string `json:"root"`
|
|
PublicURL string `json:"publicUrl"`
|
|
Endpoint string `json:"endpoint"`
|
|
Region string `json:"region"`
|
|
Bucket string `json:"bucket"`
|
|
AccountID string `json:"accountId"`
|
|
Secure bool `json:"secure"`
|
|
MaxBytes int64 `json:"maxBytes"`
|
|
ContentTypes []string `json:"contentTypes"`
|
|
LinkTTL time.Duration `json:"linkTtl"`
|
|
RequestTimeout time.Duration `json:"requestTimeout"`
|
|
// Secrets are environment-only and cannot be populated by config files.
|
|
FilesToken string `json:"-" env:"GO_OMNI_FILES_TOKEN,secret"`
|
|
SigningKey string `json:"-" env:"GO_OMNI_STORAGE_SIGNING_KEY,secret"`
|
|
AccessKey string `json:"-" env:"GO_OMNI_STORAGE_ACCESS_KEY,secret"`
|
|
SecretKey string `json:"-" env:"GO_OMNI_STORAGE_SECRET_KEY,secret"`
|
|
SessionToken string `json:"-" env:"GO_OMNI_STORAGE_SESSION_TOKEN,secret"`
|
|
}
|
|
func (s StorageConfig) Policy()storage.UploadPolicy{return storage.UploadPolicy{MaxBytes:s.MaxBytes,ContentTypes:s.ContentTypes}}
|
|
func (s StorageConfig) Validate()error {
|
|
if s.Provider!={{printf "%q" .Storage}} {return errors.New("storage.provider must match the adapter selected at generation")}
|
|
if err:=s.Policy().Validate();err!=nil{return err}
|
|
if s.LinkTTL<time.Second || s.LinkTTL>time.Hour{return errors.New("storage.linkTtl must be 1s..1h")}
|
|
if s.RequestTimeout<=0 || s.RequestTimeout>5*time.Minute{return errors.New("storage.requestTimeout must be >0 and <=5m")}
|
|
{{if not .API}}if len(s.FilesToken)<32 || strings.ContainsAny(s.FilesToken," \t\r\n") {return errors.New("set GO_OMNI_FILES_TOKEN to a token of at least 32 bytes without whitespace")}{{end}}
|
|
{{if eq .Storage "local"}}
|
|
if s.Root=="" {return errors.New("storage.root is required")}
|
|
u,err:=url.Parse(s.PublicURL)
|
|
if err!=nil || u.Host=="" || u.Hostname()=="" || (u.Scheme!="http" && u.Scheme!="https") || u.User!=nil || u.RawQuery!="" || u.Fragment!="" || u.Path!="/storage/object" {return errors.New("storage.publicUrl must be an absolute HTTP(S) URL ending in /storage/object")}
|
|
if len(s.SigningKey)<32 {return errors.New("set GO_OMNI_STORAGE_SIGNING_KEY to a stable secret of at least 32 bytes")}
|
|
{{else}}
|
|
if s.Bucket=="" || s.Region=="" || s.AccessKey=="" || s.SecretKey=="" {return errors.New("storage bucket, region and GO_OMNI_STORAGE_ACCESS_KEY/SECRET_KEY are required")}
|
|
{{if eq .Storage "r2"}}
|
|
if !regexp.MustCompile(`^[a-fA-F0-9]{32}$`).MatchString(s.AccountID) || s.Region!="auto" || !s.Secure {return errors.New("R2 requires a 32-hex accountId, region auto and secure=true")}
|
|
if s.Endpoint!=""{return errors.New("this R2 profile derives endpoint from accountId; customize servicecontext for a jurisdiction-specific endpoint")}
|
|
{{else if eq .Storage "s3"}}
|
|
u,err:=url.Parse("https://"+s.Endpoint)
|
|
if err!=nil || s.Endpoint=="" || u.Hostname()=="" || u.User!=nil || u.Path!="" || u.RawQuery!="" || u.Fragment!="" {return errors.New("S3 endpoint must be host[:port] without scheme or path")}
|
|
{{else if eq .Storage "oss"}}
|
|
if s.Endpoint!="" {u,err:=url.Parse(s.Endpoint);if err!=nil || u.Scheme!="https" || u.Hostname()=="" || u.User!=nil || u.Path!="" || u.RawQuery!="" || u.Fragment!="" {return errors.New("OSS endpoint must be an HTTPS origin")}}
|
|
if !s.Secure{return errors.New("OSS requires secure=true")}
|
|
{{end}}
|
|
{{end}}
|
|
return nil
|
|
}
|
|
{{end}}
|