diff --git a/blueprints/appflowy/docker-compose.yml b/blueprints/appflowy/docker-compose.yml index f8429c8a..224d86b2 100644 --- a/blueprints/appflowy/docker-compose.yml +++ b/blueprints/appflowy/docker-compose.yml @@ -1,4 +1,10 @@ services: + nginx: + restart: on-failure + image: nginx + volumes: + - ../files/nginx/nginx.conf:/etc/nginx/nginx.conf + minio: restart: on-failure image: minio/minio diff --git a/blueprints/appflowy/template.toml b/blueprints/appflowy/template.toml index db93b949..34726a21 100644 --- a/blueprints/appflowy/template.toml +++ b/blueprints/appflowy/template.toml @@ -207,46 +207,255 @@ env = [ "APPFLOWY_WEB_VERSION=latest", ] -mounts = [ - "postgres_data:/var/lib/postgresql/data", - "minio_data:/data", - "letsencrypt:/etc/letsencrypt" -] - -# ============================================================================= -# 🌐 DOMAIN ROUTING CONFIGURATION -# ============================================================================= [[config.domains]] -serviceName = "appflowy_web" +serviceName = "nginx" port = 80 host = "${main_domain}" -[[config.domains]] -serviceName = "appflowy_cloud" -port = 8000 -host = "${main_domain}" -path = "/api" -[[config.domains]] -serviceName = "admin_frontend" -port = 3000 -host = "${main_domain}" -path = "/console" +[[config.mounts]] +filePath = "/nginx/nginx.conf" +content = """# Minimal nginx configuration for AppFlowy-Cloud +# Self Hosted AppFlowy Cloud user should alter this file to suit their needs, +# or use the appflowy.site.conf in external_proxy_config/nginx if they are using +# an external proxy. -[[config.domains]] -serviceName = "gotrue" -port = 9999 -host = "${main_domain}" -path = "/gotrue" +events { + worker_connections 1024; +} -[[config.domains]] -serviceName = "minio" -port = 9001 -host = "${main_domain}" -path = "/minio" +http { + # docker dns resolver + resolver 127.0.0.11 valid=10s; + #error_log /var/log/nginx/error.log debug; -[[config.domains]] -serviceName = "minio" -port = 9000 -host = "${main_domain}" -path = "/minio-api" + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + map $http_origin $cors_origin { + # AppFlowy Web origin + "~^http://localhost:3000$" $http_origin; + default "null"; + } + + server { + listen 8080; + + # https://github.com/nginxinc/nginx-prometheus-exporter + location = /stub_status { + stub_status; + } + } + + + server { + + listen 80; + client_max_body_size 10M; + + underscores_in_headers on; + set $appflowy_cloud_backend "http://appflowy_cloud:8000"; + set $gotrue_backend "http://gotrue:9999"; + set $admin_frontend_backend "http://admin_frontend:3000"; + set $appflowy_web_backend "http://appflowy_web:80"; + set $minio_backend "http://minio:9001"; + set $minio_api_backend "http://minio:9000"; + # Host name for minio, used internally within docker compose + set $minio_internal_host "minio:9000"; + set $pgadmin_backend "http://pgadmin:80"; + + # GoTrue + location /gotrue/ { + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' $cors_origin always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Headers' '*' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always; + add_header 'Access-Control-Max-Age' 3600 always; + add_header 'Content-Type' 'text/plain charset=UTF-8' always; + add_header 'Content-Length' 0 always; + return 204; + } + + proxy_pass $gotrue_backend; + + rewrite ^/gotrue(/.*)$ $1 break; + + # Allow headers like redirect_to to be handed over to the gotrue + # for correct redirecting + proxy_set_header Host $http_host; + proxy_pass_request_headers on; + } + + # WebSocket + location /ws { + proxy_pass $appflowy_cloud_backend; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_read_timeout 86400s; + } + + location /api { + proxy_pass $appflowy_cloud_backend; + proxy_set_header X-Request-Id $request_id; + proxy_set_header Host $http_host; + + # Set CORS headers for other requests + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' $cors_origin always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always; + add_header 'Access-Control-Max-Age' 3600 always; + return 204; + } + + add_header 'Access-Control-Allow-Origin' $cors_origin always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always; + add_header 'Access-Control-Max-Age' 3600 always; + + location ~* ^/api/workspace/([a-zA-Z0-9_-]+)/publish$ { + proxy_pass $appflowy_cloud_backend; + proxy_request_buffering off; + client_max_body_size 256M; + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' $cors_origin always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always; + add_header 'Access-Control-Max-Age' 3600 always; + return 204; + } + + add_header 'Access-Control-Allow-Origin' $cors_origin always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always; + add_header 'Access-Control-Max-Age' 3600 always; + } + + # AppFlowy-Cloud + location /api/chat { + proxy_pass $appflowy_cloud_backend; + + proxy_http_version 1.1; + proxy_set_header Connection ""; + chunked_transfer_encoding on; + proxy_buffering off; + proxy_cache off; + + proxy_read_timeout 600s; + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + } + + location /api/import { + proxy_pass $appflowy_cloud_backend; + + # Set headers + proxy_set_header X-Request-Id $request_id; + proxy_set_header Host $http_host; + + # Handle CORS + add_header 'Access-Control-Allow-Origin' $cors_origin always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Device-Id' always; + add_header 'Access-Control-Max-Age' 3600 always; + + # Timeouts + proxy_read_timeout 600s; + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + + # Disable buffering for large file uploads + proxy_request_buffering off; + proxy_buffering off; + proxy_cache off; + client_max_body_size 2G; + } + } + + # Minio Web UI + # Derive from: https://min.io/docs/minio/linux/integrations/setup-nginx-proxy-with-minio.html + # Optional Module, comment this section if you did not deploy minio in docker-compose.yml + # This endpoint is meant to be used for the MinIO Web UI, accessible via the admin portal + location /minio/ { + proxy_pass $minio_backend; + + rewrite ^/minio/(.*) /$1 break; + proxy_set_header Host $http_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-NginX-Proxy true; + + ## This is necessary to pass the correct IP to be hashed + real_ip_header X-Real-IP; + + proxy_connect_timeout 300s; + + ## To support websockets in MinIO versions released after January 2023 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + # Some environments may encounter CORS errors (Kubernetes + Nginx Ingress) + # Uncomment the following line to set the Origin request to an empty string + # proxy_set_header Origin ''; + + chunked_transfer_encoding off; + } + + # Optional Module, comment this section if you did not deploy minio in docker-compose.yml + # This is used for presigned url, which is needs to be exposed to the AppFlowy client application. + location /minio-api/ { + proxy_pass $minio_api_backend; + + # Set the host to internal host because the presigned url was signed against the internal host + proxy_set_header Host $minio_internal_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; + + rewrite ^/minio-api/(.*) /$1 break; + + proxy_connect_timeout 300s; + # Default is HTTP/1, keepalive is only enabled in HTTP/1.1 + proxy_http_version 1.1; + proxy_set_header Connection ""; + chunked_transfer_encoding off; + } + + # PgAdmin + # Optional Module, comment this section if you did not deploy pgadmin in docker-compose.yml + location /pgadmin/ { + set $pgadmin pgadmin; + proxy_pass $pgadmin_backend; + + proxy_set_header X-Script-Name /pgadmin; + proxy_set_header X-Scheme $scheme; + proxy_set_header Host $host; + proxy_redirect off; + } + + # Admin Frontend + # Optional Module, comment this section if you did not deploy admin_frontend in docker-compose.yml + location /console { + proxy_pass $admin_frontend_backend; + + proxy_set_header X-Scheme $scheme; + proxy_set_header Host $host; + } + + # AppFlowy Web + location / { + proxy_pass $appflowy_web_backend; + proxy_set_header X-Scheme $scheme; + proxy_set_header Host $host; + } + } + +} +"""