Add production build optimizations to Vite configuration; set target, output directory, and minification options for improved performance

This commit is contained in:
Torsten Schulz (local)
2025-10-18 21:19:07 +02:00
parent bda61c0ed4
commit 06a65e9130
16 changed files with 4210 additions and 1 deletions

59
.deployment-files Normal file
View File

@@ -0,0 +1,59 @@
# Deployment-Dateien für TimeClock v3
# Diese Datei listet alle für das Deployment relevanten Dateien auf
## Hauptdokumentation
- DEPLOYMENT.md # Vollständige Deployment-Anleitung
- QUICKSTART_DEPLOYMENT.md # Schnellstart-Guide
## Konfigurationsdateien
- nginx.conf # Nginx-Konfiguration für stechuhr3.tsschulz.de
- timeclock.service # Systemd-Service-Datei
- ecosystem.config.js # PM2-Konfiguration
- backend/env.production.template # Environment-Template
## Scripts
- deploy.sh # Automatisches Deployment-Script
## Verwendung
### Automatisches Deployment:
```bash
./deploy.sh install # Erste Installation
./deploy.sh update # Update
./deploy.sh backup # Backup
./deploy.sh status # Status
./deploy.sh logs # Logs
```
### Manuelle Installation:
Siehe DEPLOYMENT.md oder QUICKSTART_DEPLOYMENT.md
## Wichtige Pfade auf dem Server
- Projekt: /var/www/timeclock
- Backend: /var/www/timeclock/backend
- Frontend: /var/www/timeclock/frontend/dist
- Logs: /var/log/timeclock
- Backups: /var/backups/timeclock
- Nginx Config: /etc/nginx/sites-available/stechuhr3.tsschulz.de
- Systemd Service: /etc/systemd/system/timeclock.service
## Checkliste vor Deployment
- [ ] DNS A-Record für stechuhr3.tsschulz.de gesetzt
- [ ] MySQL/MariaDB läuft
- [ ] Datenbank und User erstellt
- [ ] .env Datei auf Server angepasst
- [ ] JWT_SECRET und SESSION_SECRET generiert
- [ ] E-Mail-Konfiguration angepasst (falls verwendet)
- [ ] OAuth-Credentials erstellt (falls verwendet)
## Nach dem Deployment prüfen
- [ ] https://stechuhr3.tsschulz.de erreichbar
- [ ] https://stechuhr3.tsschulz.de/api/health zeigt "ok"
- [ ] Login funktioniert
- [ ] SSL-Zertifikat gültig
- [ ] Backend-Logs ohne Fehler
- [ ] Nginx-Logs ohne Fehler

99
.deployment-info Normal file
View File

@@ -0,0 +1,99 @@
# TimeClock v3 - Deployment Information
## Deine Konfiguration
- **Domain:** stechuhr3.tsschulz.de
- **Webserver:** Apache2
- **Backend Port:** 3010
- **Process Manager:** PM2 (empfohlen) oder systemd
- **SSL:** Let's Encrypt (Certbot)
- **Database:** MySQL (tsschulz.de:3306/stechuhr2)
## Wichtige Dateien
### Für Apache2 Deployment
- `apache2.conf` - Apache VirtualHost Konfiguration
- `APACHE2_DEPLOYMENT.md` - Detaillierte Apache2-Anleitung
- `deploy.sh` - Automatisches Deployment (auf apache2 konfiguriert)
### Allgemein
- `DEPLOYMENT.md` - Vollständige Deployment-Dokumentation
- `QUICKSTART_DEPLOYMENT.md` - Schnellstart-Guide
- `backend/env.production.template` - Environment-Template
## Schnellstart
```bash
# Auf Server:
cd /var/www/timeclock
./deploy.sh install
```
Das Script ist bereits für Apache2 konfiguriert (Zeile 45: WEBSERVER="apache2").
## Apache2-spezifische Befehle
```bash
# Site aktivieren
sudo a2ensite stechuhr3.tsschulz.de
# Apache testen
sudo apache2ctl configtest
# Apache neuladen
sudo systemctl reload apache2
# Logs ansehen
sudo tail -f /var/log/apache2/stechuhr3-*.log
# Modul aktivieren (falls benötigt)
sudo a2enmod proxy proxy_http ssl rewrite headers
```
## Benötigte Apache2-Module
Diese Module müssen aktiviert sein:
- proxy
- proxy_http
- ssl
- rewrite
- headers
- deflate
- expires
Aktivieren mit:
```bash
sudo a2enmod proxy proxy_http ssl rewrite headers deflate expires
sudo systemctl restart apache2
```
## SSL-Zertifikat erstellen
```bash
# Certbot für Apache
sudo apt install certbot python3-certbot-apache
# Zertifikat erstellen
sudo certbot --apache -d stechuhr3.tsschulz.de
```
## Nach dem Deployment
✅ Frontend: https://stechuhr3.tsschulz.de
✅ API Health: https://stechuhr3.tsschulz.de/api/health
✅ Backend läuft auf: http://localhost:3010
## Dokumentation
- Vollständige Anleitung: `DEPLOYMENT.md`
- Apache2-spezifisch: `APACHE2_DEPLOYMENT.md`
- Schnellstart: `QUICKSTART_DEPLOYMENT.md`
- Scripts: `scripts/README.md`
## Support & Troubleshooting
Bei Problemen siehe:
1. `APACHE2_DEPLOYMENT.md` - Troubleshooting-Sektion
2. Apache-Logs: `/var/log/apache2/stechuhr3-*.log`
3. Backend-Logs: `pm2 logs timeclock-backend`

