Files
supabase/apps/studio/components/interfaces/Account/AccessTokens/AccessToken.utils.ts
2026-04-01 10:22:37 +02:00

150 lines
4.3 KiB
TypeScript

import dayjs from 'dayjs'
import { PERMISSION_LIST, ScopedAccessTokenPermission } from './AccessToken.constants'
import {
AccessTokenSort,
AccessTokenSortColumn,
AccessTokenSortOrder,
BaseToken,
} from './AccessToken.types'
export const handleSortChange = (
currentSort: AccessTokenSort,
column: AccessTokenSortColumn,
setSort: (sort: AccessTokenSort) => void
) => {
const [currentCol, currentOrder] = currentSort.split(':') as [
AccessTokenSortColumn,
AccessTokenSortOrder,
]
if (currentCol === column) {
if (currentOrder === 'asc') {
setSort(`${column}:desc` as AccessTokenSort)
} else {
setSort('created_at:desc')
}
} else {
setSort(`${column}:asc` as AccessTokenSort)
}
}
export const filterAndSortTokens = <T extends BaseToken>(
tokens: T[] | undefined,
searchString: string,
sort: AccessTokenSort
): T[] | undefined => {
const filtered = !searchString
? tokens
: tokens?.filter((token) => token.name.toLowerCase().includes(searchString.toLowerCase()))
if (!filtered) return filtered
const [sortCol, sortOrder] = sort.split(':') as [AccessTokenSortColumn, AccessTokenSortOrder]
const orderMultiplier = sortOrder === 'asc' ? 1 : -1
return [...filtered].sort((a, b) => {
if (sortCol === 'created_at') {
return (new Date(a.created_at).getTime() - new Date(b.created_at).getTime()) * orderMultiplier
}
if (sortCol === 'last_used_at') {
if (!a.last_used_at && !b.last_used_at) return 0
if (!a.last_used_at) return 1
if (!b.last_used_at) return -1
return (
(new Date(a.last_used_at).getTime() - new Date(b.last_used_at).getTime()) * orderMultiplier
)
}
if (sortCol === 'expires_at') {
if (!a.expires_at && !b.expires_at) return 0
if (!a.expires_at) return 1
if (!b.expires_at) return -1
return (new Date(a.expires_at).getTime() - new Date(b.expires_at).getTime()) * orderMultiplier
}
return 0
})
}
export const mapPermissionToFGA = (
resourceKey: string,
action: string
): ScopedAccessTokenPermission[] => {
const [scope, resource] = resourceKey.split(':')
const match = PERMISSION_LIST.find(
(p) => p.scope === scope && p.resource === resource && p.action === action
)
return match ? [match.id as ScopedAccessTokenPermission] : []
}
// [kemal]: Not sure how efficient this will be, but it should get permissions from shared types and transform them whenever @supabase/shared-types updates.
export const getResourcePermissions = (
resourceKey: string
): Record<string, ScopedAccessTokenPermission[]> => {
const [scope, resource] = resourceKey.split(':')
const result: Record<string, ScopedAccessTokenPermission[]> = { 'no access': [] }
PERMISSION_LIST.filter((p) => p.scope === scope && p.resource === resource).forEach((p) => {
result[p.action] = [p.id as ScopedAccessTokenPermission]
})
if (result['read'] && result['write']) {
result['read-write'] = [...result['read'], ...result['write']]
}
return result
}
export const getRealAccess = (resource: string, tokenPermissions: string[]) => {
const resourcePermissions = getResourcePermissions(resource)
const actionTypes = ['read', 'write', 'create', 'delete'] as const
const grantedActions = actionTypes.filter((action) =>
resourcePermissions[action]?.some((p) => tokenPermissions.includes(p))
)
if (grantedActions.length === 0) {
return 'no access'
}
if (grantedActions.length === 1) {
return grantedActions[0]
}
if (
grantedActions.length === 2 &&
grantedActions[0] === 'read' &&
grantedActions[1] === 'write'
) {
return 'read-write'
}
return grantedActions.join('-')
}
export const formatAccessText = (action: string): string => {
switch (action) {
case 'no access':
return 'No access'
default:
return action
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join('-')
}
}
export const getExpirationDate = (key: string): string | undefined => {
switch (key) {
case 'hour':
return dayjs().add(1, 'hours').toISOString()
case 'day':
return dayjs().add(1, 'day').toISOString()
case 'week':
return dayjs().add(7, 'days').toISOString()
case 'month':
return dayjs().add(30, 'days').toISOString()
case 'never':
return undefined
default:
return undefined
}
}