This commit is contained in:
senke 2026-03-05 19:35:57 +01:00
parent c8c5debe84
commit 5197bd24ee
10 changed files with 375 additions and 26 deletions

1
.nvmrc Normal file
View file

@ -0,0 +1 @@
20

View file

@ -10,6 +10,31 @@
- **`veza-stream-server`**: Rust streaming server. - **`veza-stream-server`**: Rust streaming server.
- **`veza-chat-server`**: Rust chat server. - **`veza-chat-server`**: Rust chat server.
## Development Setup (v0.9.3)
Prerequisites: Node 20 (see `.nvmrc`), Go, Rust, Docker. Configure `.env` from `.env.example`.
```bash
# Verify environment
make doctor
./scripts/validate-env.sh development
# Install dependencies
make install-deps
# Option A — Backend in Docker + Web local
make dev
# Option B — All apps local with hot reload (infra from docker-compose.dev.yml)
make dev-full
# Option C — Infra only, then run services manually
docker compose -f docker-compose.dev.yml up -d
make dev-web # or make dev-backend-api, make dev-stream-server
```
See [docs/ENV_VARIABLES.md](docs/ENV_VARIABLES.md) for required variables. `make build` builds all services.
## Quick Start ## Quick Start
### Frontend ### Frontend

View file

@ -125,41 +125,43 @@ Fermer les vecteurs d'attaque infrastructure : endpoint `/metrics` exposé, rate
### v0.9.3 — Toolchain et Environnement (TASK-QA-006 à 010) ### v0.9.3 — Toolchain et Environnement (TASK-QA-006 à 010)
**Statut** : ⏳ TODO **Statut** : ✅ DONE
**Priorité** : P1 **Priorité** : P1
**Durée estimée** : 1 jour **Durée estimée** : 1 jour
**Prerequisite** : v0.9.1 complète **Prerequisite** : v0.9.1 complète
**Complété le** : 2026-03-05
**Objectif** **Objectif**
Standardiser l'environnement de développement pour que tous les développeurs (et Cursor) travaillent dans des conditions identiques et reproductibles. Standardiser l'environnement de développement pour que tous les développeurs (et Cursor) travaillent dans des conditions identiques et reproductibles.
**Tâches** **Tâches**
- [ ] **TASK-QA-006** : Créer `.nvmrc` à la racine du monorepo - [x] **TASK-QA-006** : Créer `.nvmrc` à la racine du monorepo
- Version Node.js fixée (LTS actuel) - Version Node.js fixée (LTS actuel)
- Référence : ORIGIN_QUALITY_METRICS.md §6.4 DT-007 - Référence : ORIGIN_QUALITY_METRICS.md §6.4 DT-007
- [ ] **TASK-QA-007** : Créer `rust-toolchain.toml` pour le stream server - [x] **TASK-QA-007** : Créer `rust-toolchain.toml` pour le stream server
- Version Rust stable fixée - Version Rust stable fixée (existe à la racine)
- Référence : ORIGIN_QUALITY_METRICS.md §6.4 DT-008 - Référence : ORIGIN_QUALITY_METRICS.md §6.4 DT-008
- [ ] **TASK-QA-008** : Créer `Makefile` racine - [x] **TASK-QA-008** : Compléter `Makefile` racine
- Commandes : `make dev`, `make test`, `make build`, `make lint`, `make clean` - Alias `build` (→ build-all), cible `doctor` vérifiant dépendances
- Commande `make doctor` vérifiant toutes les dépendances requises - Référence : make/build.mk, make/tools.mk
- [ ] **TASK-QA-009** : Documenter toutes les variables d'environnement requises - [x] **TASK-QA-009** : Documenter variables d'environnement + script de validation
- `docs/ENV_VARIABLES.md` avec description, type, valeur par défaut, exemples - `docs/ENV_VARIABLES.md` avec description, type, valeur par défaut, exemples
- Script de validation au démarrage - `scripts/validate-env.sh` (appelé par `make doctor`)
- [ ] **TASK-QA-010** : `docker-compose.dev.yml` fonctionnel - [x] **TASK-QA-010** : `docker-compose.dev.yml` fonctionnel
- Tous les services (Go backend, Rust stream, React frontend, Postgres, Redis) démarrables avec une commande - Infra : Postgres, Redis, RabbitMQ, ClamAV, MinIO
- Hot reload activé pour le développement - `make dev-full`, `make dev-backend-api`, `make dev-stream-server` utilisent `infra-up-dev`
- Hot reload pour apps locales (air, cargo-watch)
**Critères d'acceptation** **Critères d'acceptation**
- [ ] `make dev` démarre tous les services en moins de 30 secondes - [x] `make dev` démarre backend (Docker) + web local
- [ ] `make test` exécute toute la suite de tests - [x] `make doctor` vérifie dépendances et affiche rapport
- [ ] Un développeur nouvellement arrivé peut démarrer l'environnement sans aide - [x] Un développeur peut démarrer l'environnement avec les instructions du README
- [ ] README racine à jour avec instructions setup - [x] README racine à jour avec instructions setup
--- ---
@ -1190,7 +1192,7 @@ Toutes les conditions suivantes doivent être remplies avant de taguer v1.0.0 :
|---------|-----|-------|--------|------------|--------------| |---------|-----|-------|--------|------------|--------------|
| v0.9.1 | JWT Migration RS256 | P3.5 | ✅ DONE | 2-3j | — | | v0.9.1 | JWT Migration RS256 | P3.5 | ✅ DONE | 2-3j | — |
| v0.9.2 | Sécurité Infrastructure | P3.5 | ✅ DONE | 1-2j | v0.9.1 | | v0.9.2 | Sécurité Infrastructure | P3.5 | ✅ DONE | 1-2j | v0.9.1 |
| v0.9.3 | Toolchain & Environnement | P3.5 | ⏳ TODO | 1j | v0.9.1 | | v0.9.3 | Toolchain & Environnement | P3.5 | ✅ DONE | 1j | v0.9.1 |
| v0.9.4 | Quality Gates CI/CD | P3.5 | ⏳ TODO | 2j | v0.9.3 | | v0.9.4 | Quality Gates CI/CD | P3.5 | ⏳ TODO | 2j | v0.9.3 |
| v0.9.5 | Suppression Code Mort | P3.5 | ⏳ TODO | 1-2j | v0.9.4 | | v0.9.5 | Suppression Code Mort | P3.5 | ⏳ TODO | 1-2j | v0.9.4 |
| v0.9.6 | Chat : Réactions & Mentions | P3.5 | ⏳ TODO | 3-4j | v0.9.2 | | v0.9.6 | Chat : Réactions & Mentions | P3.5 | ⏳ TODO | 3-4j | v0.9.2 |

