internal/web/templates/account.html
267 lines · 15469 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}}{{if .Reauth}} <a href="/login">Sign in again</a>{{end}}</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 <li><a href="#delete">Delete account</a></li>
24 </ul></div>
25</details>
26</nav>
27<div class="colmain">
28<section id="profile"><h2>Profile</h2>
29<p class="meta">Shown on your profile page, <a href="/{{.Viewer}}">/{{.Viewer}}</a>.</p>
30<form method="post" action="/settings" class="setform stack">
31 <input type="hidden" name="field" value="profile">
32 <label for="p-description">Description</label>
33 <input type="text" id="p-description" name="description" value="{{.Profile.Description}}" placeholder="one line, shown in listings">
34 <label for="p-website">Website</label>
35 <input type="text" id="p-website" name="website" value="{{.Profile.Website}}" placeholder="https://example.org">
36 <label for="p-links">Links</label>
37 <textarea id="p-links" name="links" rows="3" placeholder="one per line: label|https://… or a bare https://… (at most 5)">{{.LinksText}}</textarea>
38 <span class="btngroup"><button type="submit" class="btn">Save profile</button></span>
39</form>
40<h3>About</h3>
41<p class="meta">The longer text below your repositories is a file:
42<code>profile/README.md</code> in <a href="/{{.AboutRepo}}">{{.AboutRepo}}</a>.
43Write it with a push, or edit it here.</p>
44{{if .AboutEdit}}<p><a class="btn" href="{{.AboutEdit}}">Edit {{.Profile.AboutPath}}</a></p>
45{{else}}<form method="post" action="/settings" class="setform">
46 <input type="hidden" name="field" value="profile-repo">
47 <button type="submit" class="btn">Create {{.AboutRepo}}</button>
48</form>{{end}}
49</section>
50
51<section id="keys"><h2>SSH keys</h2>
52<p class="meta">Your keys are your identity here. A <code>full</code> key can run
53commands and push; a <code>git</code> key can only move git data (e.g., CI).</p>
54{{if .Keys}}<div class="tablewrap"><table class="keys nowrap">
55<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>
56{{range .Keys}}<tr>
57 <td>{{.Label}}</td>
58 <td class="mono">{{.Fingerprint}}</td>
59 <td>{{.Algo}}</td>
60 <td>{{.Scope}}</td>
61 <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>
62</tr>
63{{end}}</table></div>
64{{else}}<p class="none">No SSH keys? Which cannot be right, since you signed in.</p>{{end}}
65<details class="editbox">
66 <summary>Add an SSH key</summary>
67 <form method="post" action="/settings" class="setform stack">
68 <input type="hidden" name="field" value="key-add">
69 <label for="key">Public key</label>
70 <textarea id="key" name="key" rows="3" required spellcheck="false" placeholder="ssh-ed25519 AAAA… you@machine"></textarea>
71 <label for="key-label">Label</label>
72 <input id="key-label" name="label" maxlength="64" placeholder="defaults to the key's comment">
73 <label for="scope">Scope</label>
74 <select id="scope" name="scope">
75 <option value="full">full: commands and git</option>
76 <option value="git">git: git transport only</option>
77 </select>
78 <button type="submit" class="btn">Add key</button>
79 </form>
80</details>
81</section>
82
83<section id="emails"><h2>Email addresses</h2>
84<p class="meta">A verified address ties signed commits to this
85account and where notifications go.</p>
86{{if .Emails}}<ul class="plain">
87{{range .Emails}}<li>{{.Address}}
88 {{if .Primary}}<span class="chip">primary</span>{{end}}
89 {{if .Verified}}<span class="badge badge-verified">verified{{with .VerifiedBy}} · {{.}}{{end}}</span>
90 {{else}}<span class="badge badge-unsigned">unverified</span>{{end}}
91 {{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}}
92 <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>
93{{end}}</ul>{{end}}
94<details class="editbox">
95 <summary>Add an address</summary>
96 <form method="post" action="/settings" class="setform">
97 <input type="hidden" name="field" value="email-add">
98 <label for="address">Address</label>
99 <input type="email" id="address" name="address" required autocomplete="email" placeholder="you@example.org">
100 <button type="submit" class="btn">Send code</button>
101 </form>
102 <form method="post" action="/settings" class="setform">
103 <input type="hidden" name="field" value="email-verify">
104 <label for="code">Verification code</label>
105 <input type="text" id="code" name="code" required autocomplete="one-time-code" autocapitalize="none" spellcheck="false" placeholder="from the mail">
106 <button type="submit" class="btn">Verify</button>
107 </form>
108</details>
109</section>
110
111<section id="pgp"><h2>OpenPGP keys</h2>
112<p class="meta">Only needed if you sign commits with GPG. SSH signing
113(<code>gpg.format = ssh</code>) uses the keys above.</p>
114{{if .PGP}}<div class="tablewrap"><table class="keys">
115<tr class="cols"><th scope="col">fingerprint</th><th scope="col">identities</th><th scope="col"><span class="vh">actions</span></th></tr>
116{{range .PGP}}<tr>
117 <td class="mono">{{.Fingerprint}}</td>
118 <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>
119 <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>
120</tr>
121{{end}}</table></div>
122{{else}}<p class="none">No OpenPGP keys.</p>{{end}}
123<details class="editbox">
124 <summary>Add a PGP key</summary>
125 <form method="post" action="/settings" class="setform stack">
126 <input type="hidden" name="field" value="pgp-add">
127 <label for="pgpkey">Armored public key</label>
128 <textarea id="pgpkey" name="key" rows="6" required placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----"></textarea>
129 <button type="submit" class="btn">Add key</button>
130 </form>
131</details>
132</section>
133
134<section id="notifications"><h2>Notifications</h2>
135<form method="post" action="/settings" class="setform">
136 <input type="hidden" name="field" value="notify-mail">
137 <label for="notify-mail">Activity by mail</label>
138 <div class="check"><input type="checkbox" id="notify-mail" name="mail" value="on"{{if .MailOn}} checked{{end}}></div>
139 <button type="submit" class="btn">Save</button>
140</form>
141<p class="meta">Enable to receive activity alerts by email. Login links will arrive regardless of this setting.</p>
142{{if .ReplyOffered}}<form method="post" action="/settings" class="setform">
143 <input type="hidden" name="field" value="notify-reply">
144 <label for="notify-reply">Reply to mail to comment</label>
145 <div class="check"><input type="checkbox" id="notify-reply" name="reply" value="on"{{if .ReplyOn}} checked{{end}}></div>
146 <button type="submit" class="btn">Save</button>
147</form>
148<p class="meta">Issue and merge request mail gets a reply address. A reply posts a comment as you when it comes from one of your verified addresses; quoted text and signatures are removed.</p>
149{{end}}<form method="post" action="/settings" class="setform">
150 <input type="hidden" name="field" value="notify-watch">
151 <label for="notify-watch">Watch repositories you can write to</label>
152 <div class="check"><input type="checkbox" id="notify-watch" name="watch" value="on"{{if .WatchOn}} checked{{end}}></div>
153 <button type="submit" class="btn">Save</button>
154</form>
155<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>
156<form method="post" action="/settings" class="setform">
157 <input type="hidden" name="field" value="notify-push">
158 <label for="notify-push">Activity on your registered devices</label>
159 <div class="check"><input type="checkbox" id="notify-push" name="push" value="on"{{if .PushOn}} checked{{end}}></div>
160 <button type="submit" class="btn">Save</button>
161</form>
162<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>
163<h3>Devices</h3>
164{{if .Devices}}<div class="tablewrap"><table class="keys nowrap">
165<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>
166{{range .Devices}}<tr>
167 <td class="mono">{{.ID}}</td>
168 <td>{{.Label}}</td>
169 <td class="mono">{{.Token}}</td>
170 <td>{{if .LastSeenAt}}{{when .LastSeenAt}}{{else}}never{{end}}</td>
171 <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>
172</tr>
173{{end}}</table></div>
174{{else}}<p class="none">No registered devices.</p>{{end}}
175<p class="meta">Devices register themselves from the iOS app; there is no
176form here to add one, since a browser cannot produce an APNs token.</p>
177</section>
178
179<section id="appearance"><h2>Appearance</h2>
180<form method="post" action="/settings" class="setform">
181 <input type="hidden" name="field" value="theme">
182 <label for="theme">Colour scheme</label>
183 <select id="theme" name="theme">
184 <option value="system"{{if eq .ThemeSetting "system"}} selected{{end}}>follow the browser</option>
185 <option value="light"{{if eq .ThemeSetting "light"}} selected{{end}}>light</option>
186 <option value="dark"{{if eq .ThemeSetting "dark"}} selected{{end}}>dark</option>
187 </select>
188 <button type="submit" class="btn">Save</button>
189</form>
190<p class="meta">The same setting as <code>gitbay web theme set</code>. Following the browser takes its light or dark preference.</p>
191<form method="post" action="/settings" class="setform">
192 <input type="hidden" name="field" value="diff-layout">
193 <label for="layout">Diff layout</label>
194 <select id="layout" name="layout">
195 <option value="unified"{{if ne .DiffSetting "split"}} selected{{end}}>unified</option>
196 <option value="split"{{if eq .DiffSetting "split"}} selected{{end}}>side by side</option>
197 </select>
198 <button type="submit" class="btn">Save</button>
199</form>
200<p class="meta">The same setting as <code>gitbay web diff set</code>. A <code>?layout=split</code> or <code>?layout=unified</code> on a diff page overrides it for that request. Narrow windows always show a unified diff.</p>
201</section>
202
203<section id="export"><h2>Export</h2>
204<p class="meta">Your profile, repositories, issues and merge requests as one
205JSON bundle, the same one <code>gitbay auth export</code> writes. Keys are
206never included; a replayed bundle's emails arrive unverified.</p>
207<p><a href="/settings/export">Download bundle</a></p>
208</section>
209
210<section id="tokens"><h2>API tokens</h2>
211<p class="meta">A token signs in the iOS app, or a script, without your
212password. A phone app needs full scope to comment and merge; full scope
213on an admin account can administer the instance, so give a token the
214narrowest scope and shortest lifetime the job needs.</p>
215{{if .TokenShown}}<p class="notice" role="status">Token created. It is shown once; store it now.</p>
216<pre class="message" tabindex="0">{{.TokenShown}}</pre>{{end}}
217{{if .Tokens}}<div class="tablewrap"><table class="keys nowrap">
218<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>
219{{range .Tokens}}<tr>
220 <td>{{.Name}}</td>
221 <td>{{.Scope}}</td>
222 <td>{{when .Created}}</td>
223 <td>{{.Expires}}</td>
224 <td>{{.LastUsed}}</td>
225 <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>
226</tr>
227{{end}}</table></div>
228{{else}}<p class="none">No API tokens.</p>{{end}}
229<details class="editbox">
230 <summary>Create a token</summary>
231 <form method="post" action="/settings" class="setform stack">
232 <input type="hidden" name="field" value="token-create">
233 <label for="token-name">Name</label>
234 <input id="token-name" name="name" required autocomplete="off" placeholder="e.g. iphone">
235 <label for="token-scope">Scope</label>
236 <select id="token-scope" name="scope">
237 <option value="read" selected>read: read-only commands</option>
238 <option value="full">full: everything your account can do</option>
239 </select>
240 <label for="token-ttl">Expires after</label>
241 <input id="token-ttl" name="ttl" autocomplete="off" placeholder="e.g. 30d or 720h; empty never expires">
242 <button type="submit" class="btn">Create token</button>
243 </form>
244</details>
245</section>
246
247<section id="cli"><h2>On the command line</h2>
248<p class="meta">No page here yet, and nothing refusing one.</p>
249<pre class="message" tabindex="0">gitbay web sessions list # browser sessions
250gitbay admin ... # instance administration</pre>
251<p class="meta">All of it works from stock OpenSSH too, with the CLI's
252grouping words dropped: <code>ssh git@{{.Host}} whoami</code>,
253<code>ssh git@{{.Host}} web sessions list</code>.</p>
254</section>
255
256<section id="delete"><h2>Delete account</h2>
257<p class="meta">Deletes your repositories, snippets, keys and addresses.
258Issues, merge requests and comments you wrote on other people's
259repositories stay, under the name <code>ghost</code>. A link is mailed to
260your primary address; once it is opened the account is disabled, and it
261is deleted seven days later. Signing in during those days cancels.
262<a href="#export">Export</a> first to keep a copy.</p>
263<form method="post" action="/settings" class="setform"><input type="hidden" name="field" value="account-delete">{{template "confirmfield" .Viewer}} <button type="submit" class="danger">Mail the deletion link</button></form>
264</section>
265</div>
266</div>
267{{end}}