import { apiFetch, apiFetchBlob } from "@/lib/auth/api-client"; import { MfaEnrollment, ProfileSummary, UpdateProfileInput } from "./types"; export function updateProfile(input: UpdateProfileInput): Promise { return apiFetch("/profile", { method: "PATCH", body: JSON.stringify(input), }); } export function changePassword(currentPassword: string, newPassword: string): Promise { return apiFetch("/profile/password", { method: "POST", body: JSON.stringify({ currentPassword, newPassword }), }, { refreshOn401: false }); } export function deactivateAccount(currentPassword: string): Promise { return apiFetch("/profile", { method: "DELETE", body: JSON.stringify({ currentPassword }), }, { refreshOn401: false }); } // Sent as a base64 data: URL in a plain JSON body rather than // multipart/form-data: this deployment's Cloudflare proxy has a known // history of mishandling multipart uploads regardless of size (see the // registry-push workaround in .gitea/workflows/*-image.yml), while plain // JSON POSTs to this same backend have been reliable all along. function fileToDataUrl(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(String(reader.result)); reader.onerror = () => reject(reader.error); reader.readAsDataURL(file); }); } export async function uploadAvatar(file: File): Promise { const data = await fileToDataUrl(file); return apiFetch("/profile/avatar", { method: "POST", body: JSON.stringify({ data }), }); } export function deleteAvatar(): Promise { return apiFetch("/profile/avatar", { method: "DELETE" }); } // Fetches the avatar as a blob (the endpoint requires the caller's own // Bearer token, so a plain can't hit it directly) and hands // back an object URL for use as an source. Returns null when the user // has no avatar (404) or the request otherwise fails. export async function fetchAvatarUrl(): Promise { try { const { blob } = await apiFetchBlob("/profile/avatar", "avatar"); return URL.createObjectURL(blob); } catch { return null; } } export function enrollMfa(): Promise { return apiFetch("/profile/mfa/enroll", { method: "POST" }); } export function confirmMfaEnrollment( setupToken: string, code: string, currentPassword: string, ): Promise { return apiFetch("/profile/mfa/enroll/confirm", { method: "POST", body: JSON.stringify({ setupToken, code, currentPassword }), }, { refreshOn401: false }); } export function disableMfa(currentPassword: string, mfaCode: string): Promise { return apiFetch("/profile/mfa/disable", { method: "POST", body: JSON.stringify({ currentPassword, mfaCode }), }, { refreshOn401: false }); }