Bisher gab es den Aufklapper nur bei Meldungen mit Stack. Jetzt hat jede Zeile "details" mit Zeit, Release, voller URL, kompletter Meldung, Stack (oder Hinweis "no stack transmitted"), vollem User-Agent und anonymisierter IP, damit sich jeder Fehler bewerten laesst (Wunsch Harry). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
193 lines
10 KiB
HTML
193 lines
10 KiB
HTML
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
|
|
|
<h1>VITEC SSR — Control & Protocol</h1>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h3>Status</h3>
|
|
<table class="table table-striped">
|
|
<tr>
|
|
<th>SSR delivery</th>
|
|
<td>
|
|
<f:if condition="{flagOn}">
|
|
<f:then><span class="badge badge-success">ON — pages served from the VPS</span></f:then>
|
|
<f:else><span class="badge badge-secondary">OFF — SPA as before</span></f:else>
|
|
</f:if>
|
|
<br><small>Toggle: Site Management › Settings › VITEC</small>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>SSR server</th>
|
|
<td>
|
|
<f:if condition="{health}">
|
|
<f:then>
|
|
<span class="badge badge-success">running</span> {health.responseMs} ms<br>
|
|
Release <strong>{health.release.release}</strong>
|
|
(commit {health.release.commit})<br>
|
|
Cache: {health.cachedPages} pages / {health.cacheMB} MB
|
|
</f:then>
|
|
<f:else>
|
|
<span class="badge badge-danger">not reachable</span> {healthError}
|
|
</f:else>
|
|
</f:if>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Cache warming</th>
|
|
<td>
|
|
<f:if condition="{health.warming.running}">
|
|
<f:then>
|
|
<span class="badge badge-info">running</span>
|
|
{health.warming.done} of {health.warming.queued} pages rendered
|
|
({health.warming.skipped} still fresh, {health.warming.failed} failed)
|
|
<br><small>Reload this module to follow the progress.</small>
|
|
</f:then>
|
|
<f:else>
|
|
<f:if condition="{health.warming.startedAt}">
|
|
<f:then>
|
|
Last run: {health.warming.done} of {health.warming.queued} pages rendered
|
|
({health.warming.skipped} still fresh,
|
|
<f:if condition="{health.warming.failed}">
|
|
<f:then><span class="badge badge-danger">{health.warming.failed} failed</span></f:then>
|
|
<f:else>0 failed</f:else>
|
|
</f:if>)
|
|
<f:if condition="{health.warming.aborted}"> <span class="badge badge-warning">aborted by restart</span></f:if>
|
|
</f:then>
|
|
<f:else><span class="text-muted">no run since the server started</span></f:else>
|
|
</f:if>
|
|
</f:else>
|
|
</f:if>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Purge configuration</th>
|
|
<td>
|
|
<f:if condition="{configured}">
|
|
<f:then><span class="badge badge-success">ok</span></f:then>
|
|
<f:else><span class="badge badge-warning">var/vitec-ssr-purge.env missing</span></f:else>
|
|
</f:if>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h3>Purge cache</h3>
|
|
<form action="{f:be.uri(route: 'web_vitecssr.purge')}" method="post" class="form-group">
|
|
<label for="purge-paths">Paths (one per line or comma-separated):</label>
|
|
<textarea class="form-control" id="purge-paths" name="paths" rows="3"
|
|
placeholder="/products/aligo /insights/news"></textarea>
|
|
<input type="hidden" name="mode" value="paths">
|
|
<div style="margin-top:8px">
|
|
<button type="submit" class="btn btn-primary">Purge these paths</button>
|
|
</div>
|
|
</form>
|
|
<form action="{f:be.uri(route: 'web_vitecssr.purge')}" method="post"
|
|
onsubmit="return confirm('Really purge the ENTIRE SSR cache?');">
|
|
<input type="hidden" name="mode" value="all">
|
|
<button type="submit" class="btn btn-warning">Purge everything (*)</button>
|
|
<small class="text-muted"> — needed after menu/footer changes while auto-purge is off</small>
|
|
</form>
|
|
|
|
<h3 style="margin-top:24px">Warm cache</h3>
|
|
<form action="{f:be.uri(route: 'web_vitecssr.warm')}" method="post"
|
|
onsubmit="return confirm('This renders every sitemap page (800+) in the background and sends several thousand requests to TYPO3 within a few minutes. Continue?');">
|
|
<button type="submit" class="btn btn-success">Warm cache (all sitemap pages)</button>
|
|
<small class="text-muted"> — pre-renders every page in the background, so no visitor has to
|
|
wait for a first render. Useful after "Purge everything" or a deploy; takes a few minutes.</small>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<h3>Purge protocol <small>(last 30)</small></h3>
|
|
<table class="table table-striped table-sm">
|
|
<thead><tr><th>Time</th><th>Trigger</th><th>Paths</th><th>Result</th><th>Duration</th></tr></thead>
|
|
<tbody>
|
|
<f:for each="{purgeLog}" as="row">
|
|
<tr>
|
|
<td><f:format.date format="Y-m-d H:i:s">@{row.crdate}</f:format.date></td>
|
|
<td>{row.trigger_source}</td>
|
|
<td><code>{row.paths}</code></td>
|
|
<td>
|
|
<f:if condition="{row.success}">
|
|
<f:then><span class="badge badge-success">ok</span></f:then>
|
|
<f:else><span class="badge badge-danger">HTTP {row.http_code}</span> {row.message}</f:else>
|
|
</f:if>
|
|
</td>
|
|
<td>{row.duration_ms} ms</td>
|
|
</tr>
|
|
</f:for>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3>Health samples <small>(last 10 — history via cron <code>vitec:ssr-health</code>, every 5 minutes)</small></h3>
|
|
<table class="table table-striped table-sm">
|
|
<thead><tr><th>Time</th><th>Status</th><th>Release</th><th>Cache</th><th>Response time</th></tr></thead>
|
|
<tbody>
|
|
<f:for each="{healthLog}" as="row">
|
|
<tr>
|
|
<td><f:format.date format="Y-m-d H:i:s">@{row.crdate}</f:format.date></td>
|
|
<td>
|
|
<f:if condition="{row.ok}">
|
|
<f:then><span class="badge badge-success">ok</span></f:then>
|
|
<f:else><span class="badge badge-danger">down</span></f:else>
|
|
</f:if>
|
|
</td>
|
|
<td>{row.release_id}</td>
|
|
<td>{row.cached_pages} pages / {row.cache_mb} MB</td>
|
|
<td>{row.response_ms} ms</td>
|
|
</tr>
|
|
</f:for>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3>Client errors <small>(last 24 h — {clientErrorCount24h} report<f:if condition="{clientErrorCount24h} != 1">s</f:if>, newest 50 shown)</small></h3>
|
|
<p class="text-muted" style="font-size:11px">
|
|
Browser-side failures reported by the React frontend via <code>POST /api/vitec/client-error</code>
|
|
(JS errors, unhandled promise rejections, element error boundaries and "chunk reload" after a deploy).
|
|
Limited to 30 reports per minute and IP, 8 KB per report; IPs are stored anonymized.
|
|
Kept for 30 days (cron <code>vitec:client-error-cleanup</code>).
|
|
</p>
|
|
<table class="table table-striped table-sm">
|
|
<thead><tr><th>Time</th><th>Release</th><th>Message</th><th>Page</th><th>Browser</th><th>IP</th></tr></thead>
|
|
<tbody>
|
|
<f:for each="{clientErrors}" as="row">
|
|
<tr>
|
|
<td style="white-space:nowrap"><f:format.date format="Y-m-d H:i:s">@{row.crdate}</f:format.date></td>
|
|
<td><code>{row.release}</code></td>
|
|
<td>
|
|
<f:if condition="{row.is_chunk_reload}">
|
|
<f:then><span class="badge badge-warning">chunk reload</span> </f:then>
|
|
<f:else><span class="badge badge-danger">error</span> </f:else>
|
|
</f:if>
|
|
{row.message}
|
|
<details style="margin-top:4px">
|
|
<summary style="cursor:pointer" class="text-muted">details</summary>
|
|
<table class="table table-sm" style="margin:6px 0 0;font-size:11px;background:#fff">
|
|
<tr><th style="width:90px">Reported</th><td><f:format.date format="Y-m-d H:i:s">@{row.crdate}</f:format.date> (entry #{row.uid})</td></tr>
|
|
<tr><th>Release</th><td><code>{row.release}</code><f:if condition="!{row.release}"><span class="text-muted">not transmitted</span></f:if></td></tr>
|
|
<tr><th>URL</th><td style="word-break:break-all"><a href="{row.url}" target="_blank" rel="noopener">{row.url}</a><f:if condition="!{row.url}"><span class="text-muted">not transmitted</span></f:if></td></tr>
|
|
<tr><th>Message</th><td style="white-space:pre-wrap;word-break:break-word">{row.message}</td></tr>
|
|
<tr><th>Stack</th><td>
|
|
<f:if condition="{row.stack}">
|
|
<f:then><pre style="font-size:11px;white-space:pre-wrap;max-height:320px;overflow:auto;margin:0">{row.stack}</pre></f:then>
|
|
<f:else><span class="text-muted">no stack transmitted</span></f:else>
|
|
</f:if>
|
|
</td></tr>
|
|
<tr><th>Browser</th><td style="word-break:break-all">{row.user_agent}<f:if condition="!{row.user_agent}"><span class="text-muted">not transmitted</span></f:if></td></tr>
|
|
<tr><th>IP</th><td>{row.ip} <span class="text-muted">(anonymized)</span></td></tr>
|
|
</table>
|
|
</details>
|
|
</td>
|
|
<td><a href="{row.url}" target="_blank" rel="noopener" title="{row.url}">{row.path}</a></td>
|
|
<td title="{row.user_agent}"><small>{row.ua_short}</small></td>
|
|
<td><small>{row.ip}</small></td>
|
|
</tr>
|
|
</f:for>
|
|
<f:if condition="!{clientErrors}">
|
|
<tr><td colspan="6" class="text-muted">No client errors reported in the last 24 hours.</td></tr>
|
|
</f:if>
|
|
</tbody>
|
|
</table>
|
|
|
|
</html>
|