"use client"; import { useMutation, useQuery } from "@tanstack/react-query"; import { useSystemStatus } from "@/components/shell"; import { Badge, Button, Card, Empty, ErrorBox, KV, PageHeader, Spinner, Table, Td, Th } from "@/components/ui"; import { api, ApiError } from "@/lib/api"; type Preflight = { checked_at: number; data_key: Record & { warning?: string } | null; trade_key: Record | null }; export default function SettingsPage() { const sys = useSystemStatus(); const balance = useQuery({ queryKey: ["balance"], queryFn: () => api<{ balances: Record; fetched_at: number }>("/api/account/balance"), retry: false, enabled: !!sys.data?.credentials.data_key }); const preflight = useMutation({ mutationFn: () => api("/api/account/preflight", { method: "POST" }) }); const s = sys.data; const pf = (preflight.data ?? s?.preflight) as Preflight | null | undefined; return ( <>
preflight.mutate()} disabled={!s?.credentials.data_key && !s?.credentials.trade_key}>Run preflight}> {sys.isLoading && } {s && ( configured : not configured], ["Trade key", s.credentials.trade_key ? configured (live flag off) : not configured], ["Withdrawal permission", {s.credentials.withdrawal}], ["Live trading flag", s.live_trading_enabled ? ENABLED : disabled], ]} /> )} {preflight.error &&
} {pf ? (
Preflight {pf.checked_at ? `at ${new Date(pf.checked_at * 1000).toISOString().slice(0, 19)}Z` : ""}
{pf.data_key?.warning &&
{pf.data_key.warning}
} {pf.data_key ? ( {Object.entries(pf.data_key).filter(([k]) => k !== "warning").map(([k, v]) => )}
Probe (data key)Result
{k}{"has_trade_permission" in (v as object) ? ((v as { has_trade_permission?: boolean | null }).has_trade_permission ? has order permission : no order permission) : (v as { ok?: boolean }).ok ? ok : {(v as { error?: string }).error}}
) :

No data key to probe.

} {pf.trade_key &&
{JSON.stringify(pf.trade_key, null, 1)}
}
) :

No preflight has been run. The trade-permission probe uses AddOrder with validate=true on an order that could never fill; it is recorded in the audit log.

}
{!s?.credentials.data_key ? Set KTRADE_KRAKEN_API_KEY / SECRET on the server to enable account reads. : balance.isLoading ? : balance.error ? : balance.data && Object.keys(balance.data.balances).length ? ( {Object.entries(balance.data.balances).map(([k, v]) => )}
AssetAmount
{k}{v}
) : }
{s && `${i}m`).join(", ")], ["TimescaleDB", s.timescale ? "enabled (hypertables)" : "not installed (plain tables with indexes)"], ["Kraken REST", `${s.kraken.status}${s.kraken.rtt_ms != null ? ` ยท ${s.kraken.rtt_ms} ms` : ""}`]]} />}
  • Alert channels (e-mail / webhook) for circuit breakers, staleness and reconciliation mismatches.
  • Single-owner sessions today; role-based viewers/operators later.
  • Retention: trades 30 days, book samples 14 days (enforced by the worker); candles and audit log kept indefinitely; Postgres backups via the operator runbook.
); }