mirror of
https://github.com/go-shiori/shiori.git
synced 2025-09-07 13:35:22 +08:00
* migrate bookmark content route to new http server * new archive page * remove unused go generate comment * database mock * utils cleanup * unused var * domains refactor and tests * fixed secret key type * redirect to login on ui errors * fixed archive folder with storage domain * webroot documentation * some bookmark route tests * fixed error in bookmark domain for non existant bookmarks * centralice errors * add coverage data to unittests * added tests, refactor storage to use afero * removed mock to avoid increasing complexity * using deps to copy files around * remove config usage (to deps) * remove handler-ui file
175 lines
5.3 KiB
Go
175 lines
5.3 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
fp "path/filepath"
|
|
"time"
|
|
|
|
"github.com/go-shiori/shiori/internal/config"
|
|
"github.com/go-shiori/shiori/internal/database"
|
|
"github.com/go-shiori/shiori/internal/dependencies"
|
|
"github.com/go-shiori/shiori/internal/domains"
|
|
"github.com/go-shiori/shiori/internal/model"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/spf13/afero"
|
|
"github.com/spf13/cobra"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
// ShioriCmd returns the root command for shiori
|
|
func ShioriCmd() *cobra.Command {
|
|
rootCmd := &cobra.Command{
|
|
Use: "shiori",
|
|
Short: "Simple command-line bookmark manager built with Go",
|
|
}
|
|
|
|
rootCmd.PersistentFlags().Bool("portable", false, "run shiori in portable mode")
|
|
rootCmd.PersistentFlags().String("storage-directory", "", "path to store shiori data")
|
|
rootCmd.MarkFlagsMutuallyExclusive("portable", "storage-directory")
|
|
|
|
rootCmd.PersistentFlags().String("log-level", logrus.InfoLevel.String(), "set logrus loglevel")
|
|
rootCmd.PersistentFlags().Bool("log-caller", false, "logrus report caller or not")
|
|
|
|
rootCmd.AddCommand(
|
|
addCmd(),
|
|
printCmd(),
|
|
updateCmd(),
|
|
deleteCmd(),
|
|
openCmd(),
|
|
importCmd(),
|
|
exportCmd(),
|
|
pocketCmd(),
|
|
serveCmd(),
|
|
checkCmd(),
|
|
newVersionCommand(),
|
|
newServerCommand(),
|
|
)
|
|
|
|
return rootCmd
|
|
}
|
|
|
|
func initShiori(ctx context.Context, cmd *cobra.Command) (*config.Config, *dependencies.Dependencies) {
|
|
logger := logrus.New()
|
|
|
|
portableMode, _ := cmd.Flags().GetBool("portable")
|
|
logLevel, _ := cmd.Flags().GetString("log-level")
|
|
logCaller, _ := cmd.Flags().GetBool("log-caller")
|
|
storageDirectory, _ := cmd.Flags().GetString("storage-directory")
|
|
|
|
logger.SetReportCaller(logCaller)
|
|
logger.SetFormatter(&logrus.TextFormatter{
|
|
FullTimestamp: true,
|
|
TimestampFormat: time.RFC3339,
|
|
CallerPrettyfier: SFCallerPrettyfier,
|
|
})
|
|
|
|
if lvl, err := logrus.ParseLevel(logLevel); err != nil {
|
|
logger.WithError(err).Panic("failed to set log level")
|
|
} else {
|
|
logger.SetLevel(lvl)
|
|
}
|
|
|
|
cfg := config.ParseServerConfiguration(ctx, logger)
|
|
|
|
if storageDirectory != "" && cfg.Storage.DataDir != "" {
|
|
logger.Warn("--storage-directory is set, overriding SHIORI_DIR.")
|
|
cfg.Storage.DataDir = storageDirectory
|
|
}
|
|
|
|
cfg.SetDefaults(logger, portableMode)
|
|
|
|
err := os.MkdirAll(cfg.Storage.DataDir, model.DataDirPerm)
|
|
if err != nil {
|
|
logger.WithError(err).Fatal("error creating data directory")
|
|
}
|
|
|
|
db, err := openDatabase(logger, ctx, cfg)
|
|
if err != nil {
|
|
logger.WithError(err).Fatal("error opening database")
|
|
}
|
|
|
|
// Migrate
|
|
if err := db.Migrate(); err != nil {
|
|
logger.WithError(err).Fatalf("Error running migration")
|
|
}
|
|
|
|
if cfg.Development {
|
|
logger.Warn("Development mode is ENABLED, this will enable some helpers for local development, unsuitable for production environments")
|
|
}
|
|
|
|
dependencies := dependencies.NewDependencies(logger, db, cfg)
|
|
dependencies.Domains.Auth = domains.NewAccountsDomain(dependencies)
|
|
dependencies.Domains.Archiver = domains.NewArchiverDomain(dependencies)
|
|
dependencies.Domains.Bookmarks = domains.NewBookmarksDomain(dependencies)
|
|
dependencies.Domains.Storage = domains.NewStorageDomain(dependencies, afero.NewBasePathFs(afero.NewOsFs(), cfg.Storage.DataDir))
|
|
|
|
// Workaround: Get accounts to make sure at least one is present in the database.
|
|
// If there's no accounts in the database, create the shiori/gopher account the legacy api
|
|
// hardcoded in the login handler.
|
|
accounts, err := db.GetAccounts(cmd.Context(), database.GetAccountsOptions{})
|
|
if err != nil {
|
|
cError.Printf("Failed to get owner account: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if len(accounts) == 0 {
|
|
account := model.Account{
|
|
Username: "shiori",
|
|
Password: "gopher",
|
|
Owner: true,
|
|
}
|
|
|
|
if err := db.SaveAccount(cmd.Context(), account); err != nil {
|
|
logger.WithError(err).Fatal("error ensuring owner account")
|
|
}
|
|
}
|
|
|
|
return cfg, dependencies
|
|
}
|
|
|
|
func openDatabase(logger *logrus.Logger, ctx context.Context, cfg *config.Config) (database.DB, error) {
|
|
if cfg.Database.URL != "" {
|
|
return database.Connect(ctx, cfg.Database.URL)
|
|
}
|
|
|
|
if cfg.Database.DBMS != "" {
|
|
logger.Warnf("The use of SHIORI_DBMS is deprecated and will be removed in the future. Please migrate to SHIORI_DATABASE_URL instead.")
|
|
}
|
|
|
|
// TODO remove this the moment DBMS is deprecated
|
|
if cfg.Database.DBMS == "mysql" {
|
|
return openMySQLDatabase(ctx)
|
|
}
|
|
if cfg.Database.DBMS == "postgresql" {
|
|
return openPostgreSQLDatabase(ctx)
|
|
}
|
|
|
|
return database.OpenSQLiteDatabase(ctx, fp.Join(cfg.Storage.DataDir, "shiori.db"))
|
|
}
|
|
|
|
func openMySQLDatabase(ctx context.Context) (database.DB, error) {
|
|
user, _ := os.LookupEnv("SHIORI_MYSQL_USER")
|
|
password, _ := os.LookupEnv("SHIORI_MYSQL_PASS")
|
|
dbName, _ := os.LookupEnv("SHIORI_MYSQL_NAME")
|
|
dbAddress, _ := os.LookupEnv("SHIORI_MYSQL_ADDRESS")
|
|
|
|
connString := fmt.Sprintf("%s:%s@%s/%s?charset=utf8mb4", user, password, dbAddress, dbName)
|
|
return database.OpenMySQLDatabase(ctx, connString)
|
|
}
|
|
|
|
func openPostgreSQLDatabase(ctx context.Context) (database.DB, error) {
|
|
host, _ := os.LookupEnv("SHIORI_PG_HOST")
|
|
port, _ := os.LookupEnv("SHIORI_PG_PORT")
|
|
user, _ := os.LookupEnv("SHIORI_PG_USER")
|
|
password, _ := os.LookupEnv("SHIORI_PG_PASS")
|
|
dbName, _ := os.LookupEnv("SHIORI_PG_NAME")
|
|
sslmode, _ := os.LookupEnv("SHIORI_PG_SSLMODE")
|
|
if sslmode == "" {
|
|
sslmode = "disable"
|
|
}
|
|
|
|
connString := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s",
|
|
host, port, user, password, dbName, sslmode)
|
|
return database.OpenPGDatabase(ctx, connString)
|
|
}
|