OXYGENHUB
# e71846aa07b9 oxygenhub / templates / admin.html
5.4 KiB · HTML · last changed AI assistants: settings per site, organisation and repository 1 month ago source blame history raw
1
{% extends "base.html" %}
2
{% import "assistants_panel.html" as ai %}
3
{% block title %}Administration{% endblock %}
4
{% block content %}
5
<div class="head"><h1>Administration <small>{{ accounts.len() }} accounts · {{ orgs.len() }} organisations · {{ repos }} repositories</small></h1></div>
6
<p><a class="go small" href="/admin/storage">Storage and maintenance</a> <a class="go small" href="/admin/runners">Runners</a></p>
7
{% if let Some(n) = notice %}<p class="note">{{ n }}</p>{% endif %}
8
<div class="side-layout">
9
<div>
10
<section class="panel">
11
  <h2>Accounts</h2>
12
  <table class="small"><tr><th>Username</th><th>Email</th><th>Flags</th><th>Last seen</th><th></th></tr>
13
  {% for a in accounts %}
14
  <tr><td><a href="/{{ a.username }}">{{ a.username }}</a></td><td>{{ a.email }}{% if !a.email.is_empty() %} <span class="small mute" title="{% if a.email_verified %}verified{% else %}not verified{% endif %}">{% if a.email_verified %}✔{% else %}○{% endif %}</span>{% endif %}</td><td>{% if a.is_admin %}<span class="tag">admin</span>{% endif %}{% if a.banned %} <span class="tag bad">banned</span>{% endif %}{% if a.disabled %} <span class="tag warn">disabled</span>{% endif %}{% if a.totp_enabled() %} <span class="tag">2fa</span>{% endif %}</td>
15
  <td>{% if a.last_seen_ms > 0 %}<time title="{{ a.last_seen_ms|when_i }}">{{ a.last_seen_ms|ago_i }}</time>{% else %}<span class="mute">never</span>{% endif %}</td>
16
  <td><form class="tiny" method="post" action="/admin/account"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><input type="hidden" name="username" value="{{ a.username }}">
17
    {% if a.banned %}<button name="action" value="unban">unban</button>{% else %}<button name="action" value="ban">ban</button>{% endif %}
18
    {% if a.is_admin %}<button name="action" value="demote">demote</button>{% else %}<button name="action" value="promote">make admin</button>{% endif %}
19
    {% if a.disabled %}<button name="action" value="enable">enable</button>{% else %}<button name="action" value="disable" title="Refuse sign-in, sessions, tokens and pushes until enabled again">disable</button>{% endif %}
20
    {% if !a.blocked() %}<button name="action" value="reset_link" title="A one-time password reset link to hand over">reset link</button>{% endif %}</form>
21
    <details class="tiny delete-account"><summary>delete</summary><form class="tiny" method="post" action="/admin/account"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><input type="hidden" name="username" value="{{ a.username }}">
22
    <label class="small">Deleting removes the account, its tokens, keys and memberships for good; what it wrote stays under its name, which stays retired. Type <strong>{{ a.username }}</strong> to confirm:
23
    <input name="confirm" autocomplete="off" required></label> <button class="danger" name="action" value="delete">delete {{ a.username }}</button></form></details></td></tr>
24
  {% endfor %}
25
  </table>
26
</section>
27
<section class="panel">
28
  <h2>Audit log</h2>
29
  <table class="small"><tr><th>When</th><th>Who</th><th>Action</th><th>Target</th><th>Detail</th></tr>
30
  {% for r in audit %}<tr><td><time title="{{ r.at_ms|when_i }}">{{ r.at_ms|ago_i }}</time></td><td>{{ r.actor }}</td><td>{{ r.action }}</td><td>{{ r.target }}</td><td class="mute">{{ r.detail }}</td></tr>{% endfor %}
31
  </table>
32
</section>
33
</div>
34
<aside class="side">
35
  <h3>Registration</h3>
36
  <form class="card" method="post" action="/admin/registration">
37
    <input type="hidden" name="_csrf" value="{{ shell.csrf }}">
38
    <select name="mode">{% for m in ["open", "invite", "closed"] %}<option value="{{ m }}"{% if mode.as_str() == *m %} selected{% endif %}>{{ m }}</option>{% endfor %}</select>
39
    <button type="submit">SET</button>
40
  </form>
41
  <h3>Invitations</h3>
42
  <form class="card" method="post" action="/admin/invite"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><button type="submit">NEW INVITATION</button></form>
43
  <ul class="plain small">{% for (code, used, at) in invitations %}<li><code>{{ code }}</code> {% if used.is_some() %}<span class="mute">used</span>{% else %}<a href="/register?invite={{ code }}">link</a>{% endif %} · {{ at|ago_i }}</li>{% endfor %}</ul>
44
  <h3>Organisations</h3>
45
  <ul class="plain">{% for o in orgs %}<li><a href="/orgs/{{ o.name }}/settings">{{ o.name }}</a></li>{% endfor %}</ul>
46
  <h3>Mail</h3>
47
  <form class="card" method="post" action="/admin/mail">
48
    <input type="hidden" name="_csrf" value="{{ shell.csrf }}">
49
    <label>SMTP URL</label>
50
    <input type="text" name="smtp" value="{{ mail_smtp }}" placeholder="smtps://user:[email protected]:465">
51
    <label>From</label>
52
    <input type="text" name="from" value="{{ mail_from }}" placeholder="OxygenHub &lt;[email protected]&gt;">
53
    <button type="submit">SAVE</button>
54
  </form>
55
  <form class="card" method="post" action="/admin/mail/test"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><button type="submit"{% if !mail_enabled || my_email.is_empty() %} disabled{% endif %}>SEND A TEST{% if !my_email.is_empty() %} TO {{ my_email }}{% endif %}</button></form>
56
  <p class="small mute">{% if mail_enabled %}Mail is on: verification and reset links go by email.{% else %}Mail is off: verification and reset links are issued by hand.{% endif %} An empty URL turns it off. The password stays on the server and is shown masked.</p>
57
</aside>
58
</div>
59
{% call ai::panel("/admin", assistants, shell.csrf, "Every repository on the site may ask these, unless an organisation or repository turns one off.") %}
60
{% endblock %}