Files
VITEC-website/packages/vitec/Resources/Public/Javascript/ssr-warm-progress.js
T
orascheandClaude Fable 5.1 086a178341 SSR module: rebuilt on TYPO3 module layout, cards and core components
Ticket 0096-FE (aehm): the module now uses the core Module layout (DocHeader with reload and shortcut, module-body spacing), a Status card as description list with one badge style, an Actions card with core buttons and icons, help texts below the buttons and TYPO3 modal confirmations (ssr-module.js) instead of window.confirm, and three protocol cards. Purge protocol rows show a one-line time, the trigger as badge plus user, paths as count + first two + disclosure, result and duration right-aligned, with 30/100/500 row switches. Status shows the last health sample with relative age and the cache limit (new optional SSR_CACHE_LIMIT_MB in var/vitec-ssr-purge.env, SsrPurgeService::getCacheLimitMb). ssr-warm-progress.js now also disables the Warm button with a spinner while a run is active. backend-ssr.css holds only layout glue with core CSS variables (dark mode safe). Verified in the backend: layout, modal, running progress bar, protocol tables.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-11 10:15:42 +01:00

89 lines
3.3 KiB
JavaScript

/**
* Live progress for the "Cache warming" row of the VITEC SSR module.
* Polls the vitec_ssr_health ajax route (GET /__health of the node):
* every 2 s while a warm run is active, every 15 s otherwise so runs
* started from the CLI or the context menu show up without a reload.
* Also refreshes the "Cache: N pages / M MB" line and disables the
* "Warm cache" button while a run is active.
*/
import AjaxRequest from '@typo3/core/ajax/ajax-request.js';
const cell = document.getElementById('ssr-warm-status');
if (cell) {
const wrap = document.getElementById('ssr-warm-progress');
const bar = document.getElementById('ssr-warm-bar');
const text = document.getElementById('ssr-warm-text');
const cacheLine = document.getElementById('ssr-cache-line');
const warmButton = document.getElementById('ssr-warm-button');
let timer = null;
const num = (v) => (typeof v === 'number' ? v : 0);
const render = (h) => {
const w = h.warming || {};
const queued = num(w.queued);
const handled = num(w.done) + num(w.skipped) + num(w.failed);
const pct = queued > 0 ? Math.min(100, Math.round((handled / queued) * 100)) : 0;
const failed = num(w.failed)
? '<span class="badge badge-danger">' + num(w.failed) + ' failed</span>'
: '0 failed';
if (w.running) {
wrap.style.display = '';
bar.style.width = pct + '%';
bar.textContent = pct + ' %';
text.innerHTML = '<span class="badge badge-info">running</span> '
+ handled + ' of ' + queued + ' pages ('
+ num(w.done) + ' rendered, ' + num(w.skipped) + ' still fresh, ' + failed + ')'
+ '<div class="form-text">Live - updates every 2 seconds.</div>';
} else if (w.startedAt) {
wrap.style.display = 'none';
const finished = w.finishedAt
? ' <span class="text-body-secondary">finished ' + new Date(w.finishedAt).toLocaleTimeString() + '</span>'
: '';
text.innerHTML = 'Last run: ' + num(w.done) + ' of ' + queued + ' pages rendered ('
+ num(w.skipped) + ' still fresh, ' + failed + ')'
+ (w.aborted ? ' <span class="badge badge-warning">aborted by restart</span>' : '')
+ finished;
} else {
wrap.style.display = 'none';
text.innerHTML = '<span class="text-body-secondary">no run since the server started</span>';
}
if (cacheLine && typeof h.cachedPages === 'number') {
const limit = parseInt(cacheLine.dataset.limit || '0', 10);
cacheLine.textContent = 'Cache: ' + h.cachedPages + ' pages · ' + num(h.cacheMB) + ' MB'
+ (limit > 0 ? ' of ' + limit + ' MB' : '');
}
if (warmButton) {
warmButton.disabled = !!w.running;
warmButton.classList.toggle('is-running', !!w.running);
}
return !!w.running;
};
const schedule = (ms) => {
clearTimeout(timer);
timer = setTimeout(tick, ms);
};
const tick = () => {
new AjaxRequest(TYPO3.settings.ajaxUrls.vitec_ssr_health).get()
.then((response) => response.resolve())
.then((data) => {
const running = data && data.health ? render(data.health) : false;
schedule(running ? 2000 : 15000);
})
.catch(() => schedule(15000));
};
schedule(cell.dataset.running === '1' ? 1000 : 15000);
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
clearTimeout(timer);
} else {
tick();
}
});
}