2025-12-22 18:07:14 +01:00
|
|
|
"""Audit routes for price history."""
|
2025-12-21 21:54:26 +01:00
|
|
|
|
2025-12-22 18:07:14 +01:00
|
|
|
from fastapi import APIRouter, Depends
|
|
|
|
|
from sqlalchemy import desc, select
|
2025-12-22 16:09:05 +01:00
|
|
|
from sqlalchemy.exc import IntegrityError
|
2025-12-20 22:18:14 +01:00
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
from auth import require_permission
|
|
|
|
|
from database import get_db
|
2025-12-22 18:07:14 +01:00
|
|
|
from models import Permission, PriceHistory, User
|
2025-12-22 16:06:56 +01:00
|
|
|
from price_fetcher import PAIR_BTC_EUR, SOURCE_BITFINEX, fetch_btc_eur_price
|
2025-12-22 18:07:14 +01:00
|
|
|
from schemas import PriceHistoryResponse
|
2025-12-20 22:18:14 +01:00
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/api/audit", tags=["audit"])
|
|
|
|
|
|
|
|
|
|
|
2025-12-22 16:17:43 +01:00
|
|
|
def _to_price_history_response(record: PriceHistory) -> PriceHistoryResponse:
|
|
|
|
|
return PriceHistoryResponse(
|
|
|
|
|
id=record.id,
|
|
|
|
|
source=record.source,
|
|
|
|
|
pair=record.pair,
|
|
|
|
|
price=record.price,
|
|
|
|
|
timestamp=record.timestamp,
|
|
|
|
|
created_at=record.created_at,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-12-22 15:43:46 +01:00
|
|
|
# =============================================================================
|
|
|
|
|
# Price History Endpoints
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
|
PRICE_HISTORY_LIMIT = 20
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/price-history", response_model=list[PriceHistoryResponse])
|
|
|
|
|
async def get_price_history(
|
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
|
_current_user: User = Depends(require_permission(Permission.VIEW_AUDIT)),
|
|
|
|
|
) -> list[PriceHistoryResponse]:
|
|
|
|
|
"""Get the 20 most recent price history records."""
|
|
|
|
|
query = (
|
|
|
|
|
select(PriceHistory)
|
|
|
|
|
.order_by(desc(PriceHistory.timestamp))
|
|
|
|
|
.limit(PRICE_HISTORY_LIMIT)
|
|
|
|
|
)
|
|
|
|
|
result = await db.execute(query)
|
|
|
|
|
records = result.scalars().all()
|
|
|
|
|
|
2025-12-22 16:17:43 +01:00
|
|
|
return [_to_price_history_response(record) for record in records]
|
2025-12-22 15:43:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/price-history/fetch", response_model=PriceHistoryResponse)
|
|
|
|
|
async def fetch_price_now(
|
|
|
|
|
db: AsyncSession = Depends(get_db),
|
2025-12-22 16:22:54 +01:00
|
|
|
_current_user: User = Depends(require_permission(Permission.FETCH_PRICE)),
|
2025-12-22 15:43:46 +01:00
|
|
|
) -> PriceHistoryResponse:
|
|
|
|
|
"""Manually trigger a price fetch from Bitfinex."""
|
|
|
|
|
price, timestamp = await fetch_btc_eur_price()
|
|
|
|
|
|
|
|
|
|
record = PriceHistory(
|
2025-12-22 16:06:56 +01:00
|
|
|
source=SOURCE_BITFINEX,
|
|
|
|
|
pair=PAIR_BTC_EUR,
|
2025-12-22 15:43:46 +01:00
|
|
|
price=price,
|
|
|
|
|
timestamp=timestamp,
|
|
|
|
|
)
|
|
|
|
|
db.add(record)
|
2025-12-22 16:09:05 +01:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
await db.commit()
|
|
|
|
|
await db.refresh(record)
|
|
|
|
|
except IntegrityError:
|
|
|
|
|
# Duplicate timestamp - return the existing record
|
|
|
|
|
await db.rollback()
|
|
|
|
|
query = select(PriceHistory).where(
|
|
|
|
|
PriceHistory.source == SOURCE_BITFINEX,
|
|
|
|
|
PriceHistory.pair == PAIR_BTC_EUR,
|
|
|
|
|
PriceHistory.timestamp == timestamp,
|
|
|
|
|
)
|
|
|
|
|
result = await db.execute(query)
|
|
|
|
|
record = result.scalar_one()
|
2025-12-22 15:43:46 +01:00
|
|
|
|
2025-12-22 16:17:43 +01:00
|
|
|
return _to_price_history_response(record)
|