From 8b6ba38a82eb1d106d5f8d5569244827a892b95d Mon Sep 17 00:00:00 2001 From: tolelom <98kimsungmin@naver.com> Date: Mon, 23 Mar 2026 13:38:36 +0900 Subject: [PATCH] feat: add chain API wrapper with idempotency-key Co-Authored-By: Claude Opus 4.6 --- src/api/chain.js | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/api/chain.js diff --git a/src/api/chain.js b/src/api/chain.js new file mode 100644 index 0000000..4e808c7 --- /dev/null +++ b/src/api/chain.js @@ -0,0 +1,72 @@ +import { apiFetch } from './client'; + +const BASE = import.meta.env.VITE_API_BASE_URL || ''; + +// --- 지갑 --- +export async function getBalance() { + return apiFetch('/api/chain/balance'); +} + +export async function getWallet() { + return apiFetch('/api/chain/wallet'); +} + +// 키 내보내기는 비밀번호 오류 시 서버가 401을 반환하므로, +// apiFetch의 401 자동 refresh/로그아웃을 우회하기 위해 직접 fetch한다. +export async function exportWalletKey(password) { + const token = localStorage.getItem('token'); + const headers = { 'Content-Type': 'application/json' }; + if (token) headers['Authorization'] = `Bearer ${token}`; + const res = await fetch(BASE + '/api/chain/wallet/export', { + method: 'POST', + headers, + credentials: 'include', + body: JSON.stringify({ password }), + }); + if (!res.ok) { + const err = new Error(res.status === 401 ? '비밀번호가 올바르지 않습니다' : '키 내보내기에 실패했습니다'); + err.status = res.status; + try { const body = await res.json(); err.message = body.message || err.message; } catch {} + throw err; + } + return res.json(); +} + +// --- 자산 --- +export async function getAssets() { + return apiFetch('/api/chain/assets'); +} + +export async function getAsset(id) { + return apiFetch(`/api/chain/asset/${id}`); +} + +// --- 인벤토리 --- +export async function getInventory() { + return apiFetch('/api/chain/inventory'); +} + +// --- 마켓 --- +export async function getMarketListings() { + return apiFetch('/api/chain/market'); +} + +function idempotentPost(path, body) { + return apiFetch(path, { + method: 'POST', + headers: { 'Idempotency-Key': crypto.randomUUID() }, + body: JSON.stringify(body), + }); +} + +export async function listOnMarket(assetId, price) { + return idempotentPost('/api/chain/market/list', { asset_id: assetId, price }); +} + +export async function buyFromMarket(listingId) { + return idempotentPost('/api/chain/market/buy', { listing_id: listingId }); +} + +export async function cancelListing(listingId) { + return idempotentPost('/api/chain/market/cancel', { listing_id: listingId }); +}