mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-20 02:24:20 +01:00
* auth server * cleaning up * auth ledger class * class variables -> instance variables * annotations * add models and api route * custom amount and api prefix * add auth db * blind auth token working * jwt working * clean up * JWT works * using openid connect server * use oauth server with password flow * new realm * add keycloak docker * hopefully not garbage * auth works * auth kinda working * fix cli * auth works for send and receive * pass auth_db to Wallet * auth in info * refactor * fix supported * cache mint info * fix settings and endpoints * add description to .env.example * track changes for openid connect client * store mint in db * store credentials * clean up v1_api.py * load mint info into auth wallet * fix first login * authenticate if refresh token fails * clear auth also middleware * use regex * add cli command * pw works * persist keyset amounts * add errors.py * do not start auth server if disabled in config * upadte poetry * disvoery url * fix test * support device code flow * adopt latest spec changes * fix code flow * mint max bat dynamic * mypy ignore * fix test * do not serialize amount in authproof * all auth flows working * fix tests * submodule * refactor * test * dont sleep * test * add wallet auth tests * test differently * test only keycloak for now * fix creds * daemon * fix test * install everything * install jinja * delete wallet for every test * auth: use global rate limiter * test auth rate limit * keycloak hostname * move keycloak test data * reactivate all tests * add readme * load proofs * remove unused code * remove unused code * implement change suggestions by ok300 * add error codes * test errors
118 lines
3.5 KiB
Python
118 lines
3.5 KiB
Python
import asyncio
|
|
import sys
|
|
from collections.abc import AsyncIterator
|
|
from contextlib import asynccontextmanager
|
|
from traceback import print_exception
|
|
|
|
from fastapi import FastAPI, status
|
|
from fastapi.exceptions import RequestValidationError
|
|
from fastapi.responses import JSONResponse
|
|
from loguru import logger
|
|
from starlette.requests import Request
|
|
|
|
from ..core.errors import CashuError
|
|
from ..core.logging import configure_logger
|
|
from ..core.settings import settings
|
|
from .auth.router import auth_router
|
|
from .router import redis, router
|
|
from .router_deprecated import router_deprecated
|
|
from .startup import shutdown_mint, start_auth, start_mint
|
|
|
|
if settings.debug_profiling:
|
|
pass
|
|
|
|
if settings.mint_rate_limit:
|
|
pass
|
|
|
|
from .middleware import add_middlewares, request_validation_exception_handler
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
|
|
await start_mint()
|
|
if settings.mint_require_auth:
|
|
await start_auth()
|
|
try:
|
|
yield
|
|
except asyncio.CancelledError:
|
|
# Handle the cancellation gracefully
|
|
logger.info("Shutdown process interrupted by CancelledError")
|
|
finally:
|
|
try:
|
|
await redis.disconnect()
|
|
await shutdown_mint()
|
|
except asyncio.CancelledError:
|
|
logger.info("CancelledError during shutdown, shutting down forcefully")
|
|
|
|
|
|
def create_app(config_object="core.settings") -> FastAPI:
|
|
configure_logger()
|
|
|
|
app = FastAPI(
|
|
title="Nutshell Mint",
|
|
description="Ecash mint based on the Cashu protocol.",
|
|
version=settings.version,
|
|
license_info={
|
|
"name": "MIT License",
|
|
"url": "https://raw.githubusercontent.com/cashubtc/cashu/main/LICENSE",
|
|
},
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
return app
|
|
|
|
|
|
app = create_app()
|
|
|
|
# Add middlewares
|
|
add_middlewares(app)
|
|
|
|
|
|
@app.middleware("http")
|
|
async def catch_exceptions(request: Request, call_next):
|
|
CORS_HEADERS = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Methods": "*",
|
|
"Access-Control-Allow-Headers": "*",
|
|
"Access-Control-Allow-Credentials": "true",
|
|
}
|
|
try:
|
|
return await call_next(request)
|
|
except Exception as e:
|
|
try:
|
|
err_message = str(e)
|
|
except Exception:
|
|
err_message = e.args[0] if e.args else "Unknown error"
|
|
|
|
if isinstance(e, CashuError) or isinstance(e.args[0], CashuError):
|
|
logger.error(f"CashuError: {err_message}")
|
|
code = e.code if isinstance(e, CashuError) else e.args[0].code
|
|
# return with cors headers
|
|
return JSONResponse(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
content={"detail": err_message, "code": code},
|
|
headers=CORS_HEADERS,
|
|
)
|
|
logger.error(f"Exception: {err_message}")
|
|
if settings.debug:
|
|
print_exception(*sys.exc_info())
|
|
return JSONResponse(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
content={"detail": err_message, "code": 0},
|
|
headers=CORS_HEADERS,
|
|
)
|
|
|
|
|
|
# Add exception handlers
|
|
app.add_exception_handler(RequestValidationError, request_validation_exception_handler) # type: ignore
|
|
|
|
# Add routers
|
|
if settings.debug_mint_only_deprecated:
|
|
app.include_router(router=router_deprecated, tags=["Deprecated"], deprecated=True)
|
|
else:
|
|
app.include_router(router=router, tags=["Mint"])
|
|
app.include_router(router=router_deprecated, tags=["Deprecated"], deprecated=True)
|
|
|
|
if settings.mint_require_auth:
|
|
app.include_router(auth_router, tags=["Auth"])
|