internal/httpd/settings.go

0215292cee2c178070124c6c73abf6accee0a210
gitbay/internal/httpd/settings.go history · blame · raw

152 lines · 4945 bytes

  1package httpd
  2
  3import (
  4	"fmt"
  5	"net/http"
  6	"strings"
  7
  8	"gitbay.org/gitbay/internal/control"
  9	"gitbay.org/gitbay/internal/gitutil"
 10	"gitbay.org/gitbay/internal/store"
 11)
 12
 13// Repository settings for repo admins. Every control dispatches the
 14// command the CLI runs; the page only groups them. Destructive lifecycle
 15// — delete and transfer — stays on the CLI, where a typed confirmation
 16// is the norm.
 17
 18type settingsPage struct {
 19	repoPage
 20	Topics      []string
 21	Branches    []gitutil.Ref
 22	DepsEnabled bool
 23	Deps        control.DepsOut
 24	Runners     []store.RepoRunner
 25	Notice      string
 26}
 27
 28func (s *Server) settingsForm(w http.ResponseWriter, r *http.Request, u store.User) {
 29	repo, ok := s.repoForUser(w, r, u, policyCanAdmin)
 30	if !ok {
 31		return
 32	}
 33	p, ok := s.repoFor(w, r, "")
 34	if !ok {
 35		return
 36	}
 37	p.Tab = "settings"
 38	topics, _ := s.st.ListTopics(repo.ID)
 39	branches, _ := gitutil.Refs(p.Dir, "heads")
 40	// The toggle's state comes from the store; what the last run found
 41	// comes from the command, so the page shows the same report the CLI
 42	// prints (#164).
 43	var deps control.DepsOut
 44	s.runControlInto(u, []string{"repo", "deps", "status", repo.Path()}, &deps)
 45	var runners []store.RepoRunner
 46	s.runControlInto(u, []string{"repo", "runner", "list", repo.Path()}, &runners)
 47	s.render(w, "settings.html", settingsPage{
 48		repoPage: p, Topics: topics, Branches: branches,
 49		DepsEnabled: deps.Enabled, Deps: deps,
 50		Runners: runners,
 51		Notice:  s.takeFlash(w, r),
 52	})
 53}
 54
 55func (s *Server) settingsRedirect(w http.ResponseWriter, r *http.Request, msg string) {
 56	dest := fmt.Sprintf("/%s/%s/settings", r.PathValue("owner"), r.PathValue("repo"))
 57	s.setFlash(w, msg)
 58	http.Redirect(w, r, dest, http.StatusSeeOther)
 59}
 60
 61// settingsSubmit routes one form to its command. Keeping the mapping in
 62// one place makes what the page can reach obvious.
 63func (s *Server) settingsSubmit(w http.ResponseWriter, r *http.Request, u store.User) {
 64	repo := r.PathValue("owner") + "/" + r.PathValue("repo")
 65	v := func(k string) string { return strings.TrimSpace(r.FormValue(k)) }
 66
 67	var argv []string
 68	switch r.FormValue("field") {
 69	case "description":
 70		argv = []string{"repo", "settings", "description", repo, v("description")}
 71	case "website":
 72		argv = []string{"repo", "settings", "website", repo, v("website")}
 73	case "visibility":
 74		argv = []string{"repo", "settings", "visibility", repo, v("visibility")}
 75	case "default-branch":
 76		argv = []string{"repo", "settings", "default-branch", repo, v("default-branch")}
 77	case "git-daemon":
 78		argv = []string{"repo", "settings", "git-daemon", repo, onOff(v("git-daemon"))}
 79	case "require-checks":
 80		argv = []string{"repo", "settings", "require-checks", repo, onOff(v("require-checks"))}
 81	case "require-resolved":
 82		argv = []string{"repo", "settings", "require-resolved", repo, onOff(v("require-resolved"))}
 83	case "require-codeowners":
 84		argv = []string{"repo", "settings", "require-codeowners", repo, onOff(v("require-codeowners"))}
 85	case "require-mr":
 86		argv = []string{"repo", "settings", "require-mr", repo, onOff(v("require-mr"))}
 87	case "require-signed":
 88		argv = []string{"repo", "settings", "require-signed", repo, onOff(v("require-signed"))}
 89	case "require-approvals":
 90		argv = []string{"repo", "settings", "require-approvals", repo, v("approvals")}
 91	case "protect":
 92		argv = []string{"repo", "settings", "protect", repo, v("branch")}
 93	case "unprotect":
 94		argv = []string{"repo", "settings", "unprotect", repo, v("branch")}
 95	case "protect-tag":
 96		argv = []string{"repo", "settings", "protect-tag", repo, v("glob")}
 97	case "unprotect-tag":
 98		argv = []string{"repo", "settings", "unprotect-tag", repo, v("glob")}
 99	case "deps":
100		verb := "disable"
101		if v("deps") == "on" {
102			verb = "enable"
103		}
104		argv = []string{"repo", "deps", verb, repo}
105	case "archive":
106		verb := "archive"
107		if v("archive") != "on" {
108			verb = "unarchive"
109		}
110		argv = []string{"repo", verb, repo}
111	case "topics":
112		if add := strings.Fields(v("add")); len(add) > 0 {
113			argv = append([]string{"repo", "topics", "add", repo}, add...)
114		} else if rm := strings.Fields(v("remove")); len(rm) > 0 {
115			argv = append([]string{"repo", "topics", "remove", repo}, rm...)
116		} else {
117			s.settingsRedirect(w, r, "name at least one topic")
118			return
119		}
120	case "runner-add":
121		body := v("key")
122		if body == "" {
123			s.settingsRedirect(w, r, "paste the runner's public key")
124			return
125		}
126		msg, ok := s.runControlStdin(u, []string{"repo", "runner", "add", repo}, body+"\n")
127		if ok {
128			msg = ""
129		}
130		s.settingsRedirect(w, r, msg)
131		return
132	case "runner-remove":
133		argv = []string{"repo", "runner", "remove", repo, v("fingerprint")}
134	default:
135		s.settingsRedirect(w, r, "unknown setting")
136		return
137	}
138
139	_, msg, ok := s.runControl(u, argv)
140	if ok {
141		msg = ""
142	}
143	s.settingsRedirect(w, r, msg)
144}
145
146// onOff normalises a checkbox to the on|off the commands take.
147func onOff(v string) string {
148	if v == "on" || v == "true" {
149		return "on"
150	}
151	return "off"
152}