options.go 23 KB

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