448
APACHE2_DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,448 @@
# TimeClock v3 - Apache2 Deployment Guide
Spezielle Anleitung für das Deployment mit **Apache2** statt Nginx auf Ubuntu 22.04.
## Schnellstart
### Automatisches Deployment mit Apache2
Das `deploy.sh` Script ist bereits für Apache2 konfiguriert:
```bash
cd /var/www/timeclock
./deploy.sh install
```
Das Script erkennt automatisch, dass Apache2 verwendet wird (Variable `WEBSERVER="apache2"` in Zeile 45).
## Manuelle Apache2-Installation
### 1. Apache2 und Module installieren
```bash
# Apache2 installieren
sudo apt update
sudo apt install -y apache2
# Benötigte Module aktivieren
sudo a2enmod proxy proxy_http ssl rewrite headers deflate expires
# Apache2 neustarten
sudo systemctl restart apache2
```
### 2. VirtualHost konfigurieren
```bash
# Konfiguration kopieren
sudo cp /var/www/timeclock/apache2.conf /etc/apache2/sites-available/stechuhr3.tsschulz.de.conf
# Site aktivieren
sudo a2ensite stechuhr3.tsschulz.de
# Optional: Default-Site deaktivieren
sudo a2dissite 000-default
# Konfiguration testen
sudo apache2ctl configtest
# Apache2 neuladen
sudo systemctl reload apache2
```
### 3. SSL mit Certbot
```bash
# Certbot für Apache installieren
sudo apt install -y certbot python3-certbot-apache
# Zertifikat erstellen
sudo certbot --apache -d stechuhr3.tsschulz.de
```
## Apache2-Konfiguration erklärt
Die `apache2.conf` enthält:
### Proxy-Konfiguration für Backend-API
```apache
ProxyPass /api http://localhost:3010/api retry=0
ProxyPassReverse /api http://localhost:3010/api
```
Dies leitet alle `/api/*` Anfragen an das Node.js Backend auf Port 3010 weiter.
### SPA-Routing (Vue.js)
```apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/api
RewriteRule . /index.html [L]
</IfModule>
```
Dies sorgt dafür, dass alle Nicht-API-Anfragen an `index.html` geleitet werden (für Vue Router).
### Compression (gzip)
```apache
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css application/javascript
# ... weitere MIME-Types
</IfModule>
```
### Caching
```apache
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
# ... weitere Cache-Regeln
</IfModule>
```
### Security Headers
```apache
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Strict-Transport-Security "max-age=31536000"
# ... weitere Security-Header
```
## Wichtige Apache2-Befehle
### Sites verwalten
```bash
# Site aktivieren
sudo a2ensite stechuhr3.tsschulz.de
# Site deaktivieren
sudo a2dissite stechuhr3.tsschulz.de
# Alle aktivierten Sites anzeigen
ls -la /etc/apache2/sites-enabled/
```
### Module verwalten
```bash
# Modul aktivieren
sudo a2enmod proxy
sudo a2enmod ssl
sudo a2enmod rewrite
# Modul deaktivieren
sudo a2dismod module_name
# Aktivierte Module anzeigen
apache2ctl -M
```
### Apache2 steuern
```bash
# Status anzeigen
sudo systemctl status apache2
# Starten
sudo systemctl start apache2
# Stoppen
sudo systemctl stop apache2
# Neustarten (Downtime)
sudo systemctl restart apache2
# Neuladen (ohne Downtime)
sudo systemctl reload apache2
# Konfiguration testen
sudo apache2ctl configtest
# oder
sudo apachectl -t
```
### Logs anzeigen
```bash
# Access-Log
sudo tail -f /var/log/apache2/stechuhr3-access.log
# Error-Log
sudo tail -f /var/log/apache2/stechuhr3-error.log
# Alle Apache-Logs
sudo tail -f /var/log/apache2/*.log
```
## Troubleshooting
### Apache startet nicht
```bash
# Detaillierte Fehlerausgabe
sudo apache2ctl configtest
# Systemd-Logs
sudo journalctl -u apache2 -n 50
# Konfigurationsdateien prüfen
sudo apache2ctl -S
```
### Proxy funktioniert nicht
```bash
# Prüfe ob Modul aktiviert ist
apache2ctl -M | grep proxy
# Falls nicht aktiviert:
sudo a2enmod proxy proxy_http
sudo systemctl restart apache2
# Backend-Verfügbarkeit prüfen
curl http://localhost:3010/api/health
```
### SSL-Probleme
```bash
# Zertifikat prüfen
sudo certbot certificates
# Zertifikat erneuern
sudo certbot renew --apache
# SSL-Modul prüfen
apache2ctl -M | grep ssl
# Falls nicht aktiviert:
sudo a2enmod ssl
sudo systemctl restart apache2
```
### .htaccess wird ignoriert
```bash
# Stelle sicher, dass AllowOverride gesetzt ist
# In der VirtualHost-Konfiguration:
<Directory /var/www/timeclock/frontend/dist>
AllowOverride All
</Directory>
# Rewrite-Modul aktivieren
sudo a2enmod rewrite
sudo systemctl restart apache2
```
### Permissions-Probleme
```bash
# Korrekter Besitzer
sudo chown -R www-data:www-data /var/www/timeclock/frontend/dist
# Korrekte Berechtigungen
sudo find /var/www/timeclock/frontend/dist -type f -exec chmod 644 {} \;
sudo find /var/www/timeclock/frontend/dist -type d -exec chmod 755 {} \;
```
## Performance-Optimierung
### EnableKeepAlive
Füge in `/etc/apache2/apache2.conf` hinzu:
```apache
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
```
### MPM-Modul optimieren
```bash
# Zeige aktives MPM
apache2ctl -V | grep MPM
# Für Event MPM (empfohlen):
sudo nano /etc/apache2/mods-available/mpm_event.conf
```
Beispiel-Konfiguration:
```apache
<IfModule mpm_event_module>
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 150
MaxConnectionsPerChild 0
</IfModule>
```
### HTTP/2 aktivieren
```bash
# HTTP/2 Modul aktivieren
sudo a2enmod http2
# In VirtualHost hinzufügen:
# Protocols h2 http/1.1
sudo systemctl restart apache2
```
### Caching verbessern
Aktiviere mod_cache:
```bash
sudo a2enmod cache
sudo a2enmod cache_disk
sudo systemctl restart apache2
```
In der VirtualHost-Konfiguration:
```apache
<IfModule mod_cache.c>
CacheQuickHandler off
CacheLock on
CacheLockPath /tmp/mod_cache-lock
CacheLockMaxAge 5
CacheIgnoreHeaders Set-Cookie
</IfModule>
```
## Sicherheit
### mod_security installieren (Web Application Firewall)
```bash
# Installieren
sudo apt install -y libapache2-mod-security2
# Aktivieren
sudo a2enmod security2
# Basis-Konfiguration
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
# SecRuleEngine auf "On" setzen
sudo nano /etc/modsecurity/modsecurity.conf
# SecRuleEngine On
sudo systemctl restart apache2
```
### mod_evasive (DDoS-Schutz)
```bash
# Installieren
sudo apt install -y libapache2-mod-evasive
# Konfigurieren
sudo nano /etc/apache2/mods-available/evasive.conf
```
```apache
<IfModule mod_evasive20.c>
DOSHashTableSize 3097
DOSPageCount 5
DOSSiteCount 100
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 10
DOSEmailNotify admin@tsschulz.de
</IfModule>
```
```bash
sudo a2enmod evasive
sudo systemctl restart apache2
```
### fail2ban für Apache
```bash
# Installieren
sudo apt install -y fail2ban
# Apache-Jail aktivieren
sudo nano /etc/fail2ban/jail.local
```
```ini
[apache-auth]
enabled = true
[apache-badbots]
enabled = true
[apache-noscript]
enabled = true
[apache-overflows]
enabled = true
```
```bash
sudo systemctl restart fail2ban
```
## Vergleich Nginx vs Apache2
| Feature | Nginx | Apache2 |
|---------|-------|---------|
| **Performance** | Sehr hoch (Event-driven) | Hoch (Process/Thread-based) |
| **Konfiguration** | Einfacher | Komplexer, aber mächtiger |
| **.htaccess** | Nicht unterstützt | Unterstützt |
| **Module** | Weniger, aber effizienter | Sehr viele verfügbar |
| **Best for** | Reverse Proxy, statische Dateien | .htaccess, komplexe Setups |
| **Memory** | Geringer | Höher |
Für TimeClock ist **beides** geeignet, Apache2 bietet mehr Flexibilität, Nginx mehr Performance.
## Migration von Nginx zu Apache2
Falls du bereits Nginx installiert hast:
```bash
# Nginx stoppen und deaktivieren
sudo systemctl stop nginx
sudo systemctl disable nginx
# Apache2 installieren und einrichten (siehe oben)
# Firewall anpassen
sudo ufw delete allow 'Nginx Full'
sudo ufw allow 'Apache Full'
# SSL-Zertifikat ist kompatibel, keine Änderung nötig
```
## Nützliche Links
- [Apache2 Dokumentation](https://httpd.apache.org/docs/2.4/)
- [Apache2 auf Ubuntu](https://ubuntu.com/server/docs/web-servers-apache)
- [Let's Encrypt mit Apache](https://certbot.eff.org/instructions?ws=apache&os=ubuntufocal)
- [Apache Security Best Practices](https://geekflare.com/apache-web-server-hardening-security/)
---
**Apache2 läuft! 🚀**
Bei Fragen oder Problemen: Siehe `DEPLOYMENT.md` oder prüfe die Logs!

581
DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,581 @@
# TimeClock v3 - Deployment Guide für Ubuntu 22.04
Dieser Guide beschreibt das komplette Deployment der TimeClock-App auf einem Ubuntu 22.04 Server unter der URL **stechuhr3.tsschulz.de**.
## Voraussetzungen
### Software-Anforderungen
- Ubuntu 22.04 LTS
- Node.js v18+ (empfohlen: v20)
- MySQL 8.0 oder MariaDB 10.6+
- Nginx
- Certbot (für SSL/Let's Encrypt)
- PM2 oder systemd für Process Management
### Domain & DNS
- Domain: `stechuhr3.tsschulz.de`
- DNS A-Record muss auf die Server-IP zeigen
## Schritt 1: System-Vorbereitung
```bash
# System aktualisieren
sudo apt update && sudo apt upgrade -y
# Node.js 20.x installieren
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Nginx installieren
sudo apt install -y nginx
# Certbot für SSL installieren
sudo apt install -y certbot python3-certbot-nginx
# PM2 global installieren (empfohlen)
sudo npm install -g pm2
# MySQL/MariaDB sollte bereits installiert sein
# Falls nicht:
# sudo apt install -y mysql-server
```
## Schritt 2: Datenbank vorbereiten
```bash
# In MySQL/MariaDB einloggen
sudo mysql -u root -p
# Datenbank und User erstellen (falls noch nicht vorhanden)
CREATE DATABASE IF NOT EXISTS stechuhr2;
CREATE USER IF NOT EXISTS 'timeclock'@'localhost' IDENTIFIED BY 'SICHERES_PASSWORT';
GRANT ALL PRIVILEGES ON stechuhr2.* TO 'timeclock'@'localhost';
FLUSH PRIVILEGES;
EXIT;
# Schema importieren (falls noch nicht vorhanden)
# mysql -u timeclock -p stechuhr2 < backend/database-schema.sql
```
## Schritt 3: Projekt auf Server klonen/übertragen
```bash
# Projekt-Verzeichnis erstellen
sudo mkdir -p /var/www
cd /var/www
# Mit Git klonen (oder per SCP übertragen)
# sudo git clone https://github.com/IHR-USERNAME/TimeClock.git timeclock
# ODER das lokale Projekt übertragen:
# rsync -avz --exclude 'node_modules' ~/Programs/TimeClock/ user@server:/var/www/timeclock/
# Eigentümer setzen
sudo chown -R $USER:$USER /var/www/timeclock
```
## Schritt 4: Backend konfigurieren
```bash
cd /var/www/timeclock/backend
# Dependencies installieren
npm install --production
# .env Datei erstellen
cp .env.production.example .env
# .env bearbeiten und folgende Werte anpassen:
nano .env
```
**Wichtige .env Variablen:**
```env
NODE_ENV=production
PORT=3010
# Datenbank
DB_HOST=localhost
DB_NAME=stechuhr2
DB_USER=timeclock
DB_PASSWORD=IHR_DB_PASSWORT
# JWT
JWT_SECRET=IHR_SICHERER_JWT_SECRET_KEY
JWT_EXPIRES_IN=7d
# Session
SESSION_SECRET=IHR_SICHERER_SESSION_SECRET
# Frontend URL
FRONTEND_URL=https://stechuhr3.tsschulz.de
# Email (optional, für Passwort-Reset)
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
EMAIL_USER=your-email@example.com
EMAIL_PASSWORD=your-email-password
EMAIL_FROM=noreply@tsschulz.de
# OAuth (optional, falls Google OAuth verwendet wird)
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_CALLBACK_URL=https://stechuhr3.tsschulz.de/api/auth/oauth/google/callback
```
## Schritt 5: Frontend bauen
```bash
cd /var/www/timeclock/frontend
# Dependencies installieren
npm install
# Produktions-Build erstellen
npm run build
# Build-Dateien werden in /var/www/timeclock/frontend/dist erstellt
```
## Schritt 6: Nginx konfigurieren
```bash
# Nginx-Konfiguration erstellen
sudo nano /etc/nginx/sites-available/stechuhr3.tsschulz.de
```
Folgende Konfiguration einfügen (siehe `nginx.conf` im Projekt):
```nginx
server {
listen 80;
listen [::]:80;
server_name stechuhr3.tsschulz.de;
# Zunächst nur HTTP für Certbot
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name stechuhr3.tsschulz.de;
# SSL-Zertifikate (werden von Certbot automatisch eingefügt)
ssl_certificate /etc/letsencrypt/live/stechuhr3.tsschulz.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stechuhr3.tsschulz.de/privkey.pem;
# SSL-Konfiguration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!MD5;
# Frontend (Vue.js SPA)
root /var/www/timeclock/frontend/dist;
index index.html;
# Logging
access_log /var/log/nginx/stechuhr3.access.log;
error_log /var/log/nginx/stechuhr3.error.log;
# Gzip Compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# API Proxy zum Backend
location /api {
proxy_pass http://localhost:3010;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# SPA Fallback - alle anderen Requests zu index.html
location / {
try_files $uri $uri/ /index.html;
}
# Cache für statische Assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
```
```bash
# Konfiguration aktivieren
sudo ln -s /etc/nginx/sites-available/stechuhr3.tsschulz.de /etc/nginx/sites-enabled/
# Default-Seite deaktivieren (optional)
sudo rm -f /etc/nginx/sites-enabled/default
# Nginx-Konfiguration testen
sudo nginx -t
# Nginx neuladen
sudo systemctl reload nginx
```
## Schritt 7: SSL-Zertifikat mit Let's Encrypt
```bash
# Certbot ausführen
sudo certbot --nginx -d stechuhr3.tsschulz.de
# Folgen Sie den Anweisungen:
# - E-Mail-Adresse eingeben
# - Nutzungsbedingungen akzeptieren
# - Optional: Newsletter ablehnen
# - HTTPS-Redirect: Ja (empfohlen)
# Automatische Erneuerung testen
sudo certbot renew --dry-run
```
## Schritt 8: Backend als Service einrichten
### Option A: PM2 (empfohlen)
```bash
cd /var/www/timeclock/backend
# Backend mit PM2 starten
pm2 start src/index.js --name timeclock-backend --env production
# PM2 Auto-Start konfigurieren
pm2 startup systemd
# Führen Sie den angezeigten Befehl aus
# Aktuelle PM2-Prozesse speichern
pm2 save
# Status überprüfen
pm2 status
pm2 logs timeclock-backend
```
### Option B: Systemd Service
```bash
# Service-Datei erstellen
sudo nano /etc/systemd/system/timeclock.service
```
Folgende Konfiguration einfügen:
```ini
[Unit]
Description=TimeClock v3 Backend API
After=network.target mysql.service
[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/timeclock/backend
Environment=NODE_ENV=production
ExecStart=/usr/bin/node src/index.js
Restart=always
RestartSec=10
StandardOutput=append:/var/log/timeclock/backend.log
StandardError=append:/var/log/timeclock/backend.error.log
# Sicherheit
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/www/timeclock/backend
[Install]
WantedBy=multi-user.target
```
```bash
# Log-Verzeichnis erstellen
sudo mkdir -p /var/log/timeclock
sudo chown www-data:www-data /var/log/timeclock
# Service aktivieren und starten
sudo systemctl daemon-reload
sudo systemctl enable timeclock
sudo systemctl start timeclock
# Status prüfen
sudo systemctl status timeclock
# Logs ansehen
sudo journalctl -u timeclock -f
```
## Schritt 9: Firewall konfigurieren
```bash
# UFW Firewall aktivieren (falls noch nicht aktiv)
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enable
# Status prüfen
sudo ufw status
```
## Schritt 10: Deployment testen
```bash
# Backend-Health-Check
curl http://localhost:3010/api/health
# Frontend über Domain aufrufen
# https://stechuhr3.tsschulz.de
# SSL-Test
curl -I https://stechuhr3.tsschulz.de
```
## Wartung und Updates
### Backend aktualisieren
```bash
cd /var/www/timeclock/backend
git pull # oder neue Dateien übertragen
npm install --production
# Mit PM2:
pm2 restart timeclock-backend
# Mit systemd:
sudo systemctl restart timeclock
```
### Frontend aktualisieren
```bash
cd /var/www/timeclock/frontend
git pull # oder neue Dateien übertragen
npm install
npm run build
# Nginx cached automatisch neu
```
### Logs überwachen
```bash
# PM2 Logs
pm2 logs timeclock-backend
# Systemd Logs
sudo journalctl -u timeclock -f
# Nginx Logs
sudo tail -f /var/log/nginx/stechuhr3.access.log
sudo tail -f /var/log/nginx/stechuhr3.error.log
```
### Datenbank-Backup
```bash
# Tägliches Backup einrichten
sudo nano /usr/local/bin/backup-timeclock.sh
```
```bash
#!/bin/bash
BACKUP_DIR="/var/backups/timeclock"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Datenbank-Backup
mysqldump -u timeclock -p'IHR_PASSWORT' stechuhr2 | gzip > $BACKUP_DIR/timeclock_$DATE.sql.gz
# Alte Backups löschen (älter als 30 Tage)
find $BACKUP_DIR -name "timeclock_*.sql.gz" -mtime +30 -delete
echo "Backup erstellt: timeclock_$DATE.sql.gz"
```
```bash
# Ausführbar machen
sudo chmod +x /usr/local/bin/backup-timeclock.sh
# Cronjob einrichten (täglich um 2 Uhr)
sudo crontab -e
# Folgende Zeile hinzufügen:
# 0 2 * * * /usr/local/bin/backup-timeclock.sh
```
## Troubleshooting
### Backend startet nicht
```bash
# Logs prüfen
pm2 logs timeclock-backend --err
# oder
sudo journalctl -u timeclock -n 50
# Port-Belegung prüfen
sudo netstat -tulpn | grep 3010
# .env Datei prüfen
cat /var/www/timeclock/backend/.env
```
### Frontend zeigt nicht an
```bash
# Nginx-Konfiguration testen
sudo nginx -t
# Nginx neuladen
sudo systemctl reload nginx
# Dist-Verzeichnis prüfen
ls -la /var/www/timeclock/frontend/dist/
```
### SSL-Probleme
```bash
# Zertifikat erneuern
sudo certbot renew --force-renewal -d stechuhr3.tsschulz.de
# Nginx neustarten
sudo systemctl restart nginx
```
### API-Requests schlagen fehl
```bash
# CORS-Probleme: FRONTEND_URL in .env prüfen
# Nginx Proxy-Konfiguration prüfen
sudo nginx -t
# Backend-Logs prüfen
pm2 logs timeclock-backend
```
## Performance-Optimierung
### PM2 Cluster-Modus (optional)
```bash
cd /var/www/timeclock/backend
# PM2 im Cluster-Modus starten (nutzt alle CPU-Kerne)
pm2 delete timeclock-backend
pm2 start src/index.js --name timeclock-backend --instances max --env production
pm2 save
```
### Nginx Caching (optional)
Füge in der Nginx-Konfiguration hinzu:
```nginx
# Im http-Block von /etc/nginx/nginx.conf
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=100m inactive=60m use_temp_path=off;
# Im server-Block der stechuhr3.tsschulz.de Konfiguration
location /api {
proxy_cache api_cache;
proxy_cache_valid 200 5m;
proxy_cache_bypass $http_cache_control;
add_header X-Cache-Status $upstream_cache_status;
# ... rest der proxy_pass Konfiguration
}
```
## Sicherheits-Checkliste
- [x] SSL/TLS aktiviert (Let's Encrypt)
- [x] Firewall konfiguriert (UFW)
- [x] Starke Passwörter für DB und JWT_SECRET
- [x] NODE_ENV=production gesetzt
- [x] Nginx Security Headers aktiviert
- [x] Backend läuft nicht als Root
- [x] Regelmäßige Backups eingerichtet
- [ ] fail2ban installieren (optional)
- [ ] Rate Limiting in Nginx konfigurieren (optional)
- [ ] ModSecurity WAF installieren (optional)
## Monitoring (optional)
### PM2 Plus Monitoring
```bash
# PM2 Plus Account erstellen: https://id.keymetrics.io
pm2 plus
# Link folgen und Account verbinden
```
### Einfaches Uptime-Monitoring
```bash
# Einfaches Health-Check Script
sudo nano /usr/local/bin/check-timeclock.sh
```
```bash
#!/bin/bash
if ! curl -sf http://localhost:3010/api/health > /dev/null; then
echo "Backend ist down! $(date)" >> /var/log/timeclock/health-check.log
pm2 restart timeclock-backend
# oder: sudo systemctl restart timeclock
fi
```
```bash
# Ausführbar machen
sudo chmod +x /usr/local/bin/check-timeclock.sh
# Cronjob alle 5 Minuten
sudo crontab -e
# */5 * * * * /usr/local/bin/check-timeclock.sh
```
## Zusammenfassung der Endpunkte
- **Frontend:** https://stechuhr3.tsschulz.de
- **API:** https://stechuhr3.tsschulz.de/api
- **Health-Check:** https://stechuhr3.tsschulz.de/api/health
## Support
Bei Problemen:
1. Logs prüfen (`pm2 logs` oder `journalctl -u timeclock`)
2. Nginx-Logs prüfen (`/var/log/nginx/`)
3. Datenbank-Verbindung testen
4. .env Konfiguration überprüfen
---
**Deployment erfolgreich! 🎉**
Ihre TimeClock-App läuft jetzt auf https://stechuhr3.tsschulz.de

421
QUICKSTART_DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,421 @@
# 🚀 TimeClock v3 - Schnellstart Deployment
Eine Schritt-für-Schritt-Anleitung für das Deployment auf **stechuhr3.tsschulz.de**.
## Voraussetzungen
✅ Ubuntu 22.04 Server
✅ Root/Sudo-Zugriff
✅ Domain `stechuhr3.tsschulz.de` zeigt auf Server-IP
✅ MySQL/MariaDB läuft
✅ SSH-Zugang zum Server
## Option 1: Automatisches Deployment (Empfohlen) 🎯
### Auf deinem lokalen Rechner:
```bash
# 1. Projekt auf Server übertragen
cd /home/torsten/Programs/TimeClock
rsync -avz --exclude 'node_modules' --exclude '.git' . user@YOUR_SERVER_IP:/tmp/timeclock-deploy/
# 2. SSH zum Server
ssh user@YOUR_SERVER_IP
```
### Auf dem Server:
```bash
# 3. Projekt vorbereiten
sudo mkdir -p /var/www
sudo mv /tmp/timeclock-deploy /var/www/timeclock
sudo chown -R $USER:$USER /var/www/timeclock
# 4. Automatisches Deployment starten
cd /var/www/timeclock
chmod +x deploy.sh
./deploy.sh install
```
Das war's! 🎉 Das Script führt automatisch durch alle notwendigen Schritte.
---
## Option 2: Manuelles Deployment (Schritt für Schritt) 📝
### Schritt 1: System vorbereiten
```bash
# System aktualisieren
sudo apt update && sudo apt upgrade -y
# Node.js 20.x installieren
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Weitere Abhängigkeiten
sudo apt install -y nginx certbot python3-certbot-nginx
sudo npm install -g pm2
```
### Schritt 2: Datenbank einrichten
```bash
# MySQL/MariaDB
sudo mysql -u root -p
# In MySQL:
CREATE DATABASE IF NOT EXISTS stechuhr2;
CREATE USER IF NOT EXISTS 'timeclock'@'localhost' IDENTIFIED BY 'DEIN_SICHERES_PASSWORT';
GRANT ALL PRIVILEGES ON stechuhr2.* TO 'timeclock'@'localhost';
FLUSH PRIVILEGES;
EXIT;
```
### Schritt 3: Projekt auf Server übertragen
```bash
# Auf lokalem Rechner:
rsync -avz --exclude 'node_modules' ~/Programs/TimeClock/ user@YOUR_SERVER_IP:/var/www/timeclock/
# ODER per Git (wenn in Repository):
# ssh user@YOUR_SERVER_IP
# cd /var/www
# git clone https://github.com/IHR-USERNAME/TimeClock.git timeclock
```
### Schritt 4: Backend konfigurieren
```bash
cd /var/www/timeclock/backend
# Dependencies installieren
npm install --production
# .env erstellen
cp env.production.template .env
nano .env
# Wichtige Werte anpassen:
# - DB_PASSWORD=dein_db_passwort
# - JWT_SECRET=generiere_mit_node_crypto
# - SESSION_SECRET=generiere_mit_node_crypto
# - FRONTEND_URL=https://stechuhr3.tsschulz.de
# Secrets generieren:
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
```
### Schritt 5: Frontend bauen
```bash
cd /var/www/timeclock/frontend
npm install
npm run build
```
### Schritt 6: Nginx einrichten
```bash
# Konfiguration kopieren
sudo cp /var/www/timeclock/nginx.conf /etc/nginx/sites-available/stechuhr3.tsschulz.de
# Site aktivieren
sudo ln -s /etc/nginx/sites-available/stechuhr3.tsschulz.de /etc/nginx/sites-enabled/
# Default-Site deaktivieren (optional)
sudo rm /etc/nginx/sites-enabled/default
# Testen und neuladen
sudo nginx -t
sudo systemctl reload nginx
```
### Schritt 7: SSL-Zertifikat erstellen
```bash
# Let's Encrypt Zertifikat
sudo certbot --nginx -d stechuhr3.tsschulz.de
# Folge den Anweisungen:
# - E-Mail eingeben
# - Nutzungsbedingungen akzeptieren
# - HTTPS-Redirect aktivieren (empfohlen)
```
### Schritt 8: Backend starten
**Option A: Mit PM2 (empfohlen):**
```bash
cd /var/www/timeclock/backend
pm2 start src/index.js --name timeclock-backend --env production
pm2 save
pm2 startup systemd
# Führe den angezeigten Befehl aus
```
**Option B: Mit systemd:**
```bash
# Service installieren
sudo cp /var/www/timeclock/timeclock.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable timeclock
sudo systemctl start timeclock
```
### Schritt 9: Firewall konfigurieren
```bash
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enable
```
### Schritt 10: Testen
```bash
# Backend-Health-Check
curl http://localhost:3010/api/health
# Frontend aufrufen
# Browser: https://stechuhr3.tsschulz.de
```
---
## Nützliche Befehle
### Mit PM2:
```bash
pm2 status # Status anzeigen
pm2 logs timeclock-backend # Logs anzeigen
pm2 restart timeclock-backend # Neustart
pm2 stop timeclock-backend # Stoppen
pm2 monit # Monitoring
```
### Mit systemd:
```bash
sudo systemctl status timeclock # Status
sudo journalctl -u timeclock -f # Logs
sudo systemctl restart timeclock # Neustart
sudo systemctl stop timeclock # Stoppen
```
### Nginx:
```bash
sudo nginx -t # Config testen
sudo systemctl reload nginx # Neuladen
sudo tail -f /var/log/nginx/stechuhr3.access.log
sudo tail -f /var/log/nginx/stechuhr3.error.log
```
### SSL:
```bash
sudo certbot certificates # Zertifikate anzeigen
sudo certbot renew # Erneuern
sudo certbot renew --dry-run # Test-Erneuerung
```
---
## Updates durchführen
### Automatisch mit Script:
```bash
cd /var/www/timeclock
./deploy.sh backup # Erst Backup
./deploy.sh update # Dann Update
```
### Manuell:
```bash
# 1. Backup erstellen
cd /var/www/timeclock
./deploy.sh backup
# 2. Code aktualisieren
git pull # oder neue Dateien übertragen
# 3. Backend
cd backend
npm install --production
pm2 restart timeclock-backend
# 4. Frontend
cd ../frontend
npm install
npm run build
```
---
## Problembehebung
### Backend startet nicht
```bash
# Logs prüfen
pm2 logs timeclock-backend --err
# .env prüfen
cat /var/www/timeclock/backend/.env
# Port-Belegung prüfen
sudo netstat -tulpn | grep 3010
```
### Frontend zeigt nicht an
```bash
# Nginx testen
sudo nginx -t
# Build-Verzeichnis prüfen
ls -la /var/www/timeclock/frontend/dist/
# Nginx neuladen
sudo systemctl reload nginx
```
### API-Anfragen schlagen fehl
```bash
# CORS: FRONTEND_URL in .env prüfen
# Backend muss FRONTEND_URL=https://stechuhr3.tsschulz.de haben
# Nginx Proxy prüfen
sudo nginx -t
```
### SSL-Probleme
```bash
# Zertifikat erneuern
sudo certbot renew --force-renewal
# Nginx neustarten
sudo systemctl restart nginx
```
---
## Backup & Restore
### Backup erstellen:
```bash
# Automatisch
./deploy.sh backup
# Manuell - Datenbank
mysqldump -u timeclock -p stechuhr2 | gzip > backup_$(date +%Y%m%d).sql.gz
# Manuell - Code
tar -czf backup_code_$(date +%Y%m%d).tar.gz /var/www/timeclock
```
### Restore:
```bash
# Datenbank
gunzip < backup_20251018.sql.gz | mysql -u timeclock -p stechuhr2
# Code
./deploy.sh rollback
```
---
## Performance-Tipps
### PM2 Cluster-Modus (nutzt alle CPU-Kerne):
```bash
pm2 delete timeclock-backend
pm2 start src/index.js --name timeclock-backend --instances max --env production
pm2 save
```
### Nginx Caching aktivieren:
Füge in `/etc/nginx/sites-available/stechuhr3.tsschulz.de` hinzu:
```nginx
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=100m;
location /api {
proxy_cache api_cache;
proxy_cache_valid 200 5m;
# ... rest der Config
}
```
---
## Sicherheits-Checkliste
- [ ] Starke Passwörter für DB, JWT, Session
- [ ] SSL/TLS aktiviert
- [ ] Firewall konfiguriert
- [ ] NODE_ENV=production
- [ ] Console-Logs deaktiviert (Vite Build)
- [ ] Regelmäßige Backups eingerichtet
- [ ] fail2ban installieren (optional)
- [ ] Rate Limiting aktivieren (optional)
---
## Monitoring einrichten
### Einfaches Health-Check Script:
```bash
# /usr/local/bin/check-timeclock.sh
#!/bin/bash
if ! curl -sf http://localhost:3010/api/health > /dev/null; then
echo "Backend down! $(date)" >> /var/log/timeclock/health.log
pm2 restart timeclock-backend
fi
```
```bash
sudo chmod +x /usr/local/bin/check-timeclock.sh
# Cronjob alle 5 Minuten
sudo crontab -e
# */5 * * * * /usr/local/bin/check-timeclock.sh
```
### PM2 Plus (Cloud-Monitoring):
```bash
pm2 plus
# Link folgen und Account verbinden
```
---
## Support
Detaillierte Dokumentation: `DEPLOYMENT.md`
Bei Problemen:
1. Logs prüfen (`pm2 logs` oder `journalctl -u timeclock`)
2. Nginx-Logs prüfen (`/var/log/nginx/`)
3. Health-Check testen: `curl http://localhost:3010/api/health`
---
**Deployment erfolgreich! 🎉**
Deine App läuft auf: **https://stechuhr3.tsschulz.de**

275
apache2.conf Normal file
View File

@@ -0,0 +1,275 @@
# Apache 2 VirtualHost-Konfiguration für TimeClock v3
# Datei speichern unter: /etc/apache2/sites-available/stechuhr3.tsschulz.de.conf
#
# Installation:
# sudo cp apache2.conf /etc/apache2/sites-available/stechuhr3.tsschulz.de.conf
# sudo a2enmod proxy proxy_http ssl rewrite headers deflate expires
# sudo a2ensite stechuhr3.tsschulz.de
# sudo apache2ctl configtest
# sudo systemctl reload apache2
# HTTP VirtualHost - Redirect zu HTTPS
<VirtualHost *:80>
ServerName stechuhr3.tsschulz.de
ServerAdmin admin@tsschulz.de
# Let's Encrypt ACME Challenge
DocumentRoot /var/www/certbot
<Directory /var/www/certbot>
Require all granted
</Directory>
# Alle anderen Requests zu HTTPS umleiten
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
ErrorLog ${APACHE_LOG_DIR}/stechuhr3-error.log
CustomLog ${APACHE_LOG_DIR}/stechuhr3-access.log combined
</VirtualHost>
# HTTPS VirtualHost - Hauptkonfiguration
<VirtualHost *:443>
ServerName stechuhr3.tsschulz.de
ServerAdmin admin@tsschulz.de
# =================================================================
# SSL-Konfiguration (wird von Certbot automatisch verwaltet)
# =================================================================
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/stechuhr3.tsschulz.de/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/stechuhr3.tsschulz.de/privkey.pem
# SSL-Protokolle und Cipher Suites
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
# OCSP Stapling
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
# =================================================================
# Frontend (Vue.js SPA)
# =================================================================
DocumentRoot /var/www/timeclock/frontend/dist
<Directory /var/www/timeclock/frontend/dist>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
# SPA Fallback - alle Requests zu index.html
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/api
RewriteRule . /index.html [L]
</IfModule>
</Directory>
# =================================================================
# Gzip Compression
# =================================================================
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript
AddOutputFilterByType DEFLATE application/javascript application/x-javascript application/json
AddOutputFilterByType DEFLATE application/xml application/xml+rss application/rss+xml
AddOutputFilterByType DEFLATE application/atom+xml
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE font/ttf font/woff font/woff2
</IfModule>
# =================================================================
# Security Headers
# =================================================================
<IfModule mod_headers.c>
# X-Frame-Options: Schutz vor Clickjacking
Header always set X-Frame-Options "SAMEORIGIN"
# X-Content-Type-Options: Verhindert MIME-Type Sniffing
Header always set X-Content-Type-Options "nosniff"
# X-XSS-Protection: XSS-Schutz für ältere Browser
Header always set X-XSS-Protection "1; mode=block"
# Referrer-Policy: Kontrolliert Referrer-Informationen
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Permissions-Policy: Kontrolliert Browser-Features
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Content-Security-Policy (angepasst für Vue.js)
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://stechuhr3.tsschulz.de;"
# Strict-Transport-Security (HSTS)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</IfModule>
# =================================================================
# API Reverse Proxy zum Backend
# =================================================================
<IfModule mod_proxy.c>
ProxyPreserveHost On
ProxyRequests Off
# Timeouts
ProxyTimeout 60
# API Proxy
ProxyPass /api http://localhost:3010/api retry=0
ProxyPassReverse /api http://localhost:3010/api
<Location /api>
# Proxy Headers
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
RequestHeader set X-Real-IP %{REMOTE_ADDR}s
# CORS Headers (falls benötigt, aber Backend sollte das handhaben)
# Header set Access-Control-Allow-Origin "https://stechuhr3.tsschulz.de"
# Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
# Header set Access-Control-Allow-Headers "Content-Type, Authorization"
# Header set Access-Control-Allow-Credentials "true"
</Location>
</IfModule>
# =================================================================
# Statische Assets mit langem Cache
# =================================================================
<IfModule mod_expires.c>
ExpiresActive On
# JavaScript und CSS
ExpiresByType text/css "access plus 1 year"
ExpiresByType text/javascript "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType application/x-javascript "access plus 1 year"
# Bilder
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/x-icon "access plus 1 year"
# Fonts
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType application/font-woff "access plus 1 year"
ExpiresByType application/font-woff2 "access plus 1 year"
# HTML (kein Cache)
ExpiresByType text/html "access plus 0 seconds"
</IfModule>
# Cache-Control Headers für Assets
<FilesMatch "\.(js|css|png|jpg|jpeg|gif|ico|svg|webp|woff|woff2|ttf|eot)$">
<IfModule mod_headers.c>
Header set Cache-Control "public, max-age=31536000, immutable"
</IfModule>
</FilesMatch>
# Kein Cache für HTML
<FilesMatch "\.(html|htm)$">
<IfModule mod_headers.c>
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "0"
</IfModule>
</FilesMatch>
# =================================================================
# Spezielle Dateien
# =================================================================
# robots.txt und sitemap.xml
<FilesMatch "(robots\.txt|sitemap\.xml)$">
<IfModule mod_headers.c>
Header set Cache-Control "public, max-age=86400"
</IfModule>
</FilesMatch>
# =================================================================
# Sicherheit: Verstecke sensible Dateien
# =================================================================
# Blockiere Zugriff auf versteckte Dateien (.git, .env, etc.)
<DirectoryMatch "^\.|\/\.">
Require all denied
</DirectoryMatch>
<FilesMatch "^\.">
Require all denied
</FilesMatch>
# Blockiere Zugriff auf Backup-Dateien
<FilesMatch "~$">
Require all denied
</FilesMatch>
# Blockiere .env Dateien
<FilesMatch "\.env">
Require all denied
</FilesMatch>
# =================================================================
# Limits
# =================================================================
# Client Body Size Limit (z.B. für File-Uploads)
LimitRequestBody 10485760
# Timeouts
TimeOut 300
# =================================================================
# Logging
# =================================================================
ErrorLog ${APACHE_LOG_DIR}/stechuhr3-error.log
CustomLog ${APACHE_LOG_DIR}/stechuhr3-access.log combined
# Optional: Log-Level für detailliertere Logs
# LogLevel info ssl:warn proxy:debug
</VirtualHost>
# =================================================================
# Globale SSL-Konfiguration (optional, in /etc/apache2/mods-available/ssl.conf)
# =================================================================
# <IfModule mod_ssl.c>
# # OCSP Stapling Cache
# SSLStaplingCache shmcb:/var/run/ocsp(128000)
#
# # SSL Session Cache
# SSLSessionCache shmcb:/var/run/ssl_scache(512000)
# SSLSessionCacheTimeout 300
# </IfModule>
# =================================================================
# OPTIONAL: Rate Limiting mit mod_evasive
# =================================================================
# Installieren mit: sudo apt install libapache2-mod-evasive
# Dann konfigurieren in: /etc/apache2/mods-available/evasive.conf
#
# <IfModule mod_evasive20.c>
# DOSHashTableSize 3097
# DOSPageCount 5
# DOSSiteCount 100
# DOSPageInterval 1
# DOSSiteInterval 1
# DOSBlockingPeriod 10
# DOSEmailNotify admin@tsschulz.de
# DOSLogDir /var/log/apache2/mod_evasive
# </IfModule>
# =================================================================
# OPTIONAL: Zusätzliche Security mit mod_security
# =================================================================
# Installieren mit: sudo apt install libapache2-mod-security2
# Konfiguration in: /etc/modsecurity/modsecurity.conf

View File

@@ -0,0 +1,90 @@
# TimeClock v3 - Production Environment Configuration
# Kopiere diese Datei zu .env und passe die Werte an:
# cp env.production.template .env
# =============================================================================
# NODE ENVIRONMENT
# =============================================================================
NODE_ENV=production
PORT=3010
# =============================================================================
# DATABASE CONFIGURATION
# =============================================================================
DB_HOST=tsschulz.de
DB_PORT=3306
DB_NAME=stechuhr2
DB_USER=stechuhr2
DB_PASSWORD=p3Lv9!7?+Qq
# =============================================================================
# JWT AUTHENTICATION
# =============================================================================
# Generiere einen sicheren Secret mit: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
JWT_SECRET=f393b1159fd0212df164711112bc72edf9481181b719de6bc61e4654885a6269ecf95dca0568a27081fa68b039e4de7cffcb4c9a8927bcddaf54dc869899f6e8
JWT_EXPIRES_IN=7d
JWT_REFRESH_EXPIRES_IN=30d
# =============================================================================
# SESSION CONFIGURATION
# =============================================================================
# Generiere einen sicheren Secret mit: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
SESSION_SECRET=f393b1159fd0212df164711112bc72edf9481181b719de6bc61e4654885a6269ecf95dca0568a27081fa68b039e4de7cffcb4c9a8927bcddaf54dc869899f6e8
# =============================================================================
# FRONTEND URL
# =============================================================================
FRONTEND_URL=https://stechuhr3.tsschulz.de
# =============================================================================
# EMAIL CONFIGURATION (für Passwort-Reset etc.)
# =============================================================================
# SMTP-Server Konfiguration
EMAIL_HOST=smtp.1blu.de
EMAIL_PORT=587
EMAIL_SECURE=true
EMAIL_USER=e226079_0-kontakt
EMAIL_PASSWORD=aNN31bll3Na!
EMAIL_FROM=kontakt@tsschulz.de
EMAIL_FROM_NAME=TimeClock Zeiterfassung
# =============================================================================
# OAUTH CONFIGURATION (Google OAuth)
# =============================================================================
# Optional: Falls Google OAuth verwendet werden soll
# Erstelle OAuth Credentials unter: https://console.cloud.google.com/
GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_CALLBACK_URL=https://stechuhr3.tsschulz.de/api/auth/oauth/google/callback
# =============================================================================
# SECURITY & CORS
# =============================================================================
# Allowed Origins (comma-separated, falls mehrere Domains)
ALLOWED_ORIGINS=https://stechuhr3.tsschulz.de
# =============================================================================
# LOGGING
# =============================================================================
LOG_LEVEL=info
# Optionen: error, warn, info, http, verbose, debug, silly
# =============================================================================
# APPLICATION SETTINGS
# =============================================================================
# Maximale Dateigröße für Uploads (falls implementiert)
MAX_FILE_SIZE=5242880
# Rate Limiting (Anfragen pro Fenster)
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100
# =============================================================================
# BACKUP CONFIGURATION (Optional)
# =============================================================================
# Backup-Verzeichnis
BACKUP_DIR=/var/backups/timeclock
# Backup-Retention in Tagen
BACKUP_RETENTION_DAYS=30

663
deploy.sh Executable file
View File

@@ -0,0 +1,663 @@
#!/bin/bash
# =============================================================================
# TimeClock v3 - Deployment Script für Ubuntu 22.04
# =============================================================================
# Dieses Script automatisiert das Deployment auf dem Produktionsserver
#
# Verwendung:
# ./deploy.sh [OPTION]
#
# Optionen:
# install - Erste Installation (inkl. System-Setup)
# update - Update einer bestehenden Installation
# rollback - Rollback zur vorherigen Version
# backup - Erstelle Backup der Datenbank
# status - Zeige Status der Services
# logs - Zeige Logs
# help - Zeige diese Hilfe
#
# =============================================================================
set -e # Bei Fehler abbrechen
# Farben für Output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Konfiguration
PROJECT_NAME="TimeClock"
PROJECT_DIR="/var/www/timeclock"
BACKEND_DIR="$PROJECT_DIR/backend"
FRONTEND_DIR="$PROJECT_DIR/frontend"
DOMAIN="stechuhr3.tsschulz.de"
BACKUP_DIR="/var/backups/timeclock"
LOG_DIR="/var/log/timeclock"
# Service-Name (PM2 oder systemd)
USE_PM2=true # true für PM2, false für systemd
SERVICE_NAME="timeclock"
# Webserver (nginx oder apache2)
WEBSERVER="apache2" # "nginx" oder "apache2"
# =============================================================================
# Hilfsfunktionen
# =============================================================================
print_info() {
echo -e "${BLUE} $1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_header() {
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE} $1${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
echo ""
}
check_root() {
if [ "$EUID" -eq 0 ]; then
print_error "Bitte führe dieses Script NICHT als root aus!"
print_info "Verwende stattdessen sudo für einzelne Befehle."
exit 1
fi
}
check_command() {
if ! command -v $1 &> /dev/null; then
print_error "$1 ist nicht installiert!"
return 1
fi
return 0
}
# =============================================================================
# Installation - Erste Einrichtung
# =============================================================================
install_dependencies() {
print_header "Installiere System-Abhängigkeiten"
print_info "Aktualisiere Paketquellen..."
sudo apt update
print_info "Installiere Node.js 20.x..."
if ! check_command node; then
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
else
print_success "Node.js ist bereits installiert ($(node -v))"
fi
if [ "$WEBSERVER" = "nginx" ]; then
print_info "Installiere Nginx..."
if ! check_command nginx; then
sudo apt install -y nginx
else
print_success "Nginx ist bereits installiert"
fi
else
print_info "Installiere Apache2..."
if ! check_command apache2; then
sudo apt install -y apache2
else
print_success "Apache2 ist bereits installiert"
fi
print_info "Aktiviere Apache2-Module..."
sudo a2enmod proxy proxy_http ssl rewrite headers deflate expires
fi
print_info "Installiere Certbot..."
if ! check_command certbot; then
if [ "$WEBSERVER" = "nginx" ]; then
sudo apt install -y certbot python3-certbot-nginx
else
sudo apt install -y certbot python3-certbot-apache
fi
else
print_success "Certbot ist bereits installiert"
fi
if [ "$USE_PM2" = true ]; then
print_info "Installiere PM2..."
if ! check_command pm2; then
sudo npm install -g pm2
else
print_success "PM2 ist bereits installiert"
fi
fi
print_success "Alle Abhängigkeiten installiert!"
}
setup_directories() {
print_header "Erstelle Verzeichnisse"
print_info "Erstelle Projekt-Verzeichnis..."
sudo mkdir -p $PROJECT_DIR
sudo chown -R $USER:$USER $PROJECT_DIR
print_info "Erstelle Log-Verzeichnis..."
sudo mkdir -p $LOG_DIR
sudo chown -R www-data:www-data $LOG_DIR
print_info "Erstelle Backup-Verzeichnis..."
sudo mkdir -p $BACKUP_DIR
sudo chown -R $USER:$USER $BACKUP_DIR
print_success "Verzeichnisse erstellt!"
}
copy_project_files() {
print_header "Kopiere Projekt-Dateien"
CURRENT_DIR=$(pwd)
print_info "Kopiere Backend..."
rsync -a --exclude 'node_modules' "$CURRENT_DIR/backend/" "$BACKEND_DIR/"
print_info "Kopiere Frontend..."
rsync -a --exclude 'node_modules' --exclude 'dist' "$CURRENT_DIR/frontend/" "$FRONTEND_DIR/"
print_success "Projekt-Dateien kopiert!"
}
setup_backend() {
print_header "Setup Backend"
cd $BACKEND_DIR
print_info "Installiere Backend-Dependencies..."
npm install --production
if [ ! -f .env ]; then
print_warning ".env Datei nicht gefunden!"
if [ -f env.production.template ]; then
print_info "Kopiere Template..."
cp env.production.template .env
print_warning "WICHTIG: Bitte bearbeite $BACKEND_DIR/.env und passe die Werte an!"
read -p "Drücke Enter wenn du fertig bist..."
else
print_error "Auch env.production.template nicht gefunden!"
exit 1
fi
else
print_success ".env bereits vorhanden"
fi
print_success "Backend Setup abgeschlossen!"
}
setup_frontend() {
print_header "Setup Frontend"
cd $FRONTEND_DIR
print_info "Installiere Frontend-Dependencies..."
npm install
print_info "Erstelle Produktions-Build..."
npm run build
if [ ! -d "dist" ]; then
print_error "Build fehlgeschlagen - dist/ Verzeichnis nicht gefunden!"
exit 1
fi
print_success "Frontend Build erstellt!"
}
setup_webserver() {
if [ "$WEBSERVER" = "nginx" ]; then
print_header "Setup Nginx"
WEBSERVER_CONF="/etc/nginx/sites-available/$DOMAIN"
if [ ! -f "$WEBSERVER_CONF" ]; then
print_info "Kopiere Nginx-Konfiguration..."
sudo cp "$PROJECT_DIR/nginx.conf" "$WEBSERVER_CONF"
print_info "Aktiviere Site..."
sudo ln -sf "$WEBSERVER_CONF" "/etc/nginx/sites-enabled/$DOMAIN"
print_info "Teste Nginx-Konfiguration..."
sudo nginx -t
print_info "Lade Nginx neu..."
sudo systemctl reload nginx
print_success "Nginx konfiguriert!"
else
print_warning "Nginx-Konfiguration existiert bereits"
read -p "Überschreiben? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
sudo cp "$PROJECT_DIR/nginx.conf" "$WEBSERVER_CONF"
sudo nginx -t && sudo systemctl reload nginx
print_success "Nginx-Konfiguration aktualisiert!"
fi
fi
else
print_header "Setup Apache2"
WEBSERVER_CONF="/etc/apache2/sites-available/$DOMAIN.conf"
if [ ! -f "$WEBSERVER_CONF" ]; then
print_info "Kopiere Apache2-Konfiguration..."
sudo cp "$PROJECT_DIR/apache2.conf" "$WEBSERVER_CONF"
print_info "Aktiviere Site..."
sudo a2ensite "$DOMAIN"
# Deaktiviere Default-Site (optional)
if [ -f "/etc/apache2/sites-enabled/000-default.conf" ]; then
print_info "Deaktiviere Default-Site..."
sudo a2dissite 000-default
fi
print_info "Teste Apache2-Konfiguration..."
sudo apache2ctl configtest
print_info "Lade Apache2 neu..."
sudo systemctl reload apache2
print_success "Apache2 konfiguriert!"
else
print_warning "Apache2-Konfiguration existiert bereits"
read -p "Überschreiben? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
sudo cp "$PROJECT_DIR/apache2.conf" "$WEBSERVER_CONF"
sudo apache2ctl configtest && sudo systemctl reload apache2
print_success "Apache2-Konfiguration aktualisiert!"
fi
fi
fi
}
setup_ssl() {
print_header "Setup SSL-Zertifikat"
print_info "Prüfe ob Zertifikat bereits existiert..."
if [ -d "/etc/letsencrypt/live/$DOMAIN" ]; then
print_success "SSL-Zertifikat bereits vorhanden"
return
fi
print_warning "Stelle sicher, dass $DOMAIN auf diesen Server zeigt!"
read -p "Fortfahren? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_info "SSL-Setup übersprungen"
return
fi
print_info "Erstelle SSL-Zertifikat mit Let's Encrypt..."
if [ "$WEBSERVER" = "nginx" ]; then
sudo certbot --nginx -d $DOMAIN
else
sudo certbot --apache -d $DOMAIN
fi
print_success "SSL-Zertifikat erstellt!"
}
setup_backend_service() {
print_header "Setup Backend-Service"
if [ "$USE_PM2" = true ]; then
print_info "Starte Backend mit PM2..."
cd $BACKEND_DIR
pm2 delete $SERVICE_NAME 2>/dev/null || true
pm2 start src/index.js --name $SERVICE_NAME --env production
pm2 save
print_info "Richte PM2 Auto-Start ein..."
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u $USER --hp $HOME
print_success "Backend läuft mit PM2!"
else
print_info "Installiere systemd-Service..."
sudo cp "$PROJECT_DIR/timeclock.service" /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable $SERVICE_NAME
sudo systemctl start $SERVICE_NAME
print_success "Backend läuft mit systemd!"
fi
}
setup_firewall() {
print_header "Setup Firewall"
if ! check_command ufw; then
print_warning "UFW nicht installiert, überspringe Firewall-Setup"
return
fi
print_info "Konfiguriere UFW..."
sudo ufw allow ssh
if [ "$WEBSERVER" = "nginx" ]; then
sudo ufw allow 'Nginx Full'
else
sudo ufw allow 'Apache Full'
fi
if ! sudo ufw status | grep -q "Status: active"; then
print_warning "UFW ist nicht aktiv"
read -p "UFW aktivieren? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
sudo ufw --force enable
print_success "UFW aktiviert!"
fi
else
print_success "UFW bereits konfiguriert"
fi
}
# =============================================================================
# Update - Aktualisierung einer bestehenden Installation
# =============================================================================
do_update() {
print_header "Update $PROJECT_NAME"
# Backup erstellen
do_backup
# Backend aktualisieren
print_info "Aktualisiere Backend..."
cd $BACKEND_DIR
npm install --production
# Frontend neu bauen
print_info "Aktualisiere Frontend..."
cd $FRONTEND_DIR
npm install
npm run build
# Service neu starten
restart_backend
print_success "Update abgeschlossen!"
}
# =============================================================================
# Backup & Rollback
# =============================================================================
do_backup() {
print_header "Erstelle Backup"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
print_info "Erstelle Datenbank-Backup..."
# Lese DB-Credentials aus .env
DB_NAME=$(grep DB_NAME $BACKEND_DIR/.env | cut -d '=' -f2)
DB_USER=$(grep DB_USER $BACKEND_DIR/.env | cut -d '=' -f2)
DB_PASSWORD=$(grep DB_PASSWORD $BACKEND_DIR/.env | cut -d '=' -f2)
mysqldump -u $DB_USER -p"$DB_PASSWORD" $DB_NAME | gzip > "$BACKUP_DIR/${PROJECT_NAME,,}_$TIMESTAMP.sql.gz"
print_info "Erstelle Code-Backup..."
tar -czf "$BACKUP_DIR/${PROJECT_NAME,,}_code_$TIMESTAMP.tar.gz" -C $(dirname $PROJECT_DIR) $(basename $PROJECT_DIR)
# Alte Backups löschen (älter als 30 Tage)
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete
print_success "Backup erstellt: $TIMESTAMP"
}
do_rollback() {
print_header "Rollback zu vorheriger Version"
print_warning "Rollback wird die letzte Code-Version wiederherstellen"
read -p "Fortfahren? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_info "Rollback abgebrochen"
return
fi
# Finde letztes Code-Backup
LAST_BACKUP=$(ls -t $BACKUP_DIR/${PROJECT_NAME,,}_code_*.tar.gz 2>/dev/null | head -n1)
if [ -z "$LAST_BACKUP" ]; then
print_error "Kein Backup gefunden!"
exit 1
fi
print_info "Stelle wieder her: $LAST_BACKUP"
# Aktuellen Code sichern
mv $PROJECT_DIR "${PROJECT_DIR}_rollback_backup_$(date +%Y%m%d_%H%M%S)"
# Backup wiederherstellen
tar -xzf "$LAST_BACKUP" -C $(dirname $PROJECT_DIR)
# Services neu starten
restart_backend
print_success "Rollback abgeschlossen!"
}
# =============================================================================
# Service Management
# =============================================================================
restart_backend() {
print_info "Starte Backend neu..."
if [ "$USE_PM2" = true ]; then
pm2 restart $SERVICE_NAME
else
sudo systemctl restart $SERVICE_NAME
fi
print_success "Backend neu gestartet!"
}
show_status() {
print_header "Service Status"
if [ "$USE_PM2" = true ]; then
pm2 status $SERVICE_NAME
echo ""
pm2 info $SERVICE_NAME
else
sudo systemctl status $SERVICE_NAME
fi
echo ""
if [ "$WEBSERVER" = "nginx" ]; then
print_info "Nginx Status:"
sudo systemctl status nginx --no-pager | head -n 10
else
print_info "Apache2 Status:"
sudo systemctl status apache2 --no-pager | head -n 10
fi
echo ""
print_info "SSL-Zertifikat:"
sudo certbot certificates | grep -A 5 $DOMAIN || print_warning "Kein Zertifikat gefunden"
}
show_logs() {
print_header "Logs"
echo "1) Backend-Logs"
echo "2) Webserver Access-Logs"
echo "3) Webserver Error-Logs"
echo "4) Alle Logs (follow)"
read -p "Auswahl (1-4): " choice
if [ "$WEBSERVER" = "nginx" ]; then
ACCESS_LOG="/var/log/nginx/stechuhr3.access.log"
ERROR_LOG="/var/log/nginx/stechuhr3.error.log"
else
ACCESS_LOG="/var/log/apache2/stechuhr3-access.log"
ERROR_LOG="/var/log/apache2/stechuhr3-error.log"
fi
case $choice in
1)
if [ "$USE_PM2" = true ]; then
pm2 logs $SERVICE_NAME
else
sudo journalctl -u $SERVICE_NAME -f
fi
;;
2)
sudo tail -f "$ACCESS_LOG"
;;
3)
sudo tail -f "$ERROR_LOG"
;;
4)
if [ "$USE_PM2" = true ]; then
pm2 logs $SERVICE_NAME &
PM2_PID=$!
else
sudo journalctl -u $SERVICE_NAME -f &
JOURNAL_PID=$!
fi
if [ "$WEBSERVER" = "nginx" ]; then
sudo tail -f /var/log/nginx/stechuhr3.*.log
else
sudo tail -f /var/log/apache2/stechuhr3-*.log
fi
;;
*)
print_error "Ungültige Auswahl"
;;
esac
}
# =============================================================================
# Vollständige Installation
# =============================================================================
do_install() {
print_header "$PROJECT_NAME - Vollständige Installation"
check_root
print_warning "Diese Installation wird folgendes tun:"
echo " - System-Abhängigkeiten installieren"
echo " - Projekt nach $PROJECT_DIR kopieren"
echo " - Backend und Frontend einrichten"
echo " - Nginx konfigurieren"
echo " - SSL-Zertifikat erstellen"
echo " - Backend-Service starten"
echo " - Firewall konfigurieren"
echo ""
read -p "Fortfahren? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_info "Installation abgebrochen"
exit 0
fi
install_dependencies
setup_directories
copy_project_files
setup_backend
setup_frontend
setup_webserver
setup_ssl
setup_backend_service
setup_firewall
print_header "Installation abgeschlossen! 🎉"
print_success "Deine TimeClock-App läuft jetzt auf https://$DOMAIN"
echo ""
print_info "Nützliche Befehle:"
echo " ./deploy.sh status - Zeige Service-Status"
echo " ./deploy.sh logs - Zeige Logs"
echo " ./deploy.sh update - Update durchführen"
echo " ./deploy.sh backup - Backup erstellen"
echo ""
}
# =============================================================================
# Hauptprogramm
# =============================================================================
show_help() {
cat << EOF
$PROJECT_NAME v3 - Deployment Script
Verwendung: $0 [OPTION]
Optionen:
install Erste Installation (inkl. System-Setup)
update Update einer bestehenden Installation
rollback Rollback zur vorherigen Version
backup Erstelle Backup der Datenbank
status Zeige Status der Services
logs Zeige Logs
help Zeige diese Hilfe
Beispiele:
$0 install # Erste Installation
$0 update # Update durchführen
$0 backup # Backup erstellen
$0 status # Status anzeigen
EOF
}
# Hauptlogik
case "${1:-help}" in
install)
do_install
;;
update)
do_update
;;
rollback)
do_rollback
;;
backup)
do_backup
;;
status)
show_status
;;
logs)
show_logs
;;
help|--help|-h)
show_help
;;
*)
print_error "Unbekannte Option: $1"
echo ""
show_help
exit 1
;;
esac
exit 0

