feat(fullstack): Set user roles per orga, scope notification to orga and role, add orga and role to JWT
This commit is contained in:
@@ -93,7 +93,7 @@ export function useAuth() {
|
||||
redirectGuestTo: '/login'
|
||||
})
|
||||
|
||||
async function fetchSession() {
|
||||
async function fetchSession(targetPath?: string) {
|
||||
if (sessionFetching.value) {
|
||||
console.log('already fetching session')
|
||||
return
|
||||
@@ -109,7 +109,7 @@ export function useAuth() {
|
||||
sessionFetching.value = false
|
||||
|
||||
// Only fetch JWT and organizations if we have a session and not on public routes
|
||||
if (session.value && !isPublicRoute()) {
|
||||
if (session.value && !isPublicPath(targetPath)) {
|
||||
await fetchJwtAndOrganizations()
|
||||
}
|
||||
|
||||
@@ -160,10 +160,10 @@ export function useAuth() {
|
||||
})
|
||||
}
|
||||
|
||||
function isPublicRoute(routeToCheck?: RouteLocationNormalizedLoaded) {
|
||||
const finalRoute = routeToCheck ?? route
|
||||
function isPublicPath(path?: string) {
|
||||
const finalPath = path ?? route.path
|
||||
const publicRoutes = ['/login', '/signup', '/accept-invitation']
|
||||
return publicRoutes.some((path) => finalRoute.path.startsWith(path))
|
||||
return publicRoutes.some((path) => finalPath.startsWith(path))
|
||||
}
|
||||
|
||||
async function signOut({ redirectTo }: { redirectTo?: RouteLocationRaw } = {}) {
|
||||
@@ -195,7 +195,7 @@ export function useAuth() {
|
||||
fetchSession,
|
||||
client,
|
||||
jwt,
|
||||
isPublicRoute,
|
||||
isPublicPath,
|
||||
activeMember
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,19 @@ export function useUser() {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -53,6 +66,7 @@ export function useUser() {
|
||||
return {
|
||||
createUser,
|
||||
getUserById,
|
||||
updateUser,
|
||||
deleteUser
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
UserApi,
|
||||
Configuration,
|
||||
type CreateUserDto,
|
||||
type UserDto
|
||||
} from '~/.api-client'
|
||||
import { UserApi, Configuration, type CreateUserDto, type UserDto } from '~/.api-client'
|
||||
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
|
||||
|
||||
export function useUserApi() {
|
||||
@@ -15,25 +10,32 @@ export function useUserApi() {
|
||||
cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
|
||||
)
|
||||
|
||||
const userApiClient = new UserApi(
|
||||
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
|
||||
// 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.createUser({ createUserDto })
|
||||
return userApiClient.value.createUser({ createUserDto })
|
||||
}
|
||||
|
||||
async function getUserById(id: string): Promise<UserDto> {
|
||||
return userApiClient.getUserById({ id })
|
||||
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.deleteUser({ id })
|
||||
return userApiClient.value.deleteUser({ id })
|
||||
}
|
||||
|
||||
return {
|
||||
createUser,
|
||||
getUserById,
|
||||
updateUser,
|
||||
deleteUser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default defineNuxtRouteMiddleware(async (to: RouteLocationNormalized) =>
|
||||
console.log('[1] Auth middleware disabled for this route:', to.path)
|
||||
return
|
||||
}
|
||||
const { loggedIn, options, fetchSession, isPublicRoute } = useAuth()
|
||||
const { loggedIn, options, fetchSession, isPublicPath } = useAuth()
|
||||
const { only, redirectUserTo, redirectGuestTo } = defu(to.meta?.auth, options)
|
||||
|
||||
// 2. If guest mode, redirect if authenticated
|
||||
@@ -55,7 +55,7 @@ export default defineNuxtRouteMiddleware(async (to: RouteLocationNormalized) =>
|
||||
if (import.meta.client) {
|
||||
console.log('[3] Client-side navigation, fetching session')
|
||||
try {
|
||||
await fetchSession()
|
||||
await fetchSession(to.path)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export default defineNuxtRouteMiddleware(async (to: RouteLocationNormalized) =>
|
||||
|
||||
// 4. If not authenticated, redirect to home or guest route
|
||||
if (!loggedIn.value) {
|
||||
if (isPublicRoute(to)) {
|
||||
if (isPublicPath(to.path)) {
|
||||
console.log('[4] Not authenticated, but route is public:', to.path)
|
||||
// Continue navigating to the public route
|
||||
return
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@nuxtjs/i18n": "10.0.3",
|
||||
"@pinia/nuxt": "0.10.1",
|
||||
"@vueuse/core": "^13.6.0",
|
||||
"better-auth": "1.3.4",
|
||||
"better-auth": "1.3.9",
|
||||
"better-sqlite3": "11.8.1",
|
||||
"nuxt": "3.16.1",
|
||||
"pinia": "3.0.1",
|
||||
|
||||
125
legalconsenthub/pnpm-lock.yaml
generated
125
legalconsenthub/pnpm-lock.yaml
generated
@@ -10,7 +10,7 @@ importers:
|
||||
dependencies:
|
||||
'@nuxt/ui-pro':
|
||||
specifier: 3.1.1
|
||||
version: 3.1.1(@babel/parser@7.28.0)(axios@1.7.9)(db0@0.3.1(better-sqlite3@11.8.1))(embla-carousel@8.6.0)(ioredis@5.6.0)(magicast@0.3.5)(typescript@5.7.3)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.0))(vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))(zod@4.0.10)
|
||||
version: 3.1.1(@babel/parser@7.28.0)(axios@1.7.9)(db0@0.3.1(better-sqlite3@11.8.1))(embla-carousel@8.6.0)(ioredis@5.6.0)(magicast@0.3.5)(typescript@5.7.3)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.0))(vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))(zod@4.1.8)
|
||||
'@nuxtjs/i18n':
|
||||
specifier: 10.0.3
|
||||
version: 10.0.3(@vue/compiler-dom@3.5.18)(db0@0.3.1(better-sqlite3@11.8.1))(eslint@9.20.1(jiti@2.4.2))(ioredis@5.6.0)(magicast@0.3.5)(rollup@4.38.0)(vue@3.5.13(typescript@5.7.3))
|
||||
@@ -21,8 +21,8 @@ importers:
|
||||
specifier: ^13.6.0
|
||||
version: 13.6.0(vue@3.5.13(typescript@5.7.3))
|
||||
better-auth:
|
||||
specifier: 1.3.4
|
||||
version: 1.3.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
specifier: 1.3.9
|
||||
version: 1.3.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
better-sqlite3:
|
||||
specifier: 11.8.1
|
||||
version: 11.8.1
|
||||
@@ -243,8 +243,8 @@ packages:
|
||||
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@better-auth/utils@0.2.5':
|
||||
resolution: {integrity: sha512-uI2+/8h/zVsH8RrYdG8eUErbuGBk16rZKQfz8CjxQOyCE6v7BqFYEbFwvOkvl1KbUdxhqOnXp78+uE5h8qVEgQ==}
|
||||
'@better-auth/utils@0.2.6':
|
||||
resolution: {integrity: sha512-3y/vaL5Ox33dBwgJ6ub3OPkVqr6B5xL2kgxNHG8eHZuryLyG/4JSPGqjbdRSgjuy9kALUZYDFl+ORIAxlWMSuA==}
|
||||
|
||||
'@better-fetch/fetch@1.1.18':
|
||||
resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==}
|
||||
@@ -1031,12 +1031,13 @@ packages:
|
||||
resolution: {integrity: sha512-z6okREyK8in0486a22Oro0k+YsuyEjDXJt46FpgeOgXqKJ9ElM8QPll0iuLBkpbH33ENiNbIPLd1cuClRQnhiw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@noble/ciphers@0.6.0':
|
||||
resolution: {integrity: sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ==}
|
||||
'@noble/ciphers@2.0.0':
|
||||
resolution: {integrity: sha512-j/l6jpnpaIBM87cAYPJzi/6TgqmBv9spkqPyCXvRYsu5uxqh6tPJZDnD85yo8VWqzTuTQPgfv7NgT63u7kbwAQ==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
'@noble/hashes@1.8.0':
|
||||
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
|
||||
engines: {node: ^14.21.3 || >=16}
|
||||
'@noble/hashes@2.0.0':
|
||||
resolution: {integrity: sha512-h8VUBlE8R42+XIDO229cgisD287im3kdY6nbNZJFjc6ZvKIXPYXe6Vc/t+kyjFdMFyt5JpapzTsEg8n63w5/lw==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
@@ -1694,11 +1695,11 @@ packages:
|
||||
'@selderee/plugin-htmlparser2@0.11.0':
|
||||
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
|
||||
|
||||
'@simplewebauthn/browser@13.1.0':
|
||||
resolution: {integrity: sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg==}
|
||||
'@simplewebauthn/browser@13.1.2':
|
||||
resolution: {integrity: sha512-aZnW0KawAM83fSBUgglP5WofbrLbLyr7CoPqYr66Eppm7zO86YX6rrCjRB3hQKPrL7ATvY4FVXlykZ6w6FwYYw==}
|
||||
|
||||
'@simplewebauthn/server@13.1.1':
|
||||
resolution: {integrity: sha512-1hsLpRHfSuMB9ee2aAdh0Htza/X3f4djhYISrggqGe3xopNjOcePiSDkDDoPzDYaaMCrbqGP1H2TYU7bgL9PmA==}
|
||||
'@simplewebauthn/server@13.1.2':
|
||||
resolution: {integrity: sha512-VwoDfvLXSCaRiD+xCIuyslU0HLxVggeE5BL06+GbsP2l1fGf5op8e0c3ZtKoi+vSg1q4ikjtAghC23ze2Q3H9g==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@sindresorhus/is@7.0.1':
|
||||
@@ -2357,19 +2358,22 @@ packages:
|
||||
resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
better-auth@1.3.4:
|
||||
resolution: {integrity: sha512-JbZYam6Cs3Eu5CSoMK120zSshfaKvrCftSo/+v7524H1RvhryQ7UtMbzagBcXj0Digjj8hZtVkkR4tTZD/wK2g==}
|
||||
better-auth@1.3.9:
|
||||
resolution: {integrity: sha512-Ty6BHzuShlqSs7I4RMlBRQ3duOWNB7WWriIu2FJVGjQAOtTVvamzFCR4/j5ROFLoNkpvNTRF7BJozsrMICL1gw==}
|
||||
peerDependencies:
|
||||
'@lynx-js/react': '*'
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
react-dom: ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@lynx-js/react':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
better-call@1.0.12:
|
||||
resolution: {integrity: sha512-ssq5OfB9Ungv2M1WVrRnMBomB0qz1VKuhkY2WxjHaLtlsHoSe9EPolj1xf7xf8LY9o3vfk3Rx6rCWI4oVHeBRg==}
|
||||
better-call@1.0.18:
|
||||
resolution: {integrity: sha512-Ojyck3P3fs/egBmCW50tvfbCJorNV5KphfPOKrkCxPfOr8Brth1ruDtAJuhHVHEUiWrXv+vpEgWQk7m7FzhbbQ==}
|
||||
|
||||
better-sqlite3@11.8.1:
|
||||
resolution: {integrity: sha512-9BxNaBkblMjhJW8sMRZxnxVTRgbRmssZW0Oxc1MPBTfiR+WW21e2Mk4qu8CzrcZb1LwPCnFsfDEzq+SNcBU8eg==}
|
||||
@@ -3688,8 +3692,8 @@ packages:
|
||||
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
|
||||
hasBin: true
|
||||
|
||||
jose@5.10.0:
|
||||
resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==}
|
||||
jose@6.1.0:
|
||||
resolution: {integrity: sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==}
|
||||
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
@@ -3766,8 +3770,8 @@ packages:
|
||||
kolorist@1.8.0:
|
||||
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
|
||||
|
||||
kysely@0.28.3:
|
||||
resolution: {integrity: sha512-svKnkSH72APRdjfVCCOknxaC9Eb3nA2StHG9d5/sKOqRvHRp2Dtf1XwDvc92b4B5v6LV+EAGWXQbZ5jMOvHaDw==}
|
||||
kysely@0.28.6:
|
||||
resolution: {integrity: sha512-QQlpW/Db5yhhY9+c1jiCBCUCJqZoWLw6c1rE+H+FqGujMuIAxerCSdQNvyP3zyhQUO913J9Ank1NsQEb5a15mA==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
launch-editor@2.10.0:
|
||||
@@ -5746,33 +5750,33 @@ packages:
|
||||
peerDependencies:
|
||||
zod: ^3.24.1
|
||||
|
||||
zod@4.0.10:
|
||||
resolution: {integrity: sha512-3vB+UU3/VmLL2lvwcY/4RV2i9z/YU0DTV/tDuYjrwmx5WeJ7hwy+rGEEx8glHp6Yxw7ibRbKSaIFBgReRPe5KA==}
|
||||
zod@4.1.8:
|
||||
resolution: {integrity: sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@ai-sdk/provider-utils@2.2.8(zod@4.0.10)':
|
||||
'@ai-sdk/provider-utils@2.2.8(zod@4.1.8)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.1.3
|
||||
nanoid: 3.3.11
|
||||
secure-json-parse: 2.7.0
|
||||
zod: 4.0.10
|
||||
zod: 4.1.8
|
||||
|
||||
'@ai-sdk/provider@1.1.3':
|
||||
dependencies:
|
||||
json-schema: 0.4.0
|
||||
|
||||
'@ai-sdk/ui-utils@1.2.11(zod@4.0.10)':
|
||||
'@ai-sdk/ui-utils@1.2.11(zod@4.1.8)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.1.3
|
||||
'@ai-sdk/provider-utils': 2.2.8(zod@4.0.10)
|
||||
zod: 4.0.10
|
||||
zod-to-json-schema: 3.24.5(zod@4.0.10)
|
||||
'@ai-sdk/provider-utils': 2.2.8(zod@4.1.8)
|
||||
zod: 4.1.8
|
||||
zod-to-json-schema: 3.24.5(zod@4.1.8)
|
||||
|
||||
'@ai-sdk/vue@1.2.12(vue@3.5.13(typescript@5.7.3))(zod@4.0.10)':
|
||||
'@ai-sdk/vue@1.2.12(vue@3.5.13(typescript@5.7.3))(zod@4.1.8)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider-utils': 2.2.8(zod@4.0.10)
|
||||
'@ai-sdk/ui-utils': 1.2.11(zod@4.0.10)
|
||||
'@ai-sdk/provider-utils': 2.2.8(zod@4.1.8)
|
||||
'@ai-sdk/ui-utils': 1.2.11(zod@4.1.8)
|
||||
swrv: 1.1.0(vue@3.5.13(typescript@5.7.3))
|
||||
optionalDependencies:
|
||||
vue: 3.5.13(typescript@5.7.3)
|
||||
@@ -5981,9 +5985,8 @@ snapshots:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
|
||||
'@better-auth/utils@0.2.5':
|
||||
'@better-auth/utils@0.2.6':
|
||||
dependencies:
|
||||
typescript: 5.8.3
|
||||
uncrypto: 0.1.3
|
||||
|
||||
'@better-fetch/fetch@1.1.18': {}
|
||||
@@ -6613,9 +6616,9 @@ snapshots:
|
||||
|
||||
'@netlify/serverless-functions-api@1.36.0': {}
|
||||
|
||||
'@noble/ciphers@0.6.0': {}
|
||||
'@noble/ciphers@2.0.0': {}
|
||||
|
||||
'@noble/hashes@1.8.0': {}
|
||||
'@noble/hashes@2.0.0': {}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
dependencies:
|
||||
@@ -6983,12 +6986,12 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@nuxt/ui-pro@3.1.1(@babel/parser@7.28.0)(axios@1.7.9)(db0@0.3.1(better-sqlite3@11.8.1))(embla-carousel@8.6.0)(ioredis@5.6.0)(magicast@0.3.5)(typescript@5.7.3)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.0))(vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))(zod@4.0.10)':
|
||||
'@nuxt/ui-pro@3.1.1(@babel/parser@7.28.0)(axios@1.7.9)(db0@0.3.1(better-sqlite3@11.8.1))(embla-carousel@8.6.0)(ioredis@5.6.0)(magicast@0.3.5)(typescript@5.7.3)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.0))(vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))(zod@4.1.8)':
|
||||
dependencies:
|
||||
'@ai-sdk/vue': 1.2.12(vue@3.5.13(typescript@5.7.3))(zod@4.0.10)
|
||||
'@ai-sdk/vue': 1.2.12(vue@3.5.13(typescript@5.7.3))(zod@4.1.8)
|
||||
'@nuxt/kit': 3.17.2(magicast@0.3.5)
|
||||
'@nuxt/schema': 3.17.2
|
||||
'@nuxt/ui': 3.1.1(@babel/parser@7.28.0)(axios@1.7.9)(db0@0.3.1(better-sqlite3@11.8.1))(embla-carousel@8.6.0)(ioredis@5.6.0)(magicast@0.3.5)(typescript@5.7.3)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.0))(vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))(zod@4.0.10)
|
||||
'@nuxt/ui': 3.1.1(@babel/parser@7.28.0)(axios@1.7.9)(db0@0.3.1(better-sqlite3@11.8.1))(embla-carousel@8.6.0)(ioredis@5.6.0)(magicast@0.3.5)(typescript@5.7.3)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.0))(vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))(zod@4.1.8)
|
||||
'@standard-schema/spec': 1.0.0
|
||||
'@vueuse/core': 13.6.0(vue@3.5.13(typescript@5.7.3))
|
||||
consola: 3.4.2
|
||||
@@ -7006,7 +7009,7 @@ snapshots:
|
||||
unplugin-auto-import: 19.1.2(@nuxt/kit@3.17.2(magicast@0.3.5))(@vueuse/core@13.6.0(vue@3.5.13(typescript@5.7.3)))
|
||||
unplugin-vue-components: 28.5.0(@babel/parser@7.28.0)(@nuxt/kit@3.17.2(magicast@0.3.5))(vue@3.5.13(typescript@5.7.3))
|
||||
optionalDependencies:
|
||||
zod: 4.0.10
|
||||
zod: 4.1.8
|
||||
transitivePeerDependencies:
|
||||
- '@azure/app-configuration'
|
||||
- '@azure/cosmos'
|
||||
@@ -7047,7 +7050,7 @@ snapshots:
|
||||
- vue
|
||||
- vue-router
|
||||
|
||||
'@nuxt/ui@3.1.1(@babel/parser@7.28.0)(axios@1.7.9)(db0@0.3.1(better-sqlite3@11.8.1))(embla-carousel@8.6.0)(ioredis@5.6.0)(magicast@0.3.5)(typescript@5.7.3)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.0))(vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))(zod@4.0.10)':
|
||||
'@nuxt/ui@3.1.1(@babel/parser@7.28.0)(axios@1.7.9)(db0@0.3.1(better-sqlite3@11.8.1))(embla-carousel@8.6.0)(ioredis@5.6.0)(magicast@0.3.5)(typescript@5.7.3)(vite@6.2.3(@types/node@22.13.14)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.0))(vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))(zod@4.1.8)':
|
||||
dependencies:
|
||||
'@iconify/vue': 4.3.0(vue@3.5.13(typescript@5.7.3))
|
||||
'@internationalized/date': 3.8.0
|
||||
@@ -7094,7 +7097,7 @@ snapshots:
|
||||
vue-component-type-helpers: 2.2.10
|
||||
optionalDependencies:
|
||||
vue-router: 4.5.0(vue@3.5.13(typescript@5.7.3))
|
||||
zod: 4.0.10
|
||||
zod: 4.1.8
|
||||
transitivePeerDependencies:
|
||||
- '@azure/app-configuration'
|
||||
- '@azure/cosmos'
|
||||
@@ -7652,9 +7655,9 @@ snapshots:
|
||||
domhandler: 5.0.3
|
||||
selderee: 0.11.0
|
||||
|
||||
'@simplewebauthn/browser@13.1.0': {}
|
||||
'@simplewebauthn/browser@13.1.2': {}
|
||||
|
||||
'@simplewebauthn/server@13.1.1':
|
||||
'@simplewebauthn/server@13.1.2':
|
||||
dependencies:
|
||||
'@hexagon/base64': 1.1.28
|
||||
'@levischuck/tiny-cbor': 0.2.11
|
||||
@@ -8395,25 +8398,25 @@ snapshots:
|
||||
|
||||
basic-ftp@5.0.5: {}
|
||||
|
||||
better-auth@1.3.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
better-auth@1.3.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
'@better-auth/utils': 0.2.5
|
||||
'@better-auth/utils': 0.2.6
|
||||
'@better-fetch/fetch': 1.1.18
|
||||
'@noble/ciphers': 0.6.0
|
||||
'@noble/hashes': 1.8.0
|
||||
'@simplewebauthn/browser': 13.1.0
|
||||
'@simplewebauthn/server': 13.1.1
|
||||
better-call: 1.0.12
|
||||
'@noble/ciphers': 2.0.0
|
||||
'@noble/hashes': 2.0.0
|
||||
'@simplewebauthn/browser': 13.1.2
|
||||
'@simplewebauthn/server': 13.1.2
|
||||
better-call: 1.0.18
|
||||
defu: 6.1.4
|
||||
jose: 5.10.0
|
||||
kysely: 0.28.3
|
||||
jose: 6.1.0
|
||||
kysely: 0.28.6
|
||||
nanostores: 0.11.4
|
||||
zod: 4.0.10
|
||||
zod: 4.1.8
|
||||
optionalDependencies:
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
|
||||
better-call@1.0.12:
|
||||
better-call@1.0.18:
|
||||
dependencies:
|
||||
'@better-fetch/fetch': 1.1.18
|
||||
rou3: 0.5.1
|
||||
@@ -9875,7 +9878,7 @@ snapshots:
|
||||
|
||||
jiti@2.4.2: {}
|
||||
|
||||
jose@5.10.0: {}
|
||||
jose@6.1.0: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
@@ -9935,7 +9938,7 @@ snapshots:
|
||||
|
||||
kolorist@1.8.0: {}
|
||||
|
||||
kysely@0.28.3: {}
|
||||
kysely@0.28.6: {}
|
||||
|
||||
launch-editor@2.10.0:
|
||||
dependencies:
|
||||
@@ -12112,8 +12115,8 @@ snapshots:
|
||||
compress-commons: 6.0.2
|
||||
readable-stream: 4.7.0
|
||||
|
||||
zod-to-json-schema@3.24.5(zod@4.0.10):
|
||||
zod-to-json-schema@3.24.5(zod@4.1.8):
|
||||
dependencies:
|
||||
zod: 4.0.10
|
||||
zod: 4.1.8
|
||||
|
||||
zod@4.0.10: {}
|
||||
zod@4.1.8: {}
|
||||
|
||||
@@ -8,20 +8,50 @@ import {
|
||||
worksCouncilMemberRole,
|
||||
employeeRole,
|
||||
adminRole,
|
||||
ownerRole,
|
||||
ROLES,
|
||||
type LegalRole
|
||||
} from './permissions'
|
||||
|
||||
const db = new Database('./sqlite.db')
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: new Database('./sqlite.db'),
|
||||
database: db,
|
||||
onAPIError: { throw: true },
|
||||
emailAndPassword: { enabled: true, autoSignIn: false },
|
||||
emailAndPassword: { enabled: true, autoSignIn: false, minPasswordLength: 1 },
|
||||
trustedOrigins: ['http://localhost:3001'],
|
||||
plugins: [
|
||||
jwt({
|
||||
jwt: {
|
||||
issuer: 'http://192.168.178.114:3001',
|
||||
expirationTime: '1yr'
|
||||
expirationTime: '1yr',
|
||||
definePayload: ({ user, session }) => {
|
||||
let userRoles: string[] = []
|
||||
|
||||
if (session.activeOrganizationId) {
|
||||
try {
|
||||
const roleQuery = db.prepare(`
|
||||
SELECT role
|
||||
FROM member
|
||||
WHERE userId = ? AND organizationId = ?
|
||||
`)
|
||||
const memberRole = roleQuery.get(user.id, session.activeOrganizationId) as { role: string } | undefined
|
||||
|
||||
if (memberRole?.role) {
|
||||
userRoles = [memberRole.role]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error querying user role:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
roles: userRoles,
|
||||
organizationId: session.activeOrganizationId
|
||||
}
|
||||
}
|
||||
},
|
||||
jwks: {
|
||||
keyPairConfig: {
|
||||
@@ -37,11 +67,10 @@ export const auth = betterAuth({
|
||||
[ROLES.EMPLOYER]: employerRole,
|
||||
[ROLES.WORKS_COUNCIL_MEMBER]: worksCouncilMemberRole,
|
||||
[ROLES.EMPLOYEE]: employeeRole,
|
||||
[ROLES.ADMIN]: adminRole
|
||||
[ROLES.ADMIN]: adminRole,
|
||||
[ROLES.OWNER]: ownerRole
|
||||
},
|
||||
|
||||
// Creator gets admin role by default
|
||||
creatorRole: ROLES.ADMIN,
|
||||
creatorRole: ROLES.ADMIN, // OWNER fixen here!
|
||||
|
||||
async sendInvitationEmail(data) {
|
||||
console.log('Sending invitation email', data)
|
||||
@@ -51,7 +80,8 @@ export const auth = betterAuth({
|
||||
[ROLES.EMPLOYER]: 'Arbeitgeber',
|
||||
[ROLES.EMPLOYEE]: 'Arbeitnehmer',
|
||||
[ROLES.WORKS_COUNCIL_MEMBER]: 'Betriebsrat',
|
||||
[ROLES.ADMIN]: 'Administrator'
|
||||
[ROLES.ADMIN]: 'Administrator',
|
||||
[ROLES.OWNER]: 'Eigentümer'
|
||||
}
|
||||
|
||||
const roleDisplayName = roleDisplayNames[data.role as LegalRole] || data.role
|
||||
@@ -70,7 +100,7 @@ export const auth = betterAuth({
|
||||
})
|
||||
|
||||
if (result.error) {
|
||||
throw new Error(`Email sending failed: ${result.error?.statusCode} ${result.error?.error}`)
|
||||
throw new Error(`Email sending failed: ${result.error.message || result.error.name || 'Unknown error'}`)
|
||||
}
|
||||
|
||||
console.log('Email invite link:', inviteLink)
|
||||
|
||||
@@ -91,12 +91,33 @@ export const useOrganizationStore = defineStore('Organization', () => {
|
||||
try {
|
||||
await organizationApi.acceptInvitation(invitationId)
|
||||
await navigateTo('/')
|
||||
await syncUserRoleToBackend()
|
||||
} catch (e) {
|
||||
toast.add({ title: 'Error accepting invitation', color: 'error' })
|
||||
console.error('Error accepting invitation:', e)
|
||||
}
|
||||
}
|
||||
|
||||
async function syncUserRoleToBackend() {
|
||||
try {
|
||||
const { updateUser } = useUser()
|
||||
const { user } = useAuth()
|
||||
|
||||
if (!user.value?.id) {
|
||||
console.warn('No user ID available for role sync')
|
||||
return
|
||||
}
|
||||
|
||||
// Call updateUser without userDto to trigger JWT-based sync
|
||||
await updateUser(user.value.id)
|
||||
|
||||
console.log('Successfully synced user role to backend from JWT')
|
||||
} catch (error) {
|
||||
console.error('Failed to sync user role to backend from JWT:', error)
|
||||
// Don't throw - role sync failure shouldn't prevent invitation acceptance
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelSentInvitation(invitationId: string) {
|
||||
try {
|
||||
await organizationApi.cancelSentInvitation(invitationId)
|
||||
|
||||
Reference in New Issue
Block a user