| Server IP : 85.155.190.233 / Your IP : 216.73.216.103 Web Server : nginx/1.24.0 System : Linux antigravity-cli 6.8.0-31-generic #31-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 20 00:40:06 UTC 2024 x86_64 User : wp-moonbloom ( 1001) PHP Version : 8.3.6 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /opt/moonbloom-dashboard/ |
Upload File : |
services:
postgres:
image: postgres:16-alpine
container_name: moonbloom-dash-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
# Auto-applied once (only on first init of an empty data dir) by the
# official postgres entrypoint. Re-running the stack does NOT re-run it.
- ./sql/schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
# No host ports published on purpose — Postgres is reachable only from
# other containers on the internal network (grafana, collector), never
# from the host network or the public internet.
networks:
- moonbloom-dash
grafana:
image: grafana/grafana-oss:latest
container_name: moonbloom-dash-grafana
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
# Loopback only. Public access is added later by an nginx reverse proxy
# on the host (https://analytics.moonbloomwalls.com -> 127.0.0.1:3000)
# with its own TLS via certbot — not part of this compose file.
ports:
- "127.0.0.1:3000:3000"
environment:
GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER}
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
GF_USERS_ALLOW_SIGN_UP: "false"
GF_SERVER_ROOT_URL: ${GF_SERVER_ROOT_URL:-https://analytics.moonbloomwalls.com}
# Business Charts (Apache ECharts inside Grafana) — used for donut KPI
# rings with a centered total number, matching the Phase 0 mockup.
# Maintained by Grafana Labs (VolkovLabs), catalog-signed. If a future
# Grafana image ever rejects it as unsigned, add:
# GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: volkovlabs-echarts-panel
GF_INSTALL_PLUGINS: volkovlabs-echarts-panel
# Exposed so the datasource provisioning file can interpolate them
# ($POSTGRES_DB / $POSTGRES_USER / $POSTGRES_PASSWORD).
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
networks:
- moonbloom-dash
# Data collector — pulls Google Ads / Pinterest Ads / GA4 / Etsy data and
# writes it into Postgres. Code + Dockerfile land in a later phase; the
# build context reference below is enough to wire the stack together now.
#
# Not started by `docker compose up -d` (postgres + grafana only, thanks
# to the "collect" profile). Run on demand or via cron, e.g.:
# docker compose run --rm collector --backfill 90
# docker compose run --rm collector --days 3
collector:
build: ./collector
container_name: moonbloom-dash-collector
env_file:
- .env
environment:
# Path INSIDE the container where the GA4 service-account key is
# mounted (see the volumes entry below and GA4_SA_JSON in .env).
GOOGLE_APPLICATION_CREDENTIALS: ${GA4_SA_JSON}
# Matches collect.py's own defaults — set explicitly so the three Etsy
# volume mounts below are actually picked up.
ETSY_ORDERS_CSV: /data/orders.csv
ETSY_ADS_CSV_DIR: /data/etsy_ads_imports
ETSY_OFFSITE_ADS_CSV: /data/etsy_offsite_ads_imports/offsite_ads_monthly.csv
depends_on:
postgres:
condition: service_healthy
volumes:
# Host path (GA4_SA_JSON_HOST) -> in-container path (GA4_SA_JSON), read-only.
- ${GA4_SA_JSON_HOST}:${GA4_SA_JSON}:ro
# Etsy has no public Ads API — orders.csv is exported by hand from Shop
# Manager, etsy_ads_imports/ holds manually-dropped Etsy Ads CSV
# exports, and etsy_offsite_ads_imports/ holds the hand-maintained
# Offsite Ads monthly snapshot (that product has no export at all).
# All mounted read-only; missing/empty is handled gracefully by
# sources/etsy_csv.py, never a hard failure.
- ${ETSY_ORDERS_CSV_HOST}:/data/orders.csv:ro
- ./etsy_ads_imports:/data/etsy_ads_imports:ro
- ./etsy_offsite_ads_imports:/data/etsy_offsite_ads_imports:ro
networks:
- moonbloom-dash
restart: "no"
profiles:
- collect
networks:
moonbloom-dash:
name: moonbloom-dash-net
volumes:
pgdata:
name: moonbloom-dash-pgdata
grafana-data:
name: moonbloom-dash-grafana-data