@bagelink/auth 1.4.169 → 1.4.174

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/useAuth.ts CHANGED
@@ -1,22 +1,22 @@
1
1
  import type { AxiosInstance } from 'axios'
2
2
  import type { App } from 'vue'
3
- import type { User, NewUser, UpdatePasswordForm, AuthEventMap } from './types'
4
- import { ref } from 'vue'
3
+ import type {
4
+ AccountInfo,
5
+ User,
6
+ NewUser,
7
+ UpdatePasswordForm,
8
+ UpdateAccountRequest,
9
+ AuthEventMap,
10
+ } from './types'
11
+ import { ref, computed } from 'vue'
5
12
  import { AuthApi } from './api'
6
- import { AuthState } from './types'
13
+ import { AuthState, accountToUser } from './types'
7
14
  import { EventEmitter } from './utils'
8
15
 
9
16
  // Global state
10
17
  let authApi: AuthApi | null = null
11
18
  let eventEmitter: EventEmitter | null = null
12
- const currentUser = ref<User>({
13
- id: '',
14
- email: '',
15
- first_name: '',
16
- last_name: '',
17
- is_superuser: false,
18
- is_active: false,
19
- })
19
+ const accountInfo = ref<AccountInfo | null>(null)
20
20
 
21
21
  // Initialize auth
