Add user settings endpoint and exchange rate fallback APIs

Backend now stores user settings (dashboard config) in a JSONB column and
exposes CRUD via /settings/. Exchange rate service gains multiple fallback
providers (ExchangeRate-API, currency-api, FloatRates) so USD/CRC rates
stay available when BCCR is down.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Carlos Escalante
2026-03-22 14:45:20 -06:00
parent 58ab395d95
commit 4d468036c6
4 changed files with 198 additions and 28 deletions

View File

@@ -0,0 +1,59 @@
from datetime import datetime
from fastapi import APIRouter, Depends
from sqlmodel import Session, select
from app.auth import get_current_user
from app.db import get_session
from app.models.models import UserSettings, UserSettingsRead, UserSettingsUpdate
router = APIRouter(prefix="/settings", tags=["settings"])
DEFAULT_SETTINGS = {
"dashboard": {
"sections": {
"crc_accounts": {"label": "CRC Accounts", "color": "primary", "cardColor": "primary", "visible": True, "order": 0, "expanded": False},
"usd_accounts": {"label": "USD Accounts", "color": "chart-1", "cardColor": "chart-1", "visible": True, "order": 1, "expanded": False},
"pension": {"label": "Pension", "color": "chart-2", "cardColor": "chart-2", "visible": True, "order": 2, "expanded": False},
"savings": {"label": "Savings", "color": "chart-3", "cardColor": "chart-3", "visible": True, "order": 3, "expanded": False},
"liabilities": {"label": "Liabilities", "color": "destructive", "cardColor": "destructive", "visible": True, "order": 4, "expanded": False},
"crypto": {"label": "Crypto", "color": "chart-4", "cardColor": "chart-4", "visible": True, "order": 5, "expanded": False},
}
}
}
@router.get("/", response_model=UserSettingsRead)
def get_settings(
session: Session = Depends(get_session),
_user: str = Depends(get_current_user),
):
settings = session.exec(
select(UserSettings).where(UserSettings.key == "default")
).first()
if not settings:
settings = UserSettings(key="default", data=DEFAULT_SETTINGS)
session.add(settings)
session.commit()
session.refresh(settings)
return settings
@router.patch("/", response_model=UserSettingsRead)
def update_settings(
body: UserSettingsUpdate,
session: Session = Depends(get_session),
_user: str = Depends(get_current_user),
):
settings = session.exec(
select(UserSettings).where(UserSettings.key == "default")
).first()
if not settings:
settings = UserSettings(key="default", data=body.data)
else:
settings.data = body.data
settings.updated_at = datetime.utcnow()
session.add(settings)
session.commit()
session.refresh(settings)
return settings

View File

@@ -7,6 +7,7 @@ from app.api.v1.endpoints import (
categories,
exchange_rate,
import_transactions,
settings,
tokens,
transactions,
)
@@ -20,3 +21,4 @@ api_router.include_router(import_transactions.router)
api_router.include_router(exchange_rate.router)
api_router.include_router(tokens.router)
api_router.include_router(analytics.router)
api_router.include_router(settings.router)