It appears you have a well-structured Git repository with various files, including SVG icons and HTML documents. Here's a brief overview:
This commit is contained in:
238
backend/scripts/compress-assets.js
Normal file
238
backend/scripts/compress-assets.js
Normal file
@ -0,0 +1,238 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Asset-Komprimierungsskript für MYP Backend
|
||||
* Komprimiert CSS und JS-Dateien mit gzip für bessere Performance
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const zlib = require('zlib');
|
||||
const { promisify } = require('util');
|
||||
|
||||
const gzip = promisify(zlib.gzip);
|
||||
const readFile = promisify(fs.readFile);
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
const stat = promisify(fs.stat);
|
||||
|
||||
// Konfiguration
|
||||
const STATIC_DIR = path.join(__dirname, '..', 'static');
|
||||
const COMPRESSION_LEVEL = 9; // Maximale Komprimierung
|
||||
|
||||
// Dateierweiterungen die komprimiert werden sollen
|
||||
const COMPRESSIBLE_EXTENSIONS = ['.css', '.js', '.html', '.svg', '.json'];
|
||||
|
||||
// Mindestgröße für Komprimierung (in Bytes)
|
||||
const MIN_SIZE_FOR_COMPRESSION = 1024; // 1KB
|
||||
|
||||
/**
|
||||
* Komprimiert eine einzelne Datei
|
||||
*/
|
||||
async function compressFile(filePath) {
|
||||
try {
|
||||
const stats = await stat(filePath);
|
||||
|
||||
// Überspringe kleine Dateien
|
||||
if (stats.size < MIN_SIZE_FOR_COMPRESSION) {
|
||||
console.log(`⏭️ Überspringe ${filePath} (zu klein: ${stats.size} bytes)`);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await readFile(filePath);
|
||||
const compressed = await gzip(data, { level: COMPRESSION_LEVEL });
|
||||
|
||||
const gzPath = filePath + '.gz';
|
||||
await writeFile(gzPath, compressed);
|
||||
|
||||
const compressionRatio = ((stats.size - compressed.length) / stats.size * 100).toFixed(1);
|
||||
const originalSize = formatBytes(stats.size);
|
||||
const compressedSize = formatBytes(compressed.length);
|
||||
|
||||
console.log(`✅ ${path.basename(filePath)}: ${originalSize} → ${compressedSize} (-${compressionRatio}%)`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Fehler beim Komprimieren von ${filePath}:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Durchsucht Verzeichnis rekursiv nach komprimierbaren Dateien
|
||||
*/
|
||||
async function findCompressibleFiles(dir) {
|
||||
const files = [];
|
||||
|
||||
try {
|
||||
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
// Rekursiv in Unterverzeichnisse
|
||||
const subFiles = await findCompressibleFiles(fullPath);
|
||||
files.push(...subFiles);
|
||||
} else if (entry.isFile()) {
|
||||
const ext = path.extname(entry.name);
|
||||
|
||||
// Überspringe bereits komprimierte Dateien
|
||||
if (entry.name.endsWith('.gz')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prüfe ob Dateiart komprimierbar ist
|
||||
if (COMPRESSIBLE_EXTENSIONS.includes(ext)) {
|
||||
files.push(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ Kann Verzeichnis nicht lesen: ${dir} - ${error.message}`);
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatiert Byte-Größen lesbar
|
||||
*/
|
||||
function formatBytes(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Bereinigt alte .gz-Dateien
|
||||
*/
|
||||
async function cleanupOldGzFiles(dir) {
|
||||
try {
|
||||
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
||||
let cleaned = 0;
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
cleaned += await cleanupOldGzFiles(fullPath);
|
||||
} else if (entry.name.endsWith('.gz')) {
|
||||
const originalPath = fullPath.slice(0, -3); // Entferne .gz
|
||||
|
||||
try {
|
||||
await stat(originalPath);
|
||||
// Original existiert, behalte .gz
|
||||
} catch (error) {
|
||||
// Original existiert nicht, lösche .gz
|
||||
await fs.promises.unlink(fullPath);
|
||||
console.log(`🗑️ Bereinigt: ${entry.name}`);
|
||||
cleaned++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cleaned;
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ Fehler beim Bereinigen von ${dir}: ${error.message}`);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hauptfunktion
|
||||
*/
|
||||
async function main() {
|
||||
console.log('🚀 MYP Asset-Komprimierung gestartet...\n');
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
// Bereinige alte .gz-Dateien
|
||||
console.log('🧹 Bereinige alte komprimierte Dateien...');
|
||||
const cleanedFiles = await cleanupOldGzFiles(STATIC_DIR);
|
||||
if (cleanedFiles > 0) {
|
||||
console.log(`✅ ${cleanedFiles} verwaiste .gz-Dateien bereinigt\n`);
|
||||
} else {
|
||||
console.log('✅ Keine Bereinigung erforderlich\n');
|
||||
}
|
||||
|
||||
// Finde alle komprimierbaren Dateien
|
||||
console.log('🔍 Suche nach komprimierbaren Dateien...');
|
||||
const files = await findCompressibleFiles(STATIC_DIR);
|
||||
console.log(`📁 ${files.length} komprimierbare Dateien gefunden\n`);
|
||||
|
||||
if (files.length === 0) {
|
||||
console.log('ℹ️ Keine Dateien zum Komprimieren gefunden.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Komprimiere alle Dateien parallel (aber begrenzt)
|
||||
console.log('📦 Komprimiere Dateien...');
|
||||
const BATCH_SIZE = 10; // Maximale parallele Verarbeitung
|
||||
|
||||
let totalOriginalSize = 0;
|
||||
let totalCompressedSize = 0;
|
||||
let processedFiles = 0;
|
||||
|
||||
for (let i = 0; i < files.length; i += BATCH_SIZE) {
|
||||
const batch = files.slice(i, i + BATCH_SIZE);
|
||||
const promises = batch.map(async (file) => {
|
||||
try {
|
||||
const stats = await stat(file);
|
||||
if (stats.size >= MIN_SIZE_FOR_COMPRESSION) {
|
||||
const data = await readFile(file);
|
||||
const compressed = await gzip(data, { level: COMPRESSION_LEVEL });
|
||||
await writeFile(file + '.gz', compressed);
|
||||
|
||||
totalOriginalSize += stats.size;
|
||||
totalCompressedSize += compressed.length;
|
||||
processedFiles++;
|
||||
|
||||
return {
|
||||
file: path.basename(file),
|
||||
original: stats.size,
|
||||
compressed: compressed.length
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ ${path.basename(file)}: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
// Zeige Fortschritt für diese Batch
|
||||
results.filter(r => r).forEach(result => {
|
||||
const ratio = ((result.original - result.compressed) / result.original * 100).toFixed(1);
|
||||
console.log(`✅ ${result.file}: ${formatBytes(result.original)} → ${formatBytes(result.compressed)} (-${ratio}%)`);
|
||||
});
|
||||
}
|
||||
|
||||
const endTime = Date.now();
|
||||
const duration = ((endTime - startTime) / 1000).toFixed(2);
|
||||
|
||||
console.log('\n🎉 Komprimierung abgeschlossen!');
|
||||
console.log(`📊 Statistiken:`);
|
||||
console.log(` • Verarbeitete Dateien: ${processedFiles}`);
|
||||
console.log(` • Originalgröße: ${formatBytes(totalOriginalSize)}`);
|
||||
console.log(` • Komprimierte Größe: ${formatBytes(totalCompressedSize)}`);
|
||||
|
||||
if (totalOriginalSize > 0) {
|
||||
const totalSavings = ((totalOriginalSize - totalCompressedSize) / totalOriginalSize * 100).toFixed(1);
|
||||
console.log(` • Gesamt-Einsparung: ${formatBytes(totalOriginalSize - totalCompressedSize)} (-${totalSavings}%)`);
|
||||
}
|
||||
|
||||
console.log(` • Dauer: ${duration}s`);
|
||||
console.log('\n💡 Tipp: Verwenden Sie gzip-komprimierte Dateien mit Flask-Compress für optimale Performance!');
|
||||
}
|
||||
|
||||
// Führe Skript nur aus wenn direkt aufgerufen
|
||||
if (require.main === module) {
|
||||
main().catch(error => {
|
||||
console.error('💥 Kritischer Fehler:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { compressFile, findCompressibleFiles, cleanupOldGzFiles };
|
Reference in New Issue
Block a user