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:
@@ -87,7 +87,7 @@ function renderHealthDashboard() {
|
|||||||
<td>${esc(s.title)}</td>
|
<td>${esc(s.title)}</td>
|
||||||
<td class="text-secondary" style="max-width:300px;">${esc(s.description || "")}</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><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;">
|
<td style="white-space:nowrap;">
|
||||||
<button class="btn btn-success btn-small" onclick="handleSuggestion(${s.id}, true)">Annehmen</button>
|
<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>
|
<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><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>${esc(s.title)}</td>
|
||||||
<td><span class="badge badge-${s.status === "accepted" ? "active" : "inactive"}">${s.status === "accepted" ? "Angenommen" : "Abgelehnt"}</span></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>`,
|
</tr>`,
|
||||||
)
|
)
|
||||||
.join("")}
|
.join("")}
|
||||||
@@ -148,7 +148,7 @@ function renderHealthDashboard() {
|
|||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h2>Health-Check Ergebnisse</h2>
|
<h2>Health-Check Ergebnisse</h2>
|
||||||
<span class="text-secondary" style="font-size:13px;">
|
<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"}
|
||||||
|
|
|
|
||||||
<span class="text-danger">${healthData.errors} Fehler</span>
|
<span class="text-danger">${healthData.errors} Fehler</span>
|
||||||
<span class="text-warning">${healthData.warnings} Warnungen</span>
|
<span class="text-warning">${healthData.warnings} Warnungen</span>
|
||||||
@@ -173,7 +173,7 @@ function renderHealthDashboard() {
|
|||||||
<td>${CHECK_TYPE_LABELS[c.check_type] || c.check_type}</td>
|
<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><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 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>`,
|
</tr>`,
|
||||||
)
|
)
|
||||||
.join("")}
|
.join("")}
|
||||||
@@ -243,7 +243,7 @@ async function runHealthCheck() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Hilfsfunktionen ---
|
// --- Hilfsfunktionen ---
|
||||||
function formatDate(dateStr) {
|
function formatDateTime(dateStr) {
|
||||||
if (!dateStr) return "-";
|
if (!dateStr) return "-";
|
||||||
try {
|
try {
|
||||||
const d = new Date(dateStr);
|
const d = new Date(dateStr);
|
||||||
@@ -261,10 +261,11 @@ function formatDate(dateStr) {
|
|||||||
|
|
||||||
|
|
||||||
// --- Sonnet-Recherche für kaputte Quelle ---
|
// --- 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;
|
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.disabled = true;
|
||||||
btn.textContent = "Sucht...";
|
btn.textContent = "Sucht...";
|
||||||
|
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren