Files
carrotskin/src/lib/api.ts
lan a9ff72a9bf feat: 增加用户皮肤管理功能和Yggdrasil密码重置
- 在用户资料页面添加皮肤选择和管理功能,支持上传、配置和移除皮肤
- 实现Yggdrasil密码重置功能,用户可生成新密码并显示
- 优化皮肤展示和选择界面,增强用户体验
- 更新SkinViewer组件,支持跑步和跳跃动画
2025-12-04 22:33:46 +08:00

316 lines
8.0 KiB
TypeScript

const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8080/api/v1';
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;
is_active: boolean;
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;
}
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',
},
});
return response.json();
}
// 获取材质详情
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(),
});
return response.json();
}
// 获取用户收藏的材质列表
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(),
});
return response.json();
}
// 获取用户档案列表
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();
}
// 更新档案
export async function updateProfile(uuid: string, data: {
name?: string;
skin_id?: number;
cape_id?: number;
}): 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 setActiveProfile(uuid: string): Promise<ApiResponse<{ message: string }>> {
const response = await fetch(`${API_BASE_URL}/profile/${uuid}/activate`, {
method: 'POST',
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();
}
// 生成头像上传URL
export async function generateAvatarUploadUrl(fileName: string): Promise<ApiResponse<{
post_url: string;
form_data: Record<string, string>;
avatar_url: string;
expires_in: number;
}>> {
const response = await fetch(`${API_BASE_URL}/user/avatar/upload-url`, {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ file_name: fileName }),
});
return response.json();
}
// 更新头像URL
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();
}
// 重置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();
}