Sammelt alle internen Links von 24 öffentlichen Seiten und prüft jedes Ziel einmal gegen die echte Domain. Weiterleitungen (3xx, z.B. auf eine Zugangswand) gelten als gültig, nur 4xx/5xx als toter Link. Ergebnis: 41 Ziele, alle in Ordnung, 0 kaputt. Co-Authored-By: Claude Opus 5 <[email protected]>
99 lines
4.1 KiB
JavaScript
99 lines
4.1 KiB
JavaScript
/* =====================================================================
|
|
Zeigt irgendein interner Link ins Leere?
|
|
|
|
Ein toter Link in der Navigation oder im Text schickt Besucher auf die
|
|
404-Seite. Beim normalen Klicken merkt man das kaum -- man klickt ja
|
|
die Links, von denen man weiß, dass sie gehen. Dieser Test sammelt ALLE
|
|
internen Links von den öffentlichen Seiten ein und prüft für jedes Ziel
|
|
EINMAL, ob es wirklich existiert (kein 404/5xx).
|
|
|
|
Gegen die echte Domain, damit auch die serverseitige Auslieferung
|
|
(Weiterleitungen, Zugangswände) mitgeprüft wird.
|
|
|
|
Aufruf: node pruef-links.mjs
|
|
===================================================================== */
|
|
import { chromium } from "playwright";
|
|
|
|
const BASIS = "https://dogfather-universe.com";
|
|
|
|
/* Die öffentlich verlinkten Seiten -- von hier aus wird die Verlinkung
|
|
aufgesammelt. Unterseiten, die nur von diesen aus verlinkt sind, werden
|
|
über ihre Links automatisch mitgeprüft. */
|
|
const SEITEN = [
|
|
"/", "/dogfather.html", "/hasidog.html", "/manager.html", "/casper.html",
|
|
"/vanvan.html", "/tt-muddi.html", "/zeitreise.html", "/streamer.html",
|
|
"/community.html", "/stimmen.html", "/streamplan.html", "/team-modis.html",
|
|
"/team-scouts.html", "/creator.html", "/werte.html", "/bewerben.html",
|
|
"/kontakt.html", "/links.html", "/supporter.html", "/abonnieren.html",
|
|
"/medien-dogfather.html", "/medien-hasidog.html", "/medien-casper.html",
|
|
];
|
|
|
|
const browser = await chromium.launch();
|
|
|
|
/* 1. Alle internen Ziele einsammeln. */
|
|
const ziele = new Map(); // pfad -> Set der Seiten, die darauf verlinken
|
|
const seiteFehler = [];
|
|
|
|
for (const pfad of SEITEN) {
|
|
const seite = await browser.newPage();
|
|
try {
|
|
const antwort = await seite.goto(BASIS + pfad, { waitUntil: "domcontentloaded", timeout: 30000 });
|
|
if (!antwort || antwort.status() >= 400) {
|
|
seiteFehler.push(`${pfad} selbst -> ${antwort ? antwort.status() : "keine Antwort"}`);
|
|
}
|
|
const links = await seite.evaluate(() => {
|
|
return [...document.querySelectorAll('a[href]')]
|
|
.map((a) => a.getAttribute("href"))
|
|
.filter((h) => h && !h.startsWith("#") && !h.startsWith("mailto:") &&
|
|
!h.startsWith("tel:") && !h.startsWith("javascript:"));
|
|
});
|
|
for (const h of links) {
|
|
/* Nur interne Links prüfen. Externe (andere Domains) lassen wir aus
|
|
-- die kann diese Seite nicht garantieren, und sie ändern sich
|
|
außerhalb unserer Kontrolle. */
|
|
let url;
|
|
try { url = new URL(h, BASIS + pfad); } catch { continue; }
|
|
if (url.origin !== BASIS) continue;
|
|
const ziel = url.pathname + url.search;
|
|
if (!ziele.has(ziel)) ziele.set(ziel, new Set());
|
|
ziele.get(ziel).add(pfad);
|
|
}
|
|
} catch (e) {
|
|
seiteFehler.push(`${pfad} selbst -> ${String(e.message).slice(0, 40)}`);
|
|
}
|
|
await seite.close();
|
|
}
|
|
|
|
console.log(`\n${ziele.size} verschiedene interne Ziele von ${SEITEN.length} Seiten gesammelt.\n`);
|
|
|
|
/* 2. Jedes Ziel EINMAL prüfen. */
|
|
const pruefSeite = await browser.newPage();
|
|
let ok = 0;
|
|
const kaputt = [];
|
|
|
|
for (const [ziel, quellen] of [...ziele].sort()) {
|
|
try {
|
|
const a = await pruefSeite.goto(BASIS + ziel, { waitUntil: "commit", timeout: 20000 });
|
|
const code = a ? a.status() : 0;
|
|
/* < 400 gilt als in Ordnung -- auch Weiterleitungen (3xx) auf eine
|
|
Zugangswand sind ein gültiges Ziel, kein toter Link. */
|
|
if (code < 400) { ok++; }
|
|
else { kaputt.push({ ziel, code, quellen: [...quellen] }); }
|
|
} catch (e) {
|
|
kaputt.push({ ziel, code: String(e.message).slice(0, 30), quellen: [...quellen] });
|
|
}
|
|
}
|
|
|
|
await browser.close();
|
|
|
|
console.log(`===== ${ok} Ziele in Ordnung, ${kaputt.length} kaputt, ${seiteFehler.length} Seiten selbst fehlerhaft =====`);
|
|
if (seiteFehler.length) {
|
|
console.log("\nSeiten, die selbst nicht luden:");
|
|
for (const f of seiteFehler) console.log(" - " + f);
|
|
}
|
|
if (kaputt.length) {
|
|
console.log("\nTote Links (Ziel -> Code, verlinkt von):");
|
|
for (const k of kaputt) console.log(` - ${k.ziel} -> ${k.code} (von: ${k.quellen.join(", ")})`);
|
|
}
|
|
process.exitCode = kaputt.length || seiteFehler.length ? 1 : 0;
|