| 1 | |
| 2 | services: |
| 3 | |
| 4 | |
| 5 | postgres: |
| 6 | image: timescale/timescaledb-ha:pg16 |
| 7 | environment: |
| 8 | POSTGRES_USER: ${POSTGRES_USER:-drophawk} |
| 9 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} |
| 10 | POSTGRES_DB: ${POSTGRES_DB:-drophawk} |
| 11 | POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C" |
| 12 | volumes: |
| 13 | - pg-data:/var/lib/postgresql/data |
| 14 | networks: |
| 15 | - drophawk-net |
| 16 | healthcheck: |
| 17 | test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-drophawk} -d ${POSTGRES_DB:-drophawk}"] |
| 18 | interval: 5s |
| 19 | timeout: 5s |
| 20 | retries: 10 |
| 21 | start_period: 30s |
| 22 | restart: on-failure |
| 23 | |
| 24 | redis: |
| 25 | image: redis:7.4.3-alpine3.21 |
| 26 | command: > |
| 27 | redis-server |
| 28 | --requirepass ${REDIS_PASSWORD} |
| 29 | --maxmemory 200mb |
| 30 | --maxmemory-policy allkeys-lru |
| 31 | --save 60 100 |
| 32 | --appendonly yes |
| 33 | volumes: |
| 34 | - redis-data:/data |
| 35 | networks: |
| 36 | - drophawk-net |
| 37 | healthcheck: |
| 38 | test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] |
| 39 | interval: 5s |
| 40 | timeout: 3s |
| 41 | retries: 10 |
| 42 | start_period: 10s |
| 43 | restart: on-failure |
| 44 | |
| 45 | nats: |
| 46 | image: nats:2.14.2-alpine3.22 |
| 47 | command: > |
| 48 | -js |
| 49 | -sd /data |
| 50 | -m 8222 |
| 51 | volumes: |
| 52 | - nats-data:/data |
| 53 | networks: |
| 54 | - drophawk-net |
| 55 | healthcheck: |
| 56 | test: ["CMD-SHELL", "wget -qO- http://localhost:8222/healthz | grep -q ok"] |
| 57 | interval: 5s |
| 58 | timeout: 3s |
| 59 | retries: 10 |
| 60 | start_period: 10s |
| 61 | restart: on-failure |
| 62 | |
| 63 | |
| 64 | ntfy: |
| 65 | image: binwiederhier/ntfy:v2.14.0 |
| 66 | command: serve --config /etc/ntfy/server.yml |
| 67 | volumes: |
| 68 | - ntfy-data:/var/lib/ntfy |
| 69 | - ./infra/ntfy.server.yml:/etc/ntfy/server.yml:ro |
| 70 | networks: |
| 71 | - drophawk-net |
| 72 | healthcheck: |
| 73 | test: ["CMD-SHELL", "wget -qO- http://localhost:80/v1/health | grep -q '\"healthy\":true'"] |
| 74 | interval: 10s |
| 75 | timeout: 5s |
| 76 | retries: 6 |
| 77 | start_period: 15s |
| 78 | restart: on-failure |
| 79 | |
| 80 | |
| 81 | backend: |
| 82 | build: |
| 83 | context: ./backend |
| 84 | dockerfile: Dockerfile |
| 85 | environment: |
| 86 | DATABASE_URL: ${DATABASE_URL} |
| 87 | REDIS_URL: ${REDIS_URL} |
| 88 | NATS_URL: ${NATS_URL} |
| 89 | NTFY_BASE_URL: ${NTFY_BASE_URL:-http://ntfy:80} |
| 90 | NTFY_TOPIC: ${NTFY_TOPIC:-drophawk-drops} |
| 91 | NTFY_TOKEN: ${NTFY_TOKEN} |
| 92 | TICKETMASTER_API_KEYS: ${TICKETMASTER_API_KEYS} |
| 93 | SEATGEEK_CLIENT_ID: ${SEATGEEK_CLIENT_ID:-} |
| 94 | TICKETMASTER_AFFILIATE_ID: ${TICKETMASTER_AFFILIATE_ID:-} |
| 95 | SEATGEEK_AFFILIATE_ID: ${SEATGEEK_AFFILIATE_ID:-} |
| 96 | LOG_LEVEL: ${LOG_LEVEL:-info} |
| 97 | OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-} |
| 98 | PUBLIC_BASE_URL: ${PUBLIC_BASE_URL:-http://localhost} |
| 99 | API_RATE_LIMIT_PER_MIN: ${API_RATE_LIMIT_PER_MIN:-100} |
| 100 | SSE_MAX_CLIENTS: ${SSE_MAX_CLIENTS:-200} |
| 101 | POLL_DEFAULT_INTERVAL_S: ${POLL_DEFAULT_INTERVAL_S:-300} |
| 102 | POLL_TIGHT_INTERVAL_S: ${POLL_TIGHT_INTERVAL_S:-10} |
| 103 | POLL_TIGHT_WINDOW_S: ${POLL_TIGHT_WINDOW_S:-900} |
| 104 | WATCHLIST_MAX_RULES: ${WATCHLIST_MAX_RULES:-500} |
| 105 | networks: |
| 106 | - drophawk-net |
| 107 | depends_on: |
| 108 | postgres: |
| 109 | condition: service_healthy |
| 110 | redis: |
| 111 | condition: service_healthy |
| 112 | nats: |
| 113 | condition: service_healthy |
| 114 | restart: on-failure |
| 115 | |
| 116 | frontend: |
| 117 | build: |
| 118 | context: ./frontend |
| 119 | dockerfile: Dockerfile |
| 120 | environment: |
| 121 | NEXT_PUBLIC_API_BASE_URL: ${NEXT_PUBLIC_API_BASE_URL:-/api} |
| 122 | NEXT_PUBLIC_NTFY_PUBLIC_URL: ${NEXT_PUBLIC_NTFY_PUBLIC_URL:-/ntfy} |
| 123 | NEXT_PUBLIC_NTFY_PUBLIC_TOPIC: ${NEXT_PUBLIC_NTFY_PUBLIC_TOPIC:-drophawk-drops} |
| 124 | NEXT_PUBLIC_SENTRY_DSN: ${NEXT_PUBLIC_SENTRY_DSN:-} |
| 125 | networks: |
| 126 | - drophawk-net |
| 127 | depends_on: |
| 128 | backend: |
| 129 | condition: service_started |
| 130 | healthcheck: |
| 131 | test: ["CMD-SHELL", "wget -qO- http://localhost:3000/ | grep -q DropHawk || wget -qO- http://localhost:3000/api/health 2>/dev/null; exit 0"] |
| 132 | interval: 15s |
| 133 | timeout: 10s |
| 134 | retries: 6 |
| 135 | start_period: 60s |
| 136 | restart: on-failure |
| 137 | |
| 138 | |
| 139 | caddy: |
| 140 | image: caddy:2.11.4-alpine |
| 141 | ports: |
| 142 | - "80:80" |
| 143 | - "443:443" |
| 144 | volumes: |
| 145 | - ./infra/Caddyfile:/etc/caddy/Caddyfile:ro |
| 146 | - caddy-data:/data |
| 147 | - caddy-config:/config |
| 148 | networks: |
| 149 | - drophawk-net |
| 150 | depends_on: |
| 151 | frontend: |
| 152 | condition: service_started |
| 153 | backend: |
| 154 | condition: service_started |
| 155 | ntfy: |
| 156 | condition: service_healthy |
| 157 | healthcheck: |
| 158 | test: ["CMD-SHELL", "wget -qO- http://localhost:80/ 2>/dev/null | head -c 1 | wc -c | grep -q '[1-9]' || caddy validate --config /etc/caddy/Caddyfile"] |
| 159 | interval: 15s |
| 160 | timeout: 10s |
| 161 | retries: 6 |
| 162 | start_period: 20s |
| 163 | restart: on-failure |
| 164 | |
| 165 | |
| 166 | prometheus: |
| 167 | image: prom/prometheus:v3.4.1 |
| 168 | command: |
| 169 | - "--config.file=/etc/prometheus/prometheus.yml" |
| 170 | - "--storage.tsdb.path=/prometheus" |
| 171 | - "--storage.tsdb.retention.time=30d" |
| 172 | - "--web.enable-lifecycle" |
| 173 | volumes: |
| 174 | - ./infra/prometheus.yml:/etc/prometheus/prometheus.yml:ro |
| 175 | - prometheus-data:/prometheus |
| 176 | networks: |
| 177 | - drophawk-net |
| 178 | healthcheck: |
| 179 | test: ["CMD-SHELL", "wget -qO- http://localhost:9090/-/healthy | grep -q Healthy"] |
| 180 | interval: 15s |
| 181 | timeout: 5s |
| 182 | retries: 6 |
| 183 | start_period: 20s |
| 184 | restart: on-failure |
| 185 | |
| 186 | grafana: |
| 187 | image: grafana/grafana-oss:13.0.2 |
| 188 | environment: |
| 189 | GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin} |
| 190 | GF_PATHS_PROVISIONING: /etc/grafana/provisioning |
| 191 | GF_SERVER_ROOT_URL: "%(protocol)s://%(domain)s/grafana" |
| 192 | GF_SERVER_SERVE_FROM_SUB_PATH: "true" |
| 193 | volumes: |
| 194 | - grafana-data:/var/lib/grafana |
| 195 | - ./infra/grafana/datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml:ro |
| 196 | - ./infra/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro |
| 197 | networks: |
| 198 | - drophawk-net |
| 199 | depends_on: |
| 200 | prometheus: |
| 201 | condition: service_healthy |
| 202 | loki: |
| 203 | condition: service_healthy |
| 204 | healthcheck: |
| 205 | test: ["CMD-SHELL", "wget -qO- http://localhost:3000/api/health | grep -q ok"] |
| 206 | interval: 15s |
| 207 | timeout: 5s |
| 208 | retries: 6 |
| 209 | start_period: 30s |
| 210 | restart: on-failure |
| 211 | |
| 212 | loki: |
| 213 | image: grafana/loki:3.5.5 |
| 214 | command: -config.file=/etc/loki/loki-config.yml |
| 215 | user: "0" |
| 216 | volumes: |
| 217 | - ./infra/loki/loki-config.yml:/etc/loki/loki-config.yml:ro |
| 218 | - loki-data:/var/loki |
| 219 | networks: |
| 220 | - drophawk-net |
| 221 | healthcheck: |
| 222 | test: ["CMD-SHELL", "wget -qO- http://localhost:3100/ready | grep -q ready"] |
| 223 | interval: 15s |
| 224 | timeout: 5s |
| 225 | retries: 6 |
| 226 | start_period: 30s |
| 227 | restart: on-failure |
| 228 | |
| 229 | |
| 230 | volumes: |
| 231 | pg-data: |
| 232 | redis-data: |
| 233 | nats-data: |
| 234 | ntfy-data: |
| 235 | caddy-data: |
| 236 | caddy-config: |
| 237 | prometheus-data: |
| 238 | grafana-data: |
| 239 | loki-data: |
| 240 | |
| 241 | |
| 242 | networks: |
| 243 | drophawk-net: |
| 244 | driver: bridge |
| 245 | name: drophawk-net |