samar m commited on
Commit
09a5796
·
1 Parent(s): a9a0a73

feat: implement apiFetch wrapper and auth API calls

Browse files
frontend/src/api/auth.ts CHANGED
@@ -1 +1,45 @@
1
- // Auth API calls implemented in Phase 2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { apiFetch } from './client'
2
+ import type { AuthResponse } from '../types'
3
+
4
+ export async function login(email: string, password: string): Promise<AuthResponse> {
5
+ const res = await apiFetch('/api/auth/login', {
6
+ method: 'POST',
7
+ body: JSON.stringify({ email, password }),
8
+ })
9
+ if (!res.ok) {
10
+ const err = await res.json()
11
+ throw new Error(err.detail ?? 'Login failed')
12
+ }
13
+ return res.json()
14
+ }
15
+
16
+ export async function signup(
17
+ full_name: string,
18
+ email: string,
19
+ password: string,
20
+ role: string,
21
+ class_code?: string
22
+ ): Promise<AuthResponse> {
23
+ const res = await apiFetch('/api/auth/signup', {
24
+ method: 'POST',
25
+ body: JSON.stringify({ full_name, email, password, role, class_code }),
26
+ })
27
+ if (!res.ok) {
28
+ const err = await res.json()
29
+ throw new Error(err.detail ?? 'Signup failed')
30
+ }
31
+ return res.json()
32
+ }
33
+
34
+ export async function logout(): Promise<void> {
35
+ await apiFetch('/api/auth/logout', { method: 'POST' })
36
+ }
37
+
38
+ export async function refreshToken(): Promise<{ access_token: string }> {
39
+ const res = await fetch('/api/auth/refresh', {
40
+ method: 'POST',
41
+ credentials: 'include',
42
+ })
43
+ if (!res.ok) throw new Error('Refresh failed')
44
+ return res.json()
45
+ }
frontend/src/api/client.ts ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useAuthStore } from '../store/authStore'
2
+
3
+ const API_BASE = ''
4
+
5
+ async function refreshToken(): Promise<boolean> {
6
+ const res = await fetch(`${API_BASE}/api/auth/refresh`, {
7
+ method: 'POST',
8
+ credentials: 'include',
9
+ })
10
+ if (!res.ok) return false
11
+ const data = await res.json()
12
+ const { user } = useAuthStore.getState()
13
+ if (user) useAuthStore.getState().setAuth(data.access_token, user)
14
+ return true
15
+ }
16
+
17
+ export async function apiFetch(path: string, options: RequestInit = {}): Promise<Response> {
18
+ const token = useAuthStore.getState().accessToken
19
+
20
+ const res = await fetch(`${API_BASE}${path}`, {
21
+ ...options,
22
+ headers: {
23
+ 'Content-Type': 'application/json',
24
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
25
+ ...options.headers,
26
+ },
27
+ credentials: 'include',
28
+ })
29
+
30
+ if (res.status === 401) {
31
+ const refreshed = await refreshToken()
32
+ if (!refreshed) {
33
+ useAuthStore.getState().clearAuth()
34
+ window.location.href = '/login'
35
+ return res
36
+ }
37
+ const newToken = useAuthStore.getState().accessToken
38
+ return fetch(`${API_BASE}${path}`, {
39
+ ...options,
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ ...(newToken ? { Authorization: `Bearer ${newToken}` } : {}),
43
+ ...options.headers,
44
+ },
45
+ credentials: 'include',
46
+ })
47
+ }
48
+
49
+ return res
50
+ }