98
ecosystem.config.js Normal file
View File

@@ -0,0 +1,98 @@
// PM2 Ecosystem File für TimeClock v3
// Verwendung:
// pm2 start ecosystem.config.js
// pm2 start ecosystem.config.js --env production
// pm2 stop timeclock-backend
// pm2 restart timeclock-backend
// pm2 logs timeclock-backend
// pm2 monit
module.exports = {
apps: [
{
// Backend-API
name: 'timeclock-backend',
cwd: '/var/www/timeclock/backend',
script: 'src/index.js',
// Instanzen (1 = single, 0 oder 'max' = alle CPU-Kerne)
instances: 1,
exec_mode: 'fork', // 'fork' oder 'cluster'
// Environment
env: {
NODE_ENV: 'development',
PORT: 3010
},
env_production: {
NODE_ENV: 'production',
PORT: 3010
},
// Auto-Restart
watch: false, // In Produktion false
watch_delay: 1000,
ignore_watch: [
'node_modules',
'logs',
'*.log'
],
// Restart-Verhalten
autorestart: true,
max_restarts: 10,
min_uptime: '10s',
restart_delay: 4000,
// Limits
max_memory_restart: '500M',
// Logs
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
error_file: '/var/log/timeclock/pm2-error.log',
out_file: '/var/log/timeclock/pm2-out.log',
log_file: '/var/log/timeclock/pm2-combined.log',
// Merge Logs (alle Instanzen in eine Datei)
merge_logs: true,
// Source Maps Support
source_map_support: false,
// Graceful Shutdown
kill_timeout: 5000,
wait_ready: false,
listen_timeout: 10000,
// Process Management
pid_file: '/var/run/timeclock-backend.pid',
// Cron Restart (optional)
// cron_restart: '0 2 * * *', // Täglich um 2 Uhr neustarten
// Monitoring
instance_var: 'INSTANCE_ID',
// Post-Deployment-Hooks (optional)
post_update: [
'npm install --production',
'echo "Backend updated"'
]
}
],
// Deployment-Konfiguration (optional, für pm2 deploy)
deploy: {
production: {
user: 'torsten',
host: 'stechuhr3.tsschulz.de',
ref: 'origin/main',
repo: 'git@github.com:IHR-USERNAME/TimeClock.git',
path: '/var/www/timeclock',
'pre-deploy-local': '',
'post-deploy': 'cd backend && npm install --production && pm2 reload ecosystem.config.js --env production',
'pre-setup': ''
}
}
};

