mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 18:05:11 +03:00
Merge pull request #13285 from supabase/master
[On Hold] Studio prod deploy
This commit is contained in:
20 files changed
+406
-44
No files matched your search
@@ -48,6 +48,7 @@ export const gettingstarted = {
|
||||
items: [
|
||||
{ name: 'Features', url: '/guides/getting-started/features' },
|
||||
{ name: 'Architecture', url: '/guides/getting-started/architecture' },
|
||||
{ name: 'Local Development', url: '/guides/cli/local-development' },
|
||||
{
|
||||
name: 'Framework Quickstarts',
|
||||
items: [
|
||||
|
||||
@@ -218,6 +218,12 @@ export const resources = [
|
||||
href: '/guides/getting-started/architecture',
|
||||
description: "An overview of Supabase's architecture and product principles.",
|
||||
},
|
||||
{
|
||||
title: 'Local Development',
|
||||
hasLightIcon: true,
|
||||
href: '/guides/cli/local-development',
|
||||
description: 'Use the Supabase CLI to develop locally and collaborate between teams.',
|
||||
},
|
||||
]
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
@@ -42,7 +42,7 @@ const data: CommunityItem[] = [
|
||||
},
|
||||
{
|
||||
title: 'Twitter',
|
||||
stat: '60,000+',
|
||||
stat: '64,000+',
|
||||
statLabel: 'Followers',
|
||||
img: 'twitter.png',
|
||||
detail: () => (
|
||||
|
||||
@@ -8,6 +8,7 @@ SUPABASE_URL=http://localhost:8000
|
||||
SUPABASE_PUBLIC_URL=http://localhost:8000
|
||||
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE
|
||||
SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q
|
||||
SENTRY_IGNORE_API_RESOLUTION_ERROR=1
|
||||
|
||||
# TODO: evaluate these variables at runtime instead
|
||||
# https://nextjs.org/docs/basic-features/environment-variables#loading-environment-variables
|
||||
|
||||
@@ -21,7 +21,7 @@ const ProjectList: FC<Props> = ({ rewriteHref }) => {
|
||||
const { organizations, projects } = app
|
||||
const { isLoading: isLoadingProjects } = projects
|
||||
|
||||
const { data: allOverdueInvoices } = useOverdueInvoicesQuery()
|
||||
const { data: allOverdueInvoices } = useOverdueInvoicesQuery({ enabled: IS_PLATFORM })
|
||||
|
||||
const isLoadingPermissions = IS_PLATFORM ? (ui?.permissions ?? []).length === 0 : false
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import dayjs from 'dayjs'
|
||||
import Link from 'next/link'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState } from 'react'
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import {
|
||||
Button,
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import { DatabaseUpgradeStatus } from '@supabase/shared-types/out/events'
|
||||
|
||||
import { useParams, useStore } from 'hooks'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { DATABASE_UPGRADE_MESSAGES } from './UpgradingState.constants'
|
||||
import { useProjectUpgradingStatusQuery } from 'data/config/project-upgrade-status-query'
|
||||
|
||||
@@ -24,10 +25,15 @@ const UpgradingState = () => {
|
||||
const { app, ui, meta } = useStore()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
const { data } = useProjectUpgradingStatusQuery({
|
||||
projectRef: ref,
|
||||
projectStatus: ui.selectedProject?.status,
|
||||
})
|
||||
const { data } = useProjectUpgradingStatusQuery(
|
||||
{
|
||||
projectRef: ref,
|
||||
projectStatus: ui.selectedProject?.status,
|
||||
},
|
||||
{
|
||||
enabled: IS_PLATFORM,
|
||||
}
|
||||
)
|
||||
|
||||
const project = ui.selectedProject
|
||||
const { initiated_at, status, progress, target_version, error } =
|
||||
@@ -51,12 +57,12 @@ const UpgradingState = () => {
|
||||
const message = `Upgrade information:%0A• Initiated at: ${initiated_at}%0A• Target Version: ${target_version}%0A• Error: ${error}`
|
||||
|
||||
return (
|
||||
<div className="mx-auto my-16 w-full max-w-7xl space-y-16">
|
||||
<div className="w-full mx-auto my-16 space-y-16 max-w-7xl">
|
||||
<div className="mx-6 space-y-16">
|
||||
<div className="flex flex-col space-y-4 lg:flex-row lg:items-center lg:space-y-0 lg:space-x-6">
|
||||
<h1 className="text-3xl">{project?.name}</h1>
|
||||
</div>
|
||||
<div className="mx-auto mt-8 mb-16 w-full max-w-7xl">
|
||||
<div className="w-full mx-auto mt-8 mb-16 max-w-7xl">
|
||||
<div className="flex h-[500px] items-center justify-center rounded border border-scale-400 bg-scale-300 p-8">
|
||||
{isCompleted ? (
|
||||
<div className="grid gap-4">
|
||||
@@ -104,14 +110,14 @@ const UpgradingState = () => {
|
||||
) : (
|
||||
<div className="grid w-[480px] gap-4">
|
||||
<div className="relative mx-auto max-w-[300px]">
|
||||
<div className="absolute flex h-full w-full items-center justify-center">
|
||||
<div className="absolute flex items-center justify-center w-full h-full">
|
||||
<IconSettings className="animate-spin" size={20} strokeWidth={2} />
|
||||
</div>
|
||||
<IconCircle className="text-scale-900" size={50} strokeWidth={1.5} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-center">Upgrading in progress</p>
|
||||
<p className="text-center text-sm text-scale-1100">
|
||||
<p className="text-sm text-center text-scale-1100">
|
||||
Upgrades can take from a few minutes up to several hours depending on the size
|
||||
of your database. Your project will be offline while it is being upgraded.
|
||||
</p>
|
||||
@@ -124,14 +130,14 @@ const UpgradingState = () => {
|
||||
<IconMinimize2
|
||||
size="tiny"
|
||||
strokeWidth={2}
|
||||
className="absolute top-3 right-3 cursor-pointer z-10"
|
||||
className="absolute z-10 cursor-pointer top-3 right-3"
|
||||
onClick={() => setIsExpanded(false)}
|
||||
/>
|
||||
) : (
|
||||
<IconMaximize2
|
||||
size="tiny"
|
||||
strokeWidth={2}
|
||||
className="absolute top-3 right-3 cursor-pointer z-10"
|
||||
className="absolute z-10 cursor-pointer top-3 right-3"
|
||||
onClick={() => setIsExpanded(true)}
|
||||
/>
|
||||
)}
|
||||
@@ -155,7 +161,7 @@ const UpgradingState = () => {
|
||||
return (
|
||||
<div key={message.key} className="flex items-center space-x-4">
|
||||
{isCurrent ? (
|
||||
<div className="h-5 w-5 flex items-center justify-center rounded-full">
|
||||
<div className="flex items-center justify-center w-5 h-5 rounded-full">
|
||||
<IconLoader
|
||||
size={20}
|
||||
className="animate-spin text-scale-1100"
|
||||
@@ -163,11 +169,11 @@ const UpgradingState = () => {
|
||||
/>
|
||||
</div>
|
||||
) : isCompleted ? (
|
||||
<div className="h-5 w-5 flex items-center justify-center rounded-full border bg-brand-800 border-brand-700">
|
||||
<div className="flex items-center justify-center w-5 h-5 border rounded-full bg-brand-800 border-brand-700">
|
||||
<IconCheck size={12} className="text-white" strokeWidth={2} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-5 w-5 flex items-center justify-center border rounded-full bg-scale-600" />
|
||||
<div className="flex items-center justify-center w-5 h-5 border rounded-full bg-scale-600" />
|
||||
)}
|
||||
<p
|
||||
className={`text-sm ${
|
||||
@@ -193,7 +199,7 @@ const UpgradingState = () => {
|
||||
{initiated_at !== undefined && (
|
||||
<Tooltip.Root delayDuration={0}>
|
||||
<Tooltip.Trigger className="w-full">
|
||||
<p className="text-center text-sm text-scale-1000">
|
||||
<p className="text-sm text-center text-scale-1000">
|
||||
Started on: {initiatedAtUTC} (UTC)
|
||||
</p>
|
||||
</Tooltip.Trigger>
|
||||
|
||||
@@ -3,6 +3,7 @@ import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import { Alert, Button, IconX } from 'ui'
|
||||
import { useParams } from 'hooks'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { useProjectUpgradingStatusQuery } from 'data/config/project-upgrade-status-query'
|
||||
import { DatabaseUpgradeStatus } from '@supabase/shared-types/out/events'
|
||||
|
||||
@@ -10,7 +11,7 @@ import { DatabaseUpgradeStatus } from '@supabase/shared-types/out/events'
|
||||
|
||||
const ProjectUpgradeFailedBanner = () => {
|
||||
const { ref } = useParams()
|
||||
const { data } = useProjectUpgradingStatusQuery({ projectRef: ref })
|
||||
const { data } = useProjectUpgradingStatusQuery({ projectRef: ref }, { enabled: IS_PLATFORM })
|
||||
const { target_version, status, initiated_at, error } = data?.databaseUpgradeStatus ?? {}
|
||||
|
||||
const key = `supabase-upgrade-${ref}-${initiated_at}`
|
||||
@@ -39,7 +40,7 @@ const ProjectUpgradeFailedBanner = () => {
|
||||
variant={'warning'}
|
||||
title={`Postgres version upgrade to ${target_version} was not successful (Initiated at ${initiatedAtUTC} UTC)`}
|
||||
actions={
|
||||
<div className="flex h-full items-center space-x-4">
|
||||
<div className="flex items-center h-full space-x-4">
|
||||
<Link
|
||||
href={`/support/new?category=Database_unresponsive&ref=${ref}&subject=${subject}&message=${message}`}
|
||||
>
|
||||
|
||||
@@ -1256,7 +1256,7 @@ class StorageExplorerStore {
|
||||
path: prefix,
|
||||
options,
|
||||
})
|
||||
if (res.error) console.error('Error at fetchFoldersByPath:', error)
|
||||
if (res.error) console.error('Error at fetchFoldersByPath:', res.error)
|
||||
return res
|
||||
})
|
||||
)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
case 'GET':
|
||||
return handleGet(req, res)
|
||||
case 'HEAD':
|
||||
return handleHead(req, res)
|
||||
default:
|
||||
res.setHeader('Allow', ['GET', 'HEAD'])
|
||||
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
|
||||
}
|
||||
}
|
||||
|
||||
const handleGet = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const response = await fetch(`${process.env.SUPABASE_URL}/rest/v1/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
apikey: process.env.SUPABASE_SERVICE_KEY!,
|
||||
},
|
||||
})
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
|
||||
return res.status(500).json({ error: { message: 'Internal Server Error' } })
|
||||
}
|
||||
|
||||
const handleHead = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
res.status(200).end()
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
case 'POST':
|
||||
return handlePost(req, res)
|
||||
default:
|
||||
res.setHeader('Allow', ['POST'])
|
||||
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
|
||||
}
|
||||
}
|
||||
|
||||
const handlePost = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { id } = req.query
|
||||
|
||||
const { data, error } = await supabase.storage.emptyBucket(id as string)
|
||||
if (error) {
|
||||
return res.status(400).json({ error: { message: error.message } })
|
||||
}
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
case 'PATCH':
|
||||
return handlePatch(req, res)
|
||||
case 'DELETE':
|
||||
return handleDelete(req, res)
|
||||
default:
|
||||
res.setHeader('Allow', ['PATCH', 'DELETE'])
|
||||
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
|
||||
}
|
||||
}
|
||||
|
||||
const handlePatch = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { id } = req.query
|
||||
const { public: isPublicBucket } = req.body
|
||||
|
||||
const { data, error } = await supabase.storage.updateBucket(id as string, {
|
||||
public: isPublicBucket,
|
||||
})
|
||||
if (error) {
|
||||
return res.status(400).json({ error: { message: error.message } })
|
||||
}
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
|
||||
const handleDelete = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { id } = req.query
|
||||
|
||||
const { data, error } = await supabase.storage.deleteBucket(id as string)
|
||||
if (error) {
|
||||
return res.status(400).json({ error: { message: error.message } })
|
||||
}
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
case 'POST':
|
||||
return handlePost(req, res)
|
||||
default:
|
||||
res.setHeader('Allow', ['POST'])
|
||||
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
|
||||
}
|
||||
}
|
||||
|
||||
const handlePost = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { id } = req.query
|
||||
const { path } = req.body
|
||||
|
||||
const { data, error } = await supabase.storage.from(id as string).download(path)
|
||||
if (error) {
|
||||
return res.status(400).json({ error: { message: error.message } })
|
||||
}
|
||||
|
||||
return res
|
||||
.status(200)
|
||||
.setHeader('Content-Type', 'application/octet-stream')
|
||||
.send(Buffer.from(await data.arrayBuffer()))
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
case 'DELETE':
|
||||
return handleDelete(req, res)
|
||||
default:
|
||||
res.setHeader('Allow', ['DELETE'])
|
||||
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { id } = req.query
|
||||
const { paths } = req.body
|
||||
|
||||
const { data, error } = await supabase.storage.from(id as string).remove(paths as string[])
|
||||
if (error) {
|
||||
return res.status(400).json({ error: { message: error.message } })
|
||||
}
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
case 'POST':
|
||||
return handlePost(req, res)
|
||||
|
||||
default:
|
||||
res.setHeader('Allow', ['POST'])
|
||||
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
|
||||
}
|
||||
}
|
||||
|
||||
const handlePost = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { id } = req.query
|
||||
const { path, ...params } = req.body
|
||||
|
||||
const { data, error } = await supabase.storage.from(id as string).list(path, params)
|
||||
if (error) {
|
||||
return res.status(500).json({ error: error.message })
|
||||
}
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
case 'POST':
|
||||
return handlePost(req, res)
|
||||
default:
|
||||
res.setHeader('Allow', ['POST'])
|
||||
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
|
||||
}
|
||||
}
|
||||
|
||||
const handlePost = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { id } = req.query
|
||||
const { from, to } = req.body
|
||||
|
||||
const { data, error } = await supabase.storage.from(id as string).move(from, to)
|
||||
if (error) {
|
||||
return res.status(400).json({ error: { message: error.message } })
|
||||
}
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
case 'POST':
|
||||
return handlePost(req, res)
|
||||
default:
|
||||
res.setHeader('Allow', ['POST'])
|
||||
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
|
||||
}
|
||||
}
|
||||
|
||||
const handlePost = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { id } = req.query
|
||||
const { path } = req.body
|
||||
|
||||
const { data } = supabase.storage.from(id as string).getPublicUrl(path)
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
case 'POST':
|
||||
return handlePost(req, res)
|
||||
default:
|
||||
res.setHeader('Allow', ['POST'])
|
||||
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
|
||||
}
|
||||
}
|
||||
|
||||
const handlePost = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { id } = req.query
|
||||
const { path, expiresIn = 60 * 60 * 24 } = req.body
|
||||
|
||||
const { data, error } = await supabase.storage.from(id as string).createSignedUrl(path, expiresIn)
|
||||
if (error) {
|
||||
return res.status(400).json({ error: { message: error.message } })
|
||||
}
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
case 'GET':
|
||||
return handleGet(req, res)
|
||||
case 'POST':
|
||||
return handlePost(req, res)
|
||||
|
||||
default:
|
||||
res.setHeader('Allow', ['GET', 'POST'])
|
||||
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
|
||||
}
|
||||
}
|
||||
|
||||
const handleGet = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { data, error } = await supabase.storage.listBuckets()
|
||||
if (error) {
|
||||
return res.status(500).json({ error: { message: 'Internal Server Error' } })
|
||||
}
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
|
||||
const handlePost = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { id, public: isPublicBucket } = req.body
|
||||
|
||||
const { data, error } = await supabase.storage.createBucket(id, { public: isPublicBucket })
|
||||
if (error) {
|
||||
return res.status(400).json({ error: { message: error.message } })
|
||||
}
|
||||
|
||||
return res.status(200).json(data)
|
||||
}
|
||||
@@ -41,26 +41,30 @@ const Home: NextPageWithLayout = () => {
|
||||
{IS_PLATFORM && project?.status !== PROJECT_STATUS.INACTIVE && <ProjectUsageSection />}
|
||||
</div>
|
||||
|
||||
<div className="space-y-8">
|
||||
<div className="mx-6">
|
||||
<h4 className="text-lg">Client libraries</h4>
|
||||
</div>
|
||||
<div className="mx-6 mb-12 grid gap-12 md:grid-cols-3">
|
||||
{CLIENT_LIBRARIES.map((library) => (
|
||||
<ClientLibrary key={library.language} {...library} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-8">
|
||||
<div className="mx-6">
|
||||
<h4 className="text-lg">Example projects</h4>
|
||||
</div>
|
||||
<div className="mx-6 grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
{EXAMPLE_PROJECTS.sort((a, b) => a.title.localeCompare(b.title)).map((project) => (
|
||||
<ExampleProject key={project.url} {...project} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{project?.status !== PROJECT_STATUS.INACTIVE && (
|
||||
<>
|
||||
<div className="space-y-8">
|
||||
<div className="mx-6">
|
||||
<h4 className="text-lg">Client libraries</h4>
|
||||
</div>
|
||||
<div className="mx-6 mb-12 grid gap-12 md:grid-cols-3">
|
||||
{CLIENT_LIBRARIES.map((library) => (
|
||||
<ClientLibrary key={library.language} {...library} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-8">
|
||||
<div className="mx-6">
|
||||
<h4 className="text-lg">Example projects</h4>
|
||||
</div>
|
||||
<div className="mx-6 grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
{EXAMPLE_PROJECTS.sort((a, b) => a.title.localeCompare(b.title)).map((project) => (
|
||||
<ExampleProject key={project.url} {...project} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ export default class PostgresMetaInterface<T> implements IPostgresMetaInterface<
|
||||
state = this.STATES.INITIAL
|
||||
data: { [key in DataKeys]: T } = {}
|
||||
headers: any = {}
|
||||
isInitialized: boolean = false
|
||||
|
||||
constructor(
|
||||
rootStore: IRootStore,
|
||||
@@ -68,6 +69,7 @@ export default class PostgresMetaInterface<T> implements IPostgresMetaInterface<
|
||||
count: computed,
|
||||
hasError: computed,
|
||||
isLoading: computed,
|
||||
isInitialized: observable,
|
||||
load: action,
|
||||
create: action,
|
||||
update: action,
|
||||
@@ -90,10 +92,6 @@ export default class PostgresMetaInterface<T> implements IPostgresMetaInterface<
|
||||
return this.state === this.STATES.INITIAL || this.state === this.STATES.LOADING
|
||||
}
|
||||
|
||||
get isInitialized() {
|
||||
return this.state === this.STATES.LOADED || this.state === this.STATES.ERROR
|
||||
}
|
||||
|
||||
async fetchData() {
|
||||
const headers = { 'Content-Type': 'application/json', ...this.headers }
|
||||
const response = await get<T[]>(this.url, { headers })
|
||||
@@ -114,6 +112,8 @@ export default class PostgresMetaInterface<T> implements IPostgresMetaInterface<
|
||||
console.error('Load error message', e.message)
|
||||
this.setError(e)
|
||||
this.setState(ERROR)
|
||||
} finally {
|
||||
if (!this.isInitialized) this.isInitialized = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user