diff --git a/src/api/user.ts b/src/api/user.ts new file mode 100644 index 0000000..84b7b9f --- /dev/null +++ b/src/api/user.ts @@ -0,0 +1,7 @@ +import apiClient from "./axios"; +import type { SessionProfileResponse } from "@/types"; + +export const getUserProfile = (uuid: string) => { + // Этот эндпоинт не начинается с /api, поэтому указываем полный путь + return apiClient.get(`/sessionserver/session/minecraft/profile/${uuid}`); +}; diff --git a/src/stores/auth.ts b/src/stores/auth.ts index 04dfe8a..d05165f 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -1,6 +1,7 @@ import { defineStore } from "pinia"; import { ref, computed } from "vue"; import { registerUser as apiRegisterUser } from "@/api/auth"; +import { getUserProfile as apiGetUserProfile } from "@/api/user"; import type { RegisterRequest } from "@/types"; import router from "@/router"; import apiClient from "@/api/axios"; @@ -9,6 +10,7 @@ export const useAuthStore = defineStore("auth", () => { // State const user = ref(null); const token = ref(localStorage.getItem("authToken")); + const skinUrl = ref(null); const isLoading = ref(false); const error = ref(null); @@ -16,11 +18,34 @@ export const useAuthStore = defineStore("auth", () => { const isAuthenticated = computed(() => !!user.value && !!token.value); // Actions + async function fetchUserProfile() { + if (!user.value) return; + + try { + const response = await apiGetUserProfile(user.value.uuid); + if (response.data && response.data.properties) { + const textureProp = response.data.properties.find((p) => p.name === "textures"); + if (textureProp) { + // Декодируем Base64-строку и парсим JSON + const textureData = JSON.parse(atob(textureProp.value)); + if (textureData.textures?.SKIN?.url) { + skinUrl.value = textureData.textures.SKIN.url; + } else { + skinUrl.value = null; // У пользователя нет скина + } + } + } + } catch (e) { + console.error("Failed to fetch user profile:", e); + skinUrl.value = null; + } + } function setAuthData(userData: User, authToken: string) { user.value = userData; token.value = authToken; localStorage.setItem("authToken", authToken); apiClient.defaults.headers.common["Authorization"] = `Bearer ${authToken}`; + fetchUserProfile(); } async function handleLogin(credentials: LoginRequest) { @@ -61,15 +86,24 @@ export const useAuthStore = defineStore("auth", () => { function handleLogout() { user.value = null; token.value = null; + skinUrl.value = null; localStorage.removeItem("authToken"); delete apiClient.defaults.headers.common["Authorization"]; router.push({ name: "login" }); } + async function checkAuth() { + if (token.value) { + if (user.value) { + await fetchUserProfile(); + } + } + } return { // State user, token, + skinUrl, isLoading, error, // Getters @@ -78,5 +112,7 @@ export const useAuthStore = defineStore("auth", () => { handleLogin, handleRegister, handleLogout, + fetchUserProfile, + checkAuth, }; }); diff --git a/src/types.ts b/src/types.ts index 27c2c9e..7f2a72e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -26,3 +26,15 @@ export interface User { created_at: string; updated_at: string; } + +export interface ProfileProperty { + name: string; + value: string; // Base64-encoded JSON + signature: string; +} + +export interface SessionProfileResponse { + id: string; + name: string; + properties: ProfileProperty[]; +} diff --git a/src/views/AccountView.vue b/src/views/AccountView.vue index 6126192..14217cc 100644 --- a/src/views/AccountView.vue +++ b/src/views/AccountView.vue @@ -71,23 +71,49 @@ const onSkinUpload = async () => { if (skinViewer) { skinViewer.loadSkin(URL.createObjectURL(selectedFile.value)); } + await authStore.fetchUserProfile(); } catch (e: any) { uploadError.value = e.response?.data || "Ошибка при загрузке файла."; } finally { isLoading.value = false; } }; +// --- Логика для 3D-вьювера --- +const skinCanvas = ref(null); +let skinViewer: SkinViewer | null = null; -// Инициализация 3D-вьювера при монтировании компонента -onMounted(() => { - if (skinCanvas.value) { +const setupSkinViewer = () => { + if (skinCanvas.value && !skinViewer) { skinViewer = new SkinViewer({ canvas: skinCanvas.value, width: 300, height: 400, - skin: "/default_skin.png", }); } +}; + +// Следим за изменением URL скина в сторе и обновляем вьювер +watch( + () => authStore.skinUrl, + (newUrl) => { + if (skinViewer) { + if (newUrl) { + skinViewer.loadSkin(newUrl); + } else { + // Если у пользователя нет скина, показываем скин по умолчанию + skinViewer.loadSkin("/default_skin.png"); + } + } + }, + { immediate: true }, +); + +// Инициализация 3D-вьювера при монтировании компонента +onMounted(() => { + setupSkinViewer(); + if (!authStore.skinUrl && authStore.isAuthenticated) { + authStore.fetchUserProfile(); + } }); // Очистка ресурсов при размонтировании