1package archesrv
2
3import (
4 "fmt"
5 "net/http"
6 "strings"
7
8 "arche/internal/repo"
9)
10
11type adminUsersData struct {
12 User *User
13 Users []User
14}
15
16func (s *forgeServer) handleAdminUsers(w http.ResponseWriter, r *http.Request) {
17 user := s.db.currentUser(r)
18 if user == nil || !user.IsAdmin {
19 http.Error(w, "forbidden", http.StatusForbidden)
20 return
21 }
22 users, err := s.db.ListUsers()
23 if err != nil {
24 http.Error(w, "list users: "+err.Error(), http.StatusInternalServerError)
25 return
26 }
27 s.render(w, "srv_admin_users.html", adminUsersData{User: user, Users: users})
28}
29
30func (s *forgeServer) handleAdminCreateUser(w http.ResponseWriter, r *http.Request) {
31 user := s.db.currentUser(r)
32 if user == nil || !user.IsAdmin {
33 http.Error(w, "forbidden", http.StatusForbidden)
34 return
35 }
36 r.ParseForm() //nolint:errcheck
37 username := strings.TrimSpace(r.FormValue("username"))
38 password := r.FormValue("password")
39 isAdmin := r.FormValue("is_admin") == "1"
40 if username == "" || password == "" {
41 http.Error(w, "username and password required", http.StatusBadRequest)
42 return
43 }
44 if _, err := s.db.CreateUser(username, password, isAdmin); err != nil {
45 http.Error(w, "create user: "+err.Error(), http.StatusInternalServerError)
46 return
47 }
48 s.log.Info("user created", "by", user.Username, "new_user", username, "is_admin", isAdmin)
49 http.Redirect(w, r, "/admin/users", http.StatusFound)
50}
51
52func (s *forgeServer) handleAdminDeleteUser(w http.ResponseWriter, r *http.Request) {
53 user := s.db.currentUser(r)
54 if user == nil || !user.IsAdmin {
55 http.Error(w, "forbidden", http.StatusForbidden)
56 return
57 }
58 idStr := r.PathValue("id")
59 var targetID int64
60 if _, err := fmt.Sscan(idStr, &targetID); err != nil {
61 http.Error(w, "invalid user id", http.StatusBadRequest)
62 return
63 }
64 if targetID == user.ID {
65 http.Error(w, "cannot delete your own account", http.StatusBadRequest)
66 return
67 }
68 if err := s.db.DeleteUser(targetID); err != nil {
69 http.Error(w, "delete user: "+err.Error(), http.StatusInternalServerError)
70 return
71 }
72 s.log.Info("user deleted", "by", user.Username, "target_id", targetID)
73 w.WriteHeader(http.StatusNoContent)
74}
75
76func (s *forgeServer) handleAdminCreateRepo(w http.ResponseWriter, r *http.Request) {
77 user := s.db.currentUser(r)
78 if user == nil || !user.IsAdmin {
79 http.Error(w, "forbidden", http.StatusForbidden)
80 return
81 }
82
83 r.ParseForm() //nolint:errcheck
84 name := strings.TrimSpace(r.FormValue("name"))
85 desc := r.FormValue("description")
86 vis := r.FormValue("visibility")
87
88 if name == "" {
89 http.Error(w, "name required", http.StatusBadRequest)
90 return
91 }
92
93 rec, err := s.db.CreateRepo(name, desc, vis)
94 if err != nil {
95 http.Error(w, "create repo: "+err.Error(), http.StatusInternalServerError)
96 return
97 }
98
99 path := repoPath(s.dataDir(), name)
100 if _, err := repo.Init(path); err != nil {
101 s.db.DeleteRepo(name) //nolint:errcheck
102 http.Error(w, "init repo: "+err.Error(), http.StatusInternalServerError)
103 return
104 }
105
106 s.db.SetPermission(rec.ID, user.ID, "admin") //nolint:errcheck
107 s.log.Info("repo created", "by", user.Username, "repo", rec.Name, "visibility", vis)
108 fmt.Fprintf(w, `{"name":%q}`, rec.Name)
109}
110
111func (s *forgeServer) handleAdminDeleteRepo(w http.ResponseWriter, r *http.Request) {
112 user := s.db.currentUser(r)
113 if user == nil || !user.IsAdmin {
114 http.Error(w, "forbidden", http.StatusForbidden)
115 return
116 }
117 name := r.PathValue("name")
118 if err := s.db.DeleteRepo(name); err != nil {
119 http.Error(w, "delete repo: "+err.Error(), http.StatusInternalServerError)
120 return
121 }
122 s.log.Info("repo deleted", "by", user.Username, "repo", name)
123 w.WriteHeader(http.StatusNoContent)
124}
125
126type srvAdminInvitesData struct {
127 User *User
128 Invites []InviteToken
129 Link string
130}
131
132func (s *forgeServer) handleAdminInvites(w http.ResponseWriter, r *http.Request) {
133 user := s.db.currentUser(r)
134 if user == nil || !user.IsAdmin {
135 http.Error(w, "forbidden", http.StatusForbidden)
136 return
137 }
138 invites, err := s.db.ListInvites(user.ID)
139 if err != nil {
140 http.Error(w, "list invites: "+err.Error(), http.StatusInternalServerError)
141 return
142 }
143 s.render(w, "srv_admin_invites.html", srvAdminInvitesData{User: user, Invites: invites})
144}
145
146func (s *forgeServer) handleAdminCreateInvite(w http.ResponseWriter, r *http.Request) {
147 user := s.db.currentUser(r)
148 if user == nil || !user.IsAdmin {
149 http.Error(w, "forbidden", http.StatusForbidden)
150 return
151 }
152 inv, err := s.db.CreateInvite(user.ID)
153 if err != nil {
154 http.Error(w, "create invite: "+err.Error(), http.StatusInternalServerError)
155 return
156 }
157 invites, _ := s.db.ListInvites(user.ID)
158 s.render(w, "srv_admin_invites.html", srvAdminInvitesData{
159 User: user,
160 Invites: invites,
161 Link: "/register?invite=" + inv.Token,
162 })
163}
164
165func (s *forgeServer) handleAdminDeleteInvite(w http.ResponseWriter, r *http.Request) {
166 user := s.db.currentUser(r)
167 if user == nil || !user.IsAdmin {
168 http.Error(w, "forbidden", http.StatusForbidden)
169 return
170 }
171 var id int64
172 if _, err := fmt.Sscan(r.PathValue("id"), &id); err != nil {
173 http.Error(w, "invalid id", http.StatusBadRequest)
174 return
175 }
176 if err := s.db.DeleteInvite(id, user.ID); err != nil {
177 http.Error(w, "delete invite: "+err.Error(), http.StatusInternalServerError)
178 return
179 }
180 w.WriteHeader(http.StatusNoContent)
181}