Refactor config package
- Parse configuration only once during startup time - Store configuration values in a global variable
This commit is contained in:
parent
04d85b3c63
commit
228862fefa
28 changed files with 922 additions and 624 deletions
22
cli/cli.go
22
cli/cli.go
|
@ -51,9 +51,11 @@ func Parse() {
|
|||
flag.BoolVar(&flagDebugMode, "debug", false, flagDebugModeHelp)
|
||||
flag.Parse()
|
||||
|
||||
cfg := config.NewConfig()
|
||||
if err := config.ParseConfig(); err != nil {
|
||||
logger.Fatal("%v", err)
|
||||
}
|
||||
|
||||
if flagDebugMode || cfg.HasDebugMode() {
|
||||
if flagDebugMode || config.Opts.HasDebugMode() {
|
||||
logger.EnableDebug()
|
||||
}
|
||||
|
||||
|
@ -67,7 +69,15 @@ func Parse() {
|
|||
return
|
||||
}
|
||||
|
||||
db, err := database.NewConnectionPool(cfg.DatabaseURL(), cfg.DatabaseMinConns(), cfg.DatabaseMaxConns())
|
||||
if config.Opts.IsDefaultDatabaseURL() {
|
||||
logger.Info("The default value for DATABASE_URL is used")
|
||||
}
|
||||
|
||||
db, err := database.NewConnectionPool(
|
||||
config.Opts.DatabaseURL(),
|
||||
config.Opts.DatabaseMinConns(),
|
||||
config.Opts.DatabaseMaxConns(),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Fatal("Unable to connect to the database: %v", err)
|
||||
}
|
||||
|
@ -101,14 +111,14 @@ func Parse() {
|
|||
}
|
||||
|
||||
// Run migrations and start the deamon.
|
||||
if cfg.RunMigrations() {
|
||||
if config.Opts.RunMigrations() {
|
||||
database.Migrate(db)
|
||||
}
|
||||
|
||||
// Create admin user and start the deamon.
|
||||
if cfg.CreateAdmin() {
|
||||
if config.Opts.CreateAdmin() {
|
||||
createAdmin(store)
|
||||
}
|
||||
|
||||
startDaemon(cfg, store)
|
||||
startDaemon(store)
|
||||
}
|
||||
|
|
|
@ -16,13 +16,13 @@ import (
|
|||
"miniflux.app/config"
|
||||
"miniflux.app/logger"
|
||||
"miniflux.app/reader/feed"
|
||||
"miniflux.app/service/scheduler"
|
||||
"miniflux.app/service/httpd"
|
||||
"miniflux.app/service/scheduler"
|
||||
"miniflux.app/storage"
|
||||
"miniflux.app/worker"
|
||||
)
|
||||
|
||||
func startDaemon(cfg *config.Config, store *storage.Storage) {
|
||||
func startDaemon(store *storage.Storage) {
|
||||
logger.Info("Starting Miniflux...")
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
|
@ -30,17 +30,17 @@ func startDaemon(cfg *config.Config, store *storage.Storage) {
|
|||
signal.Notify(stop, syscall.SIGTERM)
|
||||
|
||||
feedHandler := feed.NewFeedHandler(store)
|
||||
pool := worker.NewPool(feedHandler, cfg.WorkerPoolSize())
|
||||
pool := worker.NewPool(feedHandler, config.Opts.WorkerPoolSize())
|
||||
|
||||
go showProcessStatistics()
|
||||
|
||||
if cfg.HasSchedulerService() {
|
||||
scheduler.Serve(cfg, store, pool)
|
||||
if config.Opts.HasSchedulerService() {
|
||||
scheduler.Serve(store, pool)
|
||||
}
|
||||
|
||||
var httpServer *http.Server
|
||||
if cfg.HasHTTPService() {
|
||||
httpServer = httpd.Serve(cfg, store, pool, feedHandler)
|
||||
if config.Opts.HasHTTPService() {
|
||||
httpServer = httpd.Serve(store, pool, feedHandler)
|
||||
}
|
||||
|
||||
<-stop
|
||||
|
@ -64,4 +64,4 @@ func showProcessStatistics() {
|
|||
runtime.NumGoroutine(), runtime.NumCPU())
|
||||
time.Sleep(30 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
272
config/config.go
272
config/config.go
|
@ -4,271 +4,11 @@
|
|||
|
||||
package config // import "miniflux.app/config"
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
// Opts contains configuration options after parsing.
|
||||
var Opts *Options
|
||||
|
||||
"miniflux.app/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBaseURL = "http://localhost"
|
||||
defaultDatabaseURL = "user=postgres password=postgres dbname=miniflux2 sslmode=disable"
|
||||
defaultWorkerPoolSize = 5
|
||||
defaultPollingFrequency = 60
|
||||
defaultBatchSize = 10
|
||||
defaultDatabaseMaxConns = 20
|
||||
defaultDatabaseMinConns = 1
|
||||
defaultArchiveReadDays = 60
|
||||
defaultListenAddr = "127.0.0.1:8080"
|
||||
defaultCertFile = ""
|
||||
defaultKeyFile = ""
|
||||
defaultCertDomain = ""
|
||||
defaultCertCache = "/tmp/cert_cache"
|
||||
defaultCleanupFrequency = 24
|
||||
defaultProxyImages = "http-only"
|
||||
defaultOAuth2ClientID = ""
|
||||
defaultOAuth2ClientSecret = ""
|
||||
defaultOAuth2RedirectURL = ""
|
||||
defaultOAuth2Provider = ""
|
||||
)
|
||||
|
||||
// Config manages configuration parameters.
|
||||
type Config struct {
|
||||
IsHTTPS bool
|
||||
baseURL string
|
||||
rootURL string
|
||||
basePath string
|
||||
}
|
||||
|
||||
func (c *Config) parseBaseURL() {
|
||||
baseURL := os.Getenv("BASE_URL")
|
||||
if baseURL == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if baseURL[len(baseURL)-1:] == "/" {
|
||||
baseURL = baseURL[:len(baseURL)-1]
|
||||
}
|
||||
|
||||
u, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
logger.Error("Invalid BASE_URL: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
scheme := strings.ToLower(u.Scheme)
|
||||
if scheme != "https" && scheme != "http" {
|
||||
logger.Error("Invalid BASE_URL: scheme must be http or https")
|
||||
return
|
||||
}
|
||||
|
||||
c.baseURL = baseURL
|
||||
c.basePath = u.Path
|
||||
|
||||
u.Path = ""
|
||||
c.rootURL = u.String()
|
||||
}
|
||||
|
||||
// HasDebugMode returns true if debug mode is enabled.
|
||||
func (c *Config) HasDebugMode() bool {
|
||||
return getBooleanValue("DEBUG")
|
||||
}
|
||||
|
||||
// BaseURL returns the application base URL with path.
|
||||
func (c *Config) BaseURL() string {
|
||||
return c.baseURL
|
||||
}
|
||||
|
||||
// RootURL returns the base URL without path.
|
||||
func (c *Config) RootURL() string {
|
||||
return c.rootURL
|
||||
}
|
||||
|
||||
// BasePath returns the application base path according to the base URL.
|
||||
func (c *Config) BasePath() string {
|
||||
return c.basePath
|
||||
}
|
||||
|
||||
// DatabaseURL returns the database URL.
|
||||
func (c *Config) DatabaseURL() string {
|
||||
value, exists := os.LookupEnv("DATABASE_URL")
|
||||
if !exists {
|
||||
logger.Info("The environment variable DATABASE_URL is not configured (the default value is used instead)")
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
value = defaultDatabaseURL
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// DatabaseMaxConns returns the maximum number of database connections.
|
||||
func (c *Config) DatabaseMaxConns() int {
|
||||
return getIntValue("DATABASE_MAX_CONNS", defaultDatabaseMaxConns)
|
||||
}
|
||||
|
||||
// DatabaseMinConns returns the minimum number of database connections.
|
||||
func (c *Config) DatabaseMinConns() int {
|
||||
return getIntValue("DATABASE_MIN_CONNS", defaultDatabaseMinConns)
|
||||
}
|
||||
|
||||
// ListenAddr returns the listen address for the HTTP server.
|
||||
func (c *Config) ListenAddr() string {
|
||||
if port := os.Getenv("PORT"); port != "" {
|
||||
return ":" + port
|
||||
}
|
||||
|
||||
return getStringValue("LISTEN_ADDR", defaultListenAddr)
|
||||
}
|
||||
|
||||
// CertFile returns the SSL certificate filename if any.
|
||||
func (c *Config) CertFile() string {
|
||||
return getStringValue("CERT_FILE", defaultCertFile)
|
||||
}
|
||||
|
||||
// KeyFile returns the private key filename for custom SSL certificate.
|
||||
func (c *Config) KeyFile() string {
|
||||
return getStringValue("KEY_FILE", defaultKeyFile)
|
||||
}
|
||||
|
||||
// CertDomain returns the domain to use for Let's Encrypt certificate.
|
||||
func (c *Config) CertDomain() string {
|
||||
return getStringValue("CERT_DOMAIN", defaultCertDomain)
|
||||
}
|
||||
|
||||
// CertCache returns the directory to use for Let's Encrypt session cache.
|
||||
func (c *Config) CertCache() string {
|
||||
return getStringValue("CERT_CACHE", defaultCertCache)
|
||||
}
|
||||
|
||||
// CleanupFrequency returns the interval for cleanup jobs.
|
||||
func (c *Config) CleanupFrequency() int {
|
||||
return getIntValue("CLEANUP_FREQUENCY", defaultCleanupFrequency)
|
||||
}
|
||||
|
||||
// WorkerPoolSize returns the number of background worker.
|
||||
func (c *Config) WorkerPoolSize() int {
|
||||
return getIntValue("WORKER_POOL_SIZE", defaultWorkerPoolSize)
|
||||
}
|
||||
|
||||
// PollingFrequency returns the interval to refresh feeds in the background.
|
||||
func (c *Config) PollingFrequency() int {
|
||||
return getIntValue("POLLING_FREQUENCY", defaultPollingFrequency)
|
||||
}
|
||||
|
||||
// BatchSize returns the number of feeds to send for background processing.
|
||||
func (c *Config) BatchSize() int {
|
||||
return getIntValue("BATCH_SIZE", defaultBatchSize)
|
||||
}
|
||||
|
||||
// IsOAuth2UserCreationAllowed returns true if user creation is allowed for OAuth2 users.
|
||||
func (c *Config) IsOAuth2UserCreationAllowed() bool {
|
||||
return getBooleanValue("OAUTH2_USER_CREATION")
|
||||
}
|
||||
|
||||
// OAuth2ClientID returns the OAuth2 Client ID.
|
||||
func (c *Config) OAuth2ClientID() string {
|
||||
return getStringValue("OAUTH2_CLIENT_ID", defaultOAuth2ClientID)
|
||||
}
|
||||
|
||||
// OAuth2ClientSecret returns the OAuth2 client secret.
|
||||
func (c *Config) OAuth2ClientSecret() string {
|
||||
return getStringValue("OAUTH2_CLIENT_SECRET", defaultOAuth2ClientSecret)
|
||||
}
|
||||
|
||||
// OAuth2RedirectURL returns the OAuth2 redirect URL.
|
||||
func (c *Config) OAuth2RedirectURL() string {
|
||||
return getStringValue("OAUTH2_REDIRECT_URL", defaultOAuth2RedirectURL)
|
||||
}
|
||||
|
||||
// OAuth2Provider returns the name of the OAuth2 provider configured.
|
||||
func (c *Config) OAuth2Provider() string {
|
||||
return getStringValue("OAUTH2_PROVIDER", defaultOAuth2Provider)
|
||||
}
|
||||
|
||||
// HasHSTS returns true if HTTP Strict Transport Security is enabled.
|
||||
func (c *Config) HasHSTS() bool {
|
||||
return !getBooleanValue("DISABLE_HSTS")
|
||||
}
|
||||
|
||||
// RunMigrations returns true if the environment variable RUN_MIGRATIONS is not empty.
|
||||
func (c *Config) RunMigrations() bool {
|
||||
return getBooleanValue("RUN_MIGRATIONS")
|
||||
}
|
||||
|
||||
// CreateAdmin returns true if the environment variable CREATE_ADMIN is not empty.
|
||||
func (c *Config) CreateAdmin() bool {
|
||||
return getBooleanValue("CREATE_ADMIN")
|
||||
}
|
||||
|
||||
// PocketConsumerKey returns the Pocket Consumer Key if defined as environment variable.
|
||||
func (c *Config) PocketConsumerKey(defaultValue string) string {
|
||||
return getStringValue("POCKET_CONSUMER_KEY", defaultValue)
|
||||
}
|
||||
|
||||
// ProxyImages returns "none" to never proxy, "http-only" to proxy non-HTTPS, "all" to always proxy.
|
||||
func (c *Config) ProxyImages() string {
|
||||
return getStringValue("PROXY_IMAGES", defaultProxyImages)
|
||||
}
|
||||
|
||||
// HasHTTPService returns true if the HTTP service is enabled.
|
||||
func (c *Config) HasHTTPService() bool {
|
||||
return !getBooleanValue("DISABLE_HTTP_SERVICE")
|
||||
}
|
||||
|
||||
// HasSchedulerService returns true if the scheduler service is enabled.
|
||||
func (c *Config) HasSchedulerService() bool {
|
||||
return !getBooleanValue("DISABLE_SCHEDULER_SERVICE")
|
||||
}
|
||||
|
||||
// ArchiveReadDays returns the number of days after which marking read items as removed.
|
||||
func (c *Config) ArchiveReadDays() int {
|
||||
return getIntValue("ARCHIVE_READ_DAYS", defaultArchiveReadDays)
|
||||
}
|
||||
|
||||
// NewConfig returns a new Config.
|
||||
func NewConfig() *Config {
|
||||
cfg := &Config{
|
||||
baseURL: defaultBaseURL,
|
||||
rootURL: defaultBaseURL,
|
||||
IsHTTPS: getBooleanValue("HTTPS"),
|
||||
}
|
||||
|
||||
cfg.parseBaseURL()
|
||||
return cfg
|
||||
}
|
||||
|
||||
func getBooleanValue(key string) bool {
|
||||
value := strings.ToLower(os.Getenv(key))
|
||||
if value == "1" || value == "yes" || value == "true" || value == "on" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getStringValue(key, fallback string) string {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func getIntValue(key string, fallback int) int {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
return fallback
|
||||
}
|
||||
|
||||
v, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return v
|
||||
// ParseConfig parses configuration options.
|
||||
func ParseConfig() (err error) {
|
||||
Opts, err = parse()
|
||||
return err
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,10 @@
|
|||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Copyright 2019 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package config handles configuration values for the application.
|
||||
Package config handles configuration management for the application.
|
||||
|
||||
*/
|
||||
package config // import "miniflux.app/config"
|
||||
|
|
214
config/options.go
Normal file
214
config/options.go
Normal file
|
@ -0,0 +1,214 @@
|
|||
// Copyright 2019 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package config // import "miniflux.app/config"
|
||||
|
||||
const (
|
||||
defaultBaseURL = "http://localhost"
|
||||
defaultWorkerPoolSize = 5
|
||||
defaultPollingFrequency = 60
|
||||
defaultBatchSize = 10
|
||||
defaultDatabaseURL = "user=postgres password=postgres dbname=miniflux2 sslmode=disable"
|
||||
defaultDatabaseMaxConns = 20
|
||||
defaultDatabaseMinConns = 1
|
||||
defaultArchiveReadDays = 60
|
||||
defaultListenAddr = "127.0.0.1:8080"
|
||||
defaultCertFile = ""
|
||||
defaultKeyFile = ""
|
||||
defaultCertDomain = ""
|
||||
defaultCertCache = "/tmp/cert_cache"
|
||||
defaultCleanupFrequency = 24
|
||||
defaultProxyImages = "http-only"
|
||||
defaultOAuth2ClientID = ""
|
||||
defaultOAuth2ClientSecret = ""
|
||||
defaultOAuth2RedirectURL = ""
|
||||
defaultOAuth2Provider = ""
|
||||
)
|
||||
|
||||
// Options contains configuration options.
|
||||
type Options struct {
|
||||
HTTPS bool
|
||||
hsts bool
|
||||
httpService bool
|
||||
schedulerService bool
|
||||
debug bool
|
||||
baseURL string
|
||||
rootURL string
|
||||
basePath string
|
||||
databaseURL string
|
||||
databaseMaxConns int
|
||||
databaseMinConns int
|
||||
runMigrations bool
|
||||
listenAddr string
|
||||
certFile string
|
||||
certDomain string
|
||||
certCache string
|
||||
certKeyFile string
|
||||
cleanupFrequency int
|
||||
archiveReadDays int
|
||||
pollingFrequency int
|
||||
batchSize int
|
||||
workerPoolSize int
|
||||
createAdmin bool
|
||||
proxyImages string
|
||||
oauth2UserCreationAllowed bool
|
||||
oauth2ClientID string
|
||||
oauth2ClientSecret string
|
||||
oauth2RedirectURL string
|
||||
oauth2Provider string
|
||||
pocketConsumerKey string
|
||||
}
|
||||
|
||||
// HasDebugMode returns true if debug mode is enabled.
|
||||
func (o *Options) HasDebugMode() bool {
|
||||
return o.debug
|
||||
}
|
||||
|
||||
// BaseURL returns the application base URL with path.
|
||||
func (o *Options) BaseURL() string {
|
||||
return o.baseURL
|
||||
}
|
||||
|
||||
// RootURL returns the base URL without path.
|
||||
func (o *Options) RootURL() string {
|
||||
return o.rootURL
|
||||
}
|
||||
|
||||
// BasePath returns the application base path according to the base URL.
|
||||
func (o *Options) BasePath() string {
|
||||
return o.basePath
|
||||
}
|
||||
|
||||
// IsDefaultDatabaseURL returns true if the default database URL is used.
|
||||
func (o *Options) IsDefaultDatabaseURL() bool {
|
||||
return o.databaseURL == defaultDatabaseURL
|
||||
}
|
||||
|
||||
// DatabaseURL returns the database URL.
|
||||
func (o *Options) DatabaseURL() string {
|
||||
return o.databaseURL
|
||||
}
|
||||
|
||||
// DatabaseMaxConns returns the maximum number of database connections.
|
||||
func (o *Options) DatabaseMaxConns() int {
|
||||
return o.databaseMaxConns
|
||||
}
|
||||
|
||||
// DatabaseMinConns returns the minimum number of database connections.
|
||||
func (o *Options) DatabaseMinConns() int {
|
||||
return o.databaseMinConns
|
||||
}
|
||||
|
||||
// ListenAddr returns the listen address for the HTTP server.
|
||||
func (o *Options) ListenAddr() string {
|
||||
return o.listenAddr
|
||||
}
|
||||
|
||||
// CertFile returns the SSL certificate filename if any.
|
||||
func (o *Options) CertFile() string {
|
||||
return o.certFile
|
||||
}
|
||||
|
||||
// CertKeyFile returns the private key filename for custom SSL certificate.
|
||||
func (o *Options) CertKeyFile() string {
|
||||
return o.certKeyFile
|
||||
}
|
||||
|
||||
// CertDomain returns the domain to use for Let's Encrypt certificate.
|
||||
func (o *Options) CertDomain() string {
|
||||
return o.certDomain
|
||||
}
|
||||
|
||||
// CertCache returns the directory to use for Let's Encrypt session cache.
|
||||
func (o *Options) CertCache() string {
|
||||
return o.certCache
|
||||
}
|
||||
|
||||
// CleanupFrequency returns the interval for cleanup jobs.
|
||||
func (o *Options) CleanupFrequency() int {
|
||||
return o.cleanupFrequency
|
||||
}
|
||||
|
||||
// WorkerPoolSize returns the number of background worker.
|
||||
func (o *Options) WorkerPoolSize() int {
|
||||
return o.workerPoolSize
|
||||
}
|
||||
|
||||
// PollingFrequency returns the interval to refresh feeds in the background.
|
||||
func (o *Options) PollingFrequency() int {
|
||||
return o.pollingFrequency
|
||||
}
|
||||
|
||||
// BatchSize returns the number of feeds to send for background processing.
|
||||
func (o *Options) BatchSize() int {
|
||||
return o.batchSize
|
||||
}
|
||||
|
||||
// IsOAuth2UserCreationAllowed returns true if user creation is allowed for OAuth2 users.
|
||||
func (o *Options) IsOAuth2UserCreationAllowed() bool {
|
||||
return o.oauth2UserCreationAllowed
|
||||
}
|
||||
|
||||
// OAuth2ClientID returns the OAuth2 Client ID.
|
||||
func (o *Options) OAuth2ClientID() string {
|
||||
return o.oauth2ClientID
|
||||
}
|
||||
|
||||
// OAuth2ClientSecret returns the OAuth2 client secret.
|
||||
func (o *Options) OAuth2ClientSecret() string {
|
||||
return o.oauth2ClientSecret
|
||||
}
|
||||
|
||||
// OAuth2RedirectURL returns the OAuth2 redirect URL.
|
||||
func (o *Options) OAuth2RedirectURL() string {
|
||||
return o.oauth2RedirectURL
|
||||
}
|
||||
|
||||
// OAuth2Provider returns the name of the OAuth2 provider configured.
|
||||
func (o *Options) OAuth2Provider() string {
|
||||
return o.oauth2Provider
|
||||
}
|
||||
|
||||
// HasHSTS returns true if HTTP Strict Transport Security is enabled.
|
||||
func (o *Options) HasHSTS() bool {
|
||||
return o.hsts
|
||||
}
|
||||
|
||||
// RunMigrations returns true if the environment variable RUN_MIGRATIONS is not empty.
|
||||
func (o *Options) RunMigrations() bool {
|
||||
return o.runMigrations
|
||||
}
|
||||
|
||||
// CreateAdmin returns true if the environment variable CREATE_ADMIN is not empty.
|
||||
func (o *Options) CreateAdmin() bool {
|
||||
return o.createAdmin
|
||||
}
|
||||
|
||||
// ProxyImages returns "none" to never proxy, "http-only" to proxy non-HTTPS, "all" to always proxy.
|
||||
func (o *Options) ProxyImages() string {
|
||||
return o.proxyImages
|
||||
}
|
||||
|
||||
// HasHTTPService returns true if the HTTP service is enabled.
|
||||
func (o *Options) HasHTTPService() bool {
|
||||
return o.httpService
|
||||
}
|
||||
|
||||
// HasSchedulerService returns true if the scheduler service is enabled.
|
||||
func (o *Options) HasSchedulerService() bool {
|
||||
return o.schedulerService
|
||||
}
|
||||
|
||||
// ArchiveReadDays returns the number of days after which marking read items as removed.
|
||||
func (o *Options) ArchiveReadDays() int {
|
||||
return o.archiveReadDays
|
||||
}
|
||||
|
||||
// PocketConsumerKey returns the Pocket Consumer Key if configured.
|
||||
func (o *Options) PocketConsumerKey(defaultValue string) string {
|
||||
if o.pocketConsumerKey != "" {
|
||||
return o.pocketConsumerKey
|
||||
}
|
||||
return defaultValue
|
||||
}
|
124
config/parser.go
Normal file
124
config/parser.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2019 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package config // import "miniflux.app/config"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parse() (opts *Options, err error) {
|
||||
opts = &Options{}
|
||||
opts.baseURL, opts.rootURL, opts.basePath, err = parseBaseURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts.debug = getBooleanValue("DEBUG")
|
||||
opts.listenAddr = parseListenAddr()
|
||||
|
||||
opts.databaseURL = getStringValue("DATABASE_URL", defaultDatabaseURL)
|
||||
opts.databaseMaxConns = getIntValue("DATABASE_MAX_CONNS", defaultDatabaseMaxConns)
|
||||
opts.databaseMinConns = getIntValue("DATABASE_MIN_CONNS", defaultDatabaseMinConns)
|
||||
opts.runMigrations = getBooleanValue("RUN_MIGRATIONS")
|
||||
|
||||
opts.hsts = !getBooleanValue("DISABLE_HSTS")
|
||||
opts.HTTPS = getBooleanValue("HTTPS")
|
||||
|
||||
opts.schedulerService = !getBooleanValue("DISABLE_SCHEDULER_SERVICE")
|
||||
opts.httpService = !getBooleanValue("DISABLE_HTTP_SERVICE")
|
||||
|
||||
opts.certFile = getStringValue("CERT_FILE", defaultCertFile)
|
||||
opts.certKeyFile = getStringValue("KEY_FILE", defaultKeyFile)
|
||||
opts.certDomain = getStringValue("CERT_DOMAIN", defaultCertDomain)
|
||||
opts.certCache = getStringValue("CERT_CACHE", defaultCertCache)
|
||||
|
||||
opts.cleanupFrequency = getIntValue("CLEANUP_FREQUENCY", defaultCleanupFrequency)
|
||||
opts.workerPoolSize = getIntValue("WORKER_POOL_SIZE", defaultWorkerPoolSize)
|
||||
opts.pollingFrequency = getIntValue("POLLING_FREQUENCY", defaultPollingFrequency)
|
||||
opts.batchSize = getIntValue("BATCH_SIZE", defaultBatchSize)
|
||||
opts.archiveReadDays = getIntValue("ARCHIVE_READ_DAYS", defaultArchiveReadDays)
|
||||
opts.proxyImages = getStringValue("PROXY_IMAGES", defaultProxyImages)
|
||||
|
||||
opts.oauth2UserCreationAllowed = getBooleanValue("OAUTH2_USER_CREATION")
|
||||
opts.oauth2ClientID = getStringValue("OAUTH2_CLIENT_ID", defaultOAuth2ClientID)
|
||||
opts.oauth2ClientSecret = getStringValue("OAUTH2_CLIENT_SECRET", defaultOAuth2ClientSecret)
|
||||
opts.oauth2RedirectURL = getStringValue("OAUTH2_REDIRECT_URL", defaultOAuth2RedirectURL)
|
||||
opts.oauth2Provider = getStringValue("OAUTH2_PROVIDER", defaultOAuth2Provider)
|
||||
|
||||
opts.pocketConsumerKey = getStringValue("POCKET_CONSUMER_KEY", "")
|
||||
|
||||
opts.createAdmin = getBooleanValue("CREATE_ADMIN")
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func parseBaseURL() (string, string, string, error) {
|
||||
baseURL := os.Getenv("BASE_URL")
|
||||
if baseURL == "" {
|
||||
return defaultBaseURL, defaultBaseURL, "", nil
|
||||
}
|
||||
|
||||
if baseURL[len(baseURL)-1:] == "/" {
|
||||
baseURL = baseURL[:len(baseURL)-1]
|
||||
}
|
||||
|
||||
u, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("Invalid BASE_URL: %v", err)
|
||||
}
|
||||
|
||||
scheme := strings.ToLower(u.Scheme)
|
||||
if scheme != "https" && scheme != "http" {
|
||||
return "", "", "", errors.New("Invalid BASE_URL: scheme must be http or https")
|
||||
}
|
||||
|
||||
basePath := u.Path
|
||||
u.Path = ""
|
||||
return baseURL, u.String(), basePath, nil
|
||||
}
|
||||
|
||||
func parseListenAddr() string {
|
||||
if port := os.Getenv("PORT"); port != "" {
|
||||
return ":" + port
|
||||
}
|
||||
|
||||
return getStringValue("LISTEN_ADDR", defaultListenAddr)
|
||||
}
|
||||
|
||||
func getBooleanValue(key string) bool {
|
||||
value := strings.ToLower(os.Getenv(key))
|
||||
if value == "1" || value == "yes" || value == "true" || value == "on" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getStringValue(key, fallback string) string {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func getIntValue(key string, fallback int) int {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
return fallback
|
||||
}
|
||||
|
||||
v, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
79
config/parser_test.go
Normal file
79
config/parser_test.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2019 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package config // import "miniflux.app/config"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetBooleanValueWithUnsetVariable(t *testing.T) {
|
||||
os.Clearenv()
|
||||
if getBooleanValue("MY_TEST_VARIABLE") {
|
||||
t.Errorf(`Unset variables should returns false`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBooleanValue(t *testing.T) {
|
||||
scenarios := map[string]bool{
|
||||
"": false,
|
||||
"1": true,
|
||||
"Yes": true,
|
||||
"yes": true,
|
||||
"True": true,
|
||||
"true": true,
|
||||
"on": true,
|
||||
"false": false,
|
||||
"off": false,
|
||||
"invalid": false,
|
||||
}
|
||||
|
||||
for input, expected := range scenarios {
|
||||
os.Clearenv()
|
||||
os.Setenv("MY_TEST_VARIABLE", input)
|
||||
result := getBooleanValue("MY_TEST_VARIABLE")
|
||||
if result != expected {
|
||||
t.Errorf(`Unexpected result for %q, got %v instead of %v`, input, result, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStringValueWithUnsetVariable(t *testing.T) {
|
||||
os.Clearenv()
|
||||
if getStringValue("MY_TEST_VARIABLE", "defaultValue") != "defaultValue" {
|
||||
t.Errorf(`Unset variables should returns the default value`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStringValue(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("MY_TEST_VARIABLE", "test")
|
||||
if getStringValue("MY_TEST_VARIABLE", "defaultValue") != "test" {
|
||||
t.Errorf(`Defined variables should returns the specified value`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIntValueWithUnsetVariable(t *testing.T) {
|
||||
os.Clearenv()
|
||||
if getIntValue("MY_TEST_VARIABLE", 42) != 42 {
|
||||
t.Errorf(`Unset variables should returns the default value`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIntValueWithInvalidInput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("MY_TEST_VARIABLE", "invalid integer")
|
||||
if getIntValue("MY_TEST_VARIABLE", 42) != 42 {
|
||||
t.Errorf(`Invalid integer should returns the default value`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIntValue(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("MY_TEST_VARIABLE", "2018")
|
||||
if getIntValue("MY_TEST_VARIABLE", 42) != 2018 {
|
||||
t.Errorf(`Defined variables should returns the specified value`)
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/http/request"
|
||||
"miniflux.app/http/response/json"
|
||||
"miniflux.app/integration"
|
||||
|
@ -22,8 +21,8 @@ import (
|
|||
)
|
||||
|
||||
// Serve handles Fever API calls.
|
||||
func Serve(router *mux.Router, cfg *config.Config, store *storage.Storage) {
|
||||
handler := &handler{cfg, store}
|
||||
func Serve(router *mux.Router, store *storage.Storage) {
|
||||
handler := &handler{store}
|
||||
|
||||
sr := router.PathPrefix("/fever").Subrouter()
|
||||
sr.Use(newMiddleware(store).serve)
|
||||
|
@ -31,7 +30,6 @@ func Serve(router *mux.Router, cfg *config.Config, store *storage.Storage) {
|
|||
}
|
||||
|
||||
type handler struct {
|
||||
cfg *config.Config
|
||||
store *storage.Storage
|
||||
}
|
||||
|
||||
|
@ -424,7 +422,7 @@ func (h *handler) handleWriteItems(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
go func() {
|
||||
integration.SendEntry(h.cfg, entry, settings)
|
||||
integration.SendEntry(entry, settings)
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
)
|
||||
|
||||
// SendEntry send the entry to the activated providers.
|
||||
func SendEntry(cfg *config.Config, entry *model.Entry, integration *model.Integration) {
|
||||
func SendEntry(entry *model.Entry, integration *model.Integration) {
|
||||
if integration.PinboardEnabled {
|
||||
client := pinboard.NewClient(integration.PinboardToken)
|
||||
err := client.AddBookmark(
|
||||
|
@ -64,7 +64,7 @@ func SendEntry(cfg *config.Config, entry *model.Entry, integration *model.Integr
|
|||
}
|
||||
|
||||
if integration.PocketEnabled {
|
||||
client := pocket.NewClient(cfg.PocketConsumerKey(integration.PocketConsumerKey), integration.PocketAccessToken)
|
||||
client := pocket.NewClient(config.Opts.PocketConsumerKey(integration.PocketConsumerKey), integration.PocketAccessToken)
|
||||
if err := client.AddURL(entry.URL, entry.Title); err != nil {
|
||||
logger.Error("[Integration] UserID #%d: %v", integration.UserID, err)
|
||||
}
|
||||
|
|
|
@ -27,17 +27,17 @@ import (
|
|||
)
|
||||
|
||||
// Serve starts a new HTTP server.
|
||||
func Serve(cfg *config.Config, store *storage.Storage, pool *worker.Pool, feedHandler *feed.Handler) *http.Server {
|
||||
certFile := cfg.CertFile()
|
||||
keyFile := cfg.KeyFile()
|
||||
certDomain := cfg.CertDomain()
|
||||
certCache := cfg.CertCache()
|
||||
listenAddr := cfg.ListenAddr()
|
||||
func Serve(store *storage.Storage, pool *worker.Pool, feedHandler *feed.Handler) *http.Server {
|
||||
certFile := config.Opts.CertFile()
|
||||
keyFile := config.Opts.CertKeyFile()
|
||||
certDomain := config.Opts.CertDomain()
|
||||
certCache := config.Opts.CertCache()
|
||||
listenAddr := config.Opts.ListenAddr()
|
||||
server := &http.Server{
|
||||
ReadTimeout: 30 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
IdleTimeout: 60 * time.Second,
|
||||
Handler: setupHandler(cfg, store, feedHandler, pool),
|
||||
Handler: setupHandler(store, feedHandler, pool),
|
||||
}
|
||||
|
||||
switch {
|
||||
|
@ -46,10 +46,10 @@ func Serve(cfg *config.Config, store *storage.Storage, pool *worker.Pool, feedHa
|
|||
case strings.HasPrefix(listenAddr, "/"):
|
||||
startUnixSocketServer(server, listenAddr)
|
||||
case certDomain != "" && certCache != "":
|
||||
cfg.IsHTTPS = true
|
||||
config.Opts.HTTPS = true
|
||||
startAutoCertTLSServer(server, certDomain, certCache)
|
||||
case certFile != "" && keyFile != "":
|
||||
cfg.IsHTTPS = true
|
||||
config.Opts.HTTPS = true
|
||||
server.Addr = listenAddr
|
||||
startTLSServer(server, certFile, keyFile)
|
||||
default:
|
||||
|
@ -156,18 +156,18 @@ func startHTTPServer(server *http.Server) {
|
|||
}()
|
||||
}
|
||||
|
||||
func setupHandler(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handler, pool *worker.Pool) *mux.Router {
|
||||
func setupHandler(store *storage.Storage, feedHandler *feed.Handler, pool *worker.Pool) *mux.Router {
|
||||
router := mux.NewRouter()
|
||||
|
||||
if cfg.BasePath() != "" {
|
||||
router = router.PathPrefix(cfg.BasePath()).Subrouter()
|
||||
if config.Opts.BasePath() != "" {
|
||||
router = router.PathPrefix(config.Opts.BasePath()).Subrouter()
|
||||
}
|
||||
|
||||
router.Use(newMiddleware(cfg).Serve)
|
||||
router.Use(middleware)
|
||||
|
||||
fever.Serve(router, cfg, store)
|
||||
fever.Serve(router, store)
|
||||
api.Serve(router, store, feedHandler)
|
||||
ui.Serve(router, cfg, store, pool, feedHandler)
|
||||
ui.Serve(router, store, pool, feedHandler)
|
||||
|
||||
router.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("OK"))
|
||||
|
|
|
@ -13,32 +13,24 @@ import (
|
|||
"miniflux.app/logger"
|
||||
)
|
||||
|
||||
type middleware struct {
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func newMiddleware(cfg *config.Config) *middleware {
|
||||
return &middleware{cfg}
|
||||
}
|
||||
|
||||
func (m *middleware) Serve(next http.Handler) http.Handler {
|
||||
func middleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
clientIP := request.FindClientIP(r)
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, request.ClientIPContextKey, clientIP)
|
||||
|
||||
if r.Header.Get("X-Forwarded-Proto") == "https" {
|
||||
m.cfg.IsHTTPS = true
|
||||
config.Opts.HTTPS = true
|
||||
}
|
||||
|
||||
protocol := "HTTP"
|
||||
if m.cfg.IsHTTPS {
|
||||
if config.Opts.HTTPS {
|
||||
protocol = "HTTPS"
|
||||
}
|
||||
|
||||
logger.Debug("[%s] %s %s %s", protocol, clientIP, r.Method, r.RequestURI)
|
||||
|
||||
if m.cfg.IsHTTPS && m.cfg.HasHSTS() {
|
||||
if config.Opts.HTTPS && config.Opts.HasHSTS() {
|
||||
w.Header().Set("Strict-Transport-Security", "max-age=31536000")
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@ import (
|
|||
)
|
||||
|
||||
// Serve starts the internal scheduler.
|
||||
func Serve(cfg *config.Config, store *storage.Storage, pool *worker.Pool) {
|
||||
func Serve(store *storage.Storage, pool *worker.Pool) {
|
||||
logger.Info(`Starting scheduler...`)
|
||||
go feedScheduler(store, pool, cfg.PollingFrequency(), cfg.BatchSize())
|
||||
go cleanupScheduler(store, cfg.CleanupFrequency(), cfg.ArchiveReadDays())
|
||||
go feedScheduler(store, pool, config.Opts.PollingFrequency(), config.Opts.BatchSize())
|
||||
go cleanupScheduler(store, config.Opts.CleanupFrequency(), config.Opts.ArchiveReadDays())
|
||||
}
|
||||
|
||||
func feedScheduler(store *storage.Storage, pool *worker.Pool, frequency, batchSize int) {
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"html/template"
|
||||
"time"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/errors"
|
||||
"miniflux.app/locale"
|
||||
"miniflux.app/logger"
|
||||
|
@ -78,10 +77,10 @@ func (e *Engine) Render(name, language string, data interface{}) []byte {
|
|||
}
|
||||
|
||||
// NewEngine returns a new template engine.
|
||||
func NewEngine(cfg *config.Config, router *mux.Router) *Engine {
|
||||
func NewEngine(router *mux.Router) *Engine {
|
||||
tpl := &Engine{
|
||||
templates: make(map[string]*template.Template),
|
||||
funcMap: newFuncMap(cfg, router),
|
||||
funcMap: &funcMap{router},
|
||||
}
|
||||
|
||||
tpl.parseAll()
|
||||
|
|
|
@ -7,8 +7,8 @@ package template // import "miniflux.app/template"
|
|||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math"
|
||||
"html/template"
|
||||
"math"
|
||||
"net/mail"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -20,12 +20,11 @@ import (
|
|||
"miniflux.app/timezone"
|
||||
"miniflux.app/url"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type funcMap struct {
|
||||
cfg *config.Config
|
||||
router *mux.Router
|
||||
}
|
||||
|
||||
|
@ -37,13 +36,13 @@ func (f *funcMap) Map() template.FuncMap {
|
|||
"truncate": truncate,
|
||||
"isEmail": isEmail,
|
||||
"baseURL": func() string {
|
||||
return f.cfg.BaseURL()
|
||||
return config.Opts.BaseURL()
|
||||
},
|
||||
"rootURL": func() string {
|
||||
return f.cfg.RootURL()
|
||||
return config.Opts.RootURL()
|
||||
},
|
||||
"hasOAuth2Provider": func(provider string) bool {
|
||||
return f.cfg.OAuth2Provider() == provider
|
||||
return config.Opts.OAuth2Provider() == provider
|
||||
},
|
||||
"route": func(name string, args ...interface{}) string {
|
||||
return route.Path(f.router, name, args...)
|
||||
|
@ -52,10 +51,10 @@ func (f *funcMap) Map() template.FuncMap {
|
|||
return template.HTML(str)
|
||||
},
|
||||
"proxyFilter": func(data string) string {
|
||||
return imageProxyFilter(f.router, f.cfg, data)
|
||||
return imageProxyFilter(f.router, data)
|
||||
},
|
||||
"proxyURL": func(link string) string {
|
||||
proxyImages := f.cfg.ProxyImages()
|
||||
proxyImages := config.Opts.ProxyImages()
|
||||
|
||||
if proxyImages == "all" || (proxyImages != "none" && !url.IsHTTPS(link)) {
|
||||
return proxify(f.router, link)
|
||||
|
@ -92,10 +91,6 @@ func (f *funcMap) Map() template.FuncMap {
|
|||
}
|
||||
}
|
||||
|
||||
func newFuncMap(cfg *config.Config, router *mux.Router) *funcMap {
|
||||
return &funcMap{cfg, router}
|
||||
}
|
||||
|
||||
func dict(values ...interface{}) (map[string]interface{}, error) {
|
||||
if len(values)%2 != 0 {
|
||||
return nil, fmt.Errorf("dict expects an even number of arguments")
|
||||
|
@ -178,8 +173,8 @@ func elapsedTime(printer *locale.Printer, tz string, t time.Time) string {
|
|||
}
|
||||
}
|
||||
|
||||
func imageProxyFilter(router *mux.Router, cfg *config.Config, data string) string {
|
||||
proxyImages := cfg.ProxyImages()
|
||||
func imageProxyFilter(router *mux.Router, data string) string {
|
||||
proxyImages := config.Opts.ProxyImages()
|
||||
if proxyImages == "none" {
|
||||
return data
|
||||
}
|
||||
|
|
|
@ -134,13 +134,13 @@ func TestElapsedTime(t *testing.T) {
|
|||
func TestProxyFilterWithHttpDefault(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "http-only")
|
||||
c := config.NewConfig()
|
||||
config.ParseConfig()
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||
output := imageProxyFilter(r, c, input)
|
||||
output := imageProxyFilter(r, input)
|
||||
expected := `<p><img src="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
|
@ -151,13 +151,13 @@ func TestProxyFilterWithHttpDefault(t *testing.T) {
|
|||
func TestProxyFilterWithHttpsDefault(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "http-only")
|
||||
c := config.NewConfig()
|
||||
config.ParseConfig()
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
output := imageProxyFilter(r, c, input)
|
||||
output := imageProxyFilter(r, input)
|
||||
expected := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
|
@ -168,13 +168,13 @@ func TestProxyFilterWithHttpsDefault(t *testing.T) {
|
|||
func TestProxyFilterWithHttpNever(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "none")
|
||||
c := config.NewConfig()
|
||||
config.ParseConfig()
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||
output := imageProxyFilter(r, c, input)
|
||||
output := imageProxyFilter(r, input)
|
||||
expected := input
|
||||
|
||||
if expected != output {
|
||||
|
@ -185,13 +185,13 @@ func TestProxyFilterWithHttpNever(t *testing.T) {
|
|||
func TestProxyFilterWithHttpsNever(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "none")
|
||||
c := config.NewConfig()
|
||||
config.ParseConfig()
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
output := imageProxyFilter(r, c, input)
|
||||
output := imageProxyFilter(r, input)
|
||||
expected := input
|
||||
|
||||
if expected != output {
|
||||
|
@ -202,13 +202,13 @@ func TestProxyFilterWithHttpsNever(t *testing.T) {
|
|||
func TestProxyFilterWithHttpAlways(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "all")
|
||||
c := config.NewConfig()
|
||||
config.ParseConfig()
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||
output := imageProxyFilter(r, c, input)
|
||||
output := imageProxyFilter(r, input)
|
||||
expected := `<p><img src="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
|
@ -219,13 +219,13 @@ func TestProxyFilterWithHttpAlways(t *testing.T) {
|
|||
func TestProxyFilterWithHttpsAlways(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "all")
|
||||
c := config.NewConfig()
|
||||
config.ParseConfig()
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
output := imageProxyFilter(r, c, input)
|
||||
output := imageProxyFilter(r, input)
|
||||
expected := `<p><img src="/proxy/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
|
@ -236,13 +236,13 @@ func TestProxyFilterWithHttpsAlways(t *testing.T) {
|
|||
func TestProxyFilterWithHttpInvalid(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "invalid")
|
||||
c := config.NewConfig()
|
||||
config.ParseConfig()
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||
output := imageProxyFilter(r, c, input)
|
||||
output := imageProxyFilter(r, input)
|
||||
expected := `<p><img src="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
|
@ -253,13 +253,13 @@ func TestProxyFilterWithHttpInvalid(t *testing.T) {
|
|||
func TestProxyFilterWithHttpsInvalid(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "invalid")
|
||||
c := config.NewConfig()
|
||||
config.ParseConfig()
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
output := imageProxyFilter(r, c, input)
|
||||
output := imageProxyFilter(r, input)
|
||||
expected := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
|
|
|
@ -37,7 +37,7 @@ func (h *handler) saveEntry(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
go func() {
|
||||
integration.SendEntry(h.cfg, entry, settings)
|
||||
integration.SendEntry(entry, settings)
|
||||
}()
|
||||
|
||||
json.Created(w, r, map[string]string{"message": "saved"})
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ui // import "miniflux.app/ui"
|
||||
package ui // import "miniflux.app/ui"
|
||||
|
||||
import (
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/reader/feed"
|
||||
"miniflux.app/storage"
|
||||
"miniflux.app/template"
|
||||
|
@ -16,7 +15,6 @@ import (
|
|||
|
||||
type handler struct {
|
||||
router *mux.Router
|
||||
cfg *config.Config
|
||||
store *storage.Storage
|
||||
tpl *template.Engine
|
||||
pool *worker.Pool
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ui // import "miniflux.app/ui"
|
||||
package ui // import "miniflux.app/ui"
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"miniflux.app/http/response/html"
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/http/request"
|
||||
"miniflux.app/http/response/html"
|
||||
"miniflux.app/http/route"
|
||||
"miniflux.app/integration/pocket"
|
||||
"miniflux.app/locale"
|
||||
|
@ -31,8 +32,8 @@ func (h *handler) pocketAuthorize(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
sess := session.New(h.store, request.SessionID(r))
|
||||
connector := pocket.NewConnector(h.cfg.PocketConsumerKey(integration.PocketConsumerKey))
|
||||
redirectURL := h.cfg.BaseURL() + route.Path(h.router, "pocketCallback")
|
||||
connector := pocket.NewConnector(config.Opts.PocketConsumerKey(integration.PocketConsumerKey))
|
||||
redirectURL := config.Opts.BaseURL() + route.Path(h.router, "pocketCallback")
|
||||
requestToken, err := connector.RequestToken(redirectURL)
|
||||
if err != nil {
|
||||
logger.Error("[Pocket:Authorize] %v", err)
|
||||
|
@ -61,7 +62,7 @@ func (h *handler) pocketCallback(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
connector := pocket.NewConnector(h.cfg.PocketConsumerKey(integration.PocketConsumerKey))
|
||||
connector := pocket.NewConnector(config.Opts.PocketConsumerKey(integration.PocketConsumerKey))
|
||||
accessToken, err := connector.AccessToken(request.PocketRequestToken(r))
|
||||
if err != nil {
|
||||
logger.Error("[Pocket:Callback] %v", err)
|
||||
|
|
|
@ -7,6 +7,7 @@ package ui // import "miniflux.app/ui"
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/http/request"
|
||||
"miniflux.app/http/response/html"
|
||||
"miniflux.app/ui/form"
|
||||
|
@ -59,7 +60,7 @@ func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) {
|
|||
view.Set("user", user)
|
||||
view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
|
||||
view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID))
|
||||
view.Set("hasPocketConsumerKeyConfigured", h.cfg.PocketConsumerKey("") != "")
|
||||
view.Set("hasPocketConsumerKeyConfigured", config.Opts.PocketConsumerKey("") != "")
|
||||
|
||||
html.OK(w, r, view.Render("integrations"))
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package ui // import "miniflux.app/ui"
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/http/cookie"
|
||||
"miniflux.app/http/request"
|
||||
"miniflux.app/http/response/html"
|
||||
|
@ -55,8 +56,8 @@ func (h *handler) checkLogin(w http.ResponseWriter, r *http.Request) {
|
|||
http.SetCookie(w, cookie.New(
|
||||
cookie.CookieUserSessionID,
|
||||
sessionToken,
|
||||
h.cfg.IsHTTPS,
|
||||
h.cfg.BasePath(),
|
||||
config.Opts.HTTPS,
|
||||
config.Opts.BasePath(),
|
||||
))
|
||||
|
||||
html.Redirect(w, r, route.Path(h.router, "unread"))
|
||||
|
|
|
@ -7,6 +7,7 @@ package ui // import "miniflux.app/ui"
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/http/cookie"
|
||||
"miniflux.app/http/request"
|
||||
"miniflux.app/http/response/html"
|
||||
|
@ -32,8 +33,8 @@ func (h *handler) logout(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
http.SetCookie(w, cookie.Expired(
|
||||
cookie.CookieUserSessionID,
|
||||
h.cfg.IsHTTPS,
|
||||
h.cfg.BasePath(),
|
||||
config.Opts.HTTPS,
|
||||
config.Opts.BasePath(),
|
||||
))
|
||||
|
||||
html.Redirect(w, r, route.Path(h.router, "login"))
|
||||
|
|
|
@ -14,21 +14,20 @@ import (
|
|||
"miniflux.app/http/request"
|
||||
"miniflux.app/http/response/html"
|
||||
"miniflux.app/http/route"
|
||||
"miniflux.app/storage"
|
||||
"miniflux.app/logger"
|
||||
"miniflux.app/model"
|
||||
"miniflux.app/storage"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type middleware struct {
|
||||
router *mux.Router
|
||||
cfg *config.Config
|
||||
store *storage.Storage
|
||||
store *storage.Storage
|
||||
}
|
||||
|
||||
func newMiddleware(router *mux.Router, cfg *config.Config, store *storage.Storage) *middleware {
|
||||
return &middleware{router, cfg, store}
|
||||
func newMiddleware(router *mux.Router, store *storage.Storage) *middleware {
|
||||
return &middleware{router, store}
|
||||
}
|
||||
|
||||
func (m *middleware) handleUserSession(next http.Handler) http.Handler {
|
||||
|
@ -61,7 +60,7 @@ func (m *middleware) handleAppSession(next http.Handler) http.Handler {
|
|||
session := m.getAppSessionValueFromCookie(r)
|
||||
|
||||
if session == nil {
|
||||
if (request.IsAuthenticated(r)) {
|
||||
if request.IsAuthenticated(r) {
|
||||
userID := request.UserID(r)
|
||||
logger.Debug("[UI:AppSession] Cookie expired but user #%d is logged: creating a new session", userID)
|
||||
session, err = m.store.CreateAppSessionWithUserPrefs(userID)
|
||||
|
@ -78,7 +77,7 @@ func (m *middleware) handleAppSession(next http.Handler) http.Handler {
|
|||
}
|
||||
}
|
||||
|
||||
http.SetCookie(w, cookie.New(cookie.CookieAppSessionID, session.ID, m.cfg.IsHTTPS, m.cfg.BasePath()))
|
||||
http.SetCookie(w, cookie.New(cookie.CookieAppSessionID, session.ID, config.Opts.HTTPS, config.Opts.BasePath()))
|
||||
} else {
|
||||
logger.Debug("[UI:AppSession] %s", session)
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
"miniflux.app/oauth2"
|
||||
)
|
||||
|
||||
func getOAuth2Manager(cfg *config.Config) *oauth2.Manager {
|
||||
func getOAuth2Manager() *oauth2.Manager {
|
||||
return oauth2.NewManager(
|
||||
cfg.OAuth2ClientID(),
|
||||
cfg.OAuth2ClientSecret(),
|
||||
cfg.OAuth2RedirectURL(),
|
||||
config.Opts.OAuth2ClientID(),
|
||||
config.Opts.OAuth2ClientSecret(),
|
||||
config.Opts.OAuth2RedirectURL(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package ui // import "miniflux.app/ui"
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/http/cookie"
|
||||
"miniflux.app/http/request"
|
||||
"miniflux.app/http/response/html"
|
||||
|
@ -43,7 +44,7 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
authProvider, err := getOAuth2Manager(h.cfg).Provider(provider)
|
||||
authProvider, err := getOAuth2Manager().Provider(provider)
|
||||
if err != nil {
|
||||
logger.Error("[OAuth2] %v", err)
|
||||
html.Redirect(w, r, route.Path(h.router, "login"))
|
||||
|
@ -90,7 +91,7 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if user == nil {
|
||||
if !h.cfg.IsOAuth2UserCreationAllowed() {
|
||||
if !config.Opts.IsOAuth2UserCreationAllowed() {
|
||||
html.Forbidden(w, r)
|
||||
return
|
||||
}
|
||||
|
@ -121,8 +122,8 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
|
|||
http.SetCookie(w, cookie.New(
|
||||
cookie.CookieUserSessionID,
|
||||
sessionToken,
|
||||
h.cfg.IsHTTPS,
|
||||
h.cfg.BasePath(),
|
||||
config.Opts.HTTPS,
|
||||
config.Opts.BasePath(),
|
||||
))
|
||||
|
||||
html.Redirect(w, r, route.Path(h.router, "unread"))
|
||||
|
|
|
@ -24,7 +24,7 @@ func (h *handler) oauth2Redirect(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
authProvider, err := getOAuth2Manager(h.cfg).Provider(provider)
|
||||
authProvider, err := getOAuth2Manager().Provider(provider)
|
||||
if err != nil {
|
||||
logger.Error("[OAuth2] %v", err)
|
||||
html.Redirect(w, r, route.Path(h.router, "login"))
|
||||
|
|
|
@ -24,7 +24,7 @@ func (h *handler) oauth2Unlink(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
authProvider, err := getOAuth2Manager(h.cfg).Provider(provider)
|
||||
authProvider, err := getOAuth2Manager().Provider(provider)
|
||||
if err != nil {
|
||||
logger.Error("[OAuth2] %v", err)
|
||||
html.Redirect(w, r, route.Path(h.router, "settings"))
|
||||
|
|
7
ui/ui.go
7
ui/ui.go
|
@ -7,7 +7,6 @@ package ui // import "miniflux.app/ui"
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/reader/feed"
|
||||
"miniflux.app/storage"
|
||||
"miniflux.app/template"
|
||||
|
@ -17,9 +16,9 @@ import (
|
|||
)
|
||||
|
||||
// Serve declares all routes for the user interface.
|
||||
func Serve(router *mux.Router, cfg *config.Config, store *storage.Storage, pool *worker.Pool, feedHandler *feed.Handler) {
|
||||
middleware := newMiddleware(router, cfg, store)
|
||||
handler := &handler{router, cfg, store, template.NewEngine(cfg, router), pool, feedHandler}
|
||||
func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool, feedHandler *feed.Handler) {
|
||||
middleware := newMiddleware(router, store)
|
||||
handler := &handler{router, store, template.NewEngine(router), pool, feedHandler}
|
||||
|
||||
uiRouter := router.NewRoute().Subrouter()
|
||||
uiRouter.Use(middleware.handleUserSession)
|
||||
|
|
Loading…
Reference in a new issue