internal/web/templates/account.html
239 lines · 13430 bytes
1{{define "width"}}wide{{end}}
2{{define "title"}}account settings{{end}}
3{{define "content"}}
4<h1>Account settings</h1>
5{{if .Notice}}<p class="error" role="alert">{{.Notice}}</p>{{end}}
6{{if .Message}}<p class="notice" role="status">{{.Message}}</p>{{end}}
7
8<div class="withcol narrow">
9<nav class="sidecol" aria-label="Sections">
10<details class="sidedrop">
11<summary><span class="sideword">Sections</span></summary>
12 <div class="grp"><h2 class="colhead">Sections</h2>
13 <ul>
14 <li><a href="#profile">Profile</a></li>
15 <li><a href="#keys">SSH keys</a></li>
16 <li><a href="#emails">Email addresses</a></li>
17 <li><a href="#pgp">OpenPGP keys</a></li>
18 <li><a href="#notifications">Notifications</a></li>
19 <li><a href="#appearance">Appearance</a></li>
20 <li><a href="#export">Export</a></li>
21 <li><a href="#tokens">API tokens</a></li>
22 <li><a href="#cli">On the command line</a></li>
23 </ul></div>
24</details>
25</nav>
26<div class="colmain">
27<section id="profile"><h2>Profile</h2>
28<p class="meta">Shown on your profile page, <a href="/{{.Viewer}}">/{{.Viewer}}</a>.</p>
29<form method="post" action="/settings" class="setform stack">
30 <input type="hidden" name="field" value="profile">
31 <label for="p-description">Description</label>
32 <input type="text" id="p-description" name="description" value="{{.Profile.Description}}" placeholder="one line, shown in listings">
33 <label for="p-website">Website</label>
34 <input type="text" id="p-website" name="website" value="{{.Profile.Website}}" placeholder="https://example.org">
35 <label for="p-links">Links</label>
36 <textarea id="p-links" name="links" rows="3" placeholder="one per line: label|https://… or a bare https://… (at most 5)">{{.LinksText}}</textarea>
37 <span class="btngroup"><button type="submit" class="btn">Save profile</button></span>
38</form>
39<h3>About</h3>
40<p class="meta">The longer text below your repositories is a file:
41<code>profile/README.md</code> in <a href="/{{.AboutRepo}}">{{.AboutRepo}}</a>.
42Write it with a push, or edit it here.</p>
43{{if .AboutEdit}}<p><a class="btn" href="{{.AboutEdit}}">Edit {{.Profile.AboutPath}}</a></p>
44{{else}}<form method="post" action="/settings" class="setform">
45 <input type="hidden" name="field" value="profile-repo">
46 <button type="submit" class="btn">Create {{.AboutRepo}}</button>
47</form>{{end}}
48</section>
49
50<section id="keys"><h2>SSH keys</h2>
51<p class="meta">Your keys are your identity here. A <code>full</code> key can run
52commands and push; a <code>git</code> key can only move git data (e.g., CI).</p>
53{{if .Keys}}<div class="tablewrap"><table class="keys nowrap">
54<tr class="cols"><th scope="col">label</th><th scope="col">fingerprint</th><th scope="col">type</th><th scope="col">scope</th><th scope="col"><span class="vh">actions</span></th></tr>
55{{range .Keys}}<tr>
56 <td>{{.Label}}</td>
57 <td class="mono">{{.Fingerprint}}</td>
58 <td>{{.Algo}}</td>
59 <td>{{.Scope}}</td>
60 <td class="act"><form method="post" action="/settings"><input type="hidden" name="field" value="key-remove"><input type="hidden" name="fingerprint" value="{{.Fingerprint}}">{{template "confirmfield" .Confirm}} <button type="submit" class="danger">Remove</button></form></td>
61</tr>
62{{end}}</table></div>
63{{else}}<p class="none">No SSH keys? Which cannot be right, since you signed in.</p>{{end}}
64<details class="editbox">
65 <summary>Add an SSH key</summary>
66 <form method="post" action="/settings" class="setform stack">
67 <input type="hidden" name="field" value="key-add">
68 <label for="key">Public key</label>
69 <textarea id="key" name="key" rows="3" required spellcheck="false" placeholder="ssh-ed25519 AAAA… you@machine"></textarea>
70 <label for="key-label">Label</label>
71 <input id="key-label" name="label" maxlength="64" placeholder="defaults to the key's comment">
72 <label for="scope">Scope</label>
73 <select id="scope" name="scope">
74 <option value="full">full: commands and git</option>
75 <option value="git">git: git transport only</option>
76 </select>
77 <button type="submit" class="btn">Add key</button>
78 </form>
79</details>
80</section>
81
82<section id="emails"><h2>Email addresses</h2>
83<p class="meta">A verified address ties signed commits to this
84account and where notifications go.</p>
85{{if .Emails}}<ul class="plain">
86{{range .Emails}}<li>{{.Address}}
87 {{if .Primary}}<span class="chip">primary</span>{{end}}
88 {{if .Verified}}<span class="badge badge-verified">verified{{with .VerifiedBy}} · {{.}}{{end}}</span>
89 {{else}}<span class="badge badge-unsigned">unverified</span>{{end}}
90 {{if not .Primary}}{{if .Verified}}<form method="post" action="/settings" class="inline"><input type="hidden" name="field" value="email-primary"><input type="hidden" name="address" value="{{.Address}}"><button type="submit" class="linklike">Make primary</button></form>{{end}}
91 <form method="post" action="/settings" class="inline"><input type="hidden" name="field" value="email-remove"><input type="hidden" name="address" value="{{.Address}}">{{template "confirmfield" .Address}} <button type="submit" class="danger">Remove</button></form>{{end}}</li>
92{{end}}</ul>{{end}}
93<details class="editbox">
94 <summary>Add an address</summary>
95 <form method="post" action="/settings" class="setform">
96 <input type="hidden" name="field" value="email-add">
97 <label for="address">Address</label>
98 <input type="email" id="address" name="address" required autocomplete="email" placeholder="you@example.org">
99 <button type="submit" class="btn">Send code</button>
100 </form>
101 <form method="post" action="/settings" class="setform">
102 <input type="hidden" name="field" value="email-verify">
103 <label for="code">Verification code</label>
104 <input type="text" id="code" name="code" required autocomplete="one-time-code" autocapitalize="none" spellcheck="false" placeholder="from the mail">
105 <button type="submit" class="btn">Verify</button>
106 </form>
107</details>
108</section>
109
110<section id="pgp"><h2>OpenPGP keys</h2>
111<p class="meta">Only needed if you sign commits with GPG. SSH signing
112(<code>gpg.format = ssh</code>) uses the keys above.</p>
113{{if .PGP}}<div class="tablewrap"><table class="keys">
114<tr class="cols"><th scope="col">fingerprint</th><th scope="col">identities</th><th scope="col"><span class="vh">actions</span></th></tr>
115{{range .PGP}}<tr>
116 <td class="mono">{{.Fingerprint}}</td>
117 <td>{{range .UIDs}}{{.}}<br>{{end}}{{if .Revoked}}<span class="badge badge-bad_signature">revoked</span>{{else if .Expired}}<span class="badge badge-signed_key_expired">expired</span>{{end}}</td>
118 <td class="act"><form method="post" action="/settings"><input type="hidden" name="field" value="pgp-remove"><input type="hidden" name="fingerprint" value="{{.Fingerprint}}">{{template "confirmfield" .Confirm}} <button type="submit" class="danger">Remove</button></form></td>
119</tr>
120{{end}}</table></div>
121{{else}}<p class="none">No OpenPGP keys.</p>{{end}}
122<details class="editbox">
123 <summary>Add a PGP key</summary>
124 <form method="post" action="/settings" class="setform stack">
125 <input type="hidden" name="field" value="pgp-add">
126 <label for="pgpkey">Armored public key</label>
127 <textarea id="pgpkey" name="key" rows="6" required placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----"></textarea>
128 <button type="submit" class="btn">Add key</button>
129 </form>
130</details>
131</section>
132
133<section id="notifications"><h2>Notifications</h2>
134<form method="post" action="/settings" class="setform">
135 <input type="hidden" name="field" value="notify-mail">
136 <label for="notify-mail">Activity by mail</label>
137 <div class="check"><input type="checkbox" id="notify-mail" name="mail" value="on"{{if .MailOn}} checked{{end}}></div>
138 <button type="submit" class="btn">Save</button>
139</form>
140<p class="meta">Enable to receive activity alerts by email. Login links will arrive regardless of this setting.</p>
141<form method="post" action="/settings" class="setform">
142 <input type="hidden" name="field" value="notify-watch">
143 <label for="notify-watch">Watch repositories you can write to</label>
144 <div class="check"><input type="checkbox" id="notify-watch" name="watch" value="on"{{if .WatchOn}} checked{{end}}></div>
145 <button type="submit" class="btn">Save</button>
146</form>
147<p class="meta">Alerts for every issue and merge request on those repositories. A watch or mute on a repository has priority over this setting.</p>
148<form method="post" action="/settings" class="setform">
149 <input type="hidden" name="field" value="notify-push">
150 <label for="notify-push">Activity on your registered devices</label>
151 <div class="check"><input type="checkbox" id="notify-push" name="push" value="on"{{if .PushOn}} checked{{end}}></div>
152 <button type="submit" class="btn">Save</button>
153</form>
154<p class="meta">Notification text is sent in full, including for private repositories, so a repository name and item number reach Apple and appear on a lock screen.</p>
155<h3>Devices</h3>
156{{if .Devices}}<div class="tablewrap"><table class="keys nowrap">
157<tr class="cols"><th scope="col">id</th><th scope="col">label</th><th scope="col">token</th><th scope="col">last seen</th><th scope="col"><span class="vh">actions</span></th></tr>
158{{range .Devices}}<tr>
159 <td class="mono">{{.ID}}</td>
160 <td>{{.Label}}</td>
161 <td class="mono">{{.Token}}</td>
162 <td>{{if .LastSeenAt}}{{when .LastSeenAt}}{{else}}never{{end}}</td>
163 <td class="act"><form method="post" action="/settings"><input type="hidden" name="field" value="device-remove"><input type="hidden" name="id" value="{{.ID}}">{{template "confirmfield" .Confirm}} <button type="submit" class="danger">Remove</button></form></td>
164</tr>
165{{end}}</table></div>
166{{else}}<p class="none">No registered devices.</p>{{end}}
167<p class="meta">Devices register themselves from the iOS app; there is no
168form here to add one, since a browser cannot produce an APNs token.</p>
169</section>
170
171<section id="appearance"><h2>Appearance</h2>
172<form method="post" action="/settings" class="setform">
173 <input type="hidden" name="field" value="theme">
174 <label for="theme">Colour scheme</label>
175 <select id="theme" name="theme">
176 <option value="system"{{if eq .ThemeSetting "system"}} selected{{end}}>follow the browser</option>
177 <option value="light"{{if eq .ThemeSetting "light"}} selected{{end}}>light</option>
178 <option value="dark"{{if eq .ThemeSetting "dark"}} selected{{end}}>dark</option>
179 </select>
180 <button type="submit" class="btn">Save</button>
181</form>
182<p class="meta">The same setting as <code>gitbay web theme set</code>. Following the browser takes its light or dark preference.</p>
183</section>
184
185<section id="export"><h2>Export</h2>
186<p class="meta">Your profile, repositories, issues and merge requests as one
187JSON bundle, the same one <code>gitbay auth export</code> writes. Keys are
188never included; a replayed bundle's emails arrive unverified.</p>
189<p><a href="/settings/export">Download bundle</a></p>
190</section>
191
192<section id="tokens"><h2>API tokens</h2>
193<p class="meta">A token signs in the iOS app, or a script, without your
194password. A phone app needs full scope to comment and merge; full scope
195on an admin account can administer the instance, so give a token the
196narrowest scope and shortest lifetime the job needs.</p>
197{{if .TokenShown}}<p class="notice" role="status">Token created. It is shown once; store it now.</p>
198<pre class="message" tabindex="0">{{.TokenShown}}</pre>{{end}}
199{{if .Tokens}}<div class="tablewrap"><table class="keys nowrap">
200<tr class="cols"><th scope="col">name</th><th scope="col">scope</th><th scope="col">created</th><th scope="col">expires</th><th scope="col">last used</th><th scope="col"><span class="vh">actions</span></th></tr>
201{{range .Tokens}}<tr>
202 <td>{{.Name}}</td>
203 <td>{{.Scope}}</td>
204 <td>{{when .Created}}</td>
205 <td>{{.Expires}}</td>
206 <td>{{.LastUsed}}</td>
207 <td class="act"><form method="post" action="/settings"><input type="hidden" name="field" value="token-revoke"><input type="hidden" name="name" value="{{.Name}}">{{template "confirmfield" .Name}} <button type="submit" class="danger">Revoke</button></form></td>
208</tr>
209{{end}}</table></div>
210{{else}}<p class="none">No API tokens.</p>{{end}}
211<details class="editbox">
212 <summary>Create a token</summary>
213 <form method="post" action="/settings" class="setform stack">
214 <input type="hidden" name="field" value="token-create">
215 <label for="token-name">Name</label>
216 <input id="token-name" name="name" required autocomplete="off" placeholder="e.g. iphone">
217 <label for="token-scope">Scope</label>
218 <select id="token-scope" name="scope">
219 <option value="read" selected>read: read-only commands</option>
220 <option value="full">full: everything your account can do</option>
221 </select>
222 <label for="token-ttl">Expires after</label>
223 <input id="token-ttl" name="ttl" autocomplete="off" placeholder="e.g. 30d or 720h; empty never expires">
224 <button type="submit" class="btn">Create token</button>
225 </form>
226</details>
227</section>
228
229<section id="cli"><h2>On the command line</h2>
230<p class="meta">No page here yet, and nothing refusing one.</p>
231<pre class="message" tabindex="0">gitbay web sessions list # browser sessions
232gitbay admin ... # instance administration</pre>
233<p class="meta">All of it works from stock OpenSSH too, with the CLI's
234grouping words dropped: <code>ssh git@{{.Host}} whoami</code>,
235<code>ssh git@{{.Host}} web sessions list</code>.</p>
236</section>
237</div>
238</div>
239{{end}}