feat(panel): detect import encryption from file extension, not a checkbox
The import form used to ask users to tick "the file is encrypted" before showing the password field, even though the server already decides purely from the envelope magic bytes. Reveal the password field automatically for a .spde file (hide it for .json) so the checkbox is no longer needed.
This commit is contained in:
@@ -117,6 +117,7 @@ func TestBackupPageOffersEncryption(t *testing.T) {
|
||||
for _, want := range []string{
|
||||
`name="encrypt"`, `name="password"`, `name="password_confirm"`,
|
||||
`name="import_password"`, "data-encrypt-toggle", "data-encrypt-fields",
|
||||
"data-import-password-fields",
|
||||
fmt.Sprintf("at least %d characters", minSecretFilePasswordLen),
|
||||
"The two passwords do not match.",
|
||||
} {
|
||||
|
||||
@@ -335,20 +335,24 @@ button.copy { flex: none; margin-top: 0.3rem; }
|
||||
}
|
||||
.actions button.danger:hover, .actions a.danger:hover, .nav button.danger:hover { background: var(--danger-bg-hover); }
|
||||
|
||||
/* The optional "encrypt this download" block on the backup, export and import
|
||||
forms. Its label is the one checkbox in the panel, so it opts out of the
|
||||
/* The optional "encrypt this download" block on the backup and export forms.
|
||||
Its label is the one checkbox in the panel, so it opts out of the
|
||||
block-level label rule above and sits on one line with its box; the fields it
|
||||
reveals are indented under it to read as its consequence rather than as three
|
||||
more fields of the form. panel.js hides the inner block until the box is
|
||||
ticked (and empties it when unticked); without JavaScript everything stays
|
||||
visible, which the server handles identically. */
|
||||
visible, which the server handles identically. The import form reuses the
|
||||
same indented .encrypt-fields look for its password field, but reveals it
|
||||
by file extension instead of a checkbox (see panel.js). */
|
||||
.encrypt { margin-top: 1.2rem; }
|
||||
.encrypt label.check {
|
||||
display: flex; align-items: center; gap: 0.5rem; margin: 0; font-weight: 600;
|
||||
}
|
||||
.encrypt label.check input { width: auto; margin: 0; }
|
||||
.encrypt-fields {
|
||||
margin-left: 1.6rem; padding-left: 0.9rem; border-left: 2px solid var(--border);
|
||||
margin-top: 1.2rem; margin-left: 1.6rem; padding-left: 0.9rem;
|
||||
border-left: 2px solid var(--border);
|
||||
}
|
||||
.encrypt .encrypt-fields { margin-top: 0; }
|
||||
.encrypt-fields label { margin-top: 0.7rem; }
|
||||
.encrypt-fields .muted { margin: 0.5rem 0 0; font-size: 0.85rem; }
|
||||
|
||||
@@ -102,6 +102,37 @@
|
||||
});
|
||||
}
|
||||
|
||||
// --- Import password field shown based on the chosen file's extension ---
|
||||
// The domain-import file decides for itself whether it is encrypted (the
|
||||
// server checks the envelope magic, not a checkbox), so the panel offers
|
||||
// the password field the same way: reveal it for a .spde file, hide and
|
||||
// clear it for a plain .json one. An unrecognised name leaves the field
|
||||
// visible rather than guessing wrong and hiding a password the file needs.
|
||||
function syncImportPasswordField(input) {
|
||||
var form = input.closest("form");
|
||||
var fields = form && form.querySelector("[data-import-password-fields]");
|
||||
if (!fields) {
|
||||
return;
|
||||
}
|
||||
var name = (input.files && input.files[0] && input.files[0].name || "").toLowerCase();
|
||||
var hide = name !== "" && /\.json$/.test(name);
|
||||
fields.hidden = hide;
|
||||
if (hide) {
|
||||
fields.querySelectorAll("input").forEach(function (pw) {
|
||||
pw.value = "";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initImportPasswordField(root) {
|
||||
root.querySelectorAll("input[data-import-file]").forEach(function (input) {
|
||||
syncImportPasswordField(input);
|
||||
input.addEventListener("change", function () {
|
||||
syncImportPasswordField(input);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// --- Section index follows the page -----------------------------------
|
||||
// The long pages list their own sections in the navigation column (the
|
||||
// "sections" template). Marking the one currently in view turns that list
|
||||
@@ -167,6 +198,7 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
initAddressFields(document);
|
||||
initEncryptFields(document);
|
||||
initImportPasswordField(document);
|
||||
initSectionIndex();
|
||||
});
|
||||
|
||||
|
||||
@@ -28,16 +28,10 @@
|
||||
{{if .ImportErr}}<p class="error">{{.ImportErr}}</p>{{end}}
|
||||
<form method="post" action="/domains/import" enctype="multipart/form-data">
|
||||
<label for="importfile">Domain export file</label>
|
||||
<input id="importfile" name="file" type="file" accept=".json,.spde,application/json" required>
|
||||
<div class="encrypt">
|
||||
<label class="check">
|
||||
<input type="checkbox" data-encrypt-toggle>
|
||||
<span>The file is encrypted (<code>.spde</code>)</span>
|
||||
</label>
|
||||
<div class="encrypt-fields" data-encrypt-fields>
|
||||
<label for="importpw">Password</label>
|
||||
<input id="importpw" name="import_password" type="password" autocomplete="off">
|
||||
</div>
|
||||
<input id="importfile" name="file" type="file" accept=".json,.spde,application/json" required data-import-file>
|
||||
<div class="encrypt-fields" data-import-password-fields>
|
||||
<label for="importpw">Password</label>
|
||||
<input id="importpw" name="import_password" type="password" autocomplete="off">
|
||||
</div>
|
||||
<button type="submit">Import domain</button>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user