diff --git a/BOT/receiver_bridge.py b/BOT/receiver_bridge.py index 0a102c1..a09bd94 100644 --- a/BOT/receiver_bridge.py +++ b/BOT/receiver_bridge.py @@ -32,9 +32,9 @@ def _env(name: str, default: str = "") -> str: return value.strip() -_storage_root_raw = _env("SREBOT_STORAGE_VOL_PATH") +_storage_root_raw = _env("STORAGE_VOL_PATH") if not _storage_root_raw: - raise RuntimeError("SREBOT_STORAGE_VOL_PATH must be set") + raise RuntimeError("STORAGE_VOL_PATH must be set") _STORAGE_ROOT = Path(_storage_root_raw) SREBOT_API_BASE_URL = _env("SREBOT_API_BASE_URL", _env("SREBOT_HTTP_URL", "http://127.0.0.1:6000")).rstrip("/") SREBOT_API_BEARER_TOKEN = _env("SREBOT_API_BEARER_TOKEN") diff --git a/BOT/render_recap.py b/BOT/render_recap.py index 5bfa7bd..c77f10a 100644 --- a/BOT/render_recap.py +++ b/BOT/render_recap.py @@ -231,9 +231,9 @@ def _smooth_rolling_curve(series: list, window: int = 8) -> list: return out -_storage_env = os.environ.get("SREBOT_STORAGE_VOL_PATH", "").strip() +_storage_env = os.environ.get("STORAGE_VOL_PATH", "").strip() if not _storage_env: - raise RuntimeError("SREBOT_STORAGE_VOL_PATH must be set") + raise RuntimeError("STORAGE_VOL_PATH must be set") _STORAGE = Path(_storage_env) SQUADRONS_DB = _STORAGE / "squadrons.db" SQ_BATTLES_DB = _STORAGE / "sq_battles.db" diff --git a/BOT/tests/smoke_player_recap.py b/BOT/tests/smoke_player_recap.py index c3476a8..93ceb74 100644 --- a/BOT/tests/smoke_player_recap.py +++ b/BOT/tests/smoke_player_recap.py @@ -12,9 +12,9 @@ import sys import tempfile from pathlib import Path -_storage_env = os.environ.get("SREBOT_STORAGE_VOL_PATH", "").strip() +_storage_env = os.environ.get("STORAGE_VOL_PATH", "").strip() if not _storage_env: - raise RuntimeError("SREBOT_STORAGE_VOL_PATH must be set") + raise RuntimeError("STORAGE_VOL_PATH must be set") STORAGE = Path(_storage_env) SEASON_START = "1772348400" SEASON_END = "1777852799" diff --git a/BOT/utils.py b/BOT/utils.py index 3a95c9d..b914c7a 100644 --- a/BOT/utils.py +++ b/BOT/utils.py @@ -46,9 +46,9 @@ load_dotenv() def require_storage_dir() -> Path: """Return the configured storage root or fail fast if misconfigured.""" - raw = os.environ.get("SREBOT_STORAGE_VOL_PATH", "").strip() + raw = os.environ.get("STORAGE_VOL_PATH", "").strip() if not raw: - raise RuntimeError("SREBOT_STORAGE_VOL_PATH must be set") + raise RuntimeError("STORAGE_VOL_PATH must be set") return Path(raw) diff --git a/README.md b/README.md index 15cce84..50cadc5 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,14 @@ 1. **Clone the repository** ```bash - git clone https://github.com/Sop-rs/SREBOT_MEOW.git - cd SREBOT_MEOW + git clone git@github.com:FURRO404/BOTS.git + cd BOTS/SREBOT ``` 2. **Set up Python virtual environment** ```bash python3 -m venv .venv - source venv/bin/activate # On Windows: venv\Scripts\activate + source .venv/bin/activate ``` 3. **Install dependencies** @@ -22,18 +22,25 @@ ```bash nano .env ``` - - Edit the existing `.env` in the repo root and keep the storage path there: + + `.env` is the single source of truth for runtime config — `ecosystem.config.js` + has no `env:` blocks; it loads `.env` via `require('dotenv').config()` and PM2 + inherits the variables when spawning each app. Keys SREBOT reads: ```env - SREBOT_DEPLOY_PATH=/absolute/path/to/SREBOT_MEOW DISCORD_KEY=your_discord_bot_token_here DEEPL_KEY=your_deepl_api_key_here # Optional GITHUB_WEBHOOK_SECRET=your_webhook_secret # For auto-deployment - SREBOT_STORAGE_VOL_PATH=/absolute/path/to/storage + STORAGE_VOL_PATH=/absolute/path/to/storage # Shared with TSSBOT SREBOT_API_BEARER_TOKEN=your_internal_api_token # Optional, protects /api/* - SREBOT_EXTERNAL_PORT=18081 # External bridge port + SREBOT_API_PORT=6000 + SREBOT_WEB_PORT=3001 + SREBOT_WEBHOOK_PORT=9000 + SREBOT_EXTERNAL_HOST=0.0.0.0 + SREBOT_EXTERNAL_PORT=18081 SREBOT_EXTERNAL_BEARER_TOKEN=your_external_bridge_token # Optional, protects the bridge API and websocket - SREBOT_EXTERNAL_UPSTREAM_URL=http://127.0.0.1:6000 # Internal SREBOT API to proxy + SREBOT_EXTERNAL_UPSTREAM_URL=http://127.0.0.1:6000 + NODE_ENV=production + PYTHONUNBUFFERED=1 ``` 5. **Run the bot** @@ -47,7 +54,7 @@ It proxies read-only SREBOT queries and broadcasts replay/GOB envelopes over websocket on the same external port. Its outbox/state files live under the shared storage volume configured in -`.env` via `SREBOT_STORAGE_VOL_PATH`. +`.env` via `STORAGE_VOL_PATH`. Useful commands: diff --git a/dateindex.js b/dateindex.js index 1ca0c2c..d745e74 100644 --- a/dateindex.js +++ b/dateindex.js @@ -1,9 +1,9 @@ require('dotenv').config(); const sqlite3 = require('sqlite3').verbose(); const path = require('path'); -const STORAGE_ROOT = (process.env.SREBOT_STORAGE_VOL_PATH || '').trim(); +const STORAGE_ROOT = (process.env.STORAGE_VOL_PATH || '').trim(); if (!STORAGE_ROOT) { - throw new Error('SREBOT_STORAGE_VOL_PATH must be set'); + throw new Error('STORAGE_VOL_PATH must be set'); } const DB_PATH = path.join(STORAGE_ROOT, 'sq_battles.db'); diff --git a/ecosystem.config.js b/ecosystem.config.js index a95c5b4..36d9ac6 100644 --- a/ecosystem.config.js +++ b/ecosystem.config.js @@ -1,6 +1,9 @@ +// Single source of truth for runtime config is SREBOT/.env (loaded here). +// Do NOT add `env:` blocks to apps below — they would override the .env values +// and create two parallel config sources to reason about. require('dotenv').config(); -const DEPLOY_PATH = process.env.SREBOT_DEPLOY_PATH || __dirname; +const DEPLOY_PATH = __dirname; module.exports = { apps: [ @@ -14,10 +17,6 @@ module.exports = { autorestart: true, watch: false, max_memory_restart: '16000M', - env: { - NODE_ENV: 'production', - PYTHONUNBUFFERED: '1' - }, log_file: './logs/bot_combined.log', out_file: './logs/bot_out.log', error_file: './logs/bot_error.log', @@ -27,7 +26,7 @@ module.exports = { restart_delay: 3000 }, - // API Server + // API Server (reads SREBOT_API_PORT from .env) { name: 'srebot-api', script: 'server.js', @@ -38,10 +37,6 @@ module.exports = { autorestart: true, watch: false, max_memory_restart: '4G', - env: { - NODE_ENV: 'production', - PORT: 6000 - }, log_file: './logs/api_combined.log', out_file: './logs/api_out.log', error_file: './logs/api_error.log', @@ -54,6 +49,7 @@ module.exports = { // External bridge for AXBot traffic: // - Proxies read-only API queries to the internal SREBOT API // - Streams bridge envelopes to AXBot over websocket + // Reads SREBOT_EXTERNAL_HOST/PORT/UPSTREAM_URL from .env. { name: 'srebot-axbot', script: 'BOT/srebot_external.py', @@ -63,13 +59,6 @@ module.exports = { autorestart: true, watch: false, max_memory_restart: '1G', - env: { - NODE_ENV: 'production', - PYTHONUNBUFFERED: '1', - SREBOT_EXTERNAL_HOST: '0.0.0.0', - SREBOT_EXTERNAL_PORT: 18081, - SREBOT_EXTERNAL_UPSTREAM_URL: 'http://127.0.0.1:6000' - }, log_file: './logs/axbot_combined.log', out_file: './logs/axbot_out.log', error_file: './logs/axbot_error.log', @@ -79,7 +68,8 @@ module.exports = { restart_delay: 2000 }, - // GitHub Webhook Receiver (auto-deploy on push to main) + // GitHub Webhook Receiver (auto-deploy on push to main). + // Reads SREBOT_WEBHOOK_PORT from .env. { name: 'srebot-webhook', script: 'github_webhook_updater.py', @@ -89,11 +79,6 @@ module.exports = { autorestart: true, watch: false, max_memory_restart: '500M', - env: { - NODE_ENV: 'production', - PYTHONUNBUFFERED: '1', - WEBHOOK_PORT: 9000 - }, log_file: './logs/webhook_combined.log', out_file: './logs/webhook_out.log', error_file: './logs/webhook_error.log', @@ -103,7 +88,7 @@ module.exports = { restart_delay: 2000 }, - // Website (Next.js) + // Website (reads SREBOT_WEB_PORT from .env) { name: 'srebot-web', script: 'server.js', @@ -113,10 +98,6 @@ module.exports = { autorestart: true, watch: false, max_memory_restart: '500M', - env: { - NODE_ENV: 'production', - PORT: 3001 - }, log_file: './logs/web_combined.log', out_file: './logs/web_out.log', error_file: './logs/web_error.log', diff --git a/github_webhook_updater.py b/github_webhook_updater.py index a5fa768..6c66d2d 100644 --- a/github_webhook_updater.py +++ b/github_webhook_updater.py @@ -270,7 +270,7 @@ def health(): if __name__ == '__main__': - port = int(os.environ.get('WEBHOOK_PORT', 9000)) + port = int(os.environ.get('SREBOT_WEBHOOK_PORT', 9000)) logger.info(f"Starting webhook server on port {port}") logger.info(f"Repo path: {REPO_PATH}") app.run(host='0.0.0.0', port=port) diff --git a/scripts/diag_entitlements.py b/scripts/diag_entitlements.py index cd093b8..f8c764c 100644 --- a/scripts/diag_entitlements.py +++ b/scripts/diag_entitlements.py @@ -26,9 +26,9 @@ load_dotenv() APPLICATION_ID = "1254679514466877540" TARGET_GUILD = "1379510072815779961" TOKEN = os.environ.get("DISCORD_KEY", "") -_storage_env = os.environ.get("SREBOT_STORAGE_VOL_PATH", "").strip() +_storage_env = os.environ.get("STORAGE_VOL_PATH", "").strip() if not _storage_env: - raise RuntimeError("SREBOT_STORAGE_VOL_PATH must be set") + raise RuntimeError("STORAGE_VOL_PATH must be set") STORAGE = Path(_storage_env) DB_PATH = STORAGE / "entitlements.db" diff --git a/scripts/leaderboard_score_probe.py b/scripts/leaderboard_score_probe.py index 66d7c6c..6c9cb7c 100644 --- a/scripts/leaderboard_score_probe.py +++ b/scripts/leaderboard_score_probe.py @@ -33,7 +33,7 @@ def _infer_storage_root(auth_file: Path) -> Path: def _prepare_env(storage_root: Path) -> None: - os.environ["SREBOT_STORAGE_VOL_PATH"] = str(storage_root) + os.environ["STORAGE_VOL_PATH"] = str(storage_root) def _score_hit(clan: dict[str, Any], needle: str) -> bool: @@ -72,10 +72,10 @@ async def _main() -> int: auth_file = Path(args.auth_file).expanduser().resolve() storage_root = _infer_storage_root(auth_file) else: - storage_root_env = os.environ.get("SREBOT_STORAGE_VOL_PATH", "").strip() + storage_root_env = os.environ.get("STORAGE_VOL_PATH", "").strip() if not storage_root_env: print( - "SREBOT_STORAGE_VOL_PATH is not set. Export it or pass --auth-file.", + "STORAGE_VOL_PATH is not set. Export it or pass --auth-file.", file=sys.stderr, ) return 2 diff --git a/scripts/migrate_clan_id.py b/scripts/migrate_clan_id.py index 5c9ed41..46af37f 100644 --- a/scripts/migrate_clan_id.py +++ b/scripts/migrate_clan_id.py @@ -46,7 +46,7 @@ logging.basicConfig( log = logging.getLogger("migrate_clan_id") -# Auto-load the repo's .env so SREBOT_STORAGE_VOL_PATH and friends resolve when +# Auto-load the repo's .env so STORAGE_VOL_PATH and friends resolve when # the script is run directly (PM2/services already get them from .env). try: from dotenv import load_dotenv @@ -59,9 +59,9 @@ try: except ImportError: pass -_storage_env = os.environ.get("SREBOT_STORAGE_VOL_PATH", "").strip() +_storage_env = os.environ.get("STORAGE_VOL_PATH", "").strip() if not _storage_env: - raise RuntimeError("SREBOT_STORAGE_VOL_PATH must be set") + raise RuntimeError("STORAGE_VOL_PATH must be set") STORAGE_DIR = Path(_storage_env) SQ_BATTLES_DB = STORAGE_DIR / "sq_battles.db" diff --git a/scripts/migrate_replays.py b/scripts/migrate_replays.py index 2ce2fb6..2a9d3b8 100644 --- a/scripts/migrate_replays.py +++ b/scripts/migrate_replays.py @@ -17,9 +17,9 @@ from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] DEFAULT_SOURCE = REPO_ROOT / "replays" -_storage_env = os.environ.get("SREBOT_STORAGE_VOL_PATH", "").strip() +_storage_env = os.environ.get("STORAGE_VOL_PATH", "").strip() if not _storage_env: - raise RuntimeError("SREBOT_STORAGE_VOL_PATH must be set") + raise RuntimeError("STORAGE_VOL_PATH must be set") DEFAULT_STORAGE = Path(_storage_env) LEGACY_REPLAY_DIR = re.compile(r"^0([0-9a-fA-F]+)$") diff --git a/server.js b/server.js index a0b5947..df2778f 100644 --- a/server.js +++ b/server.js @@ -14,9 +14,9 @@ function parseJsonColumn(data) { return JSON.parse(data); } -const STORAGE_ROOT = (process.env.SREBOT_STORAGE_VOL_PATH || '').trim(); +const STORAGE_ROOT = (process.env.STORAGE_VOL_PATH || '').trim(); if (!STORAGE_ROOT) { - throw new Error('SREBOT_STORAGE_VOL_PATH must be set'); + throw new Error('STORAGE_VOL_PATH must be set'); } const REPLAYS_PATH = path.join(STORAGE_ROOT, 'REPLAYS'); const COMPS_PATH = path.join(STORAGE_ROOT, 'COMPS'); @@ -29,7 +29,7 @@ function replayDataPath(sessionId) { } const app = express(); -const PORT = process.env.PORT || 6000; +const PORT = process.env.SREBOT_API_PORT || 6000; const API_BEARER_TOKEN = process.env.SREBOT_API_BEARER_TOKEN || ''; const ADMIN_BEARER_TOKEN = process.env.SREBOT_ADMIN_BEARER_TOKEN || null; const wlDb = new sqlite3.Database(WL_DB_PATH); diff --git a/srebot.service b/srebot.service index 6101f65..b917c74 100644 --- a/srebot.service +++ b/srebot.service @@ -17,7 +17,7 @@ SyslogIdentifier=srebot # Environment Environment=PYTHONUNBUFFERED=1 -Environment=SREBOT_STORAGE_VOL_PATH=/mnt/HC_Volume_105581488/STORAGE +Environment=STORAGE_VOL_PATH=/mnt/HC_Volume_105581488/STORAGE # Security NoNewPrivileges=true diff --git a/web/server.js b/web/server.js index c3ac1f7..d1a0165 100644 --- a/web/server.js +++ b/web/server.js @@ -89,9 +89,9 @@ function getRosterMemberByUid(uid) { } const sqlite3 = require('sqlite3').verbose(); -const STORAGE_ROOT = (process.env.SREBOT_STORAGE_VOL_PATH || '').trim(); +const STORAGE_ROOT = (process.env.STORAGE_VOL_PATH || '').trim(); if (!STORAGE_ROOT) { - throw new Error('SREBOT_STORAGE_VOL_PATH must be set'); + throw new Error('STORAGE_VOL_PATH must be set'); } const REPLAYS_ROOT = path.join(STORAGE_ROOT, 'REPLAYS'); fs.mkdirSync(REPLAYS_ROOT, { recursive: true }); @@ -175,7 +175,7 @@ function resolveReplaySessionDir(sessionId) { } const app = express(); -const PORT = process.env.PORT || 3001; +const PORT = process.env.SREBOT_WEB_PORT || 3001; const IS_PRIMARY_WORKER = !process.env.NODE_APP_INSTANCE || process.env.NODE_APP_INSTANCE === '0'; // Enable compression for all responses