| 1 |
{% extends "base.html" %} |
| 2 |
{% import "webhooks_panel.html" as w %} |
| 3 |
{% import "assistants_panel.html" as ai %} |
| 4 |
{% import "variables_panel.html" as va %} |
| 5 |
{% import "secrets_panel.html" as se %} |
| 6 |
{% block title %}{{ org.name }} settings{% endblock %} |
| 7 |
{% block content %} |
| 8 |
<div class="head"><h1>{{ org.name }} <small>organisation settings · <a href="/{{ org.name }}">repositories</a></small></h1></div> |
| 9 |
<div class="sectioned"> |
| 10 |
<nav class="section-nav" aria-label="Sections"> |
| 11 |
<a href="#general">General</a> |
| 12 |
<a href="#logo">Logo</a> |
| 13 |
<a href="#members">Members</a> |
| 14 |
<a href="#webhooks">Webhooks</a> |
| 15 |
<a href="#variables">Variables</a> |
| 16 |
<a href="#secrets">Secrets</a> |
| 17 |
<a href="#assistants">AI assistants</a> |
| 18 |
<a href="#teams">Teams</a> |
| 19 |
<a href="#storage">Storage</a> |
| 20 |
<a href="#backups">Backups</a> |
| 21 |
</nav> |
| 22 |
<div class="sections"> |
| 23 |
{% if let Some(n) = notice %}<p class="note">{{ n }}</p>{% endif %} |
| 24 |
{% if let Some(e) = error %}<p class="error">{{ e }}</p>{% endif %} |
| 25 |
<section class="panel" id="backups"><h2>Backups</h2><p class="small">Copy every repository {{ org.name }} owns, with its issues, proposals and wiki, hourly to a bucket or directory you control. <a class="go small" href="/orgs/{{ org.name }}/settings/backups">Set up backups</a></p></section> |
| 26 |
<form class="card" id="general" method="post" action="/orgs/{{ org.name }}/settings"> |
| 27 |
<input type="hidden" name="_csrf" value="{{ shell.csrf }}"> |
| 28 |
<label for="display_name">Display name</label><input type="text" id="display_name" name="display_name" value="{{ org.display_name }}"> |
| 29 |
<label for="description">Description</label><input type="text" id="description" name="description" value="{{ org.description }}"> |
| 30 |
<label for="default_role">Members' default role on the organisation's repositories</label> |
| 31 |
<select id="default_role" name="default_role">{% for r in ["none", "read", "triage", "write", "maintain", "admin"] %}<option value="{{ r }}"{% if org.default_role.as_str() == *r %} selected{% endif %}>{{ r }}</option>{% endfor %}</select> |
| 32 |
<button type="submit">SAVE</button> |
| 33 |
</form> |
| 34 |
<form class="card picture-card" id="logo" method="post" action="/orgs/{{ org.name }}/settings/avatar" enctype="multipart/form-data"> |
| 35 |
<input type="hidden" name="_csrf" value="{{ shell.csrf }}"> |
| 36 |
<h2>Logo</h2> |
| 37 |
<div class="picture-row"> |
| 38 |
<img class="avatar xl" src="/avatar/{{ org.name }}?v={{ org.avatar }}" alt="{{ org.name }} logo" width="96" height="96"> |
| 39 |
<div> |
| 40 |
<label for="logo-file">Upload a logo</label> |
| 41 |
<input type="file" id="logo-file" name="picture" accept="image/png,image/jpeg,image/webp"> |
| 42 |
<p class="small mute">PNG (transparency is kept), JPEG or WebP, up to 5 MiB. It is cropped to the middle square and resized to 400×400.</p> |
| 43 |
<button type="submit" name="action" value="upload">UPLOAD</button> |
| 44 |
{% if !org.avatar.is_empty() %}<button type="submit" name="action" value="remove" class="danger-btn">REMOVE</button>{% endif %} |
| 45 |
</div> |
| 46 |
</div> |
| 47 |
</form> |
| 48 |
<section class="panel" id="members"> |
| 49 |
<h2>Members</h2> |
| 50 |
<table class="small">{% for (a, r) in members %}<tr><td><a href="/{{ a.username }}">{{ a.username }}</a></td><td>{{ r }}</td> |
| 51 |
<td><form class="tiny" method="post" action="/orgs/{{ org.name }}/settings/members"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><input type="hidden" name="username" value="{{ a.username }}"><button name="remove" value="1">remove</button></form></td></tr>{% endfor %}</table> |
| 52 |
<form class="card" method="post" action="/orgs/{{ org.name }}/settings/members"> |
| 53 |
<input type="hidden" name="_csrf" value="{{ shell.csrf }}"> |
| 54 |
<div class="row"><div><label for="username">Account</label><input type="text" id="username" name="username" required></div> |
| 55 |
<div><label for="role">Role</label><select id="role" name="role"><option value="member">member</option><option value="owner">owner</option></select></div></div> |
| 56 |
<button type="submit">ADD OR CHANGE</button> |
| 57 |
</form> |
| 58 |
</section> |
| 59 |
<form class="card" id="storage" method="post" action="/orgs/{{ org.name }}/settings/storage"> |
| 60 |
<input type="hidden" name="_csrf" value="{{ shell.csrf }}"> |
| 61 |
<h2>Storage</h2> |
| 62 |
<p class="small mute">Where the organisation's new repositories may live. Repositories that exist stay where they are.</p> |
| 63 |
<label class="check"><input type="checkbox" name="require_bucket" value="1"{% if policy.require_bucket %} checked{% endif %}> Keep every new repository in a bucket, not on this server's disk</label> |
| 64 |
<label for="allowed">Buckets allowed, one store URL prefix per line (empty: any bucket)</label> |
| 65 |
<textarea id="allowed" name="allowed" rows="3" placeholder="s3://acme-london/repos/ gs://acme-eu/" spellcheck="false">{{ policy.allowed.join("\n") }}</textarea> |
| 66 |
<p class="small mute">Listing the organisation's own buckets keeps its data where those buckets are: the provider, the region, the country.</p> |
| 67 |
<button type="submit">SAVE</button> |
| 68 |
</form> |
| 69 |
<section class="panel" id="teams"> |
| 70 |
<h2>Teams</h2> |
| 71 |
{% for (t, ms) in teams %} |
| 72 |
<p><strong>{{ t.name }}</strong>: {% for a in ms %}{{ a.username }}{% if !loop.last %}, {% endif %}{% endfor %}{% if ms.is_empty() %}<span class="mute">nobody yet</span>{% endif %} |
| 73 |
<form class="tiny" method="post" action="/orgs/{{ org.name }}/settings/teams"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><input type="hidden" name="team" value="{{ t.name }}"><button name="action" value="delete">delete team</button></form></p> |
| 74 |
{% endfor %} |
| 75 |
<form class="card" method="post" action="/orgs/{{ org.name }}/settings/teams"> |
| 76 |
<input type="hidden" name="_csrf" value="{{ shell.csrf }}"> |
| 77 |
<div class="row"><div><label for="team">Team</label><input type="text" id="team" name="team" required></div> |
| 78 |
<div><label for="tuser">Account (for add or remove)</label><input type="text" id="tuser" name="username"></div></div> |
| 79 |
<div class="choice"><button type="submit" name="action" value="create">CREATE TEAM</button> <button type="submit" name="action" value="add">ADD MEMBER</button> <button type="submit" name="action" value="remove">REMOVE MEMBER</button></div> |
| 80 |
</form> |
| 81 |
</section> |
| 82 |
<section class="panel" id="runners"> |
| 83 |
<h2>Runners</h2> |
| 84 |
<p class="small">The OxygenFlow runners that may take this organisation's jobs: the site's, the organisation's, and those of its repositories.</p> |
| 85 |
{% if let Some(t) = runner_token %}<p class="note">Register a runner for the organisation with:</p><pre class="snippet">flow runner register {{ shell.base_url }} --token {{ t }} --name NAME</pre>{% endif %} |
| 86 |
{% if runners.is_empty() %}<p class="mute small">None yet: runs wait until a runner that serves them is added.</p>{% else %} |
| 87 |
<div class="table"><table> |
| 88 |
<tr><th>Runner</th><th>Status</th><th>Serves</th><th>Machine</th><th></th></tr> |
| 89 |
{% for (r, serves, own) in runners %}<tr><td><strong>{{ r.name }}</strong></td><td><span class="runner-status st-{{ r.status() }}">{{ r.status() }}</span></td><td class="small">{{ serves }}</td><td class="small">{% if r.arch.is_empty() %}<span class="mute">unknown</span>{% else %}{{ r.arch }} · {{ r.os }}{% endif %}</td> |
| 90 |
<td>{% if *own %}<form class="tiny" method="post" action="/orgs/{{ org.name }}/settings/runners/{{ r.id }}/remove" onsubmit="return confirm('Remove {{ r.name }}? Its credential stops working.')"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><button class="danger-btn">remove</button></form>{% endif %}</td></tr>{% endfor %} |
| 91 |
</table></div>{% endif %} |
| 92 |
<form method="post" action="/orgs/{{ org.name }}/settings/runners/token"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><button type="submit">Add a runner for the organisation</button></form> |
| 93 |
</section> |
| 94 |
{% call w::panel("/orgs/" ~ org.name ~ "/settings", hooks, shell.csrf, "These hear every repository of the organisation, as well as each repository's own webhooks.") %} |
| 95 |
{% call va::panel("/orgs/" ~ org.name ~ "/settings", variables, shell.csrf, "Jobs of every repository of the organisation get these, beside the site's and each repository's own.") %} |
| 96 |
{% call se::panel("/orgs/" ~ org.name ~ "/settings", secrets, shell.csrf, "Jobs of every repository of the organisation may use these, beside the site's and each repository's own.") %} |
| 97 |
{% call ai::panel("/orgs/" ~ org.name ~ "/settings", assistants, shell.csrf, "Every repository of the organisation may ask these, beside the site's and its own.") %} |
| 98 |
</div> |
| 99 |
</div> |
| 100 |
{% endblock %} |