Agreed with the owner on 2026-10-13, taken up on 2026-10-16 ("Да"); the
mockup is 1888dce. Both secret downloads - the full backup on Server ›
Backup and a domain's export on its settings - ask for "Your account
password": reauth (handlers_backup.go) checks the signed-in user's own
password with bcrypt, as a password change does, under the same limiter
(auth.AllowPasswordChange: ten in fifteen minutes per user, shared with the
Account form). A wrong or missing one is 401 on the same page with "Your
account password is incorrect.", logged; nothing is sent. The import and
the selfpost-backup CLI are not asked.
Also, the owner's phone screenshot of 2026-10-16 ("кнопка выбора файла не
такая, как в макете. Это косяк?"): the import's file field was a bare
<input type="file"> drawn by the browser ("Browse…"), where the mockup has
Bulma's .file - "Choose a file…" and the name chosen. The template now
follows the mockup; panel.js writes the name into .file-name on change and
at load (testdata/filename_test.js, run by TestPanelJS). No other file
field exists.
Tests: TestSecretDownloadsAskForTheAccountPassword (missing, wrong, right,
the limiter), the export refusals of the settings page with the password,
cmd/panel's restore tests and e2e's backups with it, e2e's refusal of a
backup without and with a wrong password. go vet + go test (26 packages)
and make e2e on the test server for this tree.
Docs: security.md § Backup and domain export, guide § Full backup and
restore, help (word for word with the mockup); CHANGELOG [Unreleased];
roadmap row done.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
51 lines
4.0 KiB
HTML
51 lines
4.0 KiB
HTML
{{/* Backup (mockup backup.html): the whole instance in one archive, and one
|
|
domain brought in from another SelfPost. Two boxes, each its own form and
|
|
its own POST; a refusal of either comes back as the flash above them. The
|
|
password fields of the full backup are shown when the box is ticked (panel.js
|
|
reads data-encrypt-toggle / data-encrypt-fields); the import asks for a
|
|
password only for an encrypted file (data-import-file /
|
|
data-import-password-fields). Without scripts all of them are visible. */}}
|
|
{{define "content"}}
|
|
{{template "page_head" .Head}}
|
|
{{if .Flash}}{{template "flash" .Flash}}{{end}}
|
|
|
|
<div class="columns">
|
|
<div class="column is-6">
|
|
{{template "box_open" .Full}}
|
|
{{template "body_open"}}
|
|
<p class="mb-3"><code>data/</code> (database, DKIM keys, credentials), <code>docker-compose.yml</code>, <code>.env</code> and <code>certs/</code> in one archive. The Postfix queue is left out: it cannot be copied reliably while the server is running. Restore into an empty project directory and start the <strong>same SelfPost version</strong>. The reverse-proxy vhost is not included.</p>
|
|
<form class="sp-form" method="post" action="{{.FullAction}}">
|
|
<div class="field"><label class="checkbox"><input type="checkbox" name="encrypt" value="1"{{if .Encrypt}} checked{{end}} data-encrypt-toggle> Encrypt with a password (<code>.spbk</code>)</label></div>
|
|
<div data-encrypt-fields>
|
|
<div class="sp-pair">
|
|
<div class="field"><label class="label" for="encpw">Password</label><div class="control"><input class="input" id="encpw" name="password" type="password" autocomplete="new-password" minlength="{{.PasswordMin}}" placeholder="at least {{.PasswordMin}} characters"></div></div>
|
|
<div class="field"><label class="label" for="encpw2">Repeat password</label><div class="control"><input class="input" id="encpw2" name="password_confirm" type="password" autocomplete="new-password"></div></div>
|
|
</div>
|
|
<p class="help mb-3">Keep this password: SelfPost does not store it, and the file cannot be opened without it.</p>
|
|
</div>
|
|
<div class="field"><label class="label" for="acpw">Your account password</label><div class="control"><input class="input" id="acpw" name="account_password" type="password" autocomplete="current-password" required></div>
|
|
<p class="help">The one you sign in with, asked at every download: the archive holds every secret of the server, and a session left open must not be enough to take it.</p></div>
|
|
<button type="submit" class="button is-primary"><i class="ti ti-download"></i>Download full backup</button>
|
|
</form>
|
|
{{template "body_close"}}
|
|
{{template "box_close"}}
|
|
</div>
|
|
|
|
<div class="column is-6">
|
|
{{template "box_open" .Import}}
|
|
{{template "body_open"}}
|
|
<p class="mb-3">A domain export from another instance — plain <code>.json</code> or encrypted <code>.spde</code>. Its DKIM key and application passwords come across, so the published DNS needs no change. The export itself is made on the domain's settings page.</p>
|
|
<form class="sp-form" method="post" action="{{.ImportAction}}" enctype="multipart/form-data">
|
|
<div class="field"><label class="label" for="importfile">Domain export file</label>
|
|
<div class="file has-name is-fullwidth"><label class="file-label"><input class="file-input" id="importfile" name="file" type="file" accept=".json,.spde,application/json" required data-import-file><span class="file-cta"><i class="ti ti-upload"></i><span class="file-label ml-2">Choose a file…</span></span><span class="file-name">No file chosen</span></label></div></div>
|
|
<div class="field" data-import-password-fields><label class="label" for="importpw">Password</label>
|
|
<div class="control"><input class="input" id="importpw" name="import_password" type="password" autocomplete="off"></div>
|
|
<p class="help">Asked only for an encrypted <code>.spde</code> file.</p></div>
|
|
<button type="submit" class="button is-primary"><i class="ti ti-file-import"></i>Import domain</button>
|
|
</form>
|
|
{{template "body_close"}}
|
|
{{template "box_close"}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|