From f9b884c205eb91a0d46b161615e7e3e4543fa22c Mon Sep 17 00:00:00 2001 From: benji Date: Sun, 16 Aug 2026 19:57:13 +0000 Subject: [PATCH] =?UTF-8?q?Dateien=20nach=20=E2=80=9Edemo=E2=80=9C=20hochl?= =?UTF-8?q?aden?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo/scan_repo.js | 295 +++++++++++++++++++++++++++++++++++++++++++++ demo/testaufruf.js | 103 ++++++++++++++++ 2 files changed, 398 insertions(+) create mode 100644 demo/scan_repo.js create mode 100644 demo/testaufruf.js diff --git a/demo/scan_repo.js b/demo/scan_repo.js new file mode 100644 index 0000000..040a9ae --- /dev/null +++ b/demo/scan_repo.js @@ -0,0 +1,295 @@ +const fs = require('fs'); +const path = require('path'); +require('dotenv').config({ path: path.resolve(__dirname, '../../src/.env') }); + +/** + * @fileoverview Automatisierungsskript für den Multi-Repository SAP-Deprecation Scan. + * Durchsucht lokale Unterordner (Repositories) rekursiv nach SAP- und REST-Endpunkten, + * gleicht diese über die FastAPI ab und generiert einen zusammenfassenden Report. + * @author MBT + * @version 1.3 (Zeigt in Details nur veränderte/abgekündigte Endpunkte) + */ + + +// --- KONFIGURATION --- + +const BASE_PATH = './'; +const FASTAPI_URL = 'http://127.0.0.1:8000/informations/bulk'; +const OUTPUT_FILE = './sap_api_scan_report.txt'; +const IGNORED_DIRS = ['node_modules', '.git', '__pycache__', 'venv', 'dist', 'build', '.idea', '.vscode']; + +const headers = { + "X-API-Key": process.env.READONLY_APIKEY +}; + +// Optimierte Regex (Case-Insensitive) +const ENDPOINT_REGEX = /\b(?:\/?)(?:v\d+|sap)\/[a-zA-Z0-9_\-\/{}]*(?:\/[a-zA-Z0-9_\-\/{}]+)*/i; + + +// --- FUNKTIONEN --- + +function getRepositories(baseDir) { + return fs.readdirSync(baseDir).filter(file => { + const fullPath = path.join(baseDir, file); + return fs.statSync(fullPath).isDirectory() && !IGNORED_DIRS.includes(file); + }); +} + +function walkSync(dir, filelist = []) { + const files = fs.readdirSync(dir); + files.forEach(file => { + const filepath = path.join(dir, file); + const stat = fs.statSync(filepath); + + if (stat.isDirectory()) { + if (!IGNORED_DIRS.includes(file)) { + walkSync(filepath, filelist); + } + } else { + filelist.push(filepath); + } + }); + return filelist; +} + +function scanRepositoryForEndpoints(repoPath, repoName) { + console.log(`\n🔍 [${repoName}] Starte rekursiven Scan im Ordner: ${path.resolve(repoPath)}...`); + const files = walkSync(repoPath); + const endpointLocations = {}; + let checkedFilesCount = 0; + + files.forEach(file => { + if ( + file.endsWith('scan_repo.js') || + file.endsWith('scan_all_repos.js') || + file.endsWith('.txt') || + file.endsWith('.json') || + file.endsWith('.md') || + file.includes(path.basename(OUTPUT_FILE)) + ) { + return; + } + + checkedFilesCount++; + + try { + const content = fs.readFileSync(file, 'utf8'); + + if (!/sap|v\d+/i.test(content)) { + return; + } + + const lines = content.split(/\r?\n/); + + lines.forEach((lineText, index) => { + const lineNumber = index + 1; + const match = lineText.match(ENDPOINT_REGEX); + + if (match) { + const endpoint = match[0].trim(); + if (endpoint.length >= 4) { + if (!endpointLocations[endpoint]) { + endpointLocations[endpoint] = []; + } + + const relativePath = path.relative(BASE_PATH, file); + endpointLocations[endpoint].push({ + file: relativePath, + line: lineNumber + }); + + console.log(` ✨ Treffer in [${repoName}]: "${endpoint}" gefunden in ${relativePath}:${lineNumber}`); + } + } + }); + } catch (err) { + if (err.code !== 'EISDIR' && !file.match(/\.(png|jpg|jpeg|gif|ico|zip|tar|gz|pdf|exe|dll)$/i)) { + console.warn(`⚠️ Konnte Datei nicht lesen: ${file} (${err.message})`); + } + } + }); + + console.log(`🏁 [${repoName}] Scan beendet. ${checkedFilesCount} Dateien durchsucht. ${Object.keys(endpointLocations).length} eindeutige Endpunkte gefunden.`); + return endpointLocations; +} + +async function checkEndpoints(endpoints, batchSize = 25) { + const consolidatedResult = { + total_checked: 0, + results: [] + }; + + for (let i = 0; i < endpoints.length; i += batchSize) { + const batch = endpoints.slice(i, i + batchSize); + console.log(` ⏳ Sende Batch ${Math.floor(i / batchSize) + 1} (${batch.length} Endpunkte)...`); + + const params = new URLSearchParams(); + batch.forEach(ep => params.append("endpoints", ep)); + + try { + const response = await fetch(`${FASTAPI_URL}?${params.toString()}`, { method: "GET", headers: headers }); + if (!response.ok) { + throw new Error(`Server meldete Status ${response.status}`); + } + + const batchJson = await response.json(); + + consolidatedResult.total_checked += batchJson.total_checked || batch.length; + if (batchJson.results && Array.isArray(batchJson.results)) { + consolidatedResult.results.push(...batchJson.results); + } + } catch (error) { + throw new Error(`Fehler in Batch ab Index ${i}: ${error.message}`); + } + } + + return consolidatedResult; +} + +/** + * Steuert den Scan-Workflow und baut die aggregierte Zusammenfassung sowie den gefilterten Detailbericht. + */ +async function runMultiRepoScan() { + const repos = getRepositories(BASE_PATH); + + if (repos.length === 0) { + console.log("⚠️ Keine Scan-fähigen Unterordner (Repositories) gefunden!"); + return; + } + + console.log(`📂 ${repos.length} Repositories für den Scan gefunden.`); + + // Statistik-Zähler für die Zusammenfassung + const stats = { + totalRepos: repos.length, + reposWithEndpoints: 0, + totalEndpointsFound: 0, + statusActive: 0, + statusDeprecated: 0, + statusChanged: 0, + statusUnknown: 0 + }; + + let detailedReportBody = ''; + + // Schleife über alle gefundenen Repository-Ordner + for (const repoName of repos) { + const repoPath = path.join(BASE_PATH, repoName); + console.log(`\n--------------------------------------------------`); + console.log(`📦 Verarbeite Repository: ${repoName.toUpperCase()}`); + console.log(`--------------------------------------------------`); + + const endpointLocations = scanRepositoryForEndpoints(repoPath, repoName); + const uniqueEndpoints = Object.keys(endpointLocations); + + if (uniqueEndpoints.length === 0) { + console.log(`ℹ️ Keine SAP-Endpunkte in [${repoName}] gefunden.`); + continue; + } + + stats.reposWithEndpoints++; + stats.totalEndpointsFound += uniqueEndpoints.length; + + console.log(`🚀 ${uniqueEndpoints.length} Endpunkte in [${repoName}] gefunden. Sende Abfrage an FastAPI...`); + + try { + const apiResult = await checkEndpoints(uniqueEndpoints); + let repoIssuesContent = ''; // Puffer für problematische Endpunkte in diesem spezifischen Repo + + apiResult.results.forEach(item => { + const messageLower = item.message.toLowerCase(); + const statusLower = (item.status || '').toLowerCase(); + + // Status für die Statistik auswerten + const isNoHit = messageLower.includes("keine sap-änderungen oder deprecations gefunden"); + let isIssue = false; + + if (isNoHit || statusLower === 'active' || statusLower === 'ok') { + stats.statusActive++; + } else if (statusLower.includes('deprecat')) { + stats.statusDeprecated++; + isIssue = true; + } else if (statusLower.includes('chang') || statusLower.includes('modif')) { + stats.statusChanged++; + isIssue = true; + } else { + stats.statusUnknown++; + isIssue = true; // Unbekannte Stati sicherheitshalber auch auflisten + } + + // Nur wenn ein Problem/eine Änderung vorliegt, wird der Endpunkt im Detailbericht aufgelistet + if (isIssue) { + const locations = endpointLocations[item.endpoint] || []; + const locationString = locations + .map(loc => `${loc.file}:${loc.line}`) + .join(', '); + + let logLine = `[❗Warning] Endpoint: ${item.endpoint}\n`; + logLine += ` Files: ${locationString}\n`; + logLine += ` Status: ${item.status}\n`; + logLine += ` Details: ${item.message}\n`; + logLine += ` Deprecated in Quality: ${item.deprecated[0]}\n`; + logLine += ` Deprecated in Production: ${item.deprecated[1]}\n`; + logLine += ` Decommissioned in Quality: ${item.decommissioned[0]}\n`; + logLine += ` Decommissioned in Production: ${item.decommissioned[1]}\n`; + + console.log(logLine); + repoIssuesContent += logLine + "\n"; + } + }); + + // Falls dieses Repo veränderte/abgekündigte Endpunkte hatte, hänge sie an den Report an + if (repoIssuesContent.length > 0) { + detailedReportBody += `📦 REPOSITORY: ${repoName}\n`; + detailedReportBody += `--------------------------------------------------\n`; + detailedReportBody += repoIssuesContent; + detailedReportBody += `\n`; + } + + } catch (error) { + console.error(`❌ Fehler bei API-Abfrage für [${repoName}]:`, error.message); + detailedReportBody += `📦 REPOSITORY: ${repoName}\n -> API-Fehler bei der Abfrage: ${error.message}\n\n`; + } + } + + // Zusammenbau des finalen Berichts (Zusammenfassung ganz oben!) + let finalReportContent = `======================================================================\n`; + finalReportContent += ` GLOBAL SAP DEPRECATION SCAN REPORT - ${new Date().toLocaleString()}\n`; + finalReportContent += `======================================================================\n\n`; + + finalReportContent += `📊 SUMMARY:\n`; + finalReportContent += `----------------------------------------------------------------------\n`; + finalReportContent += ` Total Repositories Scanned: ${stats.totalRepos}\n`; + finalReportContent += ` Repositories with Findings: ${stats.reposWithEndpoints}\n`; + finalReportContent += ` Total Endpoints Detected: ${stats.totalEndpointsFound}\n\n`; + finalReportContent += ` ENDPOINT STATUS OVERVIEW:\n`; + finalReportContent += ` 🟢 Active / Unchanged Endpoints: ${stats.statusActive}\n`; + finalReportContent += ` 🟡 Modified Endpoints (Changed): ${stats.statusChanged}\n`; + finalReportContent += ` 🔴 Obsolete Endpoints (Deprecated): ${stats.statusDeprecated}\n`; + if (stats.statusUnknown > 0) { + finalReportContent += ` ⚪ Unknown / Unclear Status: ${stats.statusUnknown}\n`; + } + finalReportContent += `======================================================================\n\n\n`; + + // Details (filtered issues only!) appended below + finalReportContent += `📝 ACTIONABLE DETAILS & DETECTED ISSUES (Filtered):\n`; + finalReportContent += ` (Only modified or deprecated endpoints are listed below)\n`; + finalReportContent += `======================================================================\n\n`; + + if (detailedReportBody.length === 0) { + finalReportContent += `🎉 Excellent! No modified or deprecated endpoints were found in any of the repositories.\n`; + } else { + finalReportContent += detailedReportBody; + } + + // Gesamtergebnis in Datei schreiben + try { + fs.writeFileSync(OUTPUT_FILE, finalReportContent, 'utf8'); + console.log(`\n💾 Der globale Bericht wurde erfolgreich gespeichert: ${path.resolve(OUTPUT_FILE)}`); + } catch (err) { + console.error("❌ Bericht konnte nicht geschrieben werden:", err.message); + } +} + +// --- START --- +runMultiRepoScan(); \ No newline at end of file diff --git a/demo/testaufruf.js b/demo/testaufruf.js new file mode 100644 index 0000000..8121b26 --- /dev/null +++ b/demo/testaufruf.js @@ -0,0 +1,103 @@ +/** + * @file Abfrage-Skript für den SAP Endpoint Service. + * Dieses Skript sendet eine Liste von API-Endpunkten an den lokalen FastAPI-Bulk-Service, + * wertet die Ergebnisse aus und gibt sie formatiert in der Konsole aus. + * @author MBT + * @version 1.0 + */ +const path = require('path'); +require('dotenv').config({ path: path.resolve(__dirname, '../../src/.env') }); + +/** + * @typedef {Object} EndpointResult + * @property {string} endpoint - Der geprüfte SAP-Endpunkt. + * @property {string} status - Der SAP-Status (z. B. "Deprecated", "active", "Unbekannt"). + * @property {string} message - Die detaillierte Statusmeldung der SAP-Prüfung. + */ + +/** + * @typedef {Object} BulkApiResponse + * @property {number} total_checked - Die Gesamtanzahl der geprüften Endpunkte. + * @property {EndpointResult[]} results - Die Liste der einzelnen Prüfergebnisse. + */ + +/** + * Liste der SAP-Endpunkte, die auf Änderungen oder Deprecations geprüft werden sollen. + * @type {string[]} + */ +const endpointsToCheck = [ + "v1/inventories" +]; + +/** + * Erstellt die URL-Query-Parameter für die Bulk-Abfrage. + * Jedes Element aus `endpointsToCheck` wird als separater "endpoints"-Parameter angehängt. + * @type {URLSearchParams} + */ +const params = new URLSearchParams(); +endpointsToCheck.forEach(ep => { + params.append("endpoints", ep); +}); + +/** + * Die Ziel-URL für den lokalen FastAPI-Bulk-Endpunkt. + * @type {string} + */ +const url = `http://127.0.0.1:8000/informations/bulk?${params.toString()}`; + + +const apiKeysString = process.env.APIKEYS || ""; +const firstApiKey = apiKeysString.split(",")[0].trim(); + +const headers = { + "X-API-Key": firstApiKey +}; + +// Führt den HTTP-Call an die FastAPI aus +fetch(url, { + method: "GET", + headers: headers + }) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP-Fehler! Status: ${response.status}`); + } + return response.json(); // Wandelt die Antwort in ein echtes JS-Objekt um + }) + .then(data => { + // 1. Komplette Antwort ausgeben (ohne [Array]-Kürzung durch Node.js) + console.log("Gesamtes API-Feedback:"); + console.log(JSON.stringify(data, null, 2)); + + // 2. Ergebnisse im Detail durchgehen und die berechneten SAP-Daten anzeigen + if (data && data.results) { + console.log("\n=== SAP API-Statusbericht ==="); + data.results.forEach(item => { + console.log(`\nEndpunkt: ${item.endpoint}`); + console.log(`Status: ${item.status}`); + console.log(`Meldung: ${item.message || 'Keine Meldung vorhanden.'}`); + + // Nur ausgeben, wenn wir echte Datumsdaten vom Server erhalten haben + if (item.deprecated && item.deprecated.length > 0) { + const [qDate, pDate] = item.deprecated; + const [qDecom, pDecom] = item.decommissioned; + + console.log(`\n --- Upgrade- & Rückbaufristen ---`); + console.log(` [Q-System] Abgekündigt: ${qDate} --> Geplanter Rückbau (+1 Jahr): ${qDecom}`); + console.log(` [P-System] Abgekündigt: ${pDate} --> Geplanter Rückbau (+1 Jahr): ${pDecom}`); + } else { + console.log(`\n --> Keine anstehenden Abkündigungen oder Fristen für diesen Endpunkt.`); + } + console.log("----------------------------------------------------------------------"); + }); + } else { + console.log("Keine 'results' im JSON-Response gefunden."); + } + }) + .catch( + /** + * Catch-Block für Netzwerk- oder Verarbeitungsfehler. + * @param {Error} error - Das aufgetretene Fehler-Objekt. + */ + error => console.error("Fehler bei der Abfrage:", error) + ); \ No newline at end of file