Compare commits

...

2 Commits

Author SHA1 Message Date
5f3ea04469 fix: show re-auth needed when AWS session expired 2026-06-15 06:58:37 +00:00
40a435fdfc fix: show re-auth needed when AWS session expired
The dashboard auth panel only had branches for authed and awsmfa-error,
so an expired session (awsmfa prints "Not authenticated.", Authed=false,
Err="") fell through to the "checking…" spinner and appeared stuck.

Add an authChecked flag to distinguish "not yet checked" from "checked,
not authed", and render "re-auth needed" for any non-authed result.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 08:55:55 +02:00
2 changed files with 35 additions and 7 deletions

View File

@ -90,8 +90,9 @@ type dashModel struct {
interval time.Duration interval time.Duration
self string // path to this binary, for re-invoking subcommands self string // path to this binary, for re-invoking subcommands
auth checks.AuthStatus auth checks.AuthStatus
docker dockerMsg authChecked bool // true once the first auth result has arrived
docker dockerMsg
drift driftMsg drift driftMsg
repos repoMsg repos repoMsg
@ -325,6 +326,7 @@ func (m dashModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
case authMsg: case authMsg:
m.auth = checks.AuthStatus(msg) m.auth = checks.AuthStatus(msg)
m.authChecked = true
m.lastRefresh = time.Now() m.lastRefresh = time.Now()
case dockerMsg: case dockerMsg:
m.docker = msg m.docker = msg
@ -582,17 +584,23 @@ func (m dashModel) footer() string {
func (m dashModel) authPanel() string { func (m dashModel) authPanel() string {
var s strings.Builder var s strings.Builder
s.WriteString(headerStyle.Render("AWS AUTH") + "\n") s.WriteString(headerStyle.Render("AWS AUTH") + "\n")
if m.auth.Authed { switch {
case !m.authChecked:
s.WriteString(dimStyle.Render("checking…"))
case m.auth.Authed:
mark := okStyle.Render("✓ authed") mark := okStyle.Render("✓ authed")
// Warn when the session expires within 30 min. // Warn when the session expires within 30 min.
if !m.auth.Expires.IsZero() && time.Until(m.auth.Expires) < 30*time.Minute { if !m.auth.Expires.IsZero() && time.Until(m.auth.Expires) < 30*time.Minute {
mark = badStyle.Render("⚠ expiring") mark = badStyle.Render("⚠ expiring")
} }
s.WriteString(mark + dimStyle.Render(" "+m.auth.Detail)) s.WriteString(mark + dimStyle.Render(" "+m.auth.Detail))
} else if m.auth.Err != "" { default:
s.WriteString(badStyle.Render("✗ re-auth needed") + "\n" + dimStyle.Render(truncate(m.auth.Err, 70))) // Not authed: awsmfa errored, or the session is gone/expired.
} else { detail := m.auth.Err
s.WriteString(dimStyle.Render("checking…")) if detail == "" {
detail = m.auth.Detail
}
s.WriteString(badStyle.Render("✗ re-auth needed") + "\n" + dimStyle.Render(truncate(detail, 70)))
} }
return s.String() return s.String()
} }

View File

@ -38,6 +38,26 @@ func TestDashViewRenders(t *testing.T) {
} }
} }
func TestAuthPanelExpiredShowsReauth(t *testing.T) {
m := newDash([]string{"/tmp"}, "/x/awsmfa", "", 10*time.Second)
// Before any check result has arrived, the panel shows the spinner.
if !strings.Contains(m.View(), "checking…") {
t.Errorf("initial panel should show checking…\n---\n%s", m.authPanel())
}
// Expired session: awsmfa printed "Not authenticated." → Authed false, Err empty.
um, _ := m.Update(authMsg(checks.AuthStatus{Authed: false, Detail: "Not authenticated."}))
m = um.(dashModel)
got := m.authPanel()
if strings.Contains(got, "checking…") {
t.Errorf("expired session still shows checking…\n---\n%s", got)
}
if !strings.Contains(got, "re-auth needed") {
t.Errorf("expired session should show re-auth needed\n---\n%s", got)
}
}
func names(items []suggestion) string { func names(items []suggestion) string {
var b strings.Builder var b strings.Builder
for _, it := range items { for _, it := range items {