024dc095a594e43b4d26f40c2b5d1b6d6f35143d
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ee25552a53 |
Sign in through the server's single sign-on, with a code
The sign-in screen asks the server how it can be signed in to (GET /api/auth/config) and offers what it finds: the password form, and "Sign in with <provider>" when the server can do a device login. The TUI shows a link and a short code, the person approves it in any browser, and the next poll hands over the ordinary session, so it works over SSH where no browser can be opened. The terminal never talks to the identity provider. The password form is hidden when the server has turned password login off. `auth: sso` in config.yaml starts the SSO login straight away, but not right after signing out, where that would sign the person straight back in; any other value is refused when the config is read. Polling honours the server's interval, backs off on slow_down, and gives up after repeated failures rather than retrying forever. A server without /api/auth/config answers 404 and is treated as passwords only, so the sign-in screen is the one it had. Needs terdut-server v0.29.0 for SSO. |
||
|
|
f4ca0059dc |
Sign in as a user instead of with an API key
The web UI signs in with a username and password and holds a session cookie; the TUI was the only client still needing an API key pasted into a config file. It now asks for the same credentials on a form at start. What is kept between runs is the session token, not the password, in session.json under the config directory, mode 0600 and keyed by server URL so one server's token is never offered to another. It resumes on the next start; the server's sessions last 30 days and slide with use. L signs out, which ends the session on the server and deletes the saved one even if the server cannot be reached. The client attaches the cookie by hand instead of using a cookie jar: the server marks it Secure behind https, and a jar drops a Secure cookie it is given over plain http, which would break a local server for no reason. It sends no Authorization header at all, since the server judges a request carrying one on that alone and never falls back to the cookie. Writes go through the server's cross-origin guard, which lets a client that sends neither Origin nor Sec-Fetch-Site through; checked against a real v0.20.1 server for both reads and writes. A 401 from anything means the session is gone (expired, ended from the web UI, or the account disabled), so the TUI returns to the form with the reason, forgets the saved token, and drops what the last session loaded rather than showing it to whoever signs in next. A 403 is a permission and leaves the session alone. The refresh timer is started once, so signing out and in does not leave two running. An account with no password cannot sign in, and the server answers it exactly like a wrong password, so the form's message says a password must be set first. Users created only for API access hit this. Breaking: api_key in config.yaml is no longer used. It is not an error to leave it there; the form says it is ignored. API keys still exist on the server and k in Users still manages them. |
||
|
|
27008086b0 |
Follow terdut-server into teams: switch team, per-team schedule
terdut-server v0.12 made everything team-scoped and v0.20 is what this
client now targets. Against it the old client was wrong in three ways:
the schedule moved to /api/teams/{id}/schedule, GET /api/schedule/current
became a list with one entry per team, and users, incidents, alerts and
schedule entries all grew fields the client ignored.
T steps through all teams and then each of yours. The header names what
is showing, and incident and alert rows gain a Team column when more than
one team can appear. team: in config.yaml picks the team to start on, by
name or id; an unknown one is reported and falls back to all teams.
The schedule is one team's rota, so it shows the active team, or with
all teams showing the first one you own. Writes need an owner or an
administrator, and the picker offers only the team's members, since the
server answers 404 for anybody else. Both are checked up front and the
reason goes in the status bar, rather than surfacing as a 403 after the
user has picked somebody. Stats are not team-scoped by the server and
stay that way here.
Users shows an admin/disabled Flags column. Creating and deleting users
is administrators only, and topic, keys and password work on your own
row or on anyone's for an administrator; the server enforces the same
rule, this only explains it before the round trip.
The server has no version endpoint, so an older one is recognised by
GET /api/teams answering 404, and the TUI says it needs v0.20 or later.
Connecting now also loads /api/teams and /api/me with the key, which
means a wrong key fails on start instead of on the first list; /healthz
does not check it. There is no fallback to the pre-team paths.
Rebuilding a table whose column count changes under loaded rows panicked
inside bubbles, because it re-renders the old rows on SetColumns. The
rows are now cleared first and the cursor put back, so a refresh still
does not jump to the top.
Escalation ladders, invites, integrations and the admin settings are
left to the server's web UI. Checked against a real v0.20.1 server with
two teams, an administrator and a plain member.
Breaking: requires terdut-server v0.20.0 or later. Use terdut-tui v0.9.x
with servers before v0.12.
|
||
|
|
4a579bdbc6 |
Colour themes, defaulting to gruvbox dark
CI / test (pull_request) Successful in 4s
Every colour was a 256-colour ANSI index hardcoded in styles.go, so changing the palette meant editing the styles themselves. This puts a semantic token set between the two: styles name roles, a theme supplies the colours. internal/theme holds the twelve tokens, the two built-ins (gruvbox-dark, the new default, and gruvbox-light) and the loader for user themes in ~/.config/terdut-tui/themes/. A user file may 'extends:' a built-in and override only what it cares about, and may shadow a built-in name to tweak it in place. Unknown keys, malformed colours and incomplete themes are refused with a message naming what went wrong. Colours are truecolor hex now: lipgloss downsamples for 256- and 16-colour terminals and honours NO_COLOR, so themes carry no fallbacks of their own. An ANSI index is still accepted for anyone who would rather follow their terminal's own palette. The 21 package-level style vars become a Styles struct on the Model, which is what rule 3 asked for all along; the four free functions in view.go take one as their first argument. The embedded bubbles components are restyled from the same tokens — otherwise a theme would leave a pink selected row and grey help text behind. Note that the table's Cell style deliberately keeps no foreground: bubbles renders cells before wrapping the row in Selected, so a colour there cuts the selection highlight short. |
||
|
|
d6c0f7508c |
Stop a table cursor from getting stuck at -1
Assigning an on-call week panicked with "index out of range [-1]" on an ordinary schedule. The index came from scheduleTable.Cursor(). The cursor is not ours. bubbles' SetRows clamps it down when rows shrink (`if m.cursor > len(rows)-1`) but never back up, so setting zero rows drives it to -1 and filling the table afterwards leaves it there -- -1 is not greater than len-1, so nothing corrects it. Every table in this package is rebuilt from empty exactly once, when the first WindowSizeMsg arrives before any fetch has returned, so every cursor started at -1 and stayed there until the user pressed up or down. Pressing a direction key first is why this was survivable at all. setRows restores the invariant the rest of the package already assumes: a table with rows has a usable cursor. Every rebuild goes through it. Five call sites also bounds-checked only the top of the range, and are now consistent with their siblings, which already had `i < 0 ||`. They were the same latent panic: deleting a schedule entry, deleting a user, editing a topic, opening the API key menu, and the week assignment that actually fired. The regression test deliberately never calls SetCursor. That is what the existing schedule tests do, and SetCursor clamps, which is exactly how this got past them. It drives the real order instead: size, then data, then keys. Also carries a gofmt pass, which is why untouched files appear in the diff. The move to git.ryuvia.com rewrote import paths without re-sorting them, and the new path sorts before github.com/charmbracelet/..., where the old one sorted after. go vet does not look at import order, so CI had nothing to say. |
||
|
|
6fdb4bbbf8 |
Move to Gitea: git.ryuvia.com/niklas/terdut-tui
The module path, the CI pipeline and the self-updater all named GitHub. They now name the Gitea instance everything else already runs on. The workflows are rewritten rather than translated, for the reason recorded in ci.yaml: Gitea's runner image is ubuntu:22.04, whose nodejs is Node 12, so no JS action runs there -- actions/checkout@v4 dies with a SyntaxError before doing anything. Every step is shell and checkout is a plain clone, which this public repo needs no credential for. upload-artifact/download-artifact are JS actions too, and there is no artifact store here, so the job that builds the binaries is the job that publishes them. internal/updater keeps its release and asset types unchanged: Gitea's release payload carries the same tag_name, and its attachments the same name and browser_download_url, so only the URL, the Accept header and one error string move. The asset naming in release.yaml is load-bearing for that matching. This does strand already-installed binaries, which still poll api.github.com. The GitHub repository is left in place and untouched, so they report themselves up to date rather than erroring; its last release is the bridge, and crossing it is a one-time manual download. |
||
|
|
55ee64530b |
feat: Stage 1 scaffold — config, API client, placeholder TUI
Sets up the full project structure following the hactl/gokapi-tui architecture: strict Elm-pattern Bubbletea TUI, YAML config at ~/.config/terdut-tui/config.yaml, REST API client with Bearer auth, self-update via GitHub Releases, and a three-section tab placeholder (Alerts / Schedule / Users) that verifies server connectivity on startup. |