major: Migration from better-auth to keycloak
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user