From 3a8e601a3793cdd484aa4f7ee64ccd92704ab9aa Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 5 Jun 2025 17:42:01 +0200 Subject: [PATCH] Refactor nginx setup to use host-level configuration - Remove main nginx ingress container from docker-compose - Add minimal api-nginx container for FastCGI to HTTP conversion - Expose services directly on ports 7654 (API) and 7655 (UI) - Add comprehensive NGINX_SETUP.md documentation - Include example host nginx configuration - Update docker setup script for new architecture This change allows OpnForm to integrate better with existing host nginx setups by removing the containerized ingress and exposing services directly to the host for reverse proxy configuration. --- NGINX_SETUP.md | 131 ++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 100 ++++++++++++++++++++---------- docker/api-nginx.conf | 43 +++++++++++++ nginx-host-example.conf | 67 ++++++++++++++++++++ scripts/docker-setup.sh | 7 ++- 5 files changed, 314 insertions(+), 34 deletions(-) create mode 100644 NGINX_SETUP.md create mode 100644 docker/api-nginx.conf create mode 100644 nginx-host-example.conf diff --git a/NGINX_SETUP.md b/NGINX_SETUP.md new file mode 100644 index 00000000..474e517e --- /dev/null +++ b/NGINX_SETUP.md @@ -0,0 +1,131 @@ +# OpnForm Nginx Setup Guide + +This guide explains how to set up OpnForm with a host-level nginx configuration. + +## Architecture Overview + +The modified setup removes the main nginx ingress container and exposes services directly: + +- **UI Service**: Exposed on port 7655 (HTTP) +- **API Service**: Exposed on port 7654 (HTTP via minimal nginx container) +- **Database**: PostgreSQL (internal only) +- **Redis**: Cache service (internal only) + +## Key Changes from Default Setup + +1. **Removed YAML anchors** - Each container now has its own explicit configuration to avoid conflicts +2. **Removed main ingress container** - Your host nginx handles all routing +3. **Added minimal api-nginx** - Small nginx container just to convert FastCGI to HTTP for the API +4. **Custom ports** - Using 7654-7655 range to avoid conflicts + +## Setup Steps + +### 1. Stop any existing containers + +```bash +docker compose down +docker compose -f docker-compose.dev.yml down +``` + +### 2. Run the setup script + +```bash +./scripts/docker-setup.sh +``` + +### 3. Verify services are running + +```bash +docker compose ps +``` + +You should see: +- opnform-api (healthy) +- opnform-api-nginx (healthy) +- opnform-api-worker (running) +- opnform-api-scheduler (running) +- opnform-client (healthy) +- opnform-redis (healthy) +- opnform-db (healthy) + +### 4. Configure your host nginx + +Copy the example configuration: + +```bash +sudo cp nginx-host-example.conf /etc/nginx/sites-available/forms.portnimara.dev +sudo ln -s /etc/nginx/sites-available/forms.portnimara.dev /etc/nginx/sites-enabled/ +``` + +Edit the file to adjust: +- SSL certificate paths +- Server name if different +- Any other site-specific settings + +### 5. Test nginx configuration + +```bash +sudo nginx -t +``` + +### 6. Reload nginx + +```bash +sudo systemctl reload nginx +``` + +## Troubleshooting + +### Port already in use + +If you get "port already allocated" errors: + +1. Check what's using the ports: + ```bash + sudo lsof -i :7654 + sudo lsof -i :7655 + ``` + +2. Stop conflicting services or change the ports in docker-compose.yml + +### API not responding + +1. Check the api-nginx logs: + ```bash + docker logs opnform-api-nginx + ``` + +2. Verify the API container is running: + ```bash + docker logs opnform-api + ``` + +### UI not loading + +1. Check the client logs: + ```bash + docker logs opnform-client + ``` + +2. Ensure the client/.env file has correct API URL settings + +## Port Reference + +- **7654**: API (HTTP) - proxied through api-nginx to PHP-FPM +- **7655**: UI (HTTP) - Nuxt.js frontend +- **9000**: PHP-FPM (internal only, FastCGI protocol) +- **5432**: PostgreSQL (internal only) +- **6379**: Redis (internal only) + +## Security Notes + +1. Ports are bound to 127.0.0.1 only, not exposed to external network +2. All traffic should go through your host nginx with SSL +3. The minimal api-nginx container only handles FastCGI conversion, no SSL termination + +## Default Credentials + +- Email: admin@opnform.com +- Password: password + +**Important**: Change these immediately after first login! diff --git a/docker-compose.yml b/docker-compose.yml index c1ca2438..13803b8e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,10 @@ ---- services: - api: &api-environment + api: image: jhumanj/opnform-api:latest container_name: opnform-api - volumes: &api-environment-volumes + volumes: - opnform_storage:/usr/share/nginx/html/storage:rw - environment: &api-env + environment: APP_ENV: production # Database settings DB_HOST: db @@ -25,7 +24,7 @@ services: db: condition: service_healthy redis: - condition: service_healthy # Depend on redis being healthy too + condition: service_healthy healthcheck: test: ["CMD-SHELL", "php /usr/share/nginx/html/artisan about || exit 1"] interval: 30s @@ -33,13 +32,49 @@ services: retries: 3 start_period: 60s + api-nginx: + image: nginx:alpine + container_name: opnform-api-nginx + volumes: + - ./docker/api-nginx.conf:/etc/nginx/nginx.conf:ro + - opnform_storage:/usr/share/nginx/html/storage:ro + ports: + - "127.0.0.1:7654:80" # API on port 7654 + depends_on: + - api + healthcheck: + test: ["CMD-SHELL", "wget --spider -q http://localhost/ || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + api-worker: - <<: *api-environment + image: jhumanj/opnform-api:latest container_name: opnform-api-worker command: ["php", "artisan", "queue:work"] + volumes: + - opnform_storage:/usr/share/nginx/html/storage:rw environment: - <<: *api-env APP_ENV: production + # Database settings + DB_HOST: db + REDIS_HOST: redis + DB_DATABASE: ${DB_DATABASE:-forge} + DB_USERNAME: ${DB_USERNAME:-forge} + DB_PASSWORD: ${DB_PASSWORD:-forge} + DB_CONNECTION: ${DB_CONNECTION:-pgsql} + # PHP Configuration + PHP_MEMORY_LIMIT: "1G" + PHP_MAX_EXECUTION_TIME: "600" + PHP_UPLOAD_MAX_FILESIZE: "64M" + PHP_POST_MAX_SIZE: "64M" + env_file: + - ./api/.env + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy healthcheck: test: ["CMD-SHELL", "pgrep -f 'php artisan queue:work' > /dev/null || exit 1"] interval: 60s @@ -48,22 +83,44 @@ services: start_period: 30s api-scheduler: - <<: *api-environment + image: jhumanj/opnform-api:latest container_name: opnform-api-scheduler command: ["php", "artisan", "schedule:work"] + volumes: + - opnform_storage:/usr/share/nginx/html/storage:rw environment: - <<: *api-env APP_ENV: production + # Database settings + DB_HOST: db + REDIS_HOST: redis + DB_DATABASE: ${DB_DATABASE:-forge} + DB_USERNAME: ${DB_USERNAME:-forge} + DB_PASSWORD: ${DB_PASSWORD:-forge} + DB_CONNECTION: ${DB_CONNECTION:-pgsql} + # PHP Configuration + PHP_MEMORY_LIMIT: "1G" + PHP_MAX_EXECUTION_TIME: "600" + PHP_UPLOAD_MAX_FILESIZE: "64M" + PHP_POST_MAX_SIZE: "64M" + env_file: + - ./api/.env + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy healthcheck: test: ["CMD-SHELL", "php /usr/share/nginx/html/artisan app:scheduler-status --mode=check --max-minutes=3 || exit 1"] interval: 60s timeout: 30s retries: 3 - start_period: 70s # Allow time for first scheduled run and cache write + start_period: 70s ui: image: jhumanj/opnform-client:latest container_name: opnform-client + ports: + - "127.0.0.1:7655:3000" # UI on port 7655 env_file: - ./client/.env healthcheck: @@ -95,27 +152,6 @@ services: volumes: - postgres-data:/var/lib/postgresql/data - ingress: - image: nginx:1 - container_name: opnform-ingress - volumes: - - ./docker/nginx.conf:/etc/nginx/templates/default.conf.template - ports: - - 80:80 - environment: - - NGINX_MAX_BODY_SIZE=64m - depends_on: - api: - condition: service_started - ui: - condition: service_started - healthcheck: - test: ["CMD-SHELL", "nginx -t && curl -f http://localhost/ || exit 1"] - interval: 30s - timeout: 5s - retries: 3 - start_period: 10s - volumes: postgres-data: - opnform_storage: \ No newline at end of file + opnform_storage: diff --git a/docker/api-nginx.conf b/docker/api-nginx.conf new file mode 100644 index 00000000..8294d37e --- /dev/null +++ b/docker/api-nginx.conf @@ -0,0 +1,43 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + server { + listen 80; + server_name _; + root /usr/share/nginx/html/public; + index index.php; + + client_max_body_size 64M; + + # Logging + access_log /dev/stdout; + error_log /dev/stderr; + + # Handle all requests through PHP + location / { + try_files $uri $uri/ /index.php$is_args$args; + } + + # PHP-FPM configuration + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass opnform-api:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/public/index.php; + fastcgi_param DOCUMENT_ROOT /usr/share/nginx/html/public; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_read_timeout 300; + } + + # Deny access to . files + location ~ /\. { + deny all; + } + } +} diff --git a/nginx-host-example.conf b/nginx-host-example.conf new file mode 100644 index 00000000..680dde73 --- /dev/null +++ b/nginx-host-example.conf @@ -0,0 +1,67 @@ +# Example nginx configuration for forms.portnimara.dev +# Place this in /etc/nginx/sites-available/forms.portnimara.dev +# Then create a symlink: ln -s /etc/nginx/sites-available/forms.portnimara.dev /etc/nginx/sites-enabled/ + +server { + listen 80; + server_name forms.portnimara.dev; + + # Redirect HTTP to HTTPS + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name forms.portnimara.dev; + + # SSL certificates - adjust paths as needed + ssl_certificate /etc/letsencrypt/live/forms.portnimara.dev/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/forms.portnimara.dev/privkey.pem; + + # SSL configuration + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + # Client upload size + client_max_body_size 64M; + + # Logging + access_log /var/log/nginx/forms.portnimara.dev.access.log; + error_log /var/log/nginx/forms.portnimara.dev.error.log; + + # API routes - proxy to the api-nginx container + location ~ ^/(api|open|local/temp|forms/assets)/ { + proxy_pass http://127.0.0.1:7654; + 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_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_read_timeout 300s; + proxy_connect_timeout 75s; + } + + # Everything else goes to the UI container + location / { + proxy_pass http://127.0.0.1:7655; + 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; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + + # WebSocket support for hot reload and real-time features + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_cache_bypass $http_upgrade; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; +} diff --git a/scripts/docker-setup.sh b/scripts/docker-setup.sh index 1f3b765f..ef5dc5fb 100755 --- a/scripts/docker-setup.sh +++ b/scripts/docker-setup.sh @@ -63,9 +63,12 @@ if [ "$DEV_MODE" = true ]; then else echo -e "${BLUE}Production environment setup complete!${NC}" echo -e "${YELLOW}Please wait a moment for all services to start${NC}" - echo -e "${GREEN}Then visit: http://localhost${NC}" + echo -e "${GREEN}Services are available on:${NC}" + echo -e "${GREEN}- UI: http://localhost:7655${NC}" + echo -e "${GREEN}- API: http://localhost:7654${NC}" + echo -e "${YELLOW}Note: Configure your host nginx to proxy to these ports${NC}" fi echo -e "${BLUE}Default admin credentials:${NC}" echo -e "${GREEN}Email: admin@opnform.com${NC}" -echo -e "${GREEN}Password: password${NC}" +echo -e "${GREEN}Password: password${NC}"