mirror of
https://github.com/supabase/supabase.git
synced 2026-06-14 14:08:31 +08:00
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
150 lines
4.3 KiB
TypeScript
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
|
|
}
|
|
}
|