132 lines
3.1 KiB
TypeScript
132 lines
3.1 KiB
TypeScript
import { computed, ref } from 'vue'
|
|
import { defineStore } from 'pinia'
|
|
import {
|
|
AUTH_EXPIRED_EVENT,
|
|
AUTH_UPDATED_EVENT,
|
|
apiRequest,
|
|
clearAccessToken,
|
|
refreshSession,
|
|
setAccessToken,
|
|
} from '@/lib/api'
|
|
import type { AuthResponse, AuthUser } from '@/types/api'
|
|
import type { Profile } from '@/types/database'
|
|
|
|
export const useAuthStore = defineStore('auth', () => {
|
|
const user = ref<AuthUser | null>(null)
|
|
const profile = ref<Profile | null>(null)
|
|
const loading = ref(true)
|
|
let listenersAttached = false
|
|
|
|
const isLoggedIn = computed(() => !!user.value)
|
|
const isAdmin = computed(() => profile.value?.role === 'admin')
|
|
|
|
function applyAuth(auth: AuthResponse) {
|
|
setAccessToken(auth.access_token)
|
|
user.value = auth.user
|
|
profile.value = {
|
|
id: auth.user.id,
|
|
username: auth.user.username,
|
|
avatar_url: auth.user.avatar_url,
|
|
role: auth.user.role,
|
|
is_disabled: auth.user.is_disabled,
|
|
created_at: auth.user.created_at,
|
|
}
|
|
}
|
|
|
|
function clearAuth() {
|
|
clearAccessToken()
|
|
user.value = null
|
|
profile.value = null
|
|
}
|
|
|
|
async function login(email: string, password: string) {
|
|
const auth = await apiRequest<AuthResponse>('/auth/login', {
|
|
method: 'POST',
|
|
auth: false,
|
|
body: { email, password },
|
|
})
|
|
applyAuth(auth)
|
|
}
|
|
|
|
async function register(email: string, password: string, username: string) {
|
|
await apiRequest('/auth/register', {
|
|
method: 'POST',
|
|
auth: false,
|
|
body: { email, password, username },
|
|
})
|
|
}
|
|
|
|
async function logout() {
|
|
try {
|
|
await apiRequest('/auth/logout', { method: 'POST', retry: false })
|
|
} finally {
|
|
clearAuth()
|
|
}
|
|
}
|
|
|
|
async function updateUsername(username: string) {
|
|
const updatedUser = await apiRequest<AuthUser>('/profiles/me', {
|
|
method: 'PATCH',
|
|
body: { username },
|
|
})
|
|
user.value = updatedUser
|
|
profile.value = {
|
|
id: updatedUser.id,
|
|
username: updatedUser.username,
|
|
avatar_url: updatedUser.avatar_url,
|
|
role: updatedUser.role,
|
|
is_disabled: updatedUser.is_disabled,
|
|
created_at: updatedUser.created_at,
|
|
}
|
|
}
|
|
|
|
async function updatePassword(password: string) {
|
|
const auth = await apiRequest<AuthResponse>('/auth/password', {
|
|
method: 'PATCH',
|
|
body: { password },
|
|
})
|
|
applyAuth(auth)
|
|
}
|
|
|
|
async function sendPasswordReset(email: string) {
|
|
await apiRequest('/auth/forgot-password', {
|
|
method: 'POST',
|
|
auth: false,
|
|
body: { email },
|
|
})
|
|
}
|
|
|
|
async function initialize() {
|
|
if (!listenersAttached) {
|
|
window.addEventListener(AUTH_UPDATED_EVENT, event => {
|
|
applyAuth((event as CustomEvent<AuthResponse>).detail)
|
|
})
|
|
window.addEventListener(AUTH_EXPIRED_EVENT, clearAuth)
|
|
listenersAttached = true
|
|
}
|
|
|
|
try {
|
|
applyAuth(await refreshSession())
|
|
} catch {
|
|
clearAuth()
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
return {
|
|
user,
|
|
profile,
|
|
loading,
|
|
isLoggedIn,
|
|
isAdmin,
|
|
login,
|
|
register,
|
|
logout,
|
|
updateUsername,
|
|
updatePassword,
|
|
sendPasswordReset,
|
|
initialize,
|
|
}
|
|
})
|