Add docs, scripts, and update README

- Added docs/AUTH.md with authentication system documentation
- Added server scripts (auto-deploy, backup, monitor, webhook-server)
- Updated README with deployment info and project structure
- Added gitignore for backup archives
This commit is contained in:
Leon Bösche
2026-01-13 19:23:33 +01:00
parent 233f1dd315
commit 294b28d1a8
10 changed files with 819 additions and 88 deletions

101
scripts/auto-deploy.sh Executable file
View File

@@ -0,0 +1,101 @@
#!/bin/bash
# Daily auto-deploy script for Flutter/Go projects
# Runs at 3AM daily - no more disruptive deployments!
# Run as root/admin - no extra users needed!
set -e
# Configuration
GIT_REPO="https://lab.b0esche.cloud/b0esche/b0esche_cloud.git"
DEPLOY_DIR="/opt/auto-deploy/b0esche_cloud_rollout"
BUILD_DIR="/opt/go/data/postgres/backend/go_cloud"
LOG_FILE="/var/log/auto-deploy.log"
TIMEOUT=1800 # 30 minutes for Flutter build (increased from default)
SKIP_IF_RECENT_HOURS=2 # Skip if deployed within last 2 hours
# Logging
exec > >(tee -a "$LOG_FILE") 2>&1
# Check if we should skip deployment
LAST_DEPLOY_FILE="/tmp/last_deploy_time"
if [ -f "$LAST_DEPLOY_FILE" ]; then
LAST_DEPLOY=$(cat "$LAST_DEPLOY_FILE")
CURRENT_TIME=$(date +%s)
if [ $((CURRENT_TIME - LAST_DEPLOY)) -lt $((SKIP_IF_RECENT_HOURS * 3600)) ]; then
echo "=== Skipping deployment - recent deploy was less than $SKIP_IF_RECENT_HOURS hours ago ==="
echo "=== Auto-deploy skipped at $(date) ==="
exit 0
fi
fi
echo "=== Scheduled auto-deploy started at $(date) ==="
# Create deploy directory if it doesn't exist
mkdir -p "$DEPLOY_DIR"
cd "$DEPLOY_DIR"
echo "Pulling latest changes..."
if [ -d ".git" ]; then
git fetch origin
git reset --hard origin/main
else
git clone "$GIT_REPO" .
fi
echo "=== Deploying Backend ==="
# Copy backend code to build directory
echo "Copying backend code to build directory..."
mkdir -p "$BUILD_DIR"
cp -r go_cloud/* "$BUILD_DIR/"
# Build and start backend from traefik directory
cd /opt/traefik
echo "Building go-backend container..."
docker-compose build --no-cache go-backend
echo "Recreating go-backend container with fresh environment..."
docker-compose up -d --force-recreate go-backend
echo "Backend deployed successfully!"
echo "=== Deploying Frontend ==="
# Build Flutter for web with proper timeout
echo "Building Flutter web app..."
FRONTEND_DIR="$DEPLOY_DIR/b0esche_cloud"
cd "$FRONTEND_DIR"
timeout 900 sudo -u admin /opt/flutter/bin/flutter build web --release || {
echo "Flutter build failed or timed out"
exit 1
}
# Copy built files to nginx volume
echo "Copying built files to web server..."
rm -rf /opt/traefik/web/*
cp -r build/web/* /opt/traefik/web/
# Restart nginx container
echo "Restarting flutter-web container..."
cd /opt/traefik
docker-compose up -d --force-recreate flutter-web
echo "=== Deployment completed successfully at $(date) ==="
# Record deployment time
date +%s > "$LAST_DEPLOY_FILE"
# Health checks
echo "=== Running health checks ==="
sleep 10
if curl -f -s https://go.b0esche.cloud/health > /dev/null 2>&1; then
echo "✅ Backend health check passed"
else
echo "❌ Backend health check failed"
fi
if curl -f -s https://www.b0esche.cloud > /dev/null 2>&1; then
echo "✅ Frontend health check passed"
else
echo "❌ Frontend health check failed"
fi
echo "=== Scheduled auto-deploy completed ==="

50
scripts/backup.sh Executable file
View File

@@ -0,0 +1,50 @@
#!/bin/bash
# b0esche.cloud Backup Script
# Usage: ./backup.sh
set -e
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/opt/backups/b0esche_cloud/$DATE"
RETENTION_DAYS=30
echo "Starting backup for b0esche.cloud - $DATE"
# Create backup directory
mkdir -p "$BACKUP_DIR"
echo "Backing up Go backend PostgreSQL database..."
docker exec go-postgres pg_dump -U go_backend -Fc go_backend > "$BACKUP_DIR/go_backend.sqlc"
echo "Backing up Nextcloud database..."
docker exec nextcloud-db mysqldump -u nextcloud -pSu11Fd02!!! nextcloud > "$BACKUP_DIR/nextcloud.sql"
echo "Backing up Traefik certificates..."
cp -r /opt/traefik/acme "$BACKUP_DIR/"
echo "Backing up configuration files..."
cp /opt/go/.env.production "$BACKUP_DIR/"
cp /opt/go/docker-compose.yml "$BACKUP_DIR/go-docker-compose.yml"
cp /opt/flutter/docker-compose.yml "$BACKUP_DIR/flutter-docker-compose.yml"
cp /opt/flutter/nginx.conf "$BACKUP_DIR/"
cp /opt/traefik/docker-compose.yml "$BACKUP_DIR/traefik-docker-compose.yml"
cp /opt/traefik/traefik.yml "$BACKUP_DIR/"
echo "Backing up Docker volumes..."
docker run --rm -v nextcloud_data_31:/data -v "$BACKUP_DIR":/backup alpine tar czf /backup/nextcloud_data.tar.gz -C /data .
echo "Compressing backup..."
cd /opt/backups/b0esche_cloud
tar czf "$DATE.tar.gz" "$DATE"
rm -rf "$DATE"
echo "Backup completed: /opt/backups/b0esche_cloud/$DATE.tar.gz"
# Clean up old backups
echo "Cleaning up backups older than $RETENTION_DAYS days..."
find /opt/backups/b0esche_cloud -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup process completed successfully!"
# Show backup size
ls -lh "/opt/backups/b0esche_cloud/$DATE.tar.gz"

9
scripts/deploy-now.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Manual deploy script - call this when you want immediate deployment
# Usage: ./deploy-now.sh
echo "🚀 Starting immediate deployment..."
/opt/scripts/auto-deploy.sh
echo "✅ Manual deployment completed!"

120
scripts/monitor.sh Executable file
View File

@@ -0,0 +1,120 @@
#!/bin/bash
# b0esche.cloud Monitoring Script
# Usage: ./monitor.sh
set -e
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo "=== b0esche.cloud Service Status ==="
echo
# Check critical services
services=("traefik" "go-backend" "go-postgres" "flutter-web" "nextcloud" "nextcloud-db" "collabora")
for service in "${services[@]}"; do
if docker ps --format "table {{.Names}}" | grep -q "^$service$"; then
echo -e "${GREEN}${NC} $service is running"
else
echo -e "${RED}${NC} $service is not running"
fi
done
echo
echo "=== Service Health Checks ==="
echo
# HTTP/HTTPS health checks
echo -n "Flutter Web (www.b0esche.cloud): "
if curl -s --max-time 5 https://www.b0esche.cloud | grep -q "b0esche_cloud"; then
echo -e "${GREEN}OK${NC}"
else
echo -e "${RED}FAILED${NC}"
fi
echo -n "Go Backend (go.b0esche.cloud): "
if curl -s --max-time 5 https://go.b0esche.cloud/health | grep -q "ok"; then
echo -e "${GREEN}OK${NC}"
else
echo -e "${RED}FAILED${NC}"
fi
echo -n "Nextcloud (storage.b0esche.cloud): "
if curl -s --max-time 5 -I https://storage.b0esche.cloud | grep -q "HTTP/2 200"; then
echo -e "${GREEN}OK${NC}"
else
echo -e "${RED}FAILED${NC}"
fi
echo -n "Collabora (of.b0esche.cloud): "
if curl -s --max-time 5 -I https://of.b0esche.cloud | grep -q "HTTP/2"; then
echo -e "${GREEN}OK${NC}"
else
echo -e "${YELLOW}DEGRADED${NC}"
fi
echo
echo "=== Resource Usage ==="
echo
# Show container resource usage
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" | grep -E "(traefik|go-backend|flutter-web|nextcloud|collabora)"
echo
echo "=== Recent Error Logs ==="
echo
# Show recent error logs from each service
for service in traefik go-backend nextcloud collabora; do
errors=$(docker logs "$service" --since=1h 2>&1 | grep -i error | tail -3 | wc -l)
if [ "$errors" -gt 0 ]; then
echo -e "${YELLOW}$service:${NC} $errors errors in last hour"
docker logs "$service" --since=1h 2>&1 | grep -i error | tail -3 | sed 's/^/ /'
fi
done
echo
echo "=== SSL Certificate Status ==="
echo
# Check certificate expiry for main domains
domains=("www.b0esche.cloud" "go.b0esche.cloud" "storage.b0esche.cloud" "of.b0esche.cloud")
for domain in "${domains[@]}"; do
expiry=$(echo | openssl s_client -servername "$domain" -connect "$domain:443" 2>/dev/null | openssl x509 -noout -dates 2>/dev/null | grep notAfter | cut -d= -f2)
if [ -n "$expiry" ]; then
expiry_epoch=$(date -d "$expiry" +%s)
current_epoch=$(date +%s)
days_left=$(( (expiry_epoch - current_epoch) / 86400 ))
if [ "$days_left" -lt 7 ]; then
echo -e "${RED}$domain: ${NC}Expires in $days_left days"
elif [ "$days_left" -lt 30 ]; then
echo -e "${YELLOW}$domain: ${NC}Expires in $days_left days"
else
echo -e "${GREEN}$domain: ${NC}Expires in $days_left days"
fi
else
echo -e "${RED}$domain: ${NC}Certificate check failed"
fi
done
echo
echo "=== Disk Usage ==="
echo
# Show disk usage for critical directories
echo "PostgreSQL data:"
du -sh /opt/go/data/postgres 2>/dev/null || echo " Not accessible"
echo "Backup directory:"
du -sh /opt/backups 2>/dev/null || echo " Not found"
echo "Docker volumes:"
docker system df --format "table {{.Type}}\t{{.TotalCount}}\t{{.Size}}"
echo
echo "=== Monitoring Complete ==="

92
scripts/webhook-server.py Executable file
View File

@@ -0,0 +1,92 @@
#!/usr/bin/env python3
# Simple webhook server for Gitea
# Listens for push events and triggers auto-deploy
import http.server
import socketserver
import json
import subprocess
import os
import hmac
import hashlib
from urllib.parse import urlparse
# Configuration
PORT = 8080
SECRET = 'your-webhook-secret' # Change this!
DEPLOY_SCRIPT = '/opt/scripts/auto-deploy.sh'
class WebhookHandler(http.server.BaseHTTPRequestHandler):
def do_POST(self):
if self.path == '/webhook':
try:
# Read the payload
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
# Verify signature (optional but recommended)
signature = self.headers.get('X-Gitea-Signature', '')
if signature:
expected_sig = hmac.new(
SECRET.encode(),
post_data,
hashlib.sha256
).hexdigest()
expected_sig = f'sha256={expected_sig}'
if not hmac.compare_digest(signature, expected_sig):
self.send_response(401)
self.end_headers()
self.wfile.write(b'Invalid signature')
return
# Parse the webhook payload
data = json.loads(post_data.decode('utf-8'))
# Only deploy on manual trigger (with "deploy" in commit message)
commit_message = data.get('commits', [{}])[0].get('message', '').lower()
if 'deploy now' in commit_message or 'manual deploy' in commit_message:
print(f"Manual deploy requested: {commit_message}")
# Run the deploy script in background
subprocess.Popen([
'nohup', 'bash', '/opt/scripts/deploy-now.sh'
], stdout=open('/var/log/webhook-deploy.log', 'a'),
stderr=subprocess.STDOUT)
self.send_response(200)
self.end_headers()
self.wfile.write(b'Manual deployment triggered')
else:
print(f"Ignoring push - not a manual deploy trigger")
self.send_response(200)
self.end_headers()
self.wfile.write(b'Push received - daily deploy scheduled for 3AM')
except Exception as e:
print(f"Webhook error: {e}")
self.send_response(500)
self.end_headers()
self.wfile.write(b'Internal server error')
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b'Not found')
def do_GET(self):
if self.path == '/health':
self.send_response(200)
self.end_headers()
self.wfile.write(b'OK')
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b'Not found')
if __name__ == '__main__':
os.makedirs('/var/log', exist_ok=True)
with socketserver.TCPServer(("", PORT), WebhookHandler) as httpd:
print(f"Webhook server listening on port {PORT}")
httpd.serve_forever()