1// Copyright 2019 Frédéric Guillot. All rights reserved.
2// Use of this source code is governed by the Apache 2.0
3// license that can be found in the LICENSE file.
4
5package config // import "miniflux.app/config"
6
7import (
8	"fmt"
9	"sort"
10	"strings"
11	"time"
12
13	"miniflux.app/version"
14)
15
16const (
17	defaultHTTPS                              = false
18	defaultLogDateTime                        = false
19	defaultHSTS                               = true
20	defaultHTTPService                        = true
21	defaultSchedulerService                   = true
22	defaultDebug                              = false
23	defaultTiming                             = false
24	defaultBaseURL                            = "http://localhost"
25	defaultRootURL                            = "http://localhost"
26	defaultBasePath                           = ""
27	defaultWorkerPoolSize                     = 5
28	defaultPollingFrequency                   = 60
29	defaultBatchSize                          = 100
30	defaultPollingScheduler                   = "round_robin"
31	defaultSchedulerEntryFrequencyMinInterval = 5
32	defaultSchedulerEntryFrequencyMaxInterval = 24 * 60
33	defaultPollingParsingErrorLimit           = 3
34	defaultRunMigrations                      = false
35	defaultDatabaseURL                        = "user=postgres password=postgres dbname=miniflux2 sslmode=disable"
36	defaultDatabaseMaxConns                   = 20
37	defaultDatabaseMinConns                   = 1
38	defaultDatabaseConnectionLifetime         = 5
39	defaultListenAddr                         = "127.0.0.1:8080"
40	defaultCertFile                           = ""
41	defaultKeyFile                            = ""
42	defaultCertDomain                         = ""
43	defaultCleanupFrequencyHours              = 24
44	defaultCleanupArchiveReadDays             = 60
45	defaultCleanupArchiveUnreadDays           = 180
46	defaultCleanupArchiveBatchSize            = 10000
47	defaultCleanupRemoveSessionsDays          = 30
48	defaultProxyImages                        = "http-only"
49	defaultFetchYouTubeWatchTime              = false
50	defaultCreateAdmin                        = false
51	defaultAdminUsername                      = ""
52	defaultAdminPassword                      = ""
53	defaultOAuth2UserCreation                 = false
54	defaultOAuth2ClientID                     = ""
55	defaultOAuth2ClientSecret                 = ""
56	defaultOAuth2RedirectURL                  = ""
57	defaultOAuth2OidcDiscoveryEndpoint        = ""
58	defaultOAuth2Provider                     = ""
59	defaultPocketConsumerKey                  = ""
60	defaultHTTPClientTimeout                  = 20
61	defaultHTTPClientMaxBodySize              = 15
62	defaultHTTPClientProxy                    = ""
63	defaultAuthProxyHeader                    = ""
64	defaultAuthProxyUserCreation              = false
65	defaultMaintenanceMode                    = false
66	defaultMaintenanceMessage                 = "Miniflux is currently under maintenance"
67	defaultMetricsCollector                   = false
68	defaultMetricsRefreshInterval             = 60
69	defaultMetricsAllowedNetworks             = "127.0.0.1/8"
70	defaultWatchdog                           = true
71	defaultInvidiousInstance                  = "yewtu.be"
72)
73
74var defaultHTTPClientUserAgent = "Mozilla/5.0 (compatible; Miniflux/" + version.Version + "; +https://miniflux.app)"
75
76// Option contains a key to value map of a single option. It may be used to output debug strings.
77type Option struct {
78	Key   string
79	Value interface{}
80}
81
82// Options contains configuration options.
83type Options struct {
84	HTTPS                              bool
85	logDateTime                        bool
86	hsts                               bool
87	httpService                        bool
88	schedulerService                   bool
89	debug                              bool
90	serverTimingHeader                 bool
91	baseURL                            string
92	rootURL                            string
93	basePath                           string
94	databaseURL                        string
95	databaseMaxConns                   int
96	databaseMinConns                   int
97	databaseConnectionLifetime         int
98	runMigrations                      bool
99	listenAddr                         string
100	certFile                           string
101	certDomain                         string
102	certKeyFile                        string
103	cleanupFrequencyHours              int
104	cleanupArchiveReadDays             int
105	cleanupArchiveUnreadDays           int
106	cleanupArchiveBatchSize            int
107	cleanupRemoveSessionsDays          int
108	pollingFrequency                   int
109	batchSize                          int
110	pollingScheduler                   string
111	schedulerEntryFrequencyMinInterval int
112	schedulerEntryFrequencyMaxInterval int
113	pollingParsingErrorLimit           int
114	workerPoolSize                     int
115	createAdmin                        bool
116	adminUsername                      string
117	adminPassword                      string
118	proxyImages                        string
119	fetchYouTubeWatchTime              bool
120	oauth2UserCreationAllowed          bool
121	oauth2ClientID                     string
122	oauth2ClientSecret                 string
123	oauth2RedirectURL                  string
124	oauth2OidcDiscoveryEndpoint        string
125	oauth2Provider                     string
126	pocketConsumerKey                  string
127	httpClientTimeout                  int
128	httpClientMaxBodySize              int64
129	httpClientProxy                    string
130	httpClientUserAgent                string
131	authProxyHeader                    string
132	authProxyUserCreation              bool
133	maintenanceMode                    bool
134	maintenanceMessage                 string
135	metricsCollector                   bool
136	metricsRefreshInterval             int
137	metricsAllowedNetworks             []string
138	watchdog                           bool
139	invidiousInstance                  string
140}
141
142// NewOptions returns Options with default values.
143func NewOptions() *Options {
144	return &Options{
145		HTTPS:                              defaultHTTPS,
146		logDateTime:                        defaultLogDateTime,
147		hsts:                               defaultHSTS,
148		httpService:                        defaultHTTPService,
149		schedulerService:                   defaultSchedulerService,
150		debug:                              defaultDebug,
151		serverTimingHeader:                 defaultTiming,
152		baseURL:                            defaultBaseURL,
153		rootURL:                            defaultRootURL,
154		basePath:                           defaultBasePath,
155		databaseURL:                        defaultDatabaseURL,
156		databaseMaxConns:                   defaultDatabaseMaxConns,
157		databaseMinConns:                   defaultDatabaseMinConns,
158		databaseConnectionLifetime:         defaultDatabaseConnectionLifetime,
159		runMigrations:                      defaultRunMigrations,
160		listenAddr:                         defaultListenAddr,
161		certFile:                           defaultCertFile,
162		certDomain:                         defaultCertDomain,
163		certKeyFile:                        defaultKeyFile,
164		cleanupFrequencyHours:              defaultCleanupFrequencyHours,
165		cleanupArchiveReadDays:             defaultCleanupArchiveReadDays,
166		cleanupArchiveUnreadDays:           defaultCleanupArchiveUnreadDays,
167		cleanupArchiveBatchSize:            defaultCleanupArchiveBatchSize,
168		cleanupRemoveSessionsDays:          defaultCleanupRemoveSessionsDays,
169		pollingFrequency:                   defaultPollingFrequency,
170		batchSize:                          defaultBatchSize,
171		pollingScheduler:                   defaultPollingScheduler,
172		schedulerEntryFrequencyMinInterval: defaultSchedulerEntryFrequencyMinInterval,
173		schedulerEntryFrequencyMaxInterval: defaultSchedulerEntryFrequencyMaxInterval,
174		pollingParsingErrorLimit:           defaultPollingParsingErrorLimit,
175		workerPoolSize:                     defaultWorkerPoolSize,
176		createAdmin:                        defaultCreateAdmin,
177		proxyImages:                        defaultProxyImages,
178		fetchYouTubeWatchTime:              defaultFetchYouTubeWatchTime,
179		oauth2UserCreationAllowed:          defaultOAuth2UserCreation,
180		oauth2ClientID:                     defaultOAuth2ClientID,
181		oauth2ClientSecret:                 defaultOAuth2ClientSecret,
182		oauth2RedirectURL:                  defaultOAuth2RedirectURL,
183		oauth2OidcDiscoveryEndpoint:        defaultOAuth2OidcDiscoveryEndpoint,
184		oauth2Provider:                     defaultOAuth2Provider,
185		pocketConsumerKey:                  defaultPocketConsumerKey,
186		httpClientTimeout:                  defaultHTTPClientTimeout,
187		httpClientMaxBodySize:              defaultHTTPClientMaxBodySize * 1024 * 1024,
188		httpClientProxy:                    defaultHTTPClientProxy,
189		httpClientUserAgent:                defaultHTTPClientUserAgent,
190		authProxyHeader:                    defaultAuthProxyHeader,
191		authProxyUserCreation:              defaultAuthProxyUserCreation,
192		maintenanceMode:                    defaultMaintenanceMode,
193		maintenanceMessage:                 defaultMaintenanceMessage,
194		metricsCollector:                   defaultMetricsCollector,
195		metricsRefreshInterval:             defaultMetricsRefreshInterval,
196		metricsAllowedNetworks:             []string{defaultMetricsAllowedNetworks},
197		watchdog:                           defaultWatchdog,
198		invidiousInstance:                  defaultInvidiousInstance,
199	}
200}
201
202// LogDateTime returns true if the date/time should be displayed in log messages.
203func (o *Options) LogDateTime() bool {
204	return o.logDateTime
205}
206
207// HasMaintenanceMode returns true if maintenance mode is enabled.
208func (o *Options) HasMaintenanceMode() bool {
209	return o.maintenanceMode
210}
211
212// MaintenanceMessage returns maintenance message.
213func (o *Options) MaintenanceMessage() string {
214	return o.maintenanceMessage
215}
216
217// HasDebugMode returns true if debug mode is enabled.
218func (o *Options) HasDebugMode() bool {
219	return o.debug
220}
221
222// HasServerTimingHeader returns true if server-timing headers enabled.
223func (o *Options) HasServerTimingHeader() bool {
224	return o.serverTimingHeader
225}
226
227// BaseURL returns the application base URL with path.
228func (o *Options) BaseURL() string {
229	return o.baseURL
230}
231
232// RootURL returns the base URL without path.
233func (o *Options) RootURL() string {
234	return o.rootURL
235}
236
237// BasePath returns the application base path according to the base URL.
238func (o *Options) BasePath() string {
239	return o.basePath
240}
241
242// IsDefaultDatabaseURL returns true if the default database URL is used.
243func (o *Options) IsDefaultDatabaseURL() bool {
244	return o.databaseURL == defaultDatabaseURL
245}
246
247// DatabaseURL returns the database URL.
248func (o *Options) DatabaseURL() string {
249	return o.databaseURL
250}
251
252// DatabaseMaxConns returns the maximum number of database connections.
253func (o *Options) DatabaseMaxConns() int {
254	return o.databaseMaxConns
255}
256
257// DatabaseMinConns returns the minimum number of database connections.
258func (o *Options) DatabaseMinConns() int {
259	return o.databaseMinConns
260}
261
262// DatabaseConnectionLifetime returns the maximum amount of time a connection may be reused.
263func (o *Options) DatabaseConnectionLifetime() time.Duration {
264	return time.Duration(o.databaseConnectionLifetime) * time.Minute
265}
266
267// ListenAddr returns the listen address for the HTTP server.
268func (o *Options) ListenAddr() string {
269	return o.listenAddr
270}
271
272// CertFile returns the SSL certificate filename if any.
273func (o *Options) CertFile() string {
274	return o.certFile
275}
276
277// CertKeyFile returns the private key filename for custom SSL certificate.
278func (o *Options) CertKeyFile() string {
279	return o.certKeyFile
280}
281
282// CertDomain returns the domain to use for Let's Encrypt certificate.
283func (o *Options) CertDomain() string {
284	return o.certDomain
285}
286
287// CleanupFrequencyHours returns the interval in hours for cleanup jobs.
288func (o *Options) CleanupFrequencyHours() int {
289	return o.cleanupFrequencyHours
290}
291
292// CleanupArchiveReadDays returns the number of days after which marking read items as removed.
293func (o *Options) CleanupArchiveReadDays() int {
294	return o.cleanupArchiveReadDays
295}
296
297// CleanupArchiveUnreadDays returns the number of days after which marking unread items as removed.
298func (o *Options) CleanupArchiveUnreadDays() int {
299	return o.cleanupArchiveUnreadDays
300}
301
302// CleanupArchiveBatchSize returns the number of entries to archive for each interval.
303func (o *Options) CleanupArchiveBatchSize() int {
304	return o.cleanupArchiveBatchSize
305}
306
307// CleanupRemoveSessionsDays returns the number of days after which to remove sessions.
308func (o *Options) CleanupRemoveSessionsDays() int {
309	return o.cleanupRemoveSessionsDays
310}
311
312// WorkerPoolSize returns the number of background worker.
313func (o *Options) WorkerPoolSize() int {
314	return o.workerPoolSize
315}
316
317// PollingFrequency returns the interval to refresh feeds in the background.
318func (o *Options) PollingFrequency() int {
319	return o.pollingFrequency
320}
321
322// BatchSize returns the number of feeds to send for background processing.
323func (o *Options) BatchSize() int {
324	return o.batchSize
325}
326
327// PollingScheduler returns the scheduler used for polling feeds.
328func (o *Options) PollingScheduler() string {
329	return o.pollingScheduler
330}
331
332// SchedulerEntryFrequencyMaxInterval returns the maximum interval in minutes for the entry frequency scheduler.
333func (o *Options) SchedulerEntryFrequencyMaxInterval() int {
334	return o.schedulerEntryFrequencyMaxInterval
335}
336
337// SchedulerEntryFrequencyMinInterval returns the minimum interval in minutes for the entry frequency scheduler.
338func (o *Options) SchedulerEntryFrequencyMinInterval() int {
339	return o.schedulerEntryFrequencyMinInterval
340}
341
342// PollingParsingErrorLimit returns the limit of errors when to stop polling.
343func (o *Options) PollingParsingErrorLimit() int {
344	return o.pollingParsingErrorLimit
345}
346
347// IsOAuth2UserCreationAllowed returns true if user creation is allowed for OAuth2 users.
348func (o *Options) IsOAuth2UserCreationAllowed() bool {
349	return o.oauth2UserCreationAllowed
350}
351
352// OAuth2ClientID returns the OAuth2 Client ID.
353func (o *Options) OAuth2ClientID() string {
354	return o.oauth2ClientID
355}
356
357// OAuth2ClientSecret returns the OAuth2 client secret.
358func (o *Options) OAuth2ClientSecret() string {
359	return o.oauth2ClientSecret
360}
361
362// OAuth2RedirectURL returns the OAuth2 redirect URL.
363func (o *Options) OAuth2RedirectURL() string {
364	return o.oauth2RedirectURL
365}
366
367// OAuth2OidcDiscoveryEndpoint returns the OAuth2 OIDC discovery endpoint.
368func (o *Options) OAuth2OidcDiscoveryEndpoint() string {
369	return o.oauth2OidcDiscoveryEndpoint
370}
371
372// OAuth2Provider returns the name of the OAuth2 provider configured.
373func (o *Options) OAuth2Provider() string {
374	return o.oauth2Provider
375}
376
377// HasHSTS returns true if HTTP Strict Transport Security is enabled.
378func (o *Options) HasHSTS() bool {
379	return o.hsts
380}
381
382// RunMigrations returns true if the environment variable RUN_MIGRATIONS is not empty.
383func (o *Options) RunMigrations() bool {
384	return o.runMigrations
385}
386
387// CreateAdmin returns true if the environment variable CREATE_ADMIN is not empty.
388func (o *Options) CreateAdmin() bool {
389	return o.createAdmin
390}
391
392// AdminUsername returns the admin username if defined.
393func (o *Options) AdminUsername() string {
394	return o.adminUsername
395}
396
397// AdminPassword returns the admin password if defined.
398func (o *Options) AdminPassword() string {
399	return o.adminPassword
400}
401
402// FetchYouTubeWatchTime returns true if the YouTube video duration
403// should be fetched and used as a reading time.
404func (o *Options) FetchYouTubeWatchTime() bool {
405	return o.fetchYouTubeWatchTime
406}
407
408// ProxyImages returns "none" to never proxy, "http-only" to proxy non-HTTPS, "all" to always proxy.
409func (o *Options) ProxyImages() string {
410	return o.proxyImages
411}
412
413// HasHTTPService returns true if the HTTP service is enabled.
414func (o *Options) HasHTTPService() bool {
415	return o.httpService
416}
417
418// HasSchedulerService returns true if the scheduler service is enabled.
419func (o *Options) HasSchedulerService() bool {
420	return o.schedulerService
421}
422
423// PocketConsumerKey returns the Pocket Consumer Key if configured.
424func (o *Options) PocketConsumerKey(defaultValue string) string {
425	if o.pocketConsumerKey != "" {
426		return o.pocketConsumerKey
427	}
428	return defaultValue
429}
430
431// HTTPClientTimeout returns the time limit in seconds before the HTTP client cancel the request.
432func (o *Options) HTTPClientTimeout() int {
433	return o.httpClientTimeout
434}
435
436// HTTPClientMaxBodySize returns the number of bytes allowed for the HTTP client to transfer.
437func (o *Options) HTTPClientMaxBodySize() int64 {
438	return o.httpClientMaxBodySize
439}
440
441// HTTPClientProxy returns the proxy URL for HTTP client.
442func (o *Options) HTTPClientProxy() string {
443	return o.httpClientProxy
444}
445
446// HasHTTPClientProxyConfigured returns true if the HTTP proxy is configured.
447func (o *Options) HasHTTPClientProxyConfigured() bool {
448	return o.httpClientProxy != ""
449}
450
451// AuthProxyHeader returns an HTTP header name that contains username for
452// authentication using auth proxy.
453func (o *Options) AuthProxyHeader() string {
454	return o.authProxyHeader
455}
456
457// IsAuthProxyUserCreationAllowed returns true if user creation is allowed for
458// users authenticated using auth proxy.
459func (o *Options) IsAuthProxyUserCreationAllowed() bool {
460	return o.authProxyUserCreation
461}
462
463// HasMetricsCollector returns true if metrics collection is enabled.
464func (o *Options) HasMetricsCollector() bool {
465	return o.metricsCollector
466}
467
468// MetricsRefreshInterval returns the refresh interval in seconds.
469func (o *Options) MetricsRefreshInterval() int {
470	return o.metricsRefreshInterval
471}
472
473// MetricsAllowedNetworks returns the list of networks allowed to connect to the metrics endpoint.
474func (o *Options) MetricsAllowedNetworks() []string {
475	return o.metricsAllowedNetworks
476}
477
478// HTTPClientUserAgent returns the global User-Agent header for miniflux.
479func (o *Options) HTTPClientUserAgent() string {
480	return o.httpClientUserAgent
481}
482
483// HasWatchdog returns true if the systemd watchdog is enabled.
484func (o *Options) HasWatchdog() bool {
485	return o.watchdog
486}
487
488// InvidiousInstance returns the invidious instance used by miniflux
489func (o *Options) InvidiousInstance() string {
490	return o.invidiousInstance
491}
492
493// SortedOptions returns options as a list of key value pairs, sorted by keys.
494func (o *Options) SortedOptions(redactSecret bool) []*Option {
495	var keyValues = map[string]interface{}{
496		"ADMIN_PASSWORD":                         redactSecretValue(o.adminPassword, redactSecret),
497		"ADMIN_USERNAME":                         o.adminUsername,
498		"AUTH_PROXY_HEADER":                      o.authProxyHeader,
499		"AUTH_PROXY_USER_CREATION":               o.authProxyUserCreation,
500		"BASE_PATH":                              o.basePath,
501		"BASE_URL":                               o.baseURL,
502		"BATCH_SIZE":                             o.batchSize,
503		"CERT_DOMAIN":                            o.certDomain,
504		"CERT_FILE":                              o.certFile,
505		"CLEANUP_ARCHIVE_READ_DAYS":              o.cleanupArchiveReadDays,
506		"CLEANUP_ARCHIVE_UNREAD_DAYS":            o.cleanupArchiveUnreadDays,
507		"CLEANUP_ARCHIVE_BATCH_SIZE":             o.cleanupArchiveBatchSize,
508		"CLEANUP_FREQUENCY_HOURS":                o.cleanupFrequencyHours,
509		"CLEANUP_REMOVE_SESSIONS_DAYS":           o.cleanupRemoveSessionsDays,
510		"CREATE_ADMIN":                           o.createAdmin,
511		"DATABASE_MAX_CONNS":                     o.databaseMaxConns,
512		"DATABASE_MIN_CONNS":                     o.databaseMinConns,
513		"DATABASE_CONNECTION_LIFETIME":           o.databaseConnectionLifetime,
514		"DATABASE_URL":                           redactSecretValue(o.databaseURL, redactSecret),
515		"DEBUG":                                  o.debug,
516		"DISABLE_HSTS":                           !o.hsts,
517		"DISABLE_SCHEDULER_SERVICE":              !o.schedulerService,
518		"DISABLE_HTTP_SERVICE":                   !o.httpService,
519		"FETCH_YOUTUBE_WATCH_TIME":               o.fetchYouTubeWatchTime,
520		"HTTPS":                                  o.HTTPS,
521		"HTTP_CLIENT_MAX_BODY_SIZE":              o.httpClientMaxBodySize,
522		"HTTP_CLIENT_PROXY":                      o.httpClientProxy,
523		"HTTP_CLIENT_TIMEOUT":                    o.httpClientTimeout,
524		"HTTP_CLIENT_USER_AGENT":                 o.httpClientUserAgent,
525		"HTTP_SERVICE":                           o.httpService,
526		"KEY_FILE":                               o.certKeyFile,
527		"INVIDIOUS_INSTANCE":                     o.invidiousInstance,
528		"LISTEN_ADDR":                            o.listenAddr,
529		"LOG_DATE_TIME":                          o.logDateTime,
530		"MAINTENANCE_MESSAGE":                    o.maintenanceMessage,
531		"MAINTENANCE_MODE":                       o.maintenanceMode,
532		"METRICS_ALLOWED_NETWORKS":               strings.Join(o.metricsAllowedNetworks, ","),
533		"METRICS_COLLECTOR":                      o.metricsCollector,
534		"METRICS_REFRESH_INTERVAL":               o.metricsRefreshInterval,
535		"OAUTH2_CLIENT_ID":                       o.oauth2ClientID,
536		"OAUTH2_CLIENT_SECRET":                   redactSecretValue(o.oauth2ClientSecret, redactSecret),
537		"OAUTH2_OIDC_DISCOVERY_ENDPOINT":         o.oauth2OidcDiscoveryEndpoint,
538		"OAUTH2_PROVIDER":                        o.oauth2Provider,
539		"OAUTH2_REDIRECT_URL":                    o.oauth2RedirectURL,
540		"OAUTH2_USER_CREATION":                   o.oauth2UserCreationAllowed,
541		"POCKET_CONSUMER_KEY":                    redactSecretValue(o.pocketConsumerKey, redactSecret),
542		"POLLING_FREQUENCY":                      o.pollingFrequency,
543		"POLLING_PARSING_ERROR_LIMIT":            o.pollingParsingErrorLimit,
544		"POLLING_SCHEDULER":                      o.pollingScheduler,
545		"PROXY_IMAGES":                           o.proxyImages,
546		"ROOT_URL":                               o.rootURL,
547		"RUN_MIGRATIONS":                         o.runMigrations,
548		"SCHEDULER_ENTRY_FREQUENCY_MAX_INTERVAL": o.schedulerEntryFrequencyMaxInterval,
549		"SCHEDULER_ENTRY_FREQUENCY_MIN_INTERVAL": o.schedulerEntryFrequencyMinInterval,
550		"SCHEDULER_SERVICE":                      o.schedulerService,
551		"SERVER_TIMING_HEADER":                   o.serverTimingHeader,
552		"WORKER_POOL_SIZE":                       o.workerPoolSize,
553		"WATCHDOG":                               o.watchdog,
554	}
555
556	keys := make([]string, 0, len(keyValues))
557	for key := range keyValues {
558		keys = append(keys, key)
559	}
560	sort.Strings(keys)
561
562	var sortedOptions []*Option
563	for _, key := range keys {
564		sortedOptions = append(sortedOptions, &Option{Key: key, Value: keyValues[key]})
565	}
566	return sortedOptions
567}
568
569func (o *Options) String() string {
570	var builder strings.Builder
571
572	for _, option := range o.SortedOptions(false) {
573		fmt.Fprintf(&builder, "%s=%v\n", option.Key, option.Value)
574	}
575
576	return builder.String()
577}
578
579func redactSecretValue(value string, redactSecret bool) string {
580	if redactSecret && value != "" {
581		return "******"
582	}
583	return value
584}
585