Yolo changes to make this work

This commit is contained in:
Karl Cordes 2025-08-08 11:24:48 +10:00
parent 6f538e3e4d
commit 2ea0398f41
6 changed files with 460 additions and 26 deletions

344
DEPLOYMENT-CADDY.md Normal file
View file

@ -0,0 +1,344 @@
# CMC Sales Deployment Guide with Caddy
## Overview
This guide covers deploying the CMC Sales application to a Debian 12 VM using Caddy as the reverse proxy with automatic HTTPS.
## Architecture
- **Production**: `https://cmc.springupsoftware.com`
- **Staging**: `https://staging.cmc.springupsoftware.com`
- **Reverse Proxy**: Caddy (running on host)
- **Applications**: Docker containers
- CakePHP legacy app
- Go modern app
- MariaDB database
- **SSL**: Automatic via Caddy (Let's Encrypt)
- **Authentication**: Basic auth configured in Caddy
## Prerequisites
### 1. Server Setup (Debian 12)
```bash
# Update system
sudo apt update && sudo apt upgrade -y
# Install Docker
sudo apt install -y docker.io docker-compose-plugin
sudo systemctl enable docker
sudo systemctl start docker
# Add user to docker group
sudo usermod -aG docker $USER
# Log out and back in
# Create directories
sudo mkdir -p /var/backups/cmc-sales
sudo chown $USER:$USER /var/backups/cmc-sales
```
### 2. Install Caddy
```bash
# Run the installation script
sudo ./scripts/install-caddy.sh
# Or manually install
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
```
### 3. DNS Configuration
Ensure DNS records point to your server:
- `cmc.springupsoftware.com` → Server IP
- `staging.cmc.springupsoftware.com` → Server IP
## Initial Deployment
### 1. Clone Repository
```bash
cd /home/cmc
git clone git@code.springupsoftware.com:cmc/cmc-sales.git cmc-sales
sudo chown -R $USER:$USER cmc-sales
cd cmc-sales
```
### 2. Environment Configuration
```bash
# Copy environment files
cp .env.staging go-app/.env.staging
cp .env.production go-app/.env.production
# Edit with actual passwords
nano .env.staging
nano .env.production
# Create credential directories
mkdir -p credentials/staging credentials/production
```
### 3. Setup Basic Authentication
```bash
# Generate password hashes for Caddy
./scripts/setup-caddy-auth.sh
# Or manually
caddy hash-password
# Copy the hash and update Caddyfile
```
### 4. Configure Caddy
```bash
# Copy Caddyfile
sudo cp Caddyfile /etc/caddy/Caddyfile
# Edit to update passwords and email
sudo nano /etc/caddy/Caddyfile
# Validate configuration
caddy validate --config /etc/caddy/Caddyfile
# Start Caddy
sudo systemctl start caddy
sudo systemctl status caddy
```
### 5. Gmail OAuth Setup
Same as before - set up OAuth credentials for each environment:
- Staging: `credentials/staging/credentials.json`
- Production: `credentials/production/credentials.json`
### 6. Database Initialization
```bash
# Start database containers
docker compose -f docker-compose.caddy-staging.yml up -d db-staging
docker compose -f docker-compose.caddy-production.yml up -d db-production
# Wait for databases
sleep 30
# Restore production database (if you have a backup)
./scripts/restore-db.sh production /path/to/backup.sql.gz
```
## Deployment Commands
### Starting Services
```bash
# Start staging environment
docker compose -f docker-compose.caddy-staging.yml up -d
# Start production environment
docker compose -f docker-compose.caddy-production.yml up -d
# Reload Caddy configuration
sudo systemctl reload caddy
```
### Updating Applications
```bash
# Pull latest code
git pull origin main
# Update staging
docker compose -f docker-compose.caddy-staging.yml down
docker compose -f docker-compose.caddy-staging.yml build --no-cache
docker compose -f docker-compose.caddy-staging.yml up -d
# Test staging, then update production
docker compose -f docker-compose.caddy-production.yml down
docker compose -f docker-compose.caddy-production.yml build --no-cache
docker compose -f docker-compose.caddy-production.yml up -d
```
## Caddy Management
### Configuration
```bash
# Edit Caddyfile
sudo nano /etc/caddy/Caddyfile
# Validate configuration
caddy validate --config /etc/caddy/Caddyfile
# Reload configuration (zero downtime)
sudo systemctl reload caddy
```
### Monitoring
```bash
# Check Caddy status
sudo systemctl status caddy
# View Caddy logs
sudo journalctl -u caddy -f
# View access logs
sudo tail -f /var/log/caddy/cmc-production.log
sudo tail -f /var/log/caddy/cmc-staging.log
```
### SSL Certificates
Caddy handles SSL automatically! To check certificates:
```bash
# List certificates
sudo ls -la /var/lib/caddy/.local/share/caddy/certificates/
# Force certificate renewal (rarely needed)
sudo systemctl stop caddy
sudo rm -rf /var/lib/caddy/.local/share/caddy/certificates/*
sudo systemctl start caddy
```
## Container Port Mapping
| Service | Container Port | Host Port | Access |
|---------|---------------|-----------|---------|
| cmc-php-staging | 80 | 8091 | localhost only |
| cmc-go-staging | 8080 | 8092 | localhost only |
| cmc-db-staging | 3306 | 3307 | localhost only |
| cmc-php-production | 80 | 8093 | localhost only |
| cmc-go-production | 8080 | 8094 | localhost only |
| cmc-db-production | 3306 | - | internal only |
## Monitoring and Maintenance
### Health Checks
```bash
# Check all containers
docker ps
# Check application health
curl -I https://cmc.springupsoftware.com
curl -I https://staging.cmc.springupsoftware.com
# Internal health checks (from server)
curl http://localhost:8094/api/v1/health # Production Go
curl http://localhost:8092/api/v1/health # Staging Go
```
### Database Backups
Same backup scripts work:
```bash
# Manual backup
./scripts/backup-db.sh production
./scripts/backup-db.sh staging
# Automated backups
sudo crontab -e
# Add:
# 0 2 * * * /home/cmc/cmc-sales/scripts/backup-db.sh production
# 0 3 * * * /home/cmc/cmc-sales/scripts/backup-db.sh staging
```
## Security Benefits with Caddy
1. **Automatic HTTPS**: No manual certificate management
2. **Modern TLS**: Always up-to-date TLS configuration
3. **OCSP Stapling**: Enabled by default
4. **Security Headers**: Easy to configure
5. **Rate Limiting**: Built-in support
## Troubleshooting
### Caddy Issues
```bash
# Check Caddy configuration
caddy validate --config /etc/caddy/Caddyfile
# Check Caddy service
sudo systemctl status caddy
sudo journalctl -u caddy -n 100
# Test reverse proxy
curl -v http://localhost:8094/api/v1/health
```
### Container Issues
```bash
# Check container logs
docker compose -f docker-compose.caddy-production.yml logs -f
# Restart specific service
docker compose -f docker-compose.caddy-production.yml restart cmc-go-production
```
### SSL Issues
```bash
# Caddy automatically handles SSL, but if issues arise:
# 1. Check DNS is resolving correctly
dig cmc.springupsoftware.com
# 2. Check Caddy can reach Let's Encrypt
sudo journalctl -u caddy | grep -i acme
# 3. Ensure ports 80 and 443 are open
sudo ufw status
```
## Advantages of Caddy Setup
1. **Simpler Configuration**: Caddyfile is more readable than nginx
2. **Automatic HTTPS**: No certbot or lego needed
3. **Zero-Downtime Reloads**: Config changes without dropping connections
4. **Better Performance**: More efficient than nginx for this use case
5. **Native Rate Limiting**: Built-in without additional modules
6. **Automatic Certificate Renewal**: No cron jobs needed
## Migration from Nginx
If migrating from the nginx setup:
1. Stop nginx containers: `docker compose -f docker-compose.proxy.yml down`
2. Install and configure Caddy
3. Start new containers with caddy compose files
4. Update DNS if needed
5. Monitor logs during transition
## File Structure
```
/home/cmc/cmc-sales/
├── docker-compose.caddy-staging.yml
├── docker-compose.caddy-production.yml
├── Caddyfile
├── credentials/
│ ├── staging/
│ └── production/
├── scripts/
│ ├── backup-db.sh
│ ├── restore-db.sh
│ ├── install-caddy.sh
│ └── setup-caddy-auth.sh
└── .env files
/etc/caddy/
└── Caddyfile (deployed config)
/var/log/caddy/
├── cmc-production.log
└── cmc-staging.log
```

View file

@ -45,8 +45,8 @@ Ensure these DNS records point to your server:
### 1. Clone Repository ### 1. Clone Repository
```bash ```bash
cd /opt cd /home/cmc
sudo git clone <repository-url> cmc-sales git clone git@code.springupsoftware.com:cmc/cmc-sales.git cmc-sales
sudo chown -R $USER:$USER cmc-sales sudo chown -R $USER:$USER cmc-sales
cd cmc-sales cd cmc-sales
``` ```
@ -58,7 +58,7 @@ cd cmc-sales
cp .env.staging go-app/.env.staging cp .env.staging go-app/.env.staging
cp .env.production go-app/.env.production cp .env.production go-app/.env.production
# Edit with actual passwords # Edit with actual passwords -- up to this.
nano .env.staging nano .env.staging
nano .env.production nano .env.production
@ -94,7 +94,7 @@ go run cmd/auth/main.go
docker compose -f docker-compose.proxy.yml up -d docker compose -f docker-compose.proxy.yml up -d
# Setup SSL certificates using Lego # Setup SSL certificates using Lego
./scripts/setup-lego-certs.sh admin@springupsoftware.com ./scripts/setup-lego-certs.sh accounts@springupsoftware.com
# Verify certificates # Verify certificates
./scripts/lego-list-certs.sh ./scripts/lego-list-certs.sh
@ -128,8 +128,14 @@ docker compose -f docker-compose.staging.yml up -d
# Start production environment # Start production environment
docker compose -f docker-compose.production.yml up -d docker compose -f docker-compose.production.yml up -d
# Wait for services to be ready
sleep 10
# Start reverse proxy (after both environments are running) # Start reverse proxy (after both environments are running)
docker compose -f docker-compose.proxy.yml up -d docker compose -f docker-compose.proxy.yml up -d
# Or use the make command for full stack deployment
make full-stack
``` ```
### Updating Applications ### Updating Applications

View file

@ -43,4 +43,6 @@
* *
*/ */
//EOF //EOF
require_once(dirname(__FILE__) . '/php7_compat.php');
?> ?>

View file

@ -0,0 +1,96 @@
<?php
/**
* PHP 7 Compatibility layer for CakePHP 1.2.5
* Include this file in your bootstrap.php
*/
// Fix for deprecated $HTTP_RAW_POST_DATA
if (!isset($HTTP_RAW_POST_DATA)) {
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
}
// Replace deprecated ereg functions
if (!function_exists('ereg')) {
function ereg($pattern, $string, &$regs = array()) {
return preg_match('~' . $pattern . '~', $string, $regs);
}
}
if (!function_exists('eregi')) {
function eregi($pattern, $string, &$regs = array()) {
return preg_match('~' . $pattern . '~i', $string, $regs);
}
}
if (!function_exists('ereg_replace')) {
function ereg_replace($pattern, $replacement, $string) {
return preg_replace('~' . $pattern . '~', $replacement, $string);
}
}
if (!function_exists('eregi_replace')) {
function eregi_replace($pattern, $replacement, $string) {
return preg_replace('~' . $pattern . '~i', $replacement, $string);
}
}
if (!function_exists('split')) {
function split($pattern, $string, $limit = -1) {
return preg_split('~' . $pattern . '~', $string, $limit);
}
}
// Fix for mysql_* functions if needed
if (!function_exists('mysql_connect')) {
function mysql_connect($server, $username, $password, $new_link = false, $client_flags = 0) {
return mysqli_connect($server, $username, $password);
}
function mysql_select_db($database_name, $link_identifier = null) {
return mysqli_select_db($link_identifier, $database_name);
}
function mysql_query($query, $link_identifier = null) {
return mysqli_query($link_identifier, $query);
}
function mysql_fetch_array($result, $result_type = MYSQLI_BOTH) {
return mysqli_fetch_array($result, $result_type);
}
function mysql_fetch_assoc($result) {
return mysqli_fetch_assoc($result);
}
function mysql_fetch_row($result) {
return mysqli_fetch_row($result);
}
function mysql_num_rows($result) {
return mysqli_num_rows($result);
}
function mysql_affected_rows($link_identifier = null) {
return mysqli_affected_rows($link_identifier);
}
function mysql_insert_id($link_identifier = null) {
return mysqli_insert_id($link_identifier);
}
function mysql_close($link_identifier = null) {
return mysqli_close($link_identifier);
}
function mysql_error($link_identifier = null) {
return mysqli_error($link_identifier);
}
function mysql_errno($link_identifier = null) {
return mysqli_errno($link_identifier);
}
function mysql_real_escape_string($unescaped_string, $link_identifier = null) {
return mysqli_real_escape_string($link_identifier, $unescaped_string);
}
}

View file

@ -12,8 +12,7 @@ services:
volumes: volumes:
- production_pdf_data:/var/www/cmc-sales/app/webroot/pdf - production_pdf_data:/var/www/cmc-sales/app/webroot/pdf
- production_attachments_data:/var/www/cmc-sales/app/webroot/attachments_files - production_attachments_data:/var/www/cmc-sales/app/webroot/attachments_files
networks: network_mode: bridge
- cmc-production-network
restart: unless-stopped restart: unless-stopped
environment: environment:
- APP_ENV=production - APP_ENV=production
@ -39,8 +38,7 @@ services:
depends_on: depends_on:
- cmc-php-production - cmc-php-production
restart: unless-stopped restart: unless-stopped
networks: network_mode: bridge
- cmc-production-network
environment: environment:
- NGINX_ENVSUBST_TEMPLATE_SUFFIX=.template - NGINX_ENVSUBST_TEMPLATE_SUFFIX=.template
@ -57,8 +55,7 @@ services:
volumes: volumes:
- production_db_data:/var/lib/mysql - production_db_data:/var/lib/mysql
- ./backups:/backups:ro # Backup restore directory - ./backups:/backups:ro # Backup restore directory
networks: network_mode: bridge
- cmc-production-network
restart: unless-stopped restart: unless-stopped
# No external port exposure for security # No external port exposure for security
deploy: deploy:
@ -90,8 +87,7 @@ services:
volumes: volumes:
- production_pdf_data:/root/webroot/pdf - production_pdf_data:/root/webroot/pdf
- ./credentials/production:/root/credentials:ro # Production Gmail credentials - ./credentials/production:/root/credentials:ro # Production Gmail credentials
networks: network_mode: bridge
- cmc-production-network
restart: unless-stopped restart: unless-stopped
deploy: deploy:
resources: resources:
@ -107,6 +103,3 @@ volumes:
production_pdf_data: production_pdf_data:
production_attachments_data: production_attachments_data:
networks:
cmc-production-network:
driver: bridge

View file

@ -13,8 +13,7 @@ services:
volumes: volumes:
- staging_pdf_data:/var/www/cmc-sales/app/webroot/pdf - staging_pdf_data:/var/www/cmc-sales/app/webroot/pdf
- staging_attachments_data:/var/www/cmc-sales/app/webroot/attachments_files - staging_attachments_data:/var/www/cmc-sales/app/webroot/attachments_files
networks: network_mode: bridge
- cmc-staging-network
restart: unless-stopped restart: unless-stopped
environment: environment:
- APP_ENV=staging - APP_ENV=staging
@ -31,8 +30,7 @@ services:
depends_on: depends_on:
- cmc-php-staging - cmc-php-staging
restart: unless-stopped restart: unless-stopped
networks: network_mode: bridge
- cmc-staging-network
environment: environment:
- NGINX_ENVSUBST_TEMPLATE_SUFFIX=.template - NGINX_ENVSUBST_TEMPLATE_SUFFIX=.template
@ -46,8 +44,7 @@ services:
MYSQL_PASSWORD: ${DB_PASSWORD_STAGING} MYSQL_PASSWORD: ${DB_PASSWORD_STAGING}
volumes: volumes:
- staging_db_data:/var/lib/mysql - staging_db_data:/var/lib/mysql
networks: network_mode: bridge
- cmc-staging-network
restart: unless-stopped restart: unless-stopped
ports: ports:
- "3307:3306" # Different port for staging DB access - "3307:3306" # Different port for staging DB access
@ -73,8 +70,7 @@ services:
volumes: volumes:
- staging_pdf_data:/root/webroot/pdf - staging_pdf_data:/root/webroot/pdf
- ./credentials/staging:/root/credentials:ro # Staging Gmail credentials - ./credentials/staging:/root/credentials:ro # Staging Gmail credentials
networks: network_mode: bridge
- cmc-staging-network
restart: unless-stopped restart: unless-stopped
volumes: volumes:
@ -82,6 +78,3 @@ volumes:
staging_pdf_data: staging_pdf_data:
staging_attachments_data: staging_attachments_data:
networks:
cmc-staging-network:
driver: bridge