diff --git a/README.md b/README.md index e3ff4b7..982acb7 100644 --- a/README.md +++ b/README.md @@ -28,41 +28,47 @@ cp deploy.env .env Edit the following variables in the `.env` file to reflect your setup: ```env +# Fully qualified domain name for the deployment. +FQDN= +# Change this to https if you wish to enable TLS. +SCHEME= + # PostgreSQL Settings POSTGRES_PASSWORD= -# Supabase user settings +# Postgres credential for supabase_auth_admin SUPABASE_PASSWORD= +# Minio Host +AWS_SECRET= + +## Base Url for the admin frontend. If you use the default Nginx conf provided here, this value should be /console. +## If you want to keep the previous behaviour where admin frontend is served at the root, don't set this env variable, +## or set it to empty string. +ADMIN_FRONTEND_PATH_PREFIX= + # authentication key, change this and keep the key safe and secret # self defined key, you can use any string GOTRUE_JWT_SECRET= -# If you want to use AWS SES, set the following variables: +# If you want to use AWS SES or your own mail server, set the following variables: GOTRUE_MAILER_AUTOCONFIRM=false GOTRUE_SMTP_HOST=email-smtp.us-east-1.amazonaws.com GOTRUE_SMTP_PORT=465 GOTRUE_SMTP_USER= -GOTRUE_SMTP_PASSWORD= +GOTRUE_SMTP_PASS= GOTRUE_SMTP_ADMIN_EMAIL= -# This user will be created when AppFlowy Cloud starts successfully +# This user will be created when GoTrue starts successfully # You can use this user to login to the admin panel GOTRUE_ADMIN_EMAIL= GOTRUE_ADMIN_PASSWORD= -# Change this to your own domain where you host the docker-compose or gotrue -API_EXTERNAL_URL= - -# File Storage -# This is where storage like images, files, etc. will be stored. -# By default, Minio is used as the default file storage which uses host's file system. -APPFLOWY_S3_SECRET_KEY= - # AppFlowy Cloud Mailer Configuration (same credentials as GOTRUE_SMTP_*) APPFLOWY_MAILER_SMTP_HOST=email-smtp.us-east-1.amazonaws.com APPFLOWY_MAILER_SMTP_PORT=465 APPFLOWY_MAILER_SMTP_USERNAME= +APPFLOWY_MAILER_SMTP_EMAIL= APPFLOWY_MAILER_SMTP_PASSWORD= # PgAdmin @@ -75,26 +81,31 @@ PGADMIN_DEFAULT_PASSWORD= # Portainer (username: admin) PORTAINER_PASSWORD= -# AppFlowy AI (Optional) -APPFLOWY_AI_OPENAI_API_KEY= - -# AppFlowy Web (change localhost to your host address) -APPFLOWY_WEB_URL=http://localhost:3000 +# AppFlowy AI +AI_OPENAI_API_KEY= ``` -3. Start the services: +3. Edit GoTrue Dockerfile + +Modify the base Dockerfile from the Appflowy-Cloud repo to the one in this repo that installs bash, so the healthcheck can run. + +4. Edit nginx.conf + +Modify the base nginx.conf to the version in this repo to resolve a potential websocket DNS resolution issue. + +5. Start the services: ```bash docker-compose up -d ``` -4. Confirm that your services are running: +6. Confirm that your services are running: ```bash docker ps -a ``` -5. To enable SSL, you can use certbot: +7. To enable SSL, you can use certbot: ```bash sudo apt update @@ -103,12 +114,12 @@ sudo certbot certonly --standalone -d yourdomain.com ``` This will create SSL certificates in /etc/letsencrypt/live/yourdomain.com -6. Update your docker-compose.yml file to use these certificates by mounting the certificate and private key locations: +8. Update your docker-compose.yml file to use these certificates by mounting the certificate and private key locations: ```yaml services: nginx: - restart: on-failure + restart: always image: nginx ports: - ${NGINX_PORT:-80}:80 @@ -119,14 +130,14 @@ services: - /etc/letsencrypt/live/yourdomain.com/privkey.pem:/etc/nginx/ssl/private_key.key:ro ``` -7. Restart your services: +9. Restart your services: ```bash docker-compose down docker-compose up -d ``` -8. Set up auto renewal for the certificates with cron job: +10. Set up auto renewal for the certificates with cron job: ```bash sudo crontab -e @@ -136,3 +147,37 @@ Add this line to run the renewal daily (it will only renew if necessary): ```bash 0 3 * * * certbot renew --quiet --deploy-hook "docker-compose restart nginx" ``` + +## Additional considerations + +1. If you are getting `redis-1 | WARNING Memory overcommit must be enabled!`, run: + +```bash +echo "vm.overcommit_memory = 1" | sudo tee -a /etc/sysctl.conf +sudo sysctl -p +``` + +You can verify the current value of the setting by running: + +```bash +cat /proc/sys/vm/overcommit_memory +``` + +It should return 1 if the setting was applied successfully. + +2. Also, note that the MinIO version in the docker-compose.yml may need to be updated to reflect the latest version. This is more relevant if you've been running Appflowy for a while and want to update your installation. Make sure to backup any MinIO data you may have before upgrading, although the upgrade should preserve existing data since it's using a named volume (minio_data). + +To backup MinIO, use Docker volumes: + +```bash +docker compose stop minio +docker run --rm -v appflowy-cloud_minio_data:/data -v $(pwd):/backup alpine tar czf /backup/minio-backup.tar.gz /data +``` + +If you need to restore: + +```bash +docker compose stop minio +docker run --rm -v appflowy-cloud_minio_data:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xzf /backup/minio-backup.tar.gz --strip 1" +docker compose start minio +``` \ No newline at end of file diff --git a/deploy.env b/deploy.env index 45a3936..d8f2ea3 100644 --- a/deploy.env +++ b/deploy.env @@ -1,6 +1,16 @@ # This file is a template for docker compose deployment # Copy this file to .env and change the values as needed +# Fully qualified domain name for the deployment. Replace appflowy.localhost with your domain, +# such as mydomain.com. +FQDN=localhost +# Change this to https if you wish to enable TLS. +SCHEME=http +# If this has changed, AppFlowy Web might still use the old value due to Javascript cache. +# If AppFlowy Web is sending requests to the wrong URL, do a hard reload on the browser, +# and/or purge Cloudflare cache if you are using CloudFlare. +APPFLOWY_BASE_URL=${SCHEME}://${FQDN} + # PostgreSQL Settings POSTGRES_HOST=postgres POSTGRES_USER=postgres @@ -8,17 +18,25 @@ POSTGRES_PASSWORD=changepassword POSTGRES_PORT=5432 POSTGRES_DB=postgres -# Supabase user settings +# Postgres credential for supabase_auth_admin SUPABASE_PASSWORD=root # Redis Settings REDIS_HOST=redis REDIS_PORT=6379 +# Minio Host +MINIO_HOST=minio +MINIO_PORT=9000 + +AWS_ACCESS_KEY=minioadmin +AWS_SECRET=minioadmin + # AppFlowy Cloud ## URL that connects to the gotrue docker container APPFLOWY_GOTRUE_BASE_URL=http://gotrue:9999 -## URL that connects to the postgres docker container +## URL that connects to the postgres docker container. If your password contains special characters, instead of using ${POSTGRES_PASSWORD}, +## you will need to convert them into url encoded format. For example, `p@ssword` will become `p%40ssword`. APPFLOWY_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} APPFLOWY_ACCESS_CONTROL=true APPFLOWY_WEBSOCKET_MAILBOX_SIZE=6000 @@ -31,6 +49,12 @@ APPFLOWY_REDIS_URI=redis://${REDIS_HOST}:${REDIS_PORT} ADMIN_FRONTEND_REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} ## URL that connects to gotrue docker container ADMIN_FRONTEND_GOTRUE_URL=http://gotrue:9999 +## URL that connects to the cloud docker container +ADMIN_FRONTEND_APPFLOWY_CLOUD_URL=http://appflowy_cloud:8000 +## Base Url for the admin frontend. If you use the default Nginx conf provided here, this value should be /console. +## If you want to keep the previous behaviour where admin frontend is served at the root, don't set this env variable, +## or set it to empty string. +ADMIN_FRONTEND_PATH_PREFIX=/console # authentication key, change this and keep the key safe and secret # self defined key, you can use any string @@ -56,20 +80,19 @@ GOTRUE_SMTP_USER=email_sender@some_company.com GOTRUE_SMTP_PASS=email_sender_password GOTRUE_SMTP_ADMIN_EMAIL=comp_admin@some_company.com -# This user will be created when AppFlowy Cloud starts successfully +# This user will be created when GoTrue starts successfully # You can use this user to login to the admin panel GOTRUE_ADMIN_EMAIL=admin@example.com GOTRUE_ADMIN_PASSWORD=password -# User will be redirected to this after Email or OAuth login -# Change this to your own domain where you host the docker-compose or gotrue -# If you are using a different domain, you need to change the redirect_uri in the OAuth2 configuration -# Make sure that this domain is accessible to the user -# Make sure no endswith / -API_EXTERNAL_URL=http://your-host +# Set this to true if users can only join by invite +GOTRUE_DISABLE_SIGNUP=false -# In docker environment, `postgres` is the hostname of the postgres service -# GoTrue connect to postgres using this url +# External URL where the GoTrue service is exposed. +API_EXTERNAL_URL=${APPFLOWY_BASE_URL}/gotrue + +# GoTrue connect to postgres using this url. If your password contains special characters, +# replace ${SUPABASE_PASSWORD} with the url encoded version. For example, `p@ssword` will become `p%40ssword` GOTRUE_DATABASE_URL=postgres://supabase_auth_admin:${SUPABASE_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} # Refer to this for details: https://github.com/AppFlowy-IO/AppFlowy-Cloud/blob/main/doc/AUTHENTICATION.md @@ -77,22 +100,22 @@ GOTRUE_DATABASE_URL=postgres://supabase_auth_admin:${SUPABASE_PASSWORD}@${POSTGR GOTRUE_EXTERNAL_GOOGLE_ENABLED=false GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID= GOTRUE_EXTERNAL_GOOGLE_SECRET= -GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI=${API_EXTERNAL_URL}/gotrue/callback +GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI=${API_EXTERNAL_URL}/callback # GitHub OAuth2 GOTRUE_EXTERNAL_GITHUB_ENABLED=false GOTRUE_EXTERNAL_GITHUB_CLIENT_ID= GOTRUE_EXTERNAL_GITHUB_SECRET= -GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI=${API_EXTERNAL_URL}/gotrue/callback +GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI=${API_EXTERNAL_URL}/callback # Discord OAuth2 GOTRUE_EXTERNAL_DISCORD_ENABLED=false GOTRUE_EXTERNAL_DISCORD_CLIENT_ID= GOTRUE_EXTERNAL_DISCORD_SECRET= -GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI=${API_EXTERNAL_URL}/gotrue/callback +GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI=${API_EXTERNAL_URL}/callback # Apple OAuth2 GOTRUE_EXTERNAL_APPLE_ENABLED=false GOTRUE_EXTERNAL_APPLE_CLIENT_ID= GOTRUE_EXTERNAL_APPLE_SECRET= -GOTRUE_EXTERNAL_APPLE_REDIRECT_URI=${API_EXTERNAL_URL}/gotrue/callback +GOTRUE_EXTERNAL_APPLE_REDIRECT_URI=${API_EXTERNAL_URL}/callback # File Storage # Create the bucket if not exists on AppFlowy Cloud start up. @@ -102,11 +125,15 @@ APPFLOWY_S3_CREATE_BUCKET=true # By default, Minio is used as the default file storage which uses host's file system. # Keep this as true if you are using other S3 compatible storage provider other than AWS. APPFLOWY_S3_USE_MINIO=true -APPFLOWY_S3_MINIO_URL=http://minio:9000 # change this if you are using a different address for minio -APPFLOWY_S3_ACCESS_KEY=minioadmin -APPFLOWY_S3_SECRET_KEY=minioadmin +APPFLOWY_S3_MINIO_URL=http://${MINIO_HOST}:${MINIO_PORT} # change this if you are using a different address for minio +APPFLOWY_S3_ACCESS_KEY=${AWS_ACCESS_KEY} +APPFLOWY_S3_SECRET_KEY=${AWS_SECRET} APPFLOWY_S3_BUCKET=appflowy -#APPFLOWY_S3_REGION=us-east-1 +# Uncomment this if you are using AWS S3 +APPFLOWY_S3_REGION=us-east-1 +# Uncomment this if you are using the Minio service hosted within this docker compose file +# This is so that, the presigned URL generated by AppFlowy Cloud will use the publicly availabe minio endpoint. +APPFLOWY_S3_PRESIGNED_URL_ENDPOINT=${APPFLOWY_BASE_URL}/minio-api # AppFlowy Cloud Mailer # Note that smtps (TLS) is always required, even for ports other than 465 @@ -115,6 +142,7 @@ APPFLOWY_MAILER_SMTP_PORT=465 APPFLOWY_MAILER_SMTP_USERNAME=email_sender@some_company.com APPFLOWY_MAILER_SMTP_EMAIL=email_sender@some_company.com APPFLOWY_MAILER_SMTP_PASSWORD=email_sender_password +APPFLOWY_MAILER_SMTP_TLS_KIND=wrapper # "none" "wrapper" "required" "opportunistic" # Log level for the appflowy-cloud service RUST_LOG=info @@ -137,22 +165,22 @@ CLOUDFLARE_TUNNEL_TOKEN= NGINX_PORT=80 NGINX_TLS_PORT=443 -# If you are using a different postgres database, change the following values -# GOTRUE_DATABASE_URL=postgres://supabase_auth_admin:root@:/$POSTGRES_DB -# APPFLOWY_DATABASE_URL=postgres://POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:5432/$POSTGRES_DB - # AppFlowy AI -APPFLOWY_AI_OPENAI_API_KEY= -APPFLOWY_AI_SERVER_PORT=5001 -APPFLOWY_AI_SERVER_HOST=ai -APPFLOWY_AI_DATABASE_URL=postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} -APPFLOWY_AI_REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} -APPFLOWY_LOCAL_AI_TEST_ENABLED=false +AI_OPENAI_API_KEY= +AI_SERVER_PORT=5001 +AI_SERVER_HOST=ai +AI_DATABASE_URL=postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} +AI_REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} +LOCAL_AI_TEST_ENABLED=false +AI_APPFLOWY_BUCKET_NAME=${APPFLOWY_S3_BUCKET} +AI_APPFLOWY_HOST=${APPFLOWY_BASE_URL} +AI_MINIO_URL=http://${MINIO_HOST}:${MINIO_PORT} # AppFlowy Indexer APPFLOWY_INDEXER_ENABLED=true APPFLOWY_INDEXER_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} APPFLOWY_INDEXER_REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} +APPFLOWY_INDEXER_EMBEDDING_BUFFER_SIZE=5000 # AppFlowy Collaborate APPFLOWY_COLLABORATE_MULTI_THREAD=false @@ -161,6 +189,10 @@ APPFLOWY_COLLABORATE_REMOVE_BATCH_SIZE=100 # AppFlowy Worker APPFLOWY_WORKER_REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} APPFLOWY_WORKER_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} +APPFLOWY_WORKER_DATABASE_NAME=${POSTGRES_DB} # AppFlowy Web -APPFLOWY_WEB_URL=http://localhost:3000 \ No newline at end of file +# If your AppFlowy Web is hosted on a different domain, update this variable to the correct domain +APPFLOWY_WEB_URL=${APPFLOWY_BASE_URL} +# If you are running AppFlowy Web locally for development purpose, use the following value instead +# APPFLOWY_WEB_URL=http://localhost:3000 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 96bd7db..143ddd9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,9 +15,9 @@ services: # You do not need this if you have configured to use your own s3 file storage minio: restart: always - image: minio/minio + image: minio/minio:RELEASE.2025-03-12T18-04-18Z-cpuv1 # Update to latest version environment: - - MINIO_BROWSER_REDIRECT_URL=${API_EXTERNAL_URL}/minio + - MINIO_BROWSER_REDIRECT_URL=${APPFLOWY_BASE_URL?:err}/minio - MINIO_ROOT_USER=${APPFLOWY_S3_ACCESS_KEY:-minioadmin} - MINIO_ROOT_PASSWORD=${APPFLOWY_S3_SECRET_KEY:-minioadmin} command: server /data --console-address ":9001" @@ -33,6 +33,11 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password} - POSTGRES_HOST=${POSTGRES_HOST:-postgres} - SUPABASE_PASSWORD=${SUPABASE_PASSWORD:-root} + healthcheck: + test: [ "CMD", "pg_isready", "-U", "${POSTGRES_USER}", "-d", "${POSTGRES_DB}" ] + interval: 5s + timeout: 5s + retries: 12 volumes: - ./migrations/before:/docker-entrypoint-initdb.d - postgres_data:/var/lib/postgresql/data @@ -46,14 +51,28 @@ services: build: context: docker/gotrue dockerfile: Dockerfile + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: "curl --fail http://127.0.0.1:9999/health || exit 1" + interval: 5s + timeout: 5s + retries: 12 image: appflowyinc/gotrue:${GOTRUE_VERSION:-latest} environment: # There are a lot of options to configure GoTrue. You can reference the example config: # https://github.com/supabase/gotrue/blob/master/example.env + - GOTRUE_ADMIN_EMAIL=${GOTRUE_ADMIN_EMAIL} + - GOTRUE_ADMIN_PASSWORD=${GOTRUE_ADMIN_PASSWORD} + - GOTRUE_DISABLE_SIGNUP=${GOTRUE_DISABLE_SIGNUP:-false} - GOTRUE_SITE_URL=appflowy-flutter:// # redirected to AppFlowy application - - URI_ALLOW_LIST=* # adjust restrict if necessary + - GOTRUE_URI_ALLOW_LIST=** # adjust restrict if necessary - GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET} # authentication secret - GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP} + # Without this environment variable, the createuser command will create an admin + # with the `admin` role as opposed to `supabase_admin` + - GOTRUE_JWT_ADMIN_GROUP_NAME=supabase_admin - GOTRUE_DB_DRIVER=postgres - API_EXTERNAL_URL=${API_EXTERNAL_URL} - DATABASE_URL=${GOTRUE_DATABASE_URL} @@ -106,20 +125,29 @@ services: - APPFLOWY_S3_SECRET_KEY=${APPFLOWY_S3_SECRET_KEY} - APPFLOWY_S3_BUCKET=${APPFLOWY_S3_BUCKET} - APPFLOWY_S3_REGION=${APPFLOWY_S3_REGION} + - APPFLOWY_S3_PRESIGNED_URL_ENDPOINT=${APPFLOWY_S3_PRESIGNED_URL_ENDPOINT} - APPFLOWY_MAILER_SMTP_HOST=${APPFLOWY_MAILER_SMTP_HOST} - APPFLOWY_MAILER_SMTP_PORT=${APPFLOWY_MAILER_SMTP_PORT} - APPFLOWY_MAILER_SMTP_USERNAME=${APPFLOWY_MAILER_SMTP_USERNAME} + - APPFLOWY_MAILER_SMTP_EMAIL=${APPFLOWY_MAILER_SMTP_EMAIL} - APPFLOWY_MAILER_SMTP_PASSWORD=${APPFLOWY_MAILER_SMTP_PASSWORD} + - APPFLOWY_MAILER_SMTP_TLS_KIND=${APPFLOWY_MAILER_SMTP_TLS_KIND} - APPFLOWY_ACCESS_CONTROL=${APPFLOWY_ACCESS_CONTROL} - APPFLOWY_DATABASE_MAX_CONNECTIONS=${APPFLOWY_DATABASE_MAX_CONNECTIONS} - - APPFLOWY_AI_SERVER_HOST=${APPFLOWY_AI_SERVER_HOST} - - APPFLOWY_AI_SERVER_PORT=${APPFLOWY_AI_SERVER_PORT} + - AI_SERVER_HOST=${AI_SERVER_HOST} + - AI_SERVER_PORT=${AI_SERVER_PORT} + - AI_OPENAI_API_KEY=${AI_OPENAI_API_KEY} + - APPFLOWY_ADMIN_FRONTEND_PATH_PREFIX=${ADMIN_FRONTEND_PATH_PREFIX} + - APPFLOWY_WEB_URL=${APPFLOWY_WEB_URL} build: context: . dockerfile: Dockerfile args: FEATURES: "" image: appflowyinc/appflowy_cloud:${APPFLOWY_CLOUD_VERSION:-latest} + depends_on: + gotrue: + condition: service_healthy admin_frontend: restart: always @@ -132,15 +160,22 @@ services: - ADMIN_FRONTEND_REDIS_URL=${ADMIN_FRONTEND_REDIS_URL:-redis://redis:6379} - ADMIN_FRONTEND_GOTRUE_URL=${ADMIN_FRONTEND_GOTRUE_URL:-http://gotrue:9999} - ADMIN_FRONTEND_APPFLOWY_CLOUD_URL=${ADMIN_FRONTEND_APPFLOWY_CLOUD_URL:-http://appflowy_cloud:8000} + - ADMIN_FRONTEND_PATH_PREFIX=${ADMIN_FRONTEND_PATH_PREFIX:-} + depends_on: + appflowy_cloud: + condition: service_started ai: restart: always image: appflowyinc/appflowy_ai:${APPFLOWY_AI_VERSION:-latest} environment: - - OPENAI_API_KEY=${APPFLOWY_AI_OPENAI_API_KEY} - - APPFLOWY_AI_SERVER_PORT=${APPFLOWY_AI_SERVER_PORT} - - APPFLOWY_AI_DATABASE_URL=${APPFLOWY_AI_DATABASE_URL} - - APPFLOWY_AI_REDIS_URL=${APPFLOWY_AI_REDIS_URL} + - OPENAI_API_KEY=${AI_OPENAI_API_KEY} + - APPFLOWY_AI_SERVER_PORT=${AI_SERVER_PORT} + - APPFLOWY_AI_DATABASE_URL=${AI_DATABASE_URL} + - APPFLOWY_AI_REDIS_URL=${AI_REDIS_URL} + depends_on: + postgres: + condition: service_healthy appflowy_worker: restart: always @@ -151,9 +186,10 @@ services: environment: - RUST_LOG=${RUST_LOG:-info} - APPFLOWY_ENVIRONMENT=production - - APPFLOWY_WORKER_REDIS_URL=redis://redis:6379 + - APPFLOWY_WORKER_REDIS_URL=${APPFLOWY_WORKER_REDIS_URL:-redis://redis:6379} - APPFLOWY_WORKER_ENVIRONMENT=production - APPFLOWY_WORKER_DATABASE_URL=${APPFLOWY_WORKER_DATABASE_URL} + - APPFLOWY_WORKER_DATABASE_NAME=${APPFLOWY_WORKER_DATABASE_NAME} - APPFLOWY_WORKER_IMPORT_TICK_INTERVAL=30 - APPFLOWY_S3_USE_MINIO=${APPFLOWY_S3_USE_MINIO} - APPFLOWY_S3_MINIO_URL=${APPFLOWY_S3_MINIO_URL} @@ -161,9 +197,24 @@ services: - APPFLOWY_S3_SECRET_KEY=${APPFLOWY_S3_SECRET_KEY} - APPFLOWY_S3_BUCKET=${APPFLOWY_S3_BUCKET} - APPFLOWY_S3_REGION=${APPFLOWY_S3_REGION} + - APPFLOWY_MAILER_SMTP_HOST=${APPFLOWY_MAILER_SMTP_HOST} - APPFLOWY_MAILER_SMTP_PORT=${APPFLOWY_MAILER_SMTP_PORT} - APPFLOWY_MAILER_SMTP_USERNAME=${APPFLOWY_MAILER_SMTP_USERNAME} + - APPFLOWY_MAILER_SMTP_EMAIL=${APPFLOWY_MAILER_SMTP_EMAIL} - APPFLOWY_MAILER_SMTP_PASSWORD=${APPFLOWY_MAILER_SMTP_PASSWORD} + - APPFLOWY_MAILER_SMTP_TLS_KIND=${APPFLOWY_MAILER_SMTP_TLS_KIND} + depends_on: + postgres: + condition: service_healthy + + appflowy_web: + restart: always + image: appflowyinc/appflowy_web:${APPFLOWY_WEB_VERSION:-latest} + depends_on: + - appflowy_cloud + environment: + - AF_BASE_URL=${APPFLOWY_BASE_URL:?err} + - AF_GOTRUE_URL=${API_EXTERNAL_URL} volumes: postgres_data: minio_data: \ No newline at end of file diff --git a/docker/gotrue/Dockerfile b/docker/gotrue/Dockerfile new file mode 100644 index 0000000..f84adef --- /dev/null +++ b/docker/gotrue/Dockerfile @@ -0,0 +1,24 @@ +# syntax=docker/dockerfile:1 +FROM golang as base +WORKDIR /go/src/supabase +RUN git clone https://github.com/supabase/auth.git --depth 1 --branch v2.159.1 +WORKDIR /go/src/supabase/auth +COPY patch/mfa_enabled.patch . +RUN git apply mfa_enabled.patch +RUN CGO_ENABLED=0 go build -o /auth . +RUN rm /go/src/supabase/auth/migrations/20240612123726_enable_rls_update_grants.up.sql + +FROM alpine:3.20 +RUN adduser -D -u 1000 supabase + +# added bash so that health check could run +RUN apk add --no-cache ca-certificates curl bash +USER supabase + +COPY --from=base /auth . +COPY --from=base /go/src/supabase/auth/migrations ./migrations + +COPY start.sh . + +SHELL ["/bin/bash", "-c"] +CMD ["./start.sh"] \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..ee83ba7 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,264 @@ +# Minimal nginx configuration for AppFlowy-Cloud +# Self Hosted AppFlowy Cloud user should alter this file to suit their needs + +events { + worker_connections 1024; +} + +http { + # docker dns resolver + resolver 127.0.0.11 valid=10s; + #error_log /var/log/nginx/error.log debug; + + 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 { + ssl_certificate /etc/nginx/ssl/certificate.crt; + ssl_certificate_key /etc/nginx/ssl/private_key.key; + + listen 80; + listen 443 ssl; + 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 $appflowy_ai_backend "http://ai:5001"; + 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 $portainer_backend "http://portainer: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 { + # Add explicit resolver for Docker DNS with a longer valid time + resolver 127.0.0.11 valid=30s; + set $backend "http://appflowy_cloud:8000"; + proxy_pass $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 86400; + } + + 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; + } + } + + + # AppFlowy AI + location /ai { + proxy_pass $appflowy_ai_backend; + proxy_set_header Host $host; + proxy_pass_request_headers on; + } + + # 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 are 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 300; + + ## 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 are 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 300; + # 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 are 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; + } + + # Portainer + # Optional Module, comment this section if you are did not deploy portainer in docker-compose.yml + location /portainer/ { + proxy_pass $portainer_backend; + rewrite ^/portainer/(.*) /$1 break; + } + + # Admin Frontend + # Optional Module, comment this section if you are 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; + } + } + +} \ No newline at end of file