2023-06-19 23:42:47 +02:00
|
|
|
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
2018-01-03 07:04:48 +01:00
|
|
|
|
2023-08-11 04:46:45 +02:00
|
|
|
package cli // import "miniflux.app/v2/internal/cli"
|
2018-01-03 07:04:48 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
2023-09-25 01:32:09 +02:00
|
|
|
"io"
|
|
|
|
"log/slog"
|
|
|
|
"os"
|
2018-01-03 07:04:48 +01:00
|
|
|
|
2023-08-11 04:46:45 +02:00
|
|
|
"miniflux.app/v2/internal/config"
|
|
|
|
"miniflux.app/v2/internal/database"
|
|
|
|
"miniflux.app/v2/internal/locale"
|
|
|
|
"miniflux.app/v2/internal/storage"
|
|
|
|
"miniflux.app/v2/internal/ui/static"
|
|
|
|
"miniflux.app/v2/internal/version"
|
2018-01-03 07:04:48 +01:00
|
|
|
)
|
|
|
|
|
2018-10-09 06:00:00 +02:00
|
|
|
const (
|
2023-06-25 20:23:23 +02:00
|
|
|
flagInfoHelp = "Show build information"
|
2018-11-12 00:32:48 +01:00
|
|
|
flagVersionHelp = "Show application version"
|
|
|
|
flagMigrateHelp = "Run SQL migrations"
|
2019-09-26 03:55:16 +02:00
|
|
|
flagFlushSessionsHelp = "Flush all sessions (disconnect users)"
|
2024-03-23 22:05:05 +01:00
|
|
|
flagCreateAdminHelp = "Create an admin user from an interactive terminal"
|
2018-11-12 00:32:48 +01:00
|
|
|
flagResetPasswordHelp = "Reset user password"
|
2018-10-09 06:00:00 +02:00
|
|
|
flagResetFeedErrorsHelp = "Clear all feed errors for all users"
|
2018-11-12 00:32:48 +01:00
|
|
|
flagDebugModeHelp = "Show debug logs"
|
2019-06-03 03:20:59 +02:00
|
|
|
flagConfigFileHelp = "Load configuration file"
|
|
|
|
flagConfigDumpHelp = "Print parsed configuration values"
|
2021-02-20 21:42:15 +01:00
|
|
|
flagHealthCheckHelp = `Perform a health check on the given endpoint (the value "auto" try to guess the health check endpoint).`
|
2023-06-25 20:23:23 +02:00
|
|
|
flagRefreshFeedsHelp = "Refresh a batch of feeds and exit"
|
|
|
|
flagRunCleanupTasksHelp = "Run cleanup tasks (delete old sessions and archives old entries)"
|
2023-09-28 06:15:32 +02:00
|
|
|
flagExportUserFeedsHelp = "Export user feeds (provide the username as argument)"
|
2018-10-09 06:00:00 +02:00
|
|
|
)
|
|
|
|
|
2018-01-03 07:04:48 +01:00
|
|
|
// Parse parses command line arguments.
|
|
|
|
func Parse() {
|
2018-10-09 06:00:00 +02:00
|
|
|
var (
|
2019-06-03 03:20:59 +02:00
|
|
|
err error
|
2018-11-12 00:32:48 +01:00
|
|
|
flagInfo bool
|
|
|
|
flagVersion bool
|
|
|
|
flagMigrate bool
|
|
|
|
flagFlushSessions bool
|
|
|
|
flagCreateAdmin bool
|
|
|
|
flagResetPassword bool
|
2018-10-09 06:00:00 +02:00
|
|
|
flagResetFeedErrors bool
|
2018-11-12 00:32:48 +01:00
|
|
|
flagDebugMode bool
|
2019-06-03 03:20:59 +02:00
|
|
|
flagConfigFile string
|
|
|
|
flagConfigDump bool
|
2021-02-20 21:42:15 +01:00
|
|
|
flagHealthCheck string
|
2023-06-25 20:23:23 +02:00
|
|
|
flagRefreshFeeds bool
|
|
|
|
flagRunCleanupTasks bool
|
2023-09-28 06:15:32 +02:00
|
|
|
flagExportUserFeeds string
|
2018-10-09 06:00:00 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
flag.BoolVar(&flagInfo, "info", false, flagInfoHelp)
|
|
|
|
flag.BoolVar(&flagInfo, "i", false, flagInfoHelp)
|
|
|
|
flag.BoolVar(&flagVersion, "version", false, flagVersionHelp)
|
|
|
|
flag.BoolVar(&flagVersion, "v", false, flagVersionHelp)
|
|
|
|
flag.BoolVar(&flagMigrate, "migrate", false, flagMigrateHelp)
|
2019-09-26 03:55:16 +02:00
|
|
|
flag.BoolVar(&flagFlushSessions, "flush-sessions", false, flagFlushSessionsHelp)
|
2018-10-09 06:00:00 +02:00
|
|
|
flag.BoolVar(&flagCreateAdmin, "create-admin", false, flagCreateAdminHelp)
|
|
|
|
flag.BoolVar(&flagResetPassword, "reset-password", false, flagResetPasswordHelp)
|
|
|
|
flag.BoolVar(&flagResetFeedErrors, "reset-feed-errors", false, flagResetFeedErrorsHelp)
|
2018-11-12 00:32:48 +01:00
|
|
|
flag.BoolVar(&flagDebugMode, "debug", false, flagDebugModeHelp)
|
2019-06-03 03:20:59 +02:00
|
|
|
flag.StringVar(&flagConfigFile, "config-file", "", flagConfigFileHelp)
|
|
|
|
flag.StringVar(&flagConfigFile, "c", "", flagConfigFileHelp)
|
|
|
|
flag.BoolVar(&flagConfigDump, "config-dump", false, flagConfigDumpHelp)
|
2021-02-20 21:42:15 +01:00
|
|
|
flag.StringVar(&flagHealthCheck, "healthcheck", "", flagHealthCheckHelp)
|
2023-06-25 20:23:23 +02:00
|
|
|
flag.BoolVar(&flagRefreshFeeds, "refresh-feeds", false, flagRefreshFeedsHelp)
|
|
|
|
flag.BoolVar(&flagRunCleanupTasks, "run-cleanup-tasks", false, flagRunCleanupTasksHelp)
|
2023-09-28 06:15:32 +02:00
|
|
|
flag.StringVar(&flagExportUserFeeds, "export-user-feeds", "", flagExportUserFeedsHelp)
|
2018-01-03 07:04:48 +01:00
|
|
|
flag.Parse()
|
|
|
|
|
2019-06-03 03:20:59 +02:00
|
|
|
cfg := config.NewParser()
|
|
|
|
|
|
|
|
if flagConfigFile != "" {
|
|
|
|
config.Opts, err = cfg.ParseFile(flagConfigFile)
|
|
|
|
if err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(err)
|
2019-06-03 03:20:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
config.Opts, err = cfg.ParseEnvironmentVariables()
|
|
|
|
if err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(err)
|
2019-06-02 03:18:09 +02:00
|
|
|
}
|
2018-02-24 03:26:34 +01:00
|
|
|
|
2019-06-03 03:20:59 +02:00
|
|
|
if flagConfigDump {
|
|
|
|
fmt.Print(config.Opts)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-25 01:32:09 +02:00
|
|
|
if flagDebugMode {
|
|
|
|
config.Opts.SetLogLevel("debug")
|
2019-06-09 02:16:12 +02:00
|
|
|
}
|
|
|
|
|
2023-09-25 01:32:09 +02:00
|
|
|
logFile := config.Opts.LogFile()
|
|
|
|
var logFileHandler io.Writer
|
|
|
|
switch logFile {
|
|
|
|
case "stdout":
|
|
|
|
logFileHandler = os.Stdout
|
|
|
|
case "stderr":
|
|
|
|
logFileHandler = os.Stderr
|
|
|
|
default:
|
|
|
|
logFileHandler, err = os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
|
|
|
if err != nil {
|
|
|
|
printErrorAndExit(fmt.Errorf("unable to open log file: %v", err))
|
|
|
|
}
|
|
|
|
defer logFileHandler.(*os.File).Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := InitializeDefaultLogger(config.Opts.LogLevel(), logFileHandler, config.Opts.LogFormat(), config.Opts.LogDateTime()); err != nil {
|
|
|
|
printErrorAndExit(err)
|
2018-02-24 03:26:34 +01:00
|
|
|
}
|
|
|
|
|
2021-02-20 21:42:15 +01:00
|
|
|
if flagHealthCheck != "" {
|
|
|
|
doHealthCheck(flagHealthCheck)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-10-09 06:00:00 +02:00
|
|
|
if flagInfo {
|
2018-01-03 07:04:48 +01:00
|
|
|
info()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-10-09 06:00:00 +02:00
|
|
|
if flagVersion {
|
2018-01-03 07:04:48 +01:00
|
|
|
fmt.Println(version.Version)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-06-02 03:18:09 +02:00
|
|
|
if config.Opts.IsDefaultDatabaseURL() {
|
2023-09-25 01:32:09 +02:00
|
|
|
slog.Info("The default value for DATABASE_URL is used")
|
2019-06-02 03:18:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-17 07:58:44 +01:00
|
|
|
if err := locale.LoadCatalogMessages(); err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(fmt.Errorf("unable to load translations: %v", err))
|
2021-02-17 07:58:44 +01:00
|
|
|
}
|
|
|
|
|
2021-02-18 06:58:04 +01:00
|
|
|
if err := static.CalculateBinaryFileChecksums(); err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(fmt.Errorf("unable to calculate binary file checksums: %v", err))
|
2021-02-18 06:58:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := static.GenerateStylesheetsBundles(); err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(fmt.Errorf("unable to generate stylesheets bundles: %v", err))
|
2021-02-19 05:34:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := static.GenerateJavascriptBundles(); err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(fmt.Errorf("unable to generate javascript bundles: %v", err))
|
2021-02-18 06:58:04 +01:00
|
|
|
}
|
|
|
|
|
2019-06-02 03:18:09 +02:00
|
|
|
db, err := database.NewConnectionPool(
|
|
|
|
config.Opts.DatabaseURL(),
|
|
|
|
config.Opts.DatabaseMinConns(),
|
|
|
|
config.Opts.DatabaseMaxConns(),
|
2021-05-24 04:32:34 +02:00
|
|
|
config.Opts.DatabaseConnectionLifetime(),
|
2019-06-02 03:18:09 +02:00
|
|
|
)
|
2018-12-03 06:19:09 +01:00
|
|
|
if err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(fmt.Errorf("unable to connect to database: %v", err))
|
2018-12-03 06:19:09 +01:00
|
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
|
2021-02-20 03:47:50 +01:00
|
|
|
store := storage.NewStorage(db)
|
|
|
|
|
|
|
|
if err := store.Ping(); err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(err)
|
2021-02-20 03:47:50 +01:00
|
|
|
}
|
|
|
|
|
2018-10-09 06:00:00 +02:00
|
|
|
if flagMigrate {
|
2021-02-21 22:42:49 +01:00
|
|
|
if err := database.Migrate(db); err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(err)
|
2021-02-21 22:42:49 +01:00
|
|
|
}
|
2018-01-03 07:04:48 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-10-09 06:00:00 +02:00
|
|
|
if flagResetFeedErrors {
|
2018-06-30 23:22:45 +02:00
|
|
|
store.ResetFeedErrors()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-28 06:15:32 +02:00
|
|
|
if flagExportUserFeeds != "" {
|
|
|
|
exportUserFeeds(store, flagExportUserFeeds)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-10-09 06:00:00 +02:00
|
|
|
if flagFlushSessions {
|
2018-01-03 07:04:48 +01:00
|
|
|
flushSessions(store)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-10-09 06:00:00 +02:00
|
|
|
if flagCreateAdmin {
|
2024-03-23 22:05:05 +01:00
|
|
|
createAdminUserFromInteractiveTerminal(store)
|
2018-01-03 07:04:48 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-10-09 06:00:00 +02:00
|
|
|
if flagResetPassword {
|
2018-01-03 07:18:24 +01:00
|
|
|
resetPassword(store)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-05-13 15:38:32 +02:00
|
|
|
// Run migrations and start the daemon.
|
2019-06-02 03:18:09 +02:00
|
|
|
if config.Opts.RunMigrations() {
|
2021-01-03 01:33:41 +01:00
|
|
|
if err := database.Migrate(db); err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(err)
|
2021-01-03 01:33:41 +01:00
|
|
|
}
|
2018-04-16 05:27:10 +02:00
|
|
|
}
|
|
|
|
|
2020-03-21 23:01:16 +01:00
|
|
|
if err := database.IsSchemaUpToDate(db); err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
printErrorAndExit(err)
|
2020-03-21 23:01:16 +01:00
|
|
|
}
|
|
|
|
|
2019-06-02 03:18:09 +02:00
|
|
|
if config.Opts.CreateAdmin() {
|
2024-03-23 22:05:05 +01:00
|
|
|
createAdminUserFromEnvironmentVariables(store)
|
2018-04-16 05:27:10 +02:00
|
|
|
}
|
|
|
|
|
2023-06-25 20:23:23 +02:00
|
|
|
if flagRefreshFeeds {
|
|
|
|
refreshFeeds(store)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if flagRunCleanupTasks {
|
|
|
|
runCleanupTasks(store)
|
2023-06-25 07:06:48 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-06-02 03:18:09 +02:00
|
|
|
startDaemon(store)
|
2018-01-03 07:04:48 +01:00
|
|
|
}
|
2023-09-25 01:32:09 +02:00
|
|
|
|
|
|
|
func printErrorAndExit(err error) {
|
|
|
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|