v0.280.0: the setup gate (decision 46); R-710 'I changed it' + absent-record window; R-709 password fields off the page; password:N:special generator
gates / gates (push) Successful in 25s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-29 08:51:46 +02:00
parent 2548b4c924
commit 7fa8768cfd
37 changed files with 4015 additions and 107 deletions
+47 -11
View File
@@ -744,15 +744,12 @@
class="form-control" value=""
placeholder="Legalább 12 karakter"
data-field-type="password"
required
>
required>
<button type="button" class="btn btn-sm btn-outline pw-toggle-btn"
onclick="togglePasswordField('field-ADMIN_PASSWORD', 'field-confirm-ADMIN_PASSWORD', this)"
title="Megjelenítés">&#128065;</button>
<button type="button" class="btn btn-sm btn-outline"
onclick="generatePassword('field-ADMIN_PASSWORD', 'field-confirm-ADMIN_PASSWORD')">Generálás</button>
onclick="generatePassword('field-ADMIN_PASSWORD', 'field-confirm-ADMIN_PASSWORD', '')">Generálás</button>
</div>
<div class="input-with-button" style="margin-top:.25rem">
@@ -1022,6 +1019,30 @@ document.addEventListener('DOMContentLoaded', function() {
function revealPasswordField(stackName, envVar, btn) {
var el = document.getElementById('field-' + envVar);
if (!el) return;
if (btn.dataset.shown === '1') {
el.value = '';
el.type = 'password';
btn.dataset.shown = '';
return;
}
btn.disabled = true;
fetch('/stacks/' + encodeURIComponent(stackName) + '/auto-field/reveal', {
method: 'POST',
headers: Object.assign({'Content-Type': 'application/x-www-form-urlencoded'}, csrfHeaders()),
credentials: 'same-origin',
body: 'env_var=' + encodeURIComponent(envVar)
}).then(function (r) { return r.json(); }).then(function (j) {
btn.disabled = false;
if (!j.ok) { showAlert(j.error || 'A lekérés nem sikerült.'); return; }
el.value = j.data.value;
el.type = 'text';
btn.dataset.shown = '1';
}).catch(function () { btn.disabled = false; showAlert('A lekérés nem sikerült.'); });
}
function revealAutoField(stackName, envVar, btn) {
var el = document.getElementById('auto-field-' + envVar);
if (!el) return;
@@ -1053,13 +1074,28 @@ function toggleAutoField(fieldId, btn) {
el.type = el.type === 'password' ? 'text' : 'password';
btn.textContent = el.type === 'password' ? 'Megjelenítés' : 'Elrejtés';
}
function generatePassword(fieldId, confirmFieldId) {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
function generatePassword(fieldId, confirmFieldId, spec) {
const letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const SPECIAL = '-_.!@#%+=';
var parts = (spec || '').split(':');
var n = parseInt(parts[1], 10);
if (parts[0] !== 'password' || !(n >= 12 && n <= 64)) { n = 16; }
var special = parts[2] === 'special';
var chars = special ? letters + SPECIAL : letters;
let pass = '';
const arr = new Uint8Array(16);
crypto.getRandomValues(arr);
for (let i = 0; i < 16; i++) {
pass += chars[arr[i] % chars.length];
for (;;) {
pass = '';
const arr = new Uint32Array(n);
crypto.getRandomValues(arr);
for (let i = 0; i < n; i++) {
pass += chars[arr[i] % chars.length];
}
if (!special || (/[a-z]/.test(pass) && /[A-Z]/.test(pass) && /[0-9]/.test(pass) && /[-_.!@#%+=]/.test(pass) && /^[A-Za-z0-9]/.test(pass))) {
break;
}
}
document.getElementById(fieldId).value = pass;
if (confirmFieldId) {