cli.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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. )
  32. // Parse parses command line arguments.
  33. func Parse() {
  34. var (
  35. err error
  36. flagInfo bool
  37. flagVersion bool
  38. flagMigrate bool
  39. flagFlushSessions bool
  40. flagCreateAdmin bool
  41. flagResetPassword bool
  42. flagResetFeedErrors bool
  43. flagDebugMode bool
  44. flagConfigFile string
  45. flagConfigDump bool
  46. flagHealthCheck string
  47. flagRefreshFeeds bool
  48. flagRunCleanupTasks bool
  49. )
  50. flag.BoolVar(&flagInfo, "info", false, flagInfoHelp)
  51. flag.BoolVar(&flagInfo, "i", false, flagInfoHelp)
  52. flag.BoolVar(&flagVersion, "version", false, flagVersionHelp)
  53. flag.BoolVar(&flagVersion, "v", false, flagVersionHelp)
  54. flag.BoolVar(&flagMigrate, "migrate", false, flagMigrateHelp)
  55. flag.BoolVar(&flagFlushSessions, "flush-sessions", false, flagFlushSessionsHelp)
  56. flag.BoolVar(&flagCreateAdmin, "create-admin", false, flagCreateAdminHelp)
  57. flag.BoolVar(&flagResetPassword, "reset-password", false, flagResetPasswordHelp)
  58. flag.BoolVar(&flagResetFeedErrors, "reset-feed-errors", false, flagResetFeedErrorsHelp)
  59. flag.BoolVar(&flagDebugMode, "debug", false, flagDebugModeHelp)
  60. flag.StringVar(&flagConfigFile, "config-file", "", flagConfigFileHelp)
  61. flag.StringVar(&flagConfigFile, "c", "", flagConfigFileHelp)
  62. flag.BoolVar(&flagConfigDump, "config-dump", false, flagConfigDumpHelp)
  63. flag.StringVar(&flagHealthCheck, "healthcheck", "", flagHealthCheckHelp)
  64. flag.BoolVar(&flagRefreshFeeds, "refresh-feeds", false, flagRefreshFeedsHelp)
  65. flag.BoolVar(&flagRunCleanupTasks, "run-cleanup-tasks", false, flagRunCleanupTasksHelp)
  66. flag.Parse()
  67. cfg := config.NewParser()
  68. if flagConfigFile != "" {
  69. config.Opts, err = cfg.ParseFile(flagConfigFile)
  70. if err != nil {
  71. printErrorAndExit(err)
  72. }
  73. }
  74. config.Opts, err = cfg.ParseEnvironmentVariables()
  75. if err != nil {
  76. printErrorAndExit(err)
  77. }
  78. if flagConfigDump {
  79. fmt.Print(config.Opts)
  80. return
  81. }
  82. if flagDebugMode {
  83. config.Opts.SetLogLevel("debug")
  84. }
  85. logFile := config.Opts.LogFile()
  86. var logFileHandler io.Writer
  87. switch logFile {
  88. case "stdout":
  89. logFileHandler = os.Stdout
  90. case "stderr":
  91. logFileHandler = os.Stderr
  92. default:
  93. logFileHandler, err = os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
  94. if err != nil {
  95. printErrorAndExit(fmt.Errorf("unable to open log file: %v", err))
  96. }
  97. defer logFileHandler.(*os.File).Close()
  98. }
  99. if err := InitializeDefaultLogger(config.Opts.LogLevel(), logFileHandler, config.Opts.LogFormat(), config.Opts.LogDateTime()); err != nil {
  100. printErrorAndExit(err)
  101. }
  102. if flagHealthCheck != "" {
  103. doHealthCheck(flagHealthCheck)
  104. return
  105. }
  106. if flagInfo {
  107. info()
  108. return
  109. }
  110. if flagVersion {
  111. fmt.Println(version.Version)
  112. return
  113. }
  114. if config.Opts.IsDefaultDatabaseURL() {
  115. slog.Info("The default value for DATABASE_URL is used")
  116. }
  117. if err := locale.LoadCatalogMessages(); err != nil {
  118. printErrorAndExit(fmt.Errorf("unable to load translations: %v", err))
  119. }
  120. if err := static.CalculateBinaryFileChecksums(); err != nil {
  121. printErrorAndExit(fmt.Errorf("unable to calculate binary file checksums: %v", err))
  122. }
  123. if err := static.GenerateStylesheetsBundles(); err != nil {
  124. printErrorAndExit(fmt.Errorf("unable to generate stylesheets bundles: %v", err))
  125. }
  126. if err := static.GenerateJavascriptBundles(); err != nil {
  127. printErrorAndExit(fmt.Errorf("unable to generate javascript bundles: %v", err))
  128. }
  129. db, err := database.NewConnectionPool(
  130. config.Opts.DatabaseURL(),
  131. config.Opts.DatabaseMinConns(),
  132. config.Opts.DatabaseMaxConns(),
  133. config.Opts.DatabaseConnectionLifetime(),
  134. )
  135. if err != nil {
  136. printErrorAndExit(fmt.Errorf("unable to connect to database: %v", err))
  137. }
  138. defer db.Close()
  139. store := storage.NewStorage(db)
  140. if err := store.Ping(); err != nil {
  141. printErrorAndExit(err)
  142. }
  143. if flagMigrate {
  144. if err := database.Migrate(db); err != nil {
  145. printErrorAndExit(err)
  146. }
  147. return
  148. }
  149. if flagResetFeedErrors {
  150. store.ResetFeedErrors()
  151. return
  152. }
  153. if flagFlushSessions {
  154. flushSessions(store)
  155. return
  156. }
  157. if flagCreateAdmin {
  158. createAdmin(store)
  159. return
  160. }
  161. if flagResetPassword {
  162. resetPassword(store)
  163. return
  164. }
  165. // Run migrations and start the daemon.
  166. if config.Opts.RunMigrations() {
  167. if err := database.Migrate(db); err != nil {
  168. printErrorAndExit(err)
  169. }
  170. }
  171. if err := database.IsSchemaUpToDate(db); err != nil {
  172. printErrorAndExit(err)
  173. }
  174. // Create admin user and start the daemon.
  175. if config.Opts.CreateAdmin() {
  176. createAdmin(store)
  177. }
  178. if flagRefreshFeeds {
  179. refreshFeeds(store)
  180. return
  181. }
  182. if flagRunCleanupTasks {
  183. runCleanupTasks(store)
  184. return
  185. }
  186. startDaemon(store)
  187. }
  188. func printErrorAndExit(err error) {
  189. fmt.Fprintf(os.Stderr, "%v\n", err)
  190. os.Exit(1)
  191. }