mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-08-09 11:04:36 +00:00
release-v-0-1-0
v 0 1 0
This commit is contained in:
+2
-2
@@ -24,13 +24,13 @@ MODEL_BASED_PRICING = "true"
|
||||
# EXCHANGE_FEE = "1.005" # 0.5 % currency exchange fee
|
||||
|
||||
# password used to log into admin interface
|
||||
ADMIN_PASSWORD="CHANGE-THIS"
|
||||
# ADMIN_PASSWORD=""
|
||||
|
||||
# Public Endpoint
|
||||
HTTP_URL="https://your.domain.com"
|
||||
|
||||
# Tor Endpoint (copy from docker logs)
|
||||
ONION_URL=".onion"
|
||||
# ONION_URL=".onion"
|
||||
|
||||
RELAYS="wss://relay.routstr.com,wss://relay.nostr.band"
|
||||
CASHU_MINTS="https://mint.minibits.cash/Bitcoin,https://mint.cubabitcoin.org"
|
||||
|
||||
@@ -9,6 +9,7 @@ wallet.sqlite3
|
||||
.*wallet.sqlite3
|
||||
*models.json
|
||||
.cashu
|
||||
.dockerignore
|
||||
|
||||
compose.override.yml
|
||||
|
||||
|
||||
@@ -32,22 +32,32 @@ sequenceDiagram
|
||||
- **Cashu Wallet Integration** – Accept Lightning payments and redeem eCash tokens before forwarding requests
|
||||
- **API Key Management** – Hashed keys stored in SQLite with balance tracking and optional expiry/refund address
|
||||
- **Model-Based Pricing** – Convert USD prices in `models.json` to sats using live BTC/USD rates
|
||||
- **Admin Dashboard** – Simple HTML interface at `/admin` to view balances and API keys
|
||||
- **Admin Dashboard** – Simple HTML interface at `/admin/` to view balances and API keys
|
||||
- **Discovery** – Fetch available providers from Nostr relays
|
||||
- **Docker Support** – Provided `Dockerfile` and `compose.yml` for running with an optional Tor hidden service
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Requirements
|
||||
### Running the proxy using Docker
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name routstr-proxy \
|
||||
-p 8000:8000 \
|
||||
-e UPSTREAM_BASE_URL=https://api.openai.com/v1 \
|
||||
-e UPSTREAM_API_KEY=your-openai-api-key \
|
||||
ghcr.io/routstr/proxy:latest
|
||||
```
|
||||
|
||||
### Development Requirements
|
||||
|
||||
- Python 3.11+
|
||||
- [uv](https://github.com/astral-sh/uv) package manager (used in development)
|
||||
- A Cashu wallet secret (`NSEC`) and Lightning address for receiving payments
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
uv sync --dev # install dependencies
|
||||
uv sync # install dependencies
|
||||
```
|
||||
|
||||
Create a `.env` file based on `.env.example` and fill in the required values:
|
||||
@@ -78,15 +88,18 @@ The most common settings are shown below. See `.env.example` for the full list.
|
||||
|
||||
- `UPSTREAM_BASE_URL` – URL of the OpenAI-compatible service
|
||||
- `UPSTREAM_API_KEY` – API key for the upstream service (optional)
|
||||
- `RECEIVE_LN_ADDRESS` – Lightning address that receives payouts
|
||||
- `MINIMUM_PAYOUT` – Minimum sats before forwarding earnings
|
||||
- `MODEL_BASED_PRICING` – Set to `true` to use pricing from `models.json`
|
||||
- `REFUND_PROCESSING_INTERVAL` – Seconds between automatic refunds
|
||||
- `ADMIN_PASSWORD` – Password for the `/admin` dashboard
|
||||
- `ADMIN_PASSWORD` – Password for the `/admin/` dashboard
|
||||
- `CASHU_MINTS` – Comma-separated list of Cashu mint URLs
|
||||
- `NAME` – Name of the proxy
|
||||
- `DESCRIPTION` – Description of the proxy
|
||||
- `NPUB` – Nostr public key of the proxy
|
||||
- `HTTP_URL` – Public-facing URL of the proxy
|
||||
- `ONION_URL` – Tor hidden service URL of the proxy
|
||||
|
||||
## Withdrawing Balance
|
||||
|
||||
Go to `https://<your.routstr.proxy>/admin/` (NOTE: be sure to add the '/' at the end), enter the `ADMIN_PASSWORD` you set above and withdraw your balance as a Cashu token.
|
||||
Go to `https://<your.routstr.proxy>/admin/` (NOTE: be sure to add the '/' at the end), enter the `ADMIN_PASSWORD` you set above and withdraw your balance as a Cashu token.
|
||||
|
||||
## Example Client
|
||||
|
||||
@@ -147,7 +160,8 @@ The proxy should implement either a dedicated endpoint to communicate minimum eC
|
||||
|
||||
To use this feature, you'll need a client that handles both OpenAI API calls and eCash header management. The following clients provide seamless integration:
|
||||
|
||||
- **[cashu-402-client](https://github.com/9qeklajc/ecash-402-client)** – rust client with automatic wallet management
|
||||
- **[routstr-chat](https://github.com/routstr/routstr-chat)** – chat app for the routstr network
|
||||
- **[otrta-client](https://github.com/routstr/otrta-client)** – rust web app for the routstr network
|
||||
|
||||
clients automatically:
|
||||
|
||||
|
||||
@@ -276,6 +276,7 @@ def setup_logging() -> None:
|
||||
"propagate": False,
|
||||
},
|
||||
"watchfiles.main": {"level": "WARNING", "handlers": [], "propagate": False},
|
||||
"aiosqlite": {"level": "ERROR", "handlers": [], "propagate": False},
|
||||
},
|
||||
"root": {
|
||||
"level": log_level,
|
||||
|
||||
+3
-7
@@ -10,7 +10,7 @@ from ..balance import balance_router, deprecated_wallet_router
|
||||
from ..discovery import providers_router
|
||||
from ..payment.models import MODELS, models_router, update_sats_pricing
|
||||
from ..proxy import proxy_router
|
||||
from ..wallet import check_for_refunds, periodic_payout
|
||||
from ..wallet import periodic_payout
|
||||
from .admin import admin_router
|
||||
from .db import init_db
|
||||
from .logging import get_logger, setup_logging
|
||||
@@ -19,7 +19,7 @@ from .logging import get_logger, setup_logging
|
||||
setup_logging()
|
||||
logger = get_logger(__name__)
|
||||
|
||||
__version__ = "0.0.1"
|
||||
__version__ = "0.1.0"
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@@ -30,7 +30,6 @@ async def lifespan(_: FastAPI) -> AsyncGenerator[None, None]:
|
||||
await init_db()
|
||||
|
||||
pricing_task = asyncio.create_task(update_sats_pricing())
|
||||
refund_task = asyncio.create_task(check_for_refunds())
|
||||
payout_task = asyncio.create_task(periodic_payout())
|
||||
|
||||
yield
|
||||
@@ -44,14 +43,11 @@ async def lifespan(_: FastAPI) -> AsyncGenerator[None, None]:
|
||||
finally:
|
||||
logger.info("Application shutdown initiated")
|
||||
|
||||
refund_task.cancel()
|
||||
pricing_task.cancel()
|
||||
payout_task.cancel()
|
||||
|
||||
try:
|
||||
await asyncio.gather(
|
||||
pricing_task, refund_task, payout_task, return_exceptions=True
|
||||
)
|
||||
await asyncio.gather(pricing_task, payout_task, return_exceptions=True)
|
||||
logger.info("Background tasks stopped successfully")
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
|
||||
@@ -10,17 +10,12 @@ from .models import MODELS
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
UPSTREAM_BASE_URL = os.environ["UPSTREAM_BASE_URL"]
|
||||
|
||||
UPSTREAM_BASE_URL = os.environ.get("UPSTREAM_BASE_URL", "")
|
||||
UPSTREAM_API_KEY = os.environ.get("UPSTREAM_API_KEY", "")
|
||||
|
||||
logger.info(
|
||||
"Payment helpers initialized",
|
||||
extra={
|
||||
"upstream_base_url": UPSTREAM_BASE_URL,
|
||||
"has_upstream_api_key": bool(UPSTREAM_API_KEY),
|
||||
"model_based_pricing": MODEL_BASED_PRICING,
|
||||
},
|
||||
)
|
||||
if not UPSTREAM_BASE_URL:
|
||||
raise ValueError("Please set the UPSTREAM_BASE_URL environment variable")
|
||||
|
||||
|
||||
def get_cost_per_request(model: str | None = None) -> int:
|
||||
|
||||
@@ -18,7 +18,6 @@ async def kraken_btc_usd(client: httpx.AsyncClient) -> float | None:
|
||||
"""Fetch BTC/USD price from Kraken API."""
|
||||
api = "https://api.kraken.com/0/public/Ticker?pair=XBTUSD"
|
||||
try:
|
||||
logger.debug("Fetching BTC price from Kraken")
|
||||
response = await client.get(api)
|
||||
price_data = response.json()
|
||||
price = float(price_data["result"]["XXBTZUSD"]["c"][0])
|
||||
@@ -40,7 +39,6 @@ async def coinbase_btc_usd(client: httpx.AsyncClient) -> float | None:
|
||||
"""Fetch BTC/USD price from Coinbase API."""
|
||||
api = "https://api.coinbase.com/v2/prices/BTC-USD/spot"
|
||||
try:
|
||||
logger.debug("Fetching BTC price from Coinbase")
|
||||
response = await client.get(api)
|
||||
price_data = response.json()
|
||||
price = float(price_data["data"]["amount"])
|
||||
@@ -62,7 +60,6 @@ async def binance_btc_usdt(client: httpx.AsyncClient) -> float | None:
|
||||
"""Fetch BTC/USDT price from Binance API."""
|
||||
api = "https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT"
|
||||
try:
|
||||
logger.debug("Fetching BTC price from Binance")
|
||||
response = await client.get(api)
|
||||
price_data = response.json()
|
||||
price = float(price_data["price"])
|
||||
@@ -82,7 +79,6 @@ async def binance_btc_usdt(client: httpx.AsyncClient) -> float | None:
|
||||
|
||||
async def btc_usd_ask_price() -> float:
|
||||
"""Get the highest BTC/USD price from multiple exchanges with fee adjustment."""
|
||||
logger.debug("Starting BTC price aggregation from multiple exchanges")
|
||||
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
try:
|
||||
@@ -113,7 +109,6 @@ async def btc_usd_ask_price() -> float:
|
||||
|
||||
async def sats_usd_ask_price() -> float:
|
||||
"""Get the USD price per satoshi."""
|
||||
logger.debug("Calculating satoshi price from BTC price")
|
||||
|
||||
try:
|
||||
btc_price = await btc_usd_ask_price()
|
||||
|
||||
+2
-5
@@ -11,7 +11,8 @@ logger = get_logger(__name__)
|
||||
|
||||
CurrencyUnit = Literal["sat", "msat"]
|
||||
|
||||
TRUSTED_MINTS = os.environ["CASHU_MINTS"].split(",")
|
||||
CASHU_MINTS = os.environ.get("CASHU_MINTS", "https://mint.minibits.cash/Bitcoin")
|
||||
TRUSTED_MINTS = CASHU_MINTS.split(",")
|
||||
PRIMARY_MINT_URL = TRUSTED_MINTS[0]
|
||||
|
||||
|
||||
@@ -122,10 +123,6 @@ async def send_to_lnurl(amount: int, unit: CurrencyUnit, lnurl: str) -> dict[str
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
async def check_for_refunds() -> None:
|
||||
logger.warning("check_for_refunds, temporary not implemented")
|
||||
|
||||
|
||||
async def periodic_payout() -> None:
|
||||
logger.warning("periodic_payout, temporary not implemented")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user