Files
usbcheck.it/public/assets/js/fakecheck/fakecheck.serial.js
2025-11-30 03:11:24 +01:00

185 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// /public/assets/js/fakecheck/fakecheck.serial.js
(function () {
if (!window.usbcheck) return;
const {
cfg,
t,
tFmt,
log: logLine
} = window.usbcheck;
const rootSc = document.getElementById("serialcheck-root");
if (!rootSc) return; // Partial nicht eingebunden → nichts tun
// Elemente
const form = rootSc.querySelector("#serialcheck-form");
const errorBox = rootSc.querySelector("#serialcheck-error");
const resultBox = rootSc.querySelector("#serialcheck-result");
const manufacturerInput = rootSc.querySelector("#sc-manufacturer");
const vidInput = rootSc.querySelector("#sc-vid");
const pidInput = rootSc.querySelector("#sc-pid");
const serialInput = rootSc.querySelector("#sc-serial");
// API-Basis:
// - cfg.apiBase kommt aus fakecheck.core.js (z.B. https://api.usbcheck.it)
// - wir hängen /v1/quickcheck dran
// - Fallback: /api/v1/quickcheck auf demselben Host
const apiBaseRaw = (cfg && cfg.apiBase) ? cfg.apiBase : "";
const apiBase = apiBaseRaw.replace(/\/+$/, "");
const apiUrl = apiBase ? (apiBase + "/v1/quickcheck") : "/api/v1/quickcheck";
// -------------------------------------------------------
// Fehleranzeige
// -------------------------------------------------------
function showScError(msgKey, fallback, vars = {}) {
if (!errorBox) return;
const msg = tFmt(msgKey, fallback, vars);
errorBox.textContent = msg;
errorBox.classList.remove("hidden");
if (resultBox) resultBox.classList.add("hidden");
}
function clearScError() {
if (!errorBox) return;
errorBox.classList.add("hidden");
errorBox.textContent = "";
}
// -------------------------------------------------------
// Ergebnis rendern
// -------------------------------------------------------
function renderScResult(data) {
if (!resultBox) return;
clearScError();
resultBox.classList.remove("hidden");
const rating = data.rating || "unknown";
const input = data.input || {};
const vendorInfo = data.vendor_detected || {};
const serialInfo = data.serial_analysis || {};
const consistency = data.consistency || {};
// Rating → Label + Beschreibung
const ratingLabel = t(`serial.rating.${rating}.label`, rating);
const ratingDesc = t(`serial.rating.${rating}.desc`, "");
// Auffälligkeiten
const issues = serialInfo.issues || [];
const issuesHtml = issues.length
? '<ul class="list-disc list-inside mt-1">' +
issues.map(i => `<li>${i}</li>`).join("") +
'</ul>'
: `<span class="text-emerald-600 text-[11px]">${t("serial.issues_none", "Keine besonderen Auffälligkeiten.")}</span>`;
// Vendor
const vendorLine = vendorInfo.found
? tFmt("serial.vendor_detected", "{vendor} (VID {vid})", {
vendor: vendorInfo.vendor,
vid: vendorInfo.vid
})
: vendorInfo.vid
? tFmt("serial.vendor_unknown", "Unbekannter Hersteller für VID {vid}", { vid: vendorInfo.vid })
: t("serial.vendor_none", "Keine Vendor-ID angegeben");
// Konsistenz-Notizen
const notes = consistency.notes || [];
const notesHtml = notes.length
? '<ul class="list-disc list-inside mt-1 text-[11px]">' +
notes.map(n => `<li>${n}</li>`).join("") +
'</ul>'
: "";
// HTML einsetzen
resultBox.innerHTML = `
<div class="mb-3">
<span class="inline-flex items-center rounded-full px-3 py-1 text-[11px] font-semibold
${rating === "ok" ? "bg-emerald-100 text-emerald-800" : ""}
${rating === "needs_review" ? "bg-amber-100 text-amber-800" : ""}
${rating === "suspicious" ? "bg-red-100 text-red-800" : ""}
${rating === "invalid" ? "bg-slate-100 text-slate-700" : ""}
">
${t("serial.rating_label", "Bewertung")}: ${ratingLabel}
</span>
<p class="mt-1 text-xs text-slate-600 dark:text-slate-300">${ratingDesc}</p>
</div>
<div class="border border-slate-200 dark:border-slate-700 rounded-xl p-3 mb-3">
<h3 class="text-xs font-semibold mb-1">${t("serial.heading_input", "Eingabedaten")}</h3>
<dl class="text-[11px] space-y-1 text-slate-600 dark:text-slate-300">
<div><dt class="font-medium">${t("serial.input.manufacturer", "Hersteller (Angabe):")}</dt><dd>${input.manufacturer || '<span class="text-slate-400">' + t("serial.none", "keine Angabe") + '</span>'}</dd></div>
<div><dt class="font-medium">${t("serial.input.vidpid", "VID/PID:")}</dt><dd>${(input.vid || "") + " / " + (input.pid || "")}</dd></div>
<div><dt class="font-medium">${t("serial.input.vendor_detected", "Vendor aus VID:")}</dt><dd>${vendorLine}</dd></div>
</dl>
</div>
<div class="border border-slate-200 dark:border-slate-700 rounded-xl p-3 mb-3">
<h3 class="text-xs font-semibold mb-1">${t("serial.heading_analysis", "Seriennummer-Analyse")}</h3>
<dl class="text-[11px] space-y-1 text-slate-600 dark:text-slate-300">
<div><dt class="font-medium">${t("serial.analysis.serial", "Seriennummer:")}</dt><dd><code class="text-[10px] bg-slate-100 dark:bg-slate-800 px-1.5 py-0.5 rounded">${serialInfo.serial || ""}</code></dd></div>
<div><dt class="font-medium">${t("serial.analysis.length", "Länge:")}</dt><dd>${serialInfo.length || 0} ${t("serial.chars", "Zeichen")}</dd></div>
<div><dt class="font-medium">${t("serial.analysis.category", "Kategorie:")}</dt><dd>${serialInfo.category || "-"}</dd></div>
<div><dt class="font-medium">${t("serial.analysis.score", "Score:")}</dt><dd>${typeof serialInfo.score === "number" ? serialInfo.score : "-"} / 100</dd></div>
<div><dt class="font-medium">${t("serial.analysis.issues", "Auffälligkeiten:")}</dt><dd>${issuesHtml}</dd></div>
</dl>
</div>
<div class="border border-slate-200 dark:border-slate-700 rounded-xl p-3">
<h3 class="text-xs font-semibold mb-1">${t("serial.heading_consistency", "Hersteller-Konsistenz")}</h3>
${notesHtml}
<p class="mt-2 text-[10px] text-slate-500">
${t("serial.disclaimer", "Diese Einschätzung basiert auf Heuristiken und kann keine Echtheit garantieren.")}
</p>
</div>
`;
}
// -------------------------------------------------------
// Submit-Handler
// -------------------------------------------------------
if (!form) return;
form.addEventListener("submit", (e) => {
e.preventDefault();
clearScError();
if (resultBox) resultBox.classList.add("hidden");
const payload = {
manufacturer: manufacturerInput ? manufacturerInput.value.trim() : "",
vid: vidInput ? vidInput.value.trim() : "",
pid: pidInput ? pidInput.value.trim() : "",
serial: serialInput ? serialInput.value.trim() : ""
};
if (!payload.serial) {
showScError("serial.error.no_serial", "Bitte gib eine Seriennummer ein.");
return;
}
fetch(apiUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
})
.then(res => {
if (!res.ok) {
throw new Error("HTTP " + res.status);
}
return res.json();
})
.then(data => {
if (!data || !data.success) {
throw new Error((data && data.error) || t("serial.error.unknown", "Unerwartete Antwort vom Server."));
}
renderScResult(data);
})
.catch(err => {
showScError("serial.error.api", "Fehler bei der Prüfung: {msg}", { msg: err.message });
});
});
})();