From b99701a7ba6c8efde550e2b714c7a4b18dfb06d5 Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Sun, 17 Nov 2024 13:21:40 +1000 Subject: [PATCH] explicit loadSecrets+some tidying up --- cmd/slackdump/internal/bootstrap/provider.go | 2 +- cmd/slackdump/internal/cfg/cfg.go | 10 +++--- .../internal/workspace/wiz_select.go | 33 ++++++++++--------- .../internal/workspace/workspaceui/new.go | 30 ++++++++++++++--- cmd/slackdump/main.go | 18 ++++++++++ downloader/deprecated.go | 4 +-- 6 files changed, 70 insertions(+), 27 deletions(-) diff --git a/cmd/slackdump/internal/bootstrap/provider.go b/cmd/slackdump/internal/bootstrap/provider.go index 241f0caa..f54bc0e4 100644 --- a/cmd/slackdump/internal/bootstrap/provider.go +++ b/cmd/slackdump/internal/bootstrap/provider.go @@ -18,7 +18,7 @@ func CurrentOrNewProviderCtx(ctx context.Context) (context.Context, error) { if err != nil { if errors.Is(err, cache.ErrNoWorkspaces) { // ask to create a new workspace - if err := workspaceui.ShowUI(ctx, true); err != nil { + if err := workspaceui.ShowUI(ctx, workspaceui.WithQuickLogin(), workspaceui.WithTitle("No workspaces, please choose a login method")); err != nil { return ctx, fmt.Errorf("auth error: %w", err) } // one more time... diff --git a/cmd/slackdump/internal/cfg/cfg.go b/cmd/slackdump/internal/cfg/cfg.go index b8a57cab..8abb5913 100644 --- a/cmd/slackdump/internal/cfg/cfg.go +++ b/cmd/slackdump/internal/cfg/cfg.go @@ -57,6 +57,10 @@ var ( Log logger.Interface + // LoadSecrets is a flag that indicates whether to load secrets from the + // environment variables. + LoadSecrets bool + Version BuildInfo // version propagated by main package. ) @@ -104,14 +108,14 @@ func SetBaseFlags(fs *flag.FlagSet, mask FlagMask) { if mask&OmitAuthFlags == 0 { fs.StringVar(&SlackToken, "token", osenv.Secret("SLACK_TOKEN", ""), "Slack `token`") - // COOKIE environment variable is deprecated and will be removed in v2.5.0, use SLACK_COOKIE instead. - fs.StringVar(&SlackCookie, "cookie", osenv.Secret("SLACK_COOKIE", osenv.Secret("COOKIE", "")), "d= cookie `value` or a path to a cookie.txt file\n(environment: SLACK_COOKIE)") + fs.StringVar(&SlackCookie, "cookie", osenv.Secret("SLACK_COOKIE", ""), "d= cookie `value` or a path to a cookie.txt file\n(environment: SLACK_COOKIE)") fs.Var(&Browser, "browser", "browser to use for legacy EZ-Login 3000 (default: firefox)") fs.DurationVar(&LoginTimeout, "browser-timeout", LoginTimeout, "Browser login `timeout`") fs.DurationVar(&HeadlessTimeout, "autologin-timeout", HeadlessTimeout, "headless autologin `timeout`, without the browser starting time, just the interaction time") fs.BoolVar(&LegacyBrowser, "legacy-browser", false, "use legacy browser automation (playwright) for EZ-Login 3000") fs.BoolVar(&ForceEnterprise, "enterprise", false, "enable Enteprise module, you need to specify this option if you're using Slack Enterprise Grid") fs.StringVar(&RODUserAgent, "user-agent", "", "override the user agent string for EZ-Login 3000") + fs.BoolVar(&LoadSecrets, "load-env", false, "load secrets from the .env, .env.txt or secrets.txt file") } if mask&OmitDownloadFlag == 0 { fs.BoolVar(&DownloadFiles, "files", true, "enables file attachments (to disable, specify: -files=false)") @@ -146,7 +150,5 @@ func SetBaseFlags(fs *flag.FlagSet, mask FlagMask) { if mask&OmitTimeframeFlag == 0 { fs.Var(&Oldest, "time-from", "timestamp of the oldest message to fetch (UTC timezone)") fs.Var(&Latest, "time-to", "timestamp of the newest message to fetch (UTC timezone)") - fs.Var(&Oldest, "date-from", "alias for -time-from (DEPRECATED)") - fs.Var(&Latest, "date-to", "alias for -time-to (DEPRECATED)") } } diff --git a/cmd/slackdump/internal/workspace/wiz_select.go b/cmd/slackdump/internal/workspace/wiz_select.go index 45231e6b..c4dcb874 100644 --- a/cmd/slackdump/internal/workspace/wiz_select.go +++ b/cmd/slackdump/internal/workspace/wiz_select.go @@ -23,6 +23,23 @@ func wizSelect(ctx context.Context, cmd *base.Command, args []string) error { return err } + if _, err := m.List(); err != nil { + if errors.Is(err, cache.ErrNoWorkspaces) { + if err := workspaceui.ShowUI(ctx, workspaceui.WithQuickLogin(), workspaceui.WithTitle("No workspaces, please choose the login method:")); err != nil { + return err + } + return nil + } else { + base.SetExitStatus(base.SUserError) + return err + } + } + + if _, err := m.Current(); err != nil { + base.SetExitStatus(base.SWorkspaceError) + return fmt.Errorf("error getting the current workspace: %s", err) + } + sm, err := newWspSelectModel(ctx, m) if err != nil { return err @@ -45,22 +62,6 @@ func wizSelect(ctx context.Context, cmd *base.Command, args []string) error { // newWspSelectModel creates a new workspace selection model. func newWspSelectModel(ctx context.Context, m *cache.Manager) (tea.Model, error) { - _, err := m.List() - if err != nil { - if errors.Is(err, cache.ErrNoWorkspaces) { - if err := workspaceui.ShowUI(ctx, true); err != nil { - return nil, err - } - } else { - base.SetExitStatus(base.SUserError) - return nil, err - } - } - - if _, err := m.Current(); err != nil { - base.SetExitStatus(base.SWorkspaceError) - return nil, fmt.Errorf("error getting the current workspace: %s", err) - } var refreshFn = func() (cols []table.Column, rows []table.Row, err error) { cols = []table.Column{ diff --git a/cmd/slackdump/internal/workspace/workspaceui/new.go b/cmd/slackdump/internal/workspace/workspaceui/new.go index 98c088db..a6dfeaa4 100644 --- a/cmd/slackdump/internal/workspace/workspaceui/new.go +++ b/cmd/slackdump/internal/workspace/workspaceui/new.go @@ -24,12 +24,27 @@ type manager interface { } func WorkspaceNew(ctx context.Context, _ *base.Command, _ []string) error { - return ShowUI(ctx, false) + return ShowUI(ctx) +} + +type options struct { + title string + quicklogin bool +} + +type UIOption func(*options) + +func WithTitle(title string) UIOption { + return func(o *options) { o.title = title } +} + +func WithQuickLogin() UIOption { + return func(o *options) { o.quicklogin = true } } // ShowUI shows the authentication menu. If quicklogin is set to true, // it will quit after the user has successfully authenticated. -func ShowUI(ctx context.Context, quicklogin bool) error { +func ShowUI(ctx context.Context, opts ...UIOption) error { const ( actLogin = "ezlogin" actToken = "token" @@ -44,6 +59,13 @@ func ShowUI(ctx context.Context, quicklogin bool) error { return err } + var uiOpts = options{ + title: "New Workspace", + } + for _, o := range opts { + o(&uiOpts) + } + var brwsOpts browserOptions items := []menu.Item{ @@ -101,7 +123,7 @@ func ShowUI(ctx context.Context, quicklogin bool) error { var lastID string = actLogin LOOP: for { - m := menu.New("New Workspace", items, true) + m := menu.New(uiOpts.title, items, uiOpts.quicklogin) m.Select(lastID) if _, err := tea.NewProgram(&wizModel{m: m}, tea.WithContext(ctx)).Run(); err != nil { return err @@ -123,7 +145,7 @@ LOOP: } return err } - if quicklogin { + if uiOpts.quicklogin { return nil } } diff --git a/cmd/slackdump/main.go b/cmd/slackdump/main.go index 4e27cdfd..1c9d3650 100644 --- a/cmd/slackdump/main.go +++ b/cmd/slackdump/main.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/charmbracelet/huh" + "github.com/joho/godotenv" "github.com/rusq/dlog" "github.com/rusq/tracer" "golang.org/x/term" @@ -157,6 +158,10 @@ func invoke(cmd *base.Command, args []string) error { return err } } + if cfg.LoadSecrets { + // load secrets only if we're told to. + loadSecrets(secretFiles) + } // maybe start trace if err := initTrace(cfg.TraceFile); err != nil { @@ -283,6 +288,19 @@ func iftrue[T any](cond bool, t T, f T) T { return f } +// secrets defines the names of the supported secret files that we load our +// secrets from. Inexperienced Windows users might have bad experience trying +// to create .env file with the notepad as it will battle for having the +// "txt" extension. Let it have it. +var secretFiles = []string{".env", ".env.txt", "secrets.txt"} + +// loadSecrets load secrets from the files in secrets slice. +func loadSecrets(files []string) { + for _, f := range files { + _ = godotenv.Load(f) + } +} + type choice string const ( diff --git a/downloader/deprecated.go b/downloader/deprecated.go index 31727059..9fd5e6fa 100644 --- a/downloader/deprecated.go +++ b/downloader/deprecated.go @@ -26,7 +26,7 @@ const ( // ClientV1 is the instance of the downloader. // -// Deprecated: Use Client. +// Deprecated: Use [Client]. type ClientV1 struct { v2 *Client nameFn FilenameFunc @@ -90,7 +90,7 @@ func WithNameFunc(fn FilenameFunc) OptionV1 { // NewV1 initialises new file downloader. // -// Deprecated: use NewV2 instead. +// Deprecated: use [New] instead. func NewV1(client Downloader, fs fsadapter.FS, opts ...OptionV1) *ClientV1 { c := &ClientV1{ v2: New(client, fs),