From baf6da89ad8128bd0a9224d4b6d28b26486ff210 Mon Sep 17 00:00:00 2001 From: Alaister Young Date: Thu, 16 Mar 2023 12:24:05 +0800 Subject: [PATCH 01/10] fix: add new self hosted endpoints --- studio/.env | 1 + .../UpgradingState/UpgradingState.tsx | 36 +++++++++------ .../ui/ProjectUpgradeFailedBanner.tsx | 5 +- .../storageExplorer/StorageExplorerStore.js | 2 +- studio/pages/api/projects/[ref]/api/rest.ts | 38 +++++++++++++++ .../api/storage/[ref]/buckets/[id]/empty.ts | 30 ++++++++++++ .../api/storage/[ref]/buckets/[id]/index.ts | 46 +++++++++++++++++++ .../[ref]/buckets/[id]/objects/download.ts | 34 ++++++++++++++ .../[ref]/buckets/[id]/objects/index.ts | 31 +++++++++++++ .../[ref]/buckets/[id]/objects/list.ts | 32 +++++++++++++ .../[ref]/buckets/[id]/objects/move.ts | 31 +++++++++++++ .../[ref]/buckets/[id]/objects/public-url.ts | 28 +++++++++++ .../[ref]/buckets/[id]/objects/sign.ts | 31 +++++++++++++ .../pages/api/storage/[ref]/buckets/index.ts | 42 +++++++++++++++++ 14 files changed, 369 insertions(+), 18 deletions(-) create mode 100644 studio/pages/api/projects/[ref]/api/rest.ts create mode 100644 studio/pages/api/storage/[ref]/buckets/[id]/empty.ts create mode 100644 studio/pages/api/storage/[ref]/buckets/[id]/index.ts create mode 100644 studio/pages/api/storage/[ref]/buckets/[id]/objects/download.ts create mode 100644 studio/pages/api/storage/[ref]/buckets/[id]/objects/index.ts create mode 100644 studio/pages/api/storage/[ref]/buckets/[id]/objects/list.ts create mode 100644 studio/pages/api/storage/[ref]/buckets/[id]/objects/move.ts create mode 100644 studio/pages/api/storage/[ref]/buckets/[id]/objects/public-url.ts create mode 100644 studio/pages/api/storage/[ref]/buckets/[id]/objects/sign.ts create mode 100644 studio/pages/api/storage/[ref]/buckets/index.ts diff --git a/studio/.env b/studio/.env index e66cc5523ed..68180030ee1 100644 --- a/studio/.env +++ b/studio/.env @@ -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 diff --git a/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx b/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx index ddee8e564a7..8413e9a37cf 100644 --- a/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx +++ b/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx @@ -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 ( -
+

{project?.name}

-
+
{isCompleted ? (
@@ -104,14 +110,14 @@ const UpgradingState = () => { ) : (
-
+

Upgrading in progress

-

+

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.

@@ -124,14 +130,14 @@ const UpgradingState = () => { setIsExpanded(false)} /> ) : ( setIsExpanded(true)} /> )} @@ -155,7 +161,7 @@ const UpgradingState = () => { return (
{isCurrent ? ( -
+
{ />
) : isCompleted ? ( -
+
) : ( -
+
)}

{ {initiated_at !== undefined && ( -

+

Started on: {initiatedAtUTC} (UTC)

diff --git a/studio/components/ui/ProjectUpgradeFailedBanner.tsx b/studio/components/ui/ProjectUpgradeFailedBanner.tsx index 5e23e6634e2..19bab87189a 100644 --- a/studio/components/ui/ProjectUpgradeFailedBanner.tsx +++ b/studio/components/ui/ProjectUpgradeFailedBanner.tsx @@ -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={ -
+
diff --git a/studio/localStores/storageExplorer/StorageExplorerStore.js b/studio/localStores/storageExplorer/StorageExplorerStore.js index 57533c66f99..d35a985f3d4 100644 --- a/studio/localStores/storageExplorer/StorageExplorerStore.js +++ b/studio/localStores/storageExplorer/StorageExplorerStore.js @@ -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 }) ) diff --git a/studio/pages/api/projects/[ref]/api/rest.ts b/studio/pages/api/projects/[ref]/api/rest.ts new file mode 100644 index 00000000000..d4c2a519ac3 --- /dev/null +++ b/studio/pages/api/projects/[ref]/api/rest.ts @@ -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() +} diff --git a/studio/pages/api/storage/[ref]/buckets/[id]/empty.ts b/studio/pages/api/storage/[ref]/buckets/[id]/empty.ts new file mode 100644 index 00000000000..9b162f6a637 --- /dev/null +++ b/studio/pages/api/storage/[ref]/buckets/[id]/empty.ts @@ -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) +} diff --git a/studio/pages/api/storage/[ref]/buckets/[id]/index.ts b/studio/pages/api/storage/[ref]/buckets/[id]/index.ts new file mode 100644 index 00000000000..72af258757b --- /dev/null +++ b/studio/pages/api/storage/[ref]/buckets/[id]/index.ts @@ -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) +} diff --git a/studio/pages/api/storage/[ref]/buckets/[id]/objects/download.ts b/studio/pages/api/storage/[ref]/buckets/[id]/objects/download.ts new file mode 100644 index 00000000000..fa51a404ed0 --- /dev/null +++ b/studio/pages/api/storage/[ref]/buckets/[id]/objects/download.ts @@ -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())) +} diff --git a/studio/pages/api/storage/[ref]/buckets/[id]/objects/index.ts b/studio/pages/api/storage/[ref]/buckets/[id]/objects/index.ts new file mode 100644 index 00000000000..d80425dd173 --- /dev/null +++ b/studio/pages/api/storage/[ref]/buckets/[id]/objects/index.ts @@ -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) +} diff --git a/studio/pages/api/storage/[ref]/buckets/[id]/objects/list.ts b/studio/pages/api/storage/[ref]/buckets/[id]/objects/list.ts new file mode 100644 index 00000000000..d4dc551668b --- /dev/null +++ b/studio/pages/api/storage/[ref]/buckets/[id]/objects/list.ts @@ -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) +} diff --git a/studio/pages/api/storage/[ref]/buckets/[id]/objects/move.ts b/studio/pages/api/storage/[ref]/buckets/[id]/objects/move.ts new file mode 100644 index 00000000000..d4c8b5485f0 --- /dev/null +++ b/studio/pages/api/storage/[ref]/buckets/[id]/objects/move.ts @@ -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) +} diff --git a/studio/pages/api/storage/[ref]/buckets/[id]/objects/public-url.ts b/studio/pages/api/storage/[ref]/buckets/[id]/objects/public-url.ts new file mode 100644 index 00000000000..41efb4e8107 --- /dev/null +++ b/studio/pages/api/storage/[ref]/buckets/[id]/objects/public-url.ts @@ -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) +} diff --git a/studio/pages/api/storage/[ref]/buckets/[id]/objects/sign.ts b/studio/pages/api/storage/[ref]/buckets/[id]/objects/sign.ts new file mode 100644 index 00000000000..8677334f21b --- /dev/null +++ b/studio/pages/api/storage/[ref]/buckets/[id]/objects/sign.ts @@ -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) +} diff --git a/studio/pages/api/storage/[ref]/buckets/index.ts b/studio/pages/api/storage/[ref]/buckets/index.ts new file mode 100644 index 00000000000..e426db1810c --- /dev/null +++ b/studio/pages/api/storage/[ref]/buckets/index.ts @@ -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) +} From c10e4d392bb546be6a7a8c39d3e88ddb1fb466a7 Mon Sep 17 00:00:00 2001 From: Isaiah Hamilton Date: Tue, 28 Mar 2023 18:14:37 -0400 Subject: [PATCH 02/10] hide example projects inactive --- studio/pages/project/[ref]/index.tsx | 44 +++++++++++++++------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/studio/pages/project/[ref]/index.tsx b/studio/pages/project/[ref]/index.tsx index 38d0c25de3e..b6bd9ded124 100644 --- a/studio/pages/project/[ref]/index.tsx +++ b/studio/pages/project/[ref]/index.tsx @@ -41,26 +41,30 @@ const Home: NextPageWithLayout = () => { {IS_PLATFORM && project?.status !== PROJECT_STATUS.INACTIVE && }
-
-
-

Client libraries

-
-
- {CLIENT_LIBRARIES.map((library) => ( - - ))} -
-
-
-
-

Example projects

-
-
- {EXAMPLE_PROJECTS.sort((a, b) => a.title.localeCompare(b.title)).map((project) => ( - - ))} -
-
+ {project?.status !== PROJECT_STATUS.INACTIVE && ( + <> +
+
+

Client libraries

+
+
+ {CLIENT_LIBRARIES.map((library) => ( + + ))} +
+
+
+
+

Example projects

+
+
+ {EXAMPLE_PROJECTS.sort((a, b) => a.title.localeCompare(b.title)).map((project) => ( + + ))} +
+
+ + )}
) } From 7f054586bb9072fad20318355727dbbac742815b Mon Sep 17 00:00:00 2001 From: Isaiah Hamilton Date: Tue, 28 Mar 2023 18:46:08 -0400 Subject: [PATCH 03/10] update community number --- apps/www/data/Community.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/data/Community.tsx b/apps/www/data/Community.tsx index 89013d080fe..95c35119b19 100644 --- a/apps/www/data/Community.tsx +++ b/apps/www/data/Community.tsx @@ -42,7 +42,7 @@ const data: CommunityItem[] = [ }, { title: 'Twitter', - stat: '60,000+', + stat: '64,000+', statLabel: 'Followers', img: 'twitter.png', detail: () => ( From 1c2a01d377229795a575fc8ad2aad83da03cbf1a Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Wed, 29 Mar 2023 11:41:32 +0800 Subject: [PATCH 04/10] chore: add local dev guides to getting started for visibility --- .../Navigation/NavigationMenu/NavigationMenu.constants.ts | 1 + apps/docs/pages/guides/getting-started.mdx | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 366153f7b8e..971acd0c1bf 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -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: [ diff --git a/apps/docs/pages/guides/getting-started.mdx b/apps/docs/pages/guides/getting-started.mdx index 820eae29e0c..bb335915b34 100644 --- a/apps/docs/pages/guides/getting-started.mdx +++ b/apps/docs/pages/guides/getting-started.mdx @@ -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 }) => From de02bbcbc4350860db9142c760e0f9153ddbedd3 Mon Sep 17 00:00:00 2001 From: Alaister Young Date: Wed, 29 Mar 2023 16:41:09 +1100 Subject: [PATCH 05/10] only check for overdue invoices on platform --- studio/components/interfaces/Home/ProjectList/ProjectList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx index 5a15174d37f..c973699deeb 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx @@ -21,7 +21,7 @@ const ProjectList: FC = ({ 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 From d59168190132c1fe9dc04b576fe77c9cf1d3f1cc Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 29 Mar 2023 15:24:31 +0800 Subject: [PATCH 06/10] Fix is initialized logic in postgresmeta interace --- studio/pages/projects.tsx | 5 +++++ studio/stores/common/PostgresMetaInterface.ts | 6 ++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/studio/pages/projects.tsx b/studio/pages/projects.tsx index c3da53796bb..c67987910e6 100644 --- a/studio/pages/projects.tsx +++ b/studio/pages/projects.tsx @@ -10,6 +10,11 @@ import { NextPageWithLayout } from 'types' const ProjectsPage: NextPageWithLayout = () => { const { app } = useStore() + console.log({ + isInitialized: app.organizations.isInitialized, + isLoading: app.organizations.isLoading, + }) + return ( <> {!app.organizations.isInitialized ? ( diff --git a/studio/stores/common/PostgresMetaInterface.ts b/studio/stores/common/PostgresMetaInterface.ts index bd51fd319d5..d73b33ed6a3 100644 --- a/studio/stores/common/PostgresMetaInterface.ts +++ b/studio/stores/common/PostgresMetaInterface.ts @@ -47,6 +47,7 @@ export default class PostgresMetaInterface implements IPostgresMetaInterface< state = this.STATES.INITIAL data: { [key in DataKeys]: T } = {} headers: any = {} + isInitialized: boolean = false constructor( rootStore: IRootStore, @@ -90,10 +91,6 @@ export default class PostgresMetaInterface 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(this.url, { headers }) @@ -109,6 +106,7 @@ export default class PostgresMetaInterface implements IPostgresMetaInterface< this.setError(null) this.setState(LOADING) await this.fetchData() + if (!this.isInitialized) this.isInitialized = true this.setState(LOADED) } catch (e: any) { console.error('Load error message', e.message) From ac822e30f8086c2f33c5eefc78b547c54035b060 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 29 Mar 2023 15:24:59 +0800 Subject: [PATCH 07/10] Remove console log --- studio/pages/projects.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/studio/pages/projects.tsx b/studio/pages/projects.tsx index c67987910e6..c3da53796bb 100644 --- a/studio/pages/projects.tsx +++ b/studio/pages/projects.tsx @@ -10,11 +10,6 @@ import { NextPageWithLayout } from 'types' const ProjectsPage: NextPageWithLayout = () => { const { app } = useStore() - console.log({ - isInitialized: app.organizations.isInitialized, - isLoading: app.organizations.isLoading, - }) - return ( <> {!app.organizations.isInitialized ? ( From ff91fc73f49f5c27b1e93d3fb0b1e5b6661051d0 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 29 Mar 2023 15:27:01 +0800 Subject: [PATCH 08/10] Shift setting isInitialized into the finally block --- studio/stores/common/PostgresMetaInterface.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/studio/stores/common/PostgresMetaInterface.ts b/studio/stores/common/PostgresMetaInterface.ts index d73b33ed6a3..72496fa116e 100644 --- a/studio/stores/common/PostgresMetaInterface.ts +++ b/studio/stores/common/PostgresMetaInterface.ts @@ -106,12 +106,13 @@ export default class PostgresMetaInterface implements IPostgresMetaInterface< this.setError(null) this.setState(LOADING) await this.fetchData() - if (!this.isInitialized) this.isInitialized = true this.setState(LOADED) } catch (e: any) { console.error('Load error message', e.message) this.setError(e) this.setState(ERROR) + } finally { + if (!this.isInitialized) this.isInitialized = true } } From d4fff367fa18552691f4da03b5487185bd65fdd8 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 29 Mar 2023 16:07:22 +0800 Subject: [PATCH 09/10] Small fix --- studio/pages/projects.tsx | 2 ++ studio/stores/common/PostgresMetaInterface.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/studio/pages/projects.tsx b/studio/pages/projects.tsx index c3da53796bb..871c2cd8d98 100644 --- a/studio/pages/projects.tsx +++ b/studio/pages/projects.tsx @@ -10,6 +10,8 @@ import { NextPageWithLayout } from 'types' const ProjectsPage: NextPageWithLayout = () => { const { app } = useStore() + console.log({ isInitialized: app.organizations.isInitialized }) + return ( <> {!app.organizations.isInitialized ? ( diff --git a/studio/stores/common/PostgresMetaInterface.ts b/studio/stores/common/PostgresMetaInterface.ts index 72496fa116e..c11e782498a 100644 --- a/studio/stores/common/PostgresMetaInterface.ts +++ b/studio/stores/common/PostgresMetaInterface.ts @@ -69,6 +69,7 @@ export default class PostgresMetaInterface implements IPostgresMetaInterface< count: computed, hasError: computed, isLoading: computed, + isInitialized: observable, load: action, create: action, update: action, From 5343c5e801c303e7d2fe93c370017440cd0f464f Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 29 Mar 2023 16:08:07 +0800 Subject: [PATCH 10/10] remove logs --- studio/pages/projects.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/studio/pages/projects.tsx b/studio/pages/projects.tsx index 871c2cd8d98..c3da53796bb 100644 --- a/studio/pages/projects.tsx +++ b/studio/pages/projects.tsx @@ -10,8 +10,6 @@ import { NextPageWithLayout } from 'types' const ProjectsPage: NextPageWithLayout = () => { const { app } = useStore() - console.log({ isInitialized: app.organizations.isInitialized }) - return ( <> {!app.organizations.isInitialized ? (