mirror of
https://github.com/Zproger/Aurum.git
synced 2026-10-17 04:56:40 +07:00
feat: add a Crypto tab with live CoinGecko prices synced into Net Worth
Track individual coins (identity + quantity) in a dedicated tab, backed by CoinGecko's Demo API (AURUM_COINGECKO_API_KEY). A holding is just another Asset under the hood (asset_class=crypto) with its value written as a regular AssetValuation — the existing Net Worth aggregation needs zero changes to pick it up. Prices never refresh in the background (no scheduler in this stack, same reasoning as recurring transactions): a "Refresh prices" button forces a sync, and GET /crypto/holdings lazily re-syncs at most once every 24h. CoinGecko outages leave existing values untouched (error_key: "unreachable") instead of failing the request. Backup/restore now carries CryptoHolding rows too, so a restore doesn't silently strip a holding's coin identity. Backend: 75/75 tests passing (9 new), including a real identity-map staleness bug the tests caught in the raw-Core-upsert + ORM re-read pattern, fixed via populate_existing=True. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01855PiW5Wd8n4aRW4n9kByY
This commit is contained in:
30
frontend/src/api/crypto.ts
Normal file
30
frontend/src/api/crypto.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { api } from "@/api/client";
|
||||
import type { CryptoHolding, CryptoHoldingInput, CryptoSearchResult, CryptoSyncResult } from "@/types";
|
||||
|
||||
// Also triggers the lazy once-a-day auto-refresh server-side — see
|
||||
// services/crypto_service.py.
|
||||
export function fetchCryptoHoldings() {
|
||||
return api.get<CryptoSyncResult>("/crypto/holdings");
|
||||
}
|
||||
|
||||
export function refreshCryptoPrices() {
|
||||
return api.post<CryptoSyncResult>("/crypto/refresh", {});
|
||||
}
|
||||
|
||||
export function createCryptoHolding(input: CryptoHoldingInput) {
|
||||
return api.post<CryptoHolding>("/crypto/holdings", input);
|
||||
}
|
||||
|
||||
export function updateCryptoHoldingQuantity(assetId: number, quantity: string) {
|
||||
return api.patch<CryptoHolding>(`/crypto/holdings/${assetId}`, { quantity });
|
||||
}
|
||||
|
||||
// Reuses the existing asset-delete endpoint — deleting the Asset cascades
|
||||
// to the linked crypto_holdings row server-side, no dedicated endpoint.
|
||||
export function deleteCryptoHolding(assetId: number) {
|
||||
return api.delete<void>(`/assets/${assetId}`);
|
||||
}
|
||||
|
||||
export function searchCryptoCoins(query: string) {
|
||||
return api.get<CryptoSearchResult[]>(`/crypto/search?q=${encodeURIComponent(query)}`);
|
||||
}
|
||||
Reference in New Issue
Block a user