OXYGENHUB
# 317d776c725b oxygenhub / templates / backups.html
6.4 KiB · HTML · last changed UI polish: keys, backups, wiki, editor, bookmarks, summaries, backdrops 22 days ago source blame history raw
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
<script type="application/json" id="backdrop-data">{"scene":"mark"}</script>
7
{% include "backdrop.html" %}
8
<div data-backdrop-avoid>
9
<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>
10
{% if let Some(n) = notice %}<p class="note">{{ n }}</p>{% endif %}
11
{% if let Some(e) = error %}<p class="error">{{ e }}</p>{% endif %}
12
<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>
13
14
<form class="card wide backup-form" method="post" action="{{ base }}">
15
  <input type="hidden" name="_csrf" value="{{ shell.csrf }}">
16
  <h2>Where and how often</h2>
17
  <fieldset class="choices">
18
    <legend class="small mute">How often</legend>
19
    {% for (v, label) in every() %}<label class="choice"><input type="radio" name="every" value="{{ v }}"{% if setting.every == *v %} checked{% endif %}> {{ label }}</label>{% endfor %}
20
  </fieldset>
21
  <fieldset class="choices">
22
    <legend class="small mute">Where</legend>
23
    {% 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 %}
24
    <label class="choice"><input type="radio" name="destination" value="gcs"{% if setting.destination == "gcs" || (dirs.is_empty() && setting.destination != "s3") %} checked{% endif %}> A Google Cloud Storage bucket, with no key</label>
25
    <label class="choice"><input type="radio" name="destination" value="s3"{% if setting.destination == "s3" %} checked{% endif %}> An Amazon S3 bucket, with no key</label>
26
  </fieldset>
27
  {% if !dirs.is_empty() %}
28
  <label for="dir">Directory</label>
29
  <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>
30
  {% endif %}
31
  <label for="bucket">GCS bucket</label>
32
  <input type="text" id="bucket" name="bucket" placeholder="{{ owner }}-oxygen-backup" value="{% if setting.destination == "gcs" %}{{ setting.target }}{% endif %}">
33
  <label for="provider">Workload identity provider</label>
34
  <input type="text" id="provider" name="provider" placeholder="//iam.googleapis.com/projects/NUMBER/locations/global/workloadIdentityPools/oxygenhub/providers/oxygenhub-io" value="{% if setting.destination == "gcs" %}{{ setting.provider }}{% endif %}">
35
  <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>
36
  <label for="s3_bucket">S3 bucket</label>
37
  <input type="text" id="s3_bucket" name="s3_bucket" placeholder="{{ owner }}-oxygen-backup" value="{% if setting.destination == "s3" %}{{ setting.target }}{% endif %}">
38
  <label for="s3_region">S3 region</label>
39
  <input type="text" id="s3_region" name="s3_region" placeholder="eu-west-2" value="{{ setting.region }}">
40
  <label for="s3_role">IAM role</label>
41
  <input type="text" id="s3_role" name="s3_role" placeholder="arn:aws:iam::ACCOUNT:role/oxygenhub-backup-{{ owner }}" value="{% if setting.destination == "s3" %}{{ setting.provider }}{% endif %}">
42
  <p class="small mute">The hub signs in to AWS as <code>backup:{{ owner }}</code> with a token it issues itself (audience <code>sts.amazonaws.com</code>); AWS gives it an hour's credentials of this role. No access key exists. One-time setup: docs/backups.md §6.</p>
43
  <label for="prefix">Prefix (optional)</label>
44
  <input type="text" id="prefix" name="prefix" value="{{ setting.prefix }}" placeholder="oxygenhub">
45
  <label for="keep_days">Keep runs for</label>
46
  <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>
47
  <button type="submit">SAVE</button>
48
</form>
49
50
<section class="card-box" id="runs">
51
  <div class="card-head"><h2>Runs</h2><span class="count-chip">{{ runs.len() }}</span>
52
    <form class="inline more" method="post" action="{{ base }}/test"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><button type="submit" class="small"{% if setting.updated_ms == 0 %} disabled title="Save where backups go first"{% endif %} title="Write, read and remove a probe at the destination">TEST</button></form>
53
    <form class="inline" 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>
54
  </div>
55
  {% if runs.is_empty() %}<p class="empty small">No runs yet.{% if setting.every == "off" %} Pick how often and where, then save.{% endif %}</p>
56
  {% else %}
57
  <div class="table"><table class="backup-runs">
58
    <tr><th>Started</th><th>Took</th><th>Outcome</th><th>Repositories</th><th>Files sent</th><th>Size sent</th><th></th></tr>
59
    {% for r in runs %}
60
    <tr>
61
      <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>
62
      <td>{% if let Some(f) = r.finished_ms %}{{ ((f - r.started_ms) / 1000) }} s{% else %}<span class="mute">running</span>{% endif %}</td>
63
      <td><span class="backup-status st-{{ r.status }}">{{ r.status }}</span></td>
64
      <td>{{ r.repos }}</td>
65
      <td>{{ r.sent }} <span class="mute small">of {{ r.files }}</span></td>
66
      <td>{{ (r.bytes / 1024) }} KiB</td>
67
      <td class="small mute">{{ r.detail }}</td>
68
    </tr>
69
    {% endfor %}
70
  </table></div>
71
  {% endif %}
72
</section>
73
</div>
74
{% endblock %}