Fix: formatDate-Kollision, implizites event, onclick-Escaping

- formatDate() in source-health.js zu formatDateTime() umbenannt,
  damit app.js formatDate() nicht überschrieben wird
- searchFix(): data-Attribute statt inline onclick-String (XSS-sicher)
- searchFix(btn): expliziter Parameter statt implizites event.target

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
claude-dev
2026-03-08 16:51:13 +01:00
Ursprung fb5ed358bd
Commit a8d5d16db2

Datei anzeigen

@@ -87,7 +87,7 @@ function renderHealthDashboard() {
<td>${esc(s.title)}</td>
<td class="text-secondary" style="max-width:300px;">${esc(s.description || "")}</td>
<td><span class="badge badge-priority-${s.priority}">${PRIORITY_LABELS[s.priority] || s.priority}</span></td>
<td class="text-secondary">${formatDate(s.created_at)}</td>
<td class="text-secondary">${formatDateTime(s.created_at)}</td>
<td style="white-space:nowrap;">
<button class="btn btn-success btn-small" onclick="handleSuggestion(${s.id}, true)">Annehmen</button>
<button class="btn btn-danger btn-small" onclick="handleSuggestion(${s.id}, false)">Ablehnen</button>
@@ -127,7 +127,7 @@ function renderHealthDashboard() {
<td><span class="badge badge-suggestion-${s.suggestion_type}">${SUGGESTION_TYPE_LABELS[s.suggestion_type] || s.suggestion_type}</span></td>
<td>${esc(s.title)}</td>
<td><span class="badge badge-${s.status === "accepted" ? "active" : "inactive"}">${s.status === "accepted" ? "Angenommen" : "Abgelehnt"}</span></td>
<td class="text-secondary">${formatDate(s.reviewed_at)}</td>
<td class="text-secondary">${formatDateTime(s.reviewed_at)}</td>
</tr>`,
)
.join("")}
@@ -148,7 +148,7 @@ function renderHealthDashboard() {
<div class="card-header">
<h2>Health-Check Ergebnisse</h2>
<span class="text-secondary" style="font-size:13px;">
Letzter Check: ${healthData.last_check ? formatDate(healthData.last_check) : "Noch nie"}
Letzter Check: ${healthData.last_check ? formatDateTime(healthData.last_check) : "Noch nie"}
&nbsp;|&nbsp;
<span class="text-danger">${healthData.errors} Fehler</span> &nbsp;
<span class="text-warning">${healthData.warnings} Warnungen</span> &nbsp;
@@ -173,7 +173,7 @@ function renderHealthDashboard() {
<td>${CHECK_TYPE_LABELS[c.check_type] || c.check_type}</td>
<td><span class="badge badge-health-${c.status}">${c.status === "error" ? "Fehler" : "Warnung"}</span></td>
<td class="text-secondary" style="max-width:250px;">${esc(c.message)}</td>
<td>${c.status === "error" && c.check_type === "reachability" ? \`<button class="btn btn-secondary btn-small" onclick="searchFix(\${c.source_id}, '\${esc(c.name)}')">Lösung suchen</button>\` : ""}</td>
<td>${c.status === "error" && c.check_type === "reachability" ? \`<button class="btn btn-secondary btn-small" data-source-id="\${c.source_id}" data-source-name="\${esc(c.name)}" onclick="searchFix(this)">Lösung suchen</button>\` : ""}</td>
</tr>`,
)
.join("")}
@@ -243,7 +243,7 @@ async function runHealthCheck() {
}
// --- Hilfsfunktionen ---
function formatDate(dateStr) {
function formatDateTime(dateStr) {
if (!dateStr) return "-";
try {
const d = new Date(dateStr);
@@ -261,10 +261,11 @@ function formatDate(dateStr) {
// --- Sonnet-Recherche für kaputte Quelle ---
async function searchFix(sourceId, sourceName) {
async function searchFix(btn) {
const sourceId = btn.dataset.sourceId;
const sourceName = btn.dataset.sourceName;
if (!confirm(`Sonnet mit WebSearch nach einer Lösung für "${sourceName}" suchen lassen?\n\nDas nutzt Kontingent vom Max-Abo (~$3-4).`)) return;
const btn = event.target;
btn.disabled = true;
btn.textContent = "Sucht...";