services: api: build: context: . dockerfile: Dockerfile args: - VITE_FARO_ENABLED=${VITE_FARO_ENABLED:-false} - VITE_FARO_COLLECTOR_URL=${VITE_FARO_COLLECTOR_URL:-} - VITE_POSTHOG_KEY=${VITE_POSTHOG_KEY:-} - VITE_META_PIXEL_ID=${VITE_META_PIXEL_ID:-} - VITE_CHATWOOT_BASE_URL=${VITE_CHATWOOT_BASE_URL:-} - VITE_CHATWOOT_WEBSITE_TOKEN=${VITE_CHATWOOT_WEBSITE_TOKEN:-} # Sentry browser SDK — gated on DSN presence (unset = skip init). # VITE_* args must be build-time; runtime env won't reach the bundle. - VITE_SENTRY_DSN=${VITE_SENTRY_DSN:-} - VITE_SENTRY_ENVIRONMENT=${VITE_SENTRY_ENVIRONMENT:-production} - VITE_SENTRY_RELEASE=${VITE_SENTRY_RELEASE:-} - VITE_GOOGLE_ADS_ID=${VITE_GOOGLE_ADS_ID:-} - VITE_GOOGLE_ADS_SIGNUP_LABEL=${VITE_GOOGLE_ADS_SIGNUP_LABEL:-} environment: - NODE_ENV=production - PORT=4000 - DATABASE_URL=${DATABASE_URL} - REDIS_HOST=sase-redis - REDIS_PORT=6379 - REDIS_PASSWORD=${REDIS_PASSWORD} - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET} - BETTER_AUTH_URL=${BETTER_AUTH_URL} # Süper Panel ↔ Sase.tr internal admin token (X-Internal-Token header) - INTERNAL_API_TOKEN=${INTERNAL_API_TOKEN:-} - INTERNAL_IMPERSONATION_SECRET=${INTERNAL_IMPERSONATION_SECRET:-} - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID:-} - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET:-} - MINIO_ENDPOINT=http://minio-global:9000 - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY} - MINIO_SECRET_KEY=${MINIO_SECRET_KEY} - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME:-sase-schemas} - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL} - MINIO_USE_SSL=false - CORS_ORIGIN=${CORS_ORIGIN} - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY:-} - STRIPE_PUBLISHABLE_KEY=${STRIPE_PUBLISHABLE_KEY:-} - PL24_BASE_URL=${PL24_BASE_URL:-https://www.partslink24.com} - PL24_COMPANY_CODE=${PL24_COMPANY_CODE:-} - PL24_USERNAME=${PL24_USERNAME:-} - PL24_PASSWORD=${PL24_PASSWORD:-} - PL24_COMPANY_CODE_2=${PL24_COMPANY_CODE_2:-} - PL24_USERNAME_2=${PL24_USERNAME_2:-} - PL24_PASSWORD_2=${PL24_PASSWORD_2:-} - PL24_PROXY_DE=${PL24_PROXY_DE:-} - EMEX_USERNAME=${EMEX_USERNAME:-} - EMEX_PASSWORD=${EMEX_PASSWORD:-} - EMEX_USE_PROXY=${EMEX_USE_PROXY:-false} - EMEX_PROXY_HOST=${EMEX_PROXY_HOST:-74.81.81.81} - EMEX_PROXY_USER=${EMEX_PROXY_USER:-1726bbe361918676d44e} - EMEX_PROXY_PASS=${EMEX_PROXY_PASS:-f11c7b6128cc86c6} - PCAT_USE_PROXY=${PCAT_USE_PROXY:-true} - PCAT_PROXY_HOST=${PCAT_PROXY_HOST:-gw.dataimpulse.com} - PCAT_PROXY_USER=${PCAT_PROXY_USER:-} - PCAT_PROXY_PASS=${PCAT_PROXY_PASS:-} # Vinpin ePER VIN-decode fallback (flag-gated, off by default) - VINPIN_ENABLED=${VINPIN_ENABLED:-false} - VINPIN_URL=${VINPIN_URL:-https://login.tr.vinpin.online} - VINPIN_USER=${VINPIN_USER:-} - VINPIN_PASS=${VINPIN_PASS:-} - POSTAL_API_URL=${POSTAL_API_URL:-} - POSTAL_API_KEY=${POSTAL_API_KEY:-} - POSTAL_FROM_ADDRESS=${POSTAL_FROM_ADDRESS:-noreply@sase.tr} - POSTAL_FROM_NAME=${POSTAL_FROM_NAME:-Sase.tr} # Novu lifecycle e-mail automation (Tailscale-only; sends via Postal) - NOVU_API_URL=${NOVU_API_URL:-https://api.bildirim.semih.ai} - NOVU_API_KEY=${NOVU_API_KEY:-} - APP_PUBLIC_URL=${APP_PUBLIC_URL:-https://sase.tr} - MAILTRACK_SECRET=${MAILTRACK_SECRET:-} # PostHog server-side analytics (payments, subscription lifecycle, $revenue). # Empty key → PostHogService no-ops (server-side analytics disabled). - POSTHOG_API_KEY=${POSTHOG_API_KEY:-} # Personal/secure key → LOCAL feature-flag evaluation (source kill switches, # guarded decode rollout, remote config). Empty → flags fail open (inert). - POSTHOG_PERSONAL_API_KEY=${POSTHOG_PERSONAL_API_KEY:-} # "false" on non-prod → evaluate flags but DON'T emit analytics to prod project. - POSTHOG_CAPTURE_ENABLED=${POSTHOG_CAPTURE_ENABLED:-} - POSTHOG_HOST=${POSTHOG_HOST:-https://eu.i.posthog.com} # Meta Conversions API — server-side signup tracking. Empty → CAPI no-ops. - META_CAPI_PIXEL_ID=${META_CAPI_PIXEL_ID:-} - META_CAPI_ACCESS_TOKEN=${META_CAPI_ACCESS_TOKEN:-} - META_CAPI_TEST_EVENT_CODE=${META_CAPI_TEST_EVENT_CODE:-} - OTEL_ENABLED=${OTEL_ENABLED:-false} - OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT:-} - OTEL_EXPORTER_OTLP_HEADERS=${OTEL_EXPORTER_OTLP_HEADERS:-} - OTEL_SERVICE_NAME=${OTEL_SERVICE_NAME:-sase-api} - OTEL_TRACE_SAMPLE_RATE=${OTEL_TRACE_SAMPLE_RATE:-1.0} - ML_PREDICTION_ENABLED=${ML_PREDICTION_ENABLED:-false} # Chatwoot live-chat widget — HMAC secret for verified user identity - CHATWOOT_HMAC_TOKEN=${CHATWOOT_HMAC_TOKEN:-} # Local catalog-dump lookup (CategoriesService → PcatSourceDb / EmexSourceDb). # Defaults off; when CATALOG_SOURCE_DB_ENABLED=true + URLs set, parts fetches # hit the dump first and only fall through to live upstream on miss. - CATALOG_SOURCE_DB_ENABLED=${CATALOG_SOURCE_DB_ENABLED:-false} - PCAT_SOURCE_DB_URL=${PCAT_SOURCE_DB_URL:-} - EMEX_SOURCE_DB_URL=${EMEX_SOURCE_DB_URL:-} # Per-source kill switches. pcat default off (dump doesn't cover TR vehicles). - EMEX_SOURCE_DB_ENABLED=${EMEX_SOURCE_DB_ENABLED:-true} - PCAT_SOURCE_DB_ENABLED=${PCAT_SOURCE_DB_ENABLED:-false} # Per-catalog parts-lookup allowlist (comma-separated). Empty → all # fetchCategoryParts calls return null. See sase-emex-source-db-safety # memory: catalog-wide bridge had 7-114x noise; only add a catalog once # its per-vehicle bridge is wired & OEM-verified. - EMEX_SOURCE_DB_ALLOWED_CATALOGS=${EMEX_SOURCE_DB_ALLOWED_CATALOGS:-} # P snapshot lookup (db `td`) backing the OEM detail page. Off until # both vars are set in Coolify; see sase-prod-db-api-access memory. - P_DB_ENABLED=${P_DB_ENABLED:-false} - P_DB_URL=${P_DB_URL:-} # Tedarikçi fiyat geçmişi (takip MySQL, Tailscale 100.82.193.79). Boş → # part-prices uçları matched:false döner, UI fiyat bölümü çizmez. - SUPPLIER_PRICE_DB_ENABLED=${SUPPLIER_PRICE_DB_ENABLED:-false} - SUPPLIER_PRICE_DB_URL=${SUPPLIER_PRICE_DB_URL:-} # Central Directus CMS (Süper Panel project on Coolify). Prod and staging # point at the SAME instance so blog content is shared across envs. # Internal docker-network URL — both stacks join the `coolify` network. - DIRECTUS_URL=${DIRECTUS_URL:-} - DIRECTUS_TOKEN=${DIRECTUS_TOKEN:-} - DIRECTUS_PROJECT=${DIRECTUS_PROJECT:-sase} # Bearer token for POST /blog/posts/internal (Süper Panel n8n pipeline) - BLOG_AUTOMATION_TOKEN=${BLOG_AUTOMATION_TOKEN:-} # Catalog prefetch / backfill tuning (in-process worker lives in THIS api # container, not worker.js). All empty → compiled-in defaults; set in Coolify # to ramp without a code change. PL24 rate stays conservative unless rotating # residential proxies are confirmed (upstream ban risk). See # sase-coolify-compose-env-injection + sase-catalog-backfill memories. - CATALOG_BACKFILL_ENABLED=${CATALOG_BACKFILL_ENABLED:-} - PREFETCH_CONCURRENCY=${PREFETCH_CONCURRENCY:-} - PREFETCH_RATE_MAX=${PREFETCH_RATE_MAX:-} - PREFETCH_MAX_DEPTH=${PREFETCH_MAX_DEPTH:-} - PREFETCH_RATE_PL24=${PREFETCH_RATE_PL24:-} - PREFETCH_RATE_EMEX=${PREFETCH_RATE_EMEX:-} - PREFETCH_RATE_PCAT=${PREFETCH_RATE_PCAT:-} - PREFETCH_PCAT_DELAY_MS=${PREFETCH_PCAT_DELAY_MS:-} - PREFETCH_PL24_START=${PREFETCH_PL24_START:-} - PREFETCH_PL24_END=${PREFETCH_PL24_END:-} - PREFETCH_NORESULT_MAX=${PREFETCH_NORESULT_MAX:-} - PREFETCH_NORESULT_TTL_DAYS=${PREFETCH_NORESULT_TTL_DAYS:-} - PREFETCH_COMPLETE_TTL_DAYS=${PREFETCH_COMPLETE_TTL_DAYS:-} depends_on: sase-redis: condition: service_healthy healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:4000/api/health"] interval: 15s timeout: 5s retries: 5 start_period: 30s labels: - "traefik.docker.network=coolify" expose: - "4000" networks: - default - coolify worker: build: context: . dockerfile: Dockerfile command: ["node", "--enable-source-maps", "apps/api/dist/worker.js"] environment: - NODE_ENV=production - DATABASE_URL=${DATABASE_URL} - REDIS_HOST=sase-redis - REDIS_PORT=6379 - REDIS_PASSWORD=${REDIS_PASSWORD} - MINIO_ENDPOINT=http://minio-global:9000 - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY} - MINIO_SECRET_KEY=${MINIO_SECRET_KEY} - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME:-sase-schemas} - MINIO_PUBLIC_URL=${MINIO_PUBLIC_URL} - MINIO_USE_SSL=false - PL24_BASE_URL=${PL24_BASE_URL:-https://www.partslink24.com} - PL24_COMPANY_CODE=${PL24_COMPANY_CODE:-} - PL24_USERNAME=${PL24_USERNAME:-} - PL24_PASSWORD=${PL24_PASSWORD:-} - PL24_COMPANY_CODE_2=${PL24_COMPANY_CODE_2:-} - PL24_USERNAME_2=${PL24_USERNAME_2:-} - PL24_PASSWORD_2=${PL24_PASSWORD_2:-} - PL24_PROXY_DE=${PL24_PROXY_DE:-} - EMEX_USERNAME=${EMEX_USERNAME:-} - EMEX_PASSWORD=${EMEX_PASSWORD:-} - EMEX_USE_PROXY=${EMEX_USE_PROXY:-false} - EMEX_PROXY_HOST=${EMEX_PROXY_HOST:-74.81.81.81} - EMEX_PROXY_USER=${EMEX_PROXY_USER:-1726bbe361918676d44e} - EMEX_PROXY_PASS=${EMEX_PROXY_PASS:-f11c7b6128cc86c6} - PCAT_USE_PROXY=${PCAT_USE_PROXY:-true} - PCAT_PROXY_HOST=${PCAT_PROXY_HOST:-gw.dataimpulse.com} - PCAT_PROXY_USER=${PCAT_PROXY_USER:-} - PCAT_PROXY_PASS=${PCAT_PROXY_PASS:-} # Vinpin ePER VIN-decode fallback (worker drives the headless browser) - VINPIN_ENABLED=${VINPIN_ENABLED:-false} - VINPIN_URL=${VINPIN_URL:-https://login.tr.vinpin.online} - VINPIN_USER=${VINPIN_USER:-} - VINPIN_PASS=${VINPIN_PASS:-} # Novu lifecycle e-mail automation — the worker fires trial-ending + win-back - NOVU_API_URL=${NOVU_API_URL:-https://api.bildirim.semih.ai} - NOVU_API_KEY=${NOVU_API_KEY:-} - APP_PUBLIC_URL=${APP_PUBLIC_URL:-https://sase.tr} - MAILTRACK_SECRET=${MAILTRACK_SECRET:-} # PostHog server-side analytics (payments, subscription lifecycle, $revenue). # Empty key → PostHogService no-ops (server-side analytics disabled). - POSTHOG_API_KEY=${POSTHOG_API_KEY:-} # Personal/secure key → LOCAL feature-flag evaluation (source kill switches, # guarded decode rollout, remote config). Empty → flags fail open (inert). - POSTHOG_PERSONAL_API_KEY=${POSTHOG_PERSONAL_API_KEY:-} # "false" on non-prod → evaluate flags but DON'T emit analytics to prod project. - POSTHOG_CAPTURE_ENABLED=${POSTHOG_CAPTURE_ENABLED:-} - POSTHOG_HOST=${POSTHOG_HOST:-https://eu.i.posthog.com} # Meta Conversions API — server-side signup tracking. Empty → CAPI no-ops. - META_CAPI_PIXEL_ID=${META_CAPI_PIXEL_ID:-} - META_CAPI_ACCESS_TOKEN=${META_CAPI_ACCESS_TOKEN:-} - META_CAPI_TEST_EVENT_CODE=${META_CAPI_TEST_EVENT_CODE:-} - OTEL_ENABLED=${OTEL_ENABLED:-false} - OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT:-} - OTEL_EXPORTER_OTLP_HEADERS=${OTEL_EXPORTER_OTLP_HEADERS:-} - OTEL_SERVICE_NAME=${OTEL_SERVICE_NAME:-sase-worker} - OTEL_TRACE_SAMPLE_RATE=${OTEL_TRACE_SAMPLE_RATE:-1.0} # Local catalog-dump lookup (worker drives prefetch which uses CategoriesService). - CATALOG_SOURCE_DB_ENABLED=${CATALOG_SOURCE_DB_ENABLED:-false} - PCAT_SOURCE_DB_URL=${PCAT_SOURCE_DB_URL:-} - EMEX_SOURCE_DB_URL=${EMEX_SOURCE_DB_URL:-} # Per-source kill switches. pcat default off (dump doesn't cover TR vehicles). - EMEX_SOURCE_DB_ENABLED=${EMEX_SOURCE_DB_ENABLED:-true} - PCAT_SOURCE_DB_ENABLED=${PCAT_SOURCE_DB_ENABLED:-false} # Per-catalog parts-lookup allowlist (comma-separated). Empty → all # fetchCategoryParts calls return null. See sase-emex-source-db-safety # memory: catalog-wide bridge had 7-114x noise; only add a catalog once # its per-vehicle bridge is wired & OEM-verified. - EMEX_SOURCE_DB_ALLOWED_CATALOGS=${EMEX_SOURCE_DB_ALLOWED_CATALOGS:-} # Tedarikçi fiyat tazeleme cron'u (19:30 İstanbul) bu env'lerle çalışır; # boşsa processor sessiz no-op. - SUPPLIER_PRICE_DB_ENABLED=${SUPPLIER_PRICE_DB_ENABLED:-false} - SUPPLIER_PRICE_DB_URL=${SUPPLIER_PRICE_DB_URL:-} depends_on: sase-redis: condition: service_healthy networks: - default - coolify sase-redis: image: redis:7.4-alpine command: redis-server --requirepass ${REDIS_PASSWORD} volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 10s timeout: 5s retries: 5 networks: - default volumes: redis_data: networks: coolify: external: true