source: petify-frontend/src/api/auth.ts@ ae83647

Last change on this file since ae83647 was ae83647, checked in by veronika-ils <ilioskaveronika@…>, 2 days ago

add functionality so that users can change passwords

  • Property mode set to 100644
File size: 4.6 KB
RevLine 
[92e7c7a]1export interface LoginRequest {
2 username: string
3 password: string
4}
5
6export interface SignupRequest {
7 username: string
8 email: string
9 password: string
10 firstName: string
11 lastName: string
12}
13
14export interface AuthResult {
15 token?: string
16 user?: {
17 userId: number
18 username: string
19 email: string
20 firstName: string
21 lastName: string
22 userType: string
23 verified: boolean
24 }
25 message?: string
26}
27
[ae83647]28export interface ChangePasswordRequest {
29 userId: number
30 currentPassword: string
31 newPassword: string
32}
33
34export interface ForgotPasswordRequest {
35 identifier: string
36}
37
[92e7c7a]38function getBaseUrl(): string {
39 const base = (import.meta.env.VITE_API_BASE_URL as string | undefined) ?? ''
40 return base.replace(/\/$/, '')
41}
42
43function joinUrl(base: string, path: string): string {
44 if (!base) return path
45 return `${base}${path.startsWith('/') ? '' : '/'}${path}`
46}
47
48function extractToken(data: unknown): string | null {
49 if (!data || typeof data !== 'object') return null
50 const rec = data as Record<string, unknown>
51 const candidates = [rec.token, rec.accessToken, rec.jwt, rec.idToken]
52 for (const c of candidates) {
53 if (typeof c === 'string' && c.trim()) return c
54 }
55 // Sometimes APIs wrap: { data: { token: '...' } }
56 const nested = rec.data
57 if (nested && typeof nested === 'object') {
58 const n = nested as Record<string, unknown>
59 const nestedCandidates = [n.token, n.accessToken, n.jwt, n.idToken]
60 for (const c of nestedCandidates) {
61 if (typeof c === 'string' && c.trim()) return c
62 }
63 }
64 return null
65}
66
67async function postJson<T>(path: string, body: unknown, options?: { signal?: AbortSignal }): Promise<T> {
68 const url = joinUrl(getBaseUrl(), path)
69 const res = await fetch(url, {
70 method: 'POST',
71 headers: {
72 'Content-Type': 'application/json',
73 Accept: 'application/json',
74 },
75 body: JSON.stringify(body),
76 signal: options?.signal,
77 })
78
79 const text = await res.text()
80 let json: unknown = null
81 try {
82 json = text ? (JSON.parse(text) as unknown) : null
83 } catch {
84 json = null
85 }
86
87 if (!res.ok) {
88 const message =
89 (json && typeof json === 'object' && typeof (json as any).message === 'string' && (json as any).message) ||
90 text ||
91 `Request failed (${res.status})`
92 throw new Error(message)
93 }
94
95 return json as T
96}
97
98export async function login(payload: LoginRequest, options?: { signal?: AbortSignal }): Promise<AuthResult> {
99 const data = await postJson<any>('/api/auth/login', payload, options)
100
101 // Check if there's an error message
102 if (data.message && data.message !== "Login successful") {
103 throw new Error(data.message)
104 }
105
106 // Extract user information from the response
107 if (data.userId && data.username) {
108 return {
109 user: {
110 userId: data.userId,
111 username: data.username,
112 email: data.email,
113 firstName: data.firstName,
114 lastName: data.lastName,
115 userType: data.userType,
116 verified: data.verified || false // Use verified status from API response
117 },
118 message: data.message
119 }
120 }
121
122 // If we don't get user data, something went wrong
123 throw new Error('Login failed: Invalid response from server')
124}
125
126export async function signup(payload: SignupRequest, options?: { signal?: AbortSignal }): Promise<AuthResult> {
127 const data = await postJson<any>('/api/auth/signup', payload, options)
128
129 // Check if there's an error message
130 if (data.message && data.message !== "User registered successfully") {
131 throw new Error(data.message)
132 }
133
134 // Extract user information from the response if successful
135 if (data.userId && data.username) {
136 return {
137 user: {
138 userId: data.userId,
139 username: data.username,
140 email: data.email,
141 firstName: data.firstName,
142 lastName: data.lastName,
143 userType: data.userType,
144 verified: data.verified || false // Use verified status from API response
145 },
146 message: data.message
147 }
148 }
149
150 // If registration was successful but no user data returned, that's okay
151 return { message: data.message || "Registration successful" }
152}
[ae83647]153
154export async function changePassword(payload: ChangePasswordRequest, options?: { signal?: AbortSignal }): Promise<void> {
155 await postJson<{ message?: string }>('/api/auth/change-password', payload, options)
156}
157
158export async function forgotPassword(payload: ForgotPasswordRequest, options?: { signal?: AbortSignal }): Promise<string> {
159 const data = await postJson<{ message?: string }>('/api/auth/forgot-password', payload, options)
160 return data.message || 'If an account matches that username or email, a temporary password has been sent.'
161}
Note: See TracBrowser for help on using the repository browser.