22
22
  export function initAuth({
@@ -26,26 +26,32 @@ export function initAuth({
26
26
  axios: AxiosInstance
27
27
  baseURL?: string
28
28
  }) {
29
- if (!authApi) {
29
+ if (authApi === null) {
30
30
  authApi = new AuthApi(axios, baseURL)
31
31
  }
32
32
 
33
- if (!eventEmitter) {
33
+ if (eventEmitter === null) {
34
34
  eventEmitter = new EventEmitter()
35
35
  }
36
36
 
37
37
  return {
38
38
  // Event listener methods
39
39
  on<K extends AuthState>(event: K, handler: AuthEventMap[K]): void {
40
- eventEmitter!.on(event, handler)
40
+ if (eventEmitter) {
41
+ eventEmitter.on(event, handler)
42
+ }
41
43
  },
42
44
 
43
45
  off<K extends AuthState>(event: K, handler: AuthEventMap[K]): void {
44
- eventEmitter!.off(event, handler)
46
+ if (eventEmitter) {
47
+ eventEmitter.off(event, handler)
48
+ }
45
49
  },
46
50
 
47
51
  removeAllListeners<K extends AuthState>(event?: K): void {
48
- eventEmitter!.removeAllListeners(event)
52
+ if (eventEmitter) {
53
+ eventEmitter.removeAllListeners(event)
54
+ }
49
55
  },
50
56
 
51
57
  install(app: App) {
@@ -57,147 +63,243 @@ export function initAuth({
57
63
 
58
64
  // Composable
59
65
  export function useAuth() {
60
- if (!authApi) {
66
+ if (authApi === null) {
61
67
  throw new Error('Auth not initialized. Call initAuth first.')
62
68
  }
63
69
 
70
+ if (eventEmitter === null) {
71
+ throw new Error('Event emitter not initialized. Call initAuth first.')
72
+ }
73
+
74
+ const api = authApi
75
+ const emitter = eventEmitter
76
+
77
+ // Computed user - the primary interface for accessing user data
78
+ const user = computed<User | null>(() => accountToUser(accountInfo.value))
79
+
64
80
  // Getters
65
- const getFullName = () => `${currentUser.value.first_name} ${currentUser.value.last_name}`
66
- const getIsLoggedIn = () => currentUser.value.id.length > 0
81
+ const getFullName = () => {
82
+ return user.value?.name ?? ''
83
+ }
84
+
85
+ const getIsLoggedIn = () => {
86
+ return user.value !== null
87
+ }
88
+
89
+ const getEmail = () => {
90
+ return user.value?.email ?? ''
91
+ }
92
+
93
+ const getRoles = () => {
94
+ return user.value?.roles ?? []
95
+ }
96
+
97
+ const getAccountType = () => {
98
+ return user.value?.type ?? 'person'
99
+ }
100
+
101
+ const isPersonAccount = () => {
102
+ return user.value?.type === 'person'
103
+ }
104
+
105
+ const isEntityAccount = () => {
106
+ return user.value?.type === 'entity'
107
+ }
67
108
 
68
109
  // Actions
69
110
  async function logout() {
70
- try {
71
- await authApi!.logout()
72
- // Reset current user
73
- currentUser.value = {
74
- id: '',
75
- email: '',
76
- first_name: '',
77
- last_name: '',
78
- is_superuser: false,
79
- is_active: false,
80
- }
81
- eventEmitter!.emit(AuthState.LOGOUT)
82
- } catch (error) {
83
- throw error
84
- }
111
+ // Call logout API
112
+ const logoutPromise = api.logout()
113
+ await logoutPromise.catch(() => {
114
+ // Ignore logout errors
115
+ })
116
+ // Clear local state regardless of API result
117
+ accountInfo.value = null
118
+ // Emit logout event
119
+ emitter.emit(AuthState.LOGOUT)
85
120
  }
86
121
 
87
122
  async function login(credentials: { email: string, password: string }) {
88
- try {
89
- await authApi!.login(
90
- credentials.email.toLowerCase(),
91
- credentials.password
92
- )
123
+ const { data } = await api.login(
124
+ credentials.email.toLowerCase(),
125
+ credentials.password
126
+ )
127
+
128
+ // If successful and not requiring verification, fetch user data
129
+ if (data.success === true && data.requires_verification !== true) {
93
130
  await checkAuth()
94
- eventEmitter!.emit(AuthState.LOGIN)
95
- } catch (error) {
96
- throw error
97
131
  }
132
+
133
+ emitter.emit(AuthState.LOGIN)
134
+ return data
98
135
  }
99
136
 
100
137
  async function checkAuth(): Promise<boolean> {
101
138
  try {
102
- if (!getIsLoggedIn()) {
103
- const { data } = await authApi!.getCurrentUser()
104
- currentUser.value = data
105
- if (getIsLoggedIn()) {
106
- eventEmitter!.emit(AuthState.AUTH_CHECK)
107
- }
139
+ const { data } = await api.getCurrentUser()
140
+ accountInfo.value = data
141
+ if (getIsLoggedIn()) {
142
+ emitter.emit(AuthState.AUTH_CHECK)
108
143
  }
109
- } catch (error) {
144
+ return true
145
+ } catch {
146
+ accountInfo.value = null
110
147
  return false
111
148
  }
112
- return getIsLoggedIn()
113
149
  }
114
150
 
115
- async function signup(user: NewUser) {
116
- try {
117
- if (user.password !== user.confirmPassword) {
118
- throw new Error('Passwords do not match')
119
- }
120
- const { data } = await authApi!.signup(user)
121
- currentUser.value = data
122
- eventEmitter!.emit(AuthState.SIGNUP)
123
- } catch (error) {
124
- throw error
151
+ async function signup(newUser: NewUser) {
152
+ // Check password match if password is provided
153
+ const hasPassword = newUser.password !== undefined && newUser.password.length > 0
154
+ if (hasPassword && newUser.password !== newUser.confirmPassword) {
155
+ throw new Error('Passwords do not match')
125
156
  }
126
- }
127
157
 
128
- async function recoverPassword(email: string) {
129
- try {
130
- await authApi!.passwordRecovery(email)
131
- } catch (error) {
132
- throw error
158
+ const { data } = await api.register({
159
+ email: newUser.email,
160
+ first_name: newUser.first_name,
161
+ last_name: newUser.last_name,
162
+ phone_number: newUser.phone_number,
163
+ password: newUser.password,
164
+ })
165
+
166
+ // If successful and not requiring verification, fetch user data
167
+ if (data.success === true && data.requires_verification !== true) {
168
+ await checkAuth()
133
169
  }
170
+
171
+ emitter.emit(AuthState.SIGNUP)
172
+ return data
134
173
  }
135
174
 
136
- async function resetPassword(newPassword: string) {
137
- try {
138
- await authApi!.resetPassword(newPassword)
139
- eventEmitter!.emit(AuthState.PASSWORD_RESET)
140
- } catch (error) {
141
- throw error
142
- }
175
+ async function forgotPassword(email: string) {
176
+ await api.forgotPassword(email)
143
177
  }
144
178
 
145
- async function updatePassword(form: UpdatePasswordForm) {
146
- try {
147
- if (form.new_password !== form.confirmNewPassword) {
148
- throw new Error('Passwords do not match')
149
- }
150
- await authApi!.updatePassword(form)
151
- eventEmitter!.emit(AuthState.PASSWORD_RESET)
152
- } catch (error) {
153
- throw error
154
- }
179
+ async function verifyResetToken(token: string) {
180
+ await api.verifyResetToken(token)
155
181
  }
156
182
 
157
- async function updateProfile(user: Partial<User>) {
158
- try {
159
- const { data } = await authApi!.updateUserProfile(user)
160
- currentUser.value = { ...currentUser.value, ...data }
161
- eventEmitter!.emit(AuthState.PROFILE_UPDATE)
162
- } catch (error) {
163
- throw error
183
+ async function resetPassword(token: string, newPassword: string) {
184
+ await api.resetPassword({ token, new_password: newPassword })
185
+ emitter.emit(AuthState.PASSWORD_RESET)
186
+ }
187
+
188
+ async function changePassword(form: UpdatePasswordForm) {
189
+ if (form.new_password !== form.confirmNewPassword) {
190
+ throw new Error('Passwords do not match')
164
191
  }
192
+ await api.changePassword({
193
+ current_password: form.current_password,
194
+ new_password: form.new_password,
195
+ })
196
+ emitter.emit(AuthState.PASSWORD_CHANGE)
165
197
  }
166
198
 
167
- async function toggleUserStatus(userId: string, isActive: boolean) {
168
- try {
169
- await authApi!.setUserStatus(userId, isActive)
170
- } catch (error) {
171
- throw error
199
+ async function updateProfile(updates: UpdateAccountRequest) {
200
+ const { data } = await api.updateCurrentUser(updates)
201
+ accountInfo.value = data
202
+ emitter.emit(AuthState.PROFILE_UPDATE)
203
+ }
204
+
205
+ async function activateAccount(accountId: string) {
206
+ await api.activateAccount(accountId)
207
+ }
208
+
209
+ async function deactivateAccount(accountId: string) {
210
+ await api.deactivateAccount(accountId)
211
+ }
212
+
213
+ async function deleteAccount(accountId: string) {
214
+ await api.deleteAccount(accountId)
215
+ }
216
+
217
+ async function deleteCurrentUser() {
218
+ await api.deleteCurrentUser()
219
+ accountInfo.value = null
220
+ }
221
+
222
+ async function sendVerification(email?: string) {
223
+ await api.sendVerification({ email })
224
+ }
225
+
226
+ async function verifyEmail(token: string) {
227
+ await api.verifyEmail(token)
228
+ // Refresh user data to get updated verification status
229
+ await checkAuth()
230
+ emitter.emit(AuthState.EMAIL_VERIFIED)
231
+ }
232
+
233
+ async function refreshSession() {
234
+ await api.refreshSession()
235
+ emitter.emit(AuthState.SESSION_REFRESH)
236
+ }
237
+
238
+ async function getSessions(accountId?: string) {
239
+ const id = accountId ?? user.value?.accountId
240
+ if (id === undefined || id === '') {
241
+ throw new Error('No account ID available')
172
242
  }
243
+ return api.getSessions(id)
173
244
  }
174
245
 
175
- async function deleteUser(userId: string) {
176
- try {
177
- await authApi!.deleteUser(userId)
178
- } catch (error) {
179
- throw error
246
+ async function revokeSession(sessionToken: string) {
247
+ await api.revokeSession(sessionToken)
248
+ }
249
+
250
+ async function revokeAllSessions(accountId?: string) {
251
+ const id = accountId ?? user.value?.accountId
252
+ if (id === undefined || id === '') {
253
+ throw new Error('No account ID available')
180
254
  }
255
+ await api.revokeAllSessions(id)
181
256
  }
182
257
 
183
258
  return {
184
- // State
185
- currentUser,
259
+ // Primary State (use this!)
260
+ user,
261
+
262
+ // Full account info (for advanced use cases)
263
+ accountInfo,
186
264
 
187
265
  // Getters
188
266
  getFullName,
189
267
  getIsLoggedIn,
268
+ getEmail,
269
+ getRoles,
270
+ getAccountType,
271
+ isPersonAccount,
272
+ isEntityAccount,
190
273
 
191
- // Actions
192
- logout,
274
+ // Authentication Actions
193
275
  login,
194
- checkAuth,
276
+ logout,
195
277
  signup,
196
- recoverPassword,
197
- resetPassword,
198
- updatePassword,
278
+ checkAuth,
279
+ refreshSession,
280
+
281
+ // Profile Actions
199
282
  updateProfile,
200
- toggleUserStatus,
201
- deleteUser,
283
+ deleteCurrentUser,
284
+
285
+ // Password Actions
286
+ changePassword,
287
+ forgotPassword,
288
+ verifyResetToken,
289
+ resetPassword,
290
+
291
+ // Email Verification Actions
292
+ sendVerification,
293
+ verifyEmail,
294
+
295
+ // Admin Actions
296
+ activateAccount,
297
+ deactivateAccount,
298
+ deleteAccount,
299
+
300
+ // Session Management
301
+ getSessions,
302
+ revokeSession,
303
+ revokeAllSessions,
202
304
  }
203
305
  }