elearning/Frontend-Learner/composables/useAuth.ts

245 lines
6.1 KiB
TypeScript
Raw Normal View History

2026-01-13 10:46:40 +07:00
// Shared global state for current user
2026-01-14 15:15:31 +07:00
import type { H3Event } from 'h3'
// Types based on API responses
interface User {
id: number
username: string
email: string
role: {
code: string
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 loginResponse {
token: string
refreshToken: string
user: User
profile: User['profile']
}
interface RegisterPayload {
username: string
email: string
password: string
first_name: string
last_name: string
prefix: { th: string; en: string }
phone: string
}
2026-01-13 10:46:40 +07:00
export const useAuth = () => {
2026-01-14 15:15:31 +07:00
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBase as string
2026-01-13 10:46:40 +07:00
const token = useCookie('auth_token', {
2026-01-14 15:15:31 +07:00
maxAge: 60 * 60 * 24, // 1 day
sameSite: 'lax',
secure: false // Set to true in production with HTTPS
})
const user = useCookie<User | null>('auth_user_data', {
2026-01-13 10:46:40 +07:00
maxAge: 60 * 60 * 24 * 7, // 1 week
sameSite: 'lax',
2026-01-14 15:15:31 +07:00
secure: false
2026-01-13 10:46:40 +07:00
})
const isAuthenticated = computed(() => !!token.value)
2026-01-14 15:15:31 +07:00
// Refresh Token Logic
const refreshToken = useCookie('auth_refresh_token', {
maxAge: 60 * 60 * 24 * 7, // 7 days (matching API likely)
sameSite: 'lax',
secure: false
})
// ... (previous code)
// Login
const login = async (credentials: { email: string; password: string }) => {
try {
const { data, error } = await useFetch<loginResponse>(`${API_BASE_URL}/auth/login`, {
method: 'POST',
body: credentials
})
if (error.value) {
throw error.value
}
if (data.value) {
// Validation: Only allow STUDENT role to login
if (data.value.user.role.code !== 'STUDENT') {
return { success: false, error: 'Email ไม่ถูกต้อง' }
}
2026-01-14 15:15:31 +07:00
token.value = data.value.token
refreshToken.value = data.value.refreshToken // Save refresh token
// The API returns the profile nested inside the user object
user.value = data.value.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, error } = await useFetch(`${API_BASE_URL}/auth/register`, {
method: 'POST',
body: payload
})
if (error.value) {
throw error.value
}
return { success: true, data: data.value }
} catch (err: any) {
console.error('Register failed:', err)
return {
success: false,
error: err.data?.message || err.data?.error || 'ลงทะเบียนไม่สำเร็จ'
}
}
}
// Fetch User Profile (/api/user/me)
const fetchUserProfile = async () => {
if (!token.value) return
try {
const { data, error } = await useFetch<User>(`${API_BASE_URL}/user/me`, {
headers: {
Authorization: `Bearer ${token.value}`
}
})
if (error.value) {
if (error.value.statusCode === 401) {
logout()
}
throw error.value
}
if (data.value) {
user.value = data.value
}
} catch (err) {
console.error('Failed to fetch user profile:', err)
}
}
// Request Password Reset
const requestPasswordReset = async (email: string) => {
try {
const { error } = await useFetch(`${API_BASE_URL}/auth/reset-request`, {
method: 'POST',
body: { email }
})
if (error.value) throw error.value
return { success: true }
} catch (err: any) {
return { success: false, error: err.data?.message || 'ส่งคำขอไม่สำเร็จ' }
}
}
// Confirm Reset Password
const confirmResetPassword = async (payload: { id: number; token: string; password: string }) => {
try {
const { error } = await useFetch(`${API_BASE_URL}/auth/reset-password`, {
method: 'POST',
body: payload
})
if (error.value) throw error.value
return { success: true }
} catch (err: any) {
return { success: false, error: err.data?.message || 'รีเซ็ตรหัสผ่านไม่สำเร็จ' }
}
}
// Refresh Access Token
const refreshAccessToken = async () => {
if (!refreshToken.value) return false
try {
const { data, error } = await useFetch<{ token: string; refreshToken: string }>(`${API_BASE_URL}/auth/refresh`, {
method: 'POST',
body: { refreshToken: refreshToken.value }
})
if (error.value) throw error.value
if (data.value) {
token.value = data.value.token
refreshToken.value = data.value.refreshToken
return true
}
} catch (err) {
// Refresh failed, force logout
logout()
return false
}
return false
2026-01-13 10:46:40 +07:00
}
2026-01-14 15:15:31 +07:00
// Logout
2026-01-13 10:46:40 +07:00
const logout = () => {
token.value = null
2026-01-14 15:15:31 +07:00
refreshToken.value = null // Clear refresh token
user.value = null
const router = useRouter()
router.push('/auth/login')
2026-01-13 10:46:40 +07:00
}
return {
isAuthenticated,
token,
2026-01-14 15:15:31 +07:00
user,
currentUser: computed(() => {
if (!user.value) return null
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,
firstName,
lastName,
email: user.value.email,
phone: user.value.profile?.phone || '',
photoURL: user.value.profile?.avatar_url || '',
role: user.value.role
}
}),
2026-01-13 10:46:40 +07:00
login,
2026-01-14 15:15:31 +07:00
register,
fetchUserProfile,
requestPasswordReset,
confirmResetPassword,
2026-01-13 10:46:40 +07:00
logout
}
}