Initial commit: complete WebsiteBox project
Docker-based self-hosted WordPress deployment system with: - Four-container stack (nginx, wordpress/php-fpm, mariadb, certbot) - Automatic SSL via Let's Encrypt with self-signed fallback - First-boot WordPress setup via WP-CLI (GeneratePress + child theme, plugins) - Interactive setup wizard and one-line install script - Backup, update, healthcheck, and SSL renewal scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
69
scripts/backup.sh
Executable file
69
scripts/backup.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
# WebsiteBox Manual Backup Script
|
||||
# Usage: ./scripts/backup.sh [--prune-only]
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
BACKUP_DIR="${PROJECT_DIR}/websitebox-data/backups"
|
||||
|
||||
# Load .env
|
||||
if [ -f "${PROJECT_DIR}/.env" ]; then
|
||||
# shellcheck disable=SC1091
|
||||
source "${PROJECT_DIR}/.env"
|
||||
fi
|
||||
|
||||
RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-30}"
|
||||
|
||||
prune_old_backups() {
|
||||
echo "Pruning backups older than ${RETENTION_DAYS} days..."
|
||||
if [ -d "${BACKUP_DIR}" ]; then
|
||||
find "${BACKUP_DIR}" -type f -mtime "+${RETENTION_DAYS}" -delete 2>/dev/null || true
|
||||
echo "Pruning complete."
|
||||
else
|
||||
echo "Backup directory does not exist: ${BACKUP_DIR}"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$1" = "--prune-only" ]; then
|
||||
prune_old_backups
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo " WebsiteBox Manual Backup"
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
|
||||
# Check that containers are running
|
||||
if ! docker compose -f "${PROJECT_DIR}/docker-compose.yml" ps --status running | grep -q websitebox-wordpress; then
|
||||
echo "ERROR: WordPress container is not running."
|
||||
echo "Start it with: docker compose up -d"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create database dump
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
echo "Creating database backup..."
|
||||
docker compose -f "${PROJECT_DIR}/docker-compose.yml" exec -T db \
|
||||
mariadb-dump -u"${DB_USER:-websitebox}" -p"${DB_PASSWORD}" "${DB_NAME:-websitebox}" \
|
||||
> "${BACKUP_DIR}/db_${TIMESTAMP}.sql"
|
||||
|
||||
echo "Database backup saved: websitebox-data/backups/db_${TIMESTAMP}.sql"
|
||||
|
||||
# Create files backup
|
||||
echo "Creating files backup..."
|
||||
tar czf "${BACKUP_DIR}/files_${TIMESTAMP}.tar.gz" \
|
||||
-C "${PROJECT_DIR}/websitebox-data" \
|
||||
--exclude='backups' \
|
||||
--exclude='database' \
|
||||
wordpress/ certs/
|
||||
|
||||
echo "Files backup saved: websitebox-data/backups/files_${TIMESTAMP}.tar.gz"
|
||||
|
||||
# Prune old backups
|
||||
prune_old_backups
|
||||
|
||||
echo ""
|
||||
echo "Backup complete!"
|
||||
echo "Files stored in: websitebox-data/backups/"
|
||||
52
scripts/healthcheck.sh
Executable file
52
scripts/healthcheck.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
# WebsiteBox Health Check
|
||||
# Usage: ./scripts/healthcheck.sh
|
||||
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo " WebsiteBox Health Check"
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
|
||||
ALL_HEALTHY=true
|
||||
|
||||
for service in nginx wordpress db; do
|
||||
container="websitebox-${service}"
|
||||
status=$(docker inspect --format='{{.State.Health.Status}}' "$container" 2>/dev/null || echo "not found")
|
||||
|
||||
case "$status" in
|
||||
healthy)
|
||||
echo " [OK] ${service}: healthy"
|
||||
;;
|
||||
unhealthy)
|
||||
echo " [FAIL] ${service}: unhealthy"
|
||||
ALL_HEALTHY=false
|
||||
;;
|
||||
starting)
|
||||
echo " [WAIT] ${service}: starting..."
|
||||
;;
|
||||
*)
|
||||
echo " [????] ${service}: ${status}"
|
||||
ALL_HEALTHY=false
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Certbot doesn't have a healthcheck — just check if running
|
||||
certbot_state=$(docker inspect --format='{{.State.Status}}' websitebox-certbot 2>/dev/null || echo "not found")
|
||||
if [ "$certbot_state" = "running" ]; then
|
||||
echo " [OK] certbot: running"
|
||||
else
|
||||
echo " [WARN] certbot: ${certbot_state}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
if [ "$ALL_HEALTHY" = true ]; then
|
||||
echo "All services are healthy."
|
||||
exit 0
|
||||
else
|
||||
echo "Some services are not healthy. Check logs with:"
|
||||
echo " docker compose logs -f [service]"
|
||||
exit 1
|
||||
fi
|
||||
17
scripts/ssl-renew.sh
Executable file
17
scripts/ssl-renew.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
# WebsiteBox SSL Renewal Helper
|
||||
# Manually triggers a certificate renewal attempt via the certbot container.
|
||||
# Automatic renewals are handled by the certbot container's built-in loop.
|
||||
# Use this script only if you need to force an immediate renewal.
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
echo "Requesting certificate renewal..."
|
||||
docker compose -f "${PROJECT_DIR}/docker-compose.yml" exec certbot \
|
||||
certbot renew --deploy-hook "touch /var/run/certbot-signal/reload"
|
||||
|
||||
echo "Renewal attempt complete. Check logs for details:"
|
||||
echo " docker compose logs certbot"
|
||||
102
scripts/update.sh
Executable file
102
scripts/update.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
# WebsiteBox Update Script
|
||||
# Usage: ./scripts/update.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
cd "${PROJECT_DIR}"
|
||||
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo " WebsiteBox Update"
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
|
||||
# Check for uncommitted changes to tracked files
|
||||
if ! git diff --quiet HEAD 2>/dev/null; then
|
||||
echo "WARNING: You have uncommitted changes to tracked files."
|
||||
echo "These files have been modified:"
|
||||
git diff --name-only HEAD
|
||||
echo ""
|
||||
echo "Please commit or stash your changes before updating."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Fetch latest changes
|
||||
echo "Checking for updates..."
|
||||
git fetch origin main 2>/dev/null || git fetch origin master 2>/dev/null || {
|
||||
echo "ERROR: Could not fetch from remote repository."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Determine branch name
|
||||
BRANCH="main"
|
||||
if ! git rev-parse --verify origin/main >/dev/null 2>&1; then
|
||||
BRANCH="master"
|
||||
fi
|
||||
|
||||
# Check if already up to date
|
||||
LOCAL=$(git rev-parse HEAD)
|
||||
REMOTE=$(git rev-parse "origin/${BRANCH}")
|
||||
|
||||
if [ "${LOCAL}" = "${REMOTE}" ]; then
|
||||
echo "Already up to date!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Show what's changing
|
||||
echo ""
|
||||
echo "Changes to be applied:"
|
||||
git log --oneline "HEAD..origin/${BRANCH}"
|
||||
echo ""
|
||||
|
||||
# Prompt for confirmation
|
||||
read -rp "Apply these updates? (y/N) " confirm
|
||||
if [ "${confirm}" != "y" ] && [ "${confirm}" != "Y" ]; then
|
||||
echo "Update cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Pull changes
|
||||
echo "Pulling updates..."
|
||||
git pull origin "${BRANCH}"
|
||||
|
||||
# Pull latest base images
|
||||
echo "Pulling latest Docker images..."
|
||||
docker compose pull
|
||||
|
||||
# Rebuild containers
|
||||
echo "Rebuilding containers..."
|
||||
docker compose up -d --build
|
||||
|
||||
# Clean up old images
|
||||
echo "Cleaning up old images..."
|
||||
docker image prune -f
|
||||
|
||||
# Run migrations if any exist
|
||||
MIGRATIONS_DIR="${PROJECT_DIR}/scripts/migrations"
|
||||
MIGRATIONS_STATE="${PROJECT_DIR}/.websitebox-migrations"
|
||||
|
||||
if [ -d "${MIGRATIONS_DIR}" ]; then
|
||||
touch "${MIGRATIONS_STATE}"
|
||||
for migration in "${MIGRATIONS_DIR}"/*.sh; do
|
||||
[ -f "$migration" ] || continue
|
||||
migration_name=$(basename "$migration")
|
||||
if ! grep -qF "$migration_name" "${MIGRATIONS_STATE}" 2>/dev/null; then
|
||||
echo "Running migration: ${migration_name}..."
|
||||
bash "$migration"
|
||||
echo "$migration_name" >> "${MIGRATIONS_STATE}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Health check
|
||||
echo "Checking container health..."
|
||||
sleep 10
|
||||
"${SCRIPT_DIR}/healthcheck.sh"
|
||||
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo " Update complete!"
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
Reference in New Issue
Block a user