feat: password fields with masked input, reveal toggle, confirmation
- Password deploy fields now use type=password (masked by default) - Added eye toggle button to reveal/hide password and confirm fields - Added confirmation field below each password input - Generate button fills both password and confirmation fields - Form validation checks password confirmation matches before deploy - Confirmation field only shown for new deployments (not already deployed) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -308,15 +308,25 @@
|
||||
</select>
|
||||
{{else if eq .Type "password"}}
|
||||
<div class="input-with-button">
|
||||
<input type="text" id="field-{{.EnvVar}}" name="{{.EnvVar}}"
|
||||
<input type="password" id="field-{{.EnvVar}}" name="{{.EnvVar}}"
|
||||
class="form-control" value="{{.Default}}"
|
||||
placeholder="{{.Placeholder}}"
|
||||
data-field-type="password"
|
||||
required
|
||||
{{if $.AlreadyDeployed}}disabled{{end}}>
|
||||
<button type="button" class="btn btn-sm btn-outline pw-toggle-btn"
|
||||
onclick="togglePasswordField('field-{{.EnvVar}}', 'field-confirm-{{.EnvVar}}', this)"
|
||||
title="Megjelenítés">👁</button>
|
||||
<button type="button" class="btn btn-sm btn-outline"
|
||||
onclick="generatePassword('field-{{.EnvVar}}')">Generálás</button>
|
||||
onclick="generatePassword('field-{{.EnvVar}}', 'field-confirm-{{.EnvVar}}')">Generálás</button>
|
||||
</div>
|
||||
{{if not $.AlreadyDeployed}}
|
||||
<div class="input-with-button" style="margin-top:.25rem">
|
||||
<input type="password" id="field-confirm-{{.EnvVar}}"
|
||||
class="form-control" placeholder="Jelszó megerősítése"
|
||||
data-confirm-for="field-{{.EnvVar}}">
|
||||
</div>
|
||||
{{end}}
|
||||
{{else if eq .Type "boolean"}}
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="field-{{.EnvVar}}" name="{{.EnvVar}}" value="true"
|
||||
@@ -549,7 +559,7 @@ function toggleAutoField(fieldId, btn) {
|
||||
el.type = el.type === 'password' ? 'text' : 'password';
|
||||
btn.textContent = el.type === 'password' ? 'Megjelenítés' : 'Elrejtés';
|
||||
}
|
||||
function generatePassword(fieldId) {
|
||||
function generatePassword(fieldId, confirmFieldId) {
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let pass = '';
|
||||
const arr = new Uint8Array(16);
|
||||
@@ -558,6 +568,19 @@ function generatePassword(fieldId) {
|
||||
pass += chars[arr[i] % chars.length];
|
||||
}
|
||||
document.getElementById(fieldId).value = pass;
|
||||
if (confirmFieldId) {
|
||||
var ce = document.getElementById(confirmFieldId);
|
||||
if (ce) ce.value = pass;
|
||||
}
|
||||
}
|
||||
function togglePasswordField(fieldId, confirmFieldId, btn) {
|
||||
var el = document.getElementById(fieldId);
|
||||
if (!el) return;
|
||||
var newType = el.type === 'password' ? 'text' : 'password';
|
||||
el.type = newType;
|
||||
var ce = document.getElementById(confirmFieldId);
|
||||
if (ce) ce.type = newType;
|
||||
btn.title = newType === 'password' ? 'Megjelenítés' : 'Elrejtés';
|
||||
}
|
||||
|
||||
function deleteStaleData(stackName, stalePath, btn) {
|
||||
@@ -620,6 +643,18 @@ document.getElementById('deploy-form').addEventListener('submit', async function
|
||||
}
|
||||
}
|
||||
|
||||
// Client-side validation: check password confirmation matches
|
||||
const confirmInputs = e.target.querySelectorAll('input[data-confirm-for]');
|
||||
for (const ci of confirmInputs) {
|
||||
const mainEl = document.getElementById(ci.getAttribute('data-confirm-for'));
|
||||
if (mainEl && !mainEl.disabled && ci.value !== mainEl.value) {
|
||||
const label = mainEl.closest('.form-group').querySelector('label').textContent.trim();
|
||||
showAlert('A két jelszó nem egyezik: ' + label);
|
||||
ci.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Client-side validation: check subdomain format
|
||||
const subdomainField = e.target.querySelector('.subdomain-input');
|
||||
if (subdomainField && !subdomainField.disabled) {
|
||||
|
||||
Reference in New Issue
Block a user