mirror of
https://github.com/supabase/supabase.git
synced 2026-10-18 23:59:08 +03:00
* chore: cleanup packages - Avoid circular imports - Export API-types as types - pg-format without depending on Node internal Buffer (not browser-compatible) - Avoid importing from barrel files in ui dir * chore: avoid barrel file imports in studio (#27771) * chore: avoid barrel file imports - Removes some unused imports - Avoids barrel file import for faster builds + less memory * add eslint rule * type fixes * delete layouts barrel * delete components/grid barrel file * delete components/grid/utils barrel file * delete components/grid/components/common barrel file * delete components/grid/components/editor barrel file * delete components/grid/components/formatter barrel file * delete components/grid/components/grid barrel file * delete components/grid/components/header/filter barrel file * remote components/grid/store barrel file * remove components/interfaces/Auth/Policies barrel file * delete components/interfaces/Settings/Logs barrel file * delete components/ui/CodeEditor barrel file * delete components/ui/Forms barrel file * delete components/ui/Shimmers barrel file * delete data/analytics barrel file * delete hooks barrel file * cleanup lib/common/fetch barrel file * final * barral files cleanup * global react-data-grid styles * remove console.log --------- Co-authored-by: Kevin Grüneberg <k.grueneberg1994@gmail.com> * fix build --------- Co-authored-by: Kevin Grüneberg <k.grueneberg1994@gmail.com>
209 lines
6.9 KiB
TypeScript
209 lines
6.9 KiB
TypeScript
import { useParams } from 'common'
|
|
import dayjs from 'dayjs'
|
|
import { useRouter } from 'next/router'
|
|
import { useEffect, useState } from 'react'
|
|
import toast from 'react-hot-toast'
|
|
|
|
import AuthorizeRequesterDetails from 'components/interfaces/Organization/OAuthApps/AuthorizeRequesterDetails'
|
|
import APIAuthorizationLayout from 'components/layouts/APIAuthorizationLayout'
|
|
import { FormPanel } from 'components/ui/Forms/FormPanel'
|
|
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
|
import { useApiAuthorizationApproveMutation } from 'data/api-authorization/api-authorization-approve-mutation'
|
|
import { useApiAuthorizationDeclineMutation } from 'data/api-authorization/api-authorization-decline-mutation'
|
|
import { useApiAuthorizationQuery } from 'data/api-authorization/api-authorization-query'
|
|
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
|
|
import { withAuth } from 'hooks/misc/withAuth'
|
|
import type { NextPageWithLayout } from 'types'
|
|
import { Alert, Button, Listbox } from 'ui'
|
|
|
|
// Need to handle if no organizations in account
|
|
// Need to handle if not logged in yet state
|
|
|
|
const APIAuthorizationPage: NextPageWithLayout = () => {
|
|
const router = useRouter()
|
|
const { auth_id } = useParams()
|
|
const [selectedOrgSlug, setSelectedOrgSlug] = useState<string>()
|
|
|
|
const { data: organizations, isLoading: isLoadingOrganizations } = useOrganizationsQuery()
|
|
const {
|
|
data: requester,
|
|
isLoading,
|
|
isError,
|
|
error,
|
|
} = useApiAuthorizationQuery({ id: auth_id, slug: selectedOrgSlug })
|
|
const isApproved = (requester?.approved_at ?? null) !== null
|
|
const isExpired = dayjs().isAfter(dayjs(requester?.expires_at))
|
|
|
|
const { mutate: approveRequest, isLoading: isApproving } = useApiAuthorizationApproveMutation({
|
|
onSuccess: (res) => {
|
|
window.location.href = res.url
|
|
},
|
|
})
|
|
const { mutate: declineRequest, isLoading: isDeclining } = useApiAuthorizationDeclineMutation({
|
|
onSuccess: () => {
|
|
toast.success('Declined API authorization request')
|
|
router.push('/projects')
|
|
},
|
|
})
|
|
const isSubmitting = isApproving || isDeclining
|
|
|
|
useEffect(() => {
|
|
if (!isLoadingOrganizations) {
|
|
setSelectedOrgSlug(organizations?.[0].slug ?? undefined)
|
|
}
|
|
}, [isLoadingOrganizations])
|
|
|
|
const onApproveRequest = async () => {
|
|
if (!auth_id) {
|
|
return toast.error('Unable to approve request: auth_id is missing ')
|
|
}
|
|
if (!selectedOrgSlug) {
|
|
return toast.error('Unable to approve request: No organization selected')
|
|
}
|
|
|
|
approveRequest({ id: auth_id, slug: selectedOrgSlug })
|
|
}
|
|
|
|
const onDeclineRequest = async () => {
|
|
if (!auth_id) {
|
|
return toast.error('Unable to decline request: auth_id is missing ')
|
|
}
|
|
if (!selectedOrgSlug) {
|
|
return toast.error('Unable to decline request: No organization selected')
|
|
}
|
|
|
|
declineRequest({ id: auth_id, slug: selectedOrgSlug })
|
|
}
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<FormPanel header={<p>Authorize API access</p>}>
|
|
<div className="w-[500px] px-8 py-6 space-y-2">
|
|
<ShimmeringLoader />
|
|
<ShimmeringLoader className="w-3/4" />
|
|
<ShimmeringLoader className="w-1/2" />
|
|
</div>
|
|
</FormPanel>
|
|
)
|
|
}
|
|
|
|
if (auth_id === undefined) {
|
|
return (
|
|
<FormPanel header={<p>Authorization for API access</p>}>
|
|
<div className="w-[500px] px-8 py-6">
|
|
<Alert withIcon variant="warning" title="Missing authorization ID">
|
|
Please provide a valid authorization ID in the URL
|
|
</Alert>
|
|
</div>
|
|
</FormPanel>
|
|
)
|
|
}
|
|
|
|
if (isError) {
|
|
return (
|
|
<FormPanel header={<p>Authorize API access</p>}>
|
|
<div className="w-[500px] px-8 py-6">
|
|
<Alert
|
|
withIcon
|
|
variant="warning"
|
|
title="Failed to fetch details for API authorization request"
|
|
>
|
|
<p>Please retry your authorization request from the requesting app</p>
|
|
{error !== undefined && <p className="mt-2">Error: {error?.message}</p>}
|
|
</Alert>
|
|
</div>
|
|
</FormPanel>
|
|
)
|
|
}
|
|
|
|
if (isApproved) {
|
|
const approvedOrganization = organizations?.find(
|
|
(org) => org.slug === requester.approved_organization_slug
|
|
)
|
|
|
|
return (
|
|
<FormPanel header={<p>Authorize API access for {requester?.name}</p>}>
|
|
<div className="w-full md:w-[500px] px-8 py-6 space-y-8">
|
|
<Alert withIcon variant="success" title="This authorization request has been approved">
|
|
<p>
|
|
{requester.name} has read and write access to the organization "
|
|
{approvedOrganization?.name ?? 'Unknown'}" and all of its projects
|
|
</p>
|
|
<p className="mt-2">
|
|
Approved on: {dayjs(requester.approved_at).format('DD MMM YYYY HH:mm:ss (ZZ)')}
|
|
</p>
|
|
</Alert>
|
|
</div>
|
|
</FormPanel>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<FormPanel
|
|
header={<p>Authorize API access for {requester?.name}</p>}
|
|
footer={
|
|
<div className="flex items-center justify-end py-4 px-8">
|
|
<div className="flex items-center space-x-2">
|
|
<Button type="default" disabled={isSubmitting || isExpired} onClick={onDeclineRequest}>
|
|
Decline
|
|
</Button>
|
|
<Button
|
|
loading={isSubmitting}
|
|
disabled={isSubmitting || isExpired}
|
|
onClick={onApproveRequest}
|
|
>
|
|
Authorize {requester?.name}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
}
|
|
>
|
|
<div className="w-full px-8 py-6 space-y-8">
|
|
{/* API Authorization requester details */}
|
|
<AuthorizeRequesterDetails
|
|
icon={requester.icon}
|
|
name={requester.name}
|
|
domain={requester.domain}
|
|
scopes={requester.scopes}
|
|
/>
|
|
|
|
{/* Expiry warning */}
|
|
{isExpired && (
|
|
<Alert withIcon variant="warning" title="This authorization request is expired">
|
|
Please retry your authorization request from the requesting app
|
|
</Alert>
|
|
)}
|
|
|
|
{/* Organization selection */}
|
|
{isLoadingOrganizations ? (
|
|
<div className="py-4 space-y-2">
|
|
<ShimmeringLoader />
|
|
<ShimmeringLoader className="w-3/4" />
|
|
</div>
|
|
) : (
|
|
<Listbox
|
|
label="Select an organization to grant API access to"
|
|
value={selectedOrgSlug}
|
|
disabled={isExpired}
|
|
onChange={setSelectedOrgSlug}
|
|
>
|
|
{(organizations ?? []).map((organization) => (
|
|
<Listbox.Option
|
|
key={organization.slug}
|
|
label={organization.name}
|
|
value={organization.slug}
|
|
>
|
|
{organization.name}
|
|
</Listbox.Option>
|
|
))}
|
|
</Listbox>
|
|
)}
|
|
</div>
|
|
</FormPanel>
|
|
)
|
|
}
|
|
|
|
APIAuthorizationPage.getLayout = (page) => <APIAuthorizationLayout>{page}</APIAuthorizationLayout>
|
|
|
|
export default withAuth(APIAuthorizationPage)
|