Skip to content

Commit ea1a16a

Browse files
committed
fix(TAP-12185): restore missing permission cache
1 parent c219ee7 commit ea1a16a

5 files changed

Lines changed: 165 additions & 41 deletions

File tree

‎apps/daas/src/directives/index.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { ClipboardPlugin, Cookie } from '@tap/shared'
2+
import { getCachedPermissions } from '@/utils/util'
23

34
export function hasPermissionByCode(code) {
4-
let permissions = sessionStorage.getItem('tapdata_permissions')
5-
permissions = JSON.parse(permissions)
5+
const permissions = getCachedPermissions()
66

77
if (!permissions || permissions.length === 0) {
88
return false

‎apps/daas/src/layouts/Sidebar.vue‎

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { computed, inject, onMounted, ref, type Ref } from 'vue'
44
import { useRoute, useRouter } from 'vue-router'
55
import { useStore } from 'vuex'
66
import { MENU as menuSetting } from '@/router/menu'
7+
import { getCachedPermissions } from '@/utils/util'
78
89
// Types
910
interface MenuItem {
@@ -47,15 +48,7 @@ const isMenuEnabled = computed(() => store.getters['feature/isMenuEnabled'])
4748
4849
// Methods
4950
const getMenus = (hideMenuMap: Record<string, boolean> = {}) => {
50-
let permissions: any[] = []
51-
const permissionsStr = sessionStorage.getItem('tapdata_permissions')
52-
if (permissionsStr) {
53-
try {
54-
permissions = JSON.parse(permissionsStr)
55-
} catch (error) {
56-
console.error('Failed to parse permissions', error)
57-
}
58-
}
51+
const permissions = getCachedPermissions() || []
5952
6053
const routerMap: Record<string, any> = {}
6154
const routes = router.options.routes

‎apps/daas/src/main.ts‎

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import App from '@/App.vue'
1818
import { installOEM } from '@/oem'
1919
import { installAllPlugins } from '@/plugins'
2020
import { initRequestClient } from '@/plugins/axios'
21-
import { configUser, getUrlSearch } from '@/utils/util'
21+
import { configUser, getUrlSearch, signOut } from '@/utils/util'
2222
import store from '@/vuex' // 引入全局数据控制
2323
import { installDirectives } from './directives'
2424
import i18n from './i18n'
@@ -122,23 +122,23 @@ const loading = ElLoading.service({ fullscreen: true })
122122
const bootstrap = async () => {
123123
if (token) {
124124
//无权限,说明是首次进入页面,重新请求后台获取
125-
const user = await getUserInfoByToken().catch(() => {
126-
init()
127-
return null
128-
})
125+
const user = await getUserInfoByToken().catch(() => null)
129126

130-
if (user) {
131-
configUser(user)
127+
if (!user) {
128+
signOut()
129+
return
130+
}
132131

133-
const settings = await fetchSettings()
134-
setSettings(settings)
132+
configUser(user)
135133

136-
await store.dispatch('feature/getFeatures')
134+
const settings = await fetchSettings()
135+
setSettings(settings)
137136

138-
if (settings.length) {
139-
localStorage.setItem('TAPDATA_SETTINGS', JSON.stringify(settings))
140-
store.commit('setAppearanceBySetting', settings)
141-
}
137+
await store.dispatch('feature/getFeatures')
138+
139+
if (settings.length) {
140+
localStorage.setItem('TAPDATA_SETTINGS', JSON.stringify(settings))
141+
store.commit('setAppearanceBySetting', settings)
142142
}
143143
}
144144

‎apps/daas/src/router/index.ts‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import {
1010
} from 'vue-router'
1111

1212
import i18n from '@/i18n'
13+
import { ensurePermissions, signOut } from '@/utils/util'
1314
import { routes } from './routes'
1415

1516
const router = createRouter({
1617
history: createWebHashHistory(),
1718
routes: routes as readonly RouteRecordRaw[],
1819
})
1920

20-
router.beforeEach((to, from, next) => {
21+
router.beforeEach(async (to, from, next) => {
2122
if (!to.matched.length) {
2223
Message.error({
2324
message: 'Page not found!',
@@ -30,9 +31,14 @@ router.beforeEach((to, from, next) => {
3031
}
3132
const token = Cookie.get('access_token')
3233
if (token) {
33-
//若token存在,获取权限
34-
const permissionsStr = sessionStorage.getItem('tapdata_permissions')
35-
const permissions = JSON.parse(permissionsStr)
34+
let permissions: Awaited<ReturnType<typeof ensurePermissions>> = []
35+
36+
try {
37+
permissions = await ensurePermissions()
38+
} catch {
39+
signOut()
40+
return
41+
}
3642

3743
//判断当前路由的页面是否有权限,无权限则不跳转,有权限则执行跳转
3844
let matched = true

‎apps/daas/src/utils/util.ts‎

Lines changed: 137 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,150 @@
1+
import { getUserInfoByToken } from '@tap/api/src/core/users'
12
import Cookie from '@tap/shared/src/cookie'
23
import dayjs from 'dayjs'
34
import i18n from '@/i18n'
45

6+
type PermissionResource = { code?: string } & Record<string, any>
7+
8+
const PERMISSIONS_STORAGE_KEY = 'tapdata_permissions'
9+
10+
let cachedPermissions: PermissionResource[] | null = null
11+
let permissionsRequest: Promise<PermissionResource[]> | null = null
12+
13+
function getPermissionDebugContext() {
14+
const navigation = performance.getEntriesByType?.('navigation')?.[0] as
15+
| PerformanceNavigationTiming
16+
| undefined
17+
18+
return {
19+
href: location.href,
20+
navigationType: navigation?.type,
21+
visibilityState: document.visibilityState,
22+
wasDiscarded: (document as Document & { wasDiscarded?: boolean })
23+
.wasDiscarded,
24+
}
25+
}
26+
27+
function debugPermissions(message: string, details: Record<string, any> = {}) {
28+
// eslint-disable-next-line no-console
29+
console.debug('[tapdata_permissions]', message, {
30+
...getPermissionDebugContext(),
31+
...details,
32+
})
33+
}
34+
35+
function flattenPermissions(user: Record<string, any> = {}) {
36+
const permissions: PermissionResource[] = []
37+
const list = user?.permissions || []
38+
39+
list.forEach((permission: Record<string, any>) => {
40+
if (permission.resources && permission.resources.length > 0) {
41+
permission.resources.forEach((res: PermissionResource) => {
42+
permissions.push(res)
43+
})
44+
}
45+
})
46+
47+
return permissions
48+
}
49+
50+
function setPermissions(permissions: PermissionResource[]) {
51+
cachedPermissions = permissions
52+
sessionStorage.setItem(PERMISSIONS_STORAGE_KEY, JSON.stringify(permissions))
53+
54+
if (!permissions.length) {
55+
debugPermissions('wrote empty permissions cache')
56+
}
57+
}
58+
59+
export function clearPermissions() {
60+
cachedPermissions = null
61+
permissionsRequest = null
62+
sessionStorage.removeItem(PERMISSIONS_STORAGE_KEY)
63+
debugPermissions('cleared permissions cache')
64+
}
65+
66+
export function getCachedPermissions() {
67+
const permissionsStr = sessionStorage.getItem(PERMISSIONS_STORAGE_KEY)
68+
69+
if (permissionsStr !== null) {
70+
try {
71+
const permissions = JSON.parse(permissionsStr)
72+
73+
if (Array.isArray(permissions)) {
74+
cachedPermissions = permissions
75+
return permissions as PermissionResource[]
76+
}
77+
debugPermissions('removed invalid permissions cache', {
78+
valueType: typeof permissions,
79+
})
80+
sessionStorage.removeItem(PERMISSIONS_STORAGE_KEY)
81+
} catch (error) {
82+
debugPermissions('removed unparsable permissions cache', {
83+
errorMessage: error?.message,
84+
valueLength: permissionsStr.length,
85+
})
86+
sessionStorage.removeItem(PERMISSIONS_STORAGE_KEY)
87+
}
88+
}
89+
90+
if (cachedPermissions) {
91+
debugPermissions('using in-memory permissions cache', {
92+
permissionsCount: cachedPermissions.length,
93+
})
94+
return cachedPermissions
95+
}
96+
97+
return null
98+
}
99+
5100
export function configUser(user: Record<string, any> = {}) {
6101
Cookie.set('email', user.email)
7102
Cookie.set('username', user.username || '')
8103
Cookie.set('isAdmin', String(Number.parseInt(user.role) || 0))
9104
Cookie.set('user_id', user.id)
10-
const permissions: string[] = []
11-
const list = user?.permissions || []
12-
if (list.length) {
13-
list.forEach((permission: Record<string, any>) => {
14-
if (permission.resources && permission.resources.length > 0) {
15-
permission.resources.forEach((res: string) => {
16-
permissions.push(res)
105+
const permissions = flattenPermissions(user)
106+
107+
setPermissions(permissions)
108+
109+
return permissions
110+
}
111+
112+
export function ensurePermissions() {
113+
const permissions = getCachedPermissions()
114+
115+
if (permissions) {
116+
return Promise.resolve(permissions)
117+
}
118+
119+
debugPermissions('permissions cache missing, refetching user permissions')
120+
121+
if (!permissionsRequest) {
122+
permissionsRequest = getUserInfoByToken()
123+
.then((user) => {
124+
const nextPermissions = configUser(user || {})
125+
126+
debugPermissions('refetched user permissions', {
127+
hasUser: Boolean(user),
128+
permissionsCount: nextPermissions.length,
129+
userId: user?.id,
17130
})
18-
}
19-
})
20-
sessionStorage.setItem('tapdata_permissions', JSON.stringify(permissions))
131+
132+
return nextPermissions
133+
})
134+
.catch((error) => {
135+
debugPermissions('failed to refetch user permissions', {
136+
errorMessage: error?.message,
137+
status: error?.response?.status,
138+
})
139+
140+
throw error
141+
})
142+
.finally(() => {
143+
permissionsRequest = null
144+
})
21145
}
22-
return permissions
146+
147+
return permissionsRequest
23148
}
24149

25150
export function signOut() {
@@ -29,7 +154,7 @@ export function signOut() {
29154
Cookie.remove('isAdmin')
30155
Cookie.remove('user_id')
31156
sessionStorage.setItem('lastLocationHref', location.href)
32-
sessionStorage.removeItem('tapdata_permissions')
157+
clearPermissions()
33158
location.href = `${location.href.split('#')[0]}#/login`
34159
return null
35160
}

0 commit comments

Comments
 (0)