Files
BetiX/resources/js/composables/useVault.ts
Dolo 0280278978
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
Initialer Laravel Commit für BetiX
2026-04-04 18:01:50 +02:00

181 lines
5.0 KiB
TypeScript

import { ref } from 'vue';
import { csrfFetch } from '@/utils/csrfFetch';
export interface VaultBalances {
balance: string;
vault_balance: string;
vault_balances: Record<string, string>;
currency: string | null;
}
export function useVault() {
const loading = ref(false);
const error = ref<string | null>(null);
const balances = ref<VaultBalances | null>(null);
const transfers = ref<any>(null);
const pinRequired = ref(false);
const lockedUntil = ref<string | null>(null);
const sessionPin = ref<string | null>(null);
async function load(perPage = 10) {
loading.value = true;
error.value = null;
try {
const resp = await fetch(`/api/wallet/vault?per_page=${perPage}`, {
headers: { 'Accept': 'application/json' }
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const json = await resp.json();
balances.value = {
balance: json.balance,
vault_balance: json.vault_balance,
vault_balances: json.vault_balances ?? { BTX: json.vault_balance },
currency: json.currency,
};
transfers.value = json.transfers;
} catch (e: any) {
error.value = e?.message || 'Failed to load vault';
} finally {
loading.value = false;
}
}
function uuidv4(): string {
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
return (crypto as any).randomUUID();
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
async function deposit(amount: string, currency = 'BTX', pin?: string) {
return doAction('/api/wallet/vault/deposit', amount, currency, pin);
}
async function withdraw(amount: string, currency = 'BTX', pin?: string) {
return doAction('/api/wallet/vault/withdraw', amount, currency, pin);
}
async function doAction(url: string, amount: string, currency: string, pin?: string) {
loading.value = true;
error.value = null;
pinRequired.value = false;
lockedUntil.value = null;
try {
const usePin = pin ?? sessionPin.value;
if (!usePin) {
pinRequired.value = true;
throw new Error('PIN required');
}
const body = { amount, currency, pin: usePin, idempotency_key: uuidv4() };
const resp = await csrfFetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify(body),
});
if (resp.status === 423) {
const j = await resp.json().catch(() => ({}));
pinRequired.value = true;
lockedUntil.value = j?.locked_until || null;
throw new Error(j?.message || 'PIN required');
}
if (!resp.ok) {
const j = await resp.json().catch(() => ({}));
throw new Error(j?.message || `HTTP ${resp.status}`);
}
const json = await resp.json();
if (json?.balances) {
balances.value = {
balance: json.balances.balance,
vault_balance: json.balances.vault_balance,
vault_balances: json.balances.vault_balances ?? { BTX: json.balances.vault_balance },
currency: balances.value?.currency || 'BTX',
};
} else {
await load();
}
return json;
} catch (e: any) {
error.value = e?.message || 'Vault action failed';
throw e;
} finally {
loading.value = false;
}
}
async function verifyPin(pin: string) {
error.value = null;
try {
const resp = await csrfFetch('/api/wallet/vault/pin/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify({ pin }),
});
if (!resp.ok) {
const j = await resp.json().catch(() => ({}));
if (resp.status === 423) lockedUntil.value = j?.locked_until || null;
throw new Error(j?.message || `HTTP ${resp.status}`);
}
sessionPin.value = pin;
pinRequired.value = false;
lockedUntil.value = null;
return await resp.json();
} catch (e: any) {
error.value = e?.message || 'PIN verification failed';
throw e;
}
}
function clearSessionPin() {
sessionPin.value = null;
}
async function setPin(pin: string, current_pin?: string) {
error.value = null;
const payload: any = { pin };
if (current_pin) payload.current_pin = current_pin;
const resp = await csrfFetch('/api/wallet/vault/pin/set', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify(payload),
});
if (!resp.ok) {
const j = await resp.json().catch(() => ({}));
throw new Error(j?.message || `HTTP ${resp.status}`);
}
sessionPin.value = pin;
return await resp.json();
}
return {
loading,
error,
balances,
transfers,
pinRequired,
lockedUntil,
load,
deposit,
withdraw,
verifyPin,
setPin,
clearSessionPin,
};
}