FASHIONISTAR CI/CD
🔄 Celery Queues Deploy: d915798567b1052828794606dda8e62b4500f337 [GitHub Actions]
607bf71 | .DEFAULT_GOAL := help | |
| # Default to development settings for local make workflows while still allowing | |
| # overrides like: `make DJANGO_SETTINGS_MODULE=backend.config.production <target>`. | |
| DJANGO_SETTINGS_MODULE ?= backend.config.development | |
| export DJANGO_SETTINGS_MODULE | |
| # Keep uv cache inside the repo so local Windows/dev-sandbox runs never depend | |
| # on a global AppData cache with different filesystem permissions. | |
| UV_CACHE_DIR ?= $(subst /,\,$(CURDIR))\.uv-cache | |
| export UV_CACHE_DIR | |
| # Windows `make` recipes run through cmd.exe in this repo, so Redis/bootstrap | |
| # helpers need Windows-friendly commands instead of POSIX shell fragments. | |
| TMP_REDIS_DIR := $(subst /,\,$(abspath ../.tmp_redis)) | |
| TMP_REDIS_EXE := $(TMP_REDIS_DIR)\redis-server.exe | |
| ENSURE_TMP_REDIS = powershell -NoProfile -ExecutionPolicy Bypass -Command "if ((Test-NetConnection -ComputerName 127.0.0.1 -Port 6379 -WarningAction SilentlyContinue).TcpTestSucceeded) { exit 0 }; if (Test-Path '$(TMP_REDIS_EXE)') { Start-Process -FilePath '$(TMP_REDIS_EXE)' -ArgumentList '--port','6379' -WorkingDirectory '$(TMP_REDIS_DIR)' -WindowStyle Hidden; Start-Sleep -Seconds 1 } else { Write-Host 'Portable Redis not found at $(TMP_REDIS_EXE)' }" | |
| # ─── Colors ─── | |
| CYAN := \033[0;36m | |
| GREEN := \033[0;32m | |
| YELLOW := \033[0;33m | |
| RED := \033[0;31m | |
| BOLD := \033[1m | |
| NC := \033[0m | |
| ##@ Help | |
| help: ## Display this help message | |
| @echo "$(BOLD)$(CYAN)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$(NC)" | |
| @echo "$(BOLD)$(CYAN) FASHIONISTAR AI — Backend Developer Commands$(NC)" | |
| @echo "$(CYAN) Django 6.0 · Python 3.14+ · Dual-Engine (DRF + Ninja)$(NC)" | |
| @echo "$(BOLD)$(CYAN)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$(NC)" | |
| @echo "" | |
| @awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make $(CYAN)<target>$(NC)\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " $(CYAN)%-26s$(NC) %s\n", $$1, $$2 } /^##@/ { printf "\n$(YELLOW)%s$(NC)\n", substr($$0, 5) } ' $(MAKEFILE_LIST) | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Development | |
| # ═══════════════════════════════════════════════════════════════ | |
| install: ## Install Python dependencies from requirements.txt | |
| @echo "$(CYAN)Installing dependencies...$(NC)" | |
| uv sync | |
| @echo "$(GREEN)✓ Dependencies installed$(NC)" | |
| install-dev: ## Install dev dependencies (linting, testing, typing) | |
| @echo "$(CYAN)Installing dev dependencies...$(NC)" | |
| uv sync | |
| @echo "$(GREEN)✓ Dev dependencies installed$(NC)" | |
| setup: install install-dev migrate static ## Full first-time setup | |
| @echo "$(GREEN)✓ Setup complete — run 'make dev' to start$(NC)" | |
| shell: ## Open Django interactive shell (development settings) | |
| uv run manage.py shell --settings=$(DJANGO_SETTINGS_MODULE) | |
| shell-plus: ## Open enhanced Django shell (requires django-extensions) | |
| uv run manage.py shell_plus --ipython --settings=$(DJANGO_SETTINGS_MODULE) 2>NUL || uv run manage.py shell --settings=$(DJANGO_SETTINGS_MODULE) | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Database & Migrations | |
| # ═══════════════════════════════════════════════════════════════ | |
| migrate: ## Run makemigrations + migrate | |
| @echo "$(CYAN)Running migrations...$(NC)" | |
| uv run manage.py makemigrations | |
| uv run manage.py migrate | |
| @echo "$(GREEN)✓ Migrations applied$(NC)" | |
| mmig: ## Make migrations (optionally for a specific app: make mmig app=authentication) | |
| @if [ -z "$(app)" ]; then \ | |
| uv run manage.py makemigrations; \ | |
| else \ | |
| uv run manage.py makemigrations "$(app)"; \ | |
| fi | |
| mig: ## Apply migrations (optionally for a specific app: make mig app=authentication) | |
| @if [ -z "$(app)" ]; then \ | |
| uv run manage.py migrate; \ | |
| else \ | |
| uv run manage.py migrate "$(app)"; \ | |
| fi | |
| showmig: ## Show migration status for all apps | |
| uv run manage.py showmigrations | |
| squash: ## Squash migrations for an app (usage: make squash app=authentication start=0001) | |
| uv run manage.py squashmigrations $(app) $(start) | |
| db-reset: ## ⚠️ Reset SQLite database (destructive — dev only) | |
| @echo "$(RED)⚠ Resetting database...$(NC)" | |
| rm -f db.sqlite3 | |
| uv run manage.py makemigrations | |
| uv run manage.py migrate | |
| @echo "$(GREEN)✓ Database reset complete$(NC)" | |
| db-shell: ## Open database shell (dbshell) | |
| uv run manage.py dbshell | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Admin & Users | |
| # ═══════════════════════════════════════════════════════════════ | |
| superuser: ## Create a Django superuser (interactive — uses UnifiedUser via correct settings) | |
| uv run manage.py createsuperuser --settings=$(DJANGO_SETTINGS_MODULE) | |
| su: ## Create UnifiedUser superuser non-interactively (make su EMAIL=x PASS=y) | |
| uv run scripts/create_superuser.py "$(EMAIL)" "$(PASS)" | |
| changepass: ## Change a UnifiedUser password (uses correct settings) | |
| uv run manage.py changepassword --settings=$(DJANGO_SETTINGS_MODULE) | |
| static: ## Collect static files | |
| @echo "$(CYAN)Collecting static files...$(NC)" | |
| uv run manage.py collectstatic --noinput | |
| @echo "$(GREEN)✓ Static files collected$(NC)" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Code Quality & Linting | |
| # ═══════════════════════════════════════════════════════════════ | |
| lint: ## Run Ruff linter on the entire project | |
| @echo "$(CYAN)Running Ruff linter...$(NC)" | |
| uv run ruff check . --fix | |
| @echo "$(GREEN)✓ Linting complete$(NC)" | |
| format: ## Format code with Ruff formatter | |
| @echo "$(CYAN)Formatting code...$(NC)" | |
| uv run ruff format . | |
| @echo "$(GREEN)✓ Code formatted$(NC)" | |
| type-check: ## Run mypy static type checking | |
| @echo "$(CYAN)Running mypy type check...$(NC)" | |
| uv run mypy apps/ --ignore-missing-imports | |
| @echo "$(GREEN)✓ Type check passed$(NC)" | |
| quality: lint format type-check ## Run all code quality checks (lint + format + types) | |
| @echo "$(GREEN)✓ All quality checks passed$(NC)" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Testing | |
| # ═══════════════════════════════════════════════════════════════ | |
| test: ## Run full test suite with pytest | |
| @echo "$(CYAN)Running tests...$(NC)" | |
| uv run pytest --disable-warnings -vv -x | |
| @echo "$(GREEN)✓ Tests passed$(NC)" | |
| test-cov: ## Run tests with HTML coverage report | |
| @echo "$(CYAN)Running tests with coverage...$(NC)" | |
| uv run pytest --cov=apps --cov-report=html --cov-report=term-missing -vv | |
| @echo "$(GREEN)✓ Coverage report generated → htmlcov/index.html$(NC)" | |
| test-fast: ## Run tests without warnings (fast mode) | |
| uv run pytest --disable-warnings -q | |
| test-app: ## Run tests for a specific app (usage: make test-app app=authentication) | |
| uv run pytest apps/$(app)/ -vv | |
| test-watch: ## Run tests in watch mode (requires pytest-watch) | |
| uv run ptw -- --disable-warnings -vv | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Celery & Background Tasks | |
| # ═══════════════════════════════════════════════════════════════ | |
| celery: ## Start Celery worker — general queue (dev settings, console email) | |
| @echo "$(CYAN)Ensuring Redis is running ...$(NC)" | |
| @$(ENSURE_TMP_REDIS) | |
| @echo "$(CYAN)Starting Celery worker (DJANGO_SETTINGS_MODULE=development)...$(NC)" | |
| @echo "$(YELLOW) Pool: solo (Windows-safe: no prefork shared memory)&$(NC)" | |
| @echo "$(YELLOW) Email: console.EmailBackend (OTP printed to THIS terminal)$(NC)" | |
| uv run celery -A backend worker --loglevel=info --pool=solo --events | |
| celery-emails: ## Start Celery worker for email queue (dev, console email visible) | |
| @$(ENSURE_TMP_REDIS) | |
| uv run celery -A backend worker -Q emails --loglevel=info --pool=solo | |
| celery-critical: ## Start Celery worker for critical queue (auto-starts Redis) | |
| @$(ENSURE_TMP_REDIS) | |
| uv run celery -A backend worker -Q critical --loglevel=info --concurrency=2 | |
| celery-analytics: ## Start Celery worker for analytics queue | |
| uv run celery -A backend worker -Q analytics --loglevel=info --concurrency=1 | |
| celery-beat: ## Start Celery Beat scheduler | |
| uv run celery -A backend beat --loglevel=info --scheduler django_celery_beat.schedulers:DatabaseScheduler | |
| flower: ## Start Flower monitoring dashboard (port 5555) | |
| @echo "$(CYAN)Starting Flower at http://localhost:5555$(NC)" | |
| uv run celery -A backend flower --port=5555 | |
| purge-tasks: ## ⚠️ Purge all queued Celery tasks | |
| @echo "$(RED)⚠ Purging all queued tasks...$(NC)" | |
| uv run celery -A backend purge -f | |
| inspect-active: ## Inspect currently active Celery tasks | |
| uv run celery -A backend inspect active | |
| inspect-stats: ## Show Celery worker statistics | |
| uv run celery -A backend inspect stats | |
| start-workers: ## Display instructions to start all workers | |
| @echo "$(BOLD)$(CYAN)Start each in a separate terminal:$(NC)" | |
| @echo " $(CYAN)Terminal 1:$(NC) make celery" | |
| @echo " $(CYAN)Terminal 2:$(NC) make celery-emails" | |
| @echo " $(CYAN)Terminal 3:$(NC) make celery-critical" | |
| @echo " $(CYAN)Terminal 4:$(NC) make celery-beat" | |
| @echo " $(CYAN)Terminal 5:$(NC) make flower" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Docker — Development | |
| # ═══════════════════════════════════════════════════════════════ | |
| docker-build: ## Build Docker image (no cache) | |
| @echo "$(CYAN)Building Docker image...$(NC)" | |
| docker-compose -f ../docker-compose.yml build --no-cache | |
| @echo "$(GREEN)✓ Docker image built$(NC)" | |
| docker-up: ## Start development containers (detached) | |
| @echo "$(CYAN)Starting Docker containers...$(NC)" | |
| docker-compose -f ../docker-compose.yml up -d | |
| @echo "$(GREEN)✓ Containers started$(NC)" | |
| docker-down: ## Stop and remove containers | |
| @echo "$(YELLOW)Stopping containers...$(NC)" | |
| docker-compose -f ../docker-compose.yml down | |
| @echo "$(GREEN)✓ Containers stopped$(NC)" | |
| docker-down-v: ## Stop containers and remove volumes (⚠️ data loss) | |
| @echo "$(RED)⚠ Stopping containers and removing volumes...$(NC)" | |
| docker-compose -f ../docker-compose.yml down -v | |
| docker-restart: docker-down docker-up ## Restart all containers | |
| docker-logs: ## Tail container logs (all services) | |
| docker-compose -f ../docker-compose.yml logs -f | |
| docker-logs-web: ## Tail logs for web service only | |
| docker-compose -f ../docker-compose.yml logs -f web | |
| docker-logs-celery: ## Tail logs for Celery workers | |
| docker-compose -f ../docker-compose.yml logs -f celery-worker celery-beat | |
| docker-ps: ## Show running containers | |
| docker-compose -f ../docker-compose.yml ps | |
| docker-exec: ## Open shell in web container | |
| docker-compose -f ../docker-compose.yml exec web /bin/sh | |
| docker-exec-db: ## Open PostgreSQL shell in db container | |
| docker-compose -f ../docker-compose.yml exec db psql -U $${DB_USER:-postgres} -d $${DB_NAME:-fashionistar} | |
| docker-rebuild: ## Full rebuild (stop → clean → build → start) | |
| @echo "$(CYAN)Full Docker rebuild...$(NC)" | |
| docker-compose -f ../docker-compose.yml down -v | |
| docker-compose -f ../docker-compose.yml build --no-cache | |
| docker-compose -f ../docker-compose.yml up -d | |
| @echo "$(GREEN)✓ Full rebuild complete$(NC)" | |
| docker-compose -f ../docker-compose.yml logs -f | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Docker — Production | |
| # ═══════════════════════════════════════════════════════════════ | |
| prod-up: ## Start production environment | |
| @echo "$(CYAN)Starting production environment...$(NC)" | |
| docker-compose -f ../docker-compose.prod.yml up -d --build | |
| @echo "$(GREEN)✓ Production environment started$(NC)" | |
| prod-down: ## Stop production environment | |
| docker-compose -f ../docker-compose.prod.yml down | |
| prod-logs: ## Tail production logs | |
| docker-compose -f ../docker-compose.prod.yml logs -f | |
| prod-restart: prod-down prod-up ## Restart production environment | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Infrastructure | |
| # ═══════════════════════════════════════════════════════════════ | |
| infra-up: ## Start Redis + PostgreSQL locally via Docker | |
| @echo "$(CYAN)Starting infrastructure services...$(NC)" | |
| docker run -d --name fashionistar-redis -p 6379:6379 redis:7-alpine || echo "$(YELLOW)Redis already running$(NC)" | |
| docker run -d --name fashionistar-postgres -p 5432:5432 \ | |
| -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=fashionistar \ | |
| postgres:17-alpine || echo "$(YELLOW)PostgreSQL already running$(NC)" | |
| @echo "$(GREEN)✓ Infrastructure ready$(NC)" | |
| infra-down: ## Stop infrastructure containers | |
| docker stop fashionistar-redis fashionistar-postgres 2>/dev/null || true | |
| docker rm fashionistar-redis fashionistar-postgres 2>/dev/null || true | |
| @echo "$(GREEN)✓ Infrastructure stopped$(NC)" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Local Redis (Windows Portable) | |
| # ═══════════════════════════════════════════════════════════════ | |
| start-redis: ## Start local portable Redis server on port 6379 (background) | |
| @echo "$(CYAN)Starting local portable Redis server...$(NC)" | |
| @$(ENSURE_TMP_REDIS) | |
| @echo "$(GREEN)✓ Redis start command issued for 127.0.0.1:6379$(NC)" | |
| stop-redis: ## Stop local portable Redis server | |
| @echo "$(CYAN)Stopping local portable Redis server...$(NC)" | |
| taskkill /F /IM redis-server.exe /T 2>NUL || echo "$(YELLOW)Redis is not running$(NC)" | |
| @echo "$(GREEN)✓ Redis stopped$(NC)" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Health Checks & Monitoring | |
| # ═══════════════════════════════════════════════════════════════ | |
| health: ## Check API health endpoint | |
| @echo "$(CYAN)Checking system health...$(NC)" | |
| @curl -sf http://localhost:8001/health/ | uv run python -m json.tool 2>/dev/null || echo "$(RED)✗ API not running on port 8001$(NC)" | |
| health-redis: ## Check Redis connectivity | |
| @echo "$(CYAN)Checking Redis...$(NC)" | |
| @uv run python -c "import redis; r = redis.from_url('$${REDIS_URL:-redis://localhost:6379/0}'); r.ping(); print('\033[0;32m✓ Redis connected\033[0m')" 2>/dev/null || echo "$(RED)✗ Redis not available$(NC)" | |
| test-metrics: ## Check Prometheus metrics endpoint | |
| @echo "$(CYAN)Testing metrics...$(NC)" | |
| @curl -sf http://localhost:8001/metrics/ | head -10 || echo "$(RED)✗ Metrics endpoint not available$(NC)" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ CI/CD Pipeline | |
| # ═══════════════════════════════════════════════════════════════ | |
| ci: quality test ## Run full CI pipeline (lint + format + types + tests) | |
| @echo "$(GREEN)✓ CI pipeline passed$(NC)" | |
| ci-fast: lint test-fast ## Run fast CI pipeline (lint + quick tests) | |
| @echo "$(GREEN)✓ Fast CI pipeline passed$(NC)" | |
| pre-commit: quality ## Pre-commit hook: run all quality checks | |
| @echo "$(GREEN)✓ Pre-commit checks passed$(NC)" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Cleanup | |
| # ═══════════════════════════════════════════════════════════════ | |
| clean: ## Remove Python cache files (.pyc, __pycache__) | |
| @echo "$(YELLOW)Cleaning Python cache...$(NC)" | |
| find . -type f -name '*.pyc' -delete | |
| find . -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null || true | |
| find . -type d -name '.mypy_cache' -exec rm -rf {} + 2>/dev/null || true | |
| find . -type d -name '.pytest_cache' -exec rm -rf {} + 2>/dev/null || true | |
| rm -rf htmlcov/ .coverage | |
| @echo "$(GREEN)✓ Cleaned$(NC)" | |
| clean-docker: ## Remove all Docker containers, images, and volumes | |
| @echo "$(RED)⚠ Cleaning all Docker resources...$(NC)" | |
| docker-compose down -v | |
| docker rmi $$(docker images -q --filter "reference=fashionistar*") 2>/dev/null || true | |
| @echo "$(GREEN)✓ Docker cleaned$(NC)" | |
| clean-all: clean clean-docker ## Nuclear clean (Python cache + Docker) | |
| @echo "$(GREEN)✓ Everything cleaned$(NC)" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Environment & Configuration | |
| # ═══════════════════════════════════════════════════════════════ | |
| env-setup: ## Create .env from .env.example (safe — won't overwrite) | |
| @if [ ! -f .env ]; then \ | |
| cp .env.example .env; \ | |
| echo "$(GREEN)✓ Created .env from .env.example — edit with your secrets$(NC)"; \ | |
| else \ | |
| echo "$(YELLOW)⚠ .env already exists — skipped$(NC)"; \ | |
| fi | |
| env-check: ## Display current environment configuration | |
| @echo "$(CYAN)Current environment:$(NC)" | |
| @if [ -f .env ]; then \ | |
| grep -v '^\s*#' .env | grep -v '^\s*$$' | sed 's/=.*/=***/' ; \ | |
| else \ | |
| echo "$(RED)✗ .env not found — run 'make env-setup'$(NC)"; \ | |
| fi | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Project Information | |
| # ═══════════════════════════════════════════════════════════════ | |
| info: ## Display project information | |
| @echo "$(BOLD)$(CYAN)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$(NC)" | |
| @echo "$(BOLD) FASHIONISTAR AI — Backend$(NC)" | |
| @echo "$(CYAN)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$(NC)" | |
| @echo " Python: $$(uv run python --version 2>&1)" | |
| @echo " Django: $$(uv run python -c 'import django; print(django.VERSION)' 2>/dev/null || echo 'not installed')" | |
| @echo " Architecture: Dual-Engine (DRF Sync + Ninja Async)" | |
| @echo " Database: PostgreSQL 17 / SQLite (dev)" | |
| @echo " Cache: Redis" | |
| @echo " Task Engine: Celery → Django 6.0 Native Tasks" | |
| @echo " API Docs: http://localhost:8001/swagger/" | |
| @echo "$(CYAN)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$(NC)" | |
| urls: ## Display key API endpoints | |
| @echo "$(BOLD)$(CYAN)Key Endpoints:$(NC)" | |
| @echo " $(CYAN)API Root:$(NC) http://localhost:8001/api/" | |
| @echo " $(CYAN)Swagger:$(NC) http://localhost:8001/swagger/" | |
| @echo " $(CYAN)ReDoc:$(NC) http://localhost:8001/redoc/" | |
| @echo " $(CYAN)Admin:$(NC) http://localhost:8001/$${DJANGO_SECRET_ADMIN_URL:-admin/}" | |
| @echo " $(CYAN)Ninja Async:$(NC) http://localhost:8001/api/v1/ninja/" | |
| deps: ## List installed Python packages | |
| uv pip list --format=columns | |
| outdated: ## Check for outdated Python packages | |
| uv pip list --outdated | |
| req-update: ## Freeze current packages to requirements.txt | |
| @echo "$(YELLOW)⚠ Updating requirements.txt from installed packages...$(NC)" | |
| uv pip freeze > requirements.txt | |
| @echo "$(GREEN)✓ requirements.txt updated$(NC)" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Quick Commands | |
| # ═══════════════════════════════════════════════════════════════ | |
| quick-start: env-setup install install-dev migrate static dev ## 🚀 First-time setup → run | |
| quick-docker: docker-build docker-up ## 🐳 Build and start Docker | |
| quick-test: lint test-cov ## 🧪 Lint + test with coverage | |
| full-reset: clean db-reset install migrate static ## 🔄 Nuclear reset → fresh start | |
| @echo "$(GREEN)✓ Full reset complete — run 'make dev' to start$(NC)" | |
| dashboards: ## 📊 Show all service URLs | |
| @echo "$(BOLD)$(CYAN)━━━ Service Dashboards ━━━$(NC)" | |
| @echo " $(CYAN)Django API:$(NC) http://localhost:8001" | |
| @echo " $(CYAN)Swagger UI:$(NC) http://localhost:8001/swagger/" | |
| @echo " $(CYAN)Admin:$(NC) http://localhost:8001/$${DJANGO_SECRET_ADMIN_URL:-admin/}" | |
| @echo " $(CYAN)Flower:$(NC) http://localhost:5555" | |
| @echo " $(CYAN)Prometheus:$(NC) http://localhost:9090" | |
| @echo " $(CYAN)Grafana:$(NC) http://localhost:3000" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ ngrok — Public Tunnels (Cloudinary Webhooks / Mobile Testing) | |
| # ═══════════════════════════════════════════════════════════════ | |
| ## ngrok launcher — Directly targets your installed system ngrok (v3.39+) | |
| ## Bypasses outdated pnpm wrappers to ensure flawless Windows/Agent compatibility. | |
| NGROK ?= ngrok | |
| NGROK_WEB_ADDR ?= 127.0.0.1:4040 | |
| NGROK_URL ?= 127.0.0.1:8001 | |
| ngrok-backend: ## 🌐 🔗 Start ngrok tunnel for backend port 8001 → localhost:8001 (Django ASGI/dev) | |
| @echo "$(CYAN)Starting ngrok tunnel for backend port 8001 → http://$(NGROK_URL) ...$(NC)" | |
| @echo "$(YELLOW) Webhook URL: https://<tunnel>.ngrok-free.app/api/v1/upload/webhook/cloudinary/$(NC)" | |
| @echo "$(YELLOW) ngrok Web UI: http://$(NGROK_WEB_ADDR)$(NC)" | |
| @echo "$(YELLOW) Authtoken: global (stored locally)$(NC)" | |
| @echo "$(YELLOW) Dev server must be running: make uvicorn (port 8001)$(NC)" | |
| $(NGROK) http 8001 | |
| ngrok-dev: ## 🌐 🔗 Start ngrok tunnel for backend port 8001 → localhost:8001 (Alias for ngrok-backend) | |
| @echo "$(CYAN)Starting ngrok tunnel for backend port 8001 → http://$(NGROK_URL) ...$(NC)" | |
| @echo "$(YELLOW) Webhook URL: https://<tunnel>.ngrok-free.app/api/v1/upload/webhook/cloudinary/$(NC)" | |
| @echo "$(YELLOW) ngrok Web UI: http://$(NGROK_WEB_ADDR)$(NC)" | |
| @echo "$(YELLOW) Authtoken: global (stored locally)$(NC)" | |
| @echo "$(YELLOW) Dev server must be running: make uvicorn (port 8001)$(NC)" | |
| $(NGROK) http 8001 | |
| ngrok-asgi: ## 🔗 🌐 Start ngrok tunnel for backend port 8001 → localhost:8001 (Django ASGI/dev) | |
| @echo "$(CYAN)Starting ngrok tunnel → http://$(NGROK_URL) (ASGI) ...$(NC)" | |
| @echo "$(YELLOW) Webhook URL: https://<tunnel>.ngrok-free.app/api/v1/upload/webhook/cloudinary/$(NC)" | |
| @echo "$(YELLOW) ngrok Web UI: http://$(NGROK_WEB_ADDR)$(NC)" | |
| @echo "$(YELLOW) Authtoken: global (stored locally)$(NC)" | |
| @echo "$(YELLOW) Dev server must be running: make run-asgi (port 8001)$(NC)" | |
| $(NGROK) http 8001 | |
| ngrok-url: ## 🔍 Print current active backend ngrok tunnel public URL (inspector API) | |
| @echo "$(CYAN)Active backend ngrok tunnels:$(NC)" | |
| @curl -s http://127.0.0.1:4040/api/tunnels 2>/dev/null | \ | |
| uv run python -c "import sys,json; d=json.load(sys.stdin); [print(' ✔ ' + t['public_url'] + ' -> port 8001:' + t['config']['addr']) for t in d.get('tunnels',[])]" \ | |
| || echo "$(RED)✗ ngrok not running — start with / Run: 'make ngrok-backend' or 'make ngrok-asgi'$(NC)" | |
| ngrok-inspect: ## 🔍 Open ngrok web inspector in browser (localhost:4040) | |
| @echo "$(CYAN)ngrok Web Inspector: http://127.0.0.1:4040$(NC)" | |
| @start http://127.0.0.1:4040 2>/dev/null || xdg-open http://127.0.0.1:4040 2>/dev/null || echo "Open: http://127.0.0.1:4040" | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Local Server Startup Commands | |
| # ═══════════════════════════════════════════════════════════════ | |
| dev: ## Start Django development server (sync WSGI — port 8001, console email) | |
| @echo "$(CYAN)Starting Django dev server (WSGI, DEBUG=True, ConsoleEmail)...$(NC)" | |
| @echo "$(YELLOW) Settings: backend.config.development$(NC)" | |
| @echo "$(YELLOW) Email: console (OTP printed to this terminal)$(NC)" | |
| @echo "$(YELLOW) URL: http://127.0.0.1:8001/$(NC)" | |
| uv run manage.py runserver 0.0.0.0:8001 --settings=$(DJANGO_SETTINGS_MODULE) | |
| uvicorn: ## Start Uvicorn ASGI (dev, port 8001, console email, access logs) | |
| @echo "$(CYAN)Starting Uvicorn ASGI server (development settings)...$(NC)" | |
| @$(ENSURE_TMP_REDIS) | |
| @echo "$(YELLOW) Settings: backend.config.development (ALLOWED_HOSTS=*)$(NC)" | |
| @echo "$(YELLOW) URL: http://127.0.0.1:8001/ or http://localhost:8001/$(NC)" | |
| @echo "$(YELLOW) Logs: Access log printed here (ALL requests — 2xx, 4xx, 5xx)$(NC)" | |
| uv run uvicorn backend.asgi:application --host 0.0.0.0 --port 8001 --reload --ws auto --log-config uvicorn_log_config.json | |
| run-asgi: ## Start ASGI + Uvicorn (auto-starts Redis first) | |
| @echo "$(CYAN)Ensuring Redis is running ...$(NC)" | |
| @$(ENSURE_TMP_REDIS) | |
| @echo "$(CYAN)Starting Uvicorn ASGI server (access logs on)...$(NC)" | |
| uv run uvicorn backend.asgi:application --host 0.0.0.0 --port 8001 --reload --ws auto --log-config uvicorn_log_config.json | |
| run-daphne: ## Start Daphne ASGI (WebSocket — auto-starts Redis first) | |
| @echo "$(CYAN)Ensuring Redis is running ...$(NC)" | |
| @$(ENSURE_TMP_REDIS) | |
| @echo "$(CYAN)Starting Daphne ASGI server (development settings)...$(NC)" | |
| uv run daphne -b 0.0.0.0 -p 8001 backend.asgi:application | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Server & Tunnel Integration Commands | |
| # ═══════════════════════════════════════════════════════════════ | |
| # These cleanly mapped commands handle starting your Django/Uvicorn | |
| # backend alongside ngrok tunnels. Use these to securely expose your | |
| # local environment to the internet for Webhooks or frontend testing. | |
| # ═══════════════════════════════════════════════════════════════ | |
| dev-tunnel: ## 🚀 Start Django WSGI (:8001) dev server & display tunnel instructions | |
| @echo "$(BOLD)$(CYAN)━━━ FASHIONISTAR WSGI + Tunnel ━━━$(NC)" | |
| @echo "$(CYAN)Step 1:$(NC) Starting Django WSGI dev server → :8001 ..." | |
| uv run manage.py runserver 0.0.0.0:8001 --settings=$(DJANGO_SETTINGS_MODULE) & | |
| @echo "" | |
| @echo "$(YELLOW)Step 2 (Action Required): Open a second terminal window and run:$(NC)" | |
| @echo " $(CYAN)make ngrok-dev$(NC)" | |
| asgi-tunnel: ## 🚀 Start Uvicorn ASGI (:8001) server & display tunnel instructions | |
| @echo "$(BOLD)$(CYAN)━━━ FASHIONISTAR ASGI + Tunnel ━━━$(NC)" | |
| @echo "$(CYAN)Step 1:$(NC) Starting Uvicorn ASGI server → :8001 ..." | |
| @$(ENSURE_TMP_REDIS) | |
| @echo "" | |
| @echo "$(YELLOW)Step 2 (Action Required): Open a second terminal window and run:$(NC)" | |
| @echo " $(CYAN)make ngrok-asgi$(NC)" | |
| uv run uvicorn backend.asgi:application --host 0.0.0.0 --port 8001 --reload --ws auto --log-config uvicorn_log_config.json | |
| # ───────────────────────────────────────────────────────────────── | |
| # Sequential Execution Alternatives | |
| # (Runs both processes gracefully in the exact same terminal window) | |
| # ───────────────────────────────────────────────────────────────── | |
| run-dev-then-ngrok: ## 🚀 Sequential: Start Django WSGI (:8001) → then start ngrok | |
| @echo "$(CYAN)Starting Django dev server ...$(NC)" | |
| uv run manage.py runserver 0.0.0.0:8001 --settings=$(DJANGO_SETTINGS_MODULE) | |
| run-ngrok-then-dev: ## 🚀 Sequential: Start ngrok tunnel → then start Django WSGI (:8001) | |
| @echo "$(CYAN)Starting ngrok tunnel → :8001 ...$(NC)" | |
| $(NGROK) http 8001 | |
| @echo "" | |
| @echo "$(CYAN)Starting Django dev server ...$(NC)" | |
| uv run manage.py runserver 0.0.0.0:8001 --settings=$(DJANGO_SETTINGS_MODULE) | |
| run-asgi-then-ngrok: ## 🚀 Sequential: Start Uvicorn ASGI (:8001) → then start ngrok | |
| @echo "$(CYAN)Starting Uvicorn ASGI server → :8001 ...$(NC)" | |
| @$(ENSURE_TMP_REDIS) | |
| uv run uvicorn backend.asgi:application --host 0.0.0.0 --port 8001 --reload --ws auto --log-config uvicorn_log_config.json | |
| run-ngrok-then-asgi: ## 🚀 Sequential: Start ngrok tunnel → then start Uvicorn ASGI (:8001) | |
| @echo "$(CYAN)Starting ngrok tunnel → :8001 ...$(NC)" | |
| $(NGROK) http 8001 | |
| @echo "" | |
| @echo "$(CYAN)Starting Uvicorn ASGI server ...$(NC)" | |
| @$(ENSURE_TMP_REDIS) | |
| uv run uvicorn backend.asgi:application --host 0.0.0.0 --port 8001 --reload --ws auto --log-config uvicorn_log_config.json | |
| # ═══════════════════════════════════════════════════════════════ | |
| ##@ Alternative Aliases & Production Start | |
| # ═══════════════════════════════════════════════════════════════ | |
| run-dev: dev ## Alias: Start Django dev server (same as make dev) | |
| run-uvicorn: uvicorn ## Alias: Start Uvicorn ASGI server (same as make uvicorn) | |
| asgi: run-asgi ## Alias: Start ASGI server with auto-Redis (same as make run-asgi) | |
| daphne: run-daphne ## Alias: Start Daphne ASGI with auto-Redis (same as make run-daphne) | |
| wsgi: ## 🚀 Start Gunicorn WSGI server (sync production — port 8001) | |
| @echo "$(CYAN)Starting Gunicorn WSGI server (Production Mode)...$(NC)" | |
| uv run gunicorn backend.wsgi:application --bind 0.0.0.0:8001 --workers 4 --timeout 120 | |