// TODO: Add EUPL banner everywhere package main import ( "embed" "flag" "fmt" "net/http" "os" "os/signal" "path" "sync" "git.mstar.dev/mstar/goutils/other" "github.com/rs/zerolog/log" "gopkg.in/natefinch/lumberjack.v2" "gorm.io/driver/postgres" "gorm.io/gorm" "git.mstar.dev/mstar/linstrom/config" "git.mstar.dev/mstar/linstrom/shared" storagenew "git.mstar.dev/mstar/linstrom/storage-new" "git.mstar.dev/mstar/linstrom/storage-new/dbgen" webdebug "git.mstar.dev/mstar/linstrom/web/debug" webpublic "git.mstar.dev/mstar/linstrom/web/public" ) // TODO: Add frontend overwrite // Serve frontend files from a given directory instead of embedded data //go:embed frontend-reactive/dist/* frontend-reactive/dist/assets var reactiveFS embed.FS //go:embed frontend-noscript var nojsFS embed.FS //go:embed duck.webp var defaultDuck string //go:embed duck.webp var duckFS embed.FS func main() { _ = reactiveFS _ = nojsFS other.SetupFlags() flag.Parse() logfile := getLogFilePathOrNil() if logfile == nil { other.ConfigureLogging(nil) log.Warn().Msg("Can't write to target logfile, not creating one") } else { other.ConfigureLogging(&lumberjack.Logger{ Filename: *logfile, MaxSize: 500, // Megabytes MaxBackups: 3, MaxAge: 3, // Days Compress: false, }) log.Info().Str("logfile", *logfile).Msg("Logging to stderr and logfile") } if err := config.ReadAndWriteToGlobal(*shared.FlagConfigFile); err != nil { log.Fatal(). Err(err). Str("config-file", *shared.FlagConfigFile). Msg("Failed to read config and couldn't write default") } log.Trace().Any("config", config.GlobalConfig).Msg("Full config") // Request to only check config if *shared.FlagConfigOnly { return } newServer() } func getLogFilePathOrNil() *string { logfile := *shared.FlagLogFile logPath := path.Dir(logfile) if shared.IsWritable(logPath) { return &logfile } else { return nil } } func newServer() { log.Info().Msg("Connecting to db") db, err := gorm.Open( postgres.Open(config.GlobalConfig.Storage.BuildPostgresDSN()), &gorm.Config{ Logger: shared.NewGormLogger(log.Logger), }, ) if err != nil { log.Fatal().Err(err).Msg("Failed to start db") } dbgen.SetDefault(db) log.Info().Msg("Applying migrations") if err = storagenew.Migrate(db); err != nil { log.Fatal().Err(err).Msg("Failed to automigrate structure") } log.Info().Msg("Inserting self into db") if err = storagenew.InsertSelf(); err != nil { log.Fatal().Err(err).Msg("Failed to insert self properly") } log.Info().Msg("Inserting placeholder/unknown user") if err = storagenew.InsertUnknownActorPlaceholder(); err != nil { log.Fatal().Err(err).Msg("Failed to insert self properly") } debugShutdownChan := make(chan *sync.WaitGroup, 1) interuptChan := make(chan os.Signal, 1) signal.Notify(interuptChan, os.Interrupt) if *shared.FlagStartDebugServer { go func() { log.Info().Msg("Starting debug server") s := webdebug.New(*shared.FlagDebugPort) go func() { wg := <-debugShutdownChan if err := s.Stop(); err != nil { log.Fatal().Err(err).Msg("Failed to cleanly stop debug server") } log.Info().Msg("Debug server stopped") wg.Done() }() if err = s.Start(); err != nil && err != http.ErrServerClosed { log.Fatal().Err(err).Msg("Debug server failed") } }() } public := webpublic.New( fmt.Sprintf(":%v", config.GlobalConfig.General.PrivatePort), &defaultDuck, duckFS, ) go func() { log.Info().Msg("Starting public server") if err = public.Start(); err != nil && err != http.ErrServerClosed { log.Fatal().Err(err).Msg("Failed to start public server") } }() <-interuptChan log.Info().Msg("Received interrupt, shutting down") wg := sync.WaitGroup{} wg.Add(1) debugShutdownChan <- &wg if err = public.Stop(); err != nil { log.Fatal().Err(err).Msg("Failed to stop public server") } log.Info().Msg("Public server stopped") wg.Wait() }