2026-02-12 20:54:06 +08:00
|
|
|
|
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || '/api/v1';
|
2025-12-04 20:05:13 +08:00
|
|
|
|
|
|
|
|
|
|
export interface Texture {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
uploader_id: number;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
description?: string;
|
|
|
|
|
|
type: 'SKIN' | 'CAPE';
|
|
|
|
|
|
url: string;
|
|
|
|
|
|
hash: string;
|
|
|
|
|
|
size: number;
|
|
|
|
|
|
is_public: boolean;
|
|
|
|
|
|
download_count: number;
|
|
|
|
|
|
favorite_count: number;
|
|
|
|
|
|
is_slim: boolean;
|
|
|
|
|
|
status: number;
|
|
|
|
|
|
created_at: string;
|
|
|
|
|
|
updated_at: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface Profile {
|
|
|
|
|
|
uuid: string;
|
|
|
|
|
|
user_id: number;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
skin_id?: number;
|
|
|
|
|
|
cape_id?: number;
|
2026-07-08 17:36:09 +08:00
|
|
|
|
is_active?: boolean;
|
2025-12-04 20:05:13 +08:00
|
|
|
|
last_used_at?: string;
|
|
|
|
|
|
created_at: string;
|
|
|
|
|
|
updated_at: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface PaginatedResponse<T> {
|
|
|
|
|
|
list: T[];
|
|
|
|
|
|
total: number;
|
|
|
|
|
|
page: number;
|
|
|
|
|
|
page_size: number;
|
|
|
|
|
|
total_pages: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-08 17:36:09 +08:00
|
|
|
|
// 后端原始分页响应结构(统一返回 { list, total, page, per_page },不含 total_pages/page_size)
|
|
|
|
|
|
type RawPaginatedData<T> = {
|
|
|
|
|
|
list?: T[];
|
|
|
|
|
|
total?: number;
|
|
|
|
|
|
page?: number;
|
|
|
|
|
|
per_page?: number;
|
|
|
|
|
|
page_size?: number;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 在前端补齐 page_size 与 total_pages(由 total / page_size 向上取整),以便 UI 分页使用
|
|
|
|
|
|
function normalizePaginatedData<T>(raw: RawPaginatedData<T>): PaginatedResponse<T> {
|
|
|
|
|
|
const list = raw.list ?? [];
|
|
|
|
|
|
const total = raw.total ?? 0;
|
|
|
|
|
|
const page = raw.page ?? 1;
|
|
|
|
|
|
const page_size = raw.per_page ?? raw.page_size ?? 20;
|
|
|
|
|
|
const total_pages = page_size > 0 ? Math.max(1, Math.ceil(total / page_size)) : 1;
|
|
|
|
|
|
return { list, total, page, page_size, total_pages };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 后端错误响应的 data 可能不是分页结构(code !== 200),用此类型宽松接收
|
|
|
|
|
|
type RawApiResponse<T> = {
|
|
|
|
|
|
code: number;
|
|
|
|
|
|
message: string;
|
|
|
|
|
|
data: T;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-12-04 20:05:13 +08:00
|
|
|
|
export interface ApiResponse<T> {
|
|
|
|
|
|
code: number;
|
|
|
|
|
|
message: string;
|
|
|
|
|
|
data: T;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取认证头
|
|
|
|
|
|
function getAuthHeaders(): HeadersInit {
|
|
|
|
|
|
const token = typeof window !== 'undefined' ? localStorage.getItem('authToken') : null;
|
|
|
|
|
|
return {
|
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
|
...(token && { Authorization: `Bearer ${token}` }),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 搜索材质
|
|
|
|
|
|
export async function searchTextures(params: {
|
|
|
|
|
|
keyword?: string;
|
|
|
|
|
|
type?: 'SKIN' | 'CAPE';
|
|
|
|
|
|
public_only?: boolean;
|
|
|
|
|
|
page?: number;
|
|
|
|
|
|
page_size?: number;
|
|
|
|
|
|
}): Promise<ApiResponse<PaginatedResponse<Texture>>> {
|
|
|
|
|
|
const queryParams = new URLSearchParams();
|
|
|
|
|
|
if (params.keyword) queryParams.append('keyword', params.keyword);
|
|
|
|
|
|
if (params.type) queryParams.append('type', params.type);
|
|
|
|
|
|
if (params.public_only !== undefined) queryParams.append('public_only', String(params.public_only));
|
|
|
|
|
|
if (params.page) queryParams.append('page', String(params.page));
|
|
|
|
|
|
if (params.page_size) queryParams.append('page_size', String(params.page_size));
|
|
|
|
|
|
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/texture?${queryParams.toString()}`, {
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-08 17:36:09 +08:00
|
|
|
|
const result: RawApiResponse<RawPaginatedData<Texture>> = await response.json();
|
|
|
|
|
|
if (result.code === 200 && result.data) {
|
|
|
|
|
|
return { ...result, data: normalizePaginatedData(result.data) };
|
|
|
|
|
|
}
|
|
|
|
|
|
return result as ApiResponse<PaginatedResponse<Texture>>;
|
2025-12-04 20:05:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取材质详情
|
|
|
|
|
|
export async function getTexture(id: number): Promise<ApiResponse<Texture>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/texture/${id}`, {
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 切换收藏状态
|
|
|
|
|
|
export async function toggleFavorite(id: number): Promise<ApiResponse<{ is_favorited: boolean }>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/texture/${id}/favorite`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户上传的材质列表
|
|
|
|
|
|
export async function getMyTextures(params: {
|
|
|
|
|
|
page?: number;
|
|
|
|
|
|
page_size?: number;
|
|
|
|
|
|
}): Promise<ApiResponse<PaginatedResponse<Texture>>> {
|
|
|
|
|
|
const queryParams = new URLSearchParams();
|
|
|
|
|
|
if (params.page) queryParams.append('page', String(params.page));
|
|
|
|
|
|
if (params.page_size) queryParams.append('page_size', String(params.page_size));
|
|
|
|
|
|
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/texture/my?${queryParams.toString()}`, {
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-08 17:36:09 +08:00
|
|
|
|
const result: RawApiResponse<RawPaginatedData<Texture>> = await response.json();
|
|
|
|
|
|
if (result.code === 200 && result.data) {
|
|
|
|
|
|
return { ...result, data: normalizePaginatedData(result.data) };
|
|
|
|
|
|
}
|
|
|
|
|
|
return result as ApiResponse<PaginatedResponse<Texture>>;
|
2025-12-04 20:05:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户收藏的材质列表
|
|
|
|
|
|
export async function getFavoriteTextures(params: {
|
|
|
|
|
|
page?: number;
|
|
|
|
|
|
page_size?: number;
|
|
|
|
|
|
}): Promise<ApiResponse<PaginatedResponse<Texture>>> {
|
|
|
|
|
|
const queryParams = new URLSearchParams();
|
|
|
|
|
|
if (params.page) queryParams.append('page', String(params.page));
|
|
|
|
|
|
if (params.page_size) queryParams.append('page_size', String(params.page_size));
|
|
|
|
|
|
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/texture/favorites?${queryParams.toString()}`, {
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-08 17:36:09 +08:00
|
|
|
|
const result: RawApiResponse<RawPaginatedData<Texture>> = await response.json();
|
|
|
|
|
|
if (result.code === 200 && result.data) {
|
|
|
|
|
|
return { ...result, data: normalizePaginatedData(result.data) };
|
|
|
|
|
|
}
|
|
|
|
|
|
return result as ApiResponse<PaginatedResponse<Texture>>;
|
2025-12-04 20:05:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户档案列表
|
|
|
|
|
|
export async function getProfiles(): Promise<ApiResponse<Profile[]>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/profile`, {
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建档案
|
|
|
|
|
|
export async function createProfile(name: string): Promise<ApiResponse<Profile>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/profile`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
body: JSON.stringify({ name }),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新档案
|
2026-07-08 17:36:09 +08:00
|
|
|
|
// skin_id / cape_id: 显式传 null 表示移除当前皮肤的关联;传 number 表示设置;不传表示不修改。
|
2025-12-04 20:05:13 +08:00
|
|
|
|
export async function updateProfile(uuid: string, data: {
|
|
|
|
|
|
name?: string;
|
2026-07-08 17:36:09 +08:00
|
|
|
|
skin_id?: number | null;
|
|
|
|
|
|
cape_id?: number | null;
|
2025-12-04 20:05:13 +08:00
|
|
|
|
}): Promise<ApiResponse<Profile>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/profile/${uuid}`, {
|
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
body: JSON.stringify(data),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除档案
|
|
|
|
|
|
export async function deleteProfile(uuid: string): Promise<ApiResponse<null>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/profile/${uuid}`, {
|
|
|
|
|
|
method: 'DELETE',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户信息
|
|
|
|
|
|
export async function getUserProfile(): Promise<ApiResponse<{
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
username: string;
|
|
|
|
|
|
email: string;
|
|
|
|
|
|
avatar?: string;
|
|
|
|
|
|
points: number;
|
|
|
|
|
|
role: string;
|
|
|
|
|
|
status: number;
|
|
|
|
|
|
last_login_at?: string;
|
|
|
|
|
|
created_at: string;
|
|
|
|
|
|
updated_at: string;
|
|
|
|
|
|
}>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/user/profile`, {
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新用户信息
|
|
|
|
|
|
export async function updateUserProfile(data: {
|
|
|
|
|
|
avatar?: string;
|
|
|
|
|
|
old_password?: string;
|
|
|
|
|
|
new_password?: string;
|
|
|
|
|
|
}): Promise<ApiResponse<{
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
username: string;
|
|
|
|
|
|
email: string;
|
|
|
|
|
|
avatar?: string;
|
|
|
|
|
|
points: number;
|
|
|
|
|
|
role: string;
|
|
|
|
|
|
status: number;
|
|
|
|
|
|
last_login_at?: string;
|
|
|
|
|
|
created_at: string;
|
|
|
|
|
|
updated_at: string;
|
|
|
|
|
|
}>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/user/profile`, {
|
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
body: JSON.stringify(data),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 直接上传皮肤文件
|
|
|
|
|
|
export async function uploadTexture(file: File, data: {
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
description?: string;
|
|
|
|
|
|
type?: 'SKIN' | 'CAPE';
|
|
|
|
|
|
is_public?: boolean;
|
|
|
|
|
|
is_slim?: boolean;
|
|
|
|
|
|
}): Promise<ApiResponse<Texture>> {
|
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
|
formData.append('file', file);
|
|
|
|
|
|
formData.append('name', data.name);
|
|
|
|
|
|
if (data.description) formData.append('description', data.description);
|
|
|
|
|
|
if (data.type) formData.append('type', data.type);
|
|
|
|
|
|
if (data.is_public !== undefined) formData.append('is_public', String(data.is_public));
|
|
|
|
|
|
if (data.is_slim !== undefined) formData.append('is_slim', String(data.is_slim));
|
|
|
|
|
|
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/texture/upload`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
...(typeof window !== 'undefined' ? { Authorization: `Bearer ${localStorage.getItem('authToken')}` } : {}),
|
|
|
|
|
|
},
|
|
|
|
|
|
body: formData,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-08 17:36:09 +08:00
|
|
|
|
// 直接上传头像文件到后端(multipart/form-data)
|
|
|
|
|
|
// 后端接口 POST /user/avatar/upload,由后端负责写入对象存储并更新用户头像
|
|
|
|
|
|
export async function uploadAvatar(file: File): Promise<ApiResponse<{
|
2025-12-04 20:05:13 +08:00
|
|
|
|
avatar_url: string;
|
2026-07-08 17:36:09 +08:00
|
|
|
|
user: {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
username: string;
|
|
|
|
|
|
email: string;
|
|
|
|
|
|
avatar: string;
|
|
|
|
|
|
points: number;
|
|
|
|
|
|
role: string;
|
|
|
|
|
|
status: number;
|
|
|
|
|
|
last_login_at?: string;
|
|
|
|
|
|
created_at: string;
|
|
|
|
|
|
updated_at: string;
|
|
|
|
|
|
};
|
2025-12-04 20:05:13 +08:00
|
|
|
|
}>> {
|
2026-07-08 17:36:09 +08:00
|
|
|
|
const formData = new FormData();
|
|
|
|
|
|
formData.append('file', file);
|
|
|
|
|
|
|
|
|
|
|
|
const token = typeof window !== 'undefined' ? localStorage.getItem('authToken') : null;
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/user/avatar/upload`, {
|
2025-12-04 20:05:13 +08:00
|
|
|
|
method: 'POST',
|
2026-07-08 17:36:09 +08:00
|
|
|
|
headers: {
|
|
|
|
|
|
...(token && { Authorization: `Bearer ${token}` }),
|
|
|
|
|
|
},
|
|
|
|
|
|
body: formData,
|
2025-12-04 20:05:13 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-08 17:36:09 +08:00
|
|
|
|
// 更新头像URL(用于外部URL场景,区别于文件直传)
|
2025-12-04 20:05:13 +08:00
|
|
|
|
export async function updateAvatarUrl(avatarUrl: string): Promise<ApiResponse<{
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
username: string;
|
|
|
|
|
|
email: string;
|
|
|
|
|
|
avatar: string;
|
|
|
|
|
|
points: number;
|
|
|
|
|
|
role: string;
|
|
|
|
|
|
status: number;
|
|
|
|
|
|
last_login_at?: string;
|
|
|
|
|
|
created_at: string;
|
|
|
|
|
|
updated_at: string;
|
|
|
|
|
|
}>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/user/avatar?avatar_url=${encodeURIComponent(avatarUrl)}`, {
|
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-04 22:33:46 +08:00
|
|
|
|
// 重置Yggdrasil密码
|
|
|
|
|
|
export async function resetYggdrasilPassword(): Promise<ApiResponse<{
|
|
|
|
|
|
password: string;
|
|
|
|
|
|
}>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/user/yggdrasil-password/reset`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-24 11:09:37 +08:00
|
|
|
|
// 删除材质
|
|
|
|
|
|
export async function deleteTexture(id: number): Promise<ApiResponse<null>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/texture/${id}`, {
|
|
|
|
|
|
method: 'DELETE',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-08 17:36:09 +08:00
|
|
|
|
// 更新材质信息(名称、描述、公开性)
|
|
|
|
|
|
// 对应后端 PUT /texture/{id}:is_public 为布尔值时会更新该字段,会影响公开/隐藏状态
|
|
|
|
|
|
export async function updateTexture(id: number, data: {
|
|
|
|
|
|
name?: string;
|
|
|
|
|
|
description?: string;
|
|
|
|
|
|
is_public?: boolean;
|
|
|
|
|
|
}): Promise<ApiResponse<Texture>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/texture/${id}`, {
|
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
body: JSON.stringify(data),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送邮箱验证码
|
|
|
|
|
|
// type: register | reset_password | change_email
|
|
|
|
|
|
export async function sendVerificationCode(email: string, type: 'register' | 'reset_password' | 'change_email'): Promise<ApiResponse<null>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/auth/send-code`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify({ email, type }),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更换邮箱
|
|
|
|
|
|
export async function changeEmail(newEmail: string, verificationCode: string): Promise<ApiResponse<{
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
username: string;
|
|
|
|
|
|
email: string;
|
|
|
|
|
|
avatar: string;
|
|
|
|
|
|
points: number;
|
|
|
|
|
|
role: string;
|
|
|
|
|
|
status: number;
|
|
|
|
|
|
last_login_at?: string;
|
|
|
|
|
|
created_at: string;
|
|
|
|
|
|
updated_at: string;
|
|
|
|
|
|
}>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/user/change-email`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: getAuthHeaders(),
|
|
|
|
|
|
body: JSON.stringify({ new_email: newEmail, verification_code: verificationCode }),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重置密码(通过邮箱验证码)
|
|
|
|
|
|
// 对应后端 POST /auth/reset-password,无需 JWT
|
|
|
|
|
|
export async function resetPassword(email: string, verificationCode: string, newPassword: string): Promise<ApiResponse<null>> {
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/auth/reset-password`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
|
email,
|
|
|
|
|
|
verification_code: verificationCode,
|
|
|
|
|
|
new_password: newPassword,
|
|
|
|
|
|
}),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|