// Interface สำหรับข้อมูลผู้ใช้งาน (User) interface User { id: number username: string email: string created_at?: string updated_at?: string role: { code: string // เช่น 'STUDENT', 'INSTRUCTOR', 'ADMIN' name: { th: string; en: string } } profile?: { prefix: { th: string; en: string } first_name: string last_name: string phone: string | null avatar_url: string | null } } // Interface สำหรับข้อมูลตอบกลับตอน Login interface loginResponse { token: string refreshToken: string user: User profile: User['profile'] } // Interface สำหรับข้อมูลที่ใช้ลงทะเบียน interface RegisterPayload { username: string email: string password: string first_name: string last_name: string prefix: { th: string; en: string } phone: string } // ========================================== // Composable: useAuth // หน้าที่: จัดการระบบ Authentication และ Authorization // - จัดการ Login/Logout/Register // - จัดการ Token (Access Token & Refresh Token) // - เก็บ State ของผู้ใช้ปัจจุบัน (User State) // ========================================== export const useAuth = () => { const config = useRuntimeConfig() const API_BASE_URL = config.public.apiBase as string // Cookie สำหรับเก็บ Access Token (หมดอายุ 1 วัน) const token = useCookie('auth_token', { maxAge: 60 * 60 * 24, // 1 day sameSite: 'lax', secure: false // ควรเป็น true ใน production (HTTPS) }) // Cookie สำหรับเก็บข้อมูล User (หมดอายุ 7 วัน) const user = useCookie('auth_user_data', { maxAge: 60 * 60 * 24 * 7, // 1 week sameSite: 'lax', secure: false }) // Computed property เช็คว่า Login อยู่หรือไม่ const isAuthenticated = computed(() => !!token.value) // Cookie สำหรับเก็บ Refresh Token (หมดอายุ 7 วัน) const refreshToken = useCookie('auth_refresh_token', { maxAge: 60 * 60 * 24 * 7, // 7 days (ตรงกับ API) sameSite: 'lax', secure: false }) // ฟังก์ชันเข้าสู่ระบบ (Login) const login = async (credentials: { email: string; password: string }) => { try { const data = await $fetch(`${API_BASE_URL}/auth/login`, { method: 'POST', body: credentials }) if (data) { // Validation: อนุญาตเฉพาะ Role 'STUDENT' เท่านั้น if (data.user.role.code !== 'STUDENT') { return { success: false, error: 'Email ไม่ถูกต้อง' } } token.value = data.token refreshToken.value = data.refreshToken // บันทึก Refresh Token // API ส่งข้อมูล profile มาใน user object user.value = data.user return { success: true } } return { success: false, error: 'No data returned' } } catch (err: any) { console.error('Login failed:', err) return { success: false, error: err.data?.message || err.message || 'เข้าสู่ระบบไม่สำเร็จ' } } } // ฟังก์ชันลงทะเบียน (Register) const register = async (payload: RegisterPayload) => { try { const data = await $fetch(`${API_BASE_URL}/auth/register-learner`, { method: 'POST', body: payload }) return { success: true, data } } catch (err: any) { console.error('Register failed:', err) return { success: false, error: err.data?.error?.message || err.data?.message || err.message || 'ลงทะเบียนไม่สำเร็จ' } } } // ฟังก์ชันดึงข้อมูลโปรไฟล์ผู้ใช้ล่าสุด const fetchUserProfile = async () => { if (!token.value) return try { const data = await $fetch(`${API_BASE_URL}/user/me`, { headers: { Authorization: `Bearer ${token.value}` } }) if (data) { user.value = data } } catch (error: any) { // กรณี Token หมดอายุ (401) if (error.statusCode === 401) { // พยายามขอ Token ใหม่ (Refresh Token) const refreshed = await refreshAccessToken() if (refreshed) { // ถ้าได้ Token ใหม่ ให้ลองดึงข้อมูลอีกครั้ง try { const retryData = await $fetch(`${API_BASE_URL}/user/me`, { headers: { Authorization: `Bearer ${token.value}` } }) if (retryData) { user.value = retryData return } } catch (retryErr) { console.error('Failed to fetch user profile after refresh:', retryErr) } } else { // ถ้า Refresh ไม่ผ่าน ให้ Logout logout() } } else { console.error('Failed to fetch user profile:', error) } } } // ฟังก์ชันอัปเดตข้อมูลโปรไฟล์ const updateUserProfile = async (payload: { first_name: string last_name: string phone: string prefix: { th: string; en: string } }) => { if (!token.value) return try { await $fetch(`${API_BASE_URL}/user/me`, { method: 'PUT', headers: { Authorization: `Bearer ${token.value}` }, body: payload }) // หากสำเร็จ ให้ดึงข้อมูลโปรไฟล์ล่าสุดมาอัปเดตใน State await fetchUserProfile() return { success: true } } catch (err: any) { console.error('Failed to update profile:', err) return { success: false, error: err.data?.message || err.message || 'บันทึกข้อมูลไม่สำเร็จ' } } } const requestPasswordReset = async (email: string) => { try { await $fetch(`${API_BASE_URL}/auth/reset-request`, { method: 'POST', body: { email } }) return { success: true } } catch (err: any) { return { success: false, error: err.data?.message || 'ส่งคำขอไม่สำเร็จ' } } } const confirmResetPassword = async (payload: { token: string; password: string }) => { try { await $fetch(`${API_BASE_URL}/auth/reset-password`, { method: 'POST', body: payload }) return { success: true } } catch (err: any) { return { success: false, error: err.data?.message || 'รีเซ็ตรหัสผ่านไม่สำเร็จ' } } } const changePassword = async (payload: { oldPassword: string, newPassword: string }) => { if (!token.value) return { success: false, error: 'ไม่พบ Token การใช้งาน' } try { await $fetch(`${API_BASE_URL}/user/change-password`, { method: 'POST', headers: { Authorization: `Bearer ${token.value}` }, body: payload }) return { success: true } } catch (err: any) { return { success: false, error: err.data?.message || 'เปลี่ยนรหัสผ่านไม่สำเร็จ' } } } // ฟังก์ชันขอ Access Token ใหม่ด้วย Refresh Token const refreshAccessToken = async () => { if (!refreshToken.value) return false try { const data = await $fetch<{ token: string; refreshToken: string }>(`${API_BASE_URL}/auth/refresh`, { method: 'POST', body: { refreshToken: refreshToken.value } }) if (data) { token.value = data.token refreshToken.value = data.refreshToken return true } } catch (err) { // Refresh failed (เช่น Refresh Token หมดอายุ) ให้ Force Logout logout() return false } return false } // ฟังก์ชันออกจากระบบ (Logout) const logout = () => { token.value = null refreshToken.value = null // ลบ Refresh Token user.value = null const router = useRouter() router.push('/auth/login') } return { isAuthenticated, token, user, currentUser: computed(() => { if (!user.value) return null // Helper ในการดึงข้อมูล user ที่อาจซ้อนกันอยู่หลายชั้น const prefix = user.value.profile?.prefix?.th || '' const firstName = user.value.profile?.first_name || user.value.username const lastName = user.value.profile?.last_name || '' return { prefix: user.value.profile?.prefix || { th: '', en: '' }, firstName, lastName, email: user.value.email, phone: user.value.profile?.phone || '', photoURL: user.value.profile?.avatar_url || '', role: user.value.role, createdAt: user.value.created_at || new Date().toISOString() } }), login, register, fetchUserProfile, updateUserProfile, requestPasswordReset, confirmResetPassword, changePassword, refreshAccessToken, logout } }