diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7f4bd20 --- /dev/null +++ b/.env.example @@ -0,0 +1,74 @@ +# See more information here: https://doc.getlago.com/guide/self-hosted/docker + +# LAGO_VERSION=v1.13.1 + +# PostgreSQL Configuration +# POSTGRES_DB=lago +# POSTGRES_USER=lago +# POSTGRES_PASSWORD=changeme +# POSTGRES_HOST=db +# POSTGRES_PORT=5432 +# POSTGRES_SCHEMA=public +# PGDATA=/data/postgres + +# Redis Configuration +# REDIS_HOST=redis +# REDIS_PORT=6379 +# REDIS_PASSWORD= + +# Application Ports +# API_PORT=3000 +# FRONT_PORT=80 + +# Lago Global Configuration +# LAGO_DOMAIN=yourdomain.tld +# LAGO_API_URL=https://yourdomain.tld/api +# LAGO_FRONT_URL=https://yourdomain.tld +# SECRET_KEY_BASE=your-secret-key-base-hex-64 +# RAILS_ENV=production +# LAGO_RAILS_STDOUT=true +# LAGO_PDF_URL=http://pdf:3000 +# LAGO_DISABLE_SIGNUP=false +# APP_ENV=production + +# Encryption Keys +# LAGO_RSA_PRIVATE_KEY= +# LAGO_ENCRYPTION_PRIMARY_KEY=your-encryption-primary-key +# LAGO_ENCRYPTION_DETERMINISTIC_KEY=your-encryption-deterministic-key +# LAGO_ENCRYPTION_KEY_DERIVATION_SALT=your-encryption-derivation-salt + +# AWS S3 Configuration +# LAGO_USE_AWS_S3=false +# LAGO_AWS_S3_ACCESS_KEY_ID=azerty123456 +# LAGO_AWS_S3_SECRET_ACCESS_KEY=azerty123456 +# LAGO_AWS_S3_REGION=us-east-1 +# LAGO_AWS_S3_BUCKET=bucket +# LAGO_AWS_S3_ENDPOINT= + +# Google Cloud Storage Configuration +# LAGO_USE_GCS=false +# LAGO_GCS_PROJECT= +# LAGO_GCS_BUCKET= + +# Redis Cache Configuration +# LAGO_REDIS_CACHE_HOST=redis +# LAGO_REDIS_CACHE_PORT=6379 +# LAGO_REDIS_CACHE_PASSWORD= + +# Sentry Configuration +# SENTRY_DSN= +# SENTRY_DSN_FRONT= + +# Lago Feature Flags +# LAGO_DISABLE_SEGMENT= +# LAGO_DISABLE_WALLET_REFRESH= +# LAGO_SIDEKIQ_WEB= +# SIDEKIQ_EVENTS= +# SIDEKIQ_PDFS= + +# OAuth and Authentication +# LAGO_OAUTH_PROXY_URL=https://proxy.getlago.com +# GOOGLE_AUTH_CLIENT_ID= +# GOOGLE_AUTH_CLIENT_SECRET= +# NANGO_SECRET_KEY= +# LAGO_LICENSE= diff --git a/README.md b/README.md index 91e1638..2d8044a 100644 --- a/README.md +++ b/README.md @@ -105,43 +105,81 @@ To start using Lago, run the following commands in a shell: #### On a fresh install + +##### 1 :Clone the Repository: + ```bash # Get the code git clone --depth 1 https://github.com/getlago/lago.git # Go to Lago folder cd lago +``` -# Set up environment configuration -echo "LAGO_RSA_PRIVATE_KEY=\"`openssl genrsa 2048 | base64`\"" >> .env -source .env +##### 2 : Set Up Environment Variables: Run the following script to copy the example .env file, generate necessary keys, and populate the .env file: -# Start the api -docker compose up -d api -# Create the database -docker compose exec api rails db:create -docker compose exec api rails db:migrate +```bash +# Copy the example .env file and populate keys +cp .env.example .env +echo "SECRET_KEY_BASE=\"$(openssl rand -hex 64)\"" >> .env +echo "LAGO_RSA_PRIVATE_KEY=\"$(openssl genrsa 2048 | base64)\"" >> .env +echo "LAGO_ENCRYPTION_PRIMARY_KEY=\"$(openssl rand -base64 32)\"" >> .env +echo "LAGO_ENCRYPTION_DETERMINISTIC_KEY=\"$(openssl rand -base64 32)\"" >> .env +echo "LAGO_ENCRYPTION_KEY_DERIVATION_SALT=\"$(openssl rand -base64 32)\"" >> .env +``` + +##### 3 : Launch the API Service: -# Start all other components -docker compose up +```bash +docker compose -f docker-compose.new.yml up -d api ``` +##### 4 : Create and Migrate the Database: + +```bash +docker compose -f docker-compose.new.yml exec api rails db:create +docker compose -f docker-compose.new.yml exec api rails db:migrate +``` + +##### 5 : Launch All Services: + + +```bash +docker compose -f docker-compose.new.yml up -d +``` + + #### After an update ```bash -docker compose up +docker compose -f docker-compose.new.yml up -d +``` + +You can now open your browser and go to http://localhost to connect to the application. Lago's API is exposed at http://localhost/api. + +Note that if our docker server is not at http://localhost, the following env variables must be set: `LAGO_DOMAIN`. This may be on the command line or in your .env file. For example: + +``` +LAGO_DOMAIN=yourdomain.tld" ``` -You can now open your browser and go to http://localhost to connect to the application. Lago's API is exposed at http://localhost:3000. +##### Accessing Traefik Dashboard -Note that if our docker server is not at http://localhost, the following env variables must be set: `LAGO_API_URL`. This may be on the command line or in your .env file. For example: +The Traefik dashboard is available at http://traefik.localhost (or replace `localhost` with your custom `LAGO_DOMAIN`). For security reasons, we have configured basic authentication for accessing the Traefik dashboard. The default username is `user` and the default password is `password`. We recommand you to change it : +```shell +echo $(htpasswd -nB user) | sed -e s/\\$/\\$\\$/g ``` -LAGO_API_URL="http://192.168.122.71:3000" -LAGO_FRONT_URL="http://192.168.122.71" + +And modify line with your new value: + +```yaml + - "traefik.http.middlewares.test-auth.basicauth.users=user:$$2y$$05$$m2rFNkFDITSrY7oawkzjU.dV.69/w8FmvEaSeBFCtmYpvMar9UMGa" ``` +If you want to learn more about traefik auth [here](https://doc.traefik.io/traefik/operations/dashboard/#secure-mode) + ### Find your API key Your API Key can be found directly in the UI: diff --git a/docker-compose.new.yml b/docker-compose.new.yml new file mode 100644 index 0000000..3e150ac --- /dev/null +++ b/docker-compose.new.yml @@ -0,0 +1,244 @@ +volumes: + lago_postgres_data: + lago_redis_data: + lago_storage_data: + traefik_certificates: + traefik_config: + +x-backend-x: &backend-environment + LAGO_API_URL: ${LAGO_API_URL:-http://localhost/api} + LAGO_FRONT_URL: ${LAGO_FRONT_URL:-http://localhost} + REDIS_PASSWORD: ${REDIS_PASSWORD:-changeme} + SECRET_KEY_BASE: ${SECRET_KEY_BASE:-your-secret-key-base-hex-64} + RAILS_ENV: ${RAILS_ENV:-production} + RAILS_LOG_TO_STDOUT: ${LAGO_RAILS_STDOUT:-true} + SENTRY_DSN: ${SENTRY_DSN:-} + LAGO_RSA_PRIVATE_KEY: ${LAGO_RSA_PRIVATE_KEY} + LAGO_ENCRYPTION_PRIMARY_KEY: ${LAGO_ENCRYPTION_PRIMARY_KEY:-your-encryption-primary-key} + LAGO_ENCRYPTION_DETERMINISTIC_KEY: ${LAGO_ENCRYPTION_DETERMINISTIC_KEY:-your-encryption-deterministic-key} + LAGO_ENCRYPTION_KEY_DERIVATION_SALT: ${LAGO_ENCRYPTION_KEY_DERIVATION_SALT:-your-encryption-derivation-salt} + LAGO_USE_AWS_S3: ${LAGO_USE_AWS_S3:-false} + LAGO_AWS_S3_ACCESS_KEY_ID: ${LAGO_AWS_S3_ACCESS_KEY_ID:-azerty123456} + LAGO_AWS_S3_SECRET_ACCESS_KEY: ${LAGO_AWS_S3_SECRET_ACCESS_KEY:-azerty123456} + LAGO_AWS_S3_REGION: ${LAGO_AWS_S3_REGION:-us-east-1} + LAGO_AWS_S3_BUCKET: ${LAGO_AWS_S3_BUCKET:-bucket} + LAGO_AWS_S3_ENDPOINT: ${LAGO_AWS_S3_ENDPOINT:-http://s3.amazonaws.com} + LAGO_USE_GCS: ${LAGO_USE_GCS:-false} + LAGO_GCS_PROJECT: ${LAGO_GCS_PROJECT:-your-gcs-project} + LAGO_GCS_BUCKET: ${LAGO_GCS_BUCKET:-your-gcs-bucket} + LAGO_PDF_URL: ${LAGO_PDF_URL:-http://pdf:3000} + LAGO_REDIS_CACHE_PASSWORD: ${LAGO_REDIS_CACHE_PASSWORD:-changeme} + LAGO_DISABLE_SEGMENT: ${LAGO_DISABLE_SEGMENT:-false} + LAGO_DISABLE_WALLET_REFRESH: ${LAGO_DISABLE_WALLET_REFRESH:-false} + NANGO_SECRET_KEY: ${NANGO_SECRET_KEY:-your-nango-secret-key} + LAGO_LICENSE: ${LAGO_LICENSE:-your-lago-license} + +x-api-environment: &api-environment + <<: *backend-environment + LAGO_SIDEKIQ_WEB: ${LAGO_SIDEKIQ_WEB:-true} + LAGO_OAUTH_PROXY_URL: ${LAGO_OAUTH_PROXY_URL:-https://proxy.getlago.com} + GOOGLE_AUTH_CLIENT_ID: ${GOOGLE_AUTH_CLIENT_ID:-} + GOOGLE_AUTH_CLIENT_SECRET: ${GOOGLE_AUTH_CLIENT_SECRET:-} + +x-front-environment: &front-environment + API_URL: ${LAGO_API_URL:-http://localhost/api} + APP_ENV: ${APP_ENV:-production} + LAGO_DISABLE_SIGNUP: ${LAGO_DISABLE_SIGNUP:-false} + LAGO_OAUTH_PROXY_URL: ${LAGO_OAUTH_PROXY_URL:-https://proxy.getlago.com} + SENTRY_DSN: ${SENTRY_DSN_FRONT:-} + NANGO_SECRET_KEY: ${NANGO_SECRET_KEY:-} + + +services: + traefik: + image: traefik:v2.5 + container_name: traefik + restart: unless-stopped + command: + - "--api.insecure=false" + - "--api.dashboard=true" + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.web.address=:80" + - "--entrypoints.websecure.address=:443" + - "--certificatesresolvers.lagoresolver.acme.tlschallenge=true" + - "--certificatesresolvers.lagoresolver.acme.email=youremail@lagoddomain.tld" + - "--certificatesresolvers.lagoresolver.acme.storage=/letsencrypt/acme.json" + ports: + - "80:80" + - "443:443" + - "8080:8080" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock:ro" + - traefik_certificates:/letsencrypt + - traefik_config:/traefik + labels: + - "traefik.enable=true" + - "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.${LAGO_DOMAIN:-localhost}`)" + # SSL + #- "traefik.http.routers.traefik-dashboard.tls.certresolver=lagoresolver" + #- "traefik.http.routers.traefik-dashboard.tls=true" + #- "traefik.http.routers.traefik-dashboard.entrypoints=websecure" + - "traefik.http.routers.traefik-dashboard.entrypoints=web" + - "traefik.http.routers.traefik-dashboard.service=api@internal" + - "traefik.http.services.traefik.loadbalancer.server.port=8080" + - "traefik.http.routers.traefik-dashboard.middlewares=auth" + # username : user + # password : password + # you can change it using echo $(htpasswd -nB user) | sed -e s/\\$/\\$\\$/g + - "traefik.http.middlewares.auth.basicauth.users=user:$$2y$$05$$m2rFNkFDITSrY7oawkzjU.dV.69/w8FmvEaSeBFCtmYpvMar9UMGa" + + + + api: + container_name: lago-api + image: getlago/api:${LAGO_VERSION:-v1.15.1} + restart: unless-stopped + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + command: ['./scripts/start.sh'] + ports: + - ${API_PORT:-3000}:3000 + environment: + <<: *api-environment + DATABASE_URL: "postgresql://${POSTGRES_USER:-lago}:${POSTGRES_PASSWORD:-changeme}@${POSTGRES_HOST:-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-lago}?search_path=${POSTGRES_SCHEMA:-public}" + REDIS_URL: "redis://${REDIS_HOST:-redis}:${REDIS_PORT:-6379}" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 10s + timeout: 60s + retries: 5 + labels: + # SSL + # - "traefik.http.routers.api.tls=true" + # - "traefik.http.routers.api.tls.certresolver=lagoresolver" + # - "traefik.http.routers.api.entrypoints=websecure" + - "traefik.http.routers.api.entrypoints=web" + - "traefik.http.routers.api.priority=100" + - "traefik.enable=true" + - "traefik.http.routers.api.rule=Host(`${LAGO_DOMAIN:-localhost}`) && PathPrefix(`/api/`)" + - "traefik.http.services.api.loadbalancer.server.port=3000" + - "traefik.http.middlewares.api-strip-prefix.stripprefix.prefixes=/api/" + - "traefik.http.routers.api.middlewares=api-strip-prefix" + volumes: + - lago_storage_data:/app/storage + + front: + container_name: lago-front + image: getlago/front:${LAGO_VERSION:-v1.15.1} + restart: unless-stopped + depends_on: + api: + condition: service_healthy + environment: + <<: *front-environment + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:80"] + interval: 30s + timeout: 10s + retries: 3 + labels: + - "traefik.http.routers.front.priority=50" + - "traefik.enable=true" + # SSL + # - "traefik.http.routers.front.tls=true" + # - "traefik.http.routers.front.tls.certresolver=lagoresolver" + # - "traefik.http.routers.front.entrypoints=websecure" + - "traefik.http.routers.front.entrypoints=web" + - "traefik.http.routers.front.rule=Host(`${LAGO_DOMAIN:-localhost}`) && PathPrefix(`/`)" + - "traefik.http.services.front.loadbalancer.server.port=80" + volumes: + - lago_storage_data:/app/storage + + db: + image: postgres:14-alpine + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB:-lago} + POSTGRES_USER: ${POSTGRES_USER:-lago} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme} + PGDATA: ${PGDATA:-/data/postgres} + PGPORT: ${POSTGRES_PORT:-5432} + POSTGRES_SCHEMA: ${POSTGRES_SCHEMA:-public} + volumes: + - lago_postgres_data:/data/postgres + ports: + - "${POSTGRES_PORT:-5432}:${POSTGRES_PORT:-5432}" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-lago}"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:6-alpine + container_name: lago-redis + restart: unless-stopped + command: --port ${REDIS_PORT:-6379} + volumes: + - lago_redis_data:/data + ports: + - "${REDIS_PORT:-6379}:${REDIS_PORT:-6379}" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + api-worker: + container_name: lago-worker + image: getlago/api:${LAGO_VERSION:-v1.15.1} + restart: unless-stopped + depends_on: + api: + condition: service_healthy + command: ['./scripts/start.worker.sh'] + environment: + <<: *backend-environment + LAGO_REDIS_CACHE_URL: "redis://${LAGO_REDIS_CACHE_HOST:-redis}:${LAGO_REDIS_CACHE_PORT:-6379}" + DATABASE_URL: "postgresql://${POSTGRES_USER:-lago}:${POSTGRES_PASSWORD:-changeme}@${POSTGRES_HOST:-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-lago}?search_path=${POSTGRES_SCHEMA:-public}" + REDIS_URL: "redis://${REDIS_HOST:-redis}:${REDIS_PORT:-6379}" + healthcheck: + test: ['CMD-SHELL', 'bundle exec sidekiqmon | grep $(hostname) || exit 1'] + volumes: + - lago_storage_data:/app/storage + + api-clock: + container_name: lago-clock + image: getlago/api:${LAGO_VERSION:-v1.15.1} + restart: unless-stopped + depends_on: + api: + condition: service_healthy + command: ['./scripts/start.clock.sh'] + environment: + <<: *backend-environment + LAGO_REDIS_CACHE_URL: "redis://${LAGO_REDIS_CACHE_HOST:-redis}:${LAGO_REDIS_CACHE_PORT:-6379}" + DATABASE_URL: "postgresql://${POSTGRES_USER:-lago}:${POSTGRES_PASSWORD:-changeme}@${POSTGRES_HOST:-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-lago}?search_path=${POSTGRES_SCHEMA:-public}" + REDIS_URL: "redis://${REDIS_HOST:-redis}:${REDIS_PORT:-6379}" + + pdf: + image: getlago/lago-gotenberg:7.8.2 + + migrate: + container_name: lago-migrate + image: getlago/api:${LAGO_VERSION:-v1.15.1} + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + command: ['./scripts/start.migrate.sh'] + environment: + - RAILS_ENV=production + - SECRET_KEY_BASE=${SECRET_KEY_BASE:-your-secret-key-base-hex-64} + - RSA_PRIVATE_KEY=${LAGO_RSA_PRIVATE_KEY} + - LAGO_RSA_PRIVATE_KEY=${LAGO_RSA_PRIVATE_KEY} + - DATABASE_URL=postgresql://${POSTGRES_USER:-lago}:${POSTGRES_PASSWORD:-changeme}@${POSTGRES_HOST:-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-lago}?search_path=${POSTGRES_SCHEMA:-public} + - REDIS_URL=redis://${REDIS_HOST:-redis}:${REDIS_PORT:-6379} + - REDIS_PASSWORD=${REDIS_PASSWORD:-changeme} + volumes: + - lago_storage_data:/app/storage diff --git a/docker-compose.yml b/docker-compose.yml index 490774b..7b31ac5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,39 @@ +############################################################ +# # +# !!! WARNING !!! # +# # +# This file 'docker-compose.old.yaml' is DEPRECATED # +# and will be permanently removed from the repository # +# on January 1st, 2025. The 'docker-compose.new.yaml' # +# will then become 'docker-compose.yaml'. # +# # +# Please migrate to the new version by following # +# the steps below: # +# # +# 1. Perform a backup of the current volumes. # +# This step is mandatory! # +# # +# 2. Most variables will be set in the '.env' file # +# (see '.env.example' for more information). # +# # +# 3. Change the 'apiUrl' variable, which no longer # +# uses 'api.', but now ends with '/api' after # +# the domain name. # +# # +# 4. SSL certificate management is now automatic. # +# There's nothing more to do on this subject unless # +# you are using 'localhost'. # +# # +# 5. IMPORTANT: Only migrate to the new docker-compose # +# if you are using a recent version of the # +# application (at least v1.13.1). Otherwise, please # +# first run 'docker-compose.old.yaml' with updated # +# image versions before switching to the new one. # +# # +############################################################ + + + volumes: lago_postgres_data: lago_redis_data: