internal/web/templates/account.html
256 lines · 14733 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 </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{{if .ReplyOffered}}<form method="post" action="/settings" class="setform">
142 <input type="hidden" name="field" value="notify-reply">
143 <label for="notify-reply">Reply to mail to comment</label>
144 <div class="check"><input type="checkbox" id="notify-reply" name="reply" value="on"{{if .ReplyOn}} checked{{end}}></div>
145 <button type="submit" class="btn">Save</button>
146</form>
147<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>
148{{end}}<form method="post" action="/settings" class="setform">
149 <input type="hidden" name="field" value="notify-watch">
150 <label for="notify-watch">Watch repositories you can write to</label>
151 <div class="check"><input type="checkbox" id="notify-watch" name="watch" value="on"{{if .WatchOn}} checked{{end}}></div>
152 <button type="submit" class="btn">Save</button>
153</form>
154<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>
155<form method="post" action="/settings" class="setform">
156 <input type="hidden" name="field" value="notify-push">
157 <label for="notify-push">Activity on your registered devices</label>
158 <div class="check"><input type="checkbox" id="notify-push" name="push" value="on"{{if .PushOn}} checked{{end}}></div>
159 <button type="submit" class="btn">Save</button>
160</form>
161<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>
162<h3>Devices</h3>
163{{if .Devices}}<div class="tablewrap"><table class="keys nowrap">
164<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>
165{{range .Devices}}<tr>
166 <td class="mono">{{.ID}}</td>
167 <td>{{.Label}}</td>
168 <td class="mono">{{.Token}}</td>
169 <td>{{if .LastSeenAt}}{{when .LastSeenAt}}{{else}}never{{end}}</td>
170 <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>
171</tr>
172{{end}}</table></div>
173{{else}}<p class="none">No registered devices.</p>{{end}}
174<p class="meta">Devices register themselves from the iOS app; there is no
175form here to add one, since a browser cannot produce an APNs token.</p>
176</section>
177
178<section id="appearance"><h2>Appearance</h2>
179<form method="post" action="/settings" class="setform">
180 <input type="hidden" name="field" value="theme">
181 <label for="theme">Colour scheme</label>
182 <select id="theme" name="theme">
183 <option value="system"{{if eq .ThemeSetting "system"}} selected{{end}}>follow the browser</option>
184 <option value="light"{{if eq .ThemeSetting "light"}} selected{{end}}>light</option>
185 <option value="dark"{{if eq .ThemeSetting "dark"}} selected{{end}}>dark</option>
186 </select>
187 <button type="submit" class="btn">Save</button>
188</form>
189<p class="meta">The same setting as <code>gitbay web theme set</code>. Following the browser takes its light or dark preference.</p>
190<form method="post" action="/settings" class="setform">
191 <input type="hidden" name="field" value="diff-layout">
192 <label for="layout">Diff layout</label>
193 <select id="layout" name="layout">
194 <option value="unified"{{if ne .DiffSetting "split"}} selected{{end}}>unified</option>
195 <option value="split"{{if eq .DiffSetting "split"}} selected{{end}}>side by side</option>
196 </select>
197 <button type="submit" class="btn">Save</button>
198</form>
199<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>
200</section>
201
202<section id="export"><h2>Export</h2>
203<p class="meta">Your profile, repositories, issues and merge requests as one
204JSON bundle, the same one <code>gitbay auth export</code> writes. Keys are
205never included; a replayed bundle's emails arrive unverified.</p>
206<p><a href="/settings/export">Download bundle</a></p>
207</section>
208
209<section id="tokens"><h2>API tokens</h2>
210<p class="meta">A token signs in the iOS app, or a script, without your
211password. A phone app needs full scope to comment and merge; full scope
212on an admin account can administer the instance, so give a token the
213narrowest scope and shortest lifetime the job needs.</p>
214{{if .TokenShown}}<p class="notice" role="status">Token created. It is shown once; store it now.</p>
215<pre class="message" tabindex="0">{{.TokenShown}}</pre>{{end}}
216{{if .Tokens}}<div class="tablewrap"><table class="keys nowrap">
217<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>
218{{range .Tokens}}<tr>
219 <td>{{.Name}}</td>
220 <td>{{.Scope}}</td>
221 <td>{{when .Created}}</td>
222 <td>{{.Expires}}</td>
223 <td>{{.LastUsed}}</td>
224 <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>
225</tr>
226{{end}}</table></div>
227{{else}}<p class="none">No API tokens.</p>{{end}}
228<details class="editbox">
229 <summary>Create a token</summary>
230 <form method="post" action="/settings" class="setform stack">
231 <input type="hidden" name="field" value="token-create">
232 <label for="token-name">Name</label>
233 <input id="token-name" name="name" required autocomplete="off" placeholder="e.g. iphone">
234 <label for="token-scope">Scope</label>
235 <select id="token-scope" name="scope">
236 <option value="read" selected>read: read-only commands</option>
237 <option value="full">full: everything your account can do</option>
238 </select>
239 <label for="token-ttl">Expires after</label>
240 <input id="token-ttl" name="ttl" autocomplete="off" placeholder="e.g. 30d or 720h; empty never expires">
241 <button type="submit" class="btn">Create token</button>
242 </form>
243</details>
244</section>
245
246<section id="cli"><h2>On the command line</h2>
247<p class="meta">No page here yet, and nothing refusing one.</p>
248<pre class="message" tabindex="0">gitbay web sessions list # browser sessions
249gitbay admin ... # instance administration</pre>
250<p class="meta">All of it works from stock OpenSSH too, with the CLI's
251grouping words dropped: <code>ssh git@{{.Host}} whoami</code>,
252<code>ssh git@{{.Host}} web sessions list</code>.</p>
253</section>
254</div>
255</div>
256{{end}}