diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c2c59e9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,29 @@ +.git +.gitignore +.github +.gitattributes +.env +.env.* +!.env.production.example +node_modules +npm-debug.log +vendor +storage/app/* +storage/framework/cache/* +storage/framework/sessions/* +storage/framework/views/* +storage/logs/* +bootstrap/cache/* +public/storage +public/hot +*.md +!README.md +tests +.phpunit.result.cache +phpunit.xml +docker-compose*.yml +.editorconfig +.styleci.yml +*.log +.DS_Store +Thumbs.db diff --git a/.env.local.example b/.env.local.example new file mode 100644 index 0000000..87a798e --- /dev/null +++ b/.env.local.example @@ -0,0 +1,82 @@ +APP_NAME="Teren App" +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_TIMEZONE=UTC +APP_URL=http://localhost:8080 + +APP_LOCALE=sl +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=sl_SI + +APP_MAINTENANCE_DRIVER=file +APP_MAINTENANCE_STORE=database + +BCRYPT_ROUNDS=12 + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local + +# Database +DB_CONNECTION=pgsql +DB_HOST=postgres +DB_PORT=5432 +DB_DATABASE=teren_app +DB_USERNAME=teren_user +DB_PASSWORD=local_password + +# Redis +REDIS_CLIENT=phpredis +REDIS_HOST=redis +REDIS_PORT=6379 + +# Queue +QUEUE_CONNECTION=redis + +# Session +SESSION_DRIVER=redis +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN= +SESSION_SECURE_COOKIE=false +SESSION_SAME_SITE=lax + +# Cache +CACHE_STORE=redis + +# Mail (Mailpit for local testing) +MAIL_MAILER=smtp +MAIL_HOST=mailpit +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +SCOUT_PREFIX= +SCOUT_QUEUE=true + +# Sanctum +SANCTUM_STATEFUL_DOMAINS=localhost,127.0.0.1,localhost:8080,127.0.0.1:8080 + +# Logging +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +# Vite +VITE_APP_NAME="${APP_NAME}" +VITE_DEV_SERVER_KEY= +VITE_DEV_SERVER_CERT= + +# LibreOffice for document previews (Docker container path) +LIBREOFFICE_BIN=/usr/bin/soffice + +# Storage configuration for generated previews +FILES_PREVIEW_DISK=public +FILES_PREVIEW_BASE=previews/casesNEL=null +LOG_LEVEL=debug + +# Vite +VITE_DEV_SERVER_KEY= +VITE_DEV_SERVER_CERT= diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 0000000..a65afd1 --- /dev/null +++ b/.env.production.example @@ -0,0 +1,88 @@ +APP_NAME="Teren App" +APP_ENV=production +APP_KEY= # Generate with: php artisan key:generate +APP_DEBUG=false +APP_TIMEZONE=UTC +APP_URL=https://example.com # Your domain + +APP_LOCALE=sl +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=sl_SI + +APP_MAINTENANCE_DRIVER=file +APP_MAINTENANCE_STORE=database + +BCRYPT_ROUNDS=12 + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local + +# Database +DB_CONNECTION=pgsql +DB_HOST=postgres +DB_PORT=5432 +DB_DATABASE=teren_app +DB_USERNAME=teren_user +DB_PASSWORD= # Generate a strong password + +# Redis +REDIS_CLIENT=phpredis +REDIS_HOST=redis +REDIS_PORT=6379 + +# Queue +QUEUE_CONNECTION=redis + +# Session +SESSION_DRIVER=redis +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN= +SESSION_SECURE_COOKIE=true +SESSION_SAME_SITE=lax + +# Cache +CACHE_STORE=redis + +# pgAdmin +PGADMIN_EMAIL=admin@example.com +PGADMIN_PASSWORD= # Generate a strong password + +# WireGuard VPN (REQUIRED - app is VPN-only) +WG_SERVERURL=vpn.example.com # Your VPS public IP or domain +WG_UI_PASSWORD= # Generate a strong password for WireGuard dashboard + +# Mail (configure as needed) +MAIL_MAILER=log +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PA +SCOUT_DRIVER=database +SCOUT_PREFIX= +SCOUT_QUEUE=true + +# Sanctum +SANCTUM_STATEFUL_DOMAINS=example.com,www.example.com,10.13.13.1 + +# Logging +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=error + +# Vite +VITE_APP_NAME="${APP_NAME}" + +# LibreOffice for document previews (Docker container path) +LIBREOFFICE_BIN=/usr/bin/soffice + +# Storage configuration for generated previews +FILES_PREVIEW_DISK=public +FILES_PREVIEW_BASE=previews/cases +# Logging +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=error diff --git a/.gitignore b/.gitignore index 75f1845..86fff51 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,16 @@ yarn-error.log check-*.php test-*.php fix-*.php +clean-*.php +mark-*.php # Development Documentation IMPORT_*.md -V2_*.md \ No newline at end of file +V2_*.md +REPORTS_*.md +DEDUPLICATION_*.md + +# Docker Local Testing +docker-compose.local.yaml +docker-compose.override.yaml +.env.local \ No newline at end of file diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md new file mode 100644 index 0000000..094daca --- /dev/null +++ b/DEPLOYMENT_GUIDE.md @@ -0,0 +1,1045 @@ +# Teren App - Ubuntu 24.04 VPS Deployment Guide + +This guide covers the complete setup of the Teren App on Ubuntu 24.04 using Docker, including SSL, WireGuard VPN, and automated deployments. + +## Table of Contents +1. [Prerequisites](#prerequisites) +2. [Initial VPS Setup](#initial-vps-setup) +3. [Docker Installation](#docker-installation) +4. [WireGuard VPN Setup](#wireguard-vpn-setup) ⚠️ **SETUP FIRST - APP IS VPN-ONLY** +5. [Application Setup](#application-setup) +6. [SSL Certificate Setup](#ssl-certificate-setup) +7. [Portainer Setup](#portainer-setup) +8. [Automated Deployment Setup](#automated-deployment-setup) +9. [Monitoring & Maintenance](#monitoring--maintenance) +10. [Troubleshooting](#troubleshooting) + +--- + +## Prerequisites + +- Ubuntu 24.04 VPS with root access +- Domain name pointed to your VPS IP (optional - can use IP only) +- Self-hosted Gitea server +- At least 2GB RAM (4GB recommended) +- 20GB+ storage + +**IMPORTANT:** This application is configured to be accessible ONLY through WireGuard VPN. The app will NOT be publicly accessible. + +--- + +## Initial VPS Setup + +### 1. Update System + +```bash +sudo apt update && sudo apt upgrade -y +``` + +### 2. Create Deploy User + +```bash +# Create user +sudo adduser deployer +sudo usermod -aG sudo deployer + +# Switch to deploy user +su - deployer +``` + +### 3. Configure Firewall + +```bash +# Install UFW +sudo apt install ufw -y + +# Allow SSH, HTTP, HTTPS, and WireGuard +sudo ufw allow 22/tcp +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp +sudo ufw allow 51820/udp # WireGuard + +# Enable firewall +sudo ufw enable +sudo ufw status +``` + +### 4. Install Essential Tools + +```bash +sudo apt install -y \ + git \ + curl \ + wget \ + unzip \ + vim \ + htop \ + software-properties-common +``` + +--- + +## Docker Installation + +### 1. Install Docker + +```bash +# Add Docker's official GPG key +sudo apt-get update +sudo apt-get install ca-certificates curl +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc + +# Add repository +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ + $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + +# Install Docker +sudo apt-get update +sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin +``` + +### 2. Configure Docker + +```bash +# Add user to docker group +sudo usermod -aG docker $USER + +# Enable Docker service +sudo systemctl enable docker +sudo systemctl start docker + +# Verify installation +docker --version +docker compose version + +# Log out and back in for group changes to take effect +exit +su - deployer +``` + +---WireGuard VPN Setup + +⚠️ **CRITICAL: Setup WireGuard FIRST before the application!** The app is configured to be VPN-only and will not be publicly accessible. + +### 1. Enable Kernel Modules + +```bash +# Load WireGuard kernel module +sudo apt install -y wireguard-tools +sudo modprobe wireguard + +# Make it persistent +echo "wireguard" | sudo tee -a /etc/modules +``` + +### 2. Configure Environment + +```bash +cd /var/www/Teren-app + +# Edit .env file +vim .env +``` + +Set these WireGuard variables: +- `WG_SERVERURL`: Your VPS public IP or domain (e.g., `vpn.example.com` or `192.168.1.100`) +- `WG_UI_PASSWORD`: Strong password for WireGuard dashboard + +### 3. Start WireGuard Container + +```bash +# Start only WireGuard first +docker compose up -d wireguard + +# Wait for it to initialize +sleep 10 + +# Check status +docker compose logs wireguard +``` + +### 4. Access WireGuard Dashboard + +The WireGuard Web UI is temporarily accessible publicly for initial setup: + +**URL:** `http://YOUR_VPS_IP:51821` + +Login with the password you set in `WG_UI_PASSWORD`. + +### 5. Create Your First Client + +1. In the WireGuard dashboard, click **"New Client"** +2. Give it a name (e.g., "MyLaptop") +3. Click the QR code or download the config file +4. Install WireGuard on your device: + - **Windows:** https://www.wireguard.com/install/ + - **macOS:** App Store + - **Linux:** `sudo apt install wireguard` + - **iOS/Android:** App Store / Play Store + +5. ImpoStart Application Containers + +**Note:** Make sure you're connected to the WireGuard VPN before proceeding. + +```bash +# Start all application containers +docker compose up -d postgres redis app nginx certbot + +# Verify containers are running +docker compose ps + +# Test application (from your VPN-connected machine) +curl http://10.13.13.1 +# Or in browser: http://10.13.13.1 + +You should get responses from the VPN gateway. + +### 7. Secure WireGuard Dashboard (After Setup) + +After creating your initial clients, secure the WireGuard UI by editing `docker-compose.yaml`: +**Note:** SSL certificates are optional since the app is VPN-only. You can use self-signed certificates or skip SSL entirely for internal VPN access. However, if you want proper SSL: + +### Option 1: Self-Signed Certificates (Recommended for VPN-only) + +```bash +# Generate self-signed certificate +sudo mkdir -p docker/nginx/ssl +sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout docker/nginx/ssl/selfsigned.key \ + -out docker/nginx/ssl/selfsigned.crt \ + -subj "/C=SI/ST=Slovenia/L=Ljubljana/O=TerenApp/CN=10.13.13.1" + +# Update nginx config to use self-signed cert +vim docker/nginx/conf.d/app.conf +``` + +Update SSL paths: +```nginx +ssl_certificate /etc/nginx/ssl/selfsigned.crt; +ssl_certificate_key /etc/nginx/ssl/selfsigned.key; +``` + +```bash +# Restart nginx +docker compose restart nginx + +# Test (ignore certificate warning) +curl -k https://10.13.13.1 +``` + +### Option 2: Let's Encrypt (If using domain name) + +Only if you have a domain pointing to your VPS and want valid SSL: + +```bash +# Temporarily allow port 80 publicly +sudo ufw allow 80/tcp + +# Request certificate +docker compose run --rm certbot certonly \ + --standalone \ + --preferred-challenges http \ + --email your-email@example.com \ + --agree-tos \ + --no-eff-email \ + -d your-domain.com + +# Block port 80 again +sudo ufw delete allow 80/tcp + +# Update nginx config with Let's Encrypt paths +# Restart nginx +docker compose restart nginx +``` + +### Option 3: No SSL (HTTP Only) + +For internal VPN-only access, you can skip SSL entirely: + +```bash +# Edit nginx config to remove HTTPS server block +vim docker/nginx/conf.d/app.conf + +# Keep only the HTTP (port 80) server block +# Restart nginx +docker compose restart nginx + +# Access via: http://10.13.13.1 + +```bash +# Create project directory +sudo mkdir -p /var/www +sudo chown -R $USER:$USER /var/www +cd /var/www + +# Clone from your Gitea server +git clone git@your-gitea-server.com:username/Teren-app.git +cd Teren-app +``` + +### 2. Setup Environment + +```bash +# Copy example files +cp .env.production.example .env +cp docker-compose.yaml.example docker-compose.yaml + +# Edit .env file with your actual values +vim .env +``` + +**Important:** Update these values in `.env`: +- `APP_KEY` - Generate with `php artisan key:generate` (run after first container start) +- `APP_URL` - Your domain +- `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` - Strong database credentials +- `PGADMIN_EMAIL`, `PGADMIN_PASSWORD` - pgAdmin credentials + +### 3. Update Docker Compose + +Edit `docker-compose.yaml` and update: +- Replace `example.com` with your domain in nginx volume mounts +- Update environment variables as needed + +### 4. Create Required Directories + +```bash +# Create docker directories +mkdir -p docker/nginx/conf.d +mkdir -p docker/nginx/ssl +mkdir -p docker/certbot/conf +mkdir -p docker/certbot/www +mkdir -p docker/postgres/init +mkdir -p docker/supervisor/conf.d +mkdir -p docker/php + +# Set permissions +sudo chown -R $USER:$USER docker/ +``` + +### 5. Update Nginx Configuration + +Edit `docker/nginx/conf.d/app.conf` and replace `example.com` with your actual domain. + +### 6. Initial SSL Certificate (HTTP-only first) + +Before getting SSL certificates, modify nginx config temporarily: + +```bash +# Comment out SSL sections in docker/nginx/conf.d/app.conf +# Keep only the HTTP (port 80) server block + +# Start containers +docker compose up -d postgres redis app nginx + +# Verify nginx is running +docker compose ps +curl http://your-domain.com # Should get some response +``` + +--- + +## SSL Certificate Setup + +### 1. Obtain Initial Certificate + +```bash +# Request certificate for your domain +docker compose run --rm certbot certonly \ + --webroot \ + --webroot-path=/var/www/certbot \ + --email your-email@example.com \ + --agree-tos \ + --no-eff-email \ + -d your-domain.com \ + -d www.your-domain.com +``` + +### 2. Enable HTTPS in Nginx + +```bash +# Uncomment SSL sections in docker/nginx/conf.d/app.conf +vimPortainer Setup + +Portainer provides a web UI for managing Docker containers, images, networks, and volumes. + +### 1. Access Portainer + +**Prerequisites:** Must be connected to WireGuard VPN + +**URL:** `http://10.13.13.1:9000` or `https://10.13.13.1:9443` + +### 2. Initial Setup + +On first access: + +1. Create admin account: + - Username: `admin` + - Password: (set a strong password) + +2. Select **"Docker"** environment + +3. Click **"Connect"** + +### 3. Portainer Features + +- **Containers:** Start, stop, restart, view logs +- **Images:** Pull, build, remove images +- **Networks:** Manage Docker networks +- **Volumes:** View and manage volumes +- **Stacks:** Deploy docker-compose stacks +- **Events:** Real-time Docker events + +### 4. Useful Portainer Operations + +**View Container Logs:** +1. Go to Containers +2. Click on container name +3. Click "Logs" +4. Select "Auto-refresh" + +**Execute Commands:** +1. Go to Containers +2. Click on container +3. Click "Console" +4. Select "/bin/sh" or "/bin/bash" + +**Update Container:** +1. Go to Containers +2. Click container name +3. Click "Recreate" +4. Enable "Pull latest image" + +### 5. Security Notes + +- Portainer is only accessible via VPN (bound to 10.13.13.1) +- Always use strong passwords +- Enable 2FA if deploying Portainer Business Edition +- Regularly backup Portainer data volume + - "127.0.0.1:5432:5432" + +# To: +ports: + - "10.13.13.1:5432:5432" # WireGuard VPN subnet +``` + +Then restart: +```bash +docker compose down +docker compose up -d +``` + +### 5. Connect Client + +Install WireGuard on your local machine: +- **Linux:** `sudo apt install wireguard` +- **macOS:** Download from App Store +- **Windows:** Download from wireguard.com + +Import the client config and connect. + +### 6. Access Services via VPN + +Once connected to VPN: +- pgAdmin: `http://10.13.13.1:5050` +- PostgreSQL: `10.13.13.1:5432` + +--- + +## Automated Deployment Setup + +### Method 1: Gitea Webhooks (Recommended) + +#### 1. Setup Webhook Listener on VPS + +```bash +# Create webhook handler script +sudo vim /usr/local/bin/webhook-handler.sh +``` + +Add: +```bash +#!/bin/bash +# Simple webhook handler + +PORT=9000 +SECRET="your-webhook-secret" + +while true; do + echo "Listening for webhooks on port $PORT..." + echo -e "HTTP/1.1 200 OK\n\n" | nc -l -p $PORT -q 1 > /tmp/webhook.log + + # Verify secret (basic) + if grep -q "$SECRET" /tmp/webhook.log; then + echo "Valid webhook received, deploying..." + cd /var/www/Teren-app && ./deploy.sh >> /var/log/deploy.log 2>&1 + fi +done +``` + +```bash +# Make executable +sudo chmod +x /usr/local/bin/webhook-handler.sh + +# Create systemd service +sudo vim /etc/systemd/system/webhook-handler.service +``` + +Add: +```ini +[Unit] +Description=Gitea Webhook Handler +After=network.target + +[Service] +Type=simple +User=deployer +ExecStart=/usr/local/bin/webhook-handler.sh +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +```bash +# Enable and start +sudo systemctl enable webhook-handler +sudo systemctl start webhook-handler + +# Allow webhook port in firewall +sudo ufw allow 9000/tcp +``` + +#### 2. Configure Gitea Webhook + +In your Gitea repository: +1. Go to Settings → Webhooks +2. Add webhook: + - Payload URL: `http://your-vps-ip:9000` + - Secret: `your-webhook-secret` + - Trigger: Push events on `main` branch +3. Test webhook + +### Method 2: Gitea Actions (Recommended for CI/CD) + +#### 1. Enable Gitea Actions + +On your Gitea server, edit `app.ini`: +```ini +[actions] +ENABLED = true +``` + +#### 2. Setup Actions Runner on VPS + +```bash +# Download runner +cd ~ +wget https://gitea.com/gitea/act_runner/releases/latest/download/act_runner-linux-amd64 +chmod +x act_runner-linux-amd64 +sudo mv act_runner-linux-amd64 /usr/local/bin/act-runner + +# Register runner +act-runner register --instance https://your-gitea.com --token YOUR_RUNNER_TOKEN + +# Generate config +act-runner generate-config > runner-config.yaml + +# Create systemd service +sudo vim /etc/systemd/system/act-runner.service +``` + +Add: +```ini +[Unit] +Description=Gitea Actions Runner +After=network.target + +[Service] +Type=simple +User=deployer +WorkingDirectory=/home/deployer +ExecStart=/usr/local/bin/act-runner daemon --config /home/deployer/runner-config.yaml +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +```bash +sudo systemctl enable act-runner +sudo systemctl start act-runner +``` + +#### 3. Create Workflow File + +In your repository, create `.gitea/workflows/deploy.yaml`: + +```yaml +name: Deploy to Production + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Deploy via SSH + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.VPS_HOST }} + username: deployer + key: ${{ secrets.SSH_PRIVATE_KEY }} + script: | + cd /var/www/Teren-app + ./deploy.sh +``` + +Add secrets in Gitea: Settings → Secrets → Actions +- `VPS_HOST`: Your VPS IP/domain +- `SSH_PRIVATE_KEY`: SSH key for deployer user + +### 3. Make Deploy Script Executable + +```bash +cd /var/www/Teren-app +chmod +x deploy.sh + +# Edit script to match your setup +vim deploy.sh +``` + +### 4. Test Deployment + +```bash +# Manual test +./deploy.sh + +# Or trigger via git push +git commit --allow-empty -m "Test deployment" +git push origin main +``` + +--- + +## Application Initialization + +### 1. Start All Containers + +```bash +cd /var/www/Teren-app +docker compose up -d +``` + +### 2. Generate Application Key + +```bash +docker compose exec app php artisan key:generate +``` + +### 3. Run Migrations + +```bash +docker compose exec app php artisan migrate --force +``` + +### 4. Seed Database (if needed) + +```bash +docker compose exec app php artisan db:seed --force +``` + +### 5. Cache Configuration + +```bash +docker compose exec app php artisan config:cache +docker compose exec app php artisan route:cache +docker compose exec app php artisan view:cache +``` + +### 6. Set Permissions + +```bash +docker compose exec app chown -R www:www /var/www/storage +docker compose exec app chown -R www:www /var/www/bootstrap/cache +``` + +### 7. Verify Installation + +```bash +# Check containers +docker compose ps + +# Check logs +docker compose logs -f app + +# Test application +curl https://your-domain.com +``` + +--- + +## Monitoring & Maintenance + +### Container Management + +```bash +# View all containers +docker compose ps + +# View logs +docker compose logs -f app # Laravel app +docker compose logs -f nginx # Nginx +docker compose logs -f postgres # Database + +# Restart containers +docker compose restart app +docker compose restart nginx + +# Stop all +docker compose down + +# Start all +docker compose up -d +``` + +### Queue Workers + +```bash +# Check queue worker status +docker compose exec app supervisorctl status + +# Restart qServices via VPN + +**All services are only accessible when connected to WireGuard VPN:** + +| Service | URL | Purpose | +|---------|-----|---------| +| Laravel App | `http://10.13.13.1` | Main application | +| pgAdmin | `http://10.13.13.1:5050` | PostgreSQL UI | +| Portainer | `http://10.13.13.1:9000` | Docker management | +| WireGuard UI | `http://10.13.13.1:51821` | VPN management | +| PostgreSQL | `10.13.13.1:5432` | Direct DB connection | +| Redis | `10.13.13.1:6379` | Direct Redis connection | + +**pgAdmin Setup:** +1. Connect to VPN +2. Open: `http://10.13.13.1:5050` +3. Login with `PGADMIN_EMAIL` and `PGADMIN_PASSWORD` +4. Add server: + - Host: `postgres` (or `10.13.13.1` if connecting externally)nt + +```bash +# Connect to PostgreSQL +docker compose exec postgres psql -U teren_user -d teren_app + +# Backup database +docker compose exec postgres pg_dump -U teren_user teren_app > backup-$(date +%Y%m%d).sql + +# Restore database +docker compose exec -T postgres psql -U teren_user teren_app < backup-20260113.sql +``` + +### Access pgAdmin + +Via WireGuard VPN: +1. Connect to VPN +2. Open browser: `http://10.13.13.1:5050` +3. Login with `PGADMIN_EMAIL` and `PGADMIN_PASSWORD` +4. Add server: + - Host: `postgres` + - Port: `5432` + - Database: Your `DB_DATABASE` + - Username: Your `DB_USERNAME` + - Password: Your `DB_PASSWORD` + +### Log Rotation + +```bash +# Install logrotate +sudo apt install logrotate -y + +# Create config +sudo vim /etc/logrotate.d/teren-app +``` + +Add: +``` +/var/www/Teren-app/storage/logs/*.log { + daily + missingok + rotate 14 + compress + delaycompress + notifempty + create 0640 deployer deployer + sharedscripts +} +``` + +### Automated Backups + +Create backup script: +```bash +sudo vim /usr/local/bin/backup-teren.sh +``` + +Add: +```bash +#!/bin/bash +BACKUP_DIR="/backups/teren-app" +DATE=$(date +%Y%m%d_%H%M%S) + +mkdir -p $BACKUP_DIR + +# Backup database +docker compose -f /var/www/Teren-app/docker-compose.yaml exec -T postgres \ + pg_dump -U teren_user teren_app | gzip > $BACKUP_DIR/db-$DATE.sql.gz + +# Backup uploads +tar -czf $BACKUP_DIR/storage-$DATE.tar.gz -C /var/www/Teren-app storage/app/public + +# Keep only last 7 days +find $BACKUP_DIR -type f -mtime +7 -delete + +echo "Backup completed: $DATE" +``` + +```bash +sudo chmod +x /usr/local/bin/backup-teren.sh + +# Add to crontab +crontab -e +``` + +Add: +``` +0 2 * * * /usr/local/bin/backup-teren.sh >> /var/log/backup.log 2>&1 +``` + +--- + +## Troubleshooting + +### Container Won't Start + +```bash +# Check logs +docker compose logs app + +# Check container status +docker compose ps + +# Rebuild container +docker compose down +docker compose build --no-cache app +docker compose up -d +``` + +### Permission Errors + +```bash +# Fix storage permissions +docker compose exec app chown -R www:www /var/www/storage +docker compose exec app chmod -R 775 /var/www/storage +``` + +### Database Connection Issues + +```bash +# Check if PostgreSQL is running +docker compose ps postgres + +# Check PostgreSQL logs +docker compose logs postgres + +# Test connection +docker compose exec app php artisan tinker +# In tinker: DB::connection()->getPdo(); +``` + +### Queue Not Processing + +```bash +# Check supervisor status +docker compose exec app supervisorctl status + +# Restart queue workers +docker compose exec app supervisorctl restart all + +# Check worker logs +docker compose exec app tail -f storage/logs/worker.log +``` + +# Check kernel module +lsmod | grep wireguard + +# Check WireGuard interface +docker compose exec wireguard wg show +``` + +### Can't Access WireGuard Dashboard + +```bash +# Check if container is running +docker compose ps wireguard + +# Check logs +docker compose logs wireguard + +# Verify port binding +sudo netstat -tulpn | grep 51821 + +# Try accessing via IP +curl http://YOUR_VPS_IP:51821 +``` + +### Can't Access Application After VPN Connection + +```bash +# Verify VPN connection +ping 10.13.13.1 + +# Check routing table (on client) +ip route | grep 10.13.13.0 + +# Check if services are bound correctly +docker compose exec app netstat -tulpn + +# Check nginx logs +docker compose logs nginx + +# Verify nginx is listening on VPN IP +docker compose exec nginx netstat -tulpn | grep 80 + +### SSL Certificate Issues + +```bash +# Check certificate expiry +docker compose run --rm certbot certificates + +# Renew manually +docker compose run --rm certbot renew + +# Check nginx config +docker compose exec nginx nginx -t + +# Reload nginx +docker compose restart nginx +``` + +### WireGuard Not Working + +```bash +# Check WireGuard status +docker compose logs wireguard + +# Restart WireGuard +docker compose restart wireguard + +# Check firewall +sudo ufw status + +# Verify port is open +sudo ss -tulpn | grep 51820 +``` + +### High Memory Usage + +```bash +# ChecWireGuard VPN configured and working +- [ ] WireGuard UI secured (VPN-only after initial setup) +- [ ] All services bound to VPN network (10.13.13.1) +- [ ] Firewall configured (UFW) - only VPN port public +- [ ] SSH key authentication enabled +- [ ] Strong passwords for all services (DB, pgAdmin, Portainer, WireGuard) +- [ ] `.env` file not in git +- [ ] SSL certificates configured (self-signed or Let's Encrypt) +- [ ] Regular backups automated +- [ ] Log rotation configured +- [ ] Security headers in nginx +- [ ] Docker containers run as non-root +- [ ] Regular system updates scheduled +- [ ] WireGuard client configs secur +### Application Returns 500 Error + +```bash +# Check Laravel logs +docker compose exec app tail -f storage/logs/laravel.log + +# Check permissions +docker compose exec app ls -la storage/ + +# Clear caches +docker compose exec app php artisan cache:clear +docker compose exec app php artisan config:clear +docker compose exec app php artisan route:clear +docker compose exec app php artisan view:clear +``` + +--- + +## Security Checklist + +- [ ] Firewall configured (UFW) +- [ ] SSH key authentication enabled +- [ ] Strong database passwords +- [ ] `.env` file not in git +- [ ] Services behind WireGuard VPN +- [ ] SSL certificates valid +- [ ] Regular backups automated +- [ ] Log rotation configured +- [ ] Security headers in nginx +- [ ] Docker containers run as non-root +- [ ] Regular system updates scheduled + +--- + +## Useful Commands Reference + +```bash +# Deploy new version +./deploy.sh + +# View all logs +docker compose logs -f + +# Restart everything +docker compose restart + +# Update single container +docker compose up -d --no-deps --build app + +# Run artisan commands +docker compose exec app php artisan [command] + +# Access container shell +docker compose exec app sh + +# Database backup +docker compose exec postgres pg_dump -U teren_user teren_app > backup.sql + +# System monitoring +htop +docker stats +df -h +``` + +--- + +## Support & Updates + +For issues specific to the Teren App, check the application logs and supervisor status. For Docker/infrastructure issues, check container logs and system logs. + +To update the deployment guide or Docker configuration, submit changes via Gitea and they'll be automatically deployed. + +--- + +**Last Updated:** January 2026 +**Version:** 1.0.0 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f23c8aa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,83 @@ +ARG PHP_VERSION=8.4 +FROM php:${PHP_VERSION}-fpm-alpine + +# Set working directory +WORKDIR /var/www + +# Install system dependencies +RUN apk add --no-cache \ + git \ + curl \ + zip \ + unzip \ + supervisor \ + nginx \ + postgresql-dev \ + libpng-dev \ + libjpeg-turbo-dev \ + freetype-dev \ + libwebp-dev \ + oniguruma-dev \ + libxml2-dev \ + linux-headers \ + ${PHPIZE_DEPS} + +# Configure and install PHP extensions +RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-install -j$(nproc) \ + pdo_pgsql \ + pgsql \ + mbstring \ + exif \ + pcntl \ + bcmath \ + gd \ + opcache + +# Install Redis extension via PECL +RUN pecl install redis \ + && docker-php-ext-enable redis + +# Install LibreOffice from community repository +RUN apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community \ + libreoffice-common \ + libreoffice-writer \ + libreoffice-calc + +# Install Composer +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer + +# Create system user to run Composer and Artisan Commands +RUN addgroup -g 1000 -S www && \ + adduser -u 1000 -S www -G www + +# Copy application files (will be overridden by volume mount in local development) +COPY --chown=www:www . /var/www + +# Copy supervisor configuration +COPY docker/supervisor/supervisord.conf /etc/supervisor/supervisord.conf +COPY docker/supervisor/conf.d /etc/supervisor/conf.d + +# Set permissions +RUN chown -R www:www /var/www \ + && chmod -R 755 /var/www/storage \ + && chmod -R 755 /var/www/bootstrap/cache + +# PHP Configuration for production +RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" + +# Copy PHP custom configuration +COPY docker/php/custom.ini $PHP_INI_DIR/conf.d/custom.ini + +# Configure PHP-FPM to listen on all interfaces (0.0.0.0) instead of just localhost +# This is needed for nginx running in a separate container to reach PHP-FPM +RUN sed -i 's/listen = 127.0.0.1:9000/listen = 9000/' /usr/local/etc/php-fpm.d/www.conf + +# Expose port 9000 for PHP-FPM +EXPOSE 9000 + +# Create directories for supervisor logs +RUN mkdir -p /var/log/supervisor + +# Start supervisor (which will manage both PHP-FPM and Laravel queue workers) +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] diff --git a/LOCAL_TESTING_GUIDE.md b/LOCAL_TESTING_GUIDE.md new file mode 100644 index 0000000..919627a --- /dev/null +++ b/LOCAL_TESTING_GUIDE.md @@ -0,0 +1,343 @@ +# Local Testing Guide - Windows/Mac/Linux + +This guide helps you test the Teren App Docker setup on your local machine without WireGuard VPN. + +## Quick Start + +### 1. Prerequisites + +- Docker Desktop installed and running +- Git +- 8GB RAM recommended +- Ports available: 8080, 5433 (PostgreSQL), 5050, 6379, 9000, 8025, 1025 +- **Note:** If you have local PostgreSQL on port 5432, the Docker container uses 5433 instead + +### 2. Setup + +```bash +# Clone repository (if not already) +git clone YOUR_GITEA_URL +cd Teren-app + +# Copy local environment file +cp .env.local.example .env + +# Start all services +docker compose -f docker-compose.local.yaml up -d + +# Wait for services to start (30 seconds) +timeout 30 + +# Generate application key +docker compose -f docker-compose.local.yaml exec app php artisan key:generate + +# Run migrations +docker compose -f docker-compose.local.yaml exec app php artisan migrate + +# Seed database (optional) +docker compose -f docker-compose.local.yaml exec app php artisan db:seed + +# Install frontend dependencies (if needed) +npm install +npm run dev +``` + +### 3. Access Services + +| Service | URL | Credentials | +|---------|-----|-------------| +| **Laravel App** | http://localhost:8080 | - | +| **Portainer** | http://localhost:9000 | Set on first visit | +| **pgAdmin** | http://localhost:5050 | admin@local.dev / admin | +| **Mailpit** | http://localhost:8025 | - | +| **PostgreSQL** | localhost:5433 | teren_user / local_password | +| **Redis** | localhost:6379 | - | + +**Note:** PostgreSQL uses port 5433 to avoid conflicts with any local PostgreSQL installation. + +## Common Commands + +### Docker Compose Commands + +```bash +# Start all services +docker compose -f docker-compose.local.yaml up -d + +# Stop all services +docker compose -f docker-compose.local.yaml down + +# View logs +docker compose -f docker-compose.local.yaml logs -f + +# View specific service logs +docker compose -f docker-compose.local.yaml logs -f app + +# Restart a service +docker compose -f docker-compose.local.yaml restart app + +# Rebuild containers +docker compose -f docker-compose.local.yaml up -d --build + +# Stop and remove everything (including volumes) +docker compose -f docker-compose.local.yaml down -v +``` + +### Laravel Commands + +```bash +# Run artisan commands +docker compose -f docker-compose.local.yaml exec app php artisan [command] + +# Examples: +docker compose -f docker-compose.local.yaml exec app php artisan migrate +docker compose -f docker-compose.local.yaml exec app php artisan db:seed +docker compose -f docker-compose.local.yaml exec app php artisan cache:clear +docker compose -f docker-compose.local.yaml exec app php artisan config:clear +docker compose -f docker-compose.local.yaml exec app php artisan queue:work + +# Run tests +docker compose -f docker-compose.local.yaml exec app php artisan test + +# Access container shell +docker compose -f docker-compose.local.yaml exec app sh + +# Run Composer commands +docker compose -f docker-compose.local.yaml exec app composer install +docker compose -f docker-compose.local.yaml exec app composer update +``` + +### Database Commands + +```bash +# Connect to PostgreSQL (from inside container) +docker compose -f docker-compose.local.yaml exec postgres psql -U teren_user -d teren_app + +# Connect from Windows host +psql -h localhost -p 5433 -U teren_user -d teren_app + +# Backup database +docker compose -f docker-compose.local.yaml exec postgres pg_dump -U teren_user teren_app > backup.sql + +# Restore database +docker compose -f docker-compose.local.yaml exec -T postgres psql -U teren_user teren_app < backup.sql + +# Reset database +docker compose -f docker-compose.local.yaml exec app php artisan migrate:fresh --seed +``` + +## pgAdmin Setup + +1. Open http://localhost:5050 +2. Login: `admin@local.dev` / `admin` +3. Add Server: + - **General Tab:** + - Name: `Teren Local` + - **Connection Tab:** + - Host: `postgres` + - Port: `5432` + - Database: `teren_app` + - Username: `teren_user` + - Passwo + +**External Connection:** To connect from your Windows machine (e.g., DBeaver, pgAdmin desktop), use: +- Host: `localhost` +- Port: `5433` (not 5432) +- Database: `teren_app` +- Username: `teren_user` +- Password: `local_password`rd: `local_password` +4. Click Save + +## Mailpit - Email Testing + +All emails sent by the application are caught by Mailpit. + +- Access: http://localhost:8025 +- View all emails in the web interface +- Test email sending: + +```bash +docker compose -f docker-compose.local.yaml exec app php artisan tinker +# In tinker: +Mail::raw('Test email', function($msg) { + $msg->to('test@example.com')->subject('Test'); +}); +``` + +## Portainer Setup + +1. Open http://localhost:9000 +2. On first visit, create admin account +3. Select "Docker" environment +4. Click "Connect" + +Use Portainer to: +- View and manage containers +- Check logs +- Execute commands in containers +- Monitor resource usage + +## Development Workflow + +### Frontend Development + +The local setup supports live reloading: + +```bash +# Run Vite dev server (outside Docker) +npm run dev + +# Or inside Docker +docker compose -f docker-compose.local.yaml exec app npm run dev +``` + +Access: http://localhost:8080 + +### Code Changes + +All code changes are automatically reflected because the source code is mounted as a volume: + +```yaml +volumes: + - ./:/var/www # Live code mounting +``` + +### Queue Workers + +Queue workers are running via Supervisor inside the container. To restart: + +```bash +# Restart queue workers +docker compose -f docker-compose.local.yaml exec app supervisorctl restart all + +# Check status +docker compose -f docker-compose.local.yaml exec app supervisorctl status + +# View worker logs +docker compose -f docker-compose.local.yaml exec app tail -f storage/logs/worker.log +``` + +## Troubleshooting + +### Port Already in Use + +If you get "port is already allocated" error: + +```bash +# Windows - Find process using port +netstat -ano | findstr :8080 + +# Kill process by PID +taskkill /PID /F + +# Or change port in docker-compose.local.yaml +ports: + - "8081:80" # Change 8080 to 8081 +``` + +### Container Won't Start + +```bash +# Check logs +docker compose -f docker-compose.local.yaml logs app + +# Rebuild containers +docker compose -f docker-compose.local.yaml down +docker compose -f docker-compose.local.yaml up -d --build +``` + +### Permission Errors (Linux/Mac) + +```bash +# Fix storage permissions +docker compose -f docker-compose.local.yaml exec app chown -R www:www /var/www/storage +docker compose -f docker-compose.local.yaml exec app chmod -R 775 /var/www/storage +``` + +### Database Connection Failed + +```bash +# Check if PostgreSQL is running +docker compose -f docker-compose.local.yaml ps postgres + +# Check logs +docker compose -f docker-compose.local.yaml logs postgres + +# Restart PostgreSQL +docker compose -f docker-compose.local.yaml restart postgres +``` + +### Clear All Data and Start Fresh + +```bash +# Stop and remove everything +docker compose -f docker-compose.local.yaml down -v + +# Remove images +docker compose -f docker-compose.local.yaml down --rmi all + +# Start fresh +docker compose -f docker-compose.local.yaml up -d --build + +# Re-initialize +docker compose -f docker-compose.local.yaml exec app php artisan key:generate +docker compose -f docker-compose.local.yaml exec app php artisan migrate:fresh --seed +``` + +## Performance Tips + +### Windows Performance + +If using WSL2 (recommended): + +1. Clone repo inside WSL2 filesystem, not Windows filesystem +2. Use WSL2 terminal for commands +3. Enable WSL2 integration in Docker Desktop settings + +### Mac Performance + +1. Enable VirtioFS in Docker Desktop settings +2. Disable file watching if not needed +3. Use Docker volumes for vendor directories: + +```yaml +volumes: + - ./:/var/www + - /var/www/vendor # Anonymous volume for vendor + - /var/www/node_modules # Anonymous volume for node_modules +``` + +## Testing Production-Like Setup + +To test the production VPN setup locally (advanced): + +1. Enable WireGuard in `docker-compose.yaml.example` +2. Change all `10.13.13.1` bindings to `127.0.0.1` +3. Test SSL with self-signed certificates + +## Differences from Production + +| Feature | Local | Production | +|---------|-------|------------| +| **VPN** | No VPN | WireGuard required | +| **Port** | :8080 | :80/:443 | +| **SSL** | No SSL | Let's Encrypt | +| **Debug** | Enabled | Disabled | +| **Emails** | Mailpit | Real SMTP | +| **Logs** | Debug level | Error level | +| **Code** | Live mount | Built into image | + +## Next Steps + +After testing locally: + +1. Review `docker-compose.yaml.example` for production +2. Follow `DEPLOYMENT_GUIDE.md` for VPS setup +3. Configure WireGuard VPN +4. Deploy to production + +## Useful Resources + +- [Docker Compose Documentation](https://docs.docker.com/compose/) +- [Laravel Docker Documentation](https://laravel.com/docs/deployment) +- [PostgreSQL Docker](https://hub.docker.com/_/postgres) +- [Mailpit Documentation](https://github.com/axllent/mailpit) diff --git a/QUICK_START_VPN.md b/QUICK_START_VPN.md new file mode 100644 index 0000000..18541b6 --- /dev/null +++ b/QUICK_START_VPN.md @@ -0,0 +1,159 @@ +# Quick Start: VPN-Only Access Setup + +⚠️ **IMPORTANT:** This application is configured for VPN-ONLY access. It will NOT be publicly accessible. + +## Quick Setup Steps + +### 1. Install Docker (on VPS) +```bash +curl -fsSL https://get.docker.com | sh +sudo usermod -aG docker $USER +``` + +### 2. Clone & Configure +```bash +git clone YOUR_GITEA_REPO/Teren-app.git +cd Teren-app +cp docker-compose.yaml.example docker-compose.yaml +cp .env.production.example .env +``` + +### 3. Edit Configuration +```bash +vim .env +``` + +**Required changes:** +- `WG_SERVERURL` = Your VPS public IP (e.g., `123.45.67.89`) +- `WG_UI_PASSWORD` = Strong password for WireGuard dashboard +- `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` = Database credentials +- `PGADMIN_EMAIL`, `PGADMIN_PASSWORD` = pgAdmin credentials + +### 4. Start WireGuard First +```bash +# Enable kernel module +sudo modprobe wireguard + +# Start WireGuard +docker compose up -d wireguard + +# Wait 10 seconds +sleep 10 + +# Check status +docker compose logs wireguard +``` + +### 5. Setup VPN Client (on your laptop/desktop) + +**Access WireGuard Dashboard:** `http://YOUR_VPS_IP:51821` + +1. Login with password from step 3 +2. Click "New Client" +3. Name it (e.g., "MyLaptop") +4. Download config or scan QR code + +**Install WireGuard Client:** +- Windows: https://www.wireguard.com/install/ +- macOS: App Store +- Linux: `sudo apt install wireguard` +- Mobile: App Store / Play Store + +**Import config and CONNECT** + +### 6. Verify VPN Works +```bash +# From your local machine (while connected to VPN) +ping 10.13.13.1 +``` + +Should get responses ✅ + +### 7. Secure WireGuard Dashboard + +Edit `docker-compose.yaml`: +```yaml +# Find wireguard service, change: +ports: + - "51821:51821/tcp" +# To: +ports: + - "10.13.13.1:51821:51821/tcp" +``` + +```bash +docker compose down +docker compose up -d wireguard +``` + +### 8. Start All Services +```bash +# Make sure you're connected to VPN! +docker compose up -d +``` + +### 9. Initialize Application +```bash +# Generate app key +docker compose exec app php artisan key:generate + +# Run migrations +docker compose exec app php artisan migrate --force + +# Cache config +docker compose exec app php artisan config:cache +``` + +### 10. Access Your Services + +**While connected to VPN:** + +| Service | URL | +|---------|-----| +| **Laravel App** | http://10.13.13.1 | +| **Portainer** | http://10.13.13.1:9000 | +| **pgAdmin** | http://10.13.13.1:5050 | +| **WireGuard UI** | http://10.13.13.1:51821 | + +## Firewall Configuration + +```bash +sudo ufw allow 22/tcp # SSH +sudo ufw allow 51820/udp # WireGuard VPN +sudo ufw enable +``` + +**That's it!** ✅ + +## Adding More VPN Clients + +1. Connect to VPN +2. Open: `http://10.13.13.1:51821` +3. Click "New Client" +4. Download config +5. Import on new device + +## Troubleshooting + +**Can't connect to VPN:** +```bash +docker compose logs wireguard +sudo ufw status +``` + +**Can't access app after VPN connection:** +```bash +ping 10.13.13.1 +docker compose ps +docker compose logs nginx +``` + +**Check which ports are exposed:** +```bash +docker compose ps +sudo netstat -tulpn | grep 10.13.13.1 +``` + +## Full Documentation + +See `DEPLOYMENT_GUIDE.md` for complete setup instructions, SSL configuration, automated deployments, and troubleshooting. diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..e155607 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Teren App Deployment Script +# This script handles automated deployment from Gitea + +set -e # Exit on any error + +echo "🚀 Starting deployment..." + +# Configuration +PROJECT_DIR="/var/www/Teren-app" +BRANCH="main" # Change to your production branch +GITEA_REPO="git@your-gitea-server.com:username/Teren-app.git" + +# Colors for output +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Change to project directory +cd $PROJECT_DIR + +echo "📥 Pulling latest changes from $BRANCH..." +git fetch origin $BRANCH +git reset --hard origin/$BRANCH + +echo "🔧 Copying production environment file..." +if [ ! -f .env ]; then + echo "${RED}❌ .env file not found! Please create it from .env.production.example${NC}" + exit 1 +fi + +echo "🐳 Building and starting Docker containers..." +docker-compose down +docker-compose build --no-cache app +docker-compose up -d + +echo "⏳ Waiting for containers to be healthy..." +sleep 10 + +echo "📦 Installing/updating Composer dependencies..." +docker-compose exec -T app composer install --no-dev --optimize-autoloader --no-interaction + +echo "🎨 Building frontend assets..." +# If you build assets locally or in CI/CD, uncomment: +# npm ci +# npm run build + +echo "🔑 Optimizing Laravel..." +docker-compose exec -T app php artisan config:cache +docker-compose exec -T app php artisan route:cache +docker-compose exec -T app php artisan view:cache +docker-compose exec -T app php artisan event:cache + +echo "📊 Running database migrations..." +docker-compose exec -T app php artisan migrate --force + +echo "🗄️ Clearing old caches..." +docker-compose exec -T app php artisan cache:clear +docker-compose exec -T app php artisan queue:restart + +echo "🔄 Restarting queue workers..." +docker-compose restart app + +echo "${GREEN}✅ Deployment completed successfully!${NC}" + +# Optional: Send notification (Slack, Discord, etc.) +# curl -X POST -H 'Content-type: application/json' \ +# --data '{"text":"🚀 Teren App deployed successfully!"}' \ +# YOUR_WEBHOOK_URL + +# Show running containers +echo "📋 Running containers:" +docker-compose ps diff --git a/docker-compose.yaml.example b/docker-compose.yaml.example new file mode 100644 index 0000000..55ea06c --- /dev/null +++ b/docker-compose.yaml.example @@ -0,0 +1,189 @@ +version: '3.8' + +services: + # Laravel Application + app: + build: + context: . + dockerfile: Dockerfile + args: + - PHP_VERSION=8.4 + container_name: teren-app + restart: unless-stopped + working_dir: /var/www + volumes: + - ./:/var/www + - ./storage:/var/www/storage + - ./bootstrap/cache:/var/www/bootstrap/cache + environment: + - APP_ENV=${APP_ENV:-production} + - APP_DEBUG=${APP_DEBUG:-false} + - DB_CONNECTION=pgsql + - DB_HOST=postgres + - DB_PORT=5432 + - DB_DATABASE=${DB_DATABASE} + - DB_USERNAME=${DB_USERNAME} + - DB_PASSWORD=${DB_PASSWORD} + - REDIS_HOST=redis + - REDIS_PORT=6379 + - QUEUE_CONNECTION=redis + - LIBREOFFICE_BIN=/usr/bin/soffice + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - teren-network + # Supervisor runs inside the container (defined in Dockerfile) + # Includes PHP-FPM, Laravel queue workers, and queue-sms workers + + # Nginx Web Server (VPN-only access) + nginx: + image: nginx:alpine + container_name: teren-nginx + restart: unless-stopped + ports: + - "10.13.13.1:80:80" # Only accessible via WireGuard VPN + - "10.13.13.1:443:443" # Only accessible via WireGuard VPN + volumes: + - ./:/var/www + - ./docker/nginx/conf.d:/etc/nginx/conf.d + - ./docker/nginx/ssl:/etc/nginx/ssl + - ./docker/certbot/conf:/etc/letsencrypt + - ./docker/certbot/www:/var/www/certbot + depends_on: + - app + networks: + - teren-network + command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'" + + # Certbot for SSL certificates + certbot: + image: certbot/certbot + container_name: teren-certbot + restart: unless-stopped + volumes: + - ./docker/certbot/conf:/etc/letsencrypt + - ./docker/certbot/www:/var/www/certbot + entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" + networks: + - teren-network + + # PostgreSQL Database + postgres: + image: postgres:16-alpine + container_name: teren-postgres + restart: unless-stopped + ports: + - "127.0.0.1:5432:5432" # Only accessible via localhost (or VPN) + environment: + - POSTGRES_DB=${DB_DATABASE} + - POSTGRES_USER=${DB_USERNAME} + - POSTGRES_PASSWORD=${DB_PASSWORD} + - PGDATA=/var/lib/postgresql/data/pgdata + volumes: + - postgres-data:/var/lib/postgresql/data + - ./docker/postgres/init:/docker-entrypoint-initdb.d + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME}"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - teren-network + + # pgAdmin - PostgreSQL UI + pgadmin: + image: dpage/pgadmin4:latest + container_name: teren-pgadmin + restart: unless-stopped + ports: + - "127.0.0.1:5050:80" # Only accessible via localhost (or VPN) + environment: + - PGADMIN_DEFAULT_EMAIL=${PGADMIN_EMAIL:-admin@admin.com} + - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_PASSWORD:-admin} + - PGADMIN_CONFIG_SERVER_MODE=True + - PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED=True + volumes: + - pgadmin-data:/var/lib/pgadmin + depends_on: + - postgres + networks: + - teren-network + + # Redis for caching and queues + redis: + image: redis:7-alpine + container_name: teren-redis + restart: unless-stopped + ports: + - "127.0.0.1:6379:6379" + volumes: + - redis-data:/data + command: redis-server --appendonly yes + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 5 + networks: + - teren-network + + # WireGuard VPN with Web UI Dashboard + wireguard: + image: weejewel/wg-easy:latest + container_name: teren-wireguard + restart: unless-stopped + cap_add: + - NET_ADMIN + - SYS_MODULE + environment: + - WG_HOST=${WG_SERVERURL} # Your VPS public IP or domain + - PASSWORD=${WG_UI_PASSWORD} # Password for WireGuard UI + - WG_PORT=51820 + - WG_DEFAULT_ADDRESS=10.13.13.x + - WG_DEFAULT_DNS=1.1.1.1,1.0.0.1 + - WG_MTU=1420 + - WG_PERSISTENT_KEEPALIVE=25 + - WG_ALLOWED_IPS=10.13.13.0/24 + volumes: + - wireguard-data:/etc/wireguard + ports: + - "51820:51820/udp" # WireGuard VPN port (public) + - "51821:51821/tcp" # WireGuard Web UI (public for initial setup, then VPN-only) + sysctls: + - net.ipv4.conf.all.src_valid_mark=1 + - net.ipv4.ip_forward=1 + networks: + - teren-network + + # Portainer - Docker Management UI (VPN-only access) + portainer: + image: portainer/portainer-ce:latest + container_name: teren-portainer + restart: unless-stopped + ports: + - "10.13.13.1:9000:9000" # Portainer UI (VPN-only) + - "10.13.13.1:9443:9443" # Portainer HTTPS (VPN-only) + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - portainer-data:/data + networks: + - teren-network + +networks: + teren-network: + driver: bridge + +volumes: + postgres-data: + driver: local + pgadmin-data: + driver: local + redis-data: + driver: local + wireguard-data: + driver: local + portainer-data: + driver: local diff --git a/docker/nginx/conf.d/app.conf b/docker/nginx/conf.d/app.conf new file mode 100644 index 0000000..bddd8db --- /dev/null +++ b/docker/nginx/conf.d/app.conf @@ -0,0 +1,86 @@ +server { + listen 80; + listen [::]:80; + server_name example.com www.example.com; # Change this to your domain + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name example.com www.example.com; # Change this to your domain + + root /var/www/public; + index index.php index.html; + + # SSL Configuration + ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # Change this + ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # Change this + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + # Logging + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + # Laravel location configuration + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_pass app:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_hide_header X-Powered-By; + + # Increase timeouts for long-running requests + fastcgi_read_timeout 300; + fastcgi_send_timeout 300; + } + + location ~ /\.(?!well-known).* { + deny all; + } + + # Deny access to sensitive files + location ~ /\.env { + deny all; + } + + # Cache static assets + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Gzip compression + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; + gzip_disable "msie6"; + + client_max_body_size 100M; +} diff --git a/docker/nginx/conf.d/app.local.conf b/docker/nginx/conf.d/app.local.conf new file mode 100644 index 0000000..4e10ee3 --- /dev/null +++ b/docker/nginx/conf.d/app.local.conf @@ -0,0 +1,53 @@ +server { + listen 80; + server_name localhost; + + root /var/www/public; + index index.php index.html; + + # Logging + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + # Laravel location configuration + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_pass app:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_hide_header X-Powered-By; + + # Increase timeouts for debugging + fastcgi_read_timeout 3600; + fastcgi_send_timeout 3600; + } + + location ~ /\.(?!well-known).* { + deny all; + } + + # Deny access to sensitive files + location ~ /\.env { + deny all; + } + + # Cache static assets + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Gzip compression + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; + gzip_disable "msie6"; + + client_max_body_size 100M; +} diff --git a/docker/php/custom.ini b/docker/php/custom.ini new file mode 100644 index 0000000..12a0906 --- /dev/null +++ b/docker/php/custom.ini @@ -0,0 +1,23 @@ +; PHP Custom Configuration for Production + +upload_max_filesize = 100M +post_max_size = 100M +memory_limit = 512M +max_execution_time = 300 +max_input_time = 300 + +; OPcache settings +opcache.enable = 1 +opcache.memory_consumption = 256 +opcache.interned_strings_buffer = 16 +opcache.max_accelerated_files = 20000 +opcache.validate_timestamps = 0 +opcache.save_comments = 1 +opcache.fast_shutdown = 1 + +; Production settings +expose_php = Off +display_errors = Off +display_startup_errors = Off +log_errors = On +error_log = /var/log/php_errors.log diff --git a/docker/supervisor/conf.d/laravel-queue.conf b/docker/supervisor/conf.d/laravel-queue.conf new file mode 100644 index 0000000..31d3706 --- /dev/null +++ b/docker/supervisor/conf.d/laravel-queue.conf @@ -0,0 +1,25 @@ +[program:laravel-queue] +process_name=%(program_name)s_%(process_num)02d +command=/usr/local/bin/php /var/www/artisan queue:work --sleep=3 --tries=3 --timeout=300 --verbose +autostart=true +autorestart=true +user=www +numprocs=2 +redirect_stderr=true +stdout_logfile=/var/www/storage/logs/worker.log +stdout_logfile_maxbytes=20MB +stdout_logfile_backups=10 +stopwaitsecs=360 + +[program:laravel-queue-sms] +process_name=%(program_name)s_%(process_num)02d +command=/usr/local/bin/php /var/www/artisan queue:work --queue=sms --sleep=3 --tries=3 --timeout=90 --verbose +autostart=true +autorestart=true +user=www +numprocs=1 +redirect_stderr=true +stdout_logfile=/var/www/storage/logs/worker-sms.log +stdout_logfile_maxbytes=20MB +stdout_logfile_backups=10 +stopwaitsecs=360 diff --git a/docker/supervisor/conf.d/php-fpm.conf b/docker/supervisor/conf.d/php-fpm.conf new file mode 100644 index 0000000..e1151a7 --- /dev/null +++ b/docker/supervisor/conf.d/php-fpm.conf @@ -0,0 +1,11 @@ +[program:php-fpm] +command=/usr/local/sbin/php-fpm --nodaemonize --fpm-config /usr/local/etc/php-fpm.d/www.conf +autostart=true +autorestart=true +priority=5 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +stdout_events_enabled=true +stderr_events_enabled=true diff --git a/docker/supervisor/supervisord.conf b/docker/supervisor/supervisord.conf new file mode 100644 index 0000000..a014b68 --- /dev/null +++ b/docker/supervisor/supervisord.conf @@ -0,0 +1,19 @@ +[unix_http_server] +file=/var/run/supervisor.sock +chmod=0700 + +[supervisord] +nodaemon=true +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid +childlogdir=/var/log/supervisor +user=root + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///var/run/supervisor.sock + +[include] +files = /etc/supervisor/conf.d/*.conf