Files
Felis/cmd/felis/tui_summary.go
T
flyemoji 0c1cc598c1 feat(auth): migrate console login to passwordless
Replace console password auth with a passwordless surface — the pre-session
login doors plus an identifier-first discovery endpoint — and remove the
password paths.

- Login doors (Public, pre-session): email-OTP, passkey assertion, op.console
  login with in-game approval, and setup-token redeem.
- /api/v1/auth/options: identifier-first discovery reporting which console
  methods an email can use. The single sanctioned existence oracle; methods
  are computed with no role branch, so staff and player accounts in the same
  credential state return byte-identical bodies (staffness invisible by
  construction).
- Remove password auth: drop StaffUser.PasswordHash and the /auth/login,
  /auth/change-password and /users/{id}/reset-password endpoints (and test).
- Data layer: UserByEmail, verified-email uniqueness, setup-token store
  (migration 0012).
- Reconcile docs/openapi.yaml with the served surface; the method/path/face/
  tier parity gate (TestOpenAPIMatchesServedRoutes) passes.
- felis TUI: in-game MC bind, owner/break-glass OP provisioning, version.
- Velocity /felis command suite.

Consolidates the accumulated backend migration work; the frontend (panel/)
is left untouched. Full Go tree green on WSL (go build ./... && go test ./...).
2026-07-04 21:47:12 +09:00

84 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"strings"
tea "github.com/charmbracelet/bubbletea"
)
// summaryModel is the terminal screen of the setup wizard. On a first run it
// confirms what was just configured and points the operator at the panel for
// everything else. On a re-run (an Owner already exists) it doubles as a thin
// status landing screen — the whole point of the redesign is that setup runs
// once and the rest of administration happens in the panel.
type summaryModel struct {
panelURL string
ownerUsername string
setupTokenURL string // one-time first-login URL; shown once
accessLabel string
storageLabel string // build-context storage backend recap; empty to omit
routedHosts []string
alreadySetUp bool // re-run: Owner pre-existed
localHint bool // show the self-signed-cert note
}
func (m *summaryModel) Init() tea.Cmd { return nil }
// arrowNavOK lets the root repurpose ←/→ to walk back through completed steps;
// the summary takes no text input, so the horizontal arrows are free.
func (m *summaryModel) arrowNavOK() bool { return true }
func (m *summaryModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if key, ok := msg.(tea.KeyMsg); ok {
switch key.String() {
case "c", "C":
return m, func() tea.Msg { return reconfigureConnectMsg{} }
case "s", "S":
return m, func() tea.Msg { return reconfigureStorageMsg{} }
case "ctrl+c", "esc", "enter", "q":
return m, tea.Quit
}
}
return m, nil
}
func (m *summaryModel) View() string {
var b strings.Builder
if m.alreadySetUp {
b.WriteString(tuiOK.Render("✓ Felis is already set up.") + "\n\n")
} else {
b.WriteString(tuiOK.Render("✓ Setup complete.") + "\n\n")
}
var card strings.Builder
if m.ownerUsername != "" {
card.WriteString(tuiLabel.Render("owner ") + m.ownerUsername + "\n")
}
if m.setupTokenURL != "" {
card.WriteString(tuiLabel.Render("setup URL ") + tuiPassword.Render(m.setupTokenURL) + "\n")
card.WriteString(" " + tuiWarn.Render("one-time link — open it to finish login setup") + "\n")
}
if m.accessLabel != "" {
card.WriteString(tuiLabel.Render("access ") + m.accessLabel + "\n")
}
if m.storageLabel != "" {
card.WriteString(tuiLabel.Render("storage ") + m.storageLabel + "\n")
}
if len(m.routedHosts) > 0 {
card.WriteString(tuiLabel.Render("routed ") + strings.Join(m.routedHosts, ", ") + "\n")
}
if m.panelURL != "" {
card.WriteString(tuiLabel.Render("panel ") + m.panelURL + "\n")
}
b.WriteString(tuiCardStyle.Render(strings.TrimRight(card.String(), "\n")) + "\n\n")
b.WriteString(tuiHint.Render("ℹ Everything else — servers, users, plugins — is configured in the panel. You won't need this console again.") + "\n")
if m.localHint {
b.WriteString(tuiHint.Render(" The local certificate is self-signed; your browser may warn on first visit.") + "\n")
}
b.WriteString("\n" + tuiAction("c", "change connection", "s", "change storage", "enter/esc", "exit"))
return b.String()
}