Files
spyda/internal/config.go
2021-01-30 14:05:04 +10:00

196 lines
4.4 KiB
Go

package internal
import (
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net/url"
"os"
"regexp"
"strings"
"time"
"github.com/gabstv/merger"
"github.com/goccy/go-yaml"
"git.mills.io/prologic/spyda/types"
log "github.com/sirupsen/logrus"
)
var (
ErrConfigPathMissing = errors.New("error: config file missing")
)
// Settings contains Pod Settings that can be customised via the Web UI
type Settings struct {
Name string `yaml:"pod_name"`
Description string `yaml:"pod_description"`
MaxTwtLength int `yaml:"max_twt_length"`
OpenProfiles bool `yaml:"open_profiles"`
OpenRegistrations bool `yaml:"open_registrations"`
}
// Config contains the server configuration parameters
type Config struct {
Debug bool
Data string
Name string
Description string
Store string
Theme string
BaseURL string
AdminUser string
AdminName string
AdminEmail string
FeedSources []string
RegisterMessage string
CookieSecret string
TwtPrompts []string
TwtsPerPage int
MaxUploadSize int64
MaxTwtLength int
MaxCacheTTL time.Duration
MaxCacheItems int
MsgsPerPage int
OpenProfiles bool
OpenRegistrations bool
SessionExpiry time.Duration
SessionCacheTTL time.Duration
TranscoderTimeout time.Duration
MagicLinkSecret string
SMTPBind string
POP3Bind string
SMTPHost string
SMTPPort int
SMTPUser string
SMTPPass string
SMTPFrom string
MaxFetchLimit int64
APISessionTime time.Duration
APISigningKey string
baseURL *url.URL
whitelistedDomains []*regexp.Regexp
WhitelistedDomains []string
// path string
}
var _ types.FmtOpts = (*Config)(nil)
func (c *Config) IsLocalURL(url string) bool {
if NormalizeURL(url) == "" {
return false
}
return strings.HasPrefix(NormalizeURL(url), NormalizeURL(c.BaseURL))
}
func (c *Config) LocalURL() *url.URL { return c.baseURL }
func (c *Config) ExternalURL(nick, uri string) string { return URLForExternalProfile(c, nick, uri) }
func (c *Config) UserURL(url string) string { return UserURL(url) }
// Settings returns a `Settings` struct containing pod settings that can
// then be persisted to disk to override some configuration options.
func (c *Config) Settings() *Settings {
settings := &Settings{}
if err := merger.MergeOverwrite(settings, c); err != nil {
log.WithError(err).Warn("error creating pod settings")
}
return settings
}
// WhitelistedDomain returns true if the domain provided is a whiltelisted
// domain as per the configuration
func (c *Config) WhitelistedDomain(domain string) (bool, bool) {
// Always per mit our own domain
ourDomain := strings.TrimPrefix(strings.ToLower(c.baseURL.Hostname()), "www.")
if domain == ourDomain {
return true, true
}
// Check against list of whitelistedDomains (regexes)
for _, re := range c.whitelistedDomains {
if re.MatchString(domain) {
return true, false
}
}
return false, false
}
// RandomTwtPrompt returns a random Twt Prompt for display by the UI
func (c *Config) RandomTwtPrompt() string {
n := rand.Int() % len(c.TwtPrompts)
return c.TwtPrompts[n]
}
// Validate validates the configuration is valid which for the most part
// just ensures that default secrets are actually configured correctly
func (c *Config) Validate() error {
if c.Debug {
return nil
}
if c.CookieSecret == InvalidConfigValue {
return fmt.Errorf("error: COOKIE_SECRET is not configured!")
}
if c.MagicLinkSecret == InvalidConfigValue {
return fmt.Errorf("error: MAGICLINK_SECRET is not configured!")
}
if c.APISigningKey == InvalidConfigValue {
return fmt.Errorf("error: API_SIGNING_KEY is not configured!")
}
return nil
}
// LoadSettings loads pod settings from the given path
func LoadSettings(path string) (*Settings, error) {
var settings Settings
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
if err := yaml.Unmarshal(data, &settings); err != nil {
return nil, err
}
return &settings, nil
}
// Save saves the pod settings to the given path
func (s *Settings) Save(path string) error {
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return err
}
data, err := yaml.MarshalWithOptions(s, yaml.Indent(4))
if err != nil {
return err
}
if _, err = f.Write(data); err != nil {
return err
}
if err = f.Sync(); err != nil {
return err
}
return f.Close()
}