Files
cryptowallet/apps/web/src/lib/api.ts
2026-04-14 13:30:26 +03:00

134 lines
3.7 KiB
TypeScript

import { webEnv } from './env';
const API_URL = webEnv.apiUrl;
const BITOK_BASE = process.env.NEXT_PUBLIC_BITOK_URL || 'http://localhost:8000';
let accessToken: string | null = null;
export function setAccessToken(token: string | null) {
accessToken = token;
}
export function getAccessToken() {
return accessToken;
}
// ── BITOK auth calls (httpOnly cookies + access_token in body) ──
async function bitokRequest<T>(path: string, options: RequestInit = {}): Promise<T> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(options.headers as Record<string, string>),
};
const res = await fetch(`${BITOK_BASE}${path}`, {
...options,
headers,
credentials: 'include',
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(body.detail || body.error || `Request failed (${res.status})`);
}
return res.json();
}
export const bitokAuth = {
registrationStart: (email: string) =>
bitokRequest<{ success: boolean }>('/v1/auth/registration/start', {
method: 'POST',
body: JSON.stringify({ email }),
}),
registrationComplete: (email: string, password: string, code: string) =>
bitokRequest<{ id: string; email: string; access_token: string }>('/v1/auth/registration/complete', {
method: 'POST',
body: JSON.stringify({ email, password, code }),
}),
loginStart: (email: string) =>
bitokRequest<{ success: boolean }>('/v1/auth/login/start', {
method: 'POST',
body: JSON.stringify({ email }),
}),
loginComplete: (email: string, password: string, code: string) =>
bitokRequest<{ id: string; email: string; access_token: string }>('/v1/auth/login/complete', {
method: 'POST',
body: JSON.stringify({ email, password, code }),
}),
refresh: () =>
bitokRequest<{ result: boolean; access_token: string }>('/v1/jwt/refresh', { method: 'POST' }),
logout: () =>
bitokRequest<{ ok: boolean }>('/v1/auth/logout', { method: 'POST' }),
};
// ── Wallet API calls (uses Bearer token from BITOK) ──
async function walletRequest<T>(path: string, options: RequestInit = {}): Promise<T> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(options.headers as Record<string, string>),
};
if (accessToken) {
headers['Authorization'] = `Bearer ${accessToken}`;
}
const res = await fetch(`${API_URL}${path}`, {
...options,
headers,
credentials: 'include',
});
const data = await res.json();
if (!data.success) {
throw new Error(data.error || 'Request failed');
}
return data.data;
}
export interface WalletSetupPayload {
encryptedVault: string;
vaultSalt: string;
wallets: { chain: string; address: string; derivationPath: string }[];
}
export interface WalletUnlockResponse {
encryptedVault: string;
vaultSalt: string;
wallets: { chain: string; address: string; derivationPath: string }[];
mnemonicShown: boolean;
}
export const walletApi = {
setup: (data: WalletSetupPayload) =>
walletRequest<any>('/api/wallet/setup', {
method: 'POST',
body: JSON.stringify(data),
}),
unlock: () =>
walletRequest<WalletUnlockResponse>('/api/wallet/unlock'),
getWallets: () => walletRequest<any>('/api/wallets'),
confirmMnemonic: () =>
walletRequest<any>('/api/wallet/confirm-mnemonic', { method: 'POST' }),
};
// ── Legacy api object (keep for components that still reference it) ──
export const api = {
getWallets: () => walletRequest<any>('/api/wallets'),
getVault: () => walletRequest<any>('/api/vault'),
confirmMnemonic: () =>
walletRequest<any>('/api/wallet/confirm-mnemonic', { method: 'POST' }),
};