cli.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package cli // import "miniflux.app/v2/internal/cli"
  4. import (
  5. "flag"
  6. "fmt"
  7. "io"
  8. "log/slog"
  9. "os"
  10. "miniflux.app/v2/internal/config"
  11. "miniflux.app/v2/internal/database"
  12. "miniflux.app/v2/internal/locale"
  13. "miniflux.app/v2/internal/storage"
  14. "miniflux.app/v2/internal/ui/static"
  15. "miniflux.app/v2/internal/version"
  16. )
  17. const (
  18. flagInfoHelp = "Show build information"
  19. flagVersionHelp = "Show application version"
  20. flagMigrateHelp = "Run SQL migrations"
  21. flagFlushSessionsHelp = "Flush all sessions (disconnect users)"
  22. flagCreateAdminHelp = "Create admin user"
  23. flagResetPasswordHelp = "Reset user password"
  24. flagResetFeedErrorsHelp = "Clear all feed errors for all users"
  25. flagDebugModeHelp = "Show debug logs"
  26. flagConfigFileHelp = "Load configuration file"
  27. flagConfigDumpHelp = "Print parsed configuration values"
  28. flagHealthCheckHelp = `Perform a health check on the given endpoint (the value "auto" try to guess the health check endpoint).`
  29. flagRefreshFeedsHelp = "Refresh a batch of feeds and exit"
  30. flagRunCleanupTasksHelp = "Run cleanup tasks (delete old sessions and archives old entries)"
  31. flagExportUserFeedsHelp = "Export user feeds (provide the username as argument)"
  32. )
  33. // Parse parses command line arguments.
  34. func Parse() {
  35. var (
  36. err error
  37. flagInfo bool
  38. flagVersion bool
  39. flagMigrate bool
  40. flagFlushSessions bool
  41. flagCreateAdmin bool
  42. flagResetPassword bool
  43. flagResetFeedErrors bool
  44. flagDebugMode bool
  45. flagConfigFile string
  46. flagConfigDump bool
  47. flagHealthCheck string
  48. flagRefreshFeeds bool
  49. flagRunCleanupTasks bool
  50. flagExportUserFeeds string
  51. )
  52. flag.BoolVar(&flagInfo, "info", false, flagInfoHelp)
  53. flag.BoolVar(&flagInfo, "i", false, flagInfoHelp)
  54. flag.BoolVar(&flagVersion, "version", false, flagVersionHelp)
  55. flag.BoolVar(&flagVersion, "v", false, flagVersionHelp)
  56. flag.BoolVar(&flagMigrate, "migrate", false, flagMigrateHelp)
  57. flag.BoolVar(&flagFlushSessions, "flush-sessions", false, flagFlushSessionsHelp)
  58. flag.BoolVar(&flagCreateAdmin, "create-admin", false, flagCreateAdminHelp)
  59. flag.BoolVar(&flagResetPassword, "reset-password", false, flagResetPasswordHelp)
  60. flag.BoolVar(&flagResetFeedErrors, "reset-feed-errors", false, flagResetFeedErrorsHelp)
  61. flag.BoolVar(&flagDebugMode, "debug", false, flagDebugModeHelp)
  62. flag.StringVar(&flagConfigFile, "config-file", "", flagConfigFileHelp)
  63. flag.StringVar(&flagConfigFile, "c", "", flagConfigFileHelp)
  64. flag.BoolVar(&flagConfigDump, "config-dump", false, flagConfigDumpHelp)
  65. flag.StringVar(&flagHealthCheck, "healthcheck", "", flagHealthCheckHelp)
  66. flag.BoolVar(&flagRefreshFeeds, "refresh-feeds", false, flagRefreshFeedsHelp)
  67. flag.BoolVar(&flagRunCleanupTasks, "run-cleanup-tasks", false, flagRunCleanupTasksHelp)
  68. flag.StringVar(&flagExportUserFeeds, "export-user-feeds", "", flagExportUserFeedsHelp)
  69. flag.Parse()
  70. cfg := config.NewParser()
  71. if flagConfigFile != "" {
  72. config.Opts, err = cfg.ParseFile(flagConfigFile)
  73. if err != nil {
  74. printErrorAndExit(err)
  75. }
  76. }
  77. config.Opts, err = cfg.ParseEnvironmentVariables()
  78. if err != nil {
  79. printErrorAndExit(err)
  80. }
  81. if flagConfigDump {
  82. fmt.Print(config.Opts)
  83. return
  84. }
  85. if flagDebugMode {
  86. config.Opts.SetLogLevel("debug")
  87. }
  88. logFile := config.Opts.LogFile()
  89. var logFileHandler io.Writer
  90. switch logFile {
  91. case "stdout":
  92. logFileHandler = os.Stdout
  93. case "stderr":
  94. logFileHandler = os.Stderr
  95. default:
  96. logFileHandler, err = os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
  97. if err != nil {
  98. printErrorAndExit(fmt.Errorf("unable to open log file: %v", err))
  99. }
  100. defer logFileHandler.(*os.File).Close()
  101. }
  102. if err := InitializeDefaultLogger(config.Opts.LogLevel(), logFileHandler, config.Opts.LogFormat(), config.Opts.LogDateTime()); err != nil {
  103. printErrorAndExit(err)
  104. }
  105. if flagHealthCheck != "" {
  106. doHealthCheck(flagHealthCheck)
  107. return
  108. }
  109. if flagInfo {
  110. info()
  111. return
  112. }
  113. if flagVersion {
  114. fmt.Println(version.Version)
  115. return
  116. }
  117. if config.Opts.IsDefaultDatabaseURL() {
  118. slog.Info("The default value for DATABASE_URL is used")
  119. }
  120. if err := locale.LoadCatalogMessages(); err != nil {
  121. printErrorAndExit(fmt.Errorf("unable to load translations: %v", err))
  122. }
  123. if err := static.CalculateBinaryFileChecksums(); err != nil {
  124. printErrorAndExit(fmt.Errorf("unable to calculate binary file checksums: %v", err))
  125. }
  126. if err := static.GenerateStylesheetsBundles(); err != nil {
  127. printErrorAndExit(fmt.Errorf("unable to generate stylesheets bundles: %v", err))
  128. }
  129. if err := static.GenerateJavascriptBundles(); err != nil {
  130. printErrorAndExit(fmt.Errorf("unable to generate javascript bundles: %v", err))
  131. }
  132. db, err := database.NewConnectionPool(
  133. config.Opts.DatabaseURL(),
  134. config.Opts.DatabaseMinConns(),
  135. config.Opts.DatabaseMaxConns(),
  136. config.Opts.DatabaseConnectionLifetime(),
  137. )
  138. if err != nil {
  139. printErrorAndExit(fmt.Errorf("unable to connect to database: %v", err))
  140. }
  141. defer db.Close()
  142. store := storage.NewStorage(db)
  143. if err := store.Ping(); err != nil {
  144. printErrorAndExit(err)
  145. }
  146. if flagMigrate {
  147. if err := database.Migrate(db); err != nil {
  148. printErrorAndExit(err)
  149. }
  150. return
  151. }
  152. if flagResetFeedErrors {
  153. store.ResetFeedErrors()
  154. return
  155. }
  156. if flagExportUserFeeds != "" {
  157. exportUserFeeds(store, flagExportUserFeeds)
  158. return
  159. }
  160. if flagFlushSessions {
  161. flushSessions(store)
  162. return
  163. }
  164. if flagCreateAdmin {
  165. createAdmin(store)
  166. return
  167. }
  168. if flagResetPassword {
  169. resetPassword(store)
  170. return
  171. }
  172. // Run migrations and start the daemon.
  173. if config.Opts.RunMigrations() {
  174. if err := database.Migrate(db); err != nil {
  175. printErrorAndExit(err)
  176. }
  177. }
  178. if err := database.IsSchemaUpToDate(db); err != nil {
  179. printErrorAndExit(err)
  180. }
  181. // Create admin user and start the daemon.
  182. if config.Opts.CreateAdmin() {
  183. createAdmin(store)
  184. }
  185. if flagRefreshFeeds {
  186. refreshFeeds(store)
  187. return
  188. }
  189. if flagRunCleanupTasks {
  190. runCleanupTasks(store)
  191. return
  192. }
  193. startDaemon(store)
  194. }
  195. func printErrorAndExit(err error) {
  196. fmt.Fprintf(os.Stderr, "%v\n", err)
  197. os.Exit(1)
  198. }