const fs = require('fs').promises; const fsSync = require('fs'); const path = require('path'); const SOURCE_DIR = __dirname; const DEPLOY_DIR = path.join(__dirname, '..', 'DEPLOYMENT_READY'); // Dateien und Ordner die KOPIERT werden sollen const INCLUDE_FILES = [ // Docker 'Dockerfile', 'docker-compose.yml', '.dockerignore', // Package 'package.json', 'package-lock.json', // Source Code 'src/**/*', // Fonts 'fonts/**/*.svg', // Config Example '.env.example' ]; // Dateien die NICHT kopiert werden sollen const EXCLUDE_PATTERNS = [ 'node_modules', 'output', 'cache', '.git', 'bruno-tests', '*.md', 'test-*.js', 'test-*.json', 'test-*.sh', 'generate-*.js', 'server.log', '.env', 'deploy.sh', 'prepare-deployment.js', 'DEPLOYMENT_READY' ]; function shouldExclude(filePath) { const relativePath = path.relative(SOURCE_DIR, filePath); return EXCLUDE_PATTERNS.some(pattern => { if (pattern.includes('*')) { const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$'); return regex.test(path.basename(filePath)) || regex.test(relativePath); } return relativePath.startsWith(pattern) || path.basename(filePath) === pattern; }); } async function ensureDir(dir) { try { await fs.mkdir(dir, { recursive: true }); } catch (err) { if (err.code !== 'EEXIST') throw err; } } async function copyFile(src, dest) { await ensureDir(path.dirname(dest)); await fs.copyFile(src, dest); } async function removeDir(dir) { try { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { await removeDir(fullPath); } else { await fs.unlink(fullPath); } } await fs.rmdir(dir); } catch (err) { if (err.code !== 'ENOENT') throw err; } } async function pathExists(p) { try { await fs.access(p); return true; } catch { return false; } } async function copyDirectory(src, dest) { const entries = await fs.readdir(src, { withFileTypes: true }); await ensureDir(dest); for (const entry of entries) { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (shouldExclude(srcPath)) { console.log(`⏭️ Skipping: ${path.relative(SOURCE_DIR, srcPath)}`); continue; } if (entry.isDirectory()) { await copyDirectory(srcPath, destPath); } else { await copyFile(srcPath, destPath); console.log(`✅ Copied: ${path.relative(SOURCE_DIR, srcPath)}`); } } } async function main() { console.log('🚀 Preparing Deployment Package...\n'); // Deployment-Verzeichnis erstellen/leeren if (await pathExists(DEPLOY_DIR)) { console.log('🗑️ Cleaning existing deployment directory...'); await removeDir(DEPLOY_DIR); } await ensureDir(DEPLOY_DIR); console.log(`📁 Created: ${DEPLOY_DIR}\n`); // Dateien kopieren console.log('📋 Copying production files...\n'); await copyDirectory(SOURCE_DIR, DEPLOY_DIR); // Produktions-.env.example erstellen const envExample = `# Skrift Backend - Production Environment Variables # Scriptalizer API Configuration SCRIPTALIZER_LICENSE_KEY=f9918b40-d11c-11f0-b558-0800200c9a66 SCRIPTALIZER_ERR_FREQUENCY=0 # Preview Settings BATCH_SIZE=30 CACHE_LIFETIME_HOURS=2 RATE_LIMIT_PER_MINUTE=2 # Environment NODE_ENV=production PORT=4000 `; await fs.writeFile(path.join(DEPLOY_DIR, '.env.example'), envExample); console.log('✅ Created: .env.example\n'); // README für Deployment erstellen const deployReadme = `# Skrift Backend - Deployment Package Dieses Verzeichnis enthält alle notwendigen Dateien für das Deployment auf den Server. ## Schnellstart ### 1. Zum Server kopieren \`\`\`bash # Mit SCP scp -r * root@DEIN-SERVER:/opt/skrift-backend/ # Oder mit rsync (falls verfügbar) rsync -avz ./ root@DEIN-SERVER:/opt/skrift-backend/ \`\`\` ### 2. Auf dem Server einrichten \`\`\`bash ssh root@DEIN-SERVER cd /opt/skrift-backend # .env erstellen (aus .env.example) cp .env.example .env nano .env # Prüfen und anpassen falls nötig # Output-Verzeichnis erstellen mkdir -p /var/skrift-output chmod 755 /var/skrift-output # Container starten docker-compose up -d --build # Logs prüfen docker-compose logs -f \`\`\` ### 3. Testen \`\`\`bash curl http://localhost:4000/health \`\`\` ## Enthaltene Dateien - \`src/\` - Backend Source Code - \`fonts/\` - SVG Fonts (Tilda, Alva, Ellie) - \`Dockerfile\` - Docker Image Konfiguration - \`docker-compose.yml\` - Docker Compose Konfiguration - \`package.json\` - Node.js Dependencies - \`.env.example\` - Environment Variables Template ## Wichtig ⚠️ **SCRIPTALIZER_ERR_FREQUENCY=0** ist bereits gesetzt - keine durchgestrichenen Wörter! ## Nginx Proxy Manager Nach dem Start in Nginx Proxy Manager konfigurieren: - Domain: backend.deine-domain.de - Forward to: skrift-backend:4000 - SSL: Let's Encrypt aktivieren ## Support Bei Problemen: \`docker-compose logs -f\` `; await fs.writeFile(path.join(DEPLOY_DIR, 'README.txt'), deployReadme); console.log('✅ Created: README.txt\n'); // Upload-Script erstellen const uploadScript = `#!/bin/bash # Skrift Backend - Upload Script # Dieses Script lädt das Backend auf den Server hoch # KONFIGURATION - BITTE ANPASSEN! SERVER_USER="root" SERVER_HOST="" # z.B. "123.456.789.0" oder "dein-server.de" SERVER_PATH="/opt/skrift-backend" # Farben für Output RED='\\033[0;31m' GREEN='\\033[0;32m' YELLOW='\\033[1;33m' NC='\\033[0m' # No Color # Funktion: Fehler anzeigen und beenden error_exit() { echo -e "\${RED}❌ ERROR: \$1\${NC}" >&2 exit 1 } # Prüfen ob Server konfiguriert ist if [ -z "$SERVER_HOST" ]; then error_exit "SERVER_HOST ist nicht gesetzt! Bitte in upload.sh die Variable SERVER_HOST setzen." fi echo -e "\${GREEN}🚀 Skrift Backend Upload\${NC}" echo "======================================" echo "Server: \$SERVER_USER@\$SERVER_HOST" echo "Path: \$SERVER_PATH" echo "" # Prüfen ob SSH-Verbindung funktioniert echo -e "\${YELLOW}🔍 Testing SSH connection...\${NC}" ssh -o ConnectTimeout=5 -o BatchMode=yes \$SERVER_USER@\$SERVER_HOST "echo '✅ SSH connection successful'" || error_exit "SSH connection failed" echo "" # Verzeichnis auf Server erstellen echo -e "\${YELLOW}📁 Creating directory on server...\${NC}" ssh \$SERVER_USER@\$SERVER_HOST "mkdir -p \$SERVER_PATH" || error_exit "Failed to create directory" echo "" # Dateien hochladen echo -e "\${YELLOW}📤 Uploading files...\${NC}" scp -r * \$SERVER_USER@\$SERVER_HOST:\$SERVER_PATH/ || error_exit "Upload failed" echo "" echo -e "\${GREEN}✅ Upload successful!\${NC}" echo "" echo "Next steps:" echo "1. SSH to server: ssh \$SERVER_USER@\$SERVER_HOST" echo "2. Go to directory: cd \$SERVER_PATH" echo "3. Create .env: cp .env.example .env" echo "4. Create output dir: mkdir -p /var/skrift-output" echo "5. Start container: docker-compose up -d --build" echo "6. Check logs: docker-compose logs -f" `; await fs.writeFile(path.join(DEPLOY_DIR, 'upload.sh'), uploadScript); await fs.chmod(path.join(DEPLOY_DIR, 'upload.sh'), 0o755); console.log('✅ Created: upload.sh\n'); // Windows Batch Upload-Script const uploadBat = `@echo off REM Skrift Backend - Windows Upload Script REM KONFIGURATION - BITTE ANPASSEN! set SERVER_USER=root set SERVER_HOST= set SERVER_PATH=/opt/skrift-backend if "%SERVER_HOST%"=="" ( echo ERROR: SERVER_HOST ist nicht gesetzt! echo Bitte in upload.bat die Variable SERVER_HOST setzen. pause exit /b 1 ) echo ======================================== echo Skrift Backend Upload echo ======================================== echo Server: %SERVER_USER%@%SERVER_HOST% echo Path: %SERVER_PATH% echo. echo Uploading files... echo. scp -r * %SERVER_USER%@%SERVER_HOST%:%SERVER_PATH%/ if %ERRORLEVEL% NEQ 0 ( echo. echo ERROR: Upload fehlgeschlagen! pause exit /b 1 ) echo. echo ======================================== echo Upload erfolgreich! echo ======================================== echo. echo Naechste Schritte: echo 1. SSH to server: ssh %SERVER_USER%@%SERVER_HOST% echo 2. Go to directory: cd %SERVER_PATH% echo 3. Create .env: cp .env.example .env echo 4. Create output dir: mkdir -p /var/skrift-output echo 5. Start container: docker-compose up -d --build echo 6. Check logs: docker-compose logs -f echo. pause `; await fs.writeFile(path.join(DEPLOY_DIR, 'upload.bat'), uploadBat); console.log('✅ Created: upload.bat\n'); // Statistik const stats = await getDirectoryStats(DEPLOY_DIR); console.log(''); console.log('📊 Deployment Package Stats:'); console.log('====================================='); console.log(`📁 Total Files: ${stats.files}`); console.log(`📂 Total Directories: ${stats.dirs}`); console.log(`💾 Total Size: ${formatBytes(stats.size)}`); console.log(''); console.log('✅ Deployment Package Ready!'); console.log(''); console.log(`📦 Location: ${DEPLOY_DIR}`); console.log(''); console.log('Next steps:'); console.log('1. Gehe ins Verzeichnis: cd DEPLOYMENT_READY'); console.log('2. Passe upload.sh oder upload.bat an (SERVER_HOST setzen)'); console.log('3. Führe aus: ./upload.sh (Linux/Mac) oder upload.bat (Windows)'); } async function getDirectoryStats(dir) { let files = 0; let dirs = 0; let size = 0; const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { dirs++; const subStats = await getDirectoryStats(fullPath); files += subStats.files; dirs += subStats.dirs; size += subStats.size; } else { files++; const stat = await fs.stat(fullPath); size += stat.size; } } return { files, dirs, size }; } function formatBytes(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; } main().catch(err => { console.error('❌ Error:', err.message); process.exit(1); });