diff --git a/server/pruef-struktur.mjs b/server/pruef-struktur.mjs index 5b1f57ce..3011708b 100644 --- a/server/pruef-struktur.mjs +++ b/server/pruef-struktur.mjs @@ -84,9 +84,50 @@ for (const datei of serverDateien) { Die Erfassung der AUFRUFE kannte alle drei Zeichen schon (["'`]) -- nur die Erfassung der ROUTEN nicht. Zwei Regeln fuer dieselbe Frage, und eine davon war aelter. */ + /* ==== AUCH ROUTEN HINTER EINER KONSTANTEN (02.10.2026) ========= + + Gemeldet wurde „Schnittstelle gibt es nicht -> + /workspace/api/manager-ziele". Nachgesehen statt geglaubt: Die + Adresse wird nie aufgerufen; sie ist eine Konstante, und benutzt + wird `${BASIS}/stand`, `${BASIS}/eintraege` und acht weitere. + + DER EIGENTLICHE FUND LAG EINE EBENE TIEFER: Der SERVER + registriert sie genauso -- + + const BASIS = "/workspace/api/manager-ziele"; + managerZieleRouter.get(`${BASIS}/stand`, ...) + + -- und die Sammelregel hier verlangte, dass der Pfad direkt mit + `/workspace/` beginnt. ALLE ZEHN Routen dieses Moduls fehlten + damit in der Liste. Die Wache war nicht zu streng, sie war + unvollstaendig: Sie konnte zu diesen Routen gar nichts sagen -- + weder dass es sie gibt, noch dass es sie nicht gibt. + + Jetzt werden einfache Praefix-Konstanten je Datei aufgeloest. + Mehr nicht: Wer seinen Pfad aus drei Variablen zusammensetzt, + bleibt unauffindbar -- und das ist richtig so, denn geraten wird + hier nicht. */ + const konstanten = new Map(); + for (const k of text.matchAll( + /\bconst\s+([A-Za-z_$][\w$]*)\s*=\s*["'`](\/workspace\/[^"'`]*)["'`]\s*;/g)) { + konstanten.set(k[1], k[2]); + } const treffer = text.matchAll( - /Router\.(get|post|patch|put|delete|use)\(\s*\n?\s*["'`](\/workspace\/[^"'`]*)/g); - for (const t of treffer) ROUTEN.push({ art: t[1], pfad: t[2], datei }); + /Router\.(get|post|patch|put|delete|use)\(\s*\n?\s*["'`](\$\{[A-Za-z_$][\w$]*\})?(\/workspace\/[^"'`]*|\/[^"'`]*)/g); + for (const t of treffer) { + let pfad = t[3]; + if (t[2]) { + const name = t[2].slice(2, -1); + const wert = konstanten.get(name); + /* OHNE BEKANNTEN WERT WIRD NICHTS EINGETRAGEN. Eine geratene + Route waere schlimmer als eine fehlende: Sie liesse einen + toten Aufruf als lebendig durchgehen. */ + if (!wert) continue; + pfad = wert + pfad; + } + if (!pfad.startsWith("/workspace/")) continue; + ROUTEN.push({ art: t[1], pfad, datei }); + } } ok(ROUTEN.length >= 80, `${ROUTEN.length} Server-Routen eingelesen`); @@ -209,6 +250,37 @@ for (const name of [...jsDateien.map((n) => "assets/js/" + n), ...seiten]) { } continue; } + /* ==== EINE PRAEFIX-KONSTANTE IST KEIN AUFRUF (02.10.2026) ===== + + Gemeldet wurde: + + assets/js/manager-ziele.js: Schnittstelle gibt es nicht + -> /workspace/api/manager-ziele + + Nachgesehen statt geglaubt: Die Seite ruft diese Adresse NIE + auf. In Zeile 31 steht + + const BASIS = '/workspace/api/manager-ziele'; + + und benutzt wird sie ausschliesslich als `${BASIS}/stand`, + `${BASIS}/eintraege`, `${BASIS}/eintrag/${id}` -- zehn Routen + gibt es darunter, alle vorhanden. + + Der Zweig darueber faengt nur Praefixe mit Schraegstrich am + Ende ab. Eine Konstante ohne Schraegstrich fiel durch und wurde + als fehlende Schnittstelle gemeldet. Ein Fehlalarm, der bei + jedem Lauf kommt -- und eine Warnung, die immer kommt, nimmt + irgendwann die echte mit. + + DIE REGEL IST ABGELEITET, NICHT AUFGEZAEHLT: Gibt es unter + diesem Pfad Routen, aber keine EXAKT darauf, dann ist es ein + Praefix. Eine Ausnahmeliste mit „manager-ziele" darin waere die + naechste, die niemand pflegt. */ + if (!routeGibtEs(roh) + && ROUTEN.some((r) => r.art !== "use" && r.pfad.startsWith(roh + "/"))) { + nurPraefix++; + continue; + } geprueftApi++; if (!routeGibtEs(roh)) ok(false, `${name}: Schnittstelle gibt es nicht -> ${roh}`); } @@ -1443,6 +1515,22 @@ ok(routeGibtEs("/workspace/api/aufgaben"), ok(!existsSync(join(WS, "gibtesnicht.html")), "erfundene Seite wird als fehlend erkannt"); +/* DIE NEUE PRAEFIX-REGEL IN BEIDE RICHTUNGEN (02.10.2026). + + Sie laesst etwas durch -- also muss nachgewiesen sein, dass sie + nicht ALLES durchlaesst. Sonst waere aus einer Wache eine + Freigabestelle geworden. */ +{ + const istPraefix = (p) => !routeGibtEs(p) + && ROUTEN.some((r) => r.art !== "use" && r.pfad.startsWith(p + "/")); + ok(istPraefix("/workspace/api/manager-ziele"), + "eine Praefix-Konstante wird als Praefix erkannt (Routen darunter)"); + ok(!istPraefix("/workspace/api/gibtesnicht"), + " eine erfundene Adresse OHNE Routen darunter bleibt ein Fund"); + ok(!istPraefix("/workspace/api/aufgaben"), + " und eine echte Route bleibt eine echte Route, kein Praefix"); +} + /* ===================================================================== RUFT JEMAND `openssl` WIEDER DIREKT AUF? diff --git a/server/workspace-manager-ziele.js b/server/workspace-manager-ziele.js index d384827c..7893c6bf 100644 --- a/server/workspace-manager-ziele.js +++ b/server/workspace-manager-ziele.js @@ -756,7 +756,7 @@ function eintragAnlegen(person, rumpf, monat, heute) { return { fehler: "schon_eingetragen", text: w.name - ? `„${w.name}" steht diesen Monat schon in deiner Liste.` + ? `„${w.name}“ steht diesen Monat schon in deiner Liste.` : "Dieses Video steht diesen Monat schon in deiner Liste.", }; } diff --git a/workspace/assets/js/manager-ziele.js b/workspace/assets/js/manager-ziele.js index 8c342bf1..bd248bc4 100644 --- a/workspace/assets/js/manager-ziele.js +++ b/workspace/assets/js/manager-ziele.js @@ -661,12 +661,12 @@ const was = e.name || (e.link ? kurzLink(e.link) : tagText(e.datum)); const frage = window.frageNach ? await window.frageNach({ - titel: `„${was}" löschen?`, + titel: `„${was}“ löschen?`, was: 'Der Eintrag verschwindet aus deinem Monatszähler.', bleibt: e.lead_id ? 'Der Lead im Scout-CRM bleibt bestehen.' : '', endgueltig: true, ja: 'Löschen', }) - : window.confirm(`„${was}" löschen?`); + : window.confirm(`„${was}“ löschen?`); if (!frage) return; try { const a = await hole(`${BASIS}/eintrag/${e.id}`, { method: 'DELETE' }); @@ -1128,7 +1128,7 @@ platz.append(tabelle); platz.append(el('p', 'mz-ziele__gilt', `${leute.length} von ${d.leute.length} Personen · sortiert nach ` - + `„${spaltenName(teamSortierung.spalte, d)}"`)); + + `„${spaltenName(teamSortierung.spalte, d)}“`)); } catch { $('team').textContent = ''; $('team').append(el('p', 'mz-leer', 'Die Übersicht konnte nicht geladen werden.'));