fix(portal): extract inline JS to static file to resolve Qute {#raw} parse error
Deploy to Production / deploy (push) Successful in 3m5s

Quarkus 3.15 removed the {#raw} section helper. Moving the dashboard map/expires
JS to META-INF/resources/dashboard.js (served as static asset, bypasses Qute)
fixes the portal startup failure in CI.

Co-Authored-By: Mira <noreply@anthropic.com>
This commit is contained in:
Carsten Rehfeld
2026-05-14 16:01:07 +02:00
parent 68b98589cb
commit 8bc0ce1fb8
2 changed files with 101 additions and 104 deletions
@@ -0,0 +1,100 @@
(function () {
var data = window.__D || {};
var visits = data.recentVisits || [];
var hasRegistrar = typeof data.registrarLat === 'number' && typeof data.registrarLon === 'number';
// ── Expires display ──────────────────────────────────────────────────────
var expiresEl = document.getElementById('expires-val');
var expiresSub = document.getElementById('expires-sub');
if (data.expiresAt && expiresEl) {
var d = new Date(data.expiresAt);
expiresEl.textContent = d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
var days = Math.ceil((d - Date.now()) / 86400000);
expiresSub.textContent = days > 0 ? days + ' days remaining' : 'expired';
}
// ── Map ──────────────────────────────────────────────────────────────────
var wrap = document.getElementById('map-wrap');
var svg = d3.select('#world-map');
var W = wrap.clientWidth || 800;
var H = Math.round(W * 0.48);
svg.attr('viewBox', '0 0 ' + W + ' ' + H)
.attr('width', W)
.attr('height', H);
var projection = d3.geoNaturalEarth1()
.scale(W / 6.3)
.translate([W / 2, H / 2]);
var path = d3.geoPath(projection);
// Ocean
svg.append('rect')
.attr('width', W).attr('height', H)
.attr('fill', '#060d18');
fetch('https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json')
.then(function(r) { return r.json(); })
.then(function(world) {
// Land
svg.append('path')
.datum(topojson.feature(world, world.objects.countries))
.attr('d', path)
.attr('fill', '#111820')
.attr('stroke', '#1e2a38')
.attr('stroke-width', 0.5);
// Graticule (faint grid)
svg.append('path')
.datum(d3.geoGraticule()())
.attr('d', path)
.attr('fill', 'none')
.attr('stroke', '#0d1a28')
.attr('stroke-width', 0.4);
// Agent blinks — resolve duplicates by approximate cell
var cellSize = 1.5; // degrees
var cells = {};
visits.forEach(function(v) {
var cell = Math.round(v.lat / cellSize) + ',' + Math.round(v.lon / cellSize);
if (!cells[cell]) cells[cell] = { lat: v.lat, lon: v.lon, count: 0 };
cells[cell].count++;
});
var points = Object.values(cells);
points.forEach(function(pt, i) {
var pos = projection([pt.lon, pt.lat]);
if (!pos) return;
var r = Math.min(2 + Math.log1p(pt.count) * 1.5, 8);
svg.append('circle')
.attr('class', 'agent-blink')
.attr('cx', pos[0])
.attr('cy', pos[1])
.attr('r', r)
.style('animation-delay', (i * 190 % 5000) + 'ms');
});
// Registrar star — drawn last so it sits on top
if (hasRegistrar) {
var pos = projection([data.registrarLon, data.registrarLat]);
if (pos) {
svg.append('text')
.attr('class', 'registrar-star')
.attr('x', pos[0])
.attr('y', pos[1])
.text('★');
}
}
})
.catch(function(e) {
console.warn('Map load failed:', e);
});
// ── Resize ────────────────────────────────────────────────────────────────
window.addEventListener('resize', function() {
W = wrap.clientWidth || 800;
H = Math.round(W * 0.48);
svg.attr('viewBox', '0 0 ' + W + ' ' + H)
.attr('width', W).attr('height', H);
projection.scale(W / 6.3).translate([W / 2, H / 2]);
svg.selectAll('path').attr('d', path);
});
})();
@@ -186,110 +186,7 @@ var __D = {dataJson.raw};
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js"></script>
{#raw}
<script>
(function () {
var data = window.__D || {};
var visits = data.recentVisits || [];
var hasRegistrar = typeof data.registrarLat === 'number' && typeof data.registrarLon === 'number';
// ── Expires display ──────────────────────────────────────────────────────
var expiresEl = document.getElementById('expires-val');
var expiresSub = document.getElementById('expires-sub');
if (data.expiresAt && expiresEl) {
var d = new Date(data.expiresAt);
expiresEl.textContent = d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
var days = Math.ceil((d - Date.now()) / 86400000);
expiresSub.textContent = days > 0 ? days + ' days remaining' : 'expired';
}
// ── Map ──────────────────────────────────────────────────────────────────
var wrap = document.getElementById('map-wrap');
var svg = d3.select('#world-map');
var W = wrap.clientWidth || 800;
var H = Math.round(W * 0.48);
svg.attr('viewBox', '0 0 ' + W + ' ' + H)
.attr('width', W)
.attr('height', H);
var projection = d3.geoNaturalEarth1()
.scale(W / 6.3)
.translate([W / 2, H / 2]);
var path = d3.geoPath(projection);
// Ocean
svg.append('rect')
.attr('width', W).attr('height', H)
.attr('fill', '#060d18');
fetch('https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json')
.then(function(r) { return r.json(); })
.then(function(world) {
// Land
svg.append('path')
.datum(topojson.feature(world, world.objects.countries))
.attr('d', path)
.attr('fill', '#111820')
.attr('stroke', '#1e2a38')
.attr('stroke-width', 0.5);
// Graticule (faint grid)
svg.append('path')
.datum(d3.geoGraticule()())
.attr('d', path)
.attr('fill', 'none')
.attr('stroke', '#0d1a28')
.attr('stroke-width', 0.4);
// Agent blinks — resolve duplicates by approximate cell
var cellSize = 1.5; // degrees
var cells = {};
visits.forEach(function(v) {
var cell = Math.round(v.lat / cellSize) + ',' + Math.round(v.lon / cellSize);
if (!cells[cell]) cells[cell] = { lat: v.lat, lon: v.lon, count: 0 };
cells[cell].count++;
});
var points = Object.values(cells);
points.forEach(function(pt, i) {
var pos = projection([pt.lon, pt.lat]);
if (!pos) return;
var r = Math.min(2 + Math.log1p(pt.count) * 1.5, 8);
svg.append('circle')
.attr('class', 'agent-blink')
.attr('cx', pos[0])
.attr('cy', pos[1])
.attr('r', r)
.style('animation-delay', (i * 190 % 5000) + 'ms');
});
// Registrar star — drawn last so it sits on top
if (hasRegistrar) {
var pos = projection([data.registrarLon, data.registrarLat]);
if (pos) {
svg.append('text')
.attr('class', 'registrar-star')
.attr('x', pos[0])
.attr('y', pos[1])
.text('★');
}
}
})
.catch(function(e) {
console.warn('Map load failed:', e);
});
// ── Resize ────────────────────────────────────────────────────────────────
window.addEventListener('resize', function() {
W = wrap.clientWidth || 800;
H = Math.round(W * 0.48);
svg.attr('viewBox', '0 0 ' + W + ' ' + H)
.attr('width', W).attr('height', H);
projection.scale(W / 6.3).translate([W / 2, H / 2]);
svg.selectAll('path').attr('d', path);
});
})();
</script>
{/raw}
<script src="/dashboard.js"></script>
</body>
</html>