All checks were successful
CI / lint-and-test (push) Successful in 9m52s
Co-authored-by: Cursor <cursoragent@cursor.com>
134 lines
3.7 KiB
TypeScript
134 lines
3.7 KiB
TypeScript
/** Same-origin in production (nginx /api/ → backend). Local dev uses localhost:3001. */
|
|
function apiUrl(path: string): string {
|
|
const normalized = path.startsWith("/") ? path : `/${path}`;
|
|
const configured = (process.env.NEXT_PUBLIC_API_URL ?? "").trim();
|
|
|
|
if (typeof window !== "undefined") {
|
|
// Ignore localhost baked in at build time — browser must hit same-origin /api/.
|
|
if (configured && !/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?/i.test(configured)) {
|
|
return `${configured.replace(/\/$/, "")}${normalized}`;
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
if (configured) {
|
|
return `${configured.replace(/\/$/, "")}${normalized}`;
|
|
}
|
|
if (process.env.NODE_ENV === "production") {
|
|
return normalized;
|
|
}
|
|
return `http://localhost:3001${normalized}`;
|
|
}
|
|
|
|
export interface TreasuryRecord {
|
|
id: string;
|
|
organizationId: string;
|
|
chainId: number;
|
|
mainWallet: string;
|
|
label: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface TransactionRecord {
|
|
id: string;
|
|
treasuryId: string;
|
|
proposalId: number;
|
|
walletAddress: string;
|
|
to: string;
|
|
value: string;
|
|
token: string | null;
|
|
data: string | null;
|
|
status: string;
|
|
proposer: string;
|
|
createdAt: string;
|
|
executedAt: string | null;
|
|
approvalCount?: number;
|
|
}
|
|
|
|
export interface SubAccountRecord {
|
|
id: string;
|
|
treasuryId: string;
|
|
address: string;
|
|
label: string | null;
|
|
metadataHash: string | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface LedgerEntry {
|
|
id: string;
|
|
kind: "deposit" | "transfer_out";
|
|
txHash: string;
|
|
from: string;
|
|
to: string;
|
|
value: string;
|
|
token: string | null;
|
|
tokenSymbol: string | null;
|
|
timestamp: string;
|
|
blockNumber: number;
|
|
}
|
|
|
|
async function apiFetch<T>(path: string): Promise<T> {
|
|
const res = await fetch(apiUrl(path), { cache: "no-store" });
|
|
if (!res.ok) {
|
|
throw new Error(`API ${path} failed: ${res.status}`);
|
|
}
|
|
return res.json() as Promise<T>;
|
|
}
|
|
|
|
export async function fetchTreasury(wallet: string): Promise<TreasuryRecord | null> {
|
|
const data = await apiFetch<{ treasury: TreasuryRecord | null }>(
|
|
`/api/treasury?wallet=${encodeURIComponent(wallet)}`
|
|
);
|
|
return data.treasury;
|
|
}
|
|
|
|
export async function fetchTransactions(
|
|
treasuryId: string,
|
|
status?: string
|
|
): Promise<TransactionRecord[]> {
|
|
const query = status ? `?status=${encodeURIComponent(status)}` : "";
|
|
const data = await apiFetch<{ proposals: TransactionRecord[] }>(
|
|
`/api/transactions/${treasuryId}${query}`
|
|
);
|
|
return data.proposals;
|
|
}
|
|
|
|
export async function fetchPendingProposals(treasuryId: string): Promise<TransactionRecord[]> {
|
|
const data = await apiFetch<{ proposals: TransactionRecord[] }>(
|
|
`/api/proposals/${treasuryId}?status=pending&includeApprovals=1`
|
|
);
|
|
return data.proposals;
|
|
}
|
|
|
|
export async function fetchSubAccounts(treasuryId: string): Promise<SubAccountRecord[]> {
|
|
const data = await apiFetch<{ subAccounts: SubAccountRecord[] }>(
|
|
`/api/treasury/${treasuryId}/sub-accounts`
|
|
);
|
|
return data.subAccounts;
|
|
}
|
|
|
|
export async function fetchLedger(treasuryId: string): Promise<LedgerEntry[]> {
|
|
const data = await apiFetch<{ ledger: LedgerEntry[] }>(
|
|
`/api/treasury/${treasuryId}/ledger`
|
|
);
|
|
return data.ledger;
|
|
}
|
|
|
|
export async function fetchLedgerByWallet(wallet: string): Promise<LedgerEntry[]> {
|
|
const data = await apiFetch<{ ledger: LedgerEntry[] }>(
|
|
`/api/ledger?wallet=${encodeURIComponent(wallet)}`
|
|
);
|
|
return data.ledger;
|
|
}
|
|
|
|
export async function exportTransactionsCsv(treasuryId: string): Promise<string> {
|
|
const res = await fetch(
|
|
apiUrl(`/api/transactions/export?treasuryId=${encodeURIComponent(treasuryId)}&format=csv`)
|
|
);
|
|
if (!res.ok) {
|
|
throw new Error(`CSV export failed: ${res.status}`);
|
|
}
|
|
return res.text();
|
|
}
|