diff --git a/router/cashu.py b/router/cashu.py index 998054a3..3934db0c 100644 --- a/router/cashu.py +++ b/router/cashu.py @@ -19,19 +19,19 @@ DEVS_DONATION_RATE = float(os.environ.get("DEVS_DONATION_RATE", 0.021)) # 2.1% NSEC = os.environ["NSEC"] # Nostr private key for the wallet CURRENCY = cast(CurrencyUnit, os.environ.get("CURRENCY", "sat")) -WALLET: Wallet | None = None +wallet_instance: Wallet | None = None async def init_wallet() -> None: - global WALLET - WALLET = await Wallet.create(nsec=NSEC, mint_urls=[MINT], currency=CURRENCY) + global wallet_instance + wallet_instance = await Wallet.create(nsec=NSEC) def wallet() -> Wallet: - global WALLET - if WALLET is None: + global wallet_instance + if wallet_instance is None: raise ValueError("Wallet not initialized") - return WALLET + return wallet_instance async def delete_key_if_zero_balance(key: ApiKey, session: AsyncSession) -> None: diff --git a/scripts/auto_update.sh b/scripts/auto_update.sh index 0071c04f..13911ed1 100755 --- a/scripts/auto_update.sh +++ b/scripts/auto_update.sh @@ -9,6 +9,21 @@ LOG_FILE="${LOG_FILE:-/home/ubuntu/proxy/update.log}" LOCK_FILE="${LOCK_FILE:-/tmp/proxy_update.lock}" MAX_LOG_LINES="${MAX_LOG_LINES:-10000}" # Maximum number of log lines to keep +# Detect which Docker Compose command is available +detect_docker_compose_cmd() { + if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then + echo "docker compose" + elif command -v docker-compose >/dev/null 2>&1; then + echo "docker-compose" + else + log_message "ERROR: Neither 'docker compose' nor 'docker-compose' is available" + exit 1 + fi +} + +# Set the Docker Compose command +DOCKER_COMPOSE_CMD=$(detect_docker_compose_cmd) + # Function to log messages with timestamp log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE" @@ -68,11 +83,11 @@ if [ "$LOCAL_HASH" != "$REMOTE_HASH" ]; then # Stop current containers log_message "Stopping current containers..." - sudo docker compose down 2>&1 | tee -a "$LOG_FILE" + sudo $DOCKER_COMPOSE_CMD down 2>&1 | tee -a "$LOG_FILE" # Build and start updated containers log_message "Building and starting updated containers..." - if sudo docker compose up -d --build 2>&1 | tee -a "$LOG_FILE"; then + if sudo $DOCKER_COMPOSE_CMD up -d --build 2>&1 | tee -a "$LOG_FILE"; then log_message "Successfully updated and restarted containers" else log_message "ERROR: Failed to start containers" diff --git a/scripts/crontab.example b/scripts/crontab.example index 929e452b..a24c839a 100644 --- a/scripts/crontab.example +++ b/scripts/crontab.example @@ -1,3 +1,7 @@ REPO_DIR=/home/user/proxy LOG_FILE=/home/user/proxy/update.log -* * * * * /home/user/proxy/scripts/auto_update.sh >/dev/null 2>&1 \ No newline at end of file +* * * * * /home/user/proxy/scripts/auto_update.sh >/dev/null 2>&1 + +OUTPUT_FILE=/home/user/proxy/models.json +BASE_URL=https://openrouter.ai/api/v1 +0 * * * * python3 /home/user/proxy/scripts/models_meta.py >/dev/null 2>&1 diff --git a/scripts/models_meta.py b/scripts/models_meta.py old mode 100644 new mode 100755 index 79813c1e..1b441c28 --- a/scripts/models_meta.py +++ b/scripts/models_meta.py @@ -1,4 +1,7 @@ +#!/usr/bin/env python3 + import json +import os from typing import TypedDict from urllib.request import urlopen @@ -38,9 +41,13 @@ class Model(TypedDict): per_request_limits: dict | None +OUTPUT_FILE = os.getenv("OUTPUT_FILE", "models.json") +BASE_URL = os.getenv("BASE_URL", "https://openrouter.ai/api/v1") + + def fetch_openrouter_models() -> list[Model]: """Fetches model information from OpenRouter API.""" - with urlopen("https://openrouter.ai/api/v1/models") as response: + with urlopen(f"{BASE_URL}/models") as response: data = json.loads(response.read().decode("utf-8")) models_data: list[Model] = [] @@ -64,9 +71,10 @@ def main() -> None: models = fetch_openrouter_models() # Print the first model data in a nicely indented JSON format - print(json.dumps(models[0], indent=4)) + # print(json.dumps(models[0], indent=4)) + print(f"Writing {len(models)} models to {OUTPUT_FILE}") - with open("or-models.json", "w") as f: + with open(OUTPUT_FILE, "w") as f: json.dump({"models": models}, f, indent=4) diff --git a/tests/test_shutdown.py b/tests/test_shutdown.py deleted file mode 100644 index b6324cda..00000000 --- a/tests/test_shutdown.py +++ /dev/null @@ -1,58 +0,0 @@ -import asyncio -from unittest.mock import AsyncMock, MagicMock, patch - -import pytest - -from router.main import app, lifespan -from tests.conftest import TEST_ENV - - -@pytest.mark.asyncio -async def test_background_tasks_cancel_on_shutdown() -> None: - pricing_started = asyncio.Event() - pricing_cancelled = asyncio.Event() - - async def fake_update() -> None: - pricing_started.set() - try: - await asyncio.Event().wait() - except asyncio.CancelledError: - pricing_cancelled.set() - raise - - refund_started = asyncio.Event() - refund_cancelled = asyncio.Event() - - async def fake_refund() -> None: - refund_started.set() - try: - await asyncio.Event().wait() - except asyncio.CancelledError: - refund_cancelled.set() - raise - - with patch.dict("os.environ", TEST_ENV, clear=True): - mock_wallet = AsyncMock() - mock_wallet.__aenter__ = AsyncMock(return_value=mock_wallet) - mock_wallet.__aexit__ = AsyncMock(return_value=None) - mock_state = MagicMock() - mock_state.balance = 1000 - mock_wallet.fetch_wallet_state = AsyncMock(return_value=mock_state) - mock_wallet.send_to_lnurl = AsyncMock(return_value=100) - mock_wallet.redeem = AsyncMock(return_value=(1, "sat")) - mock_wallet.send = AsyncMock(return_value="cashuAtoken123") - - with ( - patch("router.cashu.Wallet.create", AsyncMock(return_value=mock_wallet)), - patch("router.cashu.WALLET", mock_wallet), - ): - with ( - patch("router.main.update_sats_pricing", new=fake_update), - patch("router.main.check_for_refunds", new=fake_refund), - ): - async with lifespan(app): - await pricing_started.wait() - await refund_started.wait() - - assert pricing_cancelled.is_set() - assert refund_cancelled.is_set()