base commit

This commit is contained in:
@kirill.komarov
2026-04-28 21:36:30 +05:00
parent 55480d4aa5
commit 2148fd7a12
24 changed files with 1578 additions and 121 deletions
+12
View File
@@ -5,3 +5,15 @@ export const apiClient = axios.create({
baseURL: apiBaseURL,
headers: { 'Content-Type': 'application/json' },
})
apiClient.interceptors.request.use((config) => {
try {
const token = localStorage.getItem('craftshop_auth_token')
if (!token) return config
config.headers = config.headers ?? {}
config.headers.Authorization = `Bearer ${token}`
return config
} catch {
return config
}
})
+98
View File
@@ -0,0 +1,98 @@
import { createEffect, createEvent, createStore, sample } from 'effector'
import { apiClient } from '@/shared/api/client'
export type AuthUser = { id: string; email: string; name?: string | null }
const TOKEN_KEY = 'craftshop_auth_token'
export const tokenSet = createEvent<string | null>()
export const logout = createEvent()
export const $token = createStore<string | null>(null)
.on(tokenSet, (_, t) => t)
.reset(logout)
export const $user = createStore<AuthUser | null>(null).reset(logout)
export const changePasswordFx = createEffect(async (params: { currentPassword?: string; newPassword: string }) => {
const { data } = await apiClient.patch<{ user: AuthUser }>('me/password', params)
return data.user
})
export const requestEmailChangeCodeFx = createEffect(async (newEmail: string) => {
await apiClient.post('me/change-email/request-code', { newEmail })
})
export const verifyEmailChangeFx = createEffect(async (params: { newEmail: string; code: string }) => {
const { data } = await apiClient.post<{ user: AuthUser }>('me/change-email/verify', params)
return data.user
})
export const updateProfileFx = createEffect(async (params: { name: string | null }) => {
const { data } = await apiClient.patch<{ user: AuthUser }>('me/profile', params)
return data.user
})
export const $changePasswordError = createStore<unknown | null>(null)
.on(changePasswordFx.failData, (_, e) => e)
.reset(changePasswordFx, logout)
export const $requestEmailChangeCodeError = createStore<unknown | null>(null)
.on(requestEmailChangeCodeFx.failData, (_, e) => e)
.reset(requestEmailChangeCodeFx, logout)
export const $verifyEmailChangeError = createStore<unknown | null>(null)
.on(verifyEmailChangeFx.failData, (_, e) => e)
.reset(verifyEmailChangeFx, logout)
export const $updateProfileError = createStore<unknown | null>(null)
.on(updateProfileFx.failData, (_, e) => e)
.reset(updateProfileFx, logout)
export const meFx = createEffect(async (token: string) => {
const { data } = await apiClient.get<{ user: AuthUser | null }>('me', {
headers: { Authorization: `Bearer ${token}` },
})
return data.user
})
sample({
clock: tokenSet,
filter: (t): t is string => Boolean(t),
target: meFx,
})
sample({
clock: meFx.doneData,
target: $user,
})
sample({
clock: [changePasswordFx.doneData, verifyEmailChangeFx.doneData, updateProfileFx.doneData],
target: $user,
})
$token.watch((t) => {
try {
if (!t) localStorage.removeItem(TOKEN_KEY)
else localStorage.setItem(TOKEN_KEY, t)
} catch {
// ignore
}
})
logout.watch(() => {
try {
localStorage.removeItem(TOKEN_KEY)
} catch {
// ignore
}
})
export function readStoredToken(): string | null {
try {
return localStorage.getItem(TOKEN_KEY)
} catch {
return null
}
}