View File

@@ -17,6 +17,31 @@ export default defineConfig({
changeOrigin: true changeOrigin: true
} }
} }
},
build: {
// Produktions-Build-Optimierungen
target: 'es2015',
outDir: 'dist',
assetsDir: 'assets',
sourcemap: false,
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // Console-Logs in Produktion entfernen
drop_debugger: true
} }
},
rollupOptions: {
output: {
manualChunks: {
// Code-Splitting für bessere Performance
'vue-vendor': ['vue', 'vue-router', 'pinia']
}
}
},
chunkSizeWarningLimit: 1000
},
// Base-URL für Produktion
base: '/'
}) })

262
nginx.conf Normal file
View File

@@ -0,0 +1,262 @@
# Nginx-Konfiguration für TimeClock v3
# Datei speichern unter: /etc/nginx/sites-available/stechuhr3.tsschulz.de
#
# Installation:
# sudo cp nginx.conf /etc/nginx/sites-available/stechuhr3.tsschulz.de
# sudo ln -s /etc/nginx/sites-available/stechuhr3.tsschulz.de /etc/nginx/sites-enabled/
# sudo nginx -t
# sudo systemctl reload nginx
# HTTP Server - Redirect zu HTTPS
server {
listen 80;
listen [::]:80;
server_name stechuhr3.tsschulz.de;
# Let's Encrypt ACME Challenge
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Alle anderen Requests zu HTTPS umleiten
location / {
return 301 https://$server_name$request_uri;
}
}
# HTTPS Server - Hauptkonfiguration
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name stechuhr3.tsschulz.de;
# =================================================================
# SSL-Konfiguration (wird von Certbot automatisch verwaltet)
# =================================================================
ssl_certificate /etc/letsencrypt/live/stechuhr3.tsschulz.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stechuhr3.tsschulz.de/privkey.pem;
# SSL-Protokolle und Ciphers
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
# SSL-Session-Cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/stechuhr3.tsschulz.de/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# =================================================================
# Frontend (Vue.js SPA)
# =================================================================
root /var/www/timeclock/frontend/dist;
index index.html;
# =================================================================
# Logging
# =================================================================
access_log /var/log/nginx/stechuhr3.access.log;
error_log /var/log/nginx/stechuhr3.error.log warn;
# =================================================================
# Gzip Compression
# =================================================================
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/xml+rss
application/rss+xml
application/atom+xml
image/svg+xml
font/ttf
font/woff
font/woff2;
# =================================================================
# Security Headers
# =================================================================
# X-Frame-Options: Schutz vor Clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
# X-Content-Type-Options: Verhindert MIME-Type Sniffing
add_header X-Content-Type-Options "nosniff" always;
# X-XSS-Protection: XSS-Schutz für ältere Browser
add_header X-XSS-Protection "1; mode=block" always;
# Referrer-Policy: Kontrolliert Referrer-Informationen
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Permissions-Policy: Kontrolliert Browser-Features
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Content-Security-Policy (angepasst für Vue.js)
# Kann bei Bedarf verschärft werden
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://stechuhr3.tsschulz.de;" always;
# Strict-Transport-Security (HSTS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# =================================================================
# API Reverse Proxy zum Backend
# =================================================================
location /api {
# Proxy Pass zum Backend auf localhost:3010
proxy_pass http://localhost:3010;
# HTTP Version
proxy_http_version 1.1;
# WebSocket Support (falls später benötigt)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
# Standard Proxy Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
# Cache Bypass
proxy_cache_bypass $http_upgrade;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Buffer-Einstellungen
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
# Fehlerbehandlung
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
proxy_next_upstream_tries 2;
}
# =================================================================
# Statische Assets mit langem Cache
# =================================================================
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp|avif)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Font-Dateien mit langem Cache
location ~* \.(woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Access-Control-Allow-Origin "*";
access_log off;
}
# =================================================================
# SPA Fallback - alle anderen Requests zu index.html
# =================================================================
location / {
try_files $uri $uri/ /index.html;
# Cache-Control für HTML-Dateien (kein Cache)
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
# =================================================================
# Spezielle Dateien
# =================================================================
# robots.txt und sitemap.xml
location ~* ^/(robots\.txt|sitemap\.xml)$ {
root /var/www/timeclock/frontend/dist;
access_log off;
}
# favicon.ico
location = /favicon.ico {
root /var/www/timeclock/frontend/dist;
access_log off;
log_not_found off;
}
# =================================================================
# Sicherheit: Verstecke sensible Dateien
# =================================================================
# Blockiere Zugriff auf .git, .env, etc.
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Blockiere Zugriff auf Backup-Dateien
location ~ ~$ {
deny all;
access_log off;
log_not_found off;
}
# =================================================================
# Limits und Rate Limiting (optional)
# =================================================================
# Client Body Size Limit (z.B. für File-Uploads)
client_max_body_size 10M;
# Client Body Timeout
client_body_timeout 30s;
# Client Header Timeout
client_header_timeout 30s;
# Send Timeout
send_timeout 30s;
# Rate Limiting für API (optional)
# Kommentare entfernen und in nginx.conf http-Block hinzufügen:
# limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# location /api {
# limit_req zone=api_limit burst=20 nodelay;
# ... rest der Konfiguration
# }
}
# =================================================================
# OPTIONAL: Rate Limiting Zone (in /etc/nginx/nginx.conf http-Block)
# =================================================================
# Uncomment und in die Haupt-nginx.conf einfügen, falls gewünscht:
#
# http {
# ...
#
# # Rate Limiting Zones
# limit_req_zone $binary_remote_addr zone=general_limit:10m rate=30r/s;
# limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;
#
# # Connection Limiting
# limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
#
# ...
# }

