diff --git a/production/README.md b/production/README.md index bd440fa7..39c625ef 100644 --- a/production/README.md +++ b/production/README.md @@ -1,36 +1,136 @@ -# To run using Docker: +# Production Deployment -1. Create a file named .backend.env with the following attributes: +## Option 1: Docker (HTTP only, for local/development) -``` +1. Create a file named `.backend.env` with the following attributes: + +```bash CLIENT_ID="client_ID" # Google Auth Secret from Prerequisites CLIENT_SEC="client_SECRET" # Google Auth Secret from Prerequisites REDIRECT_URL_DEV="http://localhost:8000/auth/callback" SESSION_KEY="generate a secret key using 'openssl rand -hex 32'" FRONTEND_ORIGIN_DEV="http://localhost" # URL of the web frontend to avoid CORS errors -CONTAINER_ORIGIN="http://YOUR_CONTAINER_NAME:8080/" # Deployed taskchampion-sync-server container, default is production-syncserver-1 +CONTAINER_ORIGIN="http://production-syncserver-1:8080/" # Deployed taskchampion-sync-server container ``` -2. Run docker-compose pull to pull the CCSync images. -3. Run docker-compose up to start the project. +2. Run `docker-compose pull` to pull the CCSync images. +3. Run `docker-compose up` to start the project. 4. The frontend should now be available at localhost:80, the backend at localhost:8000, and the sync server at localhost:8080 -# To run the project using Kubernetes: +## Option 2: Docker with nginx Reverse Proxy (HTTPS, recommended for production) + +For production deployments with HTTPS, use nginx as a reverse proxy with Let's Encrypt SSL certificates. + +### Prerequisites + +- A domain name pointing to your server +- Ubuntu/Debian server with root access + +### Step 1: Install nginx and certbot + +```bash +sudo apt update +sudo apt install -y nginx certbot python3-certbot-nginx +``` + +### Step 2: Obtain SSL certificate + +```bash +sudo systemctl stop nginx +sudo certbot certonly --standalone -d your-domain.com +sudo systemctl start nginx +``` + +### Step 3: Configure nginx + +1. Copy `example.nginx.conf` to nginx sites: + ```bash + sudo cp example.nginx.conf /etc/nginx/sites-available/ccsync + ``` + +2. Edit the file and replace `your-domain.com` with your actual domain: + ```bash + sudo sed -i 's/your-domain.com/your-actual-domain.com/g' /etc/nginx/sites-available/ccsync + ``` + +3. Enable the site: + ```bash + sudo ln -s /etc/nginx/sites-available/ccsync /etc/nginx/sites-enabled/ + sudo rm -f /etc/nginx/sites-enabled/default + sudo nginx -t + sudo systemctl reload nginx + ``` + +### Step 4: Update docker-compose.yml + +Modify the port bindings to listen only on localhost (nginx will handle external traffic): -- From WSL / Linux / Git Bash (Please run as root in order to access frontend on port 80): +```yaml +services: + frontend: + ports: + - "127.0.0.1:3000:80" # Changed from "80:80" + backend: + ports: + - "127.0.0.1:8000:8000" # Bind to localhost only + + syncserver: + ports: + - "127.0.0.1:8081:8080" # Changed from "8080:8080" ``` + +### Step 5: Create environment files + +Create `.backend.env`: +```bash +CLIENT_ID="your-google-client-id" +CLIENT_SEC="your-google-client-secret" +REDIRECT_URL_DEV="https://your-domain.com/auth/callback" +SESSION_KEY="$(openssl rand -hex 32)" +FRONTEND_ORIGIN_DEV="https://your-domain.com" +CONTAINER_ORIGIN="http://syncserver:8080/" +``` + +Create `.frontend.env` (see `example.frontend.env`): +```bash +VITE_BACKEND_URL="https://your-domain.com/" +VITE_FRONTEND_URL="https://your-domain.com" +VITE_CONTAINER_ORIGIN="https://your-domain.com:8080/" +``` + +### Step 6: Configure Google OAuth + +In Google Cloud Console, add the redirect URI: +- `https://your-domain.com/auth/callback` + +### Step 7: Deploy + +```bash +docker-compose pull +docker-compose up -d +``` + +Your CCSync instance should now be available at `https://your-domain.com` + +--- + +## Option 3: Kubernetes + +From WSL / Linux / Git Bash (run as root to access frontend on port 80): + +```bash chmod +x ./run-ports.sh ./run-ports.sh ``` -- From PowerShell (Windows): +From PowerShell (Windows): -``` +```bash bash .\run-ports.sh ``` Notes: - Ensure kubectl, tmux (for managing individual pods better) and access to your cluster are configured before running the script. -- Edit production/backend-env-configmap.yaml and create secrets as needed before running the script. +- Edit `backend-env-configmap.yaml` and create secrets as needed before running the script. diff --git a/production/example.frontend.env b/production/example.frontend.env new file mode 100644 index 00000000..531824c2 --- /dev/null +++ b/production/example.frontend.env @@ -0,0 +1,3 @@ +VITE_BACKEND_URL="https://your-domain.com/" +VITE_FRONTEND_URL="https://your-domain.com" +VITE_CONTAINER_ORIGIN="https://your-domain.com:8080/" diff --git a/production/example.nginx.conf b/production/example.nginx.conf new file mode 100644 index 00000000..01dc81e6 --- /dev/null +++ b/production/example.nginx.conf @@ -0,0 +1,203 @@ +# CCSync nginx reverse proxy configuration +# For production deployment with SSL (Let's Encrypt) +# +# Prerequisites: +# 1. Install nginx: apt install nginx +# 2. Install certbot: apt install certbot python3-certbot-nginx +# 3. Obtain certificate: certbot certonly --standalone -d your-domain.com +# 4. Copy this file to /etc/nginx/sites-available/ccsync +# 5. Create symlink: ln -s /etc/nginx/sites-available/ccsync /etc/nginx/sites-enabled/ +# 6. Remove default: rm /etc/nginx/sites-enabled/default +# 7. Test config: nginx -t +# 8. Reload nginx: systemctl reload nginx +# +# Note: Update docker-compose.yml to bind ports to localhost only: +# frontend: "127.0.0.1:3000:80" +# backend: "127.0.0.1:8000:8000" +# syncserver: "127.0.0.1:8081:8080" + +# Redirect HTTP to HTTPS +server { + listen 80; + listen [::]:80; + server_name your-domain.com; + + location / { + return 301 https://$server_name$request_uri; + } +} + +# Main HTTPS server +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name your-domain.com; + + # SSL certificates (update paths for your domain) + ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; + + # SSL configuration + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + + # Backend API routes + location /auth/ { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /api/ { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /tasks { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /add-task { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /edit-task { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /modify-task { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /complete-task { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /complete-tasks { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /delete-task { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /delete-tasks { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /sync/ { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /health { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + location /docs { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # WebSocket support + location /ws { + proxy_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 86400; + } + + # Frontend (default) + location / { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} + +# Taskchampion sync server (port 8080 with SSL) +server { + listen 8080 ssl http2; + listen [::]:8080 ssl http2; + server_name your-domain.com; + + ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + + location / { + proxy_pass http://127.0.0.1:8081; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +}