refactor(deploy): improved TUI walkthrough

This commit is contained in:
Lemon-miaow committed 2026-06-30 02:25:18 +08:00
1 parent e5f1682898
commit 28c3eee361
20 files changed
+1580 -935

No files matched your search

+19 -119
View File
@@ -6,131 +6,31 @@ import (
"time"
"felis.lolicon.best/internal/store"
tea "github.com/charmbracelet/bubbletea"
)
type migRunMsg struct {
n int
total int
err error
}
type migrationModel struct {
dbURL string
osUser string
state string
applied int
total int
lastErr error
}
func newMigrationModel(dbURL, osUser string) *migrationModel {
return &migrationModel{
dbURL: dbURL,
osUser: osUser,
state: "checking",
// applyMigrations runs any pending schema migrations and returns the resulting
// applied count. Used by the preflight stage to self-heal a freshly bootstrapped
// (or upgraded) database.
func applyMigrations(dbURL string) (int, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
drv, err := store.Open(ctx, dbURL)
if err != nil {
return 0, fmt.Errorf("open db: %w", err)
}
}
func (m *migrationModel) Init() tea.Cmd {
return func() tea.Msg {
n, err := countMigrations(m.dbURL)
if err != nil {
return migRunMsg{err: err}
}
total, err := totalMigrations()
return migRunMsg{n: n, total: total, err: err}
defer drv.Close()
migrations, err := store.LoadMigrations()
if err != nil {
return 0, err
}
}
func (m *migrationModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case migRunMsg:
if msg.err != nil {
m.state = "error"
m.lastErr = msg.err
return m, nil
}
m.applied = msg.n
m.total = msg.total
if m.applied < m.total {
m.state = "pending"
return m, nil
}
return m, func() tea.Msg { return migrationDoneMsg{n: msg.n} }
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "esc":
return m, func() tea.Msg { return switchToDashboard{} }
case "enter":
if m.state == "pending" {
m.state = "running"
return m, runMigrationsCmd(m.dbURL)
}
}
if _, err := store.Up(ctx, drv, migrations); err != nil {
return 0, err
}
return m, nil
}
func (m *migrationModel) View() string {
var b string
b += tuiHeader("Database Migrations") + "\n"
switch m.state {
case "checking":
b += tuiHint.Render("Checking migration status…") + "\n"
case "pending":
b += tuiWarn.Render(fmt.Sprintf("%d of %d migrations applied. %d pending.", m.applied, m.total, m.total-m.applied)) + "\n\n"
b += tuiInfo("Press enter to run pending migrations.") + "\n"
case "running":
b += tuiHint.Render("Running migrations…") + "\n"
b += tuiInfo("This should take only a moment.") + "\n"
case "error":
b += tuiErr.Render("Migration check failed:") + "\n"
b += tuiHint.Render(m.lastErr.Error()) + "\n"
default:
b += tuiOK.Render(fmt.Sprintf("✓ %d migrations applied.", m.applied)) + "\n"
}
b += "\n"
b += tuiSeparator() + "\n"
switch m.state {
case "pending":
b += tuiAction("enter", "run", "esc", "back")
case "running":
b += tuiAction("esc", "cancel")
default:
b += tuiAction("esc", "back")
}
return b
}
func runMigrationsCmd(dbURL string) tea.Cmd {
return func() tea.Msg {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
drv, err := store.Open(ctx, dbURL)
if err != nil {
return migRunMsg{err: fmt.Errorf("open db: %w", err)}
}
defer drv.Close()
migrations, err := store.LoadMigrations()
if err != nil {
return migRunMsg{err: err}
}
_, err = store.Up(ctx, drv, migrations)
if err != nil {
return migRunMsg{err: err}
}
applied, err := drv.AppliedVersions(ctx)
if err != nil {
return migRunMsg{err: err}
}
return migRunMsg{n: len(applied), total: len(migrations)}
applied, err := drv.AppliedVersions(ctx)
if err != nil {
return 0, err
}
return len(applied), nil
}
func totalMigrations() (int, error) {