[mirror] yet another tui rss reader github.com/olexsmir/smutok
at main 2.8 kB view raw
1package main 2 3import ( 4 "context" 5 "errors" 6 "log/slog" 7 "os" 8 9 "olexsmir.xyz/smutok/internal/config" 10 "olexsmir.xyz/smutok/internal/freshrss" 11 "olexsmir.xyz/smutok/internal/store" 12) 13 14type app struct { 15 cfg *config.Config 16 store *store.Sqlite 17 freshrss *freshrss.Client 18 freshrssSyncer *freshrss.Syncer 19 freshrssWorker *freshrss.Worker 20} 21 22func bootstrap(ctx context.Context, outputToFile bool) (*app, error) { 23 cfg, err := config.New() 24 if err != nil { 25 return nil, err 26 } 27 28 if lerr := setupLogger(cfg, outputToFile); lerr != nil { 29 return nil, lerr 30 } 31 32 store, err := store.NewSQLite(cfg.DBPath) 33 if err != nil { 34 return nil, err 35 } 36 37 if merr := store.Migrate(ctx); merr != nil { 38 return nil, merr 39 } 40 41 fr := freshrss.NewClient(cfg.FreshRSS.Host) 42 token, err := getAuthToken(ctx, fr, store, cfg) 43 if err != nil { 44 return nil, err 45 } 46 fr.SetAuthToken(token) 47 48 fs := freshrss.NewSyncer(fr, store) 49 50 writeToken, err := getWriteToken(ctx, fr, store) 51 if err != nil { 52 return nil, err 53 } 54 55 fw := freshrss.NewWorker(fr, store, writeToken, cfg.FreshRSS.Host) 56 57 return &app{ 58 cfg: cfg, 59 store: store, 60 freshrss: fr, 61 freshrssSyncer: fs, 62 freshrssWorker: fw, 63 }, nil 64} 65 66func getAuthToken( 67 ctx context.Context, 68 fr *freshrss.Client, 69 db *store.Sqlite, 70 cfg *config.Config, 71) (string, error) { 72 token, err := db.GetToken(ctx) 73 if err == nil { 74 return token, nil 75 } 76 77 if !errors.Is(err, store.ErrNotFound) { 78 return "", err 79 } 80 81 slog.Info("requesting auth key") 82 83 token, err = fr.Login(ctx, cfg.FreshRSS.Username, cfg.FreshRSS.Password) 84 if err != nil { 85 return "", err 86 } 87 88 if serr := db.SetToken(ctx, token); serr != nil { 89 return "", serr 90 } 91 92 return token, nil 93} 94 95func getWriteToken(ctx context.Context, fr *freshrss.Client, db *store.Sqlite) (string, error) { 96 token, err := db.GetWriteToken(ctx) 97 if err == nil { 98 return token, nil 99 } 100 101 if !errors.Is(err, store.ErrNotFound) { 102 return "", err 103 } 104 105 slog.Info("requesting write token") 106 107 token, err = fr.GetWriteToken(ctx) 108 if err != nil { 109 return "", err 110 } 111 112 if serr := db.SetWriteToken(ctx, token); serr != nil { 113 return "", serr 114 } 115 116 return token, nil 117} 118 119var ( 120 ErrUnknownLevel = errors.New("unknown log level") 121 loggerLevels = map[string]slog.Level{ 122 "info": slog.LevelInfo, 123 "debug": slog.LevelDebug, 124 "error": slog.LevelError, 125 "warn": slog.LevelWarn, 126 } 127) 128 129func setupLogger(cfg *config.Config, outputToFile bool) error { 130 out := os.Stdout 131 if outputToFile { 132 file, err := os.OpenFile(cfg.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666) 133 if err != nil { 134 return err 135 } 136 out = file 137 } 138 139 logLevel, ok := loggerLevels[cfg.LogLevel] 140 if !ok { 141 return ErrUnknownLevel 142 } 143 144 logger := slog.New(slog.NewTextHandler(out, &slog.HandlerOptions{ 145 Level: logLevel, 146 })) 147 slog.SetDefault(logger) 148 return nil 149}