/* * 내정보(프로필) API 래퍼 — 조회·OTP 상태·아바타 업로드. * 근거: /api/auth/me(KintexPrincipal)·/api/auth/otp/status(OtpStatusResponse). * ★ 아바타 업로드/서빙 엔드포인트는 백엔드 미구현 — 제안 계약으로 배선하되 미구현(404/501/네트워크) * 시 degrade(로컬 프리뷰 유지). 인계 노트: _workspace/mobile_myinfo_avatar_contract.md. */ import { API_BASE } from './config'; import { ApiRequestError, api, getAccessToken } from './api'; import type { ApiResponse, AvatarUploadResponse, MePrincipal, OtpStatusDto, } from './types'; import type { PickedImage } from './imagePick'; import { toPhotoFormData } from './imagePick'; /** 내 신원·행사별 역할(GET /api/auth/me). */ export async function getMe(): Promise { return api.get('/api/auth/me'); } /** 본인 OTP(2차 인증) 상태(GET /api/auth/otp/status). */ export async function getOtpStatus(): Promise { return api.get('/api/auth/otp/status'); } /** * 아바타 서빙 URL(제안 계약: GET /api/auth/users/{userId}/photo). * 는 axios/fetch 인터셉터를 거치지 않으므로 Avatar 컴포넌트에서 Bearer 헤더를 직접 부여한다. */ export function avatarUrl(userId: string): string { return `${API_BASE}/api/auth/users/${encodeURIComponent(userId)}/photo`; } /** * 아바타 업로드(제안 계약: POST /api/auth/profile/photo, multipart field "file"). * 성공 시 photoUrl. 미구현/네트워크 오류는 ApiRequestError로 전파 → 호출부가 degrade 처리. * 멀티파트는 JSON api 클라이언트를 쓰지 않고 fetch 직접 호출(Content-Type 자동 boundary). */ export async function uploadAvatar(img: PickedImage): Promise { const token = getAccessToken(); const headers: Record = {}; if (token) headers.Authorization = `Bearer ${token}`; // Content-Type은 지정하지 않는다 — RN이 multipart boundary를 자동 설정한다. let res: Response; try { res = await fetch(`${API_BASE}/api/auth/profile/photo`, { method: 'POST', headers, body: toPhotoFormData(img), }); } catch { throw new ApiRequestError('NETWORK', '네트워크 연결을 확인해 주세요.', 0); } let payload: ApiResponse | null = null; const text = await res.text(); if (text) { try { payload = JSON.parse(text) as ApiResponse; } catch { payload = null; } } if (!res.ok || (payload && payload.success === false)) { const err = payload?.error ?? null; throw new ApiRequestError( // eslint-disable-next-line @typescript-eslint/no-explicit-any (err?.code as any) ?? (res.status === 404 ? 'NOT_IMPLEMENTED' : 'UNKNOWN'), err?.message ?? `프로필 사진 업로드를 처리하지 못했습니다. (${res.status})`, res.status, ); } if (payload?.data?.photoUrl) return payload.data; // 봉투는 성공이나 data 부재 — 표준 서빙 URL로 폴백. return { photoUrl: '' }; }