diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..19d3fda --- /dev/null +++ b/.dockerignore @@ -0,0 +1,108 @@ +# Git and version control +.git +.gitignore +.gitattributes + +# Python cache and build artifacts +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Virtual environments +venv/ +env/ +ENV/ +env.bak/ +venv.bak/ + +# IDE and editor files +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store + +# Testing and coverage +.pytest_cache/ +.coverage +htmlcov/ +.tox/ +.nox/ +coverage.xml +*.cover +*.py,cover +.hypothesis/ + +# Documentation +docs/_build/ +.readthedocs.yml + +# Local development files +.env +.env.local +.env.development +.env.test +.env.production + +# Logs +*.log +logs/ + +# Database files (for local development) +*.db +*.sqlite +*.sqlite3 + +# Temporary files +*.tmp +*.temp +.cache/ + +# Node.js (if any frontend assets) +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Docker files (avoid infinite recursion) +Dockerfile* +docker-compose*.yml +.dockerignore + +# GitHub workflows (not needed in container) +.github/ + +# Local data directories +data/ +backups/ + +# Security and keys +*.pem +*.key +secrets/ + +# Misc +LICENSE +CHANGELOG.md +CONTRIBUTING.md +IMPLEMENTATION_PLAN.md diff --git a/docker-compose.yml b/docker-compose.yml index 90a96a1..fd3add7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,10 @@ services: bot: - build: . + build: + context: . + args: + INSTALL_AI: ${INSTALL_AI:-true} + image: guardden:latest container_name: guardden-bot restart: unless-stopped depends_on: @@ -10,13 +14,52 @@ services: - GUARDDEN_DISCORD_TOKEN=${GUARDDEN_DISCORD_TOKEN} - GUARDDEN_DATABASE_URL=postgresql://guardden:guardden@db:5432/guardden - GUARDDEN_LOG_LEVEL=${GUARDDEN_LOG_LEVEL:-INFO} + - GUARDDEN_ALLOWED_GUILDS=${GUARDDEN_ALLOWED_GUILDS:-} + - GUARDDEN_OWNER_IDS=${GUARDDEN_OWNER_IDS:-} - GUARDDEN_AI_PROVIDER=${GUARDDEN_AI_PROVIDER:-none} - GUARDDEN_ANTHROPIC_API_KEY=${GUARDDEN_ANTHROPIC_API_KEY:-} - GUARDDEN_OPENAI_API_KEY=${GUARDDEN_OPENAI_API_KEY:-} volumes: - - ./data:/app/data + - guardden_data:/app/data + - guardden_logs:/app/logs networks: - guardden + healthcheck: + test: ["CMD", "python", "-m", "guardden.health", "--check"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + dashboard: + build: + context: . + target: runtime + image: guardden-dashboard:latest + container_name: guardden-dashboard + restart: unless-stopped + depends_on: + db: + condition: service_healthy + ports: + - "${DASHBOARD_PORT:-8080}:8000" + environment: + - GUARDDEN_DATABASE_URL=postgresql://guardden:guardden@db:5432/guardden + - GUARDDEN_DASHBOARD_BASE_URL=${GUARDDEN_DASHBOARD_BASE_URL:-http://localhost:8080} + - GUARDDEN_DASHBOARD_SECRET_KEY=${GUARDDEN_DASHBOARD_SECRET_KEY} + - GUARDDEN_DASHBOARD_ENTRA_TENANT_ID=${GUARDDEN_DASHBOARD_ENTRA_TENANT_ID} + - GUARDDEN_DASHBOARD_ENTRA_CLIENT_ID=${GUARDDEN_DASHBOARD_ENTRA_CLIENT_ID} + - GUARDDEN_DASHBOARD_ENTRA_CLIENT_SECRET=${GUARDDEN_DASHBOARD_ENTRA_CLIENT_SECRET} + - GUARDDEN_DASHBOARD_DISCORD_CLIENT_ID=${GUARDDEN_DASHBOARD_DISCORD_CLIENT_ID} + - GUARDDEN_DASHBOARD_DISCORD_CLIENT_SECRET=${GUARDDEN_DASHBOARD_DISCORD_CLIENT_SECRET} + - GUARDDEN_DASHBOARD_OWNER_DISCORD_ID=${GUARDDEN_DASHBOARD_OWNER_DISCORD_ID} + - GUARDDEN_DASHBOARD_OWNER_ENTRA_OBJECT_ID=${GUARDDEN_DASHBOARD_OWNER_ENTRA_OBJECT_ID} + - GUARDDEN_DASHBOARD_CORS_ORIGINS=${GUARDDEN_DASHBOARD_CORS_ORIGINS:-} + volumes: + - guardden_logs:/app/logs:ro + networks: + - guardden + command: ["python", "-m", "guardden.dashboard"] db: image: postgres:15-alpine @@ -24,21 +67,87 @@ services: restart: unless-stopped environment: - POSTGRES_USER=guardden - - POSTGRES_PASSWORD=guardden + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-guardden} - POSTGRES_DB=guardden + - POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256 volumes: - postgres_data:/var/lib/postgresql/data + - ./scripts/init-db.sh:/docker-entrypoint-initdb.d/init-db.sh:ro + ports: + - "${POSTGRES_PORT:-5432}:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U guardden -d guardden"] - interval: 5s + interval: 10s timeout: 5s retries: 5 + start_period: 30s + networks: + - guardden + + # Optional: Redis for caching and rate limiting + redis: + image: redis:7-alpine + container_name: guardden-redis + restart: unless-stopped + command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-guardden_redis} + volumes: + - redis_data:/data + ports: + - "${REDIS_PORT:-6379}:6379" + healthcheck: + test: ["CMD", "redis-cli", "--raw", "incr", "ping"] + interval: 10s + timeout: 3s + retries: 5 + networks: + - guardden + + # Optional: Monitoring stack + prometheus: + image: prom/prometheus:latest + container_name: guardden-prometheus + restart: unless-stopped + profiles: + - monitoring + command: + - "--config.file=/etc/prometheus/prometheus.yml" + - "--storage.tsdb.path=/prometheus" + - "--web.console.libraries=/etc/prometheus/console_libraries" + - "--web.console.templates=/etc/prometheus/consoles" + - "--web.enable-lifecycle" + ports: + - "${PROMETHEUS_PORT:-9090}:9090" + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus_data:/prometheus + networks: + - guardden + + grafana: + image: grafana/grafana:latest + container_name: guardden-grafana + restart: unless-stopped + profiles: + - monitoring-grafana + environment: + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin} + ports: + - "${GRAFANA_PORT:-3000}:3000" + volumes: + - grafana_data:/var/lib/grafana + - ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro networks: - guardden networks: guardden: driver: bridge + name: guardden-network volumes: postgres_data: + redis_data: + guardden_data: + guardden_logs: + prometheus_data: + grafana_data: diff --git a/monitoring/README.md b/monitoring/README.md new file mode 100644 index 0000000..2c685bb --- /dev/null +++ b/monitoring/README.md @@ -0,0 +1,37 @@ +# Monitoring integration + +GuardDen exposes Prometheus metrics from the bot and dashboard services. You can +keep using your existing Grafana instance by pointing it at your Prometheus +server (yours or the optional one in this repo). + +## Option A: Use the bundled Prometheus, external Grafana + +Start only Prometheus: + +```bash +docker compose --profile monitoring up -d prometheus +``` + +Add a Prometheus datasource in your Grafana: +- URL from the same Docker network: `http://guardden-prometheus:9090` +- URL from the host: `http://localhost:9090` (or your mapped port) + +## Option B: Use your own Prometheus + Grafana + +Merge the scrape jobs from `monitoring/prometheus.yml` into your Prometheus +config. The main endpoints are: +- bot: `http://:8001/metrics` +- dashboard: `http://:8000/metrics` +- postgres-exporter: `http://:9187/metrics` +- redis-exporter: `http://:9121/metrics` + +Then add (or reuse) a Prometheus datasource in Grafana that points to your +Prometheus URL. + +## Optional internal Grafana + +If you want the repo's Grafana container, enable its profile: + +```bash +docker compose --profile monitoring --profile monitoring-grafana up -d +```