345
scripts/README.md Normal file
View File

@@ -0,0 +1,345 @@
# TimeClock v3 - Server-Management Scripts
Dieses Verzeichnis enthält hilfreiche Wartungs- und Management-Scripts für den Produktions-Server.
## Übersicht
| Script | Beschreibung | Verwendung |
|--------|--------------|------------|
| `backup-timeclock.sh` | Erstellt automatische Datenbank-Backups | Täglich per Cronjob |
| `health-check.sh` | Überwacht API-Verfügbarkeit | Alle 5 Min per Cronjob |
| `restore-backup.sh` | Stellt Datenbank-Backup wieder her | Manuell bei Bedarf |
---
## 1. backup-timeclock.sh
Erstellt automatische komprimierte Backups der MySQL-Datenbank.
### Installation
```bash
# Auf den Server kopieren
sudo cp scripts/backup-timeclock.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/backup-timeclock.sh
# Cronjob einrichten (täglich um 2 Uhr)
sudo crontab -e
```
Füge folgende Zeile hinzu:
```cron
0 2 * * * /usr/local/bin/backup-timeclock.sh >> /var/log/timeclock/backup.log 2>&1
```
### Manuell ausführen
```bash
/usr/local/bin/backup-timeclock.sh
```
### Features
- ✅ Automatische Datenbank-Backups mit Kompression
- ✅ Konfigurierbare Retention (Standard: 30 Tage)
- ✅ Optional: Code-Backups
- ✅ Optional: Remote-Backup per rsync
- ✅ Optional: E-Mail-Benachrichtigungen
- ✅ Backup-Statistiken
### Konfiguration
Bearbeite die Variablen am Anfang des Scripts:
```bash
BACKUP_DIR="/var/backups/timeclock"
RETENTION_DAYS=30
BACKUP_CODE=false # true für Code-Backups
REMOTE_BACKUP=false # true für Remote-Backup
SEND_EMAIL=false # true für E-Mail-Benachrichtigung
```
### Backup-Dateien
Backups werden gespeichert unter:
- Datenbank: `/var/backups/timeclock/timeclock_db_YYYYMMDD_HHMMSS.sql.gz`
- Code: `/var/backups/timeclock/timeclock_code_YYYYMMDD_HHMMSS.tar.gz`
---
## 2. health-check.sh
Überwacht die Verfügbarkeit des Backend-API und startet den Service bei Bedarf automatisch neu.
### Installation
```bash
# Auf den Server kopieren
sudo cp scripts/health-check.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/health-check.sh
# Cronjob einrichten (alle 5 Minuten)
crontab -e
```
Füge folgende Zeile hinzu:
```cron
*/5 * * * * /usr/local/bin/health-check.sh >> /var/log/timeclock/health-check.log 2>&1
```
### Manuell ausführen
```bash
/usr/local/bin/health-check.sh
```
### Features
- ✅ Prüft `/api/health` Endpunkt
- ✅ Automatischer Neustart bei Ausfall
- ✅ Mehrere Retry-Versuche (Standard: 3)
- ✅ Unterstützt PM2 und systemd
- ✅ Optional: E-Mail/Webhook-Benachrichtigungen
- ✅ Ressourcen-Monitoring (CPU/RAM/Disk)
### Konfiguration
```bash
API_URL="http://localhost:3010/api/health"
USE_PM2=true # false für systemd
MAX_RETRIES=3
RETRY_DELAY=5
SEND_EMAIL=false # true für E-Mail-Benachrichtigung
```
### Benachrichtigungen
Das Script kann bei Ausfällen Benachrichtigungen senden via:
- E-Mail (benötigt `mail` command)
- Webhook (Discord, Slack, etc.)
Konfiguriere die entsprechenden Variablen im Script:
```bash
SEND_EMAIL=true
EMAIL_TO="admin@tsschulz.de"
WEBHOOK_URL="https://discord.com/api/webhooks/..."
```
---
## 3. restore-backup.sh
Interaktives Script zum Wiederherstellen von Datenbank-Backups.
### Verwendung
```bash
# Interaktive Auswahl
sudo /var/www/timeclock/scripts/restore-backup.sh
# Spezifisches Backup wiederherstellen
sudo /var/www/timeclock/scripts/restore-backup.sh timeclock_db_20251018_020000.sql.gz
# Mit vollständigem Pfad
sudo /var/www/timeclock/scripts/restore-backup.sh /var/backups/timeclock/timeclock_db_20251018_020000.sql.gz
```
### Features
- ✅ Interaktive Backup-Auswahl
- ✅ Sicherheitskopie vor Restore
- ✅ Konfirmations-Prompts
- ✅ Automatisches Service-Neustart (optional)
- ✅ Datenbank-Statistiken nach Restore
### Ablauf
1. Zeigt Liste verfügbarer Backups
2. Erstellt Sicherheitskopie der aktuellen DB
3. Bestätigung erforderlich
4. Restore durchführen
5. Optional: Service neu starten
### ⚠️ WARNUNG
Das Restore überschreibt die aktuelle Datenbank! Eine Sicherheitskopie wird automatisch erstellt, aber sei vorsichtig!
---
## Logs
Alle Scripts loggen nach:
- `/var/log/timeclock/backup.log` - Backup-Logs
- `/var/log/timeclock/health-check.log` - Health-Check-Logs
### Logs ansehen
```bash
# Backup-Logs
tail -f /var/log/timeclock/backup.log
# Health-Check-Logs
tail -f /var/log/timeclock/health-check.log
# Alle Logs
tail -f /var/log/timeclock/*.log
```
---
## Cronjob-Übersicht
Empfohlene Cronjob-Konfiguration:
```cron
# TimeClock Maintenance Jobs
# Backup: Täglich um 2 Uhr morgens
0 2 * * * /usr/local/bin/backup-timeclock.sh >> /var/log/timeclock/backup.log 2>&1
# Health-Check: Alle 5 Minuten
*/5 * * * * /usr/local/bin/health-check.sh >> /var/log/timeclock/health-check.log 2>&1
# Optional: Log-Rotation - Alte Logs löschen (älter als 90 Tage)
0 3 * * 0 find /var/log/timeclock -name "*.log" -mtime +90 -delete
```
---
## Weitere nützliche Commands
### Backup-Verwaltung
```bash
# Liste aller Backups
ls -lh /var/backups/timeclock/
# Anzahl der Backups
ls -1 /var/backups/timeclock/timeclock_db_*.sql.gz | wc -l
# Gesamtgröße der Backups
du -sh /var/backups/timeclock/
# Ältestes Backup
ls -t /var/backups/timeclock/timeclock_db_*.sql.gz | tail -1
# Neuestes Backup
ls -t /var/backups/timeclock/timeclock_db_*.sql.gz | head -1
# Manuell alte Backups löschen (älter als 30 Tage)
find /var/backups/timeclock -name "timeclock_db_*.sql.gz" -mtime +30 -delete
```
### Service-Management
```bash
# Mit PM2
pm2 status
pm2 logs timeclock-backend
pm2 restart timeclock-backend
pm2 monit
# Mit systemd
sudo systemctl status timeclock
sudo journalctl -u timeclock -f
sudo systemctl restart timeclock
```
### API-Tests
```bash
# Health-Check
curl http://localhost:3010/api/health
# Mit Details
curl -s http://localhost:3010/api/health | jq
# Response-Zeit messen
time curl -s http://localhost:3010/api/health > /dev/null
```
---
## Troubleshooting
### Script läuft nicht
```bash
# Berechtigungen prüfen
ls -l /usr/local/bin/backup-timeclock.sh
# Ausführbar machen
sudo chmod +x /usr/local/bin/backup-timeclock.sh
# Script-Fehler debuggen
bash -x /usr/local/bin/backup-timeclock.sh
```
### Cronjob läuft nicht
```bash
# Cronjobs anzeigen
crontab -l
# Cron-Logs prüfen
sudo grep CRON /var/log/syslog
# Cronjob manuell testen
/usr/local/bin/backup-timeclock.sh
```
### Backup schlägt fehl
```bash
# DB-Verbindung testen
mysql -u timeclock -p -e "SHOW DATABASES;"
# .env Datei prüfen
cat /var/www/timeclock/backend/.env
# Backup-Verzeichnis prüfen
ls -ld /var/backups/timeclock
# Schreibrechte prüfen
touch /var/backups/timeclock/test.txt
```
---
## Best Practices
1. **Regelmäßige Backups testen**
- Teste mindestens monatlich ein Restore
- Verifiziere Backup-Integrität
2. **Monitoring einrichten**
- Health-Checks aktivieren
- E-Mail-Benachrichtigungen konfigurieren
3. **Logs überwachen**
- Prüfe regelmäßig die Log-Dateien
- Implementiere Log-Rotation
4. **Off-Site Backups**
- Aktiviere Remote-Backup in `backup-timeclock.sh`
- Speichere Backups an einem anderen Ort
5. **Sicherheit**
- Schütze Backup-Dateien (enthalten sensible Daten!)
- Setze restriktive Berechtigungen
---
## Support
Bei Problemen:
1. Prüfe die Log-Dateien
2. Teste Scripts manuell
3. Siehe Haupt-Dokumentation: `../DEPLOYMENT.md`
---
**Automatisierte Wartung für sorgenfreien Betrieb! 🚀**