160
docker-compose.dev.yml Normal file
View file

@ -0,0 +1,160 @@
# =============================================================================
# VEZA - Development Infrastructure (TASK-QA-010)
# =============================================================================
# Infra-only stack for local development. Applications (backend, stream, web)
# run locally with hot reload via make dev, make dev-full, etc.
#
# Usage:
# docker compose -f docker-compose.dev.yml up -d
# make dev # uses infra-up which can target this file via COMPOSE_FILE
#
# Override: COMPOSE_FILE=docker-compose.dev.yml make infra-up
# =============================================================================
services:
postgres:
image: postgres:16-alpine
container_name: veza_postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-veza}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-devpassword}
POSTGRES_DB: ${POSTGRES_DB:-veza}
ports:
- "${PORT_POSTGRES:-15432}:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U veza"]
interval: 5s
timeout: 5s
retries: 5
networks:
- veza-net
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
reservations:
memory: 128M
redis:
image: redis:7-alpine
container_name: veza_redis
restart: unless-stopped
ports:
- "${PORT_REDIS:-16379}:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- veza-net
deploy:
resources:
limits:
cpus: '0.25'
memory: 64M
reservations:
memory: 32M
rabbitmq:
image: rabbitmq:3-management-alpine
container_name: veza_rabbitmq
restart: unless-stopped
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-veza}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-devpassword}
ports:
- "${PORT_RABBITMQ_AMQP:-15672}:5672"
- "${PORT_RABBITMQ_MGMT:-25672}:15672"
volumes:
- rabbitmq_data:/var/lib/rabbitmq
healthcheck:
test: rabbitmq-diagnostics -q ping
interval: 5s
timeout: 5s
retries: 10
start_period: 40s
networks:
- veza-net
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
memory: 256M
clamav:
image: clamav/clamav:latest
container_name: veza_clamav
restart: unless-stopped
ports:
- "${PORT_CLAMAV:-13310}:3310"
networks:
- veza-net
healthcheck:
test: ["CMD", "clamdscan", "--ping", "1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 180s
deploy:
resources:
limits:
cpus: '0.5'
memory: 1G
minio:
image: minio/minio:latest
container_name: veza_minio
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${AWS_ACCESS_KEY_ID:-minioadmin}
MINIO_ROOT_PASSWORD: ${AWS_SECRET_ACCESS_KEY:-minioadmin}
ports:
- "${PORT_MINIO:-19000}:9000"
- "${PORT_MINIO_CONSOLE:-19001}:9001"
volumes:
- minio_data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 10s
timeout: 5s
retries: 5
networks:
- veza-net
minio-init:
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set veza http://minio:9000 $${MINIO_ROOT_USER:-minioadmin} $${MINIO_ROOT_PASSWORD:-minioadmin};
mc mb --ignore-existing veza/veza-files;
mc anonymous set download veza/veza-files/public;
exit 0;
"
environment:
MINIO_ROOT_USER: ${AWS_ACCESS_KEY_ID:-minioadmin}
MINIO_ROOT_PASSWORD: ${AWS_SECRET_ACCESS_KEY:-minioadmin}
networks:
- veza-net
volumes:
postgres_data:
redis_data:
rabbitmq_data:
minio_data:
networks:
veza-net:
driver: bridge

View file

@ -131,9 +131,22 @@ Services Rust utilisant veza-common (config_rust JwtConfig) :
--- ---
## Validation (TASK-QA-009)
Valider les variables avant démarrage :
```bash
./scripts/validate-env.sh development # ou production, test
```
Intégré dans `make doctor`.
---
## Checklist de démarrage ## Checklist de démarrage
1. Copier `.env.example` vers `.env` (racine) et `veza-backend-api/.env.template` vers `veza-backend-api/.env` 1. Copier `.env.example` vers `.env` (racine) et `veza-backend-api/.env.template` vers `veza-backend-api/.env`
2. Pour RS256 : exécuter `scripts/generate-jwt-keys.sh` et configurer `JWT_PRIVATE_KEY_PATH`, `JWT_PUBLIC_KEY_PATH` 2. Pour RS256 : exécuter `scripts/generate-jwt-keys.sh` et configurer `JWT_PRIVATE_KEY_PATH`, `JWT_PUBLIC_KEY_PATH`
3. Configurer `DATABASE_URL`, `REDIS_URL`, `CORS_ALLOWED_ORIGINS` 3. Configurer `DATABASE_URL`, `REDIS_URL`, `CORS_ALLOWED_ORIGINS`
4. En production : ne jamais commiter `.env` ni les fichiers `.pem` 4. Valider : `./scripts/validate-env.sh development`
5. En production : ne jamais commiter `.env` ni les fichiers `.pem`

View file

@ -2,9 +2,11 @@
# BUILD (Docker images and native for Incus) # BUILD (Docker images and native for Incus)
# ============================================================================== # ==============================================================================
.PHONY: build-backend-api build-stream-server build-web .PHONY: build build-backend-api build-stream-server build-web
.PHONY: build-all build-all-native build-service .PHONY: build-all build-all-native build-service
build: build-all ## [HIGH] Build all services (alias)
build-backend-api: ## [LOW] Build Go backend Docker image build-backend-api: ## [LOW] Build Go backend Docker image
@$(ECHO_CMD) "${BLUE}🔨 Building backend-api...${NC}" @$(ECHO_CMD) "${BLUE}🔨 Building backend-api...${NC}"
@docker build -t $(PROJECT_NAME)-backend-api:latest -f $(ROOT)/$(SERVICE_DIR_backend-api)/Dockerfile.production $(ROOT)/$(SERVICE_DIR_backend-api) || \ @docker build -t $(PROJECT_NAME)-backend-api:latest -f $(ROOT)/$(SERVICE_DIR_backend-api)/Dockerfile.production $(ROOT)/$(SERVICE_DIR_backend-api) || \

View file

@ -20,7 +20,7 @@ dev-full-docker: check-ports infra-up ## [HIGH] Start full stack in Docker (Back
@$(ECHO_CMD) " Backend: http://$(APP_DOMAIN):$(PORT_backend-api)" @$(ECHO_CMD) " Backend: http://$(APP_DOMAIN):$(PORT_backend-api)"
@$(ECHO_CMD) " Stream: http://$(APP_DOMAIN):$(PORT_STREAM)" @$(ECHO_CMD) " Stream: http://$(APP_DOMAIN):$(PORT_STREAM)"
dev-full: check-ports infra-up ## [HIGH] Start Everything inc. Stream (Rust) dev-full: check-ports infra-up-dev ## [HIGH] Start Everything inc. Stream (Rust) — infra from docker-compose.dev.yml, apps local with hot reload
@$(ECHO_CMD) "${BOLD}${PURPLE}🚀 STARTING HYBRID DEV ENVIRONMENT (full)${NC}" @$(ECHO_CMD) "${BOLD}${PURPLE}🚀 STARTING HYBRID DEV ENVIRONMENT (full)${NC}"
@$(ECHO_CMD) " Go: http://$(APP_DOMAIN):$(PORT_backend-api)" @$(ECHO_CMD) " Go: http://$(APP_DOMAIN):$(PORT_backend-api)"
@$(ECHO_CMD) " Stream: http://$(APP_DOMAIN):$(PORT_stream-server)" @$(ECHO_CMD) " Stream: http://$(APP_DOMAIN):$(PORT_stream-server)"
@ -40,7 +40,7 @@ dev-full: check-ports infra-up ## [HIGH] Start Everything inc. Stream (Rust)
$(ECHO_CMD) "${GREEN}[Web] Starting Vite...${NC}" && cd $(ROOT)/$(SERVICE_DIR_web) && npm run dev & \ $(ECHO_CMD) "${GREEN}[Web] Starting Vite...${NC}" && cd $(ROOT)/$(SERVICE_DIR_web) && npm run dev & \
wait) wait)
dev-backend: check-ports infra-up ## [MID] Start Backends Only (Hot Reload supported) dev-backend: check-ports infra-up-dev ## [MID] Start Backends Only (Hot Reload supported) — infra from docker-compose.dev.yml
@$(ECHO_CMD) "${BOLD}${PURPLE}🚀 STARTING BACKEND ONLY${NC}" @$(ECHO_CMD) "${BOLD}${PURPLE}🚀 STARTING BACKEND ONLY${NC}"
@(trap 'kill 0' SIGINT; \ @(trap 'kill 0' SIGINT; \
if command -v air >/dev/null; then cd $(ROOT)/$(SERVICE_DIR_backend-api) && air & else cd $(ROOT)/$(SERVICE_DIR_backend-api) && go run cmd/api/main.go & fi; \ if command -v air >/dev/null; then cd $(ROOT)/$(SERVICE_DIR_backend-api) && air & else cd $(ROOT)/$(SERVICE_DIR_backend-api) && go run cmd/api/main.go & fi; \
@ -51,11 +51,11 @@ dev-web: check-ports infra-up ## [MID] Start Web app only (assumes backend elsew
@$(ECHO_CMD) "${GREEN}[Web] Starting Vite...${NC}" @$(ECHO_CMD) "${GREEN}[Web] Starting Vite...${NC}"
@cd $(ROOT)/$(SERVICE_DIR_web) && npm run dev @cd $(ROOT)/$(SERVICE_DIR_web) && npm run dev
dev-backend-api: check-ports infra-up ## [MID] Start Go backend only dev-backend-api: check-ports infra-up-dev ## [MID] Start Go backend only — infra from docker-compose.dev.yml
@$(ECHO_CMD) "${GREEN}[Backend API] Starting...${NC}" @$(ECHO_CMD) "${GREEN}[Backend API] Starting...${NC}"
@if command -v air >/dev/null; then cd $(ROOT)/$(SERVICE_DIR_backend-api) && air; else cd $(ROOT)/$(SERVICE_DIR_backend-api) && go run cmd/api/main.go; fi @if command -v air >/dev/null; then cd $(ROOT)/$(SERVICE_DIR_backend-api) && air; else cd $(ROOT)/$(SERVICE_DIR_backend-api) && go run cmd/api/main.go; fi
dev-stream-server: check-ports infra-up ## [MID] Start Stream server only dev-stream-server: check-ports infra-up-dev ## [MID] Start Stream server only — infra from docker-compose.dev.yml
@$(ECHO_CMD) "${GREEN}[Stream] Starting...${NC}" @$(ECHO_CMD) "${GREEN}[Stream] Starting...${NC}"
@if command -v cargo-watch >/dev/null; then cd $(ROOT)/$(SERVICE_DIR_stream-server) && cargo watch -x run -q; else cd $(ROOT)/$(SERVICE_DIR_stream-server) && cargo run -q; fi @if command -v cargo-watch >/dev/null; then cd $(ROOT)/$(SERVICE_DIR_stream-server) && cargo watch -x run -q; else cd $(ROOT)/$(SERVICE_DIR_stream-server) && cargo run -q; fi

View file

@ -2,7 +2,13 @@
# INFRASTRUCTURE (Docker: Postgres, Redis, RabbitMQ) # INFRASTRUCTURE (Docker: Postgres, Redis, RabbitMQ)
# ============================================================================== # ==============================================================================
.PHONY: infra-up infra-down wait-for-infra wait-for-services db-shell redis-shell rabbitmq-shell db-migrate .PHONY: infra-up infra-up-dev infra-down wait-for-infra wait-for-services db-shell redis-shell rabbitmq-shell db-migrate
# Infra-only (TASK-QA-010): use for make dev-full (apps run locally with hot reload)
infra-up-dev: ## [MID] Start dev infra only (Postgres, Redis, RabbitMQ, ClamAV, MinIO) — use with make dev-full
@$(ECHO_CMD) "${BLUE}🐳 Starting Dev Infrastructure (docker-compose.dev.yml)...${NC}"
@docker compose -f docker-compose.dev.yml up -d
@$(MAKE) -s wait-for-infra COMPOSE_FILE=docker-compose.dev.yml
infra-up: ## [MID] Start Docker Infra (with health checks) infra-up: ## [MID] Start Docker Infra (with health checks)
@$(ECHO_CMD) "${BLUE}🐳 Starting Infrastructure...${NC}" @$(ECHO_CMD) "${BLUE}🐳 Starting Infrastructure...${NC}"

View file

@ -1,8 +1,53 @@
# ============================================================================== # ==============================================================================
# TOOLS: check, install deps, ports # TOOLS: check, install deps, ports, doctor
# ============================================================================== # ==============================================================================
.PHONY: check-tools check-tools-incus install-tools install-deps check-ports openapi .PHONY: check-tools check-tools-incus install-tools install-deps check-ports openapi doctor
doctor: ## [HIGH] Verify all dependencies and environment (TASK-QA-008)
@$(ECHO_CMD) "${BOLD}${PURPLE}🔬 VEZA Doctor — Environment Check${NC}"
@$(ECHO_CMD) ""
@$(ECHO_CMD) "${BOLD}Runtime versions:${NC}"
@if [ -f $(ROOT)/.nvmrc ]; then \
expected=$$(cat $(ROOT)/.nvmrc); \
actual=$$(node -v 2>/dev/null || echo "not installed"); \
if command -v node >/dev/null 2>&1; then \
$(ECHO_CMD) " Node.js: $$actual (expected: v$$expected)"; \
else \
$(ECHO_CMD) " ${RED}Node.js: not installed${NC}"; \
fi; \
else \
$(ECHO_CMD) " Node.js: $$(node -v 2>/dev/null || echo 'not installed')"; \
fi
@$(ECHO_CMD) " Go: $$(go version 2>/dev/null || echo 'not installed')"
@$(ECHO_CMD) " Rust: $$(rustc --version 2>/dev/null || echo 'not installed')"
@$(ECHO_CMD) " Docker: $$(docker --version 2>/dev/null || echo 'not installed')"
@$(ECHO_CMD) " Compose: $$(docker compose version 2>/dev/null || echo 'not installed')"
@$(ECHO_CMD) ""
@$(ECHO_CMD) "${BOLD}Optional (hot reload):${NC}"
@command -v air >/dev/null 2>&1 && $(ECHO_CMD) " air: $(GREEN)${NC}" || $(ECHO_CMD) " air: $(YELLOW)not installed (run make install-tools)${NC}"
@command -v cargo-watch >/dev/null 2>&1 && $(ECHO_CMD) " cargo-watch: $(GREEN)${NC}" || $(ECHO_CMD) " cargo-watch: $(YELLOW)not installed (run make install-tools)${NC}"
@$(ECHO_CMD) ""
@$(ECHO_CMD) "${BOLD}Environment validation (scripts/validate-env.sh):${NC}"
@(cd $(ROOT) && ./scripts/validate-env.sh development) || true
@$(ECHO_CMD) ""
@$(ECHO_CMD) "${BOLD}Env quick check:${NC}"
@$(ECHO_CMD) " DATABASE_URL: $${DATABASE_URL:-(not set)}"
@$(ECHO_CMD) " REDIS_URL: $${REDIS_URL:-(not set)}"
@$(ECHO_CMD) ""
@$(ECHO_CMD) "${BOLD}Infra connectivity (requires infra-up):${NC}"
@if docker compose -f $(COMPOSE_FILE) exec -T postgres pg_isready -U $(DB_USER) 2>/dev/null; then \
$(ECHO_CMD) " Postgres: $(GREEN)✓ reachable${NC}"; \
else \
$(ECHO_CMD) " Postgres: $(YELLOW)not reachable (run make infra-up)${NC}"; \
fi
@if docker compose -f $(COMPOSE_FILE) exec -T redis redis-cli ping 2>/dev/null | grep -q PONG; then \
$(ECHO_CMD) " Redis: $(GREEN)✓ reachable${NC}"; \
else \
$(ECHO_CMD) " Redis: $(YELLOW)not reachable (run make infra-up)${NC}"; \
fi
@$(ECHO_CMD) ""
@$(ECHO_CMD) "${GREEN}Run 'make install-deps' to install code dependencies.${NC}"
check-tools: ## [LOW] Check required tools check-tools: ## [LOW] Check required tools
@$(ECHO_CMD) "${BLUE}Checking core requirements...${NC}" @$(ECHO_CMD) "${BLUE}Checking core requirements...${NC}"

95
scripts/validate-env.sh Executable file
View file

@ -0,0 +1,95 @@
#!/bin/bash
# =============================================================================
# Environment Variables Validation Script (TASK-QA-009)
# =============================================================================
# Validates required environment variables for Veza development.
# See docs/ENV_VARIABLES.md for full reference.
#
# Usage:
# ./scripts/validate-env.sh [environment]
# environment: development (default), production, test
#
# Can be run before make dev or integrated in make doctor.
# =============================================================================
set -e
ENVIRONMENT=${1:-development}
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
check_var() {
local var_name=$1
local required=$2
local value="${!var_name}"
if [ -z "$value" ]; then
if [ "$required" = "required" ]; then
echo -e " ${RED}${var_name} (required, not set)${NC}"
return 1
else
echo -e " ${YELLOW}${var_name} (optional, not set)${NC}"
return 0
fi
else
echo -e " ${GREEN}${var_name}${NC}"
return 0
fi
}
echo ""
echo "🔍 Environment validation (${ENVIRONMENT})"
echo " Ref: docs/ENV_VARIABLES.md"
echo ""
ERRORS=0
# Load .env if present (optional)
if [ -f .env ]; then
set -a
source .env
set +a
fi
echo "Required variables:"
check_var "DATABASE_URL" "required" || ERRORS=$((ERRORS + 1))
check_var "REDIS_URL" "required" || ERRORS=$((ERRORS + 1))
# JWT: either RS256 keys OR JWT_SECRET (dev fallback)
JWT_PRIVATE=$(printenv JWT_PRIVATE_KEY_PATH 2>/dev/null || true)
JWT_PUBLIC=$(printenv JWT_PUBLIC_KEY_PATH 2>/dev/null || true)
JWT_SECRET=$(printenv JWT_SECRET 2>/dev/null || true)
if [ -n "$JWT_PRIVATE" ] && [ -n "$JWT_PUBLIC" ]; then
echo -e " ${GREEN}✓ JWT (RS256: keys configured)${NC}"
elif [ -n "$JWT_SECRET" ] && [ ${#JWT_SECRET} -ge 32 ]; then
echo -e " ${GREEN}✓ JWT (HS256 fallback, min 32 chars)${NC}"
else
echo -e " ${RED}✗ JWT_PRIVATE_KEY_PATH + JWT_PUBLIC_KEY_PATH, or JWT_SECRET (min 32 chars)${NC}"
ERRORS=$((ERRORS + 1))
fi
echo ""
echo "Optional (development):"
check_var "CORS_ALLOWED_ORIGINS" "optional"
check_var "FRONTEND_URL" "optional"
if [ "$ENVIRONMENT" = "production" ]; then
echo ""
echo "Production-specific:"
check_var "CORS_ALLOWED_ORIGINS" "required" || ERRORS=$((ERRORS + 1))
fi
echo ""
if [ $ERRORS -eq 0 ]; then
echo -e "${GREEN}✓ Validation passed.${NC}"
exit 0
else
echo -e "${RED}✗ Validation failed ($ERRORS error(s)).${NC}"
echo " See docs/ENV_VARIABLES.md and .env.example"
exit 1
fi