session.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. import { computed, ref } from 'vue'
  3. import { defineStore } from 'pinia'
  4. import { cloneDeep } from 'lodash-es'
  5. import { useSessionIdQuery } from '@shared/graphql/queries/sessionId.api'
  6. import { useCurrentUserQuery } from '@shared/graphql/queries/currentUser.api'
  7. import {
  8. QueryHandler,
  9. SubscriptionHandler,
  10. } from '@shared/server/apollo/handler'
  11. import type { UserData } from '@shared/types/store'
  12. import hasPermission from '@shared/utils/hasPermission'
  13. import type { RequiredPermission } from '@shared/types/permission'
  14. import { useCurrentUserUpdatesSubscription } from '@shared/graphql/subscriptions/currentUserUpdates.api'
  15. import type {
  16. CurrentUserQuery,
  17. CurrentUserQueryVariables,
  18. CurrentUserUpdatesSubscription,
  19. CurrentUserUpdatesSubscriptionVariables,
  20. SessionIdQuery,
  21. SessionIdQueryVariables,
  22. } from '@shared/graphql/types'
  23. import testFlags from '@shared/utils/testFlags'
  24. import log from '@shared/utils/log'
  25. import { useLocaleStore } from './locale'
  26. let sessionIdQuery: QueryHandler<SessionIdQuery, SessionIdQueryVariables>
  27. const getSessionIdQuery = () => {
  28. if (sessionIdQuery) return sessionIdQuery
  29. sessionIdQuery = new QueryHandler(
  30. useSessionIdQuery({
  31. fetchPolicy: 'no-cache',
  32. context: {
  33. error: {
  34. logLevel: 'silent',
  35. },
  36. },
  37. }),
  38. {
  39. errorShowNotification: false,
  40. },
  41. )
  42. return sessionIdQuery
  43. }
  44. let currentUserQuery: QueryHandler<CurrentUserQuery, CurrentUserQueryVariables>
  45. const getCurrentUserQuery = () => {
  46. if (currentUserQuery) return currentUserQuery
  47. currentUserQuery = new QueryHandler(useCurrentUserQuery())
  48. return currentUserQuery
  49. }
  50. export const useSessionStore = defineStore(
  51. 'session',
  52. () => {
  53. const id = ref<Maybe<string>>(null)
  54. const checkSession = async (): Promise<string | null> => {
  55. const sessionIdQuery = getSessionIdQuery()
  56. const result = await sessionIdQuery.loadedResult(true)
  57. // Refresh the current sessionId state.
  58. id.value = result?.sessionId || null
  59. return id.value
  60. }
  61. const user = ref<Maybe<UserData>>(null)
  62. let currentUserUpdateSubscription: SubscriptionHandler<
  63. CurrentUserUpdatesSubscription,
  64. CurrentUserUpdatesSubscriptionVariables
  65. >
  66. let currentUserWatchOnResultInitialized = false
  67. const getCurrentUser = async (): Promise<Maybe<UserData>> => {
  68. if (currentUserQuery && !user.value) {
  69. currentUserQuery.start()
  70. }
  71. const query = getCurrentUserQuery()
  72. // Watch on result that also the subscription to more will update the user data.
  73. if (!currentUserWatchOnResultInitialized) {
  74. query.watchOnResult((result) => {
  75. user.value = cloneDeep(result?.currentUser) || null
  76. log.debug('currentUserUpdate', user.value)
  77. })
  78. currentUserWatchOnResultInitialized = true
  79. }
  80. await query.loadedResult(true)
  81. // Check if the locale is different, then a update is needed.
  82. const locale = useLocaleStore()
  83. const userLocale = user.value?.preferences?.locale as string
  84. if (
  85. userLocale &&
  86. (!locale.localeData || userLocale !== locale.localeData.locale)
  87. ) {
  88. await locale.setLocale(userLocale)
  89. }
  90. if (user.value) {
  91. if (!currentUserUpdateSubscription) {
  92. currentUserUpdateSubscription = new SubscriptionHandler(
  93. useCurrentUserUpdatesSubscription(() => ({
  94. userId: (user.value as UserData)?.id,
  95. })),
  96. )
  97. currentUserUpdateSubscription.onResult((result) => {
  98. const user = result.data?.userUpdates.user
  99. if (!user) {
  100. testFlags.set('useCurrentUserUpdatesSubscription.subscribed')
  101. }
  102. })
  103. } else {
  104. currentUserUpdateSubscription.start()
  105. }
  106. testFlags.set('useSessionUserStore.getCurrentUser.loaded')
  107. }
  108. return user.value
  109. }
  110. const resetCurrentSession = () => {
  111. if (currentUserUpdateSubscription) currentUserUpdateSubscription.stop()
  112. if (currentUserQuery) currentUserQuery.stop()
  113. id.value = null
  114. user.value = null
  115. }
  116. const userHasPermission = (
  117. requiredPermission: RequiredPermission,
  118. ): boolean => {
  119. return hasPermission(
  120. requiredPermission,
  121. user.value?.permissions?.names || [],
  122. )
  123. }
  124. // In case of unauthenticated users, current user ID may be an empty string.
  125. // Use with care.
  126. const userId = computed(() => user.value?.id || '')
  127. return {
  128. id,
  129. checkSession,
  130. user,
  131. userId,
  132. getCurrentUser,
  133. resetCurrentSession,
  134. hasPermission: userHasPermission,
  135. }
  136. },
  137. {
  138. requiresAuth: false,
  139. },
  140. )