package cmd import ( "embed" "net/url" "os" "runtime" tea "github.com/charmbracelet/bubbletea" "github.com/mrusme/neonmodem/config" "github.com/mrusme/neonmodem/system" "github.com/mrusme/neonmodem/ui" "github.com/mrusme/neonmodem/ui/ctx" "github.com/spf13/cobra" "github.com/spf13/viper" "go.uber.org/zap" ) var EMBEDFS *embed.FS var LOG *zap.SugaredLogger var CFG config.Config func init() { cobra.OnInitialize(load) rootCmd. PersistentFlags(). Bool( "debug", false, "Debug output", ) viper.BindPFlag( "debug", rootCmd.PersistentFlags().Lookup("debug"), ) } func loadLogger(filename string, debug bool) (*zap.Logger, error) { if runtime.GOOS == "windows" { zap.RegisterSink("winfile", func(u *url.URL) (zap.Sink, error) { return os.OpenFile(u.Path[1:], os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) }) } var cfg zap.Config if debug { cfg = zap.NewDevelopmentConfig() } else { cfg = zap.NewProductionConfig() } if runtime.GOOS == "windows" { cfg.OutputPaths = []string{ "stdout", "winfile:///" + filename, } } else { cfg.OutputPaths = []string{ filename, } } return cfg.Build() } func load() { var err error var logger *zap.Logger CFG, err = config.Load() if err != nil { panic(err) } logger, err = loadLogger(CFG.Log, CFG.Debug) if err != nil { panic(err) } defer logger.Sync() LOG = logger.Sugar() if CFG.Proxy != "" { LOG.Debugf("set proxy: %s", CFG.Proxy) os.Setenv("HTTP_PROXY", CFG.Proxy) } } func loadSystems(c *ctx.Ctx) []error { var errs []error for i := 0; i < len(c.Config.Systems); i++ { sysCfg := c.Config.Systems[i] c.Logger.Debugf("loading system of type %s ...", sysCfg.Type) sysCfg.Config["proxy"] = CFG.Proxy sys, err := system.New(sysCfg.Type, &sysCfg.Config, LOG) if err != nil { c.Logger.Errorf("error loading system %s: %s", sysCfg.Type, err) c.Logger.Infof("system %s won't be available due to errors", sysCfg.Type) errs = append(errs, err) } else { c.Logger.Debugf("loaded %s system", sysCfg.Type) c.AddSystem(&sys) c.Logger.Debugf("setting system ID to %d", c.NumSystems()-1) sys.SetID(c.NumSystems() - 1) } } return errs } var rootCmd = &cobra.Command{ Use: "neonmodem", SuggestFor: []string{"bbs", "discourse", "lemmy"}, Short: "neonmodem, the bulletin board system TUI", Long: "neonmodem is a bulletin board system (BBS) text user interface written " + "in Go, supporting Discourse and Lemmy.\n" + "More info available on https://xn--gckvb8fzb.com/projects/neonmodem", Run: func(cmd *cobra.Command, args []string) { var err error c := ctx.New(EMBEDFS, &CFG, LOG) _ = loadSystems(&c) tui := tea.NewProgram(ui.NewModel(&c), tea.WithAltScreen()) err = tui.Start() if err != nil { panic(err) } }, } func Execute(efs *embed.FS) { EMBEDFS = efs if err := rootCmd.Execute(); err != nil { // LOG.Errorln(err) } }