pasty/cmd/pasty/main.go

152 lines
4.8 KiB
Go

package main
import (
"context"
"errors"
"github.com/lus/pasty/internal/cleanup"
"github.com/lus/pasty/internal/config"
"github.com/lus/pasty/internal/consolecommands"
"github.com/lus/pasty/internal/meta"
"github.com/lus/pasty/internal/reports"
"github.com/lus/pasty/internal/storage"
"github.com/lus/pasty/internal/storage/postgres"
"github.com/lus/pasty/internal/storage/sqlite"
"github.com/lus/pasty/internal/web"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"net/http"
"os"
"os/signal"
"strings"
)
func main() {
// Set up the logger
zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs
if !meta.IsProdEnvironment() {
log.Logger = log.Output(zerolog.ConsoleWriter{
Out: os.Stderr,
})
log.Warn().Msg("This distribution was compiled for development mode and is thus not meant to be run in production!")
}
// Load the configuration
config.Compatibility()
cfg, err := config.Load()
if err != nil {
log.Fatal().Err(err).Msg("Could not load the configuration.")
}
// Adjust the log level
if !meta.IsProdEnvironment() {
zerolog.SetGlobalLevel(zerolog.DebugLevel)
} else {
level, err := zerolog.ParseLevel(cfg.LogLevel)
if err != nil {
log.Warn().Msg("An invalid log level was configured. Falling back to 'info'.")
level = zerolog.InfoLevel
}
zerolog.SetGlobalLevel(level)
}
// Determine the correct storage driver to use
var driver storage.Driver
switch strings.TrimSpace(strings.ToLower(cfg.StorageDriver)) {
case "postgres":
driver = postgres.New(cfg.Postgres.DSN)
break
case "sqlite":
driver = sqlite.New(cfg.SQLite.File)
break
case "file":
// TODO: Readme notice
log.Fatal().Msg("You have configured the legacy 'file' storage driver. This storage driver has been removed in favor of PostgreSQL and SQLite, but the latter one may be a seamless alternative for you. Head over to the projects README for more information.")
break
case "mongodb":
case "s3":
// TODO: Readme notice
log.Fatal().Msg("You have configured a legacy storage driver. This storage driver has been removed in favor of PostgreSQL and SQLite, but the migration process is well-documented. Head over to the projects README for more information.")
break
default:
log.Fatal().Str("driver_name", cfg.StorageDriver).Msg("An invalid storage driver name was given.")
return
}
// Initialize the configured storage driver
log.Info().Str("driver_name", cfg.StorageDriver).Msg("Initializing the storage driver...")
if err := driver.Initialize(context.Background()); err != nil {
log.Fatal().Err(err).Str("driver_name", cfg.StorageDriver).Msg("The storage driver could not be initialized.")
return
}
defer func() {
log.Info().Msg("Shutting down the storage driver...")
if err := driver.Close(); err != nil {
log.Err(err).Str("driver_name", cfg.StorageDriver).Msg("Could not shut down the storage driver.")
}
}()
// Schedule the cleanup task if configured
if cfg.Cleanup.Enabled {
task := &cleanup.Task{
Interval: cfg.Cleanup.TaskInterval,
MaxPasteAge: cfg.Cleanup.PasteLifetime,
Repository: driver.Pastes(),
}
log.Info().Msg("Scheduling the cleanup task...")
task.Start()
defer func() {
log.Info().Msg("Shutting down the cleanup task...")
task.Stop()
}()
}
// Start the web server
log.Info().Str("address", cfg.Address).Msg("Starting the web server...")
webServer := &web.Server{
Address: cfg.Address,
Storage: driver,
PasteIDLength: cfg.PasteIDLength,
PasteIDCharset: cfg.PasteIDCharset,
PasteLengthCap: cfg.PasteLengthCap,
ModificationTokensEnabled: cfg.ModificationTokensEnabled,
ModificationTokenLength: cfg.ModificationTokenLength,
ModificationTokenCharset: cfg.ModificationTokenCharset,
}
if cfg.Reports.Enabled {
webServer.ReportClient = &reports.Client{
WebhookURL: cfg.Reports.WebhookURL,
WebhookToken: cfg.Reports.WebhookToken,
}
}
if cfg.ModificationTokenMaster != "" {
webServer.AdminTokens = []string{cfg.ModificationTokenMaster}
}
go func() {
if err := webServer.Start(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatal().Err(err).Msg("Could not start the web server.")
}
}()
defer func() {
log.Info().Msg("Shutting down the web server...")
if err := webServer.Shutdown(context.Background()); err != nil {
log.Err(err).Msg("Could not shut down the web server.")
}
}()
// Listen to console commands if enabled
if !cfg.ConsoleCommandsEnabled {
log.Info().Msg("The application has been started. Use Ctrl+C to shut it down.")
} else {
log.Info().Msg("The application has been started and listens to console commands. Use Ctrl+C or 'stop' to shut it down.")
go (&consolecommands.Router{
Config: cfg,
Storage: driver,
}).Listen()
}
// Wait for an interrupt signal
shutdownChan := make(chan os.Signal, 1)
signal.Notify(shutdownChan, os.Interrupt)
<-shutdownChan
}