From b61da71009477970e4189097f146071af4e52b81 Mon Sep 17 00:00:00 2001 From: user Date: Fri, 19 Sep 2025 18:40:19 +0300 Subject: [PATCH] config fix --- Makefile | 114 +++++++++++++++++++++++++++++++++++++++++++++ compose_name.py | 23 +++++++++ docker-compose.yml | 16 +++---- start.sh | 91 ++++++++++++++++++++++++++---------- 4 files changed, 212 insertions(+), 32 deletions(-) create mode 100644 Makefile create mode 100755 compose_name.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6e2fbeb --- /dev/null +++ b/Makefile @@ -0,0 +1,114 @@ +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 + +.PHONY: help bootstrap start up down stop restart build ps status logs logs-% tail tail-% shell shell-% env-check clean clean-images destroy nuke prune data-paths + +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 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 $(SERVICE) + +logs-%: + $(MAKE) logs SERVICE=$* + +tail: + $(COMPOSE) logs -f --tail=200 $(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 + +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 diff --git a/compose_name.py b/compose_name.py new file mode 100755 index 0000000..0d6c0f4 --- /dev/null +++ b/compose_name.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +import os +import re +import sys + +def sanitize(name: str) -> str: + slug = name.lower() + slug = re.sub(r'[^a-z0-9_-]+', '-', slug) + slug = slug.lstrip('-_') + if not slug: + slug = 'mynetwork' + if not re.match(r'^[a-z0-9]', slug): + slug = f'n{slug}' + return slug + +def main() -> None: + base = sys.argv[1] if len(sys.argv) > 1 else os.getcwd() + resolved = os.path.abspath(base) + name = os.path.basename(resolved) + print(sanitize(name)) + +if __name__ == '__main__': + main() diff --git a/docker-compose.yml b/docker-compose.yml index 32e3636..0aad6c3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,7 +22,7 @@ services: dockerfile: Dockerfile command: python -m app env_file: - - ../.env + - ./.env environment: - NODE_ROLE=primary depends_on: @@ -90,7 +90,7 @@ services: dockerfile: Dockerfile command: python -m app indexer env_file: - - ../.env + - ./.env environment: - NODE_ROLE=worker depends_on: @@ -111,7 +111,7 @@ services: context: ../uploader-bot command: python -m app ton_daemon env_file: - - ../.env + - ./.env environment: - NODE_ROLE=worker depends_on: @@ -132,7 +132,7 @@ services: context: ../uploader-bot command: python -m app license_index env_file: - - ../.env + - ./.env environment: - NODE_ROLE=worker depends_on: @@ -153,7 +153,7 @@ services: context: ../uploader-bot command: python -m app convert_process env_file: - - ../.env + - ./.env environment: - NODE_ROLE=worker depends_on: @@ -175,7 +175,7 @@ services: context: ../uploader-bot command: python -m app convert_v3 env_file: - - ../.env + - ./.env environment: - NODE_ROLE=worker depends_on: @@ -198,7 +198,7 @@ services: context: ../uploader-bot command: python -m app index_scout_v3 env_file: - - ../.env + - ./.env environment: - NODE_ROLE=worker depends_on: @@ -215,7 +215,7 @@ services: context: ../uploader-bot command: python -m app derivative_janitor env_file: - - ../.env + - ./.env environment: - NODE_ROLE=worker depends_on: diff --git a/start.sh b/start.sh index 84dcb96..d404304 100644 --- a/start.sh +++ b/start.sh @@ -4,10 +4,13 @@ set -euo pipefail echo "MY Network Node setup (interactive)" SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) -BASE_DIR="$SCRIPT_DIR/.." -BASE_DIR=$(cd "$BASE_DIR" && pwd) -ENV_FILE="$BASE_DIR/.env" -EXAMPLE_FILE="$BASE_DIR/.env.example" +BASE_DIR=$(cd "$SCRIPT_DIR/.." && pwd) +ENV_FILE="$SCRIPT_DIR/.env" +PROJECT_EXAMPLE="$BASE_DIR/.env.example" +if [[ ! -f "$SCRIPT_DIR/.env.example" && -f "$PROJECT_EXAMPLE" ]]; then + cp "$PROJECT_EXAMPLE" "$SCRIPT_DIR/.env.example" +fi +EXAMPLE_FILE="$SCRIPT_DIR/.env.example" if [[ ! -f "$ENV_FILE" ]]; then if [[ -f "$EXAMPLE_FILE" ]]; then @@ -85,11 +88,21 @@ update_env() { local key=$1 value=$2 if grep -qE "^${key}=" "$ENV_FILE"; then sed -i.bak "s|^${key}=.*$|${key}=${value}|" "$ENV_FILE" + rm -f "$ENV_FILE.bak" else echo "${key}=${value}" >> "$ENV_FILE" fi } +ensure_env_default() { + local key=$1 default=$2 + local cur + cur=$(ini_val "$key") + if [[ -z "$cur" ]]; then + update_env "$key" "$default" + fi +} + update_env NODE_PRIVACY "$NODE_PRIVACY" update_env PUBLIC_HOST "${PUBLIC_HOST:-}" if [[ -n "${PUBLIC_HOST:-}" ]]; then @@ -119,6 +132,39 @@ fi update_env TELEGRAM_API_KEY "$TELEGRAM_API_KEY" update_env CLIENT_TELEGRAM_API_KEY "$CLIENT_TELEGRAM_API_KEY" +ensure_env_default POSTGRES_DB "mysdb" +ensure_env_default POSTGRES_USER "service" +ensure_env_default POSTGRES_PASSWORD "changeme" +ensure_env_default POSTGRES_HOST "db" +ensure_env_default POSTGRES_PORT "5432" +ensure_env_default POSTGRES_FORWARD_PORT "13580" + +pg_user=$(ini_val POSTGRES_USER) +pg_pass=$(ini_val POSTGRES_PASSWORD) +pg_host=$(ini_val POSTGRES_HOST) +pg_port=$(ini_val POSTGRES_PORT) +pg_db=$(ini_val POSTGRES_DB) +update_env DATABASE_URL "postgresql+psycopg2://${pg_user}:${pg_pass}@${pg_host}:${pg_port}/${pg_db}" + +ensure_env_default FRONTEND_HTTP_PORT "13300" +ensure_env_default VITE_SENTRY_DSN "" +current_api_base=$(ini_val VITE_API_BASE_URL) +if [[ -z "$current_api_base" || "$current_api_base" == "https://my-public-node-8.projscale.dev/api/v1" ]]; then + if [[ -n "${PUBLIC_HOST:-}" ]]; then + update_env VITE_API_BASE_URL "${PUBLIC_HOST%/}/api/v1" + else + update_env VITE_API_BASE_URL "http://127.0.0.1:${BACKEND_HTTP_PORT}/api/v1" + fi +fi + +current_api_storage=$(ini_val VITE_API_BASE_STORAGE_URL) +if [[ -z "$current_api_storage" || "$current_api_storage" == "https://my-public-node-8.projscale.dev/api/v1.5/storage" ]]; then + if [[ -n "${PUBLIC_HOST:-}" ]]; then + update_env VITE_API_BASE_STORAGE_URL "${PUBLIC_HOST%/}/api/v1.5/storage" + else + update_env VITE_API_BASE_STORAGE_URL "http://127.0.0.1:${BACKEND_HTTP_PORT}/api/v1.5/storage" + fi +fi generate_kek() { if command -v openssl >/dev/null 2>&1; then openssl rand -base64 32 | tr -d '\n' @@ -191,7 +237,7 @@ PY ensure_content_key_kek # Ensure IPFS swarm key exists for private swarm by default -SWARM_KEY_FILE_DEFAULT="$BASE_DIR/configs/ipfs/swarm.key" +SWARM_KEY_FILE_DEFAULT="$SCRIPT_DIR/ipfs/swarm.key" if [[ ! -f "$SWARM_KEY_FILE_DEFAULT" ]]; then echo "Generating IPFS private swarm key at $SWARM_KEY_FILE_DEFAULT ..." mkdir -p "$(dirname "$SWARM_KEY_FILE_DEFAULT")" @@ -209,7 +255,8 @@ fi update_env IPFS_SWARM_KEY_FILE "$SWARM_KEY_FILE_DEFAULT" # Ensure data directories exist -mkdir -p "$BASE_DIR/data/ipfs" "$BASE_DIR/data/tusd" "$BASE_DIR/app-logs" "$BASE_DIR/dynamicStorage" +mkdir -p "$BASE_DIR/postgres-data" "$BASE_DIR/data/ipfs" "$BASE_DIR/data/tusd" "$BASE_DIR/app-logs" "$BASE_DIR/dynamicStorage" +update_env DB_DATA_DIR_HOST "$BASE_DIR/postgres-data" update_env IPFS_DATA_DIR_HOST "$BASE_DIR/data/ipfs" update_env TUSD_DATA_DIR_HOST "$BASE_DIR/data/tusd" if ! grep -qE '^IPFS_GATEWAY_BIND=' "$ENV_FILE"; then @@ -230,31 +277,27 @@ if ! command -v docker >/dev/null 2>&1; then exit 1 fi -if ! command -v docker compose >/dev/null 2>&1 && ! command -v docker-compose >/dev/null 2>&1; then +if ! docker compose version >/dev/null 2>&1 && ! docker-compose --version >/dev/null 2>&1; then echo "docker compose (v2) or docker-compose is required." >&2 exit 1 fi set -x -COMPOSE_FILE_PATH="$BASE_DIR/configs/docker-compose.yml" -COMPOSE_PROJECT_RAW=${COMPOSE_PROJECT_NAME:-$(basename "$BASE_DIR")} -COMPOSE_PROJECT=$(printf '%s' "$COMPOSE_PROJECT_RAW" | tr '[:upper:]' '[:lower:]' | tr -c '[:alnum:]_-' '-') -while [[ $COMPOSE_PROJECT == -* || $COMPOSE_PROJECT == _* ]]; do - COMPOSE_PROJECT=${COMPOSE_PROJECT#?} -done -if [[ -z $COMPOSE_PROJECT ]]; then - COMPOSE_PROJECT=mynetwork -fi -if [[ $COMPOSE_PROJECT != [a-z0-9]* ]]; then - COMPOSE_PROJECT="n$COMPOSE_PROJECT" -fi -if command -v docker compose >/dev/null 2>&1; then - docker compose --env-file "$ENV_FILE" -p "$COMPOSE_PROJECT" -f "$COMPOSE_FILE_PATH" down --remove-orphans - docker compose --env-file "$ENV_FILE" -p "$COMPOSE_PROJECT" -f "$COMPOSE_FILE_PATH" up -d --build --force-recreate +COMPOSE_FILE_PATH="$SCRIPT_DIR/docker-compose.yml" +COMPOSE_PROJECT=$(python3 "$SCRIPT_DIR/compose_name.py" "$BASE_DIR") + +if docker compose version >/dev/null 2>&1; then + COMPOSE_BIN=(docker compose) +elif docker-compose --version >/dev/null 2>&1; then + COMPOSE_BIN=(docker-compose) else - docker-compose --env-file "$ENV_FILE" -p "$COMPOSE_PROJECT" -f "$COMPOSE_FILE_PATH" down --remove-orphans - docker-compose --env-file "$ENV_FILE" -p "$COMPOSE_PROJECT" -f "$COMPOSE_FILE_PATH" up -d --build --force-recreate + echo "docker compose (v2) or docker-compose is required." >&2 + exit 1 fi + +COMPOSE_ARGS=(--env-file "$ENV_FILE" -p "$COMPOSE_PROJECT" -f "$COMPOSE_FILE_PATH") +"${COMPOSE_BIN[@]}" "${COMPOSE_ARGS[@]}" down --remove-orphans +"${COMPOSE_BIN[@]}" "${COMPOSE_ARGS[@]}" up -d --build --force-recreate set +x echo "Setup complete. Check health: curl -fsS http://127.0.0.1:${BACKEND_HTTP_PORT}/api/system.version"