Dateien nach „demo“ hochladen

This commit is contained in:
Markus Benjamin Tabler 2026-08-16 19:57:13 +00:00
parent 45a4808480
commit f9b884c205
2 changed files with 398 additions and 0 deletions

295
demo/scan_repo.js Normal file
View file

@ -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();

103
demo/testaufruf.js Normal file
View file

@ -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)
);