major: Migration from better-auth to keycloak

This commit is contained in:
2025-10-28 10:40:38 +01:00
parent e5e063bbde
commit 36364a7977
77 changed files with 1444 additions and 2930 deletions

View File

@@ -1,9 +1,4 @@
import {
type CreateApplicationFormDto,
type ApplicationFormDto,
type PagedApplicationFormDto,
ResponseError
} from '~/.api-client'
import { type CreateApplicationFormDto, type ApplicationFormDto, type PagedApplicationFormDto } from '~/.api-client'
import { useApplicationFormApi } from './useApplicationFormApi'
export function useApplicationForm() {
@@ -15,11 +10,7 @@ export function useApplicationForm() {
try {
return await applicationFormApi.createApplicationForm(createApplicationFormDto)
} catch (e: unknown) {
if (e instanceof ResponseError) {
console.error('Failed creating application form:', e.response)
} else {
console.error('Failed creating application form:', e)
}
console.error('Failed creating application form:', e)
return Promise.reject(e)
}
}
@@ -28,11 +19,7 @@ export function useApplicationForm() {
try {
return await applicationFormApi.getAllApplicationForms(organizationId)
} catch (e: unknown) {
if (e instanceof ResponseError) {
console.error('Failed retrieving application forms:', e.response)
} else {
console.error('Failed retrieving application forms:', e)
}
console.error('Failed retrieving application forms:', e, JSON.stringify(e))
return Promise.reject(e)
}
}
@@ -41,11 +28,7 @@ export function useApplicationForm() {
try {
return await applicationFormApi.getApplicationFormById(id)
} catch (e: unknown) {
if (e instanceof ResponseError) {
console.error(`Failed retrieving application form with ID ${id}:`, e.response)
} else {
console.error(`Failed retrieving application form with ID ${id}:`, e)
}
console.error(`Failed retrieving application form with ID ${id}:`, e)
return Promise.reject(e)
}
}
@@ -61,11 +44,7 @@ export function useApplicationForm() {
try {
return await applicationFormApi.updateApplicationForm(id, applicationFormDto)
} catch (e: unknown) {
if (e instanceof ResponseError) {
console.error(`Failed updating application form with ID ${id}:`, e.response)
} else {
console.error(`Failed updating application form with ID ${id}:`, e)
}
console.error(`Failed updating application form with ID ${id}:`, e)
return Promise.reject(e)
}
}
@@ -74,11 +53,7 @@ export function useApplicationForm() {
try {
return await applicationFormApi.deleteApplicationFormById(id)
} catch (e: unknown) {
if (e instanceof ResponseError) {
console.error(`Failed deleting application form with ID ${id}:`, e.response)
} else {
console.error(`Failed deleting application form with ID ${id}:`, e)
}
console.error(`Failed deleting application form with ID ${id}:`, e)
return Promise.reject(e)
}
}
@@ -91,11 +66,7 @@ export function useApplicationForm() {
try {
return await applicationFormApi.submitApplicationForm(id)
} catch (e: unknown) {
if (e instanceof ResponseError) {
console.error(`Failed submitting application form with ID ${id}:`, e.response)
} else {
console.error(`Failed submitting application form with ID ${id}:`, e)
}
console.error(`Failed submitting application form with ID ${id}:`, e)
return Promise.reject(e)
}
}

View File

@@ -6,18 +6,22 @@ import {
type PagedApplicationFormDto
} from '~/.api-client'
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
import { wrappedFetchWrap } from '~/utils/wrappedFetch'
export function useApplicationFormApi() {
const appBaseUrl = useRuntimeConfig().app.baseURL
const { serverApiBaseUrl, serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const { jwt } = useAuth()
const { serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const basePath = withoutTrailingSlash(
cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
cleanDoubleSlashes(
import.meta.client
? appBaseUrl + clientProxyBasePath
: useRequestURL().origin + clientProxyBasePath + serverApiBasePath
)
)
const applicationFormApiClient = new ApplicationFormApi(
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
new Configuration({ basePath, fetchApi: wrappedFetchWrap(useRequestFetch()) })
)
async function createApplicationForm(

View File

@@ -8,8 +8,8 @@ import { useApplicationFormTemplateApi } from './useApplicationFormTemplateApi'
const currentApplicationForm: Ref<ApplicationFormDto | undefined> = ref()
export function useApplicationFormTemplate() {
const applicationFormApi = useApplicationFormTemplateApi()
export async function useApplicationFormTemplate() {
const applicationFormApi = await useApplicationFormTemplateApi()
async function createApplicationFormTemplate(
createApplicationFormDto: CreateApplicationFormDto

View File

@@ -1,18 +1,22 @@
import { ApplicationFormTemplateApi, Configuration } from '../../.api-client'
import type { CreateApplicationFormDto, ApplicationFormDto, PagedApplicationFormDto } from '~/.api-client'
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
import { wrappedFetchWrap } from '~/utils/wrappedFetch'
export function useApplicationFormTemplateApi() {
export async function useApplicationFormTemplateApi() {
const appBaseUrl = useRuntimeConfig().app.baseURL
const { serverApiBaseUrl, serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const { jwt } = useAuth()
const { serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const basePath = withoutTrailingSlash(
cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
cleanDoubleSlashes(
import.meta.client
? appBaseUrl + clientProxyBasePath
: useRequestURL().origin + clientProxyBasePath + serverApiBasePath
)
)
const applicationFormApiClient = new ApplicationFormTemplateApi(
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
new Configuration({ basePath, fetchApi: wrappedFetchWrap(useRequestFetch()) })
)
async function createApplicationFormTemplate(

View File

@@ -1,107 +0,0 @@
import type { FormSubmitEvent } from '@nuxt/ui'
import type { SignInSchema, SignUpSchema } from '~/types/schemas'
import type { RouteLocationRaw } from 'vue-router'
import { useAuthClient } from '~/composables/auth/useAuthClient'
import { useAuthState } from '~/composables/auth/useAuthState'
export function useAuthActions() {
const { client } = useAuthClient()
const { session, user } = useAuthState()
const { createUser } = useUser()
const toast = useToast()
async function signOut({ redirectTo }: { redirectTo?: RouteLocationRaw } = {}) {
const res = await client.signOut()
if (res.error) {
console.error('Error signing out:', res.error)
return res
}
session.value = null
user.value = null
if (redirectTo) {
await navigateTo(redirectTo, { external: true })
}
return res
}
async function signUp(payload: FormSubmitEvent<SignUpSchema>) {
await client.signUp.email(
{
email: payload.data.email,
password: payload.data.password,
name: payload.data.name
},
{
onRequest: () => {
console.log('Sending register request')
},
onResponse: () => {
console.log('Receiving register response')
},
onSuccess: async (ctx) => {
console.log('Successfully registered!')
// Create user in backend after successful Better Auth registration
try {
console.log('Creating user in backend...', ctx.data)
await createUser({
id: ctx.data.user.id,
name: ctx.data.user.name,
status: 'ACTIVE'
})
console.log('User created in backend successfully')
} catch (error) {
console.error('Failed to create user in backend:', error)
toast.add({
title: 'Warning',
description: 'Account created but there was an issue with backend setup. Please contact support.',
color: 'warning'
})
}
await navigateTo('/')
},
onError: async (ctx) => {
console.log(ctx.error.message)
toast.add({
title: 'Fehler bei der Registrierung',
description: ctx.error.message,
color: 'error'
})
}
}
)
}
async function signIn(payload: FormSubmitEvent<SignInSchema>) {
await client.signIn.email(
{
email: payload.data.email,
password: payload.data.password
},
{
onRequest: () => {
console.log('Sending login request')
},
onSuccess: async () => {
console.log('Successfully logged in!')
await navigateTo('/')
},
onError: (ctx) => {
console.log(ctx.error.message)
toast.add({
title: 'Fehler bei der Anmeldung',
description: ctx.error.message,
color: 'error'
})
}
}
)
}
return {
signOut,
signUp,
signIn
}
}

View File

@@ -1,46 +0,0 @@
import { createAuthClient } from 'better-auth/vue'
import { jwtClient, organizationClient } from 'better-auth/client/plugins'
import {
accessControl,
adminRole,
employeeRole,
employerRole,
ownerRole,
ROLES,
worksCouncilMemberRole
} from '~/server/utils/permissions'
export function useAuthClient() {
const url = useRequestURL()
const headers = import.meta.server ? useRequestHeaders() : undefined
const client = createAuthClient({
baseURL: url.origin,
fetchOptions: {
headers
},
user: {
deleteUser: {
enabled: true
}
},
plugins: [
organizationClient({
// Pass the same access control instance and roles to client
ac: accessControl,
roles: {
[ROLES.EMPLOYER]: employerRole,
[ROLES.WORKS_COUNCIL_MEMBER]: worksCouncilMemberRole,
[ROLES.EMPLOYEE]: employeeRole,
[ROLES.ADMIN]: adminRole,
[ROLES.OWNER]: ownerRole
}
}),
jwtClient()
]
})
return {
client
}
}

View File

@@ -1,130 +0,0 @@
import type { ClientOptions, InferSessionFromClient, InferUserFromClient } from 'better-auth/client'
import type { RuntimeAuthConfig } from '~/types/auth'
import { defu } from 'defu'
import { useAuthClient } from '~/composables/auth/useAuthClient'
// Global state for auth
const session = ref<InferSessionFromClient<ClientOptions> | null>(null)
const user = ref<InferUserFromClient<ClientOptions> | null>(null)
const sessionFetching = import.meta.server ? ref(false) : ref(false)
const jwt = ref<string | null>(null)
const organizations = ref<
{
id: string
name: string
createdAt: Date
slug: string
metadata?: Record<string, unknown>
logo?: string | null
}[]
>([])
const selectedOrganization = ref<{
id: string
name: string
createdAt: Date
slug: string
metadata?: Record<string, unknown>
logo?: string | null
} | null>(null)
const activeMember = ref<{ role: string } | null>(null)
export function useAuthState() {
const { client } = useAuthClient()
const route = useRoute()
const options = defu(useRuntimeConfig().public.auth as Partial<RuntimeAuthConfig>, {
redirectUserTo: '/',
redirectGuestTo: '/login'
})
const headers = import.meta.server ? useRequestHeaders() : undefined
async function fetchSession(targetPath?: string) {
if (sessionFetching.value) {
console.log('already fetching session')
return
}
sessionFetching.value = true
const { data } = await client.getSession({
fetchOptions: {
headers
}
})
session.value = data?.session || null
user.value = data?.user || null
sessionFetching.value = false
// Only fetch JWT and organizations if we have a session and not on public routes
if (session.value && !isPublicPath(targetPath)) {
await fetchJwtAndOrganizations()
}
return data
}
async function fetchJwtAndOrganizations() {
// Fetch JWT
const tokenResult = await client.token()
jwt.value = tokenResult.data?.token ?? null
// Fetch organization
const orgResult = await client.organization.list({
fetchOptions: {
headers
}
})
organizations.value = orgResult.data ?? []
if (!selectedOrganization.value && organizations.value.length > 0) {
selectedOrganization.value = organizations.value[0]
}
// Fetch active member
const activeMemberResult = await client.organization.getActiveMember({
fetchOptions: {
headers
}
})
activeMember.value = activeMemberResult.data || null
}
function isPublicPath(path?: string) {
const finalPath = path ?? route.path
const publicRoutes = ['/login', '/signup', '/accept-invitation']
return publicRoutes.some((path) => finalPath.startsWith(path))
}
// Watch organization changes
watch(
() => selectedOrganization.value,
async (newValue) => {
if (newValue) {
await client.organization.setActive({
organizationId: newValue?.id
})
}
}
)
// Client-side session listening
if (import.meta.client) {
client.$store.listen('$sessionSignal', async (signal) => {
if (!signal) return
await fetchSession()
})
}
return {
session,
user,
sessionFetching,
jwt,
organizations,
selectedOrganization,
activeMember,
options,
fetchSession,
isPublicPath,
loggedIn: computed(() => !!session.value)
}
}

View File

@@ -1,17 +1,21 @@
import { CommentApi, Configuration, type CommentDto, type CreateCommentDto, type PagedCommentDto } from '~/.api-client'
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
import { wrappedFetchWrap } from '~/utils/wrappedFetch'
export function useCommentApi() {
const appBaseUrl = useRuntimeConfig().app.baseURL
const { serverApiBaseUrl, serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const { jwt } = useAuth()
const { serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const basePath = withoutTrailingSlash(
cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
cleanDoubleSlashes(
import.meta.client
? appBaseUrl + clientProxyBasePath
: useRequestURL().origin + clientProxyBasePath + serverApiBasePath
)
)
const commentApiClient = new CommentApi(
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
new Configuration({ basePath, fetchApi: wrappedFetchWrap(useRequestFetch()) })
)
async function createComment(

View File

@@ -3,7 +3,8 @@ import type { CreateCommentDto, CommentDto } from '~/.api-client'
export function useCommentTextarea(applicationFormId: string) {
const commentStore = useCommentStore()
const { createComment, updateComment } = commentStore
const { user } = useAuth()
const userStore = useUserStore()
const { user } = storeToRefs(userStore)
const isEditingComment = ref(false)
const currentEditedComment = ref<CommentDto | null>(null)
const commentTextAreaValue = ref('')
@@ -51,7 +52,7 @@ export function useCommentTextarea(applicationFormId: string) {
}
function isCommentByUser(comment: CommentDto) {
return comment.createdBy.id === user.value?.id
return comment.createdBy.keycloakId === user.value?.keycloakId
}
return {

View File

@@ -2,5 +2,3 @@ export { useApplicationFormTemplate } from './applicationFormTemplate/useApplica
export { useApplicationForm } from './applicationForm/useApplicationForm'
export { useNotification } from './notification/useNotification'
export { useNotificationApi } from './notification/useNotificationApi'
export { useUser } from './user/useUser'
export { useUserApi } from './user/useUserApi'

View File

@@ -1,38 +0,0 @@
import type {
VerifySignatureHashAlgorithmEnum,
VerifySignatureResponseDto,
SignPdfHashHashAlgorithmEnum
} from '~/.api-client-middleware'
import { useMiddlewareApi } from '~/composables/middleware/useMiddlewareApi'
export function useMiddleware() {
const middlewareApi = useMiddlewareApi()
async function signPdfHash(document: Blob, certificateId: string, hashAlgorithm?: SignPdfHashHashAlgorithmEnum) {
try {
return middlewareApi.signPdfHash(document, certificateId, hashAlgorithm)
} catch (e: unknown) {
console.error('Failed signing PDF hash:', e)
return Promise.reject(e)
}
}
async function verifySignature(
document: Blob,
signature: string,
certificateId?: string,
hashAlgorithm?: VerifySignatureHashAlgorithmEnum
): Promise<VerifySignatureResponseDto> {
try {
return await middlewareApi.verifySignature(document, signature, certificateId, hashAlgorithm)
} catch (e: unknown) {
console.error('Failed verifying signature:', e)
return Promise.reject(e)
}
}
return {
signPdfHash,
verifySignature
}
}

View File

@@ -1,45 +0,0 @@
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
import {
SmartCardApi,
SignatureApi,
Configuration,
type VerifySignatureHashAlgorithmEnum,
type VerifySignatureResponseDto,
type SignPdfHashHashAlgorithmEnum
} from '~/.api-client-middleware'
export function useMiddlewareApi() {
const appBaseUrl = useRuntimeConfig().app.baseURL
const { serverApiBaseUrl, serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const { jwt } = useAuth()
const basePath = withoutTrailingSlash(
cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
)
const smartCardApiClient = new SmartCardApi(
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
)
const signatureApiClient = new SignatureApi(
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
)
async function signPdfHash(document: Blob, certificateId: string, hashAlgorithm?: SignPdfHashHashAlgorithmEnum) {
return signatureApiClient.signPdfHash({ document, certificateId, hashAlgorithm })
}
async function verifySignature(
document: Blob,
signature: string,
certificateId?: string,
hashAlgorithm?: VerifySignatureHashAlgorithmEnum
): Promise<VerifySignatureResponseDto> {
return signatureApiClient.verifySignature({ document, signature, certificateId, hashAlgorithm })
}
return {
signPdfHash,
verifySignature
}
}

View File

@@ -6,18 +6,22 @@ import {
type CreateNotificationDto
} from '~/.api-client'
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
import { wrappedFetchWrap } from '~/utils/wrappedFetch'
export function useNotificationApi() {
const appBaseUrl = useRuntimeConfig().app.baseURL
const { serverApiBaseUrl, serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const { jwt } = useAuth()
const { serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const basePath = withoutTrailingSlash(
cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
cleanDoubleSlashes(
import.meta.client
? appBaseUrl + clientProxyBasePath
: useRequestURL().origin + clientProxyBasePath + serverApiBasePath
)
)
const notificationApiClient = new NotificationApi(
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
new Configuration({ basePath, fetchApi: wrappedFetchWrap(useRequestFetch()) })
)
async function createNotification(createNotificationDto: CreateNotificationDto): Promise<NotificationDto> {

View File

@@ -1,74 +0,0 @@
import type { LegalRole } from '~/server/utils/permissions'
import type { ListMembersOptions } from '~/types/auth'
export function useOrganizationApi() {
const { organization } = useAuth()
async function createOrganization(name: string, slug: string, logo?: string) {
return organization.create({ name, slug, logo })
}
async function deleteOrganization(organizationId: string) {
return organization.delete({ organizationId })
}
async function getInvitation(invitationId: string) {
return organization.getInvitation({ query: { id: invitationId } })
}
async function listInvitations(organizationId?: string) {
return organization.listInvitations(organizationId ? { query: { organizationId: organizationId } } : undefined)
}
async function inviteMember(email: string, role: LegalRole) {
return organization.inviteMember({ email, role })
}
async function removeMember(memberIdOrEmail: string) {
return organization.removeMember({ memberIdOrEmail })
}
async function acceptInvitation(invitationId: string) {
return organization.acceptInvitation({ invitationId })
}
async function cancelSentInvitation(invitationId: string) {
return organization.cancelInvitation({ invitationId })
}
async function rejectInvitation(invitationId: string) {
return organization.rejectInvitation({ invitationId })
}
async function loadOrganizations() {
return organization.list()
}
async function checkSlugAvailability(slug: string) {
return organization.checkSlug({ slug })
}
async function setActiveOrganization(organizationId: string) {
return organization.setActive({ organizationId })
}
async function listMembers(options?: ListMembersOptions) {
return organization.listMembers(options)
}
return {
createOrganization,
deleteOrganization,
getInvitation,
listInvitations,
inviteMember,
removeMember,
acceptInvitation,
cancelSentInvitation,
rejectInvitation,
loadOrganizations,
checkSlugAvailability,
setActiveOrganization,
listMembers
}
}

View File

@@ -1,19 +0,0 @@
// Copied from https://github.com/atinux/nuxthub-better-auth
import { useAuthState } from '~/composables/auth/useAuthState'
import { useAuthActions } from '~/composables/auth/useAuthActions'
import { useAuthClient } from '~/composables/auth/useAuthClient'
export function useAuth() {
const authState = useAuthState()
const authActions = useAuthActions()
const { client } = useAuthClient()
return {
...authState,
...authActions,
client,
deleteUser: client.deleteUser,
organization: client.organization
}
}

View File

@@ -1,110 +0,0 @@
import { ROLES, type LegalRole } from '~/server/utils/permissions'
export function usePermissions() {
const { organization, activeMember } = useAuth()
const currentRole = computed((): LegalRole | null => {
return (activeMember.value?.role as LegalRole) || null
})
const hasPermission = (permissions: Record<string, string[]>): boolean => {
if (!currentRole.value) return false
return organization.checkRolePermission({
permissions,
role: currentRole.value
})
}
// Specific permission helpers
const canCreateApplicationForm = computed(() =>
hasPermission({ application_form: ["create"] })
)
const canApproveApplicationForm = computed(() =>
hasPermission({ application_form: ["approve"] })
)
const canSignAgreement = computed(() =>
hasPermission({ agreement: ["sign"] })
)
const canInviteMembers = computed(() =>
hasPermission({ invitation: ["create"] })
)
const canManageOrganization = computed(() =>
hasPermission({ organization: ["update"] })
)
// Role checks
const isEmployer = computed(() => currentRole.value === ROLES.EMPLOYER)
const isEmployee = computed(() => currentRole.value === ROLES.EMPLOYEE)
const isWorksCouncilMember = computed(() => currentRole.value === ROLES.WORKS_COUNCIL_MEMBER)
const isAdmin = computed(() => currentRole.value === ROLES.ADMIN)
const isOwner = computed(() => currentRole.value === ROLES.OWNER)
const getCurrentRoleInfo = () => {
const roleInfo = {
[ROLES.EMPLOYER]: {
name: 'Arbeitgeber',
description: 'Kann Anträge genehmigen und Vereinbarungen unterzeichnen',
color: 'blue',
icon: 'i-lucide-briefcase'
},
[ROLES.EMPLOYEE]: {
name: 'Arbeitnehmer',
description: 'Kann eigene Anträge einsehen und kommentieren',
color: 'green',
icon: 'i-lucide-user'
},
[ROLES.WORKS_COUNCIL_MEMBER]: {
name: 'Betriebsrat',
description: 'Kann Anträge prüfen und Vereinbarungen unterzeichnen',
color: 'purple',
icon: 'i-lucide-users'
},
[ROLES.ADMIN]: {
name: 'Administrator',
description: 'Vollzugriff auf Organisationsverwaltung',
color: 'red',
icon: 'i-lucide-settings'
},
[ROLES.OWNER]: {
name: 'Eigentümer',
description: 'Vollzugriff und Organisationsbesitz',
color: 'yellow',
icon: 'i-lucide-crown'
}
}
return currentRole.value && currentRole.value in roleInfo ? roleInfo[currentRole.value as LegalRole] : null
}
return {
// State
currentRole,
activeMember,
// Permission checks
hasPermission,
// Role checks
isEmployer,
isEmployee,
isWorksCouncilMember,
isAdmin,
isOwner,
// Computed permissions
canCreateApplicationForm,
canApproveApplicationForm,
canSignAgreement,
canInviteMembers,
canManageOrganization,
// Utilities
getCurrentRoleInfo,
ROLES
}
}

View File

@@ -1,72 +0,0 @@
import {
type CreateUserDto,
type UserDto,
ResponseError
} from '~/.api-client'
import { useUserApi } from './useUserApi'
export function useUser() {
const userApi = useUserApi()
async function createUser(createUserDto: CreateUserDto): Promise<UserDto> {
try {
return await userApi.createUser(createUserDto)
} catch (e: unknown) {
if (e instanceof ResponseError) {
console.error('Failed creating user:', e.response)
} else {
console.error('Failed creating user:', e)
}
return Promise.reject(e)
}
}
async function getUserById(id: string): Promise<UserDto | null> {
try {
return await userApi.getUserById(id)
} catch (e: unknown) {
if (e instanceof ResponseError && e.response.status === 404) {
return null
}
if (e instanceof ResponseError) {
console.error(`Failed retrieving user with ID ${id}:`, e.response)
} else {
console.error(`Failed retrieving user with ID ${id}:`, e)
}
return Promise.reject(e)
}
}
async function updateUser(id: string, userDto?: UserDto): Promise<UserDto> {
try {
return await userApi.updateUser(id, userDto)
} catch (e: unknown) {
if (e instanceof ResponseError) {
console.error(`Failed updating user with ID ${id}:`, e.response)
} else {
console.error(`Failed updating user with ID ${id}:`, e)
}
return Promise.reject(e)
}
}
async function deleteUser(id: string): Promise<void> {
try {
return await userApi.deleteUser(id)
} catch (e: unknown) {
if (e instanceof ResponseError) {
console.error(`Failed deleting user with ID ${id}:`, e.response)
} else {
console.error(`Failed deleting user with ID ${id}:`, e)
}
return Promise.reject(e)
}
}
return {
createUser,
getUserById,
updateUser,
deleteUser
}
}

View File

@@ -1,42 +0,0 @@
import { UserApi, Configuration, type CreateUserDto, type UserDto } from '~/.api-client'
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
import { useAuthState } from '~/composables/auth/useAuthState'
export function useUserApi() {
const appBaseUrl = useRuntimeConfig().app.baseURL
const { serverApiBaseUrl, serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const { jwt } = useAuthState()
const basePath = withoutTrailingSlash(
cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
)
// Track changing JWT of user who accepts the invitation
const userApiClient = computed(
() =>
new UserApi(new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } }))
)
async function createUser(createUserDto: CreateUserDto): Promise<UserDto> {
return userApiClient.value.createUser({ createUserDto })
}
async function getUserById(id: string): Promise<UserDto> {
return userApiClient.value.getUserById({ id })
}
async function updateUser(id: string, userDto?: UserDto): Promise<UserDto> {
return userApiClient.value.updateUser({ id, userDto })
}
async function deleteUser(id: string): Promise<void> {
return userApiClient.value.deleteUser({ id })
}
return {
createUser,
getUserById,
updateUser,
deleteUser
}
}