Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Fold admin work into repository settings and a Users page

9579c1f976254fdeb2ec73d4016db5b25c7921c1 · Keenan Nemetz · 14d ago · parent 381d6aac

Fold admin work into repository settings and a Users page

Remove the combined /admin tabs. Per-repository access rules now live on the repository's Settings page, visible to instance admins while repository details and the danger zone stay with repository admins. User management moves to /users, linked from the top navigation only for instance admins, and repository creation moves to /repos/new. Align the invite, danger-zone, and settings forms with the shared card-and-actions layout, and make settings section headings consistent.

server/web/templates/admin_users.html +43−42

@@ -1,20 +1,14 @@
1 1 {{define "admin_users"}}
2 2 {{template "header" .}}
3 {{template "adminhead" .}}
3 <div class="page-head">
4 <div class="page-head-main">
5 <h1>Users</h1>
6 <p class="page-sub">Accounts, enrollment links, keys, and sessions. New accounts start with an enrollment link, never a pasted SSH key.</p>
7 </div>
8 </div>
4 9
5 10 {{if .Error}}<div class="flash bad">{{.Error}}</div>{{end}}
6 11
7 <div class="card form-card">
8 <h2>Create an account</h2>
9 <form method="post" action="/admin/users" class="form-block">
10 <input type="hidden" name="csrf" value="{{.CSRF}}">
11 <label>Account name
12 <input type="text" name="name" required maxlength="63" autocomplete="off" placeholder="lowercase-name">
13 </label>
14 <div class="form-actions"><button type="submit">{{icon "user"}}Create invite</button></div>
15 </form>
16 </div>
17
18 12 <div class="panel">
19 13 <table>
20 14 <thead><tr><th>Account</th><th>Status</th><th>Role</th><th>Keys</th><th>Sessions</th><th></th></tr></thead>
@@ -22,25 +16,35 @@
22 16 {{range .Users}}
23 17 <tr>
24 18 <td>
25 <a href="/admin/users/{{.Account.Name}}">{{.Account.Name}}</a>
19 <a href="/users/{{.Account.Name}}">{{.Account.Name}}</a>
26 20 </td>
27 21 <td><span class="state {{.Account.Status}}">{{.Account.Status}}</span></td>
28 22 <td>{{if .Account.Admin}}<span class="label">instance admin</span>{{else}}<span class="muted">account</span>{{end}}</td>
29 23 <td>{{len .Account.Keys}}</td>
30 24 <td>{{.Sessions}}</td>
31 <td><a href="/admin/users/{{.Account.Name}}">Manage</a></td>
25 <td><a href="/users/{{.Account.Name}}">Manage</a></td>
32 26 </tr>
33 27 {{end}}
34 28 </tbody>
35 29 </table>
36 30 </div>
31
32 <h2>Create an account</h2>
33 <form method="post" action="/users" class="form-block">
34 <input type="hidden" name="csrf" value="{{.CSRF}}">
35 <div class="card form-card">
36 <label>Account name
37 <input type="text" name="name" required maxlength="63" autocomplete="off" placeholder="lowercase-name">
38 </label>
39 </div>
40 <div class="form-actions"><button type="submit">{{icon "user"}}Create invite</button></div>
41 </form>
37 42 {{template "footer" .}}
38 43 {{end}}
39 44
40 45 {{define "admin_user"}}
41 46 {{template "header" .}}
42 {{template "adminhead" .}}
43 <div class="page-head subhead">
47 <div class="page-head">
44 48 <div class="page-head-main">
45 49 <h1>{{.Account.Name}}</h1>
46 50 <p class="page-sub">
@@ -49,14 +53,8 @@
49 53 · created {{since .Account.Created.Unix}}
50 54 </p>
51 55 </div>
52 </div>
53
54 {{if .Error}}<div class="flash bad">{{.Error}}</div>{{end}}
55
56 <div class="card form-card">
57 <h2>Access and enrollment</h2>
58 <div class="form-actions">
59 <form class="inline" method="post" action="/admin/users/{{.Account.Name}}/admin">
56 <div class="page-head-actions">
57 <form class="inline" method="post" action="/users/{{.Account.Name}}/admin">
60 58 <input type="hidden" name="csrf" value="{{.CSRF}}">
61 59 {{if .Account.Admin}}
62 60 <input type="hidden" name="admin" value="false">
@@ -66,19 +64,15 @@
66 64 <button type="submit" class="secondary">{{icon "user"}}Grant instance admin</button>
67 65 {{end}}
68 66 </form>
69 <form class="inline" method="post" action="/admin/users/{{.Account.Name}}/invite">
67 <form class="inline" method="post" action="/users/{{.Account.Name}}/invite">
70 68 <input type="hidden" name="csrf" value="{{.CSRF}}">
71 69 <button type="submit" class="secondary">{{icon "external-link"}}New enrollment link</button>
72 70 </form>
73 {{if .Sessions}}
74 <form class="inline" method="post" action="/admin/users/{{.Account.Name}}/sessions/revoke">
75 <input type="hidden" name="csrf" value="{{.CSRF}}">
76 <button type="submit" class="warn">{{icon "log-out"}}Revoke all sessions</button>
77 </form>
78 {{end}}
79 71 </div>
80 72 </div>
81 73
74 {{if .Error}}<div class="flash bad">{{.Error}}</div>{{end}}
75
82 76 <h2>SSH keys</h2>
83 77 {{if .Account.Keys}}
84 78 <div class="panel">
@@ -91,7 +85,7 @@
91 85 <td>{{.Type}}</td>
92 86 <td class="muted">{{.Comment}}</td>
93 87 <td>
94 <form class="inline" method="post" action="/admin/users/{{$.Account.Name}}/keys/remove">
88 <form class="inline" method="post" action="/users/{{$.Account.Name}}/keys/remove">
95 89 <input type="hidden" name="csrf" value="{{$.CSRF}}">
96 90 <input type="hidden" name="fingerprint" value="{{.Fingerprint}}">
97 91 <button type="submit" class="danger">Remove</button>
@@ -106,7 +100,15 @@
106 100 <p class="muted">No SSH keys. This account can sign in with a passkey only.</p>
107 101 {{end}}
108 102
109 <h2>Sessions</h2>
103 <div class="with-more">
104 <h2>Sessions</h2>
105 {{if .Sessions}}
106 <form class="inline" method="post" action="/users/{{.Account.Name}}/sessions/revoke">
107 <input type="hidden" name="csrf" value="{{.CSRF}}">
108 <button type="submit" class="link">Revoke all sessions</button>
109 </form>
110 {{end}}
111 </div>
110 112 {{if .Sessions}}
111 113 <div class="panel">
112 114 <table>
@@ -128,24 +130,23 @@
128 130 {{end}}
129 131
130 132 <h2>Danger zone</h2>
131 <div class="card danger-zone">
132 <form method="post" action="/admin/users/{{.Account.Name}}/retire" class="form-block">
133 <input type="hidden" name="csrf" value="{{.CSRF}}">
133 <form method="post" action="/users/{{.Account.Name}}/retire" class="form-block">
134 <input type="hidden" name="csrf" value="{{.CSRF}}">
135 <div class="card form-card danger-zone">
134 136 <h3>Retire account</h3>
135 137 <p class="muted">Disables the account, ends its sessions, and closes any enrollment links. The name is never reissued.</p>
136 138 <label>Type <code>{{.Account.Name}}</code> to confirm
137 139 <input type="text" name="confirm" required autocomplete="off">
138 140 </label>
139 <div class="form-actions"><button type="submit" class="danger">{{icon "circle-x"}}Retire account</button></div>
140 </form>
141 </div>
141 </div>
142 <div class="form-actions"><button type="submit" class="danger">{{icon "circle-x"}}Retire account</button></div>
143 </form>
142 144 {{template "footer" .}}
143 145 {{end}}
144 146
145 147 {{define "admin_invite"}}
146 148 {{template "header" .}}
147 {{template "adminhead" .}}
148 <div class="page-head subhead">
149 <div class="page-head">
149 150 <div class="page-head-main">
150 151 <h1>Enrollment link</h1>
151 152 <p class="page-sub">For <strong>{{.Account.Name}}</strong> · expires {{.Expires.UTC.Format "2006-01-02 15:04 UTC"}}</p>
@@ -163,7 +164,7 @@
163 164 </div>
164 165
165 166 <div class="form-actions">
166 <a class="button secondary" href="/admin/users/{{.Account.Name}}">{{icon "user"}}Back to account</a>
167 <a class="button secondary" href="/users/{{.Account.Name}}">{{icon "user"}}Back to account</a>
167 168 </div>
168 169 {{template "footer" .}}
169 170 {{end}}