Files
carrotskin/src/lib/api.ts
lan 2124790c8d
Some checks failed
Build and Push Docker Image / build-and-push (push) Has been cancelled
feat: add Docker support and texture deletion functionality
Add Docker configuration with standalone output mode for containerized
deployment. Implement texture deletion API with proper error handling
and user feedback. Fix skin viewer sizing issues by using explicit
dimensions and removing conflicting layout properties. Add captcha
ID parameter to registration flow. Improve profile page UX including
Yggdrasil password reset display and character card editing controls.
2026-02-24 11:09:37 +08:00

326 lines
8.2 KiB
TypeScript

export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || '/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();
}
// 删除材质
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();
}