AccessToken.utils.ts149 lines · main
1import dayjs from 'dayjs'
2
3import { PERMISSION_LIST, ScopedAccessTokenPermission } from './AccessToken.constants'
4import {
5 AccessTokenSort,
6 AccessTokenSortColumn,
7 AccessTokenSortOrder,
8 BaseToken,
9} from './AccessToken.types'
10
11export const handleSortChange = (
12 currentSort: AccessTokenSort,
13 column: AccessTokenSortColumn,
14 setSort: (sort: AccessTokenSort) => void
15) => {
16 const [currentCol, currentOrder] = currentSort.split(':') as [
17 AccessTokenSortColumn,
18 AccessTokenSortOrder,
19 ]
20 if (currentCol === column) {
21 if (currentOrder === 'asc') {
22 setSort(`${column}:desc` as AccessTokenSort)
23 } else {
24 setSort('created_at:desc')
25 }
26 } else {
27 setSort(`${column}:asc` as AccessTokenSort)
28 }
29}
30
31export const filterAndSortTokens = <T extends BaseToken>(
32 tokens: T[] | undefined,
33 searchString: string,
34 sort: AccessTokenSort
35): T[] | undefined => {
36 const filtered = !searchString
37 ? tokens
38 : tokens?.filter((token) => token.name.toLowerCase().includes(searchString.toLowerCase()))
39
40 if (!filtered) return filtered
41
42 const [sortCol, sortOrder] = sort.split(':') as [AccessTokenSortColumn, AccessTokenSortOrder]
43 const orderMultiplier = sortOrder === 'asc' ? 1 : -1
44
45 return [...filtered].sort((a, b) => {
46 if (sortCol === 'created_at') {
47 return (new Date(a.created_at).getTime() - new Date(b.created_at).getTime()) * orderMultiplier
48 }
49 if (sortCol === 'last_used_at') {
50 if (!a.last_used_at && !b.last_used_at) return 0
51 if (!a.last_used_at) return 1
52 if (!b.last_used_at) return -1
53 return (
54 (new Date(a.last_used_at).getTime() - new Date(b.last_used_at).getTime()) * orderMultiplier
55 )
56 }
57 if (sortCol === 'expires_at') {
58 if (!a.expires_at && !b.expires_at) return 0
59 if (!a.expires_at) return 1
60 if (!b.expires_at) return -1
61 return (new Date(a.expires_at).getTime() - new Date(b.expires_at).getTime()) * orderMultiplier
62 }
63 return 0
64 })
65}
66
67export const mapPermissionToFGA = (
68 resourceKey: string,
69 action: string
70): ScopedAccessTokenPermission[] => {
71 const [scope, resource] = resourceKey.split(':')
72 const match = PERMISSION_LIST.find(
73 (p) => p.scope === scope && p.resource === resource && p.action === action
74 )
75 return match ? [match.id as ScopedAccessTokenPermission] : []
76}
77
78// [kemal]: Not sure how efficient this will be, but it should get permissions from shared types and transform them whenever @supabase/shared-types updates.
79export const getResourcePermissions = (
80 resourceKey: string
81): Record<string, ScopedAccessTokenPermission[]> => {
82 const [scope, resource] = resourceKey.split(':')
83 const result: Record<string, ScopedAccessTokenPermission[]> = { 'no access': [] }
84
85 PERMISSION_LIST.filter((p) => p.scope === scope && p.resource === resource).forEach((p) => {
86 result[p.action] = [p.id as ScopedAccessTokenPermission]
87 })
88
89 if (result['read'] && result['write']) {
90 result['read-write'] = [...result['read'], ...result['write']]
91 }
92
93 return result
94}
95
96export const getRealAccess = (resource: string, tokenPermissions: string[]) => {
97 const resourcePermissions = getResourcePermissions(resource)
98 const actionTypes = ['read', 'write', 'create', 'delete'] as const
99 const grantedActions = actionTypes.filter((action) =>
100 resourcePermissions[action]?.some((p) => tokenPermissions.includes(p))
101 )
102
103 if (grantedActions.length === 0) {
104 return 'no access'
105 }
106
107 if (grantedActions.length === 1) {
108 return grantedActions[0]
109 }
110
111 if (
112 grantedActions.length === 2 &&
113 grantedActions[0] === 'read' &&
114 grantedActions[1] === 'write'
115 ) {
116 return 'read-write'
117 }
118
119 return grantedActions.join('-')
120}
121
122export const formatAccessText = (action: string): string => {
123 switch (action) {
124 case 'no access':
125 return 'No access'
126 default:
127 return action
128 .split('-')
129 .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
130 .join('-')
131 }
132}
133
134export const getExpirationDate = (key: string): string | undefined => {
135 switch (key) {
136 case 'hour':
137 return dayjs().add(1, 'hours').toISOString()
138 case 'day':
139 return dayjs().add(1, 'day').toISOString()
140 case 'week':
141 return dayjs().add(7, 'days').toISOString()
142 case 'month':
143 return dayjs().add(30, 'days').toISOString()
144 case 'never':
145 return undefined
146 default:
147 return undefined
148 }
149}