diff --git a/.github/workflows/chartpress.yaml b/.github/workflows/chartpress.yaml index 8c8f0e2c..e65751ed 100644 --- a/.github/workflows/chartpress.yaml +++ b/.github/workflows/chartpress.yaml @@ -4,6 +4,7 @@ on: branches: - 'main' - 'staging' + - 'disable_alb' jobs: build: runs-on: ubuntu-22.04 @@ -45,7 +46,7 @@ jobs: DEVELOPMENT_DB_EBS: ${{ secrets.STAGING_DB_EBS }} DEVELOPMENT_DB_PASSWORD: ${{ secrets.STAGING_DB_PASSWORD }} DEVELOPMENT_DB_USER: ${{ secrets.STAGING_DB_USER }} - DEVELOPMENT_DOMAIN_NAME: staging.openhistoricalmap.org + DEVELOPMENT_DOMAIN_NAME: ohmstaging.org DEVELOPMENT_ID_KEY: ${{ secrets.STAGING_ID_KEY }} DEVELOPMENT_ID_APPLICATION: ${{ secrets.STAGING_ID_APPLICATION }} DEVELOPMENT_OAUTH_CLIENT_ID: ${{ secrets.STAGING_OAUTH_CLIENT_ID }} @@ -70,7 +71,7 @@ jobs: OHM_SLACK_WEBHOOK_URL: ${{ secrets.OHM_SLACK_WEBHOOK_URL }} ################ Staging secrets ################ - name: Staging - substitute secrets - if: github.ref == 'refs/heads/staging' + if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/disable_alb' uses: bluwy/substitute-string-action@v1 with: _input-file: 'values.staging.template.yaml' @@ -87,7 +88,7 @@ jobs: STAGING_DB_EBS: ${{ secrets.STAGING_DB_EBS }} STAGING_DB_PASSWORD: ${{ secrets.STAGING_DB_PASSWORD }} STAGING_DB_USER: ${{ secrets.STAGING_DB_USER }} - STAGING_DOMAIN_NAME: staging.openhistoricalmap.org + STAGING_DOMAIN_NAME: ohmstaging.org STAGING_ID_KEY: ${{ secrets.STAGING_ID_KEY }} STAGING_ID_APPLICATION: ${{ secrets.STAGING_ID_APPLICATION }} STAGING_OAUTH_CLIENT_ID: ${{ secrets.STAGING_OAUTH_CLIENT_ID }} @@ -188,14 +189,14 @@ jobs: PRODUCTION_OPENSTREETMAP_AUTH_SECRET: ${{ secrets.PRODUCTION_OPENSTREETMAP_AUTH_SECRET }} - name: AWS Credentials - if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/disable_alb' uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - name: Setup Kubectl and Helm Dependencies - if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/disable_alb' run: | sudo pip install awscli --ignore-installed six sudo curl -L -o /usr/bin/kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.17.7/2020-07-08/bin/linux/amd64/kubectl @@ -208,24 +209,23 @@ jobs: sudo chmod +x /usr/local/bin/helm helm version - - name: Update kube-config staging - if: github.ref == 'refs/heads/staging' + - name: Update kube-config staging + if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/disable_alb' run: aws eks --region us-east-1 update-kubeconfig --name osmseed-staging - name: Update kube-config prod if: github.ref == 'refs/heads/main' run: aws eks --region us-east-1 update-kubeconfig --name osmseed-production-v2 - name: Add Helm repository - if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/disable_alb' run: | helm repo add osm-seed https://osm-seed.github.io/osm-seed-chart/ helm repo update - name: Install helm dependencies for - if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/disable_alb' run: cd ohm && helm dep up # Staging - name: Staging - helm deploy - # if: github.ref == 'refs/heads/staging' - if: github.ref == 'refs/heads/staging' + if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/disable_alb' run: helm upgrade --install staging --wait ohm/ -f values.staging.yaml -f ohm/values.yaml # Production - name: Production - helm deploy diff --git a/.gitignore b/.gitignore index 298be417..f5dbe850 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,7 @@ images/tiler-server/vtiles_languages.geojson !hetzner/*/.*.sample hetzner/*/.envs.*.production -.vscode \ No newline at end of file +.vscode +hetzner/traefik/cloudflare-ips.txt +hetzner/traefik/traefik.yml +.vscode/ diff --git a/hetzner/README.md b/hetzner/README.md index 02a2ea4a..d38235ce 100644 --- a/hetzner/README.md +++ b/hetzner/README.md @@ -58,3 +58,24 @@ This is important because this is charged to serve the site through the setup IP ```sh docker compose -f hetzner/services.yml up -d --remove-orphans --force-recreate ``` + + +## Deploy all services + +1. **Create a `.env` file** in the `hetzner/` directory with: + + - `CLOUDFLARED_TOKEN` – your Cloudflare Tunnel token (see `CLOUDFLARE_TUNNEL_SETUP.md`) + - `OHM_DOMAIN` – `openhistoricalmap.net` (staging) or `openhistoricalmap.org` (production) + - `TRAEFIK_CONFIG_PATH` – optional; defaults to `./traefik/traefik.yml` + +2. **Start routing and core services** (Traefik, Cloudflare Tunnel, node-exporter, cadvisor): + + ```sh + docker compose -f hetzner/services.yml up -d --remove-orphans --force-recreate + ``` + +3. **Deploy application services** using the startup script (updates Cloudflare IPs, generates Traefik config, and starts the services defined in the script): + + ```sh + ENVIRONMENT=staging ./hetzner/start_all.sh # or ENVIRONMENT=production + ``` diff --git a/hetzner/deploy.sh b/hetzner/deploy.sh index 2ad83cad..db2d606c 100755 --- a/hetzner/deploy.sh +++ b/hetzner/deploy.sh @@ -1,26 +1,39 @@ #!/bin/bash set -e -ACTION=$1 -SERVICE=$2 -ENVIRONMENT=${3:-staging} +# Parse --yes / -y so script can run non-interactively +AUTO_YES=false +ARGS=() +for a in "$@"; do + if [[ "$a" == "--yes" || "$a" == "-y" ]]; then + AUTO_YES=true + else + ARGS+=("$a") + fi +done + +ACTION=${ARGS[0]} +SERVICE=${ARGS[1]} +ENVIRONMENT=${ARGS[2]:-staging} # Check if first arg is an action (start/stop/restart) if [ "$ACTION" = "start" ] || [ "$ACTION" = "stop" ] || [ "$ACTION" = "restart" ]; then # First arg is an action, so SERVICE is $2 if [ -z "$SERVICE" ]; then - echo "Usage: $0 start|stop|restart [staging|production]" + echo "Usage: $0 [--yes|-y] start|stop|restart [staging|production]" echo "Example: $0 start taginfo staging" exit 1 fi fi if [ -z "$SERVICE" ]; then - echo "Usage: $0 [start|stop|restart] [staging|production]" + echo "Usage: $0 [--yes|-y] [start|stop|restart] [staging|production]" + echo " --yes, -y Skip confirmation prompts" echo "Examples:" echo " $0 taginfo staging # Start service" + echo " $0 --yes start taginfo production # Start without prompting" echo " $0 stop taginfo staging # Stop service" - echo " $0 restart taginfo staging # Restart service" + echo " $0 restart taginfo staging # Restart service" exit 1 fi @@ -28,6 +41,12 @@ SERVICE_DIR="$(cd "$(dirname "$0")" && pwd)/$SERVICE" BASE_FILE="$SERVICE_DIR/$SERVICE.base.yml" ENV_FILE="$SERVICE_DIR/$SERVICE.$ENVIRONMENT.yml" +# Load environment variables from .env.traefik +HETZNER_DIR="$(cd "$(dirname "$0")" && pwd)" +if [ -f "$HETZNER_DIR/.env.traefik" ]; then + export $(grep -v '^#' "$HETZNER_DIR/.env.traefik" | xargs) +fi + # For staging, only use base file. For production, use base + environment file if [ "$ENVIRONMENT" = "staging" ]; then COMPOSE_CMD="docker compose -f $BASE_FILE" @@ -79,20 +98,22 @@ case "$ACTION" in echo "================================================" echo "" - # Ask for confirmation, especially for production - if [ "$ENVIRONMENT" = "production" ]; then - echo "⚠️ WARNING: You are about to deploy to PRODUCTION" - echo "" - read -p "Do you want to continue? (yes/no): " confirm - if [ "$confirm" != "yes" ]; then - echo "Deployment cancelled." - exit 0 - fi - else - read -p "Do you want to continue with deployment? (yes/no): " confirm - if [ "$confirm" != "yes" ]; then - echo "Deployment cancelled." - exit 0 + # Ask for confirmation (skip if --yes/-y) + if [ "$AUTO_YES" != "true" ]; then + if [ "$ENVIRONMENT" = "production" ]; then + echo "⚠️ WARNING: You are about to deploy to PRODUCTION" + echo "" + read -p "Do you want to continue? (yes/no): " confirm + if [ "$confirm" != "yes" ]; then + echo "Deployment cancelled." + exit 0 + fi + else + read -p "Do you want to continue with deployment? (yes/no): " confirm + if [ "$confirm" != "yes" ]; then + echo "Deployment cancelled." + exit 0 + fi fi fi diff --git a/hetzner/nominatim/nominatim.base.yml b/hetzner/nominatim/nominatim.base.yml index 4f591a15..6449ba9b 100644 --- a/hetzner/nominatim/nominatim.base.yml +++ b/hetzner/nominatim/nominatim.base.yml @@ -1,6 +1,6 @@ services: nominatim: - container_name: nominatim_staging + container_name: nominatim image: developmentseed/osmseed-nominatim:0.1.0-0.dev.git.956.h49d677b volumes: - nominatim_data:/var/lib/postgresql/16/main @@ -16,13 +16,12 @@ services: IMPORT_GB_POSTCODES: false IMPORT_TIGER_ADDRESSES: false PGDATA: /var/lib/postgresql/16/main - OSMSEED_WEB_API_DOMAIN: www.openhistoricalmap.org + OSMSEED_WEB_API_DOMAIN: www.${OHM_DOMAIN} NOMINATIM_ADDRESS_LEVEL_CONFIG_URL: https://raw.githubusercontent.com/OpenHistoricalMap/nominatim-ui/master/address-levels.json UPDATE_MODE: continuous IMPORT_STYLE: extratags EXTRA_TAGS: "start_date,start_date:edtf,end_date,end_date:edt" restart: always - cpus: 4.0 healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:8080/status || exit 1"] interval: 30s @@ -31,20 +30,20 @@ services: start_period: 120m networks: - ohm_network - ports: - - '8081:8080' + # ports: + # - '8081:8080' env_file: - - ./.env.sample + - ./.env.nominatim nominatim_ui: - container_name: nominatim_ui_staging + container_name: nominatim_ui image: ghcr.io/openhistoricalmap/nominatim-ui:a469b5e command: - /bin/sh - -c - | set -x && \ - echo "Nominatim_Config.Nominatim_API_Endpoint = 'https://nominatim.staging.openhistoricalmap.org/';" \ + echo "Nominatim_Config.Nominatim_API_Endpoint = 'https://nominatim.${OHM_DOMAIN}/';" \ >> /usr/share/nginx/html/ui/theme/config.theme.js && \ nginx -g "daemon off;" restart: always @@ -56,8 +55,8 @@ services: start_period: 20s networks: - ohm_network - ports: - - '8082:80' + # ports: + # - '8082:80' networks: ohm_network: @@ -65,5 +64,5 @@ networks: volumes: nominatim_data: - driver: local - name: nominatim_db + name: nominatim_db_18_07_2025 + diff --git a/hetzner/nominatim/nominatim.production.yml b/hetzner/nominatim/nominatim.production.yml index a02e4acf..553ad949 100644 --- a/hetzner/nominatim/nominatim.production.yml +++ b/hetzner/nominatim/nominatim.production.yml @@ -1,24 +1,6 @@ services: nominatim: - container_name: nominatim_production - ports: !override - - '8083:8080' mem_limit: 16g - env_file: - - ./.env.nominatim.production + cpus: 4.0 + - nominatim_ui: - container_name: nominatim_ui_production - ports: !override - - '8084:80' - command: - - /bin/sh - - -c - - | - set -x && \ - echo "Nominatim_Config.Nominatim_API_Endpoint = 'https://nominatim.openhistoricalmap.org/';" \ - >> /usr/share/nginx/html/ui/theme/config.theme.js && \ - nginx -g "daemon off;" -volumes: - nominatim_data: - name: nominatim_db_18_07_2025 \ No newline at end of file diff --git a/hetzner/osmcha/.env.sample b/hetzner/osmcha/.env.sample index 48d6b2a0..f3faa18b 100644 --- a/hetzner/osmcha/.env.sample +++ b/hetzner/osmcha/.env.sample @@ -19,11 +19,11 @@ DJANGO_SECURE_SSL_REDIRECT=False DJANGO_SETTINGS_MODULE=config.settings.production OAUTH2_OSM_KEY=12345678 OAUTH2_OSM_SECRET=xyzasdesd -OAUTH_REDIRECT_URI=https://osmcha.staging.openhistoricalmap.org/authorized +OAUTH_REDIRECT_URI=https://${OHM_DOMAIN}/authorized OSMCHA_API_URL=www.staging.openhistoricalmap.org OSMCHA_FRONTEND_VERSION=v0.86.0-production -OSMCHA_URL=https://osmcha.staging.openhistoricalmap.org +OSMCHA_URL=https://${OHM_DOMAIN} OSM_PLANET_BASE_URL=https://s3.amazonaws.com/planet.openhistoricalmap.org/replication/changesets/ -OSM_SERVER_URL=https://www.staging.openhistoricalmap.org +OSM_SERVER_URL=https://www.${OHM_DOMAIN} REDIS_URL=redis://osmcha_redis:6379 diff --git a/hetzner/osmcha/osmcha.base.yml b/hetzner/osmcha/osmcha.base.yml index 2258c485..1f0b050e 100644 --- a/hetzner/osmcha/osmcha.base.yml +++ b/hetzner/osmcha/osmcha.base.yml @@ -1,9 +1,9 @@ services: osmcha-db: image: postgis/postgis:17-3.5 - container_name: osmcha-db_staging + container_name: osmcha-db restart: always - env_file: ./.env.osmcha.staging + env_file: ./.env.osmcha volumes: - osmcha_db_data:/var/lib/postgresql/data - ./data_backup:/data_backup @@ -17,20 +17,20 @@ services: - ohm_network osmcha-redis: - container_name: osmcha-redis_staging + container_name: osmcha-redis image: redis:latest networks: - ohm_network osmcha-init: - container_name: osmcha-init_staging + container_name: osmcha-init image: ghcr.io/openhistoricalmap/osmcha-django:1bd58e1 command: > sh -c " python manage.py migrate && python manage.py collectstatic --noinput " - env_file: ./.env.osmcha.staging + env_file: ./.env.osmcha volumes: - staticfiles:/app/staticfiles depends_on: @@ -42,12 +42,12 @@ services: - ohm_network osmcha-api: - container_name: osmcha-api_staging + container_name: osmcha-api image: ghcr.io/openhistoricalmap/osmcha-django:1bd58e1 command: gunicorn config.wsgi -b 0.0.0.0:5000 --access-logfile - --timeout 120 --workers 4 --threads 16 # ports: # - "5100:5000" - env_file: ./.env.osmcha.staging + env_file: ./.env.osmcha volumes: - staticfiles:/staticfiles depends_on: @@ -58,8 +58,8 @@ services: networks: - ohm_network - frontend-nginx: - container_name: frontend-nginx_staging + osmcha-web: + container_name: osmcha-web image: ghcr.io/openhistoricalmap/osmcha-frontend:df5e9b5 # ports: # - "8100:80" @@ -74,9 +74,9 @@ services: - ohm_network osmcha-cron: - container_name: osmcha-cron_staging + container_name: osmcha-cron image: ghcr.io/openhistoricalmap/osmcha-django:1bd58e1 - env_file: ./.env.osmcha.staging + env_file: ./.env.osmcha depends_on: osmcha-db: condition: service_healthy @@ -89,42 +89,16 @@ services: - ${PWD}/hetzner/osmcha/script/update.sh:/app/update.sh command: sh /app/update.sh - osmcha_ohmx_adiff: - image: ghcr.io/openhistoricalmap/ohmx-adiff-builder:0.0.1-0.dev.git.3256.h4c03a3a - container_name: osmcha_ohmx_adiff_staging - environment: - - API_URL=https://api.openhistoricalmap.org - - PLANET_PBF_URL=https://s3.amazonaws.com/planet.openhistoricalmap.org/planet/planet-260106_0350.osm.pbf - - MINUTE_REPLICATION_URL=https://planet.openhistoricalmap.org/?prefix=replication/minute/ - # Add OSMX_INITIAL_SEQNUM to start from a specific sequence number - # - OSMX_INITIAL_SEQNUM=1881020 - - AWS_S3_BUCKET=planet.openhistoricalmap.org - env_file: - - ./.env.osmcha.staging - restart: always - volumes: - - ohmx_db:/data - - ${HOME}/.aws:/root/.aws:ro - - /production/services/images/ohmx-adiff-builder/config.sh:/app/config.sh - - /production/services/images/ohmx-adiff-builder/functions.sh:/app/functions.sh - - /production/services/images/ohmx-adiff-builder/start.sh:/app/start.sh - - /production/services/images/ohmx-adiff-builder/process_min_range.sh:/app/process_min_range.sh - - /production/services/images/ohmx-adiff-builder/update.sh:/app/update.sh - networks: - - ohm_network - cpus: '8.0' - mem_limit: 20G - volumes: osmcha_db_data: driver: local - name: osmcha_db + name: osmcha_db_11102025 staticfiles: driver: local - name: osmcha_staticfiles + name: osmcha_staticfiles_11102025 ohmx_db: driver: local - name: ohmx_db_staging + name: ohmx_db_production_06012026 networks: ohm_network: diff --git a/hetzner/osmcha/osmcha.production.yml b/hetzner/osmcha/osmcha.production.yml index da5e29c8..1b3ff63b 100644 --- a/hetzner/osmcha/osmcha.production.yml +++ b/hetzner/osmcha/osmcha.production.yml @@ -1,40 +1,26 @@ services: - osmcha-db: - container_name: osmcha-db_production - env_file: !override - - ./.env.osmcha.production - osmcha-redis: - container_name: osmcha-redis_production - - osmcha-init: - container_name: osmcha-init_production - env_file: !override - - ./.env.osmcha.production - osmcha-api: - container_name: osmcha-api_production - env_file: !override - - ./.env.osmcha.production - - frontend-nginx: - container_name: frontend-nginx_production - - osmcha-cron: - container_name: osmcha-cron_production - env_file: !override - - ./.env.osmcha.production - osmcha_ohmx_adiff: - container_name: osmcha_ohmx_adiff_production - env_file: !override - - ./.env.osmcha.production - -volumes: - osmcha_db_data: - driver: local - name: osmcha_db_11102025 - staticfiles: - driver: local - name: osmcha_staticfiles_11102025 - ohmx_db: - driver: local - name: ohmx_db_production_06012026 + image: ghcr.io/openhistoricalmap/ohmx-adiff-builder:0.0.1-0.dev.git.3256.h4c03a3a + container_name: osmcha_ohmx_adiff + environment: + - API_URL=https://api.${OHM_DOMAIN} + - PLANET_PBF_URL=https://s3.amazonaws.com/planet.openhistoricalmap.org/planet/planet-260106_0350.osm.pbf + - MINUTE_REPLICATION_URL=https://planet.openhistoricalmap.org/?prefix=replication/minute/ + # Add OSMX_INITIAL_SEQNUM to start from a specific sequence number + # - OSMX_INITIAL_SEQNUM=1881020 + - AWS_S3_BUCKET=planet.openhistoricalmap.org + env_file: + - ./.env.osmcha + restart: always + volumes: + - ohmx_db:/data + - ${HOME}/.aws:/root/.aws:ro + - /production/services/images/ohmx-adiff-builder/config.sh:/app/config.sh + - /production/services/images/ohmx-adiff-builder/functions.sh:/app/functions.sh + - /production/services/images/ohmx-adiff-builder/start.sh:/app/start.sh + - /production/services/images/ohmx-adiff-builder/process_min_range.sh:/app/process_min_range.sh + - /production/services/images/ohmx-adiff-builder/update.sh:/app/update.sh + networks: + - ohm_network + cpus: '8.0' + mem_limit: 20G \ No newline at end of file diff --git a/hetzner/overpass/overpass.base.yml b/hetzner/overpass/overpass.base.yml index e90592d6..073499c0 100644 --- a/hetzner/overpass/overpass.base.yml +++ b/hetzner/overpass/overpass.base.yml @@ -1,15 +1,15 @@ services: overpass: image: developmentseed/osmseed-overpass-api:0.1.0-0.dev.git.956.h49d677b - container_name: overpass_staging - ports: - - '8085:80' + container_name: overpass + # ports: + # - '8085:80' volumes: - overpass_data:/db environment: OVERPASS_META: attic OVERPASS_MODE: init - OVERPASS_PLANET_URL: https://s3.amazonaws.com/planet.openhistoricalmap.org/planet/planet-250731_0101.osm.pbf + OVERPASS_PLANET_URL: https://s3.amazonaws.com/planet.openhistoricalmap.org/planet/planet-260210_0334.osm.pbf OVERPASS_DIFF_URL: http://s3.amazonaws.com/planet.openhistoricalmap.org/replication/minute OVERPASS_RULES_LOAD: 10 OVERPASS_PLANET_PREPROCESS: "mv /db/planet.osm.bz2 /db/planet.osm.pbf && osmium cat -o /db/planet.osm.bz2 /db/planet.osm.pbf && rm /db/planet.osm.pbf" @@ -27,8 +27,6 @@ services: timeout: 30s retries: 3 start_period: 180m - mem_limit: 16g - cpus: 8 networks: - ohm_network @@ -39,5 +37,5 @@ networks: volumes: overpass_data: driver: local - name: overpass_db + name: overpass_db_11_02_2026_v4 diff --git a/hetzner/overpass/overpass.production.yml b/hetzner/overpass/overpass.production.yml index 86d46bc6..da6272ae 100644 --- a/hetzner/overpass/overpass.production.yml +++ b/hetzner/overpass/overpass.production.yml @@ -1,10 +1,4 @@ services: overpass: - container_name: overpass_production - ports: !override - - '8086:80' - -volumes: - overpass_data: - name: overpass_db_31_07_2025_v3 - \ No newline at end of file + mem_limit: 16g + cpus: "8.0" diff --git a/hetzner/services.yml b/hetzner/services.yml index f1f4adaa..ce964341 100644 --- a/hetzner/services.yml +++ b/hetzner/services.yml @@ -1,3 +1,15 @@ +# ================================================== +# OHM Deploy - Core Services Configuration +# ================================================== +# +# IMPORTANT: Create a .env file in this directory with: +# - CLOUDFLARED_TOKEN=your_cloudflare_tunnel_token +# - OHM_DOMAIN=openhistoricalmap.net (or .org for production) +# - TRAEFIK_CONFIG_PATH=./traefik/traefik.yml +# +# See CLOUDFLARE_TUNNEL_SETUP.md for instructions +# ================================================== + services: traefik: image: docker.io/traefik:3.4.3 @@ -6,19 +18,31 @@ services: - "host.docker.internal:host-gateway" command: - --configFile=/etc/traefik/traefik.yml - ports: - - "0.0.0.0:80:80" - - "0.0.0.0:443:443" + # Ports NOT exposed publicly - Cloudflare Tunnel handles external access + # ports: + # - "0.0.0.0:80:80" + # - "0.0.0.0:443:443" volumes: - - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro + # Config file path - uses TRAEFIK_CONFIG_PATH from .env or defaults to traefik.yml + - ${TRAEFIK_CONFIG_PATH:-./traefik/traefik.yml}:/etc/traefik/traefik.yml:ro - traefik_acme:/etc/traefik/acme restart: unless-stopped networks: - ohm_network + cloudflared: + image: cloudflare/cloudflared:latest + container_name: cloudflared-ohm + restart: unless-stopped + command: tunnel --no-autoupdate run --token ${CLOUDFLARED_TOKEN} + env_file: + - .env.traefik + networks: + - ohm_network + node_exporter: image: quay.io/prometheus/node-exporter - container_name: node-exporter + container_name: node_exporter restart: always ports: - "9100:9100" diff --git a/hetzner/start_all.sh b/hetzner/start_all.sh new file mode 100755 index 00000000..e29b9378 --- /dev/null +++ b/hetzner/start_all.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +ENVIRONMENT=${ENVIRONMENT:-staging} + +# Load environment variables from .env.traefik +source "$SCRIPT_DIR/.env.traefik" +echo "########################## OHM_DOMAIN -> $OHM_DOMAIN ##########################" + +# ###################### Tiler ###################### +./hetzner/deploy.sh start tiler $ENVIRONMENT -y + +# ###################### Osmcha ###################### +./hetzner/deploy.sh start osmcha $ENVIRONMENT -y + +# ###################### Nominatim ###################### +./hetzner/deploy.sh start nominatim $ENVIRONMENT -y + +# ###################### Overpass #################### +./hetzner/deploy.sh start overpass $ENVIRONMENT -y + +#################### Taginfo #################### +./hetzner/deploy.sh start taginfo $ENVIRONMENT -y + +#################### Traefik #################### + +###### Update Cloudflare IPs and generate config file from template +cd "$SCRIPT_DIR/traefik" && ./update-cloudflare-ips.sh && cd "$SCRIPT_DIR" + +docker compose -f $SCRIPT_DIR/services.yml --env-file $SCRIPT_DIR/.env.traefik up -d --force-recreate + +## Stop services that is not requiered for staging +if [ "$ENVIRONMENT" = "staging" ]; then + docker stop node_exporter + docker stop cadvisor + docker stop tiler_db + docker stop tiler_imposm +fi diff --git a/hetzner/taginfo/.env.sample b/hetzner/taginfo/.env.sample index 7470df65..786e3d56 100644 --- a/hetzner/taginfo/.env.sample +++ b/hetzner/taginfo/.env.sample @@ -11,4 +11,3 @@ ENVIRONMENT=production INTERVAL_DOWNLOAD_DATA=7d FETCH_DB_FILES=false TAGINFO_DB_BASE_URL=https://planet.openhistoricalmap.org.s3.amazonaws.com/taginfo -AWS_S3_BUCKET=planet.openhistoricalmap.org diff --git a/hetzner/taginfo/taginfo.base.yml b/hetzner/taginfo/taginfo.base.yml index d967d93e..2079e261 100644 --- a/hetzner/taginfo/taginfo.base.yml +++ b/hetzner/taginfo/taginfo.base.yml @@ -1,18 +1,6 @@ services: - taginfo_data: - container_name: taginfo_data_staging - image: ghcr.io/osm-seed/taginfo:0.1.0-0.dev.git.989.h60325d0 - volumes: - - ${HOME}/data/staging/taginfo_data:/usr/src/app/data - - ${HOME}/.aws:/root/.aws:ro - env_file: - - ./.env.sample - restart: always - networks: - - ohm_network - taginfo_web: - container_name: taginfo_web_staging + container_name: taginfo_web image: ghcr.io/osm-seed/taginfo-web:0.1.0-0.dev.git.983.h32750c8 volumes: - ${HOME}/data/staging/taginfo_data:/usr/src/app/data @@ -24,4 +12,4 @@ services: networks: ohm_network: - external: true + external: true \ No newline at end of file diff --git a/hetzner/taginfo/taginfo.production.yml b/hetzner/taginfo/taginfo.production.yml index 00504f85..b3b518a7 100644 --- a/hetzner/taginfo/taginfo.production.yml +++ b/hetzner/taginfo/taginfo.production.yml @@ -1,9 +1,10 @@ services: taginfo_data: - container_name: taginfo_data_producion volumes: - ${HOME}/data/production/taginfo_data:/usr/src/app/data - ${HOME}/data/production/taginfo_data_planet:/osm/planet/var + environment: + - AWS_S3_BUCKET=planet.openhistoricalmap.org command: - /bin/bash - -lc @@ -14,8 +15,7 @@ services: crontab /tmp/cron_logs touch /var/log/taginfo-cron.log cron -f - + taginfo_web: - container_name: taginfo_production volumes: - - ${HOME}/data/production/taginfo_data_proceced:/usr/src/app/data + - ${HOME}/data/production/taginfo_data_proceced:/usr/src/app/data \ No newline at end of file diff --git a/hetzner/tiler/README.md b/hetzner/tiler/README.md index bdced5db..7c7bbcd6 100644 --- a/hetzner/tiler/README.md +++ b/hetzner/tiler/README.md @@ -9,32 +9,36 @@ Ensure you are using the correct Docker images for production deployment, https: 📌 Deploy Production Services -```sh -docker compose -f hetzner/tiler/tiler.production.yml up -d -# docker compose -f hetzner/tiler/tiler.production.yml up db_production -d --force-recreate -# docker compose -f hetzner/tiler/tiler.production.yml up imposm_production -d --force-recreate -# docker compose -f hetzner/tiler/tiler.production.yml up tiler_server_production -d --force-recreate -# docker compose -f hetzner/tiler/tiler.production.yml up tiler_sqs_cleaner_production -d --force-recreate -# docker compose -f hetzner/tiler/tiler.production.yml up tile_global_seeding_production -d --force-recreate -# docker compose -f hetzner/tiler/tiler.production.yml up tile_coverage_seeding_production -d --force-recreate -# docker compose -f hetzner/tiler/tiler.production.yml run tiler_s3_cleaner_production tiler-cache-cleaner clean_by_prefix -# docker compose -f hetzner/tiler/tiler.production.yml up tiler_monitor_production -d --force-recreate -``` 🛠 Deploying to Staging To deploy the staging environment, use the following commands: ```sh -docker compose -f hetzner/tiler/tiler.staging.yml up -d -# docker compose -f hetzner/tiler/tiler.staging.yml up db_staging -d --force-recreate -# docker compose -f hetzner/tiler/tiler.staging.yml up imposm_staging -d --force-recreate -# docker compose -f hetzner/tiler/tiler.staging.yml up tiler_server_staging -d --force-recreate -# docker compose -f hetzner/tiler/tiler.staging.yml up tiler_sqs_cleaner_staging -d --force-recreate -# docker compose -f hetzner/tiler/tiler.staging.yml run tiler_s3_cleaner_staging tiler-cache-cleaner clean_by_prefix -# docker compose -f hetzner/tiler/tiler.staging.yml up tiler_monitor_staging -d --force-recreate +docker compose -f hetzner/tiler/tiler.base.yml up -d +# docker compose -f hetzner/tiler/tiler.base.yml up tiler_db -d --force-recreate +# docker compose -f hetzner/tiler/tiler.base.yml up tiler_imposm -d --force-recreate +# docker compose -f hetzner/tiler/tiler.base.yml up tiler_server -d --force-recreate +# docker compose -f hetzner/tiler/tiler.base.yml up tiler_sqs_cleaner -d --force-recreate +# docker compose -f hetzner/tiler/tiler.base.yml run tiler_s3_cleaner tiler-cache-cleaner clean_by_prefix +# docker compose -f hetzner/tiler/tiler.base.yml up tiler_monitor -d --force-recreate +``` + + +```sh +docker compose -f hetzner/tiler/tiler.base.yml -f hetzner/tiler/tiler.production.yml up -d +# docker compose -f hetzner/tiler/tiler.base.yml -f hetzner/tiler/tiler.production.yml up db_production -d --force-recreate +# docker compose -f hetzner/tiler/tiler.base.yml -f hetzner/tiler/tiler.production.yml up imposm_production -d --force-recreate +# docker compose -f hetzner/tiler/tiler.base.yml -f hetzner/tiler/tiler.production.yml up tiler_server_production -d --force-recreate +# docker compose -f hetzner/tiler/tiler.base.yml -f hetzner/tiler/tiler.production.yml up tiler_sqs_cleaner_production -d --force-recreate +# docker compose -f hetzner/tiler/tiler.base.yml -f hetzner/tiler/tiler.production.yml up tile_global_seeding_production -d --force-recreate +# docker compose -f hetzner/tiler/tiler.base.yml -f hetzner/tiler/tiler.production.yml up tile_coverage_seeding_production -d --force-recreate +# docker compose -f hetzner/tiler/tiler.base.yml -f hetzner/tiler/tiler.production.yml run tiler_s3_cleaner_production tiler-cache-cleaner clean_by_prefix +# docker compose -f hetzner/tiler/tiler.base.yml -f hetzner/tiler/tiler.production.yml up tiler_monitor_production -d --force-recreate ``` + + 📌 Notes • Ensure that you are using the correct Docker images for each environment. • Manually update the images before deploying production services. diff --git a/hetzner/tiler/tiler.base.yml b/hetzner/tiler/tiler.base.yml new file mode 100644 index 00000000..f5784eb9 --- /dev/null +++ b/hetzner/tiler/tiler.base.yml @@ -0,0 +1,77 @@ +services: + tiler_db: + container_name: tiler_db + image: ghcr.io/openhistoricalmap/tiler-db:0.0.1-0.dev.git.2166.hc55c4cd + volumes: + - tiler_pgdata:/var/lib/postgresql/data + - ./config/postgresql.staging.conf:/etc/postgresql/postgresql.conf + environment: + - PGDATA=/var/lib/postgresql/data + - POSTGRES_CONFIG_FILE=/etc/postgresql/postgresql.conf + command: + - postgres + - "-c" + - "config_file=/etc/postgresql/postgresql.conf" + ports: + - "54321:5432" + env_file: + - .env.tiler + networks: + - ohm_network + + tiler_imposm: + container_name: tiler_imposm + image: ghcr.io/openhistoricalmap/tiler-imposm:0.0.1-0.dev.git.3208.h6ef73bb + # image: tiler-imposm:staging + # build: + # context: ../../images/tiler-imposm + # dockerfile: Dockerfile + volumes: + - tiler_imposm_data:/mnt/data + command: + - sh + - -c + - | + ./start.sh + env_file: + - .env.tiler + restart: always + networks: + - ohm_network + + tiler_server: + container_name: tiler_server + image: ghcr.io/openhistoricalmap/tiler-server:0.0.1-0.dev.git.3225.h2122f3e + # image: tiler-server:staging + # build: + # context: ../../images/tiler-server + # dockerfile: Dockerfile + # ports: + # - "9090:9090" + env_file: + - .env.tiler + restart: always + # volumes: + # - ../../images/tiler-server/start.sh:/app/start.sh:ro + networks: + - ohm_network + +# volumes: +# tiler_pgdata: +# driver: local +# driver_opts: +# type: none +# o: bind +# device: /mnt/HC_Volume_104590709/staging/tiler/pgdata + +# tiler_imposm_data: +# driver: local +# driver_opts: +# type: none +# o: bind +# device: /mnt/HC_Volume_104590709/staging/tiler/imposmdata + +networks: + ohm_network: + external: true + \ No newline at end of file diff --git a/hetzner/tiler/tiler.production.yml b/hetzner/tiler/tiler.production.yml index 42ba08b3..f049ead2 100644 --- a/hetzner/tiler/tiler.production.yml +++ b/hetzner/tiler/tiler.production.yml @@ -1,34 +1,18 @@ services: - # Tiler db - db_production: - container_name: tiler_db_production + tiler_db: image: ghcr.io/openhistoricalmap/tiler-db:0.0.1-0.dev.git.2166.hc55c4cd - volumes: - - tiler_production_pgdata:/var/lib/postgresql/data + volumes: !overwrite + - tiler_pgdata:/var/lib/postgresql/data - ./config/postgresql.production.conf:/etc/postgresql/postgresql.conf - environment: - - PGDATA=/var/lib/postgresql/data - - POSTGRES_CONFIG_FILE=/etc/postgresql/postgresql.conf - command: ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf"] ports: - "54329:5432" - env_file: - - .env.production - restart: always - networks: - - ohm_network - deploy: - resources: - limits: - cpus: '28.0' - memory: 55G - - # imposm3 - imposm_production: - container_name: tiler_imposm_production + mem_limit: 55G + cpus: "28.0" + + tiler_imposm: image: ghcr.io/openhistoricalmap/tiler-imposm:0.0.1-0.dev.git.3208.h6ef73bb volumes: - - tiler_production_imposmdata:/mnt/data + - tiler_imposm_data:/mnt/data command: - sh - -c @@ -39,32 +23,27 @@ services: sleep 60 done & ./start.sh - env_file: - - .env.production restart: always networks: - ohm_network - # Tiler server - tiler_server_production: - container_name: tiler_server_production + tiler_server: + container_name: tiler_server image: ghcr.io/openhistoricalmap/tiler-server:0.0.1-0.dev.git.3225.h2122f3e ports: - "9090:9090" - env_file: - - .env.production restart: always networks: - ohm_network - tiler_sqs_cleaner_production: - container_name: tiler_sqs_cleaner_production + tiler_sqs_cleaner: + container_name: tiler_sqs_cleaner image: ghcr.io/openhistoricalmap/tiler-cache:0.0.1-0.dev.git.3245.hffaf924 - env_file: - - .env.production environment: - PORT=8000 + env_file: + - .env.tiler command: - /bin/sh - -c @@ -81,39 +60,32 @@ services: networks: - ohm_network - tiler_s3_cleaner_production: - container_name: tiler_s3_cleaner_production + tiler_s3_cleaner: + container_name: tiler_s3_cleaner image: ghcr.io/openhistoricalmap/tiler-cache:0.0.1-0.dev.git.3245.hffaf924 env_file: - - .env.production + - .env.tiler networks: - ohm_network - tile_global_seeding_production: - container_name: tiler_global_seeding_production - image: ghcr.io/openhistoricalmap/tiler-server:0.0.1-0.dev.git.3225.h2122f3e - env_file: - - .env.production - volumes: - - ./seed.sh:/opt/seed.sh - entrypoint: - - /bin/bash - - "-c" - - | - /opt/seed.sh global - deploy: - resources: - limits: - cpus: '1' - memory: 1G - networks: - - ohm_network - - tile_coverage_seeding_production: - container_name: tiler_coverage_seeding_production + tile_global_seeding: + container_name: tiler_global_seeding image: ghcr.io/openhistoricalmap/tiler-server:0.0.1-0.dev.git.3225.h2122f3e env_file: - - .env.production + - .env.tiler + volumes: + - ./seed.sh:/opt/seed.sh + entrypoint: + - /bin/bash + - "-c" + - | + /opt/seed.sh global + networks: + - ohm_network + + tile_coverage_seeding: + container_name: tiler_coverage_seeding + image: ghcr.io/openhistoricalmap/tiler-server:0.0.1-0.dev.git.3225.h2122f3e volumes: - ./seed.sh:/opt/seed.sh entrypoint: @@ -121,16 +93,13 @@ services: - "-c" - | /opt/seed.sh coverage - deploy: - resources: - limits: - cpus: '1' - memory: 1G + env_file: + - .env.tiler networks: - ohm_network - tiler_monitor_production: - container_name: tiler_monitor_production + tiler_monitor: + container_name: tiler_monitor image: ghcr.io/openhistoricalmap/tiler-monitor:0.0.1-0.dev.git.2874.ha9bff68 volumes: - /var/run/docker.sock:/var/run/docker.sock @@ -138,20 +107,20 @@ services: - ../../hetzner:/app/hetzner environment: - DOCKER_CONFIG_ENVIRONMENT=production - env_file: - - .env.production stdin_open: true tty: true + env_file: + - .env.tiler networks: - ohm_network volumes: - tiler_production_pgdata: + tiler_pgdata: driver: local - name: tiler_db_2201 - tiler_production_imposmdata: + name: tiler_db_11_02 + tiler_imposm_data: driver: local - name: tiler_imposm_2201 + name: tiler_imposm_11_02 networks: ohm_network: diff --git a/hetzner/tiler/tiler.staging.yml b/hetzner/tiler/tiler.staging.yml deleted file mode 100644 index 22aa9ce0..00000000 --- a/hetzner/tiler/tiler.staging.yml +++ /dev/null @@ -1,124 +0,0 @@ -services: - db_staging: - container_name: tiler_db_staging - image: ghcr.io/openhistoricalmap/tiler-db:0.0.1-0.dev.git.2166.hc55c4cd - volumes: - - tiler_staging_pgdata:/var/lib/postgresql/data - - ./config/postgresql.staging.conf:/etc/postgresql/postgresql.conf - environment: - - PGDATA=/var/lib/postgresql/data - - POSTGRES_CONFIG_FILE=/etc/postgresql/postgresql.conf - command: - - postgres - - "-c" - - "config_file=/etc/postgresql/postgresql.conf" - ports: - - "54321:5432" - env_file: - - .env.staging - networks: - - ohm_network - - imposm_staging: - container_name: tiler_imposm_staging - image: ghcr.io/openhistoricalmap/tiler-imposm:0.0.1-0.dev.git.3208.h6ef73bb - # image: tiler-imposm:staging - # build: - # context: ../../images/tiler-imposm - # dockerfile: Dockerfile - volumes: - - tiler_staging_imposmdata:/mnt/data - command: - - sh - - -c - - | - ./start.sh - env_file: - - .env.staging - restart: always - networks: - - ohm_network - - tiler_server_staging: - container_name: tiler_server_staging - image: ghcr.io/openhistoricalmap/tiler-server:0.0.1-0.dev.git.3225.h2122f3e - # image: tiler-server:staging - # build: - # context: ../../images/tiler-server - # dockerfile: Dockerfile - ports: - - "9091:9090" - env_file: - - .env.staging - restart: always - deploy: - resources: - limits: - cpus: '0.2' - memory: 128MiB - volumes: - - /staging/ohm-deploy/images/tiler-server/start.sh:/app/start.sh - networks: - - ohm_network - - - tiler_sqs_cleaner_staging: - container_name: tiler_sqs_cleaner_staging - image: ghcr.io/openhistoricalmap/tiler-cache:0.0.1-0.dev.git.3245.hffaf924 - env_file: - - .env.staging - environment: - - PORT=8000 - command: - - /bin/sh - - -c - - | - set -x - python sqs_processor.py & - python main.py - restart: always - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8000/health"] - interval: 10s - retries: 3 - timeout: 5s - networks: - - ohm_network - - - tiler_s3_cleaner_staging: - container_name: tiler_s3_cleaner_staging - image: ghcr.io/openhistoricalmap/tiler-cache:0.0.1-0.dev.git.3245.hffaf924 - env_file: - - .env.staging - networks: - - ohm_network - - tiler_monitor_staging: - container_name: tiler_monitor_staging - image: ghcr.io/openhistoricalmap/tiler-monitor:0.0.1-0.dev.git.2706.h1b7db88 - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ../images/tiler-monitor:/app - - ../hetzner:/app/hetzner - environment: - - DOCKER_CONFIG_ENVIRONMENT=staging - env_file: - - .env.staging - stdin_open: true - tty: true - networks: - - ohm_network - -volumes: - tiler_staging_pgdata: - driver: local - name: tiler_db_05_01_v9 - tiler_staging_imposmdata: - driver: local - name: tiler_imposm_05_01_v9 - -networks: - ohm_network: - external: true - \ No newline at end of file diff --git a/hetzner/traefik/traefik.template.yml b/hetzner/traefik/traefik.template.yml new file mode 100644 index 00000000..cd5c66e4 --- /dev/null +++ b/hetzner/traefik/traefik.template.yml @@ -0,0 +1,200 @@ +log: + level: INFO + +entryPoints: + port-web: + address: ":80" + forwardedHeaders: + trustedIPs: + # Docker, red local + - "172.16.0.0/12" + - "192.168.0.0/16" + - "10.0.0.0/8" + # From Cloudflare IPv4 (https://www.cloudflare.com/ips-v4) + - "173.245.48.0/20" + - "103.21.244.0/22" + - "103.22.200.0/22" + - "103.31.4.0/22" + - "141.101.64.0/18" + - "108.162.192.0/18" + - "190.93.240.0/20" + - "188.114.96.0/20" + - "197.234.240.0/22" + - "198.41.128.0/17" + - "162.158.0.0/15" + - "104.16.0.0/13" + - "104.24.0.0/14" + - "172.64.0.0/13" + - "131.0.72.0/22" + # From Cloudflare IPv6 (https://www.cloudflare.com/ips-v6) + - "2400:cb00::/32" + - "2606:4700::/32" + - "2803:f800::/32" + - "2405:b500::/32" + - "2405:8100::/32" + - "2a06:98c0::/29" + - "2c0f:f248::/32" + +http: + middlewares: + secure-headers: + headers: + # Protects against clickjacking + frameDeny: true + # Enables XSS protection in older browsers + browserXssFilter: true + # Prevents browsers from MIME-sniffing (forces declared Content-Type) + contentTypeNosniff: true + # Enforces HTTPS via HSTS + forceSTSHeader: true + stsSeconds: 31536000 # 1 year + stsIncludeSubdomains: true + stsPreload: true + # # Trust only known proxy headers (adjust based on your infrastructure) + # hostsProxyHeaders: ["X-Forwarded-Host"] + + secure-headers-allow-iframe: + headers: + # Allows iframe embedding (for comparison tools) + frameDeny: false + # Enables XSS protection in older browsers + browserXssFilter: true + # Prevents browsers from MIME-sniffing (forces declared Content-Type) + contentTypeNosniff: true + # Enforces HTTPS via HSTS + forceSTSHeader: true + stsSeconds: 31536000 # 1 year + stsIncludeSubdomains: true + stsPreload: true + + redirect-nominatim: + redirectRegex: + # Match both http and https: behind Cloudflare Tunnel, Traefik sees the request as http + regex: "^https?://(nominatim\\.{{OHM_DOMAIN}})/?$" + replacement: "https://${1}/ui/search.html" + permanent: true + + # replace-osm-to-ohm: + # replacePathRegex: + # regex: "^/capabilities/osm\\.json$" + # replacement: "/capabilities/ohm.json" + + # replace-osm-tiles-to-ohm: + # replacePathRegex: + # regex: "^/maps/osm/(.*)$" + # replacement: "/maps/ohm/${1}" + + routers: + vtiles-router: + rule: Host(`vtiles.{{OHM_DOMAIN}}`) + entryPoints: + - port-web + service: tiler_server + middlewares: + - secure-headers-allow-iframe + # Note: Removed replace-osm-to-ohm middleware because Tegola map is named "osm" + # - replace-osm-to-ohm + # - replace-osm-tiles-to-ohm + + nominatim-router: + rule: Host(`nominatim.{{OHM_DOMAIN}}`) && !PathPrefix(`/ui`) + entryPoints: + - port-web + service: nominatim + middlewares: + - secure-headers + - redirect-nominatim + + nominatim-ui-router: + rule: Host(`nominatim.{{OHM_DOMAIN}}`) && PathPrefix(`/ui`) + entryPoints: + - port-web + service: nominatim_ui + middlewares: + - secure-headers + + overpass-api-router: + rule: Host(`overpass-api.{{OHM_DOMAIN}}`) + entryPoints: + - port-web + service: overpass_api + middlewares: + - secure-headers + + osmcha-web-router: + rule: Host(`osmcha.{{OHM_DOMAIN}}`) + entryPoints: + - port-web + service: osmcha_web + middlewares: + - secure-headers + + taginfo-router: + rule: Host(`taginfo.{{OHM_DOMAIN}}`) + entryPoints: + - port-web + service: taginfo + middlewares: + - secure-headers + + node-exporter-router: + rule: Host(`node-exporter.{{OHM_DOMAIN}}`) + entryPoints: + - port-web + service: node_exporter + middlewares: + - secure-headers + + cadvisor-router: + rule: Host(`cadvisor.{{OHM_DOMAIN}}`) + entryPoints: + - port-web + service: cadvisor + middlewares: + - secure-headers + + services: + tiler_server: + loadBalancer: + servers: + - url: http://tiler_server:9090 + + nominatim: + loadBalancer: + servers: + - url: http://nominatim:8080 + + nominatim_ui: + loadBalancer: + servers: + - url: http://nominatim_ui:80 + + overpass_api: + loadBalancer: + servers: + - url: http://overpass_api:80 + + osmcha_web: + loadBalancer: + servers: + - url: http://osmcha-web:80 + + taginfo: + loadBalancer: + servers: + - url: http://taginfo_web:4567 + + node_exporter: + loadBalancer: + servers: + - url: http://node_exporter:9100 + + cadvisor: + loadBalancer: + servers: + - url: http://cadvisor:8080 + +providers: + file: + filename: /etc/traefik/traefik.yml + watch: true diff --git a/hetzner/traefik/traefik.yml b/hetzner/traefik/traefik.yml deleted file mode 100644 index c5f105a0..00000000 --- a/hetzner/traefik/traefik.yml +++ /dev/null @@ -1,296 +0,0 @@ -log: - level: INFO - -entryPoints: - port: - address: ":80" - http: - redirections: - entryPoint: - to: port-secure - port-secure: - address: ":443" - -certificatesResolvers: - letsencrypt: - acme: - email: dev@openhistoricalmap.org - storage: /etc/traefik/acme/acme.json - httpChallenge: - entryPoint: port - -http: - middlewares: - secure-headers: - headers: - # Protects against clickjacking - frameDeny: true - # Enables XSS protection in older browsers - browserXssFilter: true - # Prevents browsers from MIME-sniffing (forces declared Content-Type) - contentTypeNosniff: true - # Enforces HTTPS via HSTS - forceSTSHeader: true - stsSeconds: 31536000 # 1 year - stsIncludeSubdomains: true - stsPreload: true - # # Trust only known proxy headers (adjust based on your infrastructure) - # hostsProxyHeaders: ["X-Forwarded-Host"] - - secure-headers-allow-iframe: - headers: - # Allows iframe embedding (for comparison tools) - frameDeny: false - # Enables XSS protection in older browsers - browserXssFilter: true - # Prevents browsers from MIME-sniffing (forces declared Content-Type) - contentTypeNosniff: true - # Enforces HTTPS via HSTS - forceSTSHeader: true - stsSeconds: 31536000 # 1 year - stsIncludeSubdomains: true - stsPreload: true - - redirect-nominatim: - redirectRegex: - regex: "^https://(nominatim(?:\\.staging)?\\.openhistoricalmap\\.org)/?$" - replacement: "https://${1}/ui/search.html" - permanent: true - - replace-osm-to-ohm: - replacePathRegex: - regex: "^/capabilities/osm\\.json$" - replacement: "/capabilities/ohm.json" - - replace-osm-tiles-to-ohm: - replacePathRegex: - regex: "^/maps/osm/(.*)$" - replacement: "/maps/ohm/${1}" - - routers: - #################### Production #################### - vtiles-production-router: - rule: Host(`vtiles.openhistoricalmap.org`) - entryPoints: - - port-secure - service: tiler_server_production - tls: - certResolver: letsencrypt - middlewares: - - secure-headers-allow-iframe - - tiler_cache-production-router: - rule: Host(`tiler-cache.openhistoricalmap.org`) - entryPoints: - - port-secure - service: tiler_cache_production - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - nominatim-production-router: - rule: Host(`nominatim.openhistoricalmap.org`) && !PathPrefix(`/ui`) - entryPoints: - - port-secure - service: nominatim_production - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - redirect-nominatim - - nominatim-ui-production-router: - rule: Host(`nominatim.openhistoricalmap.org`) && PathPrefix(`/ui`) - entryPoints: - - port-secure - service: nominatim_ui_production - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - overpass-api-production-router: - rule: Host(`overpass-api.openhistoricalmap.org`) - entryPoints: - - port-secure - service: overpass_api_production - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - osmcha-production-router: - rule: Host(`osmcha.openhistoricalmap.org`) - entryPoints: - - port-secure - service: osmcha_production - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - taginfo-production-router: - rule: Host(`taginfo.openhistoricalmap.org`) - entryPoints: - - port-secure - service: taginfo_production - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - node-exporter-router: - rule: Host(`node-exporter.openhistoricalmap.org`) - entryPoints: - - port-secure - service: node_exporter_production - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - cadvisor-router: - rule: Host(`cadvisor.openhistoricalmap.org`) - entryPoints: - - port-secure - service: cadvisor_production - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - #################### Staging #################### - vtiles-staging-router: - rule: Host(`vtiles.staging.openhistoricalmap.org`) || Host(`vtiles.openhistoricalmap.net`) - entryPoints: - - port-secure - service: tiler_server_staging - tls: - certResolver: letsencrypt - middlewares: - - secure-headers-allow-iframe - - replace-osm-to-ohm - - replace-osm-tiles-to-ohm - - overpass-api-staging-router: - rule: Host(`overpass-api.staging.openhistoricalmap.org`) || Host(`overpass-api.openhistoricalmap.net`) - entryPoints: - - port-secure - service: overpass_api_staging - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - nominatim-staging-router: - rule: (Host(`nominatim.staging.openhistoricalmap.org`) || Host(`nominatim.openhistoricalmap.net`)) && !PathPrefix(`/ui`) - entryPoints: - - port-secure - service: nominatim_staging - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - redirect-nominatim - - nominatim-ui-staging-router: - rule: (Host(`nominatim.staging.openhistoricalmap.org`) || Host(`nominatim.openhistoricalmap.net`)) && PathPrefix(`/ui`) - entryPoints: - - port-secure - service: nominatim_ui_staging - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - - taginfo-staging-router: - rule: Host(`taginfo.staging.openhistoricalmap.org`) || Host(`taginfo.openhistoricalmap.net`) - entryPoints: - - port-secure - service: taginfo_staging - tls: - certResolver: letsencrypt - middlewares: - - secure-headers - #################### Production Services #################### - - services: - - tiler_server_production: - loadBalancer: - servers: - - url: http://tiler_server_production:9090 - - tiler_cache_production: - loadBalancer: - servers: - - url: http://tiler_sqs_cleaner_production:8000 - - nominatim_production: - loadBalancer: - servers: - - url: http://nominatim_production:8080 - - nominatim_ui_production: - loadBalancer: - servers: - - url: http://nominatim_ui_production:80 - - overpass_api_production: - loadBalancer: - servers: - - url: http://overpass_production:80 - - osmcha_production: - loadBalancer: - servers: - - url: http://frontend-nginx:80 - - taginfo_production: - loadBalancer: - servers: - - url: http://taginfo_production:4567 - - node_exporter_production: - loadBalancer: - servers: - - url: http://node-exporter:9100 - - cadvisor_production: - loadBalancer: - servers: - - url: http://cadvisor:8080 - - #################### Staging services #################### - - tiler_server_staging: - loadBalancer: - servers: - - url: http://tiler_server_staging:9090 #8083 comes from production - - nominatim_staging: - loadBalancer: - servers: - - url: http://host.docker.internal:8083 # 8083 comes from production - - nominatim_ui_staging: - loadBalancer: - servers: - - url: http://host.docker.internal:8084 # 8084 comes from production - - overpass_api_staging: - loadBalancer: - servers: - - url: http://host.docker.internal:8086 # 8086 comes from production - - taginfo_staging: - loadBalancer: - servers: - - url: http://taginfo_production:4567 - -providers: - file: - filename: /etc/traefik/traefik.yml - watch: true - \ No newline at end of file diff --git a/hetzner/traefik/update-cloudflare-ips.sh b/hetzner/traefik/update-cloudflare-ips.sh new file mode 100755 index 00000000..c3aac6ee --- /dev/null +++ b/hetzner/traefik/update-cloudflare-ips.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CF_IPS_FILE="$SCRIPT_DIR/cloudflare-ips.txt" +TEMPLATE_FILE="$SCRIPT_DIR/traefik.template.yml" +OUTPUT_FILE="$SCRIPT_DIR/traefik.yml" + +# Download Cloudflare IPs +echo "Downloading Cloudflare IPs..." +IPV4_IPS=$(curl -s https://www.cloudflare.com/ips-v4) +IPV6_IPS=$(curl -s https://www.cloudflare.com/ips-v6) + +# Save IPs to file +{ + echo "# Cloudflare IPs - Updated $(date +%Y-%m-%d)" + echo "$IPV4_IPS" + echo "$IPV6_IPS" +} > "$CF_IPS_FILE" + +# Generate YAML block with IPs +gen_ips() { + echo " - \"172.16.0.0/12\"" + echo " - \"192.168.0.0/16\"" + echo " - \"10.0.0.0/8\"" + echo "$IPV4_IPS" | sed 's/^/ - "/;s/$/"/' + echo "$IPV6_IPS" | sed 's/^/ - "/;s/$/"/' +} + +# Replace trustedIPs block in template +awk ' + NR==FNR { block = block $0 "\n"; next } + /^[[:space:]]*trustedIPs:[[:space:]]*$/ { + print; printf "%s", block + while ((getline line) > 0) { + if (line !~ /^[[:space:]]+(-|#)/) { print line; break } + } + next + } + { print } +' <(gen_ips) "$TEMPLATE_FILE" > "$OUTPUT_FILE.tmp" + +# Load .env.traefik and substitute domain +source "$SCRIPT_DIR/../.env.traefik" +sed "s/{{OHM_DOMAIN}}/$OHM_DOMAIN/g" "$OUTPUT_FILE.tmp" > "$OUTPUT_FILE" +rm -f "$OUTPUT_FILE.tmp" + +echo "Configuration generated at $OUTPUT_FILE" diff --git a/images/web/Dockerfile b/images/web/Dockerfile index a460e4a9..028564e2 100644 --- a/images/web/Dockerfile +++ b/images/web/Dockerfile @@ -24,7 +24,7 @@ RUN apt-get update && \ && apt-get clean && rm -rf /var/lib/apt/lists/* # Download OHM Website using gitsha, faster than cloning -ENV OPENHISTORICALMAP_WEBSITE_GITSHA=37a55f789868ed5f9ffa42bb82bea2b52bdfa042 +ENV OPENHISTORICALMAP_WEBSITE_GITSHA=658bab0b9357cae3ac0d30f4f6a4693a16d36d4e ENV OHM_WEBSITE_URL=https://github.com/OpenHistoricalMap/ohm-website/archive/${OPENHISTORICALMAP_WEBSITE_GITSHA}.zip RUN rm -rf $workdir/* && curl -fsSL $OHM_WEBSITE_URL -o /tmp/ohm-website.zip && \ unzip /tmp/ohm-website.zip -d /tmp && \ diff --git a/ohm/requirements.yaml b/ohm/requirements.yaml index 031267d4..903dca8e 100644 --- a/ohm/requirements.yaml +++ b/ohm/requirements.yaml @@ -1,4 +1,4 @@ dependencies: - name: osm-seed - version: '0.1.0-0.dev.git.984.he0afc57' - repository: https://osm-seed.github.io/osm-seed-chart/ + version: '0.1.0-0.dev.git.984.h985db07' + repository: https://osm-seed.github.io/osm-seed-chart/ \ No newline at end of file diff --git a/values.production.template.yaml b/values.production.template.yaml index 9db5eba9..880f170b 100644 --- a/values.production.template.yaml +++ b/values.production.template.yaml @@ -14,37 +14,9 @@ osm-seed: # ==================================================================================================== AWS_S3_BUCKET: {{PRODUCTION_S3_BUCKET}} - # AWS SSL ARN - AWS_SSL_ARN: {{PRODUCTION_AWS_SSL_ARN}} - - # Specify serviceType. - # - # serviceType can be one of three values: 'NodePort', 'ClusterIP' or 'LoadBalancer' - # Use `NodePort` for local testing on minikube. - # - # The recommended setting is `ClusterIP`, and then following the instructions to - # point a DNS record to the cluster IP address. This will setup the ingress rules - # for all services as subdomains and configure SSL using Lets Encrypt. - # - # If you specify `LoadBalancer` as the service type, if you also specify - # an `AWS_SSL_ARN` that is a wildcart certificate, that will be configured - # as the SSL certificate for your services. Else, you will need to configure - # SSL separately. + # Traffic is routed via Cloudflare Tunnel, which connects directly to ClusterIP Services. + # No Ingress resources or load balancers (ALB/NLB) are needed. serviceType: ClusterIP - ingressClassNameType: "alb" # ALB works with ACM - ingressClassName: alb - alb: - certificateArn: {{PRODUCTION_AWS_SSL_ARN}} - enableWaf: - enabled: true - wafAclArn: {{PRODUCTION_AWS_WAF_WEBACL_ARN}} - # Domain that is pointed to the clusterIP - # You will need to create an A record like *.osmseed.example.com pointed to the ClusterIP - # Then, the cluster configuration will setup services at their respective subdomains: - # - web.osmseed.example.com - # - overpass.osmseed.example.com - # - nominatim.osmseed.example.com - # - etc. domain: openhistoricalmap.org # ==================================================================================================== diff --git a/values.staging.template.yaml b/values.staging.template.yaml index 727426e0..53b7ed64 100644 --- a/values.staging.template.yaml +++ b/values.staging.template.yaml @@ -6,7 +6,7 @@ osm-seed: # ==================================================================================================== # ==================================================================================================== - # The version of the image group in osm-seed, get it here: https://hub.docker.com/r/developmentseed/osmseed-web/tags/ + # The version of the image group in osm-seed, get it here: https://github.com/orgs/osm-seed/packages # osmSeedVersion: ohm-b8a0ed1 environment: staging @@ -18,45 +18,10 @@ osm-seed: # ==================================================================================================== AWS_S3_BUCKET: {{STAGING_S3_BUCKET}} - # ==================================================== - # AWS: Specify ARN for SSL certificate, currently assumes a single wildcard cert - # ==================================================== - - AWS_SSL_ARN: {{STAGING_AWS_SSL_ARN}} - - # Specify serviceType. - # - # serviceType can be one of three values: 'NodePort', 'ClusterIP' or 'LoadBalancer' - # Use `NodePort` for local testing on minikube. - # - # The recommended setting is `ClusterIP`, and then following the instructions to - # point a DNS record to the cluster IP address. This will setup the ingress rules - # for all services as subdomains and configure SSL using Lets Encrypt. - # - # If you specify `LoadBalancer` as the service type, if you also specify - # an `AWS_SSL_ARN` that is a wildcart certificate, that will be configured - # as the SSL certificate for your services. Else, you will need to configure - # SSL separately. + # Traffic is routed via Cloudflare Tunnel, which connects directly to ClusterIP Services. + # No Ingress resources or load balancers (ALB/NLB) are needed. serviceType: ClusterIP - ingressClassNameType: "alb" - ingressClassName: alb - alb: - certificateArn: {{STAGING_AWS_SSL_ARN}} - enableWaf: - enabled: true - wafAclArn: {{STAGING_AWS_WAF_WEBACL_ARN}} - # Domain that is pointed to the clusterIP - # You will need to create an A record like *.osmseed.example.com pointed to the ClusterIP - # Then, the cluster configuration will setup services at their respective subdomains: - # - web.osmseed.example.com - # - overpass.osmseed.example.com - # - nominatim.osmseed.example.com - # - etc. - domain: staging.openhistoricalmap.org - - # ==================================================================================================== - # Configuration for Lets Encrypt setup - # ==================================================================================================== + domain: ohmstaging.org # Admin Email address used when generating Lets Encrypt certificates. # You will be notified of expirations, etc. on this email address. @@ -155,12 +120,10 @@ osm-seed: enabled: true name: ohm-s3-bucket-access-staging replicaCount: 1 - serviceAnnotations: - service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "300" - ingressDomain: www.staging.openhistoricalmap.org + ingressDomain: www.ohmstaging.org env: MAILER_ADDRESS: {{MAILER_ADDRESS}} - MAILER_DOMAIN: staging.openhistoricalmap.org + MAILER_DOMAIN: ohmstaging.org MAILER_USERNAME: {{MAILER_USERNAME}} MAILER_PASSWORD: {{MAILER_PASSWORD}} # MAILER_PORT: "587" @@ -168,8 +131,8 @@ osm-seed: OAUTH_CLIENT_ID: CiBBpurbtUj1np_QZOTngIePlS7K9uGvKDW2Pcw5O7Y OAUTH_KEY: 1O8WtBWivoefehDMT6sbm9TNUU_h_EXznI4cM5XMyJw MAILER_FROM: web@noreply.openhistoricalmap.org - NOMINATIM_URL: nominatim.staging.openhistoricalmap.org - OVERPASS_URL: overpass-api.staging.openhistoricalmap.org + NOMINATIM_URL: nominatim.ohmstaging.org + OVERPASS_URL: overpass-api.ohmstaging.org NEW_RELIC_LICENSE_KEY: "none" NEW_RELIC_APP_NAME: "none" ORGANIZATION_NAME: OpenHistoricalMap @@ -610,9 +573,7 @@ osm-seed: ip: {{STAGING_TILER_SERVER_HOST}} port: 9091 replicaCount: 1 - serviceAnnotations: - service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "300" - ingressDomain: vtiles.staging.openhistoricalmap.org + ingressDomain: vtiles.ohmstaging.org env: TILER_SERVER_PORT: 9090 TILER_CACHE_TYPE: s3 @@ -716,21 +677,19 @@ osm-seed: enabled: false priorityClass: medium-priority replicaCount: 1 - serviceAnnotations: - service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '300' - ingressDomain: tm-api.staging.openhistoricalmap.org + ingressDomain: tm-api.ohmstaging.org healthCheckPath: /health env: TM_ORG_NAME: OpenHistoricalMap TM_ORG_CODE: OHM - TM_ORG_URL: staging.openhistoricalmap.org - TM_ORG_PRIVACY_POLICY_URL: staging.openhistoricalmap.org/copyright + TM_ORG_URL: ohmstaging.org + TM_ORG_PRIVACY_POLICY_URL: ohmstaging.org/copyright TM_ORG_GITHUB: github.com/openhistoricalmap - OSM_SERVER_URL: https://staging.openhistoricalmap.org - OSM_NOMINATIM_SERVER_URL: https://nominatim.openhistoricalmap.org - OSM_REGISTER_URL: https://staging.openhistoricalmap.org/user/new - ID_EDITOR_URL: https://staging.openhistoricalmap.org/edit?editor=id - POTLATCH2_EDITOR_URL: https://staging.openhistoricalmap.org/edit?editor=potlatch2 + OSM_SERVER_URL: https://ohmstaging.org + OSM_NOMINATIM_SERVER_URL: https://nominatim.osmstaging.org + OSM_REGISTER_URL: https://ohmstaging.org/user/new + ID_EDITOR_URL: https://ohmstaging.org/edit?editor=id + POTLATCH2_EDITOR_URL: https://ohmstaging.org/edit?editor=potlatch2 TM_SECRET: {{STAGING_TM_API_SECRET}} TM_EMAIL_FROM_ADDRESS: web@noreply.openhistoricalmap.org TM_EMAIL_CONTACT_ADDRESS: dev@openhistoricalmap.org @@ -739,8 +698,8 @@ osm-seed: TM_SMTP_USER: {{MAILER_USERNAME}} TM_SMTP_PASSWORD: {{MAILER_PASSWORD}} TM_DEFAULT_LOCALE: en - TM_APP_API_URL: https://tm-api.staging.openhistoricalmap.org - TM_APP_BASE_URL: https://tasks.staging.openhistoricalmap.org + TM_APP_API_URL: https://tm-api.ohmstaging.org + TM_APP_BASE_URL: https://tasks.ohmstaging.org TM_IMPORT_MAX_FILESIZE: 3000000 TM_MAX_AOI_AREA: 15000 TM_APP_API_VERSION: v2 @@ -748,11 +707,11 @@ osm-seed: TM_CLIENT_ID: EeFtCc-qwJEsKZWrD1jFQZfPHp5JpRq-da9jw55z86U TM_CLIENT_SECRET: g0TLdrT-IAu8VEhyuvJ_YBMWWUhSXXO75SpTxG2P3OI TM_DEFAULT_CHANGESET_COMMENT: '#ohm-project' - TM_REDIRECT_URI: https://tasks.staging.openhistoricalmap.org/authorized + TM_REDIRECT_URI: https://tasks.ohmstaging.org/authorized TM_SCOPE: 'read_prefs write_api' # Add extra info TM_ORG_FB: https://www.facebook.com/openhistoricalmap - TM_ORG_INSTAGRAM: https://www.openhistoricalmap.org + TM_ORG_INSTAGRAM: https://www.ohmstaging.org TM_ORG_TWITTER: https://x.com/OpenHistoricalMap TM_ORG_YOUTUBE: https://www.youtube.com/playlist?list=PLOi35w6_Hpx_CYdYBUpPeuiJ1djn5-wIx @@ -790,9 +749,7 @@ osm-seed: ip: {{STAGING_NOMINATIM_HOST}} port: 8083 priorityClass: medium-priority - serviceAnnotations: - service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '300' - ingressDomain: nominatim.staging.openhistoricalmap.org + ingressDomain: nominatim.ohmstaging.org replicaCount: 1 env: PBF_URL: https://s3.amazonaws.com/planet.openhistoricalmap.org/planet/planet-250213_0001.osm.pbf @@ -809,7 +766,7 @@ osm-seed: PGDATA: /var/lib/postgresql/16/main NOMINATIM_ADDRESS_LEVEL_CONFIG_URL: https://raw.githubusercontent.com/OpenHistoricalMap/nominatim-ui/master/address-levels.json UPDATE_MODE: continuous - OSMSEED_WEB_API_DOMAIN: www.openhistoricalmap.org + OSMSEED_WEB_API_DOMAIN: www.ohmstaging.org IMPORT_STYLE: extratags EXTRA_TAGS: start_date,start_date:edtf,end_date,end_date:edt resources: @@ -840,9 +797,7 @@ osm-seed: ip: {{STAGING_OVERPASS_HOST}} port: 8086 priorityClass: medium-priority - serviceAnnotations: - service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "300" - ingressDomain: overpass-api.staging.openhistoricalmap.org + ingressDomain: overpass-api.ohmstaging.org env: OVERPASS_META: 'attic' OVERPASS_MODE: 'init' @@ -876,9 +831,7 @@ osm-seed: serviceAccount: enabled: true name: ohm-s3-bucket-access-staging - serviceAnnotations: - service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "300" - ingressDomain: taginfo.staging.openhistoricalmap.org + ingressDomain: taginfo.ohmstaging.org env: URL_PLANET_FILE_STATE: https://s3.amazonaws.com/planet.openhistoricalmap.org/planet/state.txt URL_HISTORY_PLANET_FILE_STATE: https://s3.amazonaws.com/planet.openhistoricalmap.org/planet/full-history/state.txt @@ -999,18 +952,18 @@ osm-seed: image: name: ghcr.io/openhistoricalmap/osmcha-django tag: 1bd58e1 - ingressDomain: osmcha.staging.openhistoricalmap.org + ingressDomain: osmcha.ohmstaging.org env: DJANGO_SETTINGS_MODULE: "config.settings.production" OSMCHA_FRONTEND_VERSION: "v0.86.0-production" DJANGO_SECRET_KEY: {{STAGING_OSMCHA_DJANGO_SECRET_KEY}} DJANGO_SECURE_SSL_REDIRECT: "False" - OSM_SERVER_URL: https://www.openhistoricalmap.org - OAUTH_REDIRECT_URI: https://osmcha.staging.openhistoricalmap.org/authorized + OSM_SERVER_URL: https://www.ohmstaging.org + OAUTH_REDIRECT_URI: https://osmcha.ohmstaging.org/authorized OSM_PLANET_BASE_URL: https://s3.amazonaws.com/planet.openhistoricalmap.org/replication/changesets/ ## frontend - OSMCHA_URL: https://osmcha.staging.openhistoricalmap.org - OSMCHA_API_URL: www.openhistoricalmap.org + OSMCHA_URL: https://osmcha.ohmstaging.org + OSMCHA_API_URL: www.ohmstaging.org OAUTH2_OSM_KEY: tHRgc_GVLsUj2tuVQSg05nn5f8Tnda8mF-ZfEbI3ItA OAUTH2_OSM_SECRET: s9blyq71UditeidYeiQc_J40-DffUh3EVlDCGVcskQI resources: @@ -1133,3 +1086,4 @@ ohm: enabled: true label_key: nodegroup_type label_value: web_medium + \ No newline at end of file