223
scripts/backup-timeclock.sh Executable file
View File

@@ -0,0 +1,223 @@
#!/bin/bash
# =============================================================================
# TimeClock v3 - Automatisches Backup-Script
# =============================================================================
# Erstellt tägliche Backups der Datenbank und optional des Codes
#
# Installation:
# sudo cp backup-timeclock.sh /usr/local/bin/
# sudo chmod +x /usr/local/bin/backup-timeclock.sh
#
# Cronjob einrichten (täglich um 2 Uhr):
# sudo crontab -e
# 0 2 * * * /usr/local/bin/backup-timeclock.sh >> /var/log/timeclock/backup.log 2>&1
#
# Manuell ausführen:
# /usr/local/bin/backup-timeclock.sh
# =============================================================================
set -e
# Konfiguration
BACKUP_DIR="/var/backups/timeclock"
PROJECT_DIR="/var/www/timeclock"
ENV_FILE="$PROJECT_DIR/backend/.env"
RETENTION_DAYS=30 # Backups älter als X Tage werden gelöscht
# Datum
DATE=$(date +%Y%m%d_%H%M%S)
DATE_READABLE=$(date '+%Y-%m-%d %H:%M:%S')
# Logging
log() {
echo "[${DATE_READABLE}] $1"
}
log_error() {
echo "[${DATE_READABLE}] ERROR: $1" >&2
}
# =============================================================================
# Voraussetzungen prüfen
# =============================================================================
if [ ! -f "$ENV_FILE" ]; then
log_error ".env Datei nicht gefunden: $ENV_FILE"
exit 1
fi
# Backup-Verzeichnis erstellen
mkdir -p "$BACKUP_DIR"
# =============================================================================
# Datenbank-Credentials aus .env lesen
# =============================================================================
log "Lese Datenbank-Konfiguration..."
# Funktion zum Auslesen von .env Werten
get_env_value() {
grep "^$1=" "$ENV_FILE" | cut -d '=' -f2- | tr -d '"' | tr -d "'"
}
DB_HOST=$(get_env_value "DB_HOST")
DB_PORT=$(get_env_value "DB_PORT")
DB_NAME=$(get_env_value "DB_NAME")
DB_USER=$(get_env_value "DB_USER")
DB_PASSWORD=$(get_env_value "DB_PASSWORD")
# Defaults setzen
DB_HOST=${DB_HOST:-localhost}
DB_PORT=${DB_PORT:-3306}
if [ -z "$DB_NAME" ] || [ -z "$DB_USER" ] || [ -z "$DB_PASSWORD" ]; then
log_error "Datenbank-Credentials unvollständig in .env"
exit 1
fi
log "Datenbank: $DB_USER@$DB_HOST:$DB_PORT/$DB_NAME"
# =============================================================================
# Datenbank-Backup erstellen
# =============================================================================
log "Erstelle Datenbank-Backup..."
BACKUP_FILE="$BACKUP_DIR/timeclock_db_$DATE.sql.gz"
# mysqldump mit Kompression
if mysqldump \
--host="$DB_HOST" \
--port="$DB_PORT" \
--user="$DB_USER" \
--password="$DB_PASSWORD" \
--single-transaction \
--routines \
--triggers \
--events \
--add-drop-database \
--databases "$DB_NAME" \
| gzip > "$BACKUP_FILE"; then
# Dateigröße ermitteln
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
log "✅ Datenbank-Backup erfolgreich erstellt: $BACKUP_FILE ($SIZE)"
else
log_error "Datenbank-Backup fehlgeschlagen!"
exit 1
fi
# =============================================================================
# Code-Backup erstellen (optional)
# =============================================================================
BACKUP_CODE=false # Auf true setzen für Code-Backups
if [ "$BACKUP_CODE" = true ]; then
log "Erstelle Code-Backup..."
CODE_BACKUP_FILE="$BACKUP_DIR/timeclock_code_$DATE.tar.gz"
if tar -czf "$CODE_BACKUP_FILE" \
-C "$(dirname $PROJECT_DIR)" \
--exclude="node_modules" \
--exclude="dist" \
--exclude=".git" \
--exclude="*.log" \
"$(basename $PROJECT_DIR)"; then
SIZE=$(du -h "$CODE_BACKUP_FILE" | cut -f1)
log "✅ Code-Backup erfolgreich erstellt: $CODE_BACKUP_FILE ($SIZE)"
else
log_error "Code-Backup fehlgeschlagen!"
fi
fi
# =============================================================================
# Alte Backups löschen
# =============================================================================
log "Lösche alte Backups (älter als $RETENTION_DAYS Tage)..."
# Datenbank-Backups
DELETED_DB=$(find "$BACKUP_DIR" -name "timeclock_db_*.sql.gz" -mtime +$RETENTION_DAYS -type f -delete -print | wc -l)
if [ "$DELETED_DB" -gt 0 ]; then
log "🗑️ $DELETED_DB alte Datenbank-Backups gelöscht"
fi
# Code-Backups
if [ "$BACKUP_CODE" = true ]; then
DELETED_CODE=$(find "$BACKUP_DIR" -name "timeclock_code_*.tar.gz" -mtime +$RETENTION_DAYS -type f -delete -print | wc -l)
if [ "$DELETED_CODE" -gt 0 ]; then
log "🗑️ $DELETED_CODE alte Code-Backups gelöscht"
fi
fi
# =============================================================================
# Backup-Statistik
# =============================================================================
log "Backup-Statistik:"
log " 📊 Gesamt-Backups: $(ls -1 $BACKUP_DIR/timeclock_db_*.sql.gz 2>/dev/null | wc -l)"
log " 💾 Gesamtgröße: $(du -sh $BACKUP_DIR | cut -f1)"
log " 📅 Ältestes Backup: $(ls -t $BACKUP_DIR/timeclock_db_*.sql.gz 2>/dev/null | tail -1 | xargs basename)"
log " 📅 Neuestes Backup: $(ls -t $BACKUP_DIR/timeclock_db_*.sql.gz 2>/dev/null | head -1 | xargs basename)"
# =============================================================================
# Optional: Backup an Remote-Server senden
# =============================================================================
REMOTE_BACKUP=false # Auf true setzen für Remote-Backup
REMOTE_USER="backup"
REMOTE_HOST="backup.example.com"
REMOTE_PATH="/backups/timeclock"
if [ "$REMOTE_BACKUP" = true ]; then
log "Sende Backup an Remote-Server..."
if rsync -az --progress "$BACKUP_FILE" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/"; then
log "✅ Remote-Backup erfolgreich"
else
log_error "Remote-Backup fehlgeschlagen!"
fi
fi
# =============================================================================
# Optional: Backup-Benachrichtigung per E-Mail
# =============================================================================
SEND_EMAIL=false # Auf true setzen für E-Mail-Benachrichtigung
EMAIL_TO="admin@tsschulz.de"
if [ "$SEND_EMAIL" = true ] && command -v mail &> /dev/null; then
log "Sende E-Mail-Benachrichtigung..."
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
echo "TimeClock Backup erfolgreich erstellt
Datum: $DATE_READABLE
Datei: $BACKUP_FILE
Größe: $SIZE
Datenbank: $DB_NAME
Backup-Verzeichnis: $BACKUP_DIR
Anzahl Backups: $(ls -1 $BACKUP_DIR/timeclock_db_*.sql.gz 2>/dev/null | wc -l)
Gesamtgröße: $(du -sh $BACKUP_DIR | cut -f1)
--
TimeClock v3 Backup System
" | mail -s "✅ TimeClock Backup erfolgreich ($DATE)" "$EMAIL_TO"
log "📧 E-Mail gesendet an $EMAIL_TO"
fi
# =============================================================================
# Fertig
# =============================================================================
log "✅ Backup-Prozess abgeschlossen!"
exit 0

210
scripts/health-check.sh Executable file
View File

@@ -0,0 +1,210 @@
#!/bin/bash
# =============================================================================
# TimeClock v3 - Health-Check Script
# =============================================================================
# Überwacht die Verfügbarkeit des Backend-API und startet bei Bedarf neu
#
# Installation:
# sudo cp health-check.sh /usr/local/bin/
# sudo chmod +x /usr/local/bin/health-check.sh
#
# Cronjob einrichten (alle 5 Minuten):
# crontab -e
# */5 * * * * /usr/local/bin/health-check.sh >> /var/log/timeclock/health-check.log 2>&1
#
# Manuell ausführen:
# /usr/local/bin/health-check.sh
# =============================================================================
set -e
# Konfiguration
API_URL="http://localhost:3010/api/health"
SERVICE_NAME="timeclock-backend"
USE_PM2=true # true für PM2, false für systemd
LOG_FILE="/var/log/timeclock/health-check.log"
MAX_RETRIES=3
RETRY_DELAY=5 # Sekunden zwischen Versuchen
# Datum
DATE=$(date '+%Y-%m-%d %H:%M:%S')
# Logging
log() {
echo "[$DATE] $1" | tee -a "$LOG_FILE"
}
log_error() {
echo "[$DATE] ERROR: $1" | tee -a "$LOG_FILE" >&2
}
# =============================================================================
# Health-Check durchführen
# =============================================================================
check_health() {
# Versuche Health-Endpunkt zu erreichen
if curl -sf "$API_URL" > /dev/null 2>&1; then
return 0 # Erfolg
else
return 1 # Fehler
fi
}
# =============================================================================
# Service neu starten
# =============================================================================
restart_service() {
log "⚠️ Versuche Service neu zu starten..."
if [ "$USE_PM2" = true ]; then
# PM2
if command -v pm2 &> /dev/null; then
if pm2 restart "$SERVICE_NAME" > /dev/null 2>&1; then
log "✅ Service mit PM2 neu gestartet"
return 0
else
log_error "PM2-Neustart fehlgeschlagen!"
return 1
fi
else
log_error "PM2 nicht gefunden!"
return 1
fi
else
# systemd
if sudo systemctl restart "$SERVICE_NAME"; then
log "✅ Service mit systemd neu gestartet"
return 0
else
log_error "systemd-Neustart fehlgeschlagen!"
return 1
fi
fi
}
# =============================================================================
# Benachrichtigung senden
# =============================================================================
send_notification() {
local status=$1
local message=$2
# Optional: E-Mail-Benachrichtigung
SEND_EMAIL=false
EMAIL_TO="admin@tsschulz.de"
if [ "$SEND_EMAIL" = true ] && command -v mail &> /dev/null; then
echo "$message
Zeitpunkt: $DATE
API-URL: $API_URL
Service: $SERVICE_NAME
--
TimeClock v3 Health-Check System
" | mail -s "$status TimeClock Backend" "$EMAIL_TO"
fi
# Optional: Webhook-Benachrichtigung (z.B. Discord, Slack)
WEBHOOK_URL=""
if [ -n "$WEBHOOK_URL" ]; then
curl -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"content\": \"$status $message\"}" \
> /dev/null 2>&1
fi
}
# =============================================================================
# Hauptlogik
# =============================================================================
# Erstelle Log-Verzeichnis falls nicht vorhanden
mkdir -p "$(dirname $LOG_FILE)"
# Prüfe ob Service läuft
if [ "$USE_PM2" = true ]; then
if ! pm2 list | grep -q "$SERVICE_NAME.*online"; then
log_error "⚠️ Service läuft nicht (PM2)!"
restart_service
sleep 10
fi
else
if ! systemctl is-active --quiet "$SERVICE_NAME"; then
log_error "⚠️ Service läuft nicht (systemd)!"
restart_service
sleep 10
fi
fi
# Health-Check mit Retries
SUCCESS=false
for i in $(seq 1 $MAX_RETRIES); do
if check_health; then
# Nur beim ersten Check des Tages loggen (um Log-Spam zu vermeiden)
CURRENT_HOUR=$(date +%H)
CURRENT_MINUTE=$(date +%M)
if [ "$CURRENT_HOUR" = "00" ] && [ "$CURRENT_MINUTE" -lt 6 ]; then
log "✅ Health-Check erfolgreich"
fi
SUCCESS=true
break
else
if [ $i -lt $MAX_RETRIES ]; then
log "⚠️ Health-Check fehlgeschlagen (Versuch $i/$MAX_RETRIES), warte $RETRY_DELAY Sekunden..."
sleep $RETRY_DELAY
fi
fi
done
# Wenn alle Versuche fehlschlagen
if [ "$SUCCESS" = false ]; then
log_error "❌ Health-Check fehlgeschlagen nach $MAX_RETRIES Versuchen!"
# Service neu starten
if restart_service; then
# Nach Neustart erneut prüfen
sleep 10
if check_health; then
log "✅ Service nach Neustart wieder erreichbar"
send_notification "⚠️ RECOVERED:" "TimeClock Backend wurde neu gestartet und ist wieder erreichbar."
else
log_error "❌ Service auch nach Neustart nicht erreichbar!"
send_notification "🚨 CRITICAL:" "TimeClock Backend ist auch nach Neustart nicht erreichbar! Manuelle Intervention erforderlich."
fi
else
log_error "❌ Service-Neustart fehlgeschlagen!"
send_notification "🚨 CRITICAL:" "TimeClock Backend-Neustart fehlgeschlagen! Manuelle Intervention erforderlich."
fi
fi
# =============================================================================
# Zusätzliche Checks (optional)
# =============================================================================
# CPU & Memory Usage prüfen
if [ "$USE_PM2" = true ] && command -v pm2 &> /dev/null; then
PM2_STATUS=$(pm2 jlist | jq -r ".[] | select(.name==\"$SERVICE_NAME\") | \"CPU: \(.monit.cpu)%, Memory: \(.monit.memory / 1024 / 1024 | floor)MB\"" 2>/dev/null || echo "N/A")
# Nur bei hohem Ressourcenverbrauch loggen
# (Optional: weitere Logik implementieren)
fi
# Disk Space prüfen
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 90 ]; then
log_error "⚠️ Festplattenspeicher kritisch: ${DISK_USAGE}%"
send_notification "⚠️ WARNING:" "Festplattenspeicher auf TimeClock-Server kritisch: ${DISK_USAGE}%"
fi
exit 0

