package ui import ( "fmt" "time" tea "github.com/charmbracelet/bubbletea" "ssh-access-tui/internal/model" "ssh-access-tui/internal/sysadm" ) // updateConfirmAction drives the single generic "are you sure" screen that // backs every destructive action in the tool (lock/delete a user, delete a // host or a service). Nothing the action does runs until this confirms it; // esc/n cancels with zero effect. func (m Model) updateConfirmAction(msg tea.Msg) (tea.Model, tea.Cmd) { if key, ok := msg.(tea.KeyMsg); ok { switch key.String() { case "esc", "n": m.pendingConfirm = confirmNone m.screen = m.confirmReturnTo return m, nil case "enter", "y": return m.executeConfirmedAction() } } return m, nil } func (m Model) viewConfirmAction() string { body := warnStyle.Render("ТРЕБУЕТСЯ ПОДТВЕРЖДЕНИЕ") + "\n\n" body += m.confirmMessage + "\n\n" body += "[enter/y] подтвердить [esc/n] отмена, ничего не изменится" return borderStyle.Render(body) } func (m Model) executeConfirmedAction() (tea.Model, tea.Cmd) { kind := m.pendingConfirm target := m.confirmTarget m.pendingConfirm = confirmNone m.err, m.status = "", "" switch kind { case confirmLockUser: return m.doLockUser(target) case confirmDeleteUser: return m.doDeleteUser(target) case confirmDeleteHost: return m.doDeleteHost(target) case confirmDeleteService: return m.doDeleteService(target) } m.screen = m.confirmReturnTo return m, nil } func (m Model) doLockUser(username string) (tea.Model, tea.Cmd) { if err := sysadm.LockSystemUser(username); err != nil { m.err = err.Error() m.screen = scrUsers return m, nil } _ = sysadm.DisableAuthorizedKeys(username) m.mutate("заблокирован пользователь "+username, func(st *model.State) error { u := st.FindUser(username) if u == nil { return fmt.Errorf("user %s not found in state", username) } now := time.Now() u.Status = model.StatusLocked u.LockedAt = &now return nil }) m.logAudit(username, "admin", "locked") m.screen = scrUsers m.refreshUsersList() return m, nil } func (m Model) doDeleteUser(username string) (tea.Model, tea.Cmd) { if err := sysadm.DeleteSystemUser(username); err != nil { m.err = err.Error() m.screen = scrUsers return m, nil } m.mutate("удалён пользователь "+username, func(st *model.State) error { for i := range st.Users { if st.Users[i].Username == username { st.Users = append(st.Users[:i], st.Users[i+1:]...) break } } return nil }) m.logAudit(username, "admin", "deleted") m.screen = scrUsers m.refreshUsersList() return m, nil } // doDeleteHost removes a host (and all its services) from m.selectedGroup, // then resyncs authorized_keys for every affected user. func (m Model) doDeleteHost(hostName string) (tea.Model, tea.Cmd) { groupName := m.selectedGroup m.mutate(fmt.Sprintf("удалён хост %s из группы %s", hostName, groupName), func(st *model.State) error { g := st.FindGroup(groupName) if g == nil { return fmt.Errorf("group %s not found", groupName) } for i := range g.Hosts { if g.Hosts[i].Name == hostName { g.Hosts = append(g.Hosts[:i], g.Hosts[i+1:]...) break } } return resyncGroupUsers(st, groupName) }) m.screen = scrGroupHosts m.refreshHostsList() return m, nil } // doDeleteService removes a service from m.selectedHost within // m.selectedGroup, then resyncs authorized_keys for every affected user. func (m Model) doDeleteService(serviceName string) (tea.Model, tea.Cmd) { groupName, hostName := m.selectedGroup, m.selectedHost m.mutate(fmt.Sprintf("удалён сервис %s у хоста %s", serviceName, hostName), func(st *model.State) error { g := st.FindGroup(groupName) if g == nil { return fmt.Errorf("group %s not found", groupName) } for hi := range g.Hosts { if g.Hosts[hi].Name != hostName { continue } svcs := g.Hosts[hi].Services for si := range svcs { if svcs[si].Name == serviceName { g.Hosts[hi].Services = append(svcs[:si], svcs[si+1:]...) break } } } return resyncGroupUsers(st, groupName) }) m.screen = scrHostServices m.refreshServicesList() return m, nil }