source: chapterx-frontend/src/store/authStore.ts@ 0b502c2

main
Last change on this file since 0b502c2 was 0b502c2, checked in by kikisrbinoska <srbinoskakristina07@…>, 12 days ago

Fixed user profile and reading lists

  • Property mode set to 100644
File size: 6.5 KB
Line 
1import { create } from 'zustand'
2import { persist } from 'zustand/middleware'
3import { User, UserRole } from '../types'
4import { mockUsers } from '../data/mockData'
5import axios from 'axios'
6
7const API_BASE = 'https://localhost:7125/api'
8
9interface AuthStore {
10 currentUser: User | null
11 token: string | null
12 showMatureContent: boolean
13 allUsers: User[]
14 login: (emailOrUsername: string, password: string) => Promise<void>
15 logout: () => void
16 register: (data: { username: string; email: string; name: string; surname: string; password: string }, role: 'regular' | 'writer') => Promise<void>
17 switchUser: (userId: number) => void
18 setShowMatureContent: (show: boolean) => void
19 updateUserRole: (userId: number, role: UserRole) => void
20 updateUser: (userId: number, data: { username: string; email: string; name: string; surname: string }) => Promise<void>
21 addUser: (user: User) => void
22 fetchAllUsers: () => Promise<void>
23}
24
25export const useAuthStore = create<AuthStore>()(
26 persist(
27 (set, get) => ({
28 currentUser: null,
29 token: null,
30 showMatureContent: true,
31 allUsers: [],
32
33 login: async (emailOrUsername, password) => {
34 // Try backend first
35 try {
36 const email = emailOrUsername.includes('@')
37 ? emailOrUsername
38 : get().allUsers.find(u => u.username === emailOrUsername)?.email || emailOrUsername
39 const res = await axios.post(`${API_BASE}/auth/login`, { email, password }, { timeout: 3000 })
40 const { token, userId, username, name, surname, role } = res.data
41 const user: User = {
42 user_id: userId,
43 username,
44 email,
45 name: name ?? username,
46 surname: surname ?? '',
47 role: role ?? 'regular',
48 created_at: new Date().toISOString(),
49 follower_count: 0,
50 following_count: 0,
51 }
52 set({ currentUser: user, token })
53 return
54 } catch (err: any) {
55 // Only fall through to mock if the backend is unreachable (network/timeout)
56 // If the backend responded with an error (4xx/5xx), surface it to the user
57 if (err?.response) {
58 const message = err.response.data?.message || err.response.data || 'Invalid email or password.'
59 throw new Error(typeof message === 'string' ? message : 'Invalid email or password.')
60 }
61 // Network error / timeout — fall through to mock login
62 }
63
64 // Fallback to mock
65 const user = get().allUsers.find(
66 u => u.username === emailOrUsername || u.email === emailOrUsername
67 )
68 if (!user) throw new Error('User not found. Try using a quick-login option.')
69 set({ currentUser: user, token: null })
70 },
71
72 logout: () => set({ currentUser: null, token: null }),
73
74 register: async (data, role) => {
75 try {
76 await axios.post(`${API_BASE}/auth/register`, data, { timeout: 3000 })
77 // Register doesn't return a token — auto-login to get one
78 const loginRes = await axios.post(`${API_BASE}/auth/login`, { email: data.email, password: data.password }, { timeout: 3000 })
79 const { token, userId, username } = loginRes.data
80 const newUser: User = {
81 user_id: userId,
82 username,
83 email: data.email,
84 name: data.name,
85 surname: data.surname,
86 role,
87 created_at: new Date().toISOString(),
88 follower_count: 0,
89 following_count: 0,
90 }
91 set(state => ({ allUsers: [...state.allUsers, newUser], currentUser: newUser, token }))
92 return
93 } catch (err: any) {
94 if (err?.response) {
95 const message = err.response.data?.message || err.response.data || 'Registration failed.'
96 throw new Error(typeof message === 'string' ? message : 'Registration failed.')
97 }
98 // Network error / timeout — fall through to mock register
99 console.warn('Backend unreachable during register, using mock:', err?.message)
100 }
101
102 const newUser: User = {
103 user_id: Date.now(),
104 ...data,
105 role,
106 created_at: new Date().toISOString(),
107 follower_count: 0,
108 following_count: 0,
109 }
110 set(state => ({
111 allUsers: [...state.allUsers, newUser],
112 currentUser: newUser,
113 token: null,
114 }))
115 },
116
117 switchUser: (userId: number) => {
118 if (userId === 0) {
119 set({ currentUser: null, token: null })
120 return
121 }
122 const user = get().allUsers.find(u => u.user_id === userId)
123 if (user) set({ currentUser: user, token: null })
124 },
125
126 setShowMatureContent: (show: boolean) => set({ showMatureContent: show }),
127
128 updateUserRole: (userId: number, role: UserRole) =>
129 set(state => ({
130 allUsers: state.allUsers.map(u => (u.user_id === userId ? { ...u, role } : u)),
131 currentUser:
132 state.currentUser?.user_id === userId
133 ? { ...state.currentUser, role }
134 : state.currentUser,
135 })),
136
137 updateUser: async (userId, data) => {
138 const { token } = get()
139 await axios.put(`${API_BASE}/users/${userId}`, { id: userId, ...data }, {
140 headers: token ? { Authorization: `Bearer ${token}` } : {},
141 })
142 set(state => ({
143 allUsers: state.allUsers.map(u => u.user_id === userId ? { ...u, ...data } : u),
144 currentUser: state.currentUser?.user_id === userId ? { ...state.currentUser, ...data } : state.currentUser,
145 }))
146 },
147
148 addUser: (user: User) =>
149 set(state => ({ allUsers: [...state.allUsers, user] })),
150
151 fetchAllUsers: async () => {
152 try {
153 const res = await axios.get(`${API_BASE}/users`)
154 const data: any[] = res.data?.users ?? res.data ?? []
155 const users: User[] = data.map((u: any) => ({
156 user_id: u.id,
157 username: u.username,
158 email: u.email ?? '',
159 name: u.name ?? u.username,
160 surname: u.surname ?? '',
161 role: (u.role ?? 'regular') as UserRole,
162 created_at: u.createdAt ?? new Date().toISOString(),
163 follower_count: 0,
164 following_count: 0,
165 }))
166 set({ allUsers: users })
167 } catch {
168 // keep existing
169 }
170 },
171 }),
172 {
173 name: 'chapterx-auth',
174 partialize: s => ({
175 currentUser: s.currentUser,
176 token: s.token,
177 showMatureContent: s.showMatureContent,
178 }),
179 }
180 )
181)
Note: See TracBrowser for help on using the repository browser.