PROJECT_ROOT := $(abspath $(CURDIR)/..) ENV_FILE := .env COMPOSE_FILE := docker-compose.yml COMPOSE_PROJECT := $(shell python3 $(CURDIR)/compose_name.py $(PROJECT_ROOT)) DOCKER_COMPOSE_BIN := $(shell docker compose version >/dev/null 2>&1 && printf 'docker compose' || { docker-compose --version >/dev/null 2>&1 && printf 'docker-compose'; }) ifeq ($(strip $(DOCKER_COMPOSE_BIN)),) $(error docker compose (v2) or docker-compose is required) endif COMPOSE := $(DOCKER_COMPOSE_BIN) --env-file $(ENV_FILE) -p $(COMPOSE_PROJECT) -f $(COMPOSE_FILE) CONTAINER_NAME = $(COMPOSE_PROJECT)-$(SERVICE)-1 ENV_REQUIRED := TELEGRAM_API_KEY CLIENT_TELEGRAM_API_KEY POSTGRES_DB POSTGRES_USER POSTGRES_PASSWORD DATABASE_URL SANIC_PORT BACKEND_HTTP_PORT # Derive external domain and ports from .env for nginx/bootstrap helpers. # PUBLIC_HOST may look like https://my-public-node-103.projscale.dev or be empty. PUBLIC_HOST_RAW := $(shell awk -F= '$$1=="PUBLIC_HOST"{print $$2}' $(ENV_FILE) 2>/dev/null | tail -n1) DOMAIN_FROM_ENV := $(shell printf '%s\n' "$(PUBLIC_HOST_RAW)" | sed -E 's#^https?://##; s#/.*$$##') DOMAIN ?= $(DOMAIN_FROM_ENV) BACKEND_HTTP_PORT ?= $(shell awk -F= '$$1=="BACKEND_HTTP_PORT"{print $$2}' $(ENV_FILE) 2>/dev/null | tail -n1) FRONTEND_HTTP_PORT ?= $(shell awk -F= '$$1=="FRONTEND_HTTP_PORT"{print $$2}' $(ENV_FILE) 2>/dev/null | tail -n1) TUSD_HTTP_PORT ?= $(shell awk -F= '$$1=="TUSD_HTTP_PORT"{print $$2}' $(ENV_FILE) 2>/dev/null | tail -n1) .PHONY: help bootstrap start up down stop restart build ps status logs logs-% tail tail-% shell shell-% env-check reset-config clean clean-images destroy nuke prune data-paths setup-nginx help: @echo "Available targets:" @echo " make bootstrap # interactive setup + rebuild stack" @echo " make up # start or recreate services" @echo " make down # stop services (keep data)" @echo " make restart # restart services" @echo " make ps # show compose status" @echo " make logs # follow logs (SERVICE=name to filter)" @echo " make logs-backend-app # follow logs for backend" @echo " make shell SERVICE=x # open bash inside service" @echo " make clean # remove containers, keep data" @echo " make destroy CONFIRM=1# nuke containers + volumes + data" @echo " make reset-config # remove .env/.bak and swarm key" @echo " make setup-nginx # install nginx vhost for PUBLIC_HOST (DOMAIN=override) and optionally run certbot" @echo " make env-check # validate critical .env entries" bootstrap start: ./start.sh up: $(COMPOSE) up -d --build build: $(COMPOSE) build down stop: $(COMPOSE) down --remove-orphans restart: $(COMPOSE) down --remove-orphans $(COMPOSE) up -d --build ps status: $(COMPOSE) ps logs: $(COMPOSE) logs -f --since 1h $(SERVICE) logs-%: $(MAKE) logs SERVICE=$* tail: $(COMPOSE) logs -f --since 1h $(SERVICE) tail-%: $(MAKE) tail SERVICE=$* shell: @if [ -z "$(SERVICE)" ]; then \ echo "Specify SERVICE=backend-app (or another service)" >&2; \ exit 1; \ fi @docker exec -it $(CONTAINER_NAME) /bin/bash shell-%: $(MAKE) shell SERVICE=$* env-check: @missing=0; \ for key in $(ENV_REQUIRED); do \ if ! grep -qE "^$${key}=" $(ENV_FILE); then \ echo "[missing] $$key"; \ missing=1; \ else \ val=$$(awk -F'=' -v k=$$key '$$1==k{print substr($$0,index($$0,$$2))}' $(ENV_FILE)); \ if [ -z "$$val" ]; then \ echo "[empty] $$key"; \ missing=1; \ fi; \ fi; \ done; \ if [ $$missing -ne 0 ]; then \ echo "Environment file incomplete" >&2; \ exit 1; \ fi reset-config: rm -f $(ENV_FILE) $(ENV_FILE).bak rm -f ipfs/swarm.key @echo "Configuration artifacts removed. Next run make bootstrap." clean: $(COMPOSE) down --remove-orphans clean-images: $(COMPOSE) down --remove-orphans --rmi local prune: $(COMPOSE) down --remove-orphans --volumes data-paths: @echo "Project root: $(PROJECT_ROOT)" @echo " postgres-data -> $(PROJECT_ROOT)/postgres-data" @echo " app-logs -> $(PROJECT_ROOT)/app-logs" @echo " dynamicStorage -> $(PROJECT_ROOT)/dynamicStorage" @echo " data/ipfs -> $(PROJECT_ROOT)/data/ipfs" @echo " data/tusd -> $(PROJECT_ROOT)/data/tusd" nuke destroy: @if [ "$(CONFIRM)" != "1" ]; then \ echo "Set CONFIRM=1 to remove containers, volumes, and local data" >&2; \ exit 1; \ fi $(COMPOSE) down --remove-orphans --volumes rm -rf $(PROJECT_ROOT)/postgres-data $(PROJECT_ROOT)/app-logs $(PROJECT_ROOT)/dynamicStorage $(PROJECT_ROOT)/data # Configure host nginx as reverse proxy for this node using configs/nginx.conf as a template. # Usage: # sudo make setup-nginx # uses PUBLIC_HOST from .env # sudo make setup-nginx DOMAIN=my-public-node-103.projscale.dev # # This target: # - Validates that DOMAIN and ports are known (PUBLIC_HOST/BACKEND_HTTP_PORT/FRONTEND_HTTP_PORT/TUSD_HTTP_PORT) # - Renders /etc/nginx/sites-available/$(DOMAIN).conf from configs/nginx.conf with the correct domain/ports # - Enables the site and reloads nginx # - Optionally runs certbot --nginx if available to obtain TLS certificates setup-nginx: @if [ "$$(id -u)" -ne 0 ]; then \ echo "setup-nginx must be run as root (to write /etc/nginx and run certbot)"; \ exit 1; \ fi @if [ ! -f "$(ENV_FILE)" ]; then \ echo "$(ENV_FILE) not found; run 'make bootstrap' first." >&2; \ exit 1; \ fi @if [ -z "$(DOMAIN)" ]; then \ echo "DOMAIN is not set and PUBLIC_HOST is empty in $(ENV_FILE)." >&2; \ echo "Set PUBLIC_HOST in .env or pass DOMAIN explicitly, e.g.:" >&2; \ echo " make setup-nginx DOMAIN=my-public-node-103.projscale.dev" >&2; \ exit 1; \ fi @if [ -z "$(BACKEND_HTTP_PORT)" ] || [ -z "$(FRONTEND_HTTP_PORT)" ] || [ -z "$(TUSD_HTTP_PORT)" ]; then \ echo "One of BACKEND_HTTP_PORT/FRONTEND_HTTP_PORT/TUSD_HTTP_PORT is missing in $(ENV_FILE)." >&2; \ echo "Ensure ./start.sh finished and .env contains these ports, then retry." >&2; \ exit 1; \ fi @echo "Configuring nginx for domain $(DOMAIN) ..." @echo " Backend HTTP port: $(BACKEND_HTTP_PORT)" @echo " Frontend HTTP port: $(FRONTEND_HTTP_PORT)" @echo " tusd HTTP port: $(TUSD_HTTP_PORT)" @nginx_conf="/etc/nginx/conf.d/$(DOMAIN).conf"; \ mkdir -p /etc/nginx/conf.d; \ # Render nginx.conf template with actual domain and ports into a dedicated vhost file. # We do NOT touch the distro's default site; this file will coexist alongside it. sed -e 's/my-public-node-8\.projscale\.dev/$(DOMAIN)/g' \ -e 's|/etc/letsencrypt/live/my-public-node-8\.projscale\.dev/|/etc/letsencrypt/live/$(DOMAIN)/|g' \ -e 's/server 127\.0\.0\.1:13200;/server 127.0.0.1:$(BACKEND_HTTP_PORT);/' \ -e 's/server 127\.0\.0\.1:13300;/server 127.0.0.1:$(FRONTEND_HTTP_PORT);/' \ -e 's/server 127\.0\.0\.1:13400;/server 127.0.0.1:$(TUSD_HTTP_PORT);/' \ "$(CURDIR)/nginx.conf" > "$$nginx_conf"; \ if ! nginx -t; then \ echo "nginx configuration test failed; not reloading." >&2; \ exit 1; \ fi; \ systemctl reload nginx || nginx -s reload || true; \ if command -v certbot >/dev/null 2>&1; then \ echo "Attempting to obtain/renew TLS certificate via certbot for $(DOMAIN) ..."; \ certbot --nginx -d "$(DOMAIN)" || echo "certbot failed or was cancelled; ensure certificates are configured manually."; \ else \ echo "certbot not found; install certbot (e.g. 'apt install certbot python3-certbot-nginx') and run:"; \ echo " certbot --nginx -d $(DOMAIN)"; \ fi; \ echo "Nginx setup for $(DOMAIN) complete. Verify HTTPS availability in a browser."