19bb31ff Andy McCall 1 month ago ↶
1
{% extends "base.html" %}
e71846aa Andy McCall 1 month ago ↶
2
{% import "assistants_panel.html" as ai %}
2a83f530 Andy McCall 1 month ago ↶
3
{% import "variables_panel.html" as va %}
4ac01a73 Andy McCall 1 month ago ↶
4
{% import "secrets_panel.html" as se %}
19bb31ff Andy McCall 1 month ago ↶
5
{% block title %}Administration{% endblock %}
6
{% block content %}
7
< div class = " head " > < h1 > Administration < small > {{ accounts.len() }} accounts · {{ orgs.len() }} organisations · {{ repos }} repositories</ small > </ h1 > </ div >
e8e7158b Andy McCall 1 month ago ↶
8
< div class = " sectioned " >
9
< nav class = " section-nav " aria-label = " Sections" >
10
< a href = " #accounts" > Accounts</ a >
11
< a href = " #registration" > Registration</ a >
12
< a href = " #organisations" > Organisations</ a >
13
< a href = " #signin" > Sign in with</ a >
14
< a href = " #mail" > Mail</ a >
15
< a href = " #variables" > Variables</ a >
16
< a href = " #secrets" > Secrets</ a >
17
< a href = " #assistants" > AI assistants</ a >
18
< a href = " #audit" > Audit log</ a >
19
< a href = " /admin/runners" > Runners ↗</ a >
6ca0b971 Andy McCall 24 days ago ↶
20
< a href = " /admin/runners/images" > Images ↗</ a >
757195ad Andy McCall 23 days ago ↶
21
< a href = " /admin/diffusion" > Diffusion ↗</ a >
e8e7158b Andy McCall 1 month ago ↶
22
< a href = " /admin/storage" > Storage and maintenance ↗</ a >
23
</ nav >
24
< div class = " sections " >
19bb31ff Andy McCall 1 month ago ↶
25
{% if let Some(n) = notice %}< p class = " note " > {{ n }}</ p > {% endif %}
e8e7158b Andy McCall 1 month ago ↶
26
< section class = " panel " id = " accounts " >
19bb31ff Andy McCall 1 month ago ↶
27
< h2 > Accounts</ h2 >
e8e7158b Andy McCall 1 month ago ↶
28
< div class = " table " > < table class = " small " > < tr > < th > Username</ th > < th > Email</ th > < th > Flags</ th > < th > Last seen</ th > < th > </ th > </ tr >
19bb31ff Andy McCall 1 month ago ↶
29
{% for a in accounts %}
6a178055 Andy McCall 1 month ago ↶
30
< 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 >
19bb31ff Andy McCall 1 month ago ↶
31
< 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 >
32
< 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 }}" >
33
{% if a.banned %}< button name = " action" value = " unban" > unban</ button > {% else %}< button name = " action" value = " ban" > ban</ button > {% endif %}
34beb1a7 Andy McCall 1 month ago ↶
34
{% if a.is_admin %}< button name = " action" value = " demote" > demote</ button > {% else %}< button name = " action" value = " promote" > make admin</ button > {% endif %}
6a178055 Andy McCall 1 month ago ↶
35
{% 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 %}
36
{% if !a.blocked() %}< button name = " action" value = " reset_link" title = " A one-time password reset link to hand over" > reset link</ button > {% endif %}</ form >
37
< 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 }}" >
38
< 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:
39
< input name = " confirm" autocomplete = " off" required > </ label > < button class = " danger " name = " action" value = " delete" > delete {{ a.username }}</ button > </ form > </ details > </ td > </ tr >
19bb31ff Andy McCall 1 month ago ↶
40
{% endfor %}
e8e7158b Andy McCall 1 month ago ↶
41
</ table > </ div >
19bb31ff Andy McCall 1 month ago ↶
42
</ section >
e8e7158b Andy McCall 1 month ago ↶
43
< section class = " panel " id = " registration " >
44
< h2 > Registration and invitations</ h2 >
19bb31ff Andy McCall 1 month ago ↶
45
< form class = " card " method = " post" action = " /admin/registration" >
46
< input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" >
e8e7158b Andy McCall 1 month ago ↶
47
< label for = " regmode" > Who may register</ label >
48
< select id = " regmode " name = " mode" > {% for m in ["open", "invite", "closed"] %}< option value = " {{ m }}" {% if mode.as_str () == *m %} selected {% endif %}> {{ m }}</ option > {% endfor %}</ select >
19bb31ff Andy McCall 1 month ago ↶
49
< button type = " submit" > SET</ button >
50
</ form >
51
< form class = " card " method = " post" action = " /admin/invite" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < button type = " submit" > NEW INVITATION</ button > </ form >
e8e7158b Andy McCall 1 month ago ↶
52
{% if !invitations.is_empty() %}< 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 > {% endif %}
53
</ section >
54
< section class = " panel " id = " organisations " >
55
< h2 > Organisations</ h2 >
56
{% if orgs.is_empty() %}< p class = " mute small " > None yet.</ p > {% else %}< ul class = " plain " > {% for o in orgs %}< li > < img class = " avatar xs " src = " /avatar/{{ o.name }}" alt = " " > < a href = " /{{ o.name }}" > {{ o.name }}</ a > · < a class = " small " href = " /orgs/{{ o.name }}/settings" > settings</ a > </ li > {% endfor %}</ ul > {% endif %}
57
</ section >
58
< section class = " panel " id = " signin " >
59
< h2 > Sign in with</ h2 >
60
< p class = " small mute " > A first sign-in links the account with the same verified email, or makes an account when registration is open. Secrets are kept sealed.</ p >
83940d53 Andy McCall 1 month ago ↶
61
{% for p in providers %}
62
< form class = " card " method = " post" action = " /admin/signin" >
63
< input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < input type = " hidden" name = " provider" value = " {{ p.id }}" >
e8e7158b Andy McCall 1 month ago ↶
64
< h3 > {{ p.name }} {% if p.enabled & & p.has_secret & & !p.client_id.is_empty() %}< span class = " tag head " > on</ span > {% else %}< span class = " tag dim " > off</ span > {% endif %}</ h3 >
83940d53 Andy McCall 1 month ago ↶
65
< label for = " cid-{{ p.id }}" > Client id</ label > < input type = " text" id = " cid-{{ p.id }} " name = " client_id" value = " {{ p.client_id }}" autocomplete = " off" >
66
< label for = " sec-{{ p.id }}" > Client secret</ label > < input type = " password" id = " sec-{{ p.id }} " name = " secret" placeholder = " {% if p.has_secret %}unchanged{% else %}none{% endif %}" autocomplete = " new-password" >
67
< div class = " choice " > < label > < input type = " checkbox" name = " enabled" value = " 1" {% if p.enabled %} checked {% endif %}> on</ label > </ div >
68
< p class = " small mute " > Callback URL to register with {{ p.name }}: < code > {{ p.callback }}</ code > </ p >
69
< button type = " submit" > SAVE</ button >
70
</ form >
71
{% endfor %}
e8e7158b Andy McCall 1 month ago ↶
72
</ section >
73
< section class = " panel " id = " mail " >
74
< h2 > Mail</ h2 >
3556d08e Andy McCall 1 month ago ↶
75
< form class = " card " method = " post" action = " /admin/mail" >
76
< input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" >
e8e7158b Andy McCall 1 month ago ↶
77
< label for = " smtp" > SMTP URL</ label >
78
< input type = " text" id = " smtp " name = " smtp" value = " {{ mail_smtp }}" placeholder = " smtps://user:[email protected] :465" >
79
< label for = " mailfrom" > From</ label >
80
< input type = " text" id = " mailfrom " name = " from" value = " {{ mail_from }}" placeholder = " OxygenHub & lt; [email protected] & gt; " >
3556d08e Andy McCall 1 month ago ↶
81
< button type = " submit" > SAVE</ button >
82
</ form >
83
< 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 >
84
< 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 >
e8e7158b Andy McCall 1 month ago ↶
85
</ section >
2a83f530 Andy McCall 1 month ago ↶
86
{% call va::panel("/admin", variables, shell.csrf, "Every repository's jobs get these, unless an organisation or repository sets the same name.") %}
4ac01a73 Andy McCall 1 month ago ↶
87
{% call se::panel("/admin", secrets, shell.csrf, "Every repository's jobs may use these, unless an organisation or repository sets the same name.") %}
e71846aa Andy McCall 1 month ago ↶
88
{% call ai::panel("/admin", assistants, shell.csrf, "Every repository on the site may ask these, unless an organisation or repository turns one off.") %}
e8e7158b Andy McCall 1 month ago ↶
89
< section class = " panel " id = " audit " >
90
< h2 > Audit log</ h2 >
91
< div class = " table " > < table class = " small " > < tr > < th > When</ th > < th > Who</ th > < th > Action</ th > < th > Target</ th > < th > Detail</ th > </ tr >
92
{% 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 > < code > {{ r.action }}</ code > </ td > < td > {{ r.target }}</ td > < td class = " mute " > {{ r.detail }}</ td > </ tr > {% endfor %}
93
</ table > </ div >
94
< p class = " pager small " > < a href = " /admin#audit" > newest</ a > {% if let Some(o) = audit_older %} < a class = " go small " href = " /admin?audit_before={{ o }}#audit" > older</ a > {% endif %}</ p >
95
</ section >
96
</ div >
97
</ div >
19bb31ff Andy McCall 1 month ago ↶
98
{% endblock %}