kept data: the choice at reinstall, the list, the read-only view, the load (09 decision 36); R-690 fixed
gates / gates (push) Successful in 26s
gates / gates (push) Successful in 26s
An install over an app's kept drive folder (appdata/<app> non-empty) asks the household: "use my kept data" (a load from the newest copy of THIS drive's install, own unit or second-drive mirror, then the template's after_load) or "start fresh" (the folder is renamed into <drive>/kept/<app>/<date>/ with the removed app's unit; nothing deleted). The install API answers 409 kept_data_choice until one is chosen; DeployStack refuses too. New page Megorzott adatok / Kept data (/kept-data): Load / Look / Delete (typed confirmation, the only deletion of kept data). FileBrowser gets a read-only source. The drive-full warning names the kept folders. <drive>/kept is protected and outside every backup leg. R-690: the removed-app restore (R-487) never found a unit on a DATA drive — it asked GetStackComposePath (true for every catalog app) and restored nextcloud with no env. Now isStackDeployed; pinned with a production-shaped provider. Red-proofs: audits/night-2026-09-26/E/redproofs/. 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:
@@ -449,6 +449,7 @@
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
<div id="kept-choice" class="kept-choice card" hidden></div>
|
||||
<form id="deploy-form" class="deploy-form">
|
||||
{{/* R-351 SCENARIO A — the values below came from this app's OWN backup, so a reinstall does
|
||||
not ask the customer to remember what the backup already recorded. Stated, never silent:
|
||||
@@ -680,6 +681,32 @@ var postDeployInfo = {
|
||||
deployFields: {{json .Meta.DeployFields}}
|
||||
};
|
||||
|
||||
// showKeptChoice renders the two choices of `09` §3 decision 36. Every sentence arrives rendered in the
|
||||
// reader's language from the server (textContent only — nothing is parsed as HTML).
|
||||
function showKeptChoice(d, form) {
|
||||
var box = document.getElementById('kept-choice');
|
||||
box.textContent = '';
|
||||
function el(tag, cls, text) { var x = document.createElement(tag); if (cls) x.className = cls; if (text) x.textContent = text; return x; }
|
||||
box.appendChild(el('h3', 'kept-choice-title', d.title));
|
||||
box.appendChild(el('p', 'kept-choice-body', d.body));
|
||||
function option(label, desc, choice, enabled, extra) {
|
||||
var o = el('div', 'kept-choice-option');
|
||||
var b = el('button', enabled ? 'btn btn-primary' : 'btn btn-outline', label);
|
||||
b.type = 'button';
|
||||
b.disabled = !enabled;
|
||||
b.setAttribute('data-kept-choice', choice);
|
||||
b.addEventListener('click', function () { window.keptChoice = choice; box.hidden = true; form.requestSubmit(); });
|
||||
o.appendChild(b);
|
||||
o.appendChild(el('p', 'kept-choice-desc', desc));
|
||||
if (extra) o.appendChild(el('p', 'kept-choice-note', extra));
|
||||
return o;
|
||||
}
|
||||
box.appendChild(option(d.use_label, d.use_offered ? d.use_desc : d.use_off, 'use', d.use_offered, ''));
|
||||
box.appendChild(option(d.fresh_label, d.fresh_desc, 'fresh', true, d.not_backed_up));
|
||||
box.hidden = false;
|
||||
box.scrollIntoView({behavior: 'smooth'});
|
||||
}
|
||||
|
||||
function buildPostDeployCard(stackName) {
|
||||
var subdomain = '';
|
||||
var sdField = document.getElementById('field-SUBDOMAIN');
|
||||
@@ -992,9 +1019,16 @@ document.getElementById('deploy-form').addEventListener('submit', async function
|
||||
var resp = await fetch('/api/stacks/' + stackName + '/deploy', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({values: values})
|
||||
body: JSON.stringify({values: values, kept_data: window.keptChoice || ''})
|
||||
});
|
||||
var data = await resp.json();
|
||||
// `09` §3 decision 36: the drive already holds this app's old data — the household chooses.
|
||||
if (!data.ok && data.data && data.data.code === 'kept_data_choice') {
|
||||
showKeptChoice(data.data, e.target);
|
||||
btn.textContent = '{{T "deploy.telepites_inditasa"}}';
|
||||
btn.disabled = false;
|
||||
return;
|
||||
}
|
||||
if (!data.ok) {
|
||||
showAlert('{{T "common.hiba_kettospont"}} ' + data.error);
|
||||
btn.textContent = '{{T "deploy.telepites_inditasa"}}';
|
||||
@@ -1015,7 +1049,7 @@ document.getElementById('deploy-form').addEventListener('submit', async function
|
||||
|
||||
// Phase 2: Poll stack status
|
||||
var startTime = Date.now();
|
||||
var pollTimeout = 120000;
|
||||
var pollTimeout = window.keptChoice === 'use' ? 900000 : 120000; // a load restores a backup first
|
||||
var pollTimer = setInterval(async function() {
|
||||
var elapsed = Math.round((Date.now() - startTime) / 1000);
|
||||
elapsedEl.textContent = elapsed + ' {{T "deploy.masodperce"}}';
|
||||
|
||||
Reference in New Issue
Block a user