| 1 | <div class="container admin-page">
|
|---|
| 2 | <h1><%= t('aupd.title') %></h1>
|
|---|
| 3 | <p><a href="/admin" class="btn">← <%= t('aupd.back_admin') %></a></p>
|
|---|
| 4 |
|
|---|
| 5 | <% if (success) { %><div class="alert alert-success"><%= success %></div><% } %>
|
|---|
| 6 | <% if (error) { %><div class="alert alert-error"><%= error %></div><% } %>
|
|---|
| 7 |
|
|---|
| 8 | <section class="set-card">
|
|---|
| 9 | <h2><%= t('aupd.version_heading') %></h2>
|
|---|
| 10 |
|
|---|
| 11 | <div class="upd-row">
|
|---|
| 12 | <span class="upd-label"><%= t('aupd.app_version') %></span>
|
|---|
| 13 | <span class="upd-val"><%= appVersion || '—' %></span>
|
|---|
| 14 | </div>
|
|---|
| 15 | <div class="upd-row">
|
|---|
| 16 | <span class="upd-label"><%= t('aupd.current') %></span>
|
|---|
| 17 | <span class="upd-val">
|
|---|
| 18 | <% if (currentSha) { %><code><%= currentSha %></code> <span class="upd-desc"><%= currentDesc %></span>
|
|---|
| 19 | <% } else { %><em><%= t('aupd.current_unknown') %></em><% } %>
|
|---|
| 20 | </span>
|
|---|
| 21 | </div>
|
|---|
| 22 | <div class="upd-row">
|
|---|
| 23 | <span class="upd-label"><%= t('aupd.latest') %></span>
|
|---|
| 24 | <span class="upd-val">
|
|---|
| 25 | <% if (latestSha) { %><code><%= latestSha %></code> <span class="upd-desc"><%= latestDesc %></span>
|
|---|
| 26 | <% } else { %><em><%= t('aupd.latest_failed') %></em><% } %>
|
|---|
| 27 | </span>
|
|---|
| 28 | </div>
|
|---|
| 29 |
|
|---|
| 30 | <div class="upd-status">
|
|---|
| 31 | <% if (!canCheck) { %>
|
|---|
| 32 | <span class="upd-badge warn">⚠ <%= t('aupd.no_source') %></span>
|
|---|
| 33 | <% } else if (upToDate) { %>
|
|---|
| 34 | <span class="upd-badge ok">✓ <%= t('aupd.uptodate') %></span>
|
|---|
| 35 | <% } else { %>
|
|---|
| 36 | <span class="upd-badge new">↻ <%= t('aupd.update_available') %><% if (behind) { %> (<%= behind === '1' ? t('aupd.behind_one', { n: behind }) : t('aupd.behind_many', { n: behind }) %>)<% } %></span>
|
|---|
| 37 | <% } %>
|
|---|
| 38 | </div>
|
|---|
| 39 |
|
|---|
| 40 | <% if (canCheck && canSelfUpdate) { %>
|
|---|
| 41 | <form method="post" action="/admin/updates/run" class="set-form"
|
|---|
| 42 | onsubmit="return confirm('<%= t('aupd.run_confirm') %>');">
|
|---|
| 43 | <button type="submit" class="btn btn-primary">
|
|---|
| 44 | <%= upToDate ? '↻ ' + t('aupd.redeploy') : '⬆ ' + t('aupd.update_now') %>
|
|---|
| 45 | </button>
|
|---|
| 46 | </form>
|
|---|
| 47 | <% } else if (typeof manualCommand !== 'undefined' && manualCommand && !upToDate) { %>
|
|---|
| 48 | <p class="set-help" style="margin:.2rem 0 .45rem"><%= t('aupd.manual_hint') %></p>
|
|---|
| 49 | <pre class="upd-cmd"><code><%= manualCommand %></code></pre>
|
|---|
| 50 | <% } %>
|
|---|
| 51 |
|
|---|
| 52 | <p class="set-help" style="margin-top:1rem">
|
|---|
| 53 | <%= t('aupd.help') %>
|
|---|
| 54 | </p>
|
|---|
| 55 | </section>
|
|---|
| 56 |
|
|---|
| 57 | <% if (changes && changes.length) { %>
|
|---|
| 58 | <section class="set-card" style="margin-top:1rem">
|
|---|
| 59 | <h2><%= t('aupd.changes_heading') %></h2>
|
|---|
| 60 | <ul class="upd-changes">
|
|---|
| 61 | <% changes.forEach(function (c) { %>
|
|---|
| 62 | <li><span class="upd-ch-date"><%= c.date %></span> <span class="upd-ch-msg"><%= c.msg %></span></li>
|
|---|
| 63 | <% }) %>
|
|---|
| 64 | </ul>
|
|---|
| 65 | </section>
|
|---|
| 66 | <% } %>
|
|---|
| 67 | </div>
|
|---|
| 68 |
|
|---|
| 69 | <style>
|
|---|
| 70 | .admin-page { max-width: 700px; margin: 3rem auto; padding: 0 1rem; }
|
|---|
| 71 | .admin-page h1 { font-family: var(--font-display, serif); font-size: 2rem; margin: 0 0 .25rem; }
|
|---|
| 72 | .set-card { background: var(--paper-2); border: 1px solid var(--rule); border-radius: 12px; padding: 1.25rem; }
|
|---|
| 73 | .set-card h2 { font-family: var(--font-display, serif); font-size: 1.25rem; margin: 0 0 1rem; }
|
|---|
| 74 | .set-help { color: var(--ink-muted); font-size: .9rem; }
|
|---|
| 75 | .upd-row { display: flex; gap: 1rem; padding: .55rem 0; border-bottom: 1px solid var(--rule); align-items: baseline; }
|
|---|
| 76 | .upd-label { width: 90px; flex-shrink: 0; font-size: .8rem; font-weight: 600; color: var(--ink-muted); }
|
|---|
| 77 | .upd-val { font-size: .9rem; }
|
|---|
| 78 | .upd-val code { background: var(--paper); padding: .1rem .4rem; border-radius: 4px; font-size: .85em; }
|
|---|
| 79 | .upd-desc { color: var(--ink-muted); }
|
|---|
| 80 | .upd-status { margin: 1.1rem 0; }
|
|---|
| 81 | .upd-cmd { background: var(--paper); border: 1px solid var(--rule); border-radius: 6px; padding: .6rem .8rem; margin: 0; overflow-x: auto; }
|
|---|
| 82 | .upd-cmd code { font-size: .9em; }
|
|---|
| 83 | .upd-badge { font-size: .8rem; font-weight: 700; padding: .25rem .7rem; border-radius: 20px; }
|
|---|
| 84 | .upd-badge.ok { background: #d1fae5; color: #065f46; }
|
|---|
| 85 | .upd-badge.new { background: #fef3c7; color: #92400e; }
|
|---|
| 86 | .upd-badge.warn { background: #fee2e2; color: #991b1b; }
|
|---|
| 87 | .alert { padding: .75rem 1rem; border-radius: 6px; margin-bottom: 1rem; }
|
|---|
| 88 | .alert-success { background: #d1fae5; color: #065f46; border: 1px solid #a7f3d0; }
|
|---|
| 89 | .alert-error { background: #fee2e2; color: #991b1b; border: 1px solid #fca5a5; }
|
|---|
| 90 | .upd-changes { list-style: none; padding: 0; margin: 0; }
|
|---|
| 91 | .upd-changes li { display: flex; gap: .7rem; padding: .45rem 0; border-bottom: 1px solid var(--rule); font-size: .9rem; align-items: baseline; }
|
|---|
| 92 | .upd-changes li:last-child { border-bottom: 0; }
|
|---|
| 93 | .upd-ch-date { color: var(--ink-muted); flex-shrink: 0; font-size: .8rem; font-variant-numeric: tabular-nums; }
|
|---|
| 94 | .upd-ch-msg { word-break: break-word; }
|
|---|
| 95 | </style>
|
|---|