package main
import (
"fmt"
"strings"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"git.neuromancer.ovh/bastien-mrq/gitfed/internal/store"
)
func (m *model) refreshUsers() {
users, _ := m.ops.ListUsers()
items := make([]list.Item, len(users))
for i, u := range users {
items[i] = userItem{u}
}
m.usersList.SetItems(items)
}
func (m *model) refreshRepos() {
repos, _ := m.ops.ListRepos()
items := make([]list.Item, len(repos))
for i, r := range repos {
items[i] = repoItem{r}
}
m.reposList.SetItems(items)
}
func (m *model) refreshACL() {
repo, err := m.ops.GetRepo(m.selectedRepo)
if err != nil {
m.aclList.SetItems(nil)
return
}
items := []list.Item{
collaboratorItem{c: store.Collaborator{Principal: repo.Owner, Role: store.RoleAdmin}, isOwner: true},
}
a, err := m.ops.GetACL(m.selectedRepo)
if err == nil {
for _, c := range a.Collaborators {
items = append(items, collaboratorItem{c: c})
}
}
m.aclList.SetItems(items)
}
func (m *model) refreshTrust() {
trusted, _ := m.ops.ListTrustedCAs()
items := make([]list.Item, len(trusted))
for i, t := range trusted {
items[i] = trustItem{t}
}
m.trustList.SetItems(items)
}
func (m *model) refreshAudit() {
events, _ := m.ops.ListAudit(200)
items := make([]list.Item, len(events))
for i, e := range events {
items[i] = auditItem{e}
}
m.auditList.SetItems(items)
}
func (m model) openAddUserForm() (tea.Model, tea.Cmd) {
a := m.ops
m.activeForm = newForm("Add user",
[]string{"Username", "Public key", "Password", "Admin (y/n)"},
[]string{"alice", "ssh-ed25519 AAAA... comment", "min. 8 characters", "n"},
func(values []string) tea.Msg {
username := strings.TrimSpace(values[0])
if username == "" {
return statusMsg{err: fmt.Errorf("username is required")}
}
if err := a.CreateUser(username, values[1]); err != nil {
return statusMsg{err: err}
}
if password := values[2]; password != "" {
if err := a.SetPassword(username, password); err != nil {
return statusMsg{err: err}
}
}
if isAdmin := strings.EqualFold(strings.TrimSpace(values[3]), "y"); isAdmin {
if err := a.SetUserAdmin(username, true); err != nil {
return statusMsg{err: err}
}
}
return statusMsg{text: "user " + username + " created"}
},
func() tea.Msg { return formCancelMsg{} },
)
return m, nil
}
func (m model) deleteSelectedUser() (tea.Model, tea.Cmd) {
item, ok := m.usersList.SelectedItem().(userItem)
if !ok {
return m, nil
}
if err := m.ops.DeleteUser(item.u.Username); err != nil {
m.err, m.status = err, ""
} else {
m.status, m.err = "deleted user "+item.u.Username, nil
}
m.refreshUsers()
return m, nil
}
func (m model) openAddRepoForm() (tea.Model, tea.Cmd) {
a := m.ops
m.activeForm = newForm("Add repo",
[]string{"Name", "Owner (local username)"},
[]string{"alice/mon-projet", "alice"},
func(values []string) tea.Msg {
name := strings.TrimSpace(values[0])
owner := strings.TrimSpace(values[1])
if name == "" || owner == "" {
return statusMsg{err: fmt.Errorf("name and owner are required")}
}
if err := a.CreateRepo(name, owner); err != nil {
return statusMsg{err: err}
}
return statusMsg{text: "repo " + name + " created"}
},
func() tea.Msg { return formCancelMsg{} },
)
return m, nil
}
func (m model) deleteSelectedRepo() (tea.Model, tea.Cmd) {
item, ok := m.reposList.SelectedItem().(repoItem)
if !ok {
return m, nil
}
if err := m.ops.DeleteRepo(item.r.Name); err != nil {
m.err, m.status = err, ""
} else {
m.status, m.err = "deleted repo "+item.r.Name, nil
}
m.refreshRepos()
return m, nil
}
func (m model) openACLForSelectedRepo() (tea.Model, tea.Cmd) {
item, ok := m.reposList.SelectedItem().(repoItem)
if !ok {
return m, nil
}
m.selectedRepo = item.r.Name
m.aclList.Title = "Collaborators — " + item.r.Name
m.refreshACL()
m.view = viewACL
m.status, m.err = "", nil
return m, nil
}
func (m model) openAddCollaboratorForm() (tea.Model, tea.Cmd) {
a := m.ops
repoName := m.selectedRepo
actor := "admin@" + m.domain
m.activeForm = newForm("Add collaborator",
[]string{"Principal", "Role"},
[]string{"bob@instanceb.example", "read|write|admin"},
func(values []string) tea.Msg {
principal := strings.TrimSpace(values[0])
role := store.Role(strings.ToLower(strings.TrimSpace(values[1])))
if !role.Valid() {
return statusMsg{err: fmt.Errorf("role must be read, write or admin")}
}
if err := a.GrantCollaborator(repoName, principal, actor, role); err != nil {
return statusMsg{err: err}
}
return statusMsg{text: principal + " granted " + string(role) + " on " + repoName}
},
func() tea.Msg { return formCancelMsg{} },
)
return m, nil
}
func (m model) deleteSelectedCollaborator() (tea.Model, tea.Cmd) {
item, ok := m.aclList.SelectedItem().(collaboratorItem)
if !ok {
return m, nil
}
if item.isOwner {
m.status, m.err = "", fmt.Errorf("cannot remove the repo owner")
return m, nil
}
if err := m.ops.RevokeCollaborator(m.selectedRepo, item.c.Principal); err != nil {
m.err, m.status = err, ""
} else {
m.status, m.err = "removed "+item.c.Principal, nil
}
m.refreshACL()
return m, nil
}
func (m model) approveSelectedDomain() (tea.Model, tea.Cmd) {
item, ok := m.trustList.SelectedItem().(trustItem)
if !ok {
return m, nil
}
if err := m.ops.ApproveDomain(item.t.Domain); err != nil {
m.err, m.status = err, ""
} else {
m.status, m.err = "approved "+item.t.Domain, nil
}
m.refreshTrust()
return m, nil
}