@swiss-ai-hub/web 0.291.7 → 0.291.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app.vue CHANGED
@@ -3,6 +3,12 @@
3
3
  <NuxtPage />
4
4
  <Toast />
5
5
  <ConfirmDialog />
6
+ <div
7
+ v-if="homeResolving"
8
+ class="fixed inset-0 z-50 flex items-center justify-center bg-white dark:bg-surface-900"
9
+ >
10
+ <ProgressSpinner />
11
+ </div>
6
12
  </NuxtLayout>
7
13
  </template>
8
14
 
@@ -17,6 +23,10 @@ const localePath = useLocalePath()
17
23
  const route = useRoute()
18
24
  const toast = useToast()
19
25
  useNotificationPoller()
26
+
27
+ // Overlay lives at the root, not the page: the home-redirect middleware
28
+ // redirects before any page mounts, so the page can't show it.
29
+ const homeResolving = useHomeResolving()
20
30
  client.setConfig({
21
31
  baseURL: '/api/v1',
22
32
  auth: async () => {
@@ -0,0 +1,2 @@
1
+ // useState, not createSharedComposable: the writer is a route middleware, not a component.
2
+ export const useHomeResolving = () => useState<boolean>('home-resolving', () => false)
@@ -40,6 +40,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
40
40
  await $auth.signinSilent()
41
41
  }
42
42
  catch {
43
+ await $auth.removeUser()
43
44
  if (import.meta.client && to.fullPath !== '/' && !isAuthPath) {
44
45
  sessionStorage.setItem(REDIRECT_KEY, to.fullPath)
45
46
  }
@@ -0,0 +1,30 @@
1
+ import { getMyTenants } from '@core/sdk/client'
2
+
3
+ import { useLocalePath } from '#i18n'
4
+
5
+ const REDIRECT_KEY = 'aihub_redirect_after_login'
6
+
7
+ // Redirect runs in the guard pipeline, not the page: a page-component redirect
8
+ // here can be dropped by an in-flight navigation.
9
+ export default defineNuxtRouteMiddleware(async () => {
10
+ useHomeResolving().value = true
11
+
12
+ const localePath = useLocalePath()
13
+
14
+ const response = await getMyTenants({ composable: '$fetch' }).catch(() => null)
15
+ const tenants = response?.tenants ?? []
16
+ if (!tenants.length) {
17
+ return
18
+ }
19
+
20
+ const storedRedirect = sessionStorage.getItem(REDIRECT_KEY)
21
+ sessionStorage.removeItem(REDIRECT_KEY)
22
+
23
+ if (storedRedirect && storedRedirect !== '/') {
24
+ return navigateTo(storedRedirect, { replace: true })
25
+ }
26
+ if (tenants.length === 1) {
27
+ return navigateTo(localePath(`/${tenants[0].id}/service/openai`), { replace: true })
28
+ }
29
+ return navigateTo(localePath('/select-tenant'), { replace: true })
30
+ })
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "license": "AGPL-3.0-or-later",
4
4
  "author": "bbv Software Services AG (https://www.bbv.ch)",
5
5
  "type": "module",
6
- "version": "0.291.7",
6
+ "version": "0.291.8",
7
7
  "description": "Swiss AI Hub - Admin & Management UI (Nuxt 3 layer)",
8
8
  "main": "./nuxt.config.ts",
9
9
  "repository": {
package/pages/index.vue CHANGED
@@ -1,10 +1,6 @@
1
1
  <template>
2
2
  <div class="flex h-screen items-center justify-center">
3
- <ProgressSpinner v-if="tenantsAreLoading" />
4
- <div
5
- v-else-if="!tenants?.length"
6
- class="text-center"
7
- >
3
+ <div class="text-center">
8
4
  <h1 class="mb-4 text-2xl font-bold">
9
5
  {{ t('tenant.no_tenant_title') }}
10
6
  </h1>
@@ -16,44 +12,8 @@
16
12
  </template>
17
13
 
18
14
  <script setup lang="ts">
19
- import { getMyTenants } from '@core/sdk/client'
20
-
21
- const REDIRECT_KEY = 'aihub_redirect_after_login'
15
+ // Shown only when the user has no tenants; home-redirect middleware redirects otherwise.
16
+ definePageMeta({ middleware: 'home-redirect' })
22
17
 
23
18
  const { t } = useI18n()
24
- const localePath = useLocalePath()
25
-
26
- const tenantsAreLoading = ref(true)
27
- const tenants = ref<{ id: string }[] | null>(null)
28
-
29
- onMounted(async () => {
30
- try {
31
- const response = await getMyTenants({ composable: '$fetch' })
32
- tenants.value = response.tenants
33
-
34
- if (!response.tenants?.length) {
35
- tenantsAreLoading.value = false
36
- return
37
- }
38
-
39
- const storedRedirect = sessionStorage.getItem(REDIRECT_KEY)
40
- sessionStorage.removeItem(REDIRECT_KEY)
41
-
42
- if (storedRedirect && storedRedirect !== '/') {
43
- await navigateTo(storedRedirect, { replace: true })
44
- return
45
- }
46
-
47
- if (response.tenants.length === 1) {
48
- const tenant = response.tenants[0]
49
- await navigateTo(localePath(`/${tenant.id}/service/openai`), { replace: true })
50
- return
51
- }
52
-
53
- await navigateTo(localePath('/select-tenant'), { replace: true })
54
- }
55
- catch {
56
- tenantsAreLoading.value = false
57
- }
58
- })
59
19
  </script>
@@ -0,0 +1,13 @@
1
+ // In a plugin, not app.vue, so afterEach registers before the initial navigation
2
+ // — otherwise a full load landing on the home route never clears the flag.
3
+ export default defineNuxtPlugin(() => {
4
+ const router = useRouter()
5
+ const homeResolving = useHomeResolving()
6
+
7
+ router.afterEach(() => {
8
+ homeResolving.value = false
9
+ })
10
+ router.onError(() => {
11
+ homeResolving.value = false
12
+ })
13
+ })
@@ -40,35 +40,17 @@ export default defineNuxtPlugin(async ({ $i18n, $router }) => {
40
40
  $router.push(`/${locale}/auth/login`)
41
41
  })
42
42
 
43
- auth.events.addSilentRenewError((error) => {
43
+ auth.events.addSilentRenewError(async (error) => {
44
44
  console.error('Silent renew error:', error)
45
- // The refresh token is rejected (typically: Keycloak invalidated it).
46
- // Without a working renew path, the SDK starts sending unauthenticated
47
- // requests on the next navigation — surfacing as 401s downstream. Send
48
- // the user to the login page now so they re-auth in place.
45
+ // Refresh token rejected (e.g. Keycloak invalidated it): drop the dead
46
+ // session before redirecting so it is not reused.
47
+ await auth.removeUser()
49
48
  const locale = $i18n.locale.value
50
49
  $router.push(`/${locale}/auth/login`)
51
50
  })
52
51
 
53
- // Check for user session on startup
54
- try {
55
- const user = await auth.getUser()
56
- if (user && !user.expired) {
57
- console.log('User already logged in')
58
- }
59
- else if (user?.expired) {
60
- console.log('User session expired, attempting renewal')
61
- try {
62
- await auth.signinSilent()
63
- }
64
- catch (e) {
65
- console.error('Failed to renew session:', e)
66
- }
67
- }
68
- }
69
- catch (e) {
70
- console.error('Error checking initial user state:', e)
71
- }
52
+ // Session renewal is handled per-navigation by middleware/auth.global.ts;
53
+ // intentionally not done here (it would block app bootstrap).
72
54
 
73
55
  return {
74
56
  provide: {