options.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package config // import "miniflux.app/v2/internal/config"
  4. import (
  5. "errors"
  6. "maps"
  7. "net"
  8. "net/url"
  9. "slices"
  10. "strings"
  11. "time"
  12. )
  13. type optionPair struct {
  14. Key string
  15. Value string
  16. }
  17. type configValueType int
  18. const (
  19. stringType configValueType = iota
  20. stringListType
  21. boolType
  22. intType
  23. int64Type
  24. urlType
  25. secondType
  26. minuteType
  27. hourType
  28. dayType
  29. secretFileType
  30. bytesType
  31. )
  32. type configValue struct {
  33. parsedStringValue string
  34. parsedBoolValue bool
  35. parsedIntValue int
  36. parsedInt64Value int64
  37. parsedDuration time.Duration
  38. parsedStringList []string
  39. parsedURLValue *url.URL
  40. parsedBytesValue []byte
  41. rawValue string
  42. valueType configValueType
  43. secret bool
  44. targetKey string
  45. validator func(string) error
  46. }
  47. type configOptions struct {
  48. rootURL string
  49. basePath string
  50. youTubeEmbedDomain string
  51. options map[string]*configValue
  52. }
  53. // NewConfigOptions creates a new instance of ConfigOptions with default values.
  54. func NewConfigOptions() *configOptions {
  55. return &configOptions{
  56. rootURL: "http://localhost",
  57. basePath: "",
  58. youTubeEmbedDomain: "www.youtube-nocookie.com",
  59. options: map[string]*configValue{
  60. "ADMIN_PASSWORD": {
  61. parsedStringValue: "",
  62. rawValue: "",
  63. valueType: stringType,
  64. secret: true,
  65. },
  66. "ADMIN_PASSWORD_FILE": {
  67. parsedStringValue: "",
  68. rawValue: "",
  69. valueType: secretFileType,
  70. targetKey: "ADMIN_PASSWORD",
  71. },
  72. "ADMIN_USERNAME": {
  73. parsedStringValue: "",
  74. rawValue: "",
  75. valueType: stringType,
  76. },
  77. "ADMIN_USERNAME_FILE": {
  78. parsedStringValue: "",
  79. rawValue: "",
  80. valueType: secretFileType,
  81. targetKey: "ADMIN_USERNAME",
  82. },
  83. "AUTH_PROXY_HEADER": {
  84. parsedStringValue: "",
  85. rawValue: "",
  86. valueType: stringType,
  87. },
  88. "AUTH_PROXY_USER_CREATION": {
  89. parsedBoolValue: false,
  90. rawValue: "0",
  91. valueType: boolType,
  92. },
  93. "BASE_URL": {
  94. parsedStringValue: "http://localhost",
  95. rawValue: "http://localhost",
  96. valueType: stringType,
  97. },
  98. "BATCH_SIZE": {
  99. parsedIntValue: 100,
  100. rawValue: "100",
  101. valueType: intType,
  102. validator: func(rawValue string) error {
  103. return validateGreaterOrEqualThan(rawValue, 1)
  104. },
  105. },
  106. "CERT_DOMAIN": {
  107. parsedStringValue: "",
  108. rawValue: "",
  109. valueType: stringType,
  110. },
  111. "CERT_FILE": {
  112. parsedStringValue: "",
  113. rawValue: "",
  114. valueType: stringType,
  115. },
  116. "CLEANUP_ARCHIVE_BATCH_SIZE": {
  117. parsedIntValue: 10000,
  118. rawValue: "10000",
  119. valueType: intType,
  120. validator: func(rawValue string) error {
  121. return validateGreaterOrEqualThan(rawValue, 1)
  122. },
  123. },
  124. "CLEANUP_ARCHIVE_READ_DAYS": {
  125. parsedDuration: time.Hour * 24 * 60,
  126. rawValue: "60",
  127. valueType: dayType,
  128. },
  129. "CLEANUP_ARCHIVE_UNREAD_DAYS": {
  130. parsedDuration: time.Hour * 24 * 180,
  131. rawValue: "180",
  132. valueType: dayType,
  133. },
  134. "CLEANUP_FREQUENCY_HOURS": {
  135. parsedDuration: time.Hour * 24,
  136. rawValue: "24",
  137. valueType: hourType,
  138. validator: func(rawValue string) error {
  139. return validateGreaterOrEqualThan(rawValue, 1)
  140. },
  141. },
  142. "CLEANUP_REMOVE_SESSIONS_DAYS": {
  143. parsedDuration: time.Hour * 24 * 30,
  144. rawValue: "30",
  145. valueType: dayType,
  146. },
  147. "CREATE_ADMIN": {
  148. parsedBoolValue: false,
  149. rawValue: "0",
  150. valueType: boolType,
  151. },
  152. "DATABASE_CONNECTION_LIFETIME": {
  153. parsedDuration: time.Minute * 5,
  154. rawValue: "5",
  155. valueType: minuteType,
  156. validator: func(rawValue string) error {
  157. return validateGreaterThan(rawValue, 0)
  158. },
  159. },
  160. "DATABASE_MAX_CONNS": {
  161. parsedIntValue: 20,
  162. rawValue: "20",
  163. valueType: intType,
  164. validator: func(rawValue string) error {
  165. return validateGreaterOrEqualThan(rawValue, 1)
  166. },
  167. },
  168. "DATABASE_MIN_CONNS": {
  169. parsedIntValue: 1,
  170. rawValue: "1",
  171. valueType: intType,
  172. validator: func(rawValue string) error {
  173. return validateGreaterOrEqualThan(rawValue, 0)
  174. },
  175. },
  176. "DATABASE_URL": {
  177. parsedStringValue: "user=postgres password=postgres dbname=miniflux2 sslmode=disable",
  178. rawValue: "user=postgres password=postgres dbname=miniflux2 sslmode=disable",
  179. valueType: stringType,
  180. secret: true,
  181. },
  182. "DATABASE_URL_FILE": {
  183. parsedStringValue: "",
  184. rawValue: "",
  185. valueType: secretFileType,
  186. targetKey: "DATABASE_URL",
  187. },
  188. "DISABLE_API": {
  189. parsedBoolValue: false,
  190. rawValue: "0",
  191. valueType: boolType,
  192. },
  193. "DISABLE_HSTS": {
  194. parsedBoolValue: false,
  195. rawValue: "0",
  196. valueType: boolType,
  197. },
  198. "DISABLE_HTTP_SERVICE": {
  199. parsedBoolValue: false,
  200. rawValue: "0",
  201. valueType: boolType,
  202. },
  203. "DISABLE_LOCAL_AUTH": {
  204. parsedBoolValue: false,
  205. rawValue: "0",
  206. valueType: boolType,
  207. },
  208. "DISABLE_SCHEDULER_SERVICE": {
  209. parsedBoolValue: false,
  210. rawValue: "0",
  211. valueType: boolType,
  212. },
  213. "FETCHER_ALLOW_PRIVATE_NETWORKS": {
  214. parsedBoolValue: false,
  215. rawValue: "0",
  216. valueType: boolType,
  217. },
  218. "FETCH_BILIBILI_WATCH_TIME": {
  219. parsedBoolValue: false,
  220. rawValue: "0",
  221. valueType: boolType,
  222. },
  223. "FETCH_NEBULA_WATCH_TIME": {
  224. parsedBoolValue: false,
  225. rawValue: "0",
  226. valueType: boolType,
  227. },
  228. "FETCH_ODYSEE_WATCH_TIME": {
  229. parsedBoolValue: false,
  230. rawValue: "0",
  231. valueType: boolType,
  232. },
  233. "FETCH_YOUTUBE_WATCH_TIME": {
  234. parsedBoolValue: false,
  235. rawValue: "0",
  236. valueType: boolType,
  237. },
  238. "FORCE_REFRESH_INTERVAL": {
  239. parsedDuration: 30 * time.Minute,
  240. rawValue: "30",
  241. valueType: minuteType,
  242. validator: func(rawValue string) error {
  243. return validateGreaterThan(rawValue, 0)
  244. },
  245. },
  246. "HTTP_CLIENT_MAX_BODY_SIZE": {
  247. parsedInt64Value: 15,
  248. rawValue: "15",
  249. valueType: int64Type,
  250. validator: func(rawValue string) error {
  251. return validateGreaterOrEqualThan(rawValue, 1)
  252. },
  253. },
  254. "HTTP_CLIENT_PROXIES": {
  255. parsedStringList: []string{},
  256. rawValue: "",
  257. valueType: stringListType,
  258. secret: true,
  259. },
  260. "HTTP_CLIENT_PROXY": {
  261. parsedURLValue: nil,
  262. rawValue: "",
  263. valueType: urlType,
  264. secret: true,
  265. },
  266. "HTTP_CLIENT_TIMEOUT": {
  267. parsedDuration: 20 * time.Second,
  268. rawValue: "20",
  269. valueType: secondType,
  270. validator: func(rawValue string) error {
  271. return validateGreaterOrEqualThan(rawValue, 1)
  272. },
  273. },
  274. "HTTP_CLIENT_USER_AGENT": {
  275. parsedStringValue: "",
  276. rawValue: "",
  277. valueType: stringType,
  278. },
  279. "HTTP_SERVER_TIMEOUT": {
  280. parsedDuration: 300 * time.Second,
  281. rawValue: "300",
  282. valueType: secondType,
  283. validator: func(rawValue string) error {
  284. return validateGreaterOrEqualThan(rawValue, 1)
  285. },
  286. },
  287. "HTTPS": {
  288. parsedBoolValue: false,
  289. rawValue: "0",
  290. valueType: boolType,
  291. },
  292. "INTEGRATION_ALLOW_PRIVATE_NETWORKS": {
  293. parsedBoolValue: false,
  294. rawValue: "0",
  295. valueType: boolType,
  296. },
  297. "INVIDIOUS_INSTANCE": {
  298. parsedStringValue: "yewtu.be",
  299. rawValue: "yewtu.be",
  300. valueType: stringType,
  301. },
  302. "KEY_FILE": {
  303. parsedStringValue: "",
  304. rawValue: "",
  305. valueType: stringType,
  306. },
  307. "LISTEN_ADDR": {
  308. parsedStringList: []string{"127.0.0.1:8080"},
  309. rawValue: "127.0.0.1:8080",
  310. valueType: stringListType,
  311. },
  312. "LOG_DATE_TIME": {
  313. parsedBoolValue: false,
  314. rawValue: "0",
  315. valueType: boolType,
  316. },
  317. "LOG_FILE": {
  318. parsedStringValue: "stderr",
  319. rawValue: "stderr",
  320. valueType: stringType,
  321. },
  322. "LOG_FORMAT": {
  323. parsedStringValue: "text",
  324. rawValue: "text",
  325. valueType: stringType,
  326. validator: func(rawValue string) error {
  327. return validateChoices(rawValue, []string{"text", "json"})
  328. },
  329. },
  330. "LOG_LEVEL": {
  331. parsedStringValue: "info",
  332. rawValue: "info",
  333. valueType: stringType,
  334. validator: func(rawValue string) error {
  335. return validateChoices(rawValue, []string{"debug", "info", "warning", "error"})
  336. },
  337. },
  338. "MAINTENANCE_MESSAGE": {
  339. parsedStringValue: "Miniflux is currently under maintenance",
  340. rawValue: "Miniflux is currently under maintenance",
  341. valueType: stringType,
  342. },
  343. "MAINTENANCE_MODE": {
  344. parsedBoolValue: false,
  345. rawValue: "0",
  346. valueType: boolType,
  347. },
  348. "MEDIA_PROXY_CUSTOM_URL": {
  349. rawValue: "",
  350. valueType: urlType,
  351. },
  352. "MEDIA_PROXY_HTTP_CLIENT_TIMEOUT": {
  353. parsedDuration: 120 * time.Second,
  354. rawValue: "120",
  355. valueType: secondType,
  356. validator: func(rawValue string) error {
  357. return validateGreaterOrEqualThan(rawValue, 1)
  358. },
  359. },
  360. "MEDIA_PROXY_MODE": {
  361. parsedStringValue: "http-only",
  362. rawValue: "http-only",
  363. valueType: stringType,
  364. validator: func(rawValue string) error {
  365. return validateChoices(rawValue, []string{"none", "http-only", "all"})
  366. },
  367. },
  368. "MEDIA_PROXY_PRIVATE_KEY": {
  369. valueType: bytesType,
  370. secret: true,
  371. },
  372. "MEDIA_PROXY_RESOURCE_TYPES": {
  373. parsedStringList: []string{"image"},
  374. rawValue: "image",
  375. valueType: stringListType,
  376. validator: func(rawValue string) error {
  377. resourceTypes := parseStringListValue(rawValue, nil)
  378. if len(resourceTypes) == 0 {
  379. return errors.New("at least one resource type is required")
  380. }
  381. return validateListChoices(resourceTypes, []string{"image", "video", "audio"})
  382. },
  383. },
  384. "METRICS_ALLOWED_NETWORKS": {
  385. parsedStringList: []string{"127.0.0.1/8"},
  386. rawValue: "127.0.0.1/8",
  387. valueType: stringListType,
  388. },
  389. "METRICS_COLLECTOR": {
  390. parsedBoolValue: false,
  391. rawValue: "0",
  392. valueType: boolType,
  393. },
  394. "METRICS_PASSWORD": {
  395. parsedStringValue: "",
  396. rawValue: "",
  397. valueType: stringType,
  398. secret: true,
  399. },
  400. "METRICS_PASSWORD_FILE": {
  401. parsedStringValue: "",
  402. rawValue: "",
  403. valueType: secretFileType,
  404. targetKey: "METRICS_PASSWORD",
  405. },
  406. "METRICS_REFRESH_INTERVAL": {
  407. parsedDuration: 60 * time.Second,
  408. rawValue: "60",
  409. valueType: secondType,
  410. validator: func(rawValue string) error {
  411. return validateGreaterOrEqualThan(rawValue, 1)
  412. },
  413. },
  414. "METRICS_USERNAME": {
  415. parsedStringValue: "",
  416. rawValue: "",
  417. valueType: stringType,
  418. },
  419. "METRICS_USERNAME_FILE": {
  420. parsedStringValue: "",
  421. rawValue: "",
  422. valueType: secretFileType,
  423. targetKey: "METRICS_USERNAME",
  424. },
  425. "OAUTH2_CLIENT_ID": {
  426. parsedStringValue: "",
  427. rawValue: "",
  428. valueType: stringType,
  429. secret: true,
  430. },
  431. "OAUTH2_CLIENT_ID_FILE": {
  432. parsedStringValue: "",
  433. rawValue: "",
  434. valueType: secretFileType,
  435. targetKey: "OAUTH2_CLIENT_ID",
  436. },
  437. "OAUTH2_CLIENT_SECRET": {
  438. parsedStringValue: "",
  439. rawValue: "",
  440. valueType: stringType,
  441. secret: true,
  442. },
  443. "OAUTH2_CLIENT_SECRET_FILE": {
  444. parsedStringValue: "",
  445. rawValue: "",
  446. valueType: secretFileType,
  447. targetKey: "OAUTH2_CLIENT_SECRET",
  448. },
  449. "OAUTH2_OIDC_DISCOVERY_ENDPOINT": {
  450. parsedStringValue: "",
  451. rawValue: "",
  452. valueType: stringType,
  453. },
  454. "OAUTH2_OIDC_PROVIDER_NAME": {
  455. parsedStringValue: "OpenID Connect",
  456. rawValue: "OpenID Connect",
  457. valueType: stringType,
  458. },
  459. "OAUTH2_PROVIDER": {
  460. parsedStringValue: "",
  461. rawValue: "",
  462. valueType: stringType,
  463. validator: func(rawValue string) error {
  464. return validateChoices(rawValue, []string{"oidc", "google"})
  465. },
  466. },
  467. "OAUTH2_REDIRECT_URL": {
  468. parsedStringValue: "",
  469. rawValue: "",
  470. valueType: stringType,
  471. },
  472. "OAUTH2_USER_CREATION": {
  473. parsedBoolValue: false,
  474. rawValue: "0",
  475. valueType: boolType,
  476. },
  477. "POLLING_FREQUENCY": {
  478. parsedDuration: 60 * time.Minute,
  479. rawValue: "60",
  480. valueType: minuteType,
  481. validator: func(rawValue string) error {
  482. return validateGreaterOrEqualThan(rawValue, 1)
  483. },
  484. },
  485. "POLLING_LIMIT_PER_HOST": {
  486. parsedIntValue: 0,
  487. rawValue: "0",
  488. valueType: intType,
  489. validator: func(rawValue string) error {
  490. return validateGreaterOrEqualThan(rawValue, 0)
  491. },
  492. },
  493. "POLLING_PARSING_ERROR_LIMIT": {
  494. parsedIntValue: 3,
  495. rawValue: "3",
  496. valueType: intType,
  497. validator: func(rawValue string) error {
  498. return validateGreaterOrEqualThan(rawValue, 0)
  499. },
  500. },
  501. "POLLING_SCHEDULER": {
  502. parsedStringValue: "round_robin",
  503. rawValue: "round_robin",
  504. valueType: stringType,
  505. validator: func(rawValue string) error {
  506. return validateChoices(rawValue, []string{"round_robin", "entry_frequency"})
  507. },
  508. },
  509. "PORT": {
  510. parsedStringValue: "",
  511. rawValue: "",
  512. valueType: stringType,
  513. validator: func(rawValue string) error {
  514. return validateRange(rawValue, 1, 65535)
  515. },
  516. },
  517. "RUN_MIGRATIONS": {
  518. parsedBoolValue: false,
  519. rawValue: "0",
  520. valueType: boolType,
  521. },
  522. "SCHEDULER_ENTRY_FREQUENCY_FACTOR": {
  523. parsedIntValue: 1,
  524. rawValue: "1",
  525. valueType: intType,
  526. validator: func(rawValue string) error {
  527. return validateGreaterOrEqualThan(rawValue, 1)
  528. },
  529. },
  530. "SCHEDULER_ENTRY_FREQUENCY_MAX_INTERVAL": {
  531. parsedDuration: 24 * time.Hour,
  532. rawValue: "1440",
  533. valueType: minuteType,
  534. validator: func(rawValue string) error {
  535. return validateGreaterOrEqualThan(rawValue, 1)
  536. },
  537. },
  538. "SCHEDULER_ENTRY_FREQUENCY_MIN_INTERVAL": {
  539. parsedDuration: 5 * time.Minute,
  540. rawValue: "5",
  541. valueType: minuteType,
  542. validator: func(rawValue string) error {
  543. return validateGreaterOrEqualThan(rawValue, 1)
  544. },
  545. },
  546. "SCHEDULER_ROUND_ROBIN_MAX_INTERVAL": {
  547. parsedDuration: 1440 * time.Minute,
  548. rawValue: "1440",
  549. valueType: minuteType,
  550. validator: func(rawValue string) error {
  551. return validateGreaterOrEqualThan(rawValue, 1)
  552. },
  553. },
  554. "SCHEDULER_ROUND_ROBIN_MIN_INTERVAL": {
  555. parsedDuration: 60 * time.Minute,
  556. rawValue: "60",
  557. valueType: minuteType,
  558. validator: func(rawValue string) error {
  559. return validateGreaterOrEqualThan(rawValue, 1)
  560. },
  561. },
  562. "TRUSTED_REVERSE_PROXY_NETWORKS": {
  563. parsedStringList: []string{},
  564. rawValue: "",
  565. valueType: stringListType,
  566. validator: func(rawValue string) error {
  567. networks := parseStringListValue(rawValue, nil)
  568. if len(networks) == 0 {
  569. return errors.New("at least one CIDR notation network is required")
  570. }
  571. for _, ip := range networks {
  572. if _, _, err := net.ParseCIDR(ip); err != nil {
  573. return err
  574. }
  575. }
  576. return nil
  577. },
  578. },
  579. "WATCHDOG": {
  580. parsedBoolValue: true,
  581. rawValue: "1",
  582. valueType: boolType,
  583. },
  584. "WEBAUTHN": {
  585. parsedBoolValue: false,
  586. rawValue: "0",
  587. valueType: boolType,
  588. },
  589. "WORKER_POOL_SIZE": {
  590. parsedIntValue: 16,
  591. rawValue: "16",
  592. valueType: intType,
  593. validator: func(rawValue string) error {
  594. return validateGreaterOrEqualThan(rawValue, 1)
  595. },
  596. },
  597. "YOUTUBE_API_KEY": {
  598. parsedStringValue: "",
  599. rawValue: "",
  600. valueType: stringType,
  601. secret: true,
  602. },
  603. "YOUTUBE_EMBED_URL_OVERRIDE": {
  604. parsedStringValue: "https://www.youtube-nocookie.com/embed/",
  605. rawValue: "https://www.youtube-nocookie.com/embed/",
  606. valueType: stringType,
  607. },
  608. },
  609. }
  610. }
  611. func (c *configOptions) AdminPassword() string {
  612. return c.options["ADMIN_PASSWORD"].parsedStringValue
  613. }
  614. func (c *configOptions) AdminUsername() string {
  615. return c.options["ADMIN_USERNAME"].parsedStringValue
  616. }
  617. func (c *configOptions) AuthProxyHeader() string {
  618. return c.options["AUTH_PROXY_HEADER"].parsedStringValue
  619. }
  620. func (c *configOptions) AuthProxyUserCreation() bool {
  621. return c.options["AUTH_PROXY_USER_CREATION"].parsedBoolValue
  622. }
  623. func (c *configOptions) BasePath() string {
  624. return c.basePath
  625. }
  626. func (c *configOptions) BaseURL() string {
  627. return c.options["BASE_URL"].parsedStringValue
  628. }
  629. func (c *configOptions) RootURL() string {
  630. return c.rootURL
  631. }
  632. func (c *configOptions) BatchSize() int {
  633. return c.options["BATCH_SIZE"].parsedIntValue
  634. }
  635. func (c *configOptions) CertDomain() string {
  636. return c.options["CERT_DOMAIN"].parsedStringValue
  637. }
  638. func (c *configOptions) CertFile() string {
  639. return c.options["CERT_FILE"].parsedStringValue
  640. }
  641. func (c *configOptions) CleanupArchiveBatchSize() int {
  642. return c.options["CLEANUP_ARCHIVE_BATCH_SIZE"].parsedIntValue
  643. }
  644. func (c *configOptions) CleanupArchiveReadInterval() time.Duration {
  645. return c.options["CLEANUP_ARCHIVE_READ_DAYS"].parsedDuration
  646. }
  647. func (c *configOptions) CleanupArchiveUnreadInterval() time.Duration {
  648. return c.options["CLEANUP_ARCHIVE_UNREAD_DAYS"].parsedDuration
  649. }
  650. func (c *configOptions) CleanupFrequency() time.Duration {
  651. return c.options["CLEANUP_FREQUENCY_HOURS"].parsedDuration
  652. }
  653. func (c *configOptions) CleanupRemoveSessionsInterval() time.Duration {
  654. return c.options["CLEANUP_REMOVE_SESSIONS_DAYS"].parsedDuration
  655. }
  656. func (c *configOptions) CreateAdmin() bool {
  657. return c.options["CREATE_ADMIN"].parsedBoolValue
  658. }
  659. func (c *configOptions) DatabaseConnectionLifetime() time.Duration {
  660. return c.options["DATABASE_CONNECTION_LIFETIME"].parsedDuration
  661. }
  662. func (c *configOptions) DatabaseMaxConns() int {
  663. return c.options["DATABASE_MAX_CONNS"].parsedIntValue
  664. }
  665. func (c *configOptions) DatabaseMinConns() int {
  666. return c.options["DATABASE_MIN_CONNS"].parsedIntValue
  667. }
  668. func (c *configOptions) DatabaseURL() string {
  669. return c.options["DATABASE_URL"].parsedStringValue
  670. }
  671. func (c *configOptions) DisableHSTS() bool {
  672. return c.options["DISABLE_HSTS"].parsedBoolValue
  673. }
  674. func (c *configOptions) DisableHTTPService() bool {
  675. return c.options["DISABLE_HTTP_SERVICE"].parsedBoolValue
  676. }
  677. func (c *configOptions) DisableLocalAuth() bool {
  678. return c.options["DISABLE_LOCAL_AUTH"].parsedBoolValue
  679. }
  680. func (c *configOptions) DisableSchedulerService() bool {
  681. return c.options["DISABLE_SCHEDULER_SERVICE"].parsedBoolValue
  682. }
  683. func (c *configOptions) FetchBilibiliWatchTime() bool {
  684. return c.options["FETCH_BILIBILI_WATCH_TIME"].parsedBoolValue
  685. }
  686. func (c *configOptions) FetchNebulaWatchTime() bool {
  687. return c.options["FETCH_NEBULA_WATCH_TIME"].parsedBoolValue
  688. }
  689. func (c *configOptions) FetchOdyseeWatchTime() bool {
  690. return c.options["FETCH_ODYSEE_WATCH_TIME"].parsedBoolValue
  691. }
  692. func (c *configOptions) FetchYouTubeWatchTime() bool {
  693. return c.options["FETCH_YOUTUBE_WATCH_TIME"].parsedBoolValue
  694. }
  695. func (c *configOptions) ForceRefreshInterval() time.Duration {
  696. return c.options["FORCE_REFRESH_INTERVAL"].parsedDuration
  697. }
  698. func (c *configOptions) HasHTTPClientProxiesConfigured() bool {
  699. return len(c.options["HTTP_CLIENT_PROXIES"].parsedStringList) > 0
  700. }
  701. func (c *configOptions) HasAPI() bool {
  702. return !c.options["DISABLE_API"].parsedBoolValue
  703. }
  704. func (c *configOptions) HasHTTPService() bool {
  705. return !c.options["DISABLE_HTTP_SERVICE"].parsedBoolValue
  706. }
  707. func (c *configOptions) HasHSTS() bool {
  708. return !c.options["DISABLE_HSTS"].parsedBoolValue
  709. }
  710. func (c *configOptions) HasHTTPClientProxyURLConfigured() bool {
  711. return c.options["HTTP_CLIENT_PROXY"].parsedURLValue != nil
  712. }
  713. func (c *configOptions) HasMaintenanceMode() bool {
  714. return c.options["MAINTENANCE_MODE"].parsedBoolValue
  715. }
  716. func (c *configOptions) HasMetricsCollector() bool {
  717. return c.options["METRICS_COLLECTOR"].parsedBoolValue
  718. }
  719. func (c *configOptions) HasSchedulerService() bool {
  720. return !c.options["DISABLE_SCHEDULER_SERVICE"].parsedBoolValue
  721. }
  722. func (c *configOptions) HasWatchdog() bool {
  723. return c.options["WATCHDOG"].parsedBoolValue
  724. }
  725. func (c *configOptions) HTTPClientMaxBodySize() int64 {
  726. return c.options["HTTP_CLIENT_MAX_BODY_SIZE"].parsedInt64Value * 1024 * 1024
  727. }
  728. func (c *configOptions) HTTPClientProxies() []string {
  729. return c.options["HTTP_CLIENT_PROXIES"].parsedStringList
  730. }
  731. func (c *configOptions) HTTPClientProxyURL() *url.URL {
  732. return c.options["HTTP_CLIENT_PROXY"].parsedURLValue
  733. }
  734. func (c *configOptions) HTTPClientTimeout() time.Duration {
  735. return c.options["HTTP_CLIENT_TIMEOUT"].parsedDuration
  736. }
  737. func (c *configOptions) HTTPClientUserAgent() string {
  738. if c.options["HTTP_CLIENT_USER_AGENT"].parsedStringValue != "" {
  739. return c.options["HTTP_CLIENT_USER_AGENT"].parsedStringValue
  740. }
  741. return defaultHTTPClientUserAgent
  742. }
  743. func (c *configOptions) HTTPServerTimeout() time.Duration {
  744. return c.options["HTTP_SERVER_TIMEOUT"].parsedDuration
  745. }
  746. func (c *configOptions) HTTPS() bool {
  747. return c.options["HTTPS"].parsedBoolValue
  748. }
  749. func (c *configOptions) FetcherAllowPrivateNetworks() bool {
  750. return c.options["FETCHER_ALLOW_PRIVATE_NETWORKS"].parsedBoolValue
  751. }
  752. func (c *configOptions) IntegrationAllowPrivateNetworks() bool {
  753. if c == nil {
  754. return false
  755. }
  756. return c.options["INTEGRATION_ALLOW_PRIVATE_NETWORKS"].parsedBoolValue
  757. }
  758. func (c *configOptions) InvidiousInstance() string {
  759. return c.options["INVIDIOUS_INSTANCE"].parsedStringValue
  760. }
  761. func (c *configOptions) IsAuthProxyUserCreationAllowed() bool {
  762. return c.options["AUTH_PROXY_USER_CREATION"].parsedBoolValue
  763. }
  764. func (c *configOptions) IsDefaultDatabaseURL() bool {
  765. return c.options["DATABASE_URL"].rawValue == "user=postgres password=postgres dbname=miniflux2 sslmode=disable"
  766. }
  767. func (c *configOptions) IsOAuth2UserCreationAllowed() bool {
  768. return c.options["OAUTH2_USER_CREATION"].parsedBoolValue
  769. }
  770. func (c *configOptions) CertKeyFile() string {
  771. return c.options["KEY_FILE"].parsedStringValue
  772. }
  773. func (c *configOptions) ListenAddr() []string {
  774. return c.options["LISTEN_ADDR"].parsedStringList
  775. }
  776. func (c *configOptions) LogFile() string {
  777. return c.options["LOG_FILE"].parsedStringValue
  778. }
  779. func (c *configOptions) LogDateTime() bool {
  780. return c.options["LOG_DATE_TIME"].parsedBoolValue
  781. }
  782. func (c *configOptions) LogFormat() string {
  783. return c.options["LOG_FORMAT"].parsedStringValue
  784. }
  785. func (c *configOptions) LogLevel() string {
  786. return c.options["LOG_LEVEL"].parsedStringValue
  787. }
  788. func (c *configOptions) MaintenanceMessage() string {
  789. return c.options["MAINTENANCE_MESSAGE"].parsedStringValue
  790. }
  791. func (c *configOptions) MaintenanceMode() bool {
  792. return c.options["MAINTENANCE_MODE"].parsedBoolValue
  793. }
  794. func (c *configOptions) MediaCustomProxyURL() *url.URL {
  795. return c.options["MEDIA_PROXY_CUSTOM_URL"].parsedURLValue
  796. }
  797. func (c *configOptions) MediaProxyHTTPClientTimeout() time.Duration {
  798. return c.options["MEDIA_PROXY_HTTP_CLIENT_TIMEOUT"].parsedDuration
  799. }
  800. func (c *configOptions) MediaProxyMode() string {
  801. return c.options["MEDIA_PROXY_MODE"].parsedStringValue
  802. }
  803. func (c *configOptions) MediaProxyPrivateKey() []byte {
  804. return c.options["MEDIA_PROXY_PRIVATE_KEY"].parsedBytesValue
  805. }
  806. func (c *configOptions) MediaProxyResourceTypes() []string {
  807. return c.options["MEDIA_PROXY_RESOURCE_TYPES"].parsedStringList
  808. }
  809. func (c *configOptions) MetricsAllowedNetworks() []string {
  810. return c.options["METRICS_ALLOWED_NETWORKS"].parsedStringList
  811. }
  812. func (c *configOptions) MetricsCollector() bool {
  813. return c.options["METRICS_COLLECTOR"].parsedBoolValue
  814. }
  815. func (c *configOptions) MetricsPassword() string {
  816. return c.options["METRICS_PASSWORD"].parsedStringValue
  817. }
  818. func (c *configOptions) MetricsRefreshInterval() time.Duration {
  819. return c.options["METRICS_REFRESH_INTERVAL"].parsedDuration
  820. }
  821. func (c *configOptions) MetricsUsername() string {
  822. return c.options["METRICS_USERNAME"].parsedStringValue
  823. }
  824. func (c *configOptions) OAuth2ClientID() string {
  825. return c.options["OAUTH2_CLIENT_ID"].parsedStringValue
  826. }
  827. func (c *configOptions) OAuth2ClientSecret() string {
  828. return c.options["OAUTH2_CLIENT_SECRET"].parsedStringValue
  829. }
  830. func (c *configOptions) OAuth2OIDCDiscoveryEndpoint() string {
  831. return c.options["OAUTH2_OIDC_DISCOVERY_ENDPOINT"].parsedStringValue
  832. }
  833. func (c *configOptions) OAuth2OIDCProviderName() string {
  834. return c.options["OAUTH2_OIDC_PROVIDER_NAME"].parsedStringValue
  835. }
  836. func (c *configOptions) OAuth2Provider() string {
  837. return c.options["OAUTH2_PROVIDER"].parsedStringValue
  838. }
  839. func (c *configOptions) OAuth2RedirectURL() string {
  840. return c.options["OAUTH2_REDIRECT_URL"].parsedStringValue
  841. }
  842. func (c *configOptions) OAuth2UserCreation() bool {
  843. return c.options["OAUTH2_USER_CREATION"].parsedBoolValue
  844. }
  845. func (c *configOptions) PollingFrequency() time.Duration {
  846. return c.options["POLLING_FREQUENCY"].parsedDuration
  847. }
  848. func (c *configOptions) PollingLimitPerHost() int {
  849. return c.options["POLLING_LIMIT_PER_HOST"].parsedIntValue
  850. }
  851. func (c *configOptions) PollingParsingErrorLimit() int {
  852. return c.options["POLLING_PARSING_ERROR_LIMIT"].parsedIntValue
  853. }
  854. func (c *configOptions) PollingScheduler() string {
  855. return c.options["POLLING_SCHEDULER"].parsedStringValue
  856. }
  857. func (c *configOptions) Port() string {
  858. return c.options["PORT"].parsedStringValue
  859. }
  860. func (c *configOptions) RunMigrations() bool {
  861. return c.options["RUN_MIGRATIONS"].parsedBoolValue
  862. }
  863. func (c *configOptions) SetLogLevel(level string) {
  864. c.options["LOG_LEVEL"].parsedStringValue = level
  865. c.options["LOG_LEVEL"].rawValue = level
  866. }
  867. func (c *configOptions) SetHTTPSValue(value bool) {
  868. c.options["HTTPS"].parsedBoolValue = value
  869. if value {
  870. c.options["HTTPS"].rawValue = "1"
  871. } else {
  872. c.options["HTTPS"].rawValue = "0"
  873. }
  874. }
  875. func (c *configOptions) SchedulerEntryFrequencyFactor() int {
  876. return c.options["SCHEDULER_ENTRY_FREQUENCY_FACTOR"].parsedIntValue
  877. }
  878. func (c *configOptions) SchedulerEntryFrequencyMaxInterval() time.Duration {
  879. return c.options["SCHEDULER_ENTRY_FREQUENCY_MAX_INTERVAL"].parsedDuration
  880. }
  881. func (c *configOptions) SchedulerEntryFrequencyMinInterval() time.Duration {
  882. return c.options["SCHEDULER_ENTRY_FREQUENCY_MIN_INTERVAL"].parsedDuration
  883. }
  884. func (c *configOptions) SchedulerRoundRobinMaxInterval() time.Duration {
  885. return c.options["SCHEDULER_ROUND_ROBIN_MAX_INTERVAL"].parsedDuration
  886. }
  887. func (c *configOptions) SchedulerRoundRobinMinInterval() time.Duration {
  888. return c.options["SCHEDULER_ROUND_ROBIN_MIN_INTERVAL"].parsedDuration
  889. }
  890. func (c *configOptions) TrustedReverseProxyNetworks() []string {
  891. return c.options["TRUSTED_REVERSE_PROXY_NETWORKS"].parsedStringList
  892. }
  893. func (c *configOptions) Watchdog() bool {
  894. return c.options["WATCHDOG"].parsedBoolValue
  895. }
  896. func (c *configOptions) WebAuthn() bool {
  897. return c.options["WEBAUTHN"].parsedBoolValue
  898. }
  899. func (c *configOptions) WorkerPoolSize() int {
  900. return c.options["WORKER_POOL_SIZE"].parsedIntValue
  901. }
  902. func (c *configOptions) YouTubeAPIKey() string {
  903. return c.options["YOUTUBE_API_KEY"].parsedStringValue
  904. }
  905. func (c *configOptions) YouTubeEmbedUrlOverride() string {
  906. return c.options["YOUTUBE_EMBED_URL_OVERRIDE"].parsedStringValue
  907. }
  908. func (c *configOptions) YouTubeEmbedDomain() string {
  909. return c.youTubeEmbedDomain
  910. }
  911. func (c *configOptions) ConfigMap(redactSecret bool) []*optionPair {
  912. sortedKeys := slices.Sorted(maps.Keys(c.options))
  913. sortedOptions := make([]*optionPair, 0, len(sortedKeys))
  914. for _, key := range sortedKeys {
  915. value := c.options[key]
  916. displayValue := value.rawValue
  917. if displayValue != "" && redactSecret && value.secret {
  918. displayValue = "<redacted>"
  919. }
  920. sortedOptions = append(sortedOptions, &optionPair{Key: key, Value: displayValue})
  921. }
  922. return sortedOptions
  923. }
  924. func (c *configOptions) String() string {
  925. var builder strings.Builder
  926. for _, option := range c.ConfigMap(false) {
  927. builder.WriteString(option.Key)
  928. builder.WriteByte('=')
  929. builder.WriteString(option.Value)
  930. builder.WriteByte('\n')
  931. }
  932. return builder.String()
  933. }