407 lines
10 KiB
JavaScript
407 lines
10 KiB
JavaScript
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);
|
|
});
|