| 1 |
{% extends "base.html" %} |
| 2 |
{% import "settings_tabs.html" as t %} |
| 3 |
{% block title %}Backups{% if kind == "org" %} · {{ owner }}{% endif %}{% endblock %} |
| 4 |
{% block tabs %}{% if kind == "user" %}{% call t::tabs("backups") %}{% endif %}{% endblock %} |
| 5 |
{% block content %} |
| 6 |
<div class="head"><h1>Backups <small>{% if kind == "org" %}<a href="/orgs/{{ owner }}/settings">{{ owner }}</a>'s repositories{% else %}your repositories{% endif %}, copied to a place you control</small></h1></div> |
| 7 |
{% if let Some(n) = notice %}<p class="note">{{ n }}</p>{% endif %} |
| 8 |
{% if let Some(e) = error %}<p class="error">{{ e }}</p>{% endif %} |
| 9 |
<p class="small mute backup-what">Each run copies every repository {% if kind == "org" %}the organisation{% else %}you{% endif %} own, with its wiki, byte for byte (signatures and all), its issues, proposals, releases and settings (never a secret value){% if kind == "org" %}, and the organisation's members and teams{% endif %}. Only what changed since the last run travels, and any kept run can be restored with <code>oxygenhub admin restore</code>.</p> |
| 10 |
|
| 11 |
<form class="card wide backup-form" method="post" action="{{ base }}"> |
| 12 |
<input type="hidden" name="_csrf" value="{{ shell.csrf }}"> |
| 13 |
<h2>Where and how often</h2> |
| 14 |
<fieldset class="choices"> |
| 15 |
<legend class="small mute">How often</legend> |
| 16 |
{% for (v, label) in every() %}<label class="choice"><input type="radio" name="every" value="{{ v }}"{% if setting.every == *v %} checked{% endif %}> {{ label }}</label>{% endfor %} |
| 17 |
</fieldset> |
| 18 |
<fieldset class="choices"> |
| 19 |
<legend class="small mute">Where</legend> |
| 20 |
{% if !dirs.is_empty() %}<label class="choice"><input type="radio" name="destination" value="dir"{% if setting.destination == "dir" %} checked{% endif %}> A directory on this hub's host</label>{% endif %} |
| 21 |
<label class="choice"><input type="radio" name="destination" value="gcs"{% if setting.destination == "gcs" || dirs.is_empty() %} checked{% endif %}> A Google Cloud Storage bucket, with no key</label> |
| 22 |
</fieldset> |
| 23 |
{% if !dirs.is_empty() %} |
| 24 |
<label for="dir">Directory</label> |
| 25 |
<select id="dir" name="dir">{% for d in dirs %}<option value="{{ d }}"{% if setting.destination == "dir" && setting.target == *d %} selected{% endif %}>{{ d }}/{{ owner }}</option>{% endfor %}</select> |
| 26 |
{% endif %} |
| 27 |
<label for="bucket">GCS bucket</label> |
| 28 |
<input type="text" id="bucket" name="bucket" placeholder="{{ owner }}-oxygen-backup" value="{% if setting.destination == "gcs" %}{{ setting.target }}{% endif %}"> |
| 29 |
<label for="provider">Workload identity provider</label> |
| 30 |
<input type="text" id="provider" name="provider" placeholder="//iam.googleapis.com/projects/NUMBER/locations/global/workloadIdentityPools/oxygenhub/providers/oxygenhub-io" value="{{ setting.provider }}"> |
| 31 |
<p class="small mute">The hub signs in as <code>backup:{{ owner }}</code> with a token it issues itself; Google exchanges it for a few minutes' access to this bucket. No key file exists. One-time setup: <a href="/help#backups">Help › Backups</a> (docs/backups.md §6).</p> |
| 32 |
<label for="prefix">Prefix (optional)</label> |
| 33 |
<input type="text" id="prefix" name="prefix" value="{{ setting.prefix }}" placeholder="oxygenhub"> |
| 34 |
<label for="keep_days">Keep runs for</label> |
| 35 |
<select id="keep_days" name="keep_days">{% for k in keeps() %}<option value="{{ k }}"{% if setting.keep_days == *k %} selected{% endif %}>{{ k }} days</option>{% endfor %}</select> |
| 36 |
<button type="submit">SAVE</button> |
| 37 |
</form> |
| 38 |
|
| 39 |
<section class="card-box" id="runs"> |
| 40 |
<div class="card-head"><h2>Runs</h2><span class="count-chip">{{ runs.len() }}</span> |
| 41 |
<form class="inline more" method="post" action="{{ base }}/run"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><button type="submit" class="small primary-btn"{% if setting.updated_ms == 0 %} disabled title="Save where backups go first"{% endif %}>Back up now</button></form> |
| 42 |
</div> |
| 43 |
{% if runs.is_empty() %}<p class="empty small">No runs yet.{% if setting.every == "off" %} Pick how often and where, then save.{% endif %}</p> |
| 44 |
{% else %} |
| 45 |
<div class="table"><table class="backup-runs"> |
| 46 |
<tr><th>Started</th><th>Took</th><th>Outcome</th><th>Repositories</th><th>Files sent</th><th>Size sent</th><th></th></tr> |
| 47 |
{% for r in runs %} |
| 48 |
<tr> |
| 49 |
<td><time title="{{ r.started_ms|when_i }}">{{ r.started_ms|ago_i }}</time>{% if r.trigger == "now" %} <span class="mute small">by hand</span>{% endif %}</td> |
| 50 |
<td>{% if let Some(f) = r.finished_ms %}{{ ((f - r.started_ms) / 1000) }} s{% else %}<span class="mute">running</span>{% endif %}</td> |
| 51 |
<td><span class="backup-status st-{{ r.status }}">{{ r.status }}</span></td> |
| 52 |
<td>{{ r.repos }}</td> |
| 53 |
<td>{{ r.sent }} <span class="mute small">of {{ r.files }}</span></td> |
| 54 |
<td>{{ (r.bytes / 1024) }} KiB</td> |
| 55 |
<td class="small mute">{{ r.detail }}</td> |
| 56 |
</tr> |
| 57 |
{% endfor %} |
| 58 |
</table></div> |
| 59 |
{% endif %} |
| 60 |
</section> |
| 61 |
{% endblock %} |