Files
ssh-access-tui/internal/ui/confirm.go
T
2026-07-22 15:14:44 +03:00

151 lines
4.2 KiB
Go

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
}