Compare commits

...

11 Commits

Author SHA1 Message Date
9qeklajc
e4b825cad8 test 2025-08-06 00:09:43 +02:00
9qeklajc
6cff06f9aa test 2025-08-06 00:06:26 +02:00
9qeklajc
6c8a4a14ae test 2025-08-05 23:52:50 +02:00
9qeklajc
5c7820ef41 add prints 2025-08-05 23:51:32 +02:00
9qeklajc
047215df80 test 2025-08-05 23:43:41 +02:00
9qeklajc
d69720f39f use own impl nutshell 2025-08-05 23:28:04 +02:00
9qeklajc
77a90ac7d6 again 2025-08-05 23:12:20 +02:00
9qeklajc
9741218edd update dockerfile 2025-08-05 23:10:37 +02:00
9qeklajc
1f3d2dcd62 dump fix 2025-08-05 22:04:20 +02:00
9qeklajc
55bb8ecf11 Merge branch 'main' into fix-x-cashu-refund 2025-08-05 19:36:14 +02:00
9qeklajc
8432561652 fix refund 2025-08-05 17:25:31 +02:00
6 changed files with 970 additions and 945 deletions

View File

@@ -1,28 +1,39 @@
FROM ghcr.io/astral-sh/uv:python3.11-alpine
FROM python:3.13-slim
# Install system dependencies required for secp256k1
RUN apk add --no-cache \
pkgconf \
build-base \
automake \
# Install system dependencies required for secp256k1 and git
RUN apt-get update && apt-get install -y \
build-essential \
pkg-config \
autoconf \
automake \
libtool \
m4 \
perl
RUN apk add git
cmake \
git \
libffi-dev \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
COPY uv.lock pyproject.toml ./
RUN uv add git+https://github.com/saschanaz/secp256k1-py.git#branch=upgrade060
# RUN uv sync
# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
# Set working directory
WORKDIR /app
# Copy dependency files
COPY uv.lock pyproject.toml ./
# Install dependencies
RUN uv sync --frozen
# Copy application code
COPY . .
# Set environment variables
ENV PORT=8000
ENV PYTHONUNBUFFERED=1
# Expose port
EXPOSE 8000
CMD ["/.venv/bin/fastapi", "run", "router", "--host", "0.0.0.0"]
# Run the application
CMD ["uv", "run", "fastapi", "run", "router", "--host", "0.0.0.0"]

View File

@@ -1,5 +1,6 @@
import json
import os
from typing import Mapping, Optional
from fastapi import HTTPException, Response
@@ -96,7 +97,7 @@ def check_token_balance(headers: dict, body: dict, max_cost_for_model: int) -> N
token_obj.amount if token_obj.unit == "msat" else token_obj.amount * 1000
)
if max_cost_for_model > amount_msat:
if max_cost_for_model - 100 > amount_msat:
raise HTTPException(
status_code=413,
detail={
@@ -153,7 +154,9 @@ def get_max_cost_for_model(model: str) -> int:
return COST_PER_REQUEST
def create_error_response(error_type: str, message: str, status_code: int) -> Response:
def create_error_response(
error_type: str, message: str, status_code: int, token: Optional[str] = None
) -> Response:
"""Create a standardized error response."""
logger.info(
"Creating error response",
@@ -164,6 +167,9 @@ def create_error_response(error_type: str, message: str, status_code: int) -> Re
},
)
response_headers = {}
if token:
response_headers["X-Cashu"] = token
return Response(
content=json.dumps(
{
@@ -176,6 +182,7 @@ def create_error_response(error_type: str, message: str, status_code: int) -> Re
),
status_code=status_code,
media_type="application/json",
headers=dict(response_headers),
)

View File

@@ -125,5 +125,6 @@ async def update_sats_pricing() -> None:
@models_router.get("/v1/models")
@models_router.get("/models")
async def models() -> dict:
return {"data": MODELS}

View File

@@ -8,12 +8,7 @@ from fastapi.responses import Response, StreamingResponse
from ..core import get_logger
from ..wallet import CurrencyUnit, recieve_token, send_token
from .cost_caculation import (
CostData,
CostDataError,
MaxCostData,
calculate_cost,
)
from .cost_caculation import CostData, CostDataError, MaxCostData, calculate_cost
from .helpers import (
UPSTREAM_BASE_URL,
create_error_response,
@@ -68,21 +63,30 @@ async def x_cashu_handler(
"token_already_spent",
"The provided CASHU token has already been spent",
400,
x_cashu_token,
)
elif "invalid token" in error_message.lower():
if "invalid token" in error_message.lower():
return create_error_response(
"invalid_token", "The provided CASHU token is invalid", 400
"invalid_token",
"The provided CASHU token is invalid",
400,
x_cashu_token,
)
elif "mint error" in error_message.lower():
if "mint error" in error_message.lower():
return create_error_response(
"mint_error", f"CASHU mint error: {error_message}", 422
)
else:
# Generic error for other cases
return create_error_response(
"cashu_error", f"CASHU token processing failed: {error_message}", 400
"mint_error", f"CASHU mint error: {error_message}", 422, x_cashu_token
)
# Generic error for other cases
return create_error_response(
"cashu_error",
f"CASHU token processing failed: {error_message}",
400,
x_cashu_token,
)
async def forward_to_upstream(
request: Request, path: str, headers: dict, amount: int, unit: CurrencyUnit

View File

@@ -2,7 +2,7 @@ import os
from typing import Literal
from cashu.core.base import Token
from cashu.wallet.helpers import deserialize_token_from_string, send
from cashu.wallet.helpers import deserialize_token_from_string, receive, send
from cashu.wallet.wallet import Wallet
from .core import db, get_logger
@@ -29,6 +29,7 @@ async def get_balance(unit: CurrencyUnit) -> int:
async def recieve_token(
token: str,
) -> tuple[int, CurrencyUnit, str]: # amount, unit, mint_url
print(token)
token_obj = deserialize_token_from_string(token)
if len(token_obj.keysets) > 1:
raise ValueError("Multiple keysets per token currently not supported")
@@ -39,12 +40,13 @@ async def recieve_token(
load_all_keysets=True,
unit=token_obj.unit,
)
await wallet.load_mint(token_obj.keysets[0])
if token_obj.mint not in TRUSTED_MINTS:
return await swap_to_primary_mint(token_obj, wallet)
await wallet.redeem(token_obj.proofs)
await receive(wallet, token_obj)
return token_obj.amount, token_obj.unit, token_obj.mint

1824
uv.lock generated

File diff suppressed because it is too large Load Diff