.PHONY: help setup start stop restart logs clean clean-build-cache test build migrate admin seed-demo quick-dev quick-start upgrade db-backup # Variables DOCKER_COMPOSE = docker compose DOCKER_COMPOSE_DEV = docker compose -f docker-compose.yml -f docker-compose.dev.yml DOCKER_EXEC_BACKEND = docker exec seneca-backend DOCKER_EXEC_FRONTEND = docker exec -it seneca-frontend DOCKER_EXEC_DB = docker exec -it seneca-postgres define wait_for_backend @echo "โณ Waiting for backend to become ready..." @port=$$(grep -E '^BACKEND_PORT=' .env 2>/dev/null | cut -d= -f2); port=$${port:-3001}; \ for i in $$(seq 1 120); do \ if curl -sf "http://localhost:$$port/api/v1/health/ready" >/dev/null 2>&1; then \ break; \ fi; \ sleep 1; \ done endef define wait_for_users_table @echo "โณ Waiting for database schema..." @for i in $$(seq 1 60); do \ if docker exec seneca-postgres psql -U finflow -d finflow -tAc "select 1 from information_schema.tables where table_schema='public' and table_name='users'" 2>/dev/null | grep -q 1; then \ break; \ fi; \ sleep 2; \ done endef ##@ General help: ## Display this help message @awk 'BEGIN {FS = ":.*##"; printf "\n\033[1m\033[36mUsage:\033[0m\n make \033[36m\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) ##@ Setup setup: ## Initial setup - copy env files and generate secrets @echo "๐Ÿš€ Setting up Stoic Budget..." @npm run setup:env @echo "โœ… Setup complete!" install: ## Install dependencies (local development) @echo "๐Ÿ“ฆ Installing dependencies..." @cd backend && npm install @cd frontend && npm install @echo "โœ… Dependencies installed!" ##@ Docker Operations start: ## Start all services in production mode @echo "๐Ÿณ Starting Stoic Budget (production mode)..." @touch .env @$(DOCKER_COMPOSE) up -d --build $(call wait_for_backend) @echo "โœ… Stoic Budget is running!" @echo "Frontend: http://localhost:3000" @echo "Backend: http://localhost:3001/api/v1" @echo "Swagger: http://localhost:3001/api/docs" upgrade: ## Pull release images from GHCR and restart (pin a version: make upgrade VERSION=v1.2.0) @touch .env @$(MAKE) --no-print-directory db-backup @tag=$${VERSION:-$$(grep -E '^IMAGE_TAG=' .env 2>/dev/null | cut -d= -f2)}; tag=$${tag:-latest}; \ echo "โฌ‡๏ธ Pulling Stoic Budget $$tag..."; \ IMAGE_TAG=$$tag $(DOCKER_COMPOSE) pull backend frontend && \ IMAGE_TAG=$$tag $(DOCKER_COMPOSE) up -d --no-build --remove-orphans @echo "โณ Waiting for backend (migrations run on start)..." @port=$$(grep -E '^BACKEND_PORT=' .env 2>/dev/null | cut -d= -f2); port=$${port:-3001}; \ for i in $$(seq 1 300); do \ curl -sf "http://localhost:$$port/api/v1/health/ready" >/dev/null 2>&1 && { echo "โœ… Stoic Budget upgraded"; exit 0; }; \ sleep 1; \ done; \ echo "โŒ Backend not ready after 5 min: docker logs seneca-backend"; exit 1 @docker image prune -f >/dev/null dev: ## Start all services in development mode (with hot reload) @echo "๐Ÿณ Starting Stoic Budget (development mode)..." @touch .env @$(DOCKER_COMPOSE_DEV) up -d --build $(call wait_for_backend) @echo "โœ… Stoic Budget is running in development mode!" @echo "Frontend: http://localhost:3000" @echo "Backend: http://localhost:3001/api/v1" @echo "Swagger: http://localhost:3001/api/docs" @echo "Run 'make seed-demo' to create demo@seneca.dev / demo123" stop: ## Stop all services @echo "๐Ÿ›‘ Stopping Stoic Budget..." @$(DOCKER_COMPOSE) down @echo "โœ… Services stopped!" restart: ## Restart all services @echo "๐Ÿ”„ Restarting Stoic Budget..." @$(DOCKER_COMPOSE) restart @echo "โœ… Services restarted!" clean-build-cache: ## Remove unused Docker build cache (safe; next build re-creates what it needs) @docker builder prune -f @echo "โœ… Build cache pruned!" clean: ## Stop services and remove volumes (also resets the dev node_modules volumes) @echo "๐Ÿงน Cleaning up Stoic Budget..." @$(DOCKER_COMPOSE_DEV) down -v --remove-orphans @$(DOCKER_COMPOSE) down -v --remove-orphans @echo "โœ… Cleanup complete!" ##@ Logs logs: ## View logs from all services @$(DOCKER_COMPOSE) logs -f logs-backend: ## View backend logs @$(DOCKER_COMPOSE) logs -f backend logs-frontend: ## View frontend logs @$(DOCKER_COMPOSE) logs -f frontend logs-db: ## View database logs @$(DOCKER_COMPOSE) logs -f postgres logs-redis: ## View Redis logs @$(DOCKER_COMPOSE) logs -f redis ##@ Development (Local) backend-dev: ## Start backend in development mode (local) @echo "๐Ÿš€ Starting backend in dev mode..." @cd backend && npm run start:dev frontend-dev: ## Start frontend in development mode (local) @echo "๐Ÿš€ Starting frontend in dev mode..." @cd frontend && npm run dev db-start: ## Start only PostgreSQL and Redis @echo "๐Ÿ˜ Starting database services..." @$(DOCKER_COMPOSE) up -d postgres redis @echo "โœ… Database services started!" ##@ Database migrate: ## Run database migrations @echo "๐Ÿ”„ Running migrations..." @$(DOCKER_EXEC_BACKEND) npm run migration:run @echo "โœ… Migrations complete!" migrate-revert: ## Revert last migration @echo "โช Reverting last migration..." @$(DOCKER_EXEC_BACKEND) npm run migration:revert @echo "โœ… Migration reverted!" migrate-generate: ## Generate a new migration (usage: make migrate-generate name=MigrationName) @if [ -z "$(name)" ]; then \ echo "โŒ Error: Migration name required. Usage: make migrate-generate name=MigrationName"; \ exit 1; \ fi @echo "๐Ÿ“ Generating migration: $(name)..." @$(DOCKER_EXEC_BACKEND) npm run migration:generate -- $(name) @echo "โœ… Migration generated!" db-shell: ## Open PostgreSQL shell @echo "๐Ÿ˜ Opening database shell..." @$(DOCKER_EXEC_DB) psql -U finflow -d finflow db-backup: ## Backup database @echo "๐Ÿ’พ Creating database backup..." @# By service, not container name: containers were renamed finflow-* -> seneca-*, @# and the first upgrade after that still finds the old one running. @file=backup_$$(date +%Y%m%d_%H%M%S).sql; \ $(DOCKER_COMPOSE) exec -T postgres sh -c 'pg_dump -U "$$POSTGRES_USER" "$$POSTGRES_DB"' > $$file || { rm -f $$file; exit 1; }; \ echo "โœ… Backup created: $$file" db-restore: ## Restore database (usage: make db-restore file=backup.sql) @if [ -z "$(file)" ]; then \ echo "โŒ Error: Backup file required. Usage: make db-restore file=backup.sql"; \ exit 1; \ fi @echo "๐Ÿ“ฅ Restoring database from $(file)..." @cat $(file) | $(DOCKER_COMPOSE) exec -T postgres sh -c 'psql -U "$$POSTGRES_USER" "$$POSTGRES_DB"' @echo "โœ… Database restored!" ##@ User Management admin: ## Create admin user (usage: make admin email=admin@example.com password=admin123 name="Admin") @if [ -z "$(email)" ] || [ -z "$(password)" ] || [ -z "$(name)" ]; then \ echo "โŒ Error: All parameters required."; \ echo "Usage: make admin email=admin@example.com password=admin123 name=\"Admin User\""; \ exit 1; \ fi @echo "๐Ÿ‘ค Creating admin user..." $(call wait_for_users_table) @if docker inspect -f '{{.State.Running}}' seneca-backend >/dev/null 2>&1; then \ if [ "$$(docker exec seneca-backend printenv NODE_ENV 2>/dev/null)" = "development" ]; then \ docker exec seneca-backend sh -lc 'NODE_OPTIONS=--max-old-space-size=2048 npm run create-admin:dev -- $(email) $(password) "$(name)"'; \ elif docker exec seneca-backend sh -lc 'test -f dist/scripts/create-admin.js'; then \ docker exec seneca-backend node dist/scripts/create-admin.js $(email) $(password) "$(name)"; \ else \ echo "โŒ No create-admin entrypoint available in seneca-backend"; \ exit 1; \ fi; \ else \ cd backend && npm run create-admin -- $(email) $(password) "$(name)"; \ fi @echo "โœ… Admin user created!" seed-demo: ## Create demo user (demo@seneca.dev / demo123) @echo "๐Ÿ‘ค Creating demo user..." $(call wait_for_users_table) @if docker inspect -f '{{.State.Running}}' seneca-backend >/dev/null 2>&1; then \ if [ "$$(docker exec seneca-backend printenv NODE_ENV 2>/dev/null)" = "development" ]; then \ docker exec seneca-backend sh -lc 'NODE_OPTIONS=--max-old-space-size=2048 npm run seed:demo:dev'; \ elif docker exec seneca-backend sh -lc 'test -f dist/scripts/seed-demo.js'; then \ docker exec seneca-backend node dist/scripts/seed-demo.js; \ else \ echo "โŒ No seed-demo entrypoint available in seneca-backend"; \ exit 1; \ fi; \ else \ cd backend && npm run build:scripts && npm run seed:demo; \ fi @echo "โœ… Demo user is ready!" ##@ Testing test: ## Run all tests @echo "๐Ÿงช Running tests..." @cd backend && npm test @cd frontend && npm test @echo "โœ… Tests complete!" test-backend: ## Run backend tests @echo "๐Ÿงช Running backend tests..." @cd backend && npm test test-frontend: ## Run frontend tests @echo "๐Ÿงช Running frontend tests..." @cd frontend && npm test test-watch: ## Run backend tests in watch mode @echo "๐Ÿงช Running tests in watch mode..." @cd backend && npm run test:watch test-cov: ## Run tests with coverage @echo "๐Ÿงช Running tests with coverage..." @cd backend && npm run test:cov @echo "๐Ÿ“Š Coverage report: backend/coverage/lcov-report/index.html" test-e2e: ## Run end-to-end tests @echo "๐Ÿงช Running E2E tests..." @cd backend && npm run test:e2e ##@ Code Quality lint: ## Lint and fix code @echo "๐Ÿ” Linting code (Biome)..." @cd backend && npm run lint @cd frontend && npm run lint @echo "โœ… Linting complete!" lint-check: ## Check linting without fixing @echo "๐Ÿ” Checking linting (Biome)..." @cd backend && npm run lint:check @cd frontend && npm run lint:check @echo "โœ… Linting check complete!" format: ## Format code with Biome @echo "โœจ Formatting code..." @cd backend && npm run format @cd frontend && npm run format @echo "โœ… Formatting complete!" type-check: ## Run TypeScript type checking @echo "๐Ÿ” Type checking..." @cd backend && npm run build @cd frontend && npm run type-check || echo "No type-check script" @echo "โœ… Type checking complete!" ##@ Build build: ## Build for production @echo "๐Ÿ—๏ธ Building Stoic Budget..." @cd backend && npm run build @cd frontend && npm run build @echo "โœ… Build complete!" build-docker: ## Build Docker images @echo "๐Ÿณ Building Docker images..." @$(DOCKER_COMPOSE) build @echo "โœ… Docker images built!" ##@ Utilities shell-backend: ## Open shell in backend container @$(DOCKER_EXEC_BACKEND) bash shell-frontend: ## Open shell in frontend container @$(DOCKER_EXEC_FRONTEND) sh shell-db: ## Open shell in database container @$(DOCKER_EXEC_DB) bash health: ## Check health of all services @echo "๐Ÿฅ Checking service health..." @echo "\n๐Ÿ“ก Backend:" @curl -s http://localhost:3001/api/v1/health || echo "โŒ Backend not responding" @echo "\n๐ŸŒ Frontend:" @curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 || echo "โŒ Frontend not responding" @echo "\n๐Ÿ˜ PostgreSQL:" @$(DOCKER_EXEC_DB) pg_isready -U finflow || echo "โŒ PostgreSQL not ready" @echo "\n๐Ÿ”ด Redis:" @docker exec seneca-redis redis-cli ping || echo "โŒ Redis not responding" @echo "" ps: ## Show running containers @$(DOCKER_COMPOSE) ps stats: ## Show container resource usage @docker stats --no-stream seneca-backend seneca-frontend seneca-postgres seneca-redis ##@ Documentation docs: ## Open API documentation @echo "๐Ÿ“š Opening Swagger documentation..." @open http://localhost:3001/api/docs || xdg-open http://localhost:3001/api/docs || echo "Open http://localhost:3001/api/docs" ##@ Quick Actions quick-dev: ## Zero-config startup: dev containers + demo user @npm run setup:dev:docker quick-start: ## Legacy alias for quick-dev @$(MAKE) quick-dev @echo "" @echo "๐Ÿ“– Next steps:" @echo " - View logs: make logs" @echo " - Stop services: make stop" @echo " - Run tests: make test" @echo " - View all commands: make help" @echo "" update: ## Update dependencies @echo "๐Ÿ“ฆ Updating dependencies..." @cd backend && npm update @cd frontend && npm update @echo "โœ… Dependencies updated!" reset: clean setup start ## Complete reset - clean, setup, and start @echo "โœ… Stoic Budget has been reset!"