275
scripts/restore-backup.sh Executable file
View File

@@ -0,0 +1,275 @@
#!/bin/bash
# =============================================================================
# TimeClock v3 - Backup-Restore Script
# =============================================================================
# Stellt ein Datenbank-Backup wieder her
#
# Verwendung:
# ./restore-backup.sh [BACKUP_FILE]
# ./restore-backup.sh # Zeigt Liste der verfügbaren Backups
# ./restore-backup.sh timeclock_db_20251018_020000.sql.gz
#
# WARNUNG: Dies überschreibt die aktuelle Datenbank!
# =============================================================================
set -e
# Farben
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Konfiguration
BACKUP_DIR="/var/backups/timeclock"
PROJECT_DIR="/var/www/timeclock"
ENV_FILE="$PROJECT_DIR/backend/.env"
# Logging
log() {
echo -e "${BLUE} $1${NC}"
}
log_success() {
echo -e "${GREEN}$1${NC}"
}
log_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
log_error() {
echo -e "${RED}$1${NC}"
}
# =============================================================================
# Voraussetzungen prüfen
# =============================================================================
if [ ! -f "$ENV_FILE" ]; then
log_error ".env Datei nicht gefunden: $ENV_FILE"
exit 1
fi
if [ ! -d "$BACKUP_DIR" ]; then
log_error "Backup-Verzeichnis nicht gefunden: $BACKUP_DIR"
exit 1
fi
# =============================================================================
# Datenbank-Credentials aus .env lesen
# =============================================================================
get_env_value() {
grep "^$1=" "$ENV_FILE" | cut -d '=' -f2- | tr -d '"' | tr -d "'"
}
DB_HOST=$(get_env_value "DB_HOST")
DB_PORT=$(get_env_value "DB_PORT")
DB_NAME=$(get_env_value "DB_NAME")
DB_USER=$(get_env_value "DB_USER")
DB_PASSWORD=$(get_env_value "DB_PASSWORD")
# Defaults
DB_HOST=${DB_HOST:-localhost}
DB_PORT=${DB_PORT:-3306}
if [ -z "$DB_NAME" ] || [ -z "$DB_USER" ] || [ -z "$DB_PASSWORD" ]; then
log_error "Datenbank-Credentials unvollständig in .env"
exit 1
fi
# =============================================================================
# Verfügbare Backups auflisten
# =============================================================================
list_backups() {
echo ""
log "Verfügbare Backups in $BACKUP_DIR:"
echo ""
BACKUPS=($(ls -t "$BACKUP_DIR"/timeclock_db_*.sql.gz 2>/dev/null))
if [ ${#BACKUPS[@]} -eq 0 ]; then
log_warning "Keine Backups gefunden!"
exit 1
fi
for i in "${!BACKUPS[@]}"; do
BACKUP="${BACKUPS[$i]}"
FILENAME=$(basename "$BACKUP")
SIZE=$(du -h "$BACKUP" | cut -f1)
DATE=$(stat -c %y "$BACKUP" | cut -d'.' -f1)
printf "%2d) %-50s %6s %s\n" $((i+1)) "$FILENAME" "$SIZE" "$DATE"
done
echo ""
}
# =============================================================================
# Backup-Datei auswählen
# =============================================================================
select_backup() {
list_backups
read -p "Wähle ein Backup (Nummer) oder 'q' zum Beenden: " selection
if [ "$selection" = "q" ] || [ "$selection" = "Q" ]; then
log "Abgebrochen."
exit 0
fi
if ! [[ "$selection" =~ ^[0-9]+$ ]]; then
log_error "Ungültige Eingabe!"
exit 1
fi
BACKUPS=($(ls -t "$BACKUP_DIR"/timeclock_db_*.sql.gz 2>/dev/null))
INDEX=$((selection - 1))
if [ $INDEX -lt 0 ] || [ $INDEX -ge ${#BACKUPS[@]} ]; then
log_error "Ungültige Auswahl!"
exit 1
fi
BACKUP_FILE="${BACKUPS[$INDEX]}"
}
# =============================================================================
# Restore durchführen
# =============================================================================
restore_backup() {
local backup_file=$1
if [ ! -f "$backup_file" ]; then
log_error "Backup-Datei nicht gefunden: $backup_file"
exit 1
fi
echo ""
log_warning "WARNUNG: Dies wird die aktuelle Datenbank '$DB_NAME' überschreiben!"
log "Backup-Datei: $(basename $backup_file)"
log "Größe: $(du -h $backup_file | cut -f1)"
log "Datenbank: $DB_USER@$DB_HOST:$DB_PORT/$DB_NAME"
echo ""
read -p "Fortfahren? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
log "Abgebrochen."
exit 0
fi
# Erstelle Backup der aktuellen Datenbank (Sicherheitskopie)
log "Erstelle Sicherheitskopie der aktuellen Datenbank..."
SAFETY_BACKUP="$BACKUP_DIR/timeclock_db_pre_restore_$(date +%Y%m%d_%H%M%S).sql.gz"
if mysqldump \
--host="$DB_HOST" \
--port="$DB_PORT" \
--user="$DB_USER" \
--password="$DB_PASSWORD" \
--single-transaction \
--databases "$DB_NAME" \
| gzip > "$SAFETY_BACKUP"; then
log_success "Sicherheitskopie erstellt: $(basename $SAFETY_BACKUP)"
else
log_error "Sicherheitskopie fehlgeschlagen!"
read -p "Trotzdem fortfahren? (yes/no): " force
if [ "$force" != "yes" ]; then
exit 1
fi
fi
# Restore durchführen
log "Stelle Backup wieder her..."
# Datenbank leeren (optional, aber empfohlen)
log "Leere Datenbank..."
mysql \
--host="$DB_HOST" \
--port="$DB_PORT" \
--user="$DB_USER" \
--password="$DB_PASSWORD" \
-e "DROP DATABASE IF EXISTS $DB_NAME; CREATE DATABASE $DB_NAME;"
# Backup einspielen
if gunzip < "$backup_file" | mysql \
--host="$DB_HOST" \
--port="$DB_PORT" \
--user="$DB_USER" \
--password="$DB_PASSWORD"; then
log_success "Backup erfolgreich wiederhergestellt!"
# Datenbank-Statistiken
TABLE_COUNT=$(mysql \
--host="$DB_HOST" \
--port="$DB_PORT" \
--user="$DB_USER" \
--password="$DB_PASSWORD" \
-e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '$DB_NAME';" -s -N)
log "Datenbank-Statistik:"
log " 📊 Tabellen: $TABLE_COUNT"
# Optional: Service neu starten
log_warning "Möchtest du den Backend-Service neu starten?"
read -p "Service neu starten? (y/n): " restart
if [ "$restart" = "y" ] || [ "$restart" = "Y" ]; then
if command -v pm2 &> /dev/null && pm2 list | grep -q "timeclock-backend"; then
pm2 restart timeclock-backend
log_success "PM2-Service neu gestartet"
elif systemctl is-active --quiet timeclock; then
sudo systemctl restart timeclock
log_success "systemd-Service neu gestartet"
else
log_warning "Service-Verwaltung nicht gefunden - bitte manuell neu starten"
fi
fi
echo ""
log_success "Restore abgeschlossen!"
log "Sicherheitskopie der alten Datenbank: $(basename $SAFETY_BACKUP)"
else
log_error "Restore fehlgeschlagen!"
log "Die Sicherheitskopie befindet sich unter: $(basename $SAFETY_BACKUP)"
exit 1
fi
}
# =============================================================================
# Hauptprogramm
# =============================================================================
echo ""
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE} TimeClock v3 - Backup Restore${NC}"
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
echo ""
# Wenn Backup-Datei als Argument übergeben wurde
if [ $# -eq 1 ]; then
BACKUP_FILE="$1"
# Wenn nur Dateiname (ohne Pfad) übergeben wurde
if [ ! -f "$BACKUP_FILE" ]; then
BACKUP_FILE="$BACKUP_DIR/$1"
fi
restore_backup "$BACKUP_FILE"
else
# Interaktive Auswahl
select_backup
restore_backup "$BACKUP_FILE"
fi
exit 0

135
timeclock.service Normal file
View File

@@ -0,0 +1,135 @@
# Systemd Service für TimeClock v3 Backend
# Installation:
# sudo cp timeclock.service /etc/systemd/system/
# sudo systemctl daemon-reload
# sudo systemctl enable timeclock
# sudo systemctl start timeclock
#
# Status prüfen:
# sudo systemctl status timeclock
#
# Logs ansehen:
# sudo journalctl -u timeclock -f
[Unit]
Description=TimeClock v3 Backend API
Documentation=https://github.com/yourusername/TimeClock
After=network.target mysql.service mariadb.service
Wants=mysql.service
[Service]
# Service-Typ
Type=simple
# User und Group
User=www-data
Group=www-data
# Arbeitsverzeichnis
WorkingDirectory=/var/www/timeclock/backend
# Umgebungsvariablen
Environment=NODE_ENV=production
Environment=PATH=/usr/bin:/usr/local/bin
EnvironmentFile=/var/www/timeclock/backend/.env
# Start-Befehl
ExecStart=/usr/bin/node src/index.js
# Restart-Policy
Restart=always
RestartSec=10
# Timeouts
TimeoutStartSec=30s
TimeoutStopSec=30s
# Logging
StandardOutput=append:/var/log/timeclock/backend.log
StandardError=append:/var/log/timeclock/backend.error.log
SyslogIdentifier=timeclock
# Process Management
KillMode=mixed
KillSignal=SIGTERM
# Limits
LimitNOFILE=65536
LimitNPROC=4096
# =================================================================
# Sicherheits-Härtung (Security Hardening)
# =================================================================
# Verhindert Privilege Escalation
NoNewPrivileges=true
# Privates /tmp Verzeichnis
PrivateTmp=true
# Schützt bestimmte Kernel-Variablen
ProtectKernelTunables=true
# Verhindert Laden von Kernel-Modulen
ProtectKernelModules=true
# Schützt Kernel-Logs
ProtectKernelLogs=true
# Verhindert Zugriff auf /proc
ProtectProc=invisible
# Verhindert Zugriff auf /sys
ProtectControlGroups=true
# Schützt Hostnamen
ProtectHostname=true
# Macht /home, /root und /run/user read-only
ProtectHome=true
# Macht das System teilweise read-only
ProtectSystem=strict
# Erlaubt Schreibzugriff auf bestimmte Verzeichnisse
ReadWritePaths=/var/www/timeclock/backend
ReadWritePaths=/var/log/timeclock
# Verhindert Zugriff auf andere Benutzer
PrivateUsers=false
# Verhindert neue Devices
PrivateDevices=true
# Verhindert Zugriff auf Clock
ProtectClock=true
# Verhindert Realtime-Scheduling
RestrictRealtime=true
# Filtert gefährliche System-Calls
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources @obsolete
# Verhindert Nutzung bestimmter Namespaces
RestrictNamespaces=true
# Verhindert SUID/SGID
RestrictSUIDSGID=true
# Entfernt Capabilities
CapabilityBoundingSet=
AmbientCapabilities=
# Address Space Layout Randomization
LockPersonality=true
# Verhindert Zugriff auf andere User-Sessions
RemoveIPC=true
# Umask setzen
UMask=0077
[Install]
WantedBy=multi-user.target