satmachineclient/views_api.py

147 lines
4.8 KiB
Python

# Description: Client-focused API endpoints for DCA dashboard
from http import HTTPStatus
from typing import List, Optional
from datetime import datetime, timedelta
from fastapi import APIRouter, Depends, Query
from lnbits.core.models import WalletTypeInfo
from lnbits.decorators import require_invoice_key
from starlette.exceptions import HTTPException
from .crud import (
get_client_dashboard_summary,
get_client_transactions,
get_client_analytics,
update_client_dca_settings,
get_client_by_user_id,
)
from .models import (
ClientDashboardSummary,
ClientTransaction,
ClientAnalytics,
UpdateClientSettings,
)
satmachineclient_api_router = APIRouter()
###################################################
############## CLIENT DASHBOARD API ###############
###################################################
@satmachineclient_api_router.get("/api/v1/dashboard/summary")
async def api_get_dashboard_summary(
wallet: WalletTypeInfo = Depends(require_invoice_key),
) -> ClientDashboardSummary:
"""Get client dashboard summary metrics"""
summary = await get_client_dashboard_summary(wallet.wallet.user)
if not summary:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Client data not found"
)
return summary
@satmachineclient_api_router.get("/api/v1/dashboard/transactions")
async def api_get_client_transactions(
wallet: WalletTypeInfo = Depends(require_invoice_key),
limit: int = Query(50, ge=1, le=1000),
offset: int = Query(0, ge=0),
transaction_type: Optional[str] = Query(None),
start_date: Optional[datetime] = Query(None),
end_date: Optional[datetime] = Query(None),
) -> List[ClientTransaction]:
"""Get client's DCA transaction history with filtering"""
return await get_client_transactions(
wallet.wallet.user,
limit=limit,
offset=offset,
transaction_type=transaction_type,
start_date=start_date,
end_date=end_date
)
@satmachineclient_api_router.get("/api/v1/dashboard/analytics")
async def api_get_client_analytics(
wallet: WalletTypeInfo = Depends(require_invoice_key),
time_range: str = Query("30d", regex="^(7d|30d|90d|1y|all)$"),
) -> ClientAnalytics:
"""Get client performance analytics and cost basis data"""
analytics = await get_client_analytics(wallet.wallet.user, time_range)
if not analytics:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Analytics data not available"
)
return analytics
@satmachineclient_api_router.put("/api/v1/dashboard/settings")
async def api_update_client_settings(
settings: UpdateClientSettings,
wallet: WalletTypeInfo = Depends(require_invoice_key),
) -> dict:
"""Update client DCA settings (mode, limits, status)"""
client = await get_client_by_user_id(wallet.wallet.user)
if not client:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Client profile not found"
)
success = await update_client_dca_settings(client.id, settings)
if not success:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="Failed to update settings"
)
return {"message": "Settings updated successfully"}
@satmachineclient_api_router.get("/api/v1/dashboard/export/transactions")
async def api_export_transactions(
wallet: WalletTypeInfo = Depends(require_invoice_key),
format: str = Query("csv", regex="^(csv|json)$"),
start_date: Optional[datetime] = Query(None),
end_date: Optional[datetime] = Query(None),
):
"""Export client transaction history"""
transactions = await get_client_transactions(
wallet.wallet.user,
limit=10000, # Large limit for export
start_date=start_date,
end_date=end_date
)
if format == "csv":
# Return CSV response
from io import StringIO
import csv
output = StringIO()
writer = csv.writer(output)
writer.writerow(['Date', 'Amount (Sats)', 'Amount (Fiat)', 'Exchange Rate', 'Type', 'Status'])
for tx in transactions:
writer.writerow([
tx.created_at.isoformat(),
tx.amount_sats,
tx.amount_fiat / 100, # Convert centavos to full currency
tx.exchange_rate,
tx.transaction_type,
tx.status
])
from fastapi.responses import StreamingResponse
output.seek(0)
return StreamingResponse(
iter([output.getvalue()]),
media_type="text/csv",
headers={"Content-Disposition": "attachment; filename=dca_transactions.csv"}
)
else:
return {"transactions": transactions}