All checks were successful
Build Production Image / Build Production Image (push) Successful in 9m11s
273 lines
7.6 KiB
Go
273 lines
7.6 KiB
Go
package config
|
|
|
|
import (
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
const (
|
|
migrationsDir = "db/migrations"
|
|
|
|
// templateDir stores the name of the directory that contains templates
|
|
templateDir = "templates"
|
|
|
|
// TemplateExt stores the extension used for the template files
|
|
TemplateExt = ".gohtml"
|
|
|
|
// staticDir stores the name of the directory that will serve static files
|
|
staticDir = "static"
|
|
|
|
// StaticPrefix stores the URL prefix used when serving static files
|
|
StaticPrefix = "files"
|
|
|
|
// UserContentPrefix stores the URL prefix used when service static files that were uploaded by users.
|
|
UserContentPrefix = "usercontent"
|
|
)
|
|
|
|
type environment string
|
|
|
|
const (
|
|
// EnvLocal represents the local environment
|
|
EnvLocal environment = "local"
|
|
|
|
// EnvTest represents the test environment
|
|
EnvTest environment = "test"
|
|
|
|
// EnvProduction represents the production environment
|
|
EnvProduction environment = "prod"
|
|
)
|
|
|
|
// builtinDirectoryPath returns the path to a directory depending on whether the
|
|
// app is running in production or not. During development and testing it is
|
|
// assumed that the directory is contained in the root directory. During
|
|
// production it should be at the filesystem root since it will be included in
|
|
// the container at build time.
|
|
func builtinDirectoryPath(cfg *Config, dirname string) string {
|
|
if cfg.App.Environment == EnvProduction {
|
|
return "/" + dirname
|
|
}
|
|
return filepath.Join(cfg.Storage.RootDir, dirname)
|
|
}
|
|
|
|
func StaticDir(cfg *Config) string {
|
|
return builtinDirectoryPath(cfg, staticDir)
|
|
}
|
|
|
|
func TemplateDir(cfg *Config) string {
|
|
return builtinDirectoryPath(cfg, templateDir)
|
|
}
|
|
|
|
func MigrationsDir(cfg *Config) string {
|
|
return builtinDirectoryPath(cfg, migrationsDir)
|
|
}
|
|
|
|
type (
|
|
// Config stores complete configuration
|
|
Config struct {
|
|
HTTP HTTPConfig
|
|
App AppConfig
|
|
Flags FlagsConfig
|
|
Cache CacheConfig
|
|
Storage StorageConfig
|
|
Mail MailConfig
|
|
Mapbox MapboxConfig
|
|
Discourse DiscourseConfig
|
|
Wiki WikiConfig
|
|
Stripe StripeConfig
|
|
}
|
|
|
|
// HTTPConfig stores HTTP configuration
|
|
HTTPConfig struct {
|
|
Hostname string
|
|
Port uint16
|
|
ReadTimeout time.Duration
|
|
WriteTimeout time.Duration
|
|
IdleTimeout time.Duration
|
|
TLS struct {
|
|
Enabled bool
|
|
Certificate string
|
|
Key string
|
|
}
|
|
}
|
|
|
|
// AppConfig stores application configuration
|
|
AppConfig struct {
|
|
Name string
|
|
Environment environment
|
|
// Location is the IANA timezone database name where the users are located.
|
|
// This assumes that the application serves a narrow geographical area.
|
|
Location string
|
|
// Loc is the parsed Location
|
|
Loc *time.Location
|
|
// Should include the protocol but not the trailing slash.
|
|
PublicURL string
|
|
// THIS MUST BE OVERRIDDEN ON ANY LIVE ENVIRONMENTS
|
|
EncryptionKey string
|
|
Timeout time.Duration
|
|
EmailVerificationTokenExpiration time.Duration
|
|
|
|
// MaxWorkers defines the maximum number of workers that will be created
|
|
// in the worker pool for async tasks.
|
|
MaxWorkers int
|
|
|
|
// MetricsPort is the port on which Prometheus metrics will be served.
|
|
MetricsPort uint16
|
|
|
|
// HooksUsername is the basic auth username which protects the hooks endpoints
|
|
HooksUsername string
|
|
|
|
// HooksPassword string is the basic auth password which protects the hooks endpoints
|
|
HooksPassword string
|
|
}
|
|
|
|
FlagsConfig struct{}
|
|
|
|
// CacheConfig stores the cache configuration
|
|
// TODO: Tune these
|
|
CacheConfig struct {
|
|
NumCounters int64
|
|
MaxCost int64
|
|
BufferItems int64
|
|
Expiration struct {
|
|
StaticFile time.Duration
|
|
Page time.Duration
|
|
}
|
|
}
|
|
|
|
// Storage stores the configuration for different types of storage. The
|
|
// template and static file directories are not included here because their
|
|
// location is predefined at the root of the container when running in
|
|
// production and
|
|
StorageConfig struct {
|
|
// PhotoStorageDir is the path relative to RootDir where uploaded
|
|
// photos are stored until they can be converted and stored in the database.
|
|
PhotoStorageDir string
|
|
|
|
// DatabaseFile is the absolute path to the database sqlite file.
|
|
DatabaseFile string
|
|
|
|
// RBACPolicyPath is the path relative to the root dir containing the policy csv.
|
|
RBACPolicyPath string
|
|
|
|
// RootDir is the absolute path to the directory where the templates,
|
|
// db, and static directories can be found. This setting will default to
|
|
// "/" when not set, and should not be set in production.
|
|
RootDir string
|
|
}
|
|
|
|
// MailConfig stores the mail configuration
|
|
MailConfig struct {
|
|
// ConfigurePostmark controls whether the application will attempt to
|
|
// configure the Postmark provider by creating the message streams and
|
|
// webhooks.
|
|
ConfigurePostmark bool
|
|
|
|
Token string
|
|
Enable bool
|
|
|
|
// Domain is the part after the @ where this application is listening for emails.
|
|
// It is used for anonymous email addresses on classified posts.
|
|
Domain string
|
|
|
|
// FromAddress is the email address used to send emails from the app.
|
|
FromAddress string
|
|
|
|
// DigestsAddress is the email used to send the twice weekly email digests.
|
|
DigestsAddress string
|
|
|
|
// SupportAddress is the address which users send to when they need support.
|
|
// Emails received for this address will be forwarded to the AdminAddress.
|
|
SupportAddress string
|
|
|
|
// EventsAddress is the address used for RSVPs on events.
|
|
EventsAddress string
|
|
|
|
// AdminAddress is the address which incoming emails that are for an unknown address for for support are forwarded to.
|
|
// It should be a real mailbox, not the email provider used by this app, otherwise a loop will occur.
|
|
AdminAddress string
|
|
}
|
|
|
|
// MapboxConfig stores the configuration for interacting with Mapbox
|
|
MapboxConfig struct {
|
|
Token string
|
|
|
|
// BoundingBox is a string such as "-123.0414,48.5843,-122.7352,48.7191"
|
|
// which denotes the minimum area that should be included in the map
|
|
// image retrieved.
|
|
//
|
|
// The bounding box is also used to restrict address lookups.
|
|
BoundingBox string
|
|
|
|
// Resolution is a string such as "560x420" which determines the
|
|
// resolution of the image retrieved from MapBox.
|
|
Resolution string
|
|
}
|
|
|
|
DiscourseConfig struct {
|
|
// ClientID is the OAuth2 client_id used by Discourse to connect to this server.
|
|
ClientID string
|
|
|
|
// ClientSecret is the OAuth2 client_secret used by Discourse to connect to this server.
|
|
ClientSecret string
|
|
|
|
// Domain is the domain Discourse is accessible on.
|
|
Domain string
|
|
}
|
|
|
|
// Parameters are the same as for the DiscourseConfig.
|
|
WikiConfig struct {
|
|
ClientID string
|
|
ClientSecret string
|
|
Domain string
|
|
}
|
|
|
|
StripeConfig struct {
|
|
SecretKey string
|
|
PublishableKey string
|
|
|
|
// ID of the subscription product in Stripe. This is used for creating
|
|
// custom subscription amounts.
|
|
SubscriptionIDProduct string
|
|
|
|
// ID of the $5/yr subscription in Stripe.
|
|
SubscriptionID5 string
|
|
|
|
// ID of the $10/yr subscription in Stripe.
|
|
SubscriptionID10 string
|
|
|
|
// ID of the $25/yr subscription in Stripe.
|
|
SubscriptionID25 string
|
|
}
|
|
)
|
|
|
|
// GetConfig loads and returns configuration
|
|
func GetConfig(configFilePath string) (Config, error) {
|
|
viper.SetConfigFile(configFilePath)
|
|
|
|
configDir := filepath.Dir(configFilePath)
|
|
viper.SetDefault("storage.rootDir", configDir)
|
|
viper.SetDefault("storage.DatabaseFile", "data.sqlite")
|
|
viper.SetDefault("storage.PhotoStorageDir", "storage")
|
|
|
|
var c Config
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
return c, err
|
|
}
|
|
|
|
if err := viper.Unmarshal(&c); err != nil {
|
|
return c, err
|
|
}
|
|
|
|
var err error
|
|
c.App.Loc, err = time.LoadLocation(c.App.Location)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
|
|
// TODO: Check for required config values
|
|
|
|
return c, nil
|
|
}
|