All checks were successful
Build and Deploy Frontend Management to Dev Server / Build Frontend Management Docker Image (push) Successful in 52s
Build and Deploy Frontend Management to Dev Server / Deploy E-learning Frontend Management to Dev Server (push) Successful in 4s
Build and Deploy Frontend Management to Dev Server / Notify Deployment Status (push) Successful in 1s
218 lines
6.7 KiB
TypeScript
218 lines
6.7 KiB
TypeScript
export interface LoginRequest {
|
|
email: string;
|
|
password: string;
|
|
}
|
|
|
|
// API Response structure (from backend) - new format: only token/refreshToken
|
|
export interface ApiLoginResponse {
|
|
token: string;
|
|
refreshToken: string;
|
|
}
|
|
|
|
// JWT Payload structure (decoded from token)
|
|
export interface JwtPayload {
|
|
id: number;
|
|
username: string;
|
|
email: string;
|
|
roleCode: string;
|
|
iat: number;
|
|
exp: number;
|
|
}
|
|
|
|
// Frontend User structure
|
|
export interface LoginResponse {
|
|
token: string;
|
|
refreshToken: string;
|
|
user: {
|
|
id: string;
|
|
email: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
role: string;
|
|
avatarUrl?: string | null;
|
|
};
|
|
message?: string;
|
|
}
|
|
|
|
export interface ApiResponse<T> {
|
|
code: number;
|
|
message: string;
|
|
data: T;
|
|
}
|
|
|
|
/**
|
|
* Decode JWT payload without verification (read-only)
|
|
* Verification is handled by the backend on each request
|
|
*/
|
|
function decodeJwtPayload(token: string): JwtPayload {
|
|
const base64Url = token.split('.')[1];
|
|
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
const jsonPayload = decodeURIComponent(
|
|
atob(base64).split('').map(c =>
|
|
'%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
|
|
).join('')
|
|
);
|
|
return JSON.parse(jsonPayload);
|
|
}
|
|
|
|
export const authService = {
|
|
async login(email: string, password: string): Promise<LoginResponse> {
|
|
const config = useRuntimeConfig();
|
|
|
|
try {
|
|
const response = await $fetch<ApiResponse<ApiLoginResponse>>('/api/auth/login', {
|
|
method: 'POST',
|
|
baseURL: config.public.apiBaseUrl as string,
|
|
body: {
|
|
email,
|
|
password
|
|
}
|
|
});
|
|
|
|
const loginData = response.data;
|
|
|
|
// Decode JWT to get user info
|
|
const payload = decodeJwtPayload(loginData.token);
|
|
|
|
// Check if user role is STUDENT - block login
|
|
if (payload.roleCode === 'STUDENT') {
|
|
throw new Error('ไม่สามารถเข้าสู่ระบบได้ ระบบนี้สำหรับผู้สอนและผู้ดูแลระบบเท่านั้น');
|
|
}
|
|
|
|
// Return basic user info from JWT payload
|
|
// Full profile will be fetched via fetchUserProfile() in the auth store
|
|
return {
|
|
token: loginData.token,
|
|
refreshToken: loginData.refreshToken,
|
|
user: {
|
|
id: payload.id.toString(),
|
|
email: payload.email,
|
|
firstName: '',
|
|
lastName: '',
|
|
role: payload.roleCode,
|
|
avatarUrl: null
|
|
},
|
|
message: response.message || 'เข้าสู่ระบบสำเร็จ'
|
|
};
|
|
} catch (error: any) {
|
|
// Re-throw custom errors (like STUDENT role block)
|
|
if (error.message && !error.response) {
|
|
throw error;
|
|
}
|
|
|
|
// Handle API errors
|
|
const apiError = error.data?.error || error.data;
|
|
const errorMessage = apiError?.message || error.message;
|
|
|
|
if (errorMessage) {
|
|
throw new Error(errorMessage);
|
|
}
|
|
|
|
if (error.response?.status === 401) {
|
|
throw new Error('อีเมลหรือรหัสผ่านไม่ถูกต้อง');
|
|
}
|
|
throw new Error('เกิดข้อผิดพลาดในการเข้าสู่ระบบ');
|
|
}
|
|
},
|
|
|
|
async logout(): Promise<void> {
|
|
// Clear cookies
|
|
const tokenCookie = useCookie('token');
|
|
const refreshTokenCookie = useCookie('refreshToken');
|
|
const userCookie = useCookie('user');
|
|
|
|
tokenCookie.value = null;
|
|
refreshTokenCookie.value = null;
|
|
userCookie.value = null;
|
|
},
|
|
|
|
async forgotPassword(email: string): Promise<ApiResponse<void>> {
|
|
const config = useRuntimeConfig();
|
|
|
|
const response = await $fetch<ApiResponse<void>>('/api/auth/reset-request', {
|
|
method: 'POST',
|
|
baseURL: config.public.apiBaseUrl as string,
|
|
body: { email }
|
|
});
|
|
return response;
|
|
},
|
|
|
|
async resetPassword(token: string, password: string): Promise<void> {
|
|
const config = useRuntimeConfig();
|
|
|
|
await $fetch('/api/auth/reset-password', {
|
|
method: 'POST',
|
|
baseURL: config.public.apiBaseUrl as string,
|
|
body: { token, password }
|
|
});
|
|
},
|
|
|
|
async registerInstructor(data: RegisterInstructorRequest): Promise<void> {
|
|
const config = useRuntimeConfig();
|
|
|
|
await $fetch('/api/auth/register-instructor', {
|
|
method: 'POST',
|
|
baseURL: config.public.apiBaseUrl as string,
|
|
body: data
|
|
});
|
|
},
|
|
|
|
async refreshToken(currentRefreshToken: string): Promise<{ token: string; refreshToken: string }> {
|
|
const config = useRuntimeConfig();
|
|
|
|
if (!currentRefreshToken) {
|
|
throw new Error('No refresh token available');
|
|
}
|
|
|
|
const response = await $fetch<{ token: string; refreshToken: string }>('/api/auth/refresh', {
|
|
method: 'POST',
|
|
baseURL: config.public.apiBaseUrl as string,
|
|
body: { refreshToken: currentRefreshToken }
|
|
});
|
|
|
|
return response;
|
|
},
|
|
|
|
async sendVerifyEmail(): Promise<ApiResponse<void>> {
|
|
const config = useRuntimeConfig();
|
|
const token = useCookie('token').value;
|
|
|
|
const response = await $fetch<ApiResponse<void>>('/api/user/send-verify-email', {
|
|
method: 'POST',
|
|
baseURL: config.public.apiBaseUrl as string,
|
|
headers: {
|
|
Authorization: `Bearer ${token}`
|
|
}
|
|
});
|
|
return response;
|
|
},
|
|
|
|
async verifyEmail(verificationToken: string): Promise<ApiResponse<void>> {
|
|
const config = useRuntimeConfig();
|
|
const token = useCookie('token').value;
|
|
|
|
const response = await $fetch<ApiResponse<void>>('/api/user/verify-email', {
|
|
method: 'POST',
|
|
baseURL: config.public.apiBaseUrl as string,
|
|
headers: {
|
|
Authorization: `Bearer ${token}`
|
|
},
|
|
body: { token: verificationToken }
|
|
});
|
|
return response;
|
|
}
|
|
};
|
|
|
|
// Register Instructor Request
|
|
export interface RegisterInstructorRequest {
|
|
username: string;
|
|
email: string;
|
|
password: string;
|
|
first_name: string;
|
|
last_name: string;
|
|
prefix: {
|
|
en: string;
|
|
th: string;
|
|
};
|
|
phone: string;
|
|
}
|