internal/web/templates/account.html

bd5cf5d7d1f34fa780660fd7562b9ffd9746ee27
gitbay/internal/web/templates/account.html history · blame · raw

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}}