diff --git a/.dockerignore b/.dockerignore index 154054709d9..ff561a782b4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,8 +1,20 @@ -node_modules/ -.vercel -.next -.env.local -.env.production -.env.dev -.env.* +# Root folder of this turbo monorepo +# Doesn't send useless context to Docker daemon .git +.github +about +apps +docker +examples +i18n +**/node_modules +spec +supabase +tests + +**/.next +.vercel +.env.* + +.dockerignore +studio/Dockerfile diff --git a/apps/docs/components/CustomHTMLElements/Heading.tsx b/apps/docs/components/CustomHTMLElements/Heading.tsx index f1248c38d1c..74f3511b110 100644 --- a/apps/docs/components/CustomHTMLElements/Heading.tsx +++ b/apps/docs/components/CustomHTMLElements/Heading.tsx @@ -14,12 +14,22 @@ import { useInView } from 'react-intersection-observer' */ interface Props { - tag: string + tag?: string + parseAnchors?: boolean + customAnchor?: string } -const Heading: React.FC = ({ tag, children }) => { +/** + * This TOC is used in .mdx files and in .tsx files. + * In mdx files, we need to parse the content and format them to match the + * expected tocList format (text, link,level). + * + * In tsx files, we can generate this tocList directly. For these files, we don't + * need to parse the and generate anchors + */ +const Heading: React.FC = ({ tag, parseAnchors = false, customAnchor, children }) => { const HeadingTag = `${tag}` as any - const anchor = getAnchor(children) + const anchor = customAnchor ? customAnchor : getAnchor(children) const link = `#${anchor}` const { ref } = useInView({ @@ -33,7 +43,7 @@ const Heading: React.FC = ({ tag, children }) => { return ( - {removeAnchor(children)} + {parseAnchors ? removeAnchor(children) : children} {anchor && ( # diff --git a/apps/docs/components/MDX/storage_management.mdx b/apps/docs/components/MDX/storage_management.mdx new file mode 100644 index 00000000000..d9df719486d --- /dev/null +++ b/apps/docs/components/MDX/storage_management.mdx @@ -0,0 +1,108 @@ +If you upload additional profile photos, they'll accumulate +in the `avatars` bucket because of their random names with only the latest being referenced +from `public.profiles` and the older versions getting orphaned. + +To automatically remove obsolete storage objects, extend the database +triggers. Note that it is not sufficient to delete the objects from the +`storage.objects` table because that would orphan and leak the actual storage objects in +the S3 backend. Instead, invoke the storage API within Postgres via the `http` extension. + +Enable the [http extension for the `extensions` schema](https://app.supabase.com/project/_/database/extensions) in the Dashboard. +Then, define the following SQL functions in the SQL Editor to delete +storage objects via the API: + +```SQL +create or replace function delete_storage_object(bucket text, object text, out status int, out content varchar) +returns record +language 'plpgsql' +security definer +as $$ +declare + project_url varchar := ''; + service_role_key varchar := ''; -- full access needed + url varchar := project_url||'/storage/v1/object/'||bucket||'/'||object; +begin + select + into status, content + result.status::int, result.content::varchar + FROM extensions.http(( + 'DELETE', + url, + ARRAY[extensions.http_header('authorization','Bearer '||service_role_key)], + NULL, + NULL)::extensions.http_request) as result; +end; +$$; + +create or replace function delete_avatar(avatar_url text, out status int, out content varchar) +returns record +language 'plpgsql' +security definer +as $$ +begin + select + into status, content + result.status, result.content + from public.delete_storage_object('avatars', avatar_url) as result; +end; +$$; + +``` + +Next, add a trigger that removes any obsolete avatar whenever the +profile is updated or deleted: + +```SQL +create or replace function delete_old_avatar() +returns trigger +language 'plpgsql' +security definer +as $$ +declare + status int; + content varchar; +begin + if coalesce(old.avatar_url, '') <> '' + and (tg_op = 'DELETE' or (old.avatar_url <> new.avatar_url)) then + select + into status, content + result.status, result.content + from public.delete_avatar(old.avatar_url) as result; + if status <> 200 then + raise warning 'Could not delete avatar: % %', status, content; + end if; + end if; + if tg_op = 'DELETE' then + return old; + end if; + return new; +end; +$$; + +create trigger before_profile_changes + before update of avatar_url or delete on public.profiles + for each row execute function public.delete_old_avatar(); + +``` + +Finally, delete the `public.profile` row before a user is deleted. +If this step is omitted, you won't be able to delete users without +first manually deleting their avatar image. + +```SQL +create or replace function delete_old_profile() +returns trigger +language 'plpgsql' +security definer +as $$ +begin + delete from public.profiles where id = old.id; + return old; +end; +$$; + +create trigger before_delete_user + before delete on auth.users + for each row execute function public.delete_old_profile(); + +``` diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx index 7738ed521d9..dc2baf6ffbb 100644 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx @@ -101,7 +101,7 @@ const home = [ level: 'reference_python', }, { - label: 'Tools Reference', + label: 'Tools', }, { label: 'Management API', @@ -112,7 +112,7 @@ const home = [ { label: 'Supabase CLI', icon: '/img/icons/menu/reference-cli', - href: '/reference/cli/introduction', + href: '/guides/cli', level: 'reference_javascript', }, ], diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index c55fd5fc084..5b742d75b8f 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -49,6 +49,8 @@ export const gettingstarted = { { name: 'NextJS', url: '/guides/getting-started/quickstarts/nextjs', items: [] }, { name: 'Flutter', url: '/guides/getting-started/quickstarts/flutter', items: [] }, { name: 'SvelteKit', url: '/guides/getting-started/quickstarts/sveltekit', items: [] }, + { name: 'SolidJS', url: '/guides/getting-started/quickstarts/solidjs', items: [] }, + { name: 'Vue', url: '/guides/getting-started/quickstarts/vue', items: [] }, ], }, { @@ -486,6 +488,25 @@ export const storage = { ], } +export const supabase_cli = { + icon: 'reference-cli', + title: 'Supabase CLI', + url: '/guides/cli', + items: [ + { name: 'Overview', url: '/guides/cli', items: [] }, + { name: 'Local Development', url: '/guides/cli/local-development', items: [] }, + { name: 'Managing Environments', url: '/guides/cli/managing-environments', items: [] }, + { + name: 'Reference', + url: undefined, + items: [ + { name: 'Commands', url: '/reference/cli/introduction', items: [] }, + { name: 'Configuration', url: '/reference/cli/config', items: [] }, + ], + }, + ], +} + export const platform = { icon: 'platform', label: 'Platform', @@ -590,26 +611,6 @@ export const resources = { }, ], }, - { - name: 'Supabase CLI', - items: [ - { - name: 'Overview', - url: '/guides/resources/supabase-cli', - items: [], - }, - { - name: 'Local Development', - url: '/guides/resources/supabase-cli/local-development', - items: [], - }, - { - name: 'Managing Environments', - url: '/guides/resources/supabase-cli/managing-environments', - items: [], - }, - ], - }, ], } diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx index 65d0ec61e07..ad8e934e6e6 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx @@ -105,6 +105,9 @@ const NavigationMenu = () => { case url.includes(`/docs/guides/integrations`) && url: menuState.setMenuLevelId('integrations') break + case url.includes(`/docs/guides/cli`) && url: + menuState.setMenuLevelId('supabase_cli') + break // JS v1 case url.includes(`/docs/reference/javascript/v1`) && url: menuState.setMenuLevelId('reference_javascript_v1') @@ -125,6 +128,9 @@ const NavigationMenu = () => { case url.includes(`/docs/reference/python`) && url: menuState.setMenuLevelId('reference_python_v2') break + case url.includes(`/docs/reference/cli/config`) && url: + menuState.setMenuLevelId('supabase_cli') + break case url.includes(`/docs/reference/cli`) && url: menuState.setMenuLevelId('reference_cli') break @@ -164,6 +170,7 @@ const NavigationMenu = () => { const isFunctionsActive = 'functions' === level const isRealtimeActive = 'realtime' === level const isStorageActive = 'storage' === level + const issupabase_cliActive = 'supabase_cli' === level const isPlatformActive = 'platform' === level const isResourcesActive = 'resources' === level const isSelfHosting = 'self_hosting' === level @@ -191,6 +198,7 @@ const NavigationMenu = () => { + diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx index 690615b4a13..27f1e69ba99 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx @@ -179,7 +179,7 @@ const Content: React.FC = ({ id, lib, commonSections, sp if (!sections) console.error('no common sections imported') const menu = NavItems[id] - console.log(menu) + //console.log(menu) const databaseFunctions = find(sections, { title: 'Database' }) ? find(sections, { title: 'Database' }).items : [] diff --git a/apps/docs/components/index.tsx b/apps/docs/components/index.tsx index 587a5cfee32..0097630387b 100644 --- a/apps/docs/components/index.tsx +++ b/apps/docs/components/index.tsx @@ -20,6 +20,7 @@ import QuickstartIntro from './MDX/quickstart_intro.mdx' import ProjectSetup from './MDX/project_setup.mdx' import SocialProviderSetup from './MDX/social_provider_setup.mdx' import SocialProviderSettingsSupabase from './MDX/social_provider_settings_supabase.mdx' +import StorageManagement from './MDX/storage_management.mdx' import { Mermaid } from 'mdx-mermaid/lib/Mermaid' import InlineCodeTag from './CustomHTMLElements/InlineCode' import React from 'react' @@ -49,6 +50,7 @@ const components = { ProjectSetup, SocialProviderSetup, SocialProviderSettingsSupabase, + StorageManagement, Mermaid, Extensions, Alert: (props: any) => ( diff --git a/apps/docs/layouts/SiteLayout.tsx b/apps/docs/layouts/SiteLayout.tsx index d0eec9c2fbf..cb31612a3da 100644 --- a/apps/docs/layouts/SiteLayout.tsx +++ b/apps/docs/layouts/SiteLayout.tsx @@ -39,6 +39,10 @@ const levelsData = { icon: '/docs/img/icons/menu/storage', name: 'Storage', }, + supabase_cli: { + icon: '/docs/img/icons/menu/reference-cli', + name: 'Supabase CLI', + }, platform: { icon: '/docs/img/icons/menu/platform', name: 'Platform', diff --git a/apps/docs/pages/getting-started.mdx b/apps/docs/pages/getting-started.mdx deleted file mode 100644 index ccb7790f15e..00000000000 --- a/apps/docs/pages/getting-started.mdx +++ /dev/null @@ -1,161 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - title: 'Overview', - description: 'An overview of Supabase\'s architecture and product principles.', -} - -Supabase is open source. We choose open source tools which are scalable and make them simple to use. - -Supabase is not a 1-to-1 mapping of Firebase. While we are building many of the features that Firebase offers, we are not going about it the same way: -our technological choices are quite different; everything we use is open source; and wherever possible, we use and support existing tools rather than developing from scratch. - -Most notably, we use Postgres rather than a NoSQL store. This choice was deliberate. We believe that no other database offers the functionality required to compete with Firebase, -while maintaining the scalability required to go beyond it. - -## Architecture - -Each Supabase project consists of several tools: - -![Supabase Architecture](/docs/img/supabase-architecture.png) - -### PostgreSQL (Database) - -PostgreSQL is the core of Supabase. We do not abstract the PostgreSQL database — you can access it and use it with full privileges. We simply provide tools which makes PostgreSQL as easy to use as Firebase. - -- Official Docs: [postgresql.org/docs](https://www.postgresql.org/docs/current/index.html) -- Source code: [github.com/postgres/postgres](https://github.com/postgres/postgres) (mirror) -- License: [PostgreSQL Licence](https://www.postgresql.org/about/licence/) -- Language: C - -### Studio (Dashboard) - -An open source Dashboard for managing your database and services. - -- Official Docs: [Supabase docs](/docs) -- Source code: [github.com/supabase/supabase](https://github.com/supabase/supabase/tree/master/studio) -- License: [Apache 2](https://github.com/supabase/supabase/blob/master/LICENSE) -- Language: TypeScript - -### GoTrue (Auth) - -A JWT-based API for managing users and issuing access tokens. This integrates with PostgreSQL's Row Level Security and the API servers. - -- Official Docs: [Supabase Auth reference docs](/docs/reference/auth) -- Source code: [github.com/supabase/gotrue](https://github.com/supabase/gotrue) -- License: [MIT](https://github.com/supabase/gotrue/blob/master/LICENSE) -- Language: Go - -### PostgREST (API) - -A standalone web server that turns your PostgreSQL database directly into a RESTful API. -We use this with our [pg_graphql](https://github.com/supabase/pg_graphql) extension to provide a GraphQL API. - -- Official Docs: [postgrest.org](https://postgrest.org/) -- Source code: [github.com/PostgREST/postgrest](https://github.com/PostgREST/postgrest) -- License: [MIT](https://github.com/PostgREST/postgrest/blob/main/LICENSE) -- Language: Haskel - -### Realtime (API & multiplayer) - -A scalable websocket engine for managing user Presence, broadcasting messages, and streaming database changes. - -- Official Docs: [Supabase Realtime docs](/docs/guides/realtime) -- Source code: [github.com/supabase/realtime](https://github.com/supabase/realtime) -- License: [Apache 2](https://github.com/supabase/realtime/blob/main/LICENSE) -- Language: Elixir - -### Storage API (large file storage) - -An S3-compatible object storage service that stores metadata in Postgres. - -- Official Docs: [Supabase Storage reference docs](/docs/reference/storage) -- Source code: [github.com/supabase/storage-api](https://github.com/supabase/storage-api) -- License: [Apache 2.0](https://github.com/supabase/storage-api/blob/master/LICENSE) -- Language: NodeJS / TypeScript - -### Deno (Edge Functions) - -A modern runtime for JavaScript and TypeScript. - -- Official Docs: [deno.land](https://deno.land/) -- Source code: [github.com/denoland/deno](https://github.com/denoland/deno) -- License: [MIT](https://github.com/denoland/deno/blob/main/LICENSE.md) -- Language: TypeScript / Rust - -### postgres-meta (Database management) - -A RESTful API for managing your Postgres. Fetch tables, add roles, and run queries. - -- Official Docs: [supabase.github.io/postgres-meta](https://supabase.github.io/postgres-meta/) -- Source code: [github.com/supabase/postgres-meta](https://github.com/supabase/postgres-meta) -- License: [Apache 2.0](https://github.com/supabase/postgres-meta/blob/master/LICENSE) -- Language: NodeJS / TypeScript - -### PgBouncer - -A lightweight connection pooler for PostgreSQL. This is useful for connecting to Postgres when using Serverless functions. - -- Official Docs: [pgbouncer.org](http://www.pgbouncer.org/) -- Source code: [pgbouncer/pgbouncer](https://github.com/pgbouncer/pgbouncer) -- License: [ISC](https://github.com/pgbouncer/pgbouncer/blob/master/COPYRIGHT) -- Language: C - -### Kong (API Gateway) - -A cloud-native API gateway, built on top of Nginx. - -- Official Docs: [docs.konghq.com](https://docs.konghq.com/) -- Source code: [github.com/kong/kong](https://github.com/kong/kong) -- License: [Apache 2.0](https://github.com/Kong/kong/blob/master/LICENSE) -- Language: Lua - -## Product Principles - -It is our goal to provide an architecture that any large-scale company would design for themselves, -and then provide tooling around that architecture that is easy-to-use for indie-developers and small teams. - -We use a series of principles to ensure that scalability and usability are never mutually exclusive: - -### Everything works in isolation - -Each system must work as a standalone tool with as few moving parts as possible. -The litmus test for this is: "Can a user run this product with nothing but a Postgres database?" - -### Everything is integrated - -Supabase is composable. Even though every product works in isolation, each product on the platform needs to 10x the other products. -For integration, each tool should expose an API and Webhooks. - -### Everything is extensible - -We're deliberate about adding a new tool, and prefer instead to extend an existing one. -This is the opposite of many cloud providers whose product offering expands into niche use-cases. We provide _primitives_ for developers, which allow them to achieve any goal. -Less, but better. - -### Everything is portable - -To avoid lock-in, we make it easy to migrate in and out. Our cloud offering is compatible with our self-hosted product. -We use existing standards to increase portability (like pg_dump an CSV files). If a new standard emerges which competes with a "Supabase" approach, we will deprecate the approach in favor of the standard. -This forces us compete on experience. We aim to be the best Postgres hosting service. - -### Play the long game - -We sacrifice short-term wins for long-term gains. For example, it is tempting to run a fork of Postgres with additional functionality which only our customers need. -Instead, we prefer to support efforts to upstream missing functionality so that the entire community benefits. This has the additional benefit of ensuring portability and longevity. - -### Build for developers - -"Developers" are a specific profile of user: they are _builders_. -When assessing impact as a function of effort, developers have a large efficiency due to the type of products and systems they can build. -As the profile of a developer changes over time, Supabase will continue to evolve the product to fit this evolving profile. - -### Support existing tools - -Supabase supports existing tools and communities wherever possible. Supabase is more like a "community of communities" - each tool typically has its own community -which we work with. -Open source is something we approach [collaboratively](https://supabase.com/blog/supabase-series-b#giving-back): we employ maintainers, sponsor projects, invest in businesses, and develop our own open source tools. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/api.mdx b/apps/docs/pages/guides/api.mdx index a6b8a018fd6..4dad9373119 100644 --- a/apps/docs/pages/guides/api.mdx +++ b/apps/docs/pages/guides/api.mdx @@ -217,7 +217,7 @@ import { createClient, useQuery } from 'urql' // Prepare API key and Authorization header const headers = { apikey: , - authorization: `Bearer ${ + authorization: `Bearer ${}`, } // Create GraphQL client diff --git a/apps/docs/pages/guides/resources/supabase-cli.mdx b/apps/docs/pages/guides/cli.mdx similarity index 95% rename from apps/docs/pages/guides/resources/supabase-cli.mdx rename to apps/docs/pages/guides/cli.mdx index 394766e483f..a17186d3d76 100644 --- a/apps/docs/pages/guides/resources/supabase-cli.mdx +++ b/apps/docs/pages/guides/cli.mdx @@ -1,7 +1,6 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { - id: 'cli', title: 'Supabase CLI', description: 'The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.', @@ -111,7 +110,8 @@ brew upgrade supabase ## See also -- [Supabase CLI Reference](/docs/reference/cli/usage) +- [Supabase CLI Reference](/docs/reference/cli/introduction) +- [Supabase CLI Configuration](/docs/reference/cli/config) - [Local Development](/docs/guides/cli/local-development) - [Managing Environments](/docs/guides/cli/managing-environments) diff --git a/apps/docs/pages/guides/resources/supabase-cli/local-development.mdx b/apps/docs/pages/guides/cli/local-development.mdx similarity index 99% rename from apps/docs/pages/guides/resources/supabase-cli/local-development.mdx rename to apps/docs/pages/guides/cli/local-development.mdx index b0f45364b52..42bee86356c 100644 --- a/apps/docs/pages/guides/resources/supabase-cli/local-development.mdx +++ b/apps/docs/pages/guides/cli/local-development.mdx @@ -34,9 +34,8 @@ supabase login If you installed the Supabase CLI via NPM you may have to use `npx supabase login`. - - + ## Initialize your project diff --git a/apps/docs/pages/guides/resources/supabase-cli/managing-environments.mdx b/apps/docs/pages/guides/cli/managing-environments.mdx similarity index 100% rename from apps/docs/pages/guides/resources/supabase-cli/managing-environments.mdx rename to apps/docs/pages/guides/cli/managing-environments.mdx diff --git a/apps/docs/pages/guides/functions/secrets.mdx b/apps/docs/pages/guides/functions/secrets.mdx index dd51094d374..3d60a239a8b 100644 --- a/apps/docs/pages/guides/functions/secrets.mdx +++ b/apps/docs/pages/guides/functions/secrets.mdx @@ -25,7 +25,7 @@ By default, Edge Functions have access to these secrets: Let's create a local file for storing our secrets, and inside it we can store a secret `MY_NAME`: -```jsx +```bash echo "MY_NAME=Yoda" >> ./supabase/.env.local ``` diff --git a/apps/docs/pages/guides/getting-started/quickstarts/solidjs.mdx b/apps/docs/pages/guides/getting-started/quickstarts/solidjs.mdx new file mode 100644 index 00000000000..d72aa4da959 --- /dev/null +++ b/apps/docs/pages/guides/getting-started/quickstarts/solidjs.mdx @@ -0,0 +1,140 @@ +import Layout from '~/layouts/DefaultGuideLayout' +import StepHikeCompact from '~/components/StepHikeCompact' + +export const meta = { + title: 'Use Supabase with SolidJS', + subtitle: + 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a SolidJS app.', + breadcrumb: 'Framework Quickstarts', +} + + + + + + + [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + + After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. + + + + + + ```sql SQL_EDITOR + -- Create the table + CREATE TABLE countries ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL + ); + -- Insert some sample data into the table + INSERT INTO countries (name) VALUES ('United States'); + INSERT INTO countries (name) VALUES ('Canada'); + INSERT INTO countries (name) VALUES ('Mexico'); + ```` + + + + + + + + + + Create a SolidJS app using the `degit` command. + + + + + + ```bash Terminal + npx degit solidjs/templates/js my-app + ``` + + + + + + + + + The fastest way to get started is to use the `supabase-js` client library which provides a convenient interface for working with Supabase from a SolidJS app. + + Navigate to the SolidJS app and install `supabase-js`. + + + + + + ```bash Terminal + cd my-app && npm install @supabase/supabase-js + ``` + + + + + + + + + In `App.jsx`, create a Supabase client using your [Project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). + + Add a `getCountries` function to fetch the data and display the query result to the page. + + + + + + ```jsx src/App.jsx + import { createClient } from "@supabase/supabase-js"; + import { createEffect, createSignal, For } from "solid-js"; + + const supabase = createClient('https://.supabase.co', ''); + + function App() { + const [countries, setCountries] = createSignal(); + + createEffect(() => { + getCountries(); + }); + + async function getCountries() { + const { data } = await supabase.from("countries").select(); + setCountries(data); + } + + return ( +
    + {(country) =>
  • {country.name}
  • }
    +
+ ); + } + + export default App; + ``` + +
+ +
+ + + + + Start the app and go to http://localhost:3000 in a browser and you should see the list of countries. + + + + + + ```bash Terminal + npm run dev + ``` + + + + +
+ +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx b/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx index d9042eeab47..2bea7515a9a 100644 --- a/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx +++ b/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx @@ -77,7 +77,7 @@ export const meta = { - Create a `/src/lib` directory of your SvelteKit app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). + Create a `/src/lib` directory in your SvelteKit app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). @@ -146,7 +146,7 @@ export const meta = { - Start the app, go to http://localhost:5173 in a browser, and open the browser console and you should see the list of countries. + Start the app and go to http://localhost:5173 in a browser and you should see the list of countries. diff --git a/apps/docs/pages/guides/getting-started/quickstarts/vue.mdx b/apps/docs/pages/guides/getting-started/quickstarts/vue.mdx new file mode 100644 index 00000000000..b353d6ef35d --- /dev/null +++ b/apps/docs/pages/guides/getting-started/quickstarts/vue.mdx @@ -0,0 +1,153 @@ +import Layout from '~/layouts/DefaultGuideLayout' +import StepHikeCompact from '~/components/StepHikeCompact' + +export const meta = { + title: 'Use Supabase with Vue', + subtitle: + 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Vue app.', + breadcrumb: 'Framework Quickstarts', +} + + + + + + + [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + + After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. + + + + + + ```sql SQL_EDITOR + -- Create the table + CREATE TABLE countries ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL + ); + -- Insert some sample data into the table + INSERT INTO countries (name) VALUES ('United States'); + INSERT INTO countries (name) VALUES ('Canada'); + INSERT INTO countries (name) VALUES ('Mexico'); + ```` + + + + + + + + + + Create a Vue app using the `npm init` command. + + + + + + ```sh Terminal + npm init vue@latest my-app + ``` + + + + + + + + + The fastest way to get started is to use the `supabase-js` client library which provides a convenient interface for working with Supabase from a Vue app. + + Navigate to the Vue app and install `supabase-js`. + + + + + + ```bash Terminal + cd my-app && npm install @supabase/supabase-js + ``` + + + + + + + + + Create a `/src/lib` directory in your Vue app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). + + + + + + ```js src/lib/supabaseClient.js + import { createClient } from '@supabase/supabase-js' + + export const supabase = createClient('https://.supabase.co', '') + ``` + + + + + + + + + Replace the existing content in your `App.vue` file with the following code. + + + + + + ```vue src/App.vue + + + + ``` + + + + + + + + + Start the app and go to http://localhost:5173 in a browser and you should see the list of countries. + + + + + + ```bash Terminal + npm run dev + ``` + + + + + + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx index 8f2754453f9..b76748ad8aa 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx @@ -530,6 +530,10 @@ export class AccountComponent implements OnInit { } ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx index b60db313a51..192caa40725 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx @@ -500,6 +500,10 @@ Now you will need to run the prebuild command to get the application working on expo prebuild ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx index 6a73c7aa8ab..478390cf7c0 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx @@ -793,6 +793,10 @@ class _AccountPageState extends State { } ``` +### Storage management + + + Congratulations, that is it! You have now built a fully functional user management app using Flutter and Supabase! ## See also diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-angular.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-angular.mdx index 88c1d9e6433..4616bb796b0 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-angular.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-angular.mdx @@ -522,6 +522,10 @@ template: ` ` ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-react.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-react.mdx index c14bb343ed3..3c7c9847b96 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-react.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-react.mdx @@ -495,6 +495,10 @@ return ( ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-vue.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-vue.mdx index 2f368771b78..b5599980212 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-vue.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-vue.mdx @@ -555,6 +555,10 @@ export default defineComponent({ ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx index 81b86e96b2f..78c6c444d1e 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx @@ -718,6 +718,10 @@ return ( ) ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx index b4f5af0a1a3..844c10dc2a4 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx @@ -420,6 +420,10 @@ And then we can add the widget to the Account page: That is it! You should now be able to upload a profile photo to Supabase Storage. +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx index 7d1418b7146..981c7b33d9b 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx @@ -397,6 +397,10 @@ return ( ) ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx index cae8504a010..6fd481ea9cc 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx @@ -771,6 +771,10 @@ return ( ) ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-solidjs.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-solidjs.mdx index 7de71d63910..eaf47892298 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-solidjs.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-solidjs.mdx @@ -415,6 +415,10 @@ return ( ) ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-svelte.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-svelte.mdx index 69cdea17b87..5195fa8b135 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-svelte.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-svelte.mdx @@ -368,6 +368,10 @@ And then we can add the widget to the Account page: ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx index bbac06e6019..78f236a99d9 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx @@ -434,6 +434,10 @@ And then we can add the widget to the Account page: ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-vue-3.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-vue-3.mdx index a1ec84a8c92..a09c7e7c1ec 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-vue-3.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-vue-3.mdx @@ -379,6 +379,10 @@ And then we can add the widget to the Account page: ``` +### Storage management + + + ## Next steps At this stage you have a fully functional application! diff --git a/apps/docs/pages/guides/resources.mdx b/apps/docs/pages/guides/resources.mdx index b1b139020d3..0b2d856b541 100644 --- a/apps/docs/pages/guides/resources.mdx +++ b/apps/docs/pages/guides/resources.mdx @@ -64,20 +64,6 @@ export const meta = { -
- -
- - ### Supabase CLI - - Develop your project locally, manage your Supabase projects, handle database migrations, CI/CD workflows, and generate types from your database schema, all from the comfort of your Terminal. - -[Learn more](/docs/guides/resources/supabase-cli) - -
- -
- {/* end of container */} diff --git a/apps/docs/pages/guides/storage/access-control.mdx b/apps/docs/pages/guides/storage/access-control.mdx index 58572bd789e..4adfd73e135 100644 --- a/apps/docs/pages/guides/storage/access-control.mdx +++ b/apps/docs/pages/guides/storage/access-control.mdx @@ -117,10 +117,8 @@ using ( bucket_id = 'public' ); -- 1. Allow logged-in access to any files in the "restricted" bucket create policy "Restricted Access" on storage.objects for select -using ( - bucket_id = 'restricted' - and auth.role() = 'authenticated' -); +to authenticated +using ( bucket_id = 'restricted' ); ``` ### Allow individual access to a file diff --git a/apps/docs/pages/guides/tutorials.mdx b/apps/docs/pages/guides/tutorials.mdx deleted file mode 100644 index 887788bdb39..00000000000 --- a/apps/docs/pages/guides/tutorials.mdx +++ /dev/null @@ -1,159 +0,0 @@ -import Layout from '~/layouts/DefaultLayout' -import Link from 'next/link' -import { GlassPanel } from 'ui' - -export const meta = { - title: 'Tutorials', -} - -### Webapps - -
- {webapps.map((item) => { - return ( - - - - {item.description} - - - - ) - })} -
- -### Mobile - -
- {mobile.map((item) => { - return ( - - - - {item.description} - - - - ) - })} -
- -### Low code - -
- {lowCode.map((item) => { - return ( - - - - {item.description} - - - - ) - })} -
- -export const webapps = [ - { - title: 'NextJS', - href: '/guides/tutorials/with-nextjs', - description: - 'Learn how to build a user management app with NextJS and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/nextjs-icon', - }, - { - title: 'React', - href: '/guides/tutorials/with-react', - description: - 'Learn how to build a user management app with React and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/react-icon', - }, - { - title: 'Vue 3', - href: '/guides/tutorials/with-vue-3', - description: - 'Learn how to build a user management app with Vue 3 and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/vuejs-icon', - }, - { - title: 'Nuxt 3', - href: '/guides/tutorials/with-nuxt-3', - description: - 'Learn how to build a user management app with Nuxt 3 and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/nuxt-icon', - }, - { - title: 'Angular', - href: '/guides/tutorials/with-angular', - description: - 'Learn how to build a user management app with Angular and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/angular-icon', - }, - { - title: 'RedwoodJS', - href: '/guides/tutorials/with-redwoodjs', - description: - 'Learn how to build a user management app with RedwoodJS and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/redwood-icon', - }, - { - title: 'Svelte', - href: '/guides/tutorials/with-svelte', - description: - 'Learn how to build a user management app with Svelte and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/svelte-icon', - }, - { - title: 'SvelteKit', - href: '/guides/tutorials/with-sveltekit', - description: - 'Learn how to build a user management app with SvelteKit and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/svelte-icon', - }, -] - -export const mobile = [ - { - title: 'Flutter', - href: '/guides/tutorials/with-flutter', - description: - 'Learn how to build a user management app with Flutter and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/flutter-icon', - }, - { - title: 'Expo', - href: '/guides/tutorials/with-expo', - description: - 'Learn how to build a user management app with Expo and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/expo-icon', - }, -] - -export const lowCode = [ - { - title: 'Ionic React', - href: '/guides/tutorials/with-ionic-react', - description: - 'Learn how to build a user management app with Ionic React and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/ionic-icon', - }, - { - title: 'Ionic Vue', - href: '/guides/tutorials/with-ionic-vue', - description: - 'Learn how to build a user management app with Ionic Vue and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/ionic-icon', - }, - { - title: 'Ionic Angular', - href: '/guides/tutorials/with-ionic-angular', - description: - 'Learn how to build a user management app with Ionic Angular and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/ionic-icon', - }, -] - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/index.mdx b/apps/docs/pages/index.mdx index fbe5ab73c4e..0f687a9929e 100644 --- a/apps/docs/pages/index.mdx +++ b/apps/docs/pages/index.mdx @@ -61,7 +61,7 @@ export const meta = {
-
+
@@ -69,18 +69,28 @@ export const meta = { - - + + - - + + - - + + + + + + + + + + + +
diff --git a/apps/docs/pages/new/auth.mdx b/apps/docs/pages/new/auth.mdx deleted file mode 100644 index 5228f5129da..00000000000 --- a/apps/docs/pages/new/auth.mdx +++ /dev/null @@ -1,77 +0,0 @@ -import Layout from '~/layouts/DefaultLayout' -import Link from 'next/link' -// import { useTheme } from 'common/Providers' -import { GlassPanel } from 'ui' - -{/* // export const { isDarkMode } = useTheme() */} - -export const meta = { - title: 'Auth', -} - -## Add and manage email and password, passwordless, OAuth, and mobile logins to your project through a suite of identity providers and APIs. - -### Popular Guides - -
- {topArticles.map((item) => { - return ( - - - - {item.description} - - - - )})} - -
- -export const topArticles = [ - { - title: 'Enable Captcha Protection', - header: '/docs/img/cards/sample-card-header-4.svg', - href: '/guides/auth/auth-captcha', - description: 'Keep your platform safe from bots and malicious scripts by implementing hCaptcha.', - span: 'col-span-6', - }, - { - title: 'Auth Helpers', - header: '/docs/img/cards/sample-card-header-4.svg', - href: '/guides/auth/auth-helpers', - description: 'A collection of framework-specific utilities for implementing Supabase Auth.', - span: 'col-span-6', - }, - { - title: 'Email Login', - header: '/docs/img/cards/sample-card-header-4.svg', - href: '/guides/auth/auth-email', - description: 'Learn how to add an email and password login flow to your platform.', - }, - { - title: 'Phone Login', - header: '/docs/img/cards/sample-card-header-4.svg', - href: '/guides/auth/phone-login', - description: 'Learn how to add a login flow so your users can access your platform through one-time passwords via SMS.', - }, - { - title: 'Social Login', - header: '/docs/img/cards/sample-card-header-4.svg', - href: '/guides/auth/social-login', - description: 'Learn how to add a login flow so your users can access your platform through social providers like Apple, Facebook, or Google.', - }, -] - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/ref-pages.mdx b/apps/docs/pages/ref-pages.mdx deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/docs/pages/reference/cli/config.tsx b/apps/docs/pages/reference/cli/config.tsx index f472a9a8d58..3453d44e312 100644 --- a/apps/docs/pages/reference/cli/config.tsx +++ b/apps/docs/pages/reference/cli/config.tsx @@ -2,16 +2,31 @@ import specFile from '~/../../spec/cli_v1_config.yaml' assert { type: 'yml' } import { Parameter } from '~/lib/refGenerator/refTypes' import ReactMarkdown from 'react-markdown' +import GuidesTableOfContents from '~/components/GuidesTableOfContents' +import { Heading } from '~/components/CustomHTMLElements' // Parameters are grouped on the page by tag const TAGS = ['general', 'auth', 'api', 'database', 'dashboard', 'local', 'edge-functions'] +const tocList = TAGS.map((tag) => + specFile.parameters + .filter((param: Parameter) => param.tags[0] === tag) + .map((parameter) => { + console.log('the param', parameter) + const text = parameter.id + const link = `#${parameter.id}` + const level = '2' + return { text, link, level } + }) +).flat() + export default function Config() { return ( -
-
+
+
-

{specFile.info.title}

+

CLI configuration

+
{specFile.info.description}
{TAGS.map((tag) => @@ -22,11 +37,11 @@ export default function Config() { {index === 0 &&

{tag}

}
-

+ $ {parameter.title} -

-
+ +

@@ -63,6 +78,14 @@ export default function Config() {

+
+
+ + On this page + + +
+
) } diff --git a/apps/docs/pages/tutorials/nextjs.mdx b/apps/docs/pages/tutorials/nextjs.mdx deleted file mode 100644 index 5e22b391296..00000000000 --- a/apps/docs/pages/tutorials/nextjs.mdx +++ /dev/null @@ -1,724 +0,0 @@ -import Layout from '~/layouts/tutorials/TutorialLayout' - -export const meta = { - title: 'Creating a user management dashboard with NextJS and Supabase', - author: 'Rich Haines', - video: 'https://www.youtube-nocookie.com/embed/0Fs96oZ4se0', -} - -In this 25-minute guide, we’ll be building a collaborative user managment app using NextJS and Supabase. -As users add and move rectangles in a canvas, changes will be automatically synced and persisted, allowing for a canvas that updates in real-time across clients. -Users will also be able to see other users selections, and undo and redo actions. - -![Supabase User Management example](/docs/img/user-management-demo.png) - -### GitHub - -Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management). - -## Building the App - -Let's start building the Next.js app from scratch. - -### Initialize a Next.js app - -We can use [`create-next-app`](https://nextjs.org/docs/getting-started) to initialize -an app called `supabase-nextjs`: - - - - -```bash -npx create-next-app@latest --use-npm supabase-nextjs -cd supabase-nextjs -``` - - - - -```bash -npx create-next-app@latest --ts --use-npm supabase-nextjs -cd supabase-nextjs -``` - - - - -Then install the Supabase client library: [supabase-js](https://github.com/supabase/supabase-js) - -```bash -npm install @supabase/supabase-js -``` - -And finally we want to save the environment variables in a `.env.local`. -All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys). - -```bash title=.env.local -NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL -NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY -``` - -And one optional step is to update the CSS file `styles/globals.css` to make the app look nice. -You can find the full contents of this file [here](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/nextjs-ts-user-management/styles/globals.css). - -### Set up a Login component - -#### Supabase Auth Helpers - -Next.js is a highly versatile framework offering pre-rendering at build time (SSG), server-side rendering at request time (SSR), API routes, and middleware edge-functions. - -It can be challenging to authenticate your users in all these different environments, that's why we've created the [Supabase Auth Helpers](https://supabase.com/docs/guides/auth/auth-helpers/nextjs) to make user management and data fetching within Next.js as easy as possible. - -Install the auth helpers for React and Next.js - -```bash -npm install @supabase/auth-helpers-react @supabase/auth-helpers-nextjs -``` - - - - -Wrap your `pages/_app.js` component with the `SessionContextProvider` component: - -```jsx title=pages/_app.js -import '../styles/globals.css' -import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs' -import { SessionContextProvider } from '@supabase/auth-helpers-react' - -function MyApp({ Component, pageProps }) { - const [supabaseClient] = useState(() => createBrowserSupabaseClient()) - - return ( - - - - ) -} -export default MyApp -``` - - - - -Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component: - -```jsx lines=2,8 title=pages/_app.tsx -import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs' -import { SessionContextProvider, Session } from '@supabase/auth-helpers-react' - -function MyApp({ - Component, - pageProps, -}: AppProps<{ - initialSession: Session, -}>) { - const [supabaseClient] = useState(() => createBrowserSupabaseClient()) - - return ( - - - - ) -} -export default MyApp -``` - -See the [Auth Helpers docs](/docs/guides/auth/auth-helpers/nextjs#usage-with-typescript) for more details on usage with TypeScript. - - - - -#### Supabase Auth UI - -We can use the [Supabase Auth UI](https://supabase.com/docs/guides/auth/auth-helpers/auth-ui) a pre-built React component for authenticating users via OAuth, email, and magic links. - -Install the Supabase Auth UI for React - -```bash -npm install @supabase/auth-ui-react -``` - -Add the `Auth` component to your home page - -```jsx title=pages/index.js -import { Auth, ThemeSupa } from '@supabase/auth-ui-react' -import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react' - -const Home = () => { - const session = useSession() - const supabase = useSupabaseClient() - - return ( -
- {!session ? ( - - ) : ( -

Account page will go here.

- )} -
- ) -} - -export default Home -``` - -### Account page - -After a user is signed in we can allow them to edit their profile details and manage their account. - - - - -Let's create a new component for that called `Account.js` within a `components` folder. - -```tsx title=components/Account.js -import { useState, useEffect } from 'react' -import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react' - -export default function Account({ session }) { - const supabase = useSupabaseClient() - const user = useUser() - const [loading, setLoading] = useState(true) - const [username, setUsername] = useState(null) - const [website, setWebsite] = useState(null) - const [avatar_url, setAvatarUrl] = useState(null) - - useEffect(() => { - getProfile() - }, [session]) - - async function getProfile() { - try { - setLoading(true) - - let { data, error, status } = await supabase - .from('profiles') - .select(`username, website, avatar_url`) - .eq('id', user.id) - .single() - - if (error && status !== 406) { - throw error - } - - if (data) { - setUsername(data.username) - setWebsite(data.website) - setAvatarUrl(data.avatar_url) - } - } catch (error) { - alert('Error loading user data!') - console.log(error) - } finally { - setLoading(false) - } - } - - async function updateProfile({ username, website, avatar_url }) { - try { - setLoading(true) - - const updates = { - id: user.id, - username, - website, - avatar_url, - updated_at: new Date().toISOString(), - } - - let { error } = await supabase.from('profiles').upsert(updates) - if (error) throw error - alert('Profile updated!') - } catch (error) { - alert('Error updating the data!') - console.log(error) - } finally { - setLoading(false) - } - } - - return ( -
-
- - -
-
- - setUsername(e.target.value)} - /> -
-
- - setWebsite(e.target.value)} - /> -
- -
- -
- -
- -
-
- ) -} -``` - -
- - -Let's create a new component for that called `Account.tsx` within a `components` folder. - -```tsx title=components/Account.tsx -import { useState, useEffect } from 'react' -import { useUser, useSupabaseClient, Session } from '@supabase/auth-helpers-react' -import { Database } from '../utils/database.types' -type Profiles = Database['public']['Tables']['profiles']['Row'] - -export default function Account({ session }: { session: Session }) { - const supabase = useSupabaseClient() - const user = useUser() - const [loading, setLoading] = useState(true) - const [username, setUsername] = useState(null) - const [website, setWebsite] = useState(null) - const [avatar_url, setAvatarUrl] = useState(null) - - useEffect(() => { - getProfile() - }, [session]) - - async function getProfile() { - try { - setLoading(true) - if (!user) throw new Error('No user') - - let { data, error, status } = await supabase - .from('profiles') - .select(`username, website, avatar_url`) - .eq('id', user.id) - .single() - - if (error && status !== 406) { - throw error - } - - if (data) { - setUsername(data.username) - setWebsite(data.website) - setAvatarUrl(data.avatar_url) - } - } catch (error) { - alert('Error loading user data!') - console.log(error) - } finally { - setLoading(false) - } - } - - async function updateProfile({ - username, - website, - avatar_url, - }: { - username: Profiles['username'] - website: Profiles['website'] - avatar_url: Profiles['avatar_url'] - }) { - try { - setLoading(true) - if (!user) throw new Error('No user') - - const updates = { - id: user.id, - username, - website, - avatar_url, - updated_at: new Date().toISOString(), - } - - let { error } = await supabase.from('profiles').upsert(updates) - if (error) throw error - alert('Profile updated!') - } catch (error) { - alert('Error updating the data!') - console.log(error) - } finally { - setLoading(false) - } - } - - return ( -
-
- - -
-
- - setUsername(e.target.value)} - /> -
-
- - setWebsite(e.target.value)} - /> -
- -
- -
- -
- -
-
- ) -} -``` - -
-
- -### Launch! - -Now that we have all the components in place, let's update `pages/index.js`: - -```jsx lines=3,14 title=pages/index.js -import { Auth, ThemeSupa } from '@supabase/auth-ui-react' -import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react' -import Account from '../components/Account' - -const Home = () => { - const session = useSession() - const supabase = useSupabaseClient() - - return ( -
- {!session ? ( - - ) : ( - - )} -
- ) -} - -export default Home -``` - -Once that's done, run this in a terminal window: - -```bash -npm run dev -``` - -And then open the browser to [localhost:3000](http://localhost:3000) and you should see the completed app. - -## Bonus: Profile photos - -Every Supabase project is configured with [Storage](/docs/guides/storage) for managing large files like -photos and videos. - -### Create an upload widget - -Let's create an avatar widget for the user so that they can upload a profile photo. We can start by creating a new component: - - - - -```jsx title=components/Avatar.js -import React, { useEffect, useState } from 'react' -import { useSupabaseClient } from '@supabase/auth-helpers-react' - -export default function Avatar({ uid, url, size, onUpload }) { - const supabase = useSupabaseClient() - const [avatarUrl, setAvatarUrl] = useState(null) - const [uploading, setUploading] = useState(false) - - useEffect(() => { - if (url) downloadImage(url) - }, [url]) - - async function downloadImage(path) { - try { - const { data, error } = await supabase.storage.from('avatars').download(path) - if (error) { - throw error - } - const url = URL.createObjectURL(data) - setAvatarUrl(url) - } catch (error) { - console.log('Error downloading image: ', error) - } - } - - const uploadAvatar = async (event) => { - try { - setUploading(true) - - if (!event.target.files || event.target.files.length === 0) { - throw new Error('You must select an image to upload.') - } - - const file = event.target.files[0] - const fileExt = file.name.split('.').pop() - const fileName = `${uid}.${fileExt}` - const filePath = `${fileName}` - - let { error: uploadError } = await supabase.storage - .from('avatars') - .upload(filePath, file, { upsert: true }) - - if (uploadError) { - throw uploadError - } - - onUpload(filePath) - } catch (error) { - alert('Error uploading avatar!') - console.log(error) - } finally { - setUploading(false) - } - } - - return ( -
- {avatarUrl ? ( - Avatar - ) : ( -
- )} -
- - -
-
- ) -} -``` - - - - -```tsx title=components/Avatar.tsx -import React, { useEffect, useState } from 'react' -import { useSupabaseClient } from '@supabase/auth-helpers-react' -import { Database } from '../utils/database.types' -type Profiles = Database['public']['Tables']['profiles']['Row'] - -export default function Avatar({ - uid, - url, - size, - onUpload, -}: { - uid: string - url: Profiles['avatar_url'] - size: number - onUpload: (url: string) => void -}) { - const supabase = useSupabaseClient() - const [avatarUrl, setAvatarUrl] = useState(null) - const [uploading, setUploading] = useState(false) - - useEffect(() => { - if (url) downloadImage(url) - }, [url]) - - async function downloadImage(path: string) { - try { - const { data, error } = await supabase.storage.from('avatars').download(path) - if (error) { - throw error - } - const url = URL.createObjectURL(data) - setAvatarUrl(url) - } catch (error) { - console.log('Error downloading image: ', error) - } - } - - const uploadAvatar: React.ChangeEventHandler = async (event) => { - try { - setUploading(true) - - if (!event.target.files || event.target.files.length === 0) { - throw new Error('You must select an image to upload.') - } - - const file = event.target.files[0] - const fileExt = file.name.split('.').pop() - const fileName = `${uid}.${fileExt}` - const filePath = `${fileName}` - - let { error: uploadError } = await supabase.storage - .from('avatars') - .upload(filePath, file, { upsert: true }) - - if (uploadError) { - throw uploadError - } - - onUpload(filePath) - } catch (error) { - alert('Error uploading avatar!') - console.log(error) - } finally { - setUploading(false) - } - } - - return ( -
- {avatarUrl ? ( - Avatar - ) : ( -
- )} -
- - -
-
- ) -} -``` - - - - -### Add the new widget - -And then we can add the widget to the Account page: - -```jsx title=components/Account.js -// Import the new component -import Avatar from './Avatar' - -// ... - -return ( -
- {/* Add to the body */} - { - setAvatarUrl(url) - updateProfile({ username, website, avatar_url: url }) - }} - /> - {/* ... */} -
-) -``` - -## Next steps - -At this stage you have a fully functional application! - -- See the complete [example on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management) and deploy it to Vercel. -- Explore the [pre-built Auth UI for React](https://supabase.com/docs/guides/auth/auth-helpers/auth-ui). -- Explore the [Auth Helpers for Next.js](https://supabase.com/docs/guides/auth/auth-helpers/nextjs). -- Explore the [Supabase Cache Helpers](https://github.com/psteinroe/supabase-cache-helpers). -- See the [Next.js Subscription Payments Starter](https://github.com/vercel/nextjs-subscription-payments) template on GitHub. -- Got a question? [Ask here](https://github.com/supabase/supabase/discussions). -- Sign in: [app.supabase.com](https://app.supabase.com) - -export default ({ children }) => diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index 420340205b9..d5fa09975e2 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -1155,7 +1155,7 @@ module.exports = [ { permanent: true, source: '/docs/reference/cli/about', - destination: '/docs/reference/cli', + destination: '/docs/reference/cli/introduction', }, { permanent: true, @@ -1165,118 +1165,44 @@ module.exports = [ { permanent: true, source: '/docs/reference/cli/supabase-help', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-login', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-link', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-init', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-start', - destination: '/docs/reference/cli/usage', + destination: '/docs/reference/cli/global-flags', }, { permanent: true, source: '/docs/reference/cli/supabase-db-branch-list', - destination: '/docs/reference/cli/usage', + destination: '/docs/reference/supabase-db', }, { permanent: true, source: '/docs/reference/cli/supabase-db-branch-create', - destination: '/docs/reference/cli/usage', + destination: '/docs/reference/supabase-db', }, { permanent: true, source: '/docs/reference/cli/supabase-db-branch-delete', - destination: '/docs/reference/cli/usage', + destination: '/docs/reference/supabase-db', }, { permanent: true, source: '/docs/reference/cli/supabase-db-switch', - destination: '/docs/reference/cli/usage', + destination: '/docs/reference/supabase-db', }, { permanent: true, source: '/docs/reference/cli/supabase-db-changes', - destination: '/docs/reference/cli/usage', + destination: '/docs/reference/supabase-db', }, { permanent: true, source: '/docs/reference/cli/supabase-db-commit', - destination: '/docs/reference/cli/usage', + destination: '/docs/reference/supabase-db', }, { permanent: true, source: '/docs/reference/cli/supabase-db-remote-set', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-functions-delete', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-functions-deploy', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-functions-new', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-functions-serve', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-migration-new', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-orgs-list', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-projects-list', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-projects-create', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-secrets-list', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-secrets-set', - destination: '/docs/reference/cli/usage', - }, - { - permanent: true, - source: '/docs/reference/cli/supabase-secrets-unset', - destination: '/docs/reference/cli/usage', + destination: '/docs/reference/supabase-db', }, + { permanent: true, source: '/docs/reference/tools/reference-auth', @@ -1711,6 +1637,11 @@ module.exports = [ source: '/docs/guides/database', destination: '/docs/guides/database/overview', }, + { + permanent: true, + source: '/docs/getting-started', + destination: '/docs/guides/getting-started/architecture', + }, { permanent: true, source: '/docs/architecture', @@ -1786,6 +1717,11 @@ module.exports = [ source: '/docs/guides/with-ionic-angular', destination: '/docs/guides/getting-started/tutorials/with-ionic-angular', }, + { + permanent: true, + source: '/docs/guides/tutorials', + destination: '/docs/guides/getting-started#tutorials', + }, { permanent: true, source: '/docs/guides/hosting/overview', @@ -1808,18 +1744,18 @@ module.exports = [ }, { permanent: true, - source: '/docs/guides/cli', - destination: '/docs/guides/resources/supabase-cli', + source: '/docs/guides/resources/supabase-cli', + destination: '/docs/guides/cli', }, { permanent: true, - source: '/docs/guides/cli/local-development', - destination: '/docs/guides/resources/supabase-cli/local-development', + source: '/docs/guides/resources/supabase-cli/local-development', + destination: '/docs/guides/cli/local-development', }, { permanent: true, - source: '/docs/guides/cli/managing-environments', - destination: '/docs/guides/resources/supabase-cli/managing-environments', + source: '/docs/guides/resources/supabase-cli/managing-environments', + destination: '/docs/guides/cli/managing-environments', }, { permanent: true, diff --git a/apps/www/pages/support.tsx b/apps/www/pages/support.tsx index acb8805807f..e6babad27d1 100644 --- a/apps/www/pages/support.tsx +++ b/apps/www/pages/support.tsx @@ -199,7 +199,7 @@ const Index = ({}: Props) => { border-gray-100 bg-white p-5 pt-14 dark:border-gray-600" > - + diff --git a/docker/.dockerignore b/docker/.dockerignore deleted file mode 100644 index b5924c32836..00000000000 --- a/docker/.dockerignore +++ /dev/null @@ -1,7 +0,0 @@ -node_modules/ -.vercel -.next -.env.local -.env.production -.env.dev -.env.* \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 5b5e2125437..0080171f90f 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -211,7 +211,7 @@ services: # Comment out everything below this point if you are using an external Postgres database db: container_name: supabase-db - image: supabase/postgres:14.1.0.89 + image: supabase/postgres:14.1.0.106 healthcheck: test: pg_isready -U postgres -h localhost interval: 5s diff --git a/spec/cli_v1_commands.yaml b/spec/cli_v1_commands.yaml index c15f3125a2d..c060b75634e 100644 --- a/spec/cli_v1_commands.yaml +++ b/spec/cli_v1_commands.yaml @@ -1,7 +1,7 @@ clispec: '001' info: id: cli - version: 1.33.0 + version: 1.34.5 title: Supabase CLI language: sh source: https://github.com/supabase/cli @@ -247,12 +247,15 @@ commands: - id: supabase-start title: supabase start summary: Start containers for Supabase local development - description: |- + description: |2 + Starts the Supabase local development stack. Requires `supabase/config.toml` to be created in your current working directory by running `supabase init`. All service containers are started by default. You can exclude those not needed by passing in `-x` flag. + + Health checks are automatically added to verify the started containers. Use `--ignore-health-check` flag to ignore these errors. examples: - id: basic-usage name: Basic usage @@ -268,6 +271,13 @@ commands: Excluding container: supabase/studio:20221214-4eecc99 Seeding data supabase/seed.sql... Started supabase local development setup. + - id: ignore-health-check + name: Ignore service health checks + code: supabase start --ignore-health-check + response: | + Seeding data supabase/seed.sql... + service not healthy: [supabase_storage_cli] + Started supabase local development setup. tags: - local-dev links: [] @@ -279,6 +289,10 @@ commands: description: | Names of containers to not start. [gotrue, realtime, storage-api, imgproxy, kong, inbucket, postgrest, pgadmin-schema-diff, migra, postgres-meta, studio, deno-relay] default_value: '[]' + - id: ignore-health-check + name: --ignore-health-check + description: Ignore unhealthy services and exit 0 + default_value: 'false' - id: supabase-secrets title: supabase secrets summary: Manage Supabase secrets @@ -595,7 +609,8 @@ commands: - id: supabase-login title: supabase login summary: Authenticate using an access token - description: |- + description: |2 + Connect the Supabase CLI to your Supabase account by logging in with your [personal access token](https://app.supabase.com/account/tokens). Your access token is stored securely in [native credentials storage](https://github.com/zalando/go-keyring#dependencies). If native credentials storage is unavailable, it will be written to a plain text file at `~/.supabase/access-token`. @@ -620,7 +635,8 @@ commands: - id: supabase-link title: supabase link summary: Link to a Supabase project - description: |- + description: |2 + Link your local development project to a hosted Supabase project. PostgREST configurations are fetched from the Supabase platform and validated against your local configuration file. @@ -659,7 +675,8 @@ commands: - id: supabase-init title: supabase init summary: Initialize a local project - description: |- + description: |2 + Initialize configurations for Supabase local development. A `supabase/config.toml` file is created in your current working directory. This configuration is specific to each local project. @@ -738,6 +755,7 @@ commands: subcommands: - supabase-functions-delete - supabase-functions-deploy + - supabase-functions-download - supabase-functions-new - supabase-functions-serve flags: [] @@ -770,6 +788,20 @@ commands: usage: supabase functions new subcommands: [] flags: [] + - id: supabase-functions-download + title: supabase functions download + summary: Download a Function from Supabase + description: | + Download the source code for a Function from the linked Supabase project. + tags: [] + links: [] + usage: supabase functions download [flags] + subcommands: [] + flags: + - id: project-ref + name: --project-ref + description: Project ref of the Supabase project. + default_value: '' - id: supabase-functions-deploy title: supabase functions deploy summary: Deploy a Function to Supabase diff --git a/spec/common-cli-sections.json b/spec/common-cli-sections.json index 1407e6fa344..8e5dff89916 100644 --- a/spec/common-cli-sections.json +++ b/spec/common-cli-sections.json @@ -231,6 +231,12 @@ "slug": "supabase-functions-new", "type": "cli-command" }, + { + "id": "supabase-functions-download", + "title": "Download a function", + "slug": "supabase-functions-download", + "type": "cli-command" + }, { "id": "supabase-functions-serve", "title": "Serve a function", diff --git a/spec/supabase_py_v2.yml b/spec/supabase_py_v2.yml index 8a1fe642367..0e151526427 100644 --- a/spec/supabase_py_v2.yml +++ b/spec/supabase_py_v2.yml @@ -17,7 +17,6 @@ info: functions: - id: initializing - $ref: '@supabase/supabase-js.index.SupabaseClient.constructor' description: | You can initialize a new Supabase client using the `create_client()` method. @@ -50,7 +49,6 @@ functions: - id: sign-up title: 'sign_up()' - $ref: '@supabase/gotrue-js.GoTrueClient.signUp' notes: | - By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://app.supabase.com/project/_/auth/providers). - **Confirm email** determines if users need to confirm their email address after signing up. @@ -88,7 +86,6 @@ functions: ``` - id: sign-out title: 'sign_out()' - $ref: '@supabase/gotrue-js.GoTrueClient.signOut' notes: | - In order to use the `signOut()` method, the user needs to be signed in first. examples: @@ -100,7 +97,6 @@ functions: ``` - id: verify-otp title: 'verify_otp()' - $ref: '@supabase/gotrue-js.GoTrueClient.verifyOtp' notes: | - The `verifyOtp` method takes in different verification types. If a phone number is used, the type can either be `sms` or `phone_change`. If an email address is used, the type can be one of the following: `signup`, `magiclink`, `recovery`, `invite` or `email_change`. - The verification type used should be determined based on the corresponding auth method called before `verifyOtp` to sign up / sign-in a user. @@ -114,7 +110,6 @@ functions: - id: get-session title: 'get_session_from_url' - $ref: '@supabase/gotrue-js.GoTrueClient.getSession' examples: - id: get-session name: Get the session data from URL @@ -125,7 +120,6 @@ functions: - id: set-session title: 'set_session()' - $ref: '@supabase/gotrue-js.GoTrueClient.setSession' notes: | - `setSession()` takes in a refresh token and uses it to get a new session. - The refresh token can only be used once to obtain a new session. @@ -142,7 +136,6 @@ functions: ``` - id: refresh-session title: 'refresh_ession()' - $ref: '@supabase/gotrue-js.GoTrueClient.refreshSession' notes: | - This method will refresh the session whether the current one is expired or not. - Both examples destructure `user` and `session` from `data`. This is not required; so `const { data, error } =` is also valid. @@ -155,7 +148,6 @@ functions: ``` - id: select title: 'Fetch data: select()' - $ref: '@supabase/postgrest-js.PostgrestQueryBuilder.select' notes: | - By default, Supabase projects return a maximum of 1,000 rows. This setting can be changed in your project's [API settings](https://app.supabase.com/project/_/settings/api). It's recommended that you keep it low to limit the payload size of accidental or malicious requests. - `apikey` is a reserved keyword if you're using the [Supabase Platform](/docs/guides/platform) and [should be avoided as a column name](https://github.com/supabase/supabase/issues/5465). @@ -181,7 +173,6 @@ functions: title: 'invoke()' description: | Invoke a Supabase Function. - $ref: '@supabase/functions-js.F' notes: | - Requires an Authorization header. - When you pass in a body to your function, we automatically attach the Content-Type header for `Blob`, `ArrayBuffer`, `File`, `FormData` and `String`. If it doesn't match any of these types we assume the payload is `json`, serialise it and attach the `Content-Type` header as `application/json`. You can override this behaviour by passing in a `Content-Type` header of your own. @@ -204,7 +195,6 @@ functions: - id: list-buckets title: 'list_buckets()' - $ref: '@supabase/storage-js.packages/StorageBucketApi.default.listBuckets' notes: | - RLS policy permissions required: - `buckets` table permissions: `select` @@ -220,7 +210,6 @@ functions: - id: get-bucket title: 'get_bucket()' - $ref: '@supabase/storage-js.packages/StorageBucketApi.default.getBucket' notes: | - RLS policy permissions required: - `buckets` table permissions: `select` @@ -236,7 +225,6 @@ functions: - id: create-bucket title: 'create_bucket()' - $ref: '@supabase/storage-js.packages/StorageBucketApi.default.createBucket' notes: | - RLS policy permissions required: - `buckets` table permissions: `insert` @@ -252,7 +240,6 @@ functions: - id: empty-bucket title: 'empty_bucket()' - $ref: '@supabase/storage-js.packages/StorageBucketApi.default.emptyBucket' notes: | - RLS policy permissions required: - `buckets` table permissions: `select` @@ -267,7 +254,6 @@ functions: ``` - id: delete-bucket title: 'delete_bucket()' - $ref: '@supabase/storage-js.packages/StorageBucketApi.default.deleteBucket' notes: | - RLS policy permissions required: - `buckets` table permissions: `select` and `delete` @@ -283,7 +269,6 @@ functions: - id: from-upload title: 'from_.upload()' - $ref: '@supabase/storage-js.packages/StorageFileApi.default.upload' notes: | - RLS policy permissions required: - `buckets` table permissions: none @@ -299,7 +284,6 @@ functions: ``` - id: from-move title: 'from_.move()' - $ref: '@supabase/storage-js.packages/StorageFileApi.default.move' notes: | - RLS policy permissions required: - `buckets` table permissions: none @@ -315,7 +299,6 @@ functions: - id: from-create-signed-url title: 'from_.create_signed_url()' - $ref: '@supabase/storage-js.packages/StorageFileApi.default.createSignedUrl' notes: | - RLS policy permissions required: - `buckets` table permissions: none @@ -331,7 +314,6 @@ functions: - id: from-get-public-url title: 'from_.get_public_url()' - $ref: '@supabase/storage-js.packages/StorageFileApi.default.getPublicUrl' notes: | - The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://app.supabase.com), clicking the overflow menu on a bucket and choosing "Make public" - RLS policy permissions required: @@ -348,7 +330,6 @@ functions: - id: from-download title: 'from_.download()' - $ref: '@supabase/storage-js.packages/StorageFileApi.default.download' notes: | - RLS policy permissions required: - `buckets` table permissions: none @@ -366,7 +347,6 @@ functions: - id: from-remove title: 'from_.remove()' - $ref: '@supabase/storage-js.packages/StorageFileApi.default.remove' notes: | - RLS policy permissions required: - `buckets` table permissions: none @@ -381,7 +361,6 @@ functions: ``` - id: from-list title: 'from_.list()' - $ref: '@supabase/storage-js.packages/StorageFileApi.default.list' notes: | - RLS policy permissions required: - `buckets` table permissions: none @@ -396,6 +375,5 @@ functions: ``` - id: subscribe title: on().subscribe() - $ref: '@supabase/realtime-js.RealtimeChannel.on' notes: | - We are implementing this feature at the moment. If you have queries feel free to open an issue on the [realtime-py](https://github.com/supabase-community) repository. diff --git a/studio/.dockerignore b/studio/.dockerignore deleted file mode 100644 index b5924c32836..00000000000 --- a/studio/.dockerignore +++ /dev/null @@ -1,7 +0,0 @@ -node_modules/ -.vercel -.next -.env.local -.env.production -.env.dev -.env.* \ No newline at end of file diff --git a/studio/Dockerfile b/studio/Dockerfile index a55ed33b9ea..32fe1d0f128 100644 --- a/studio/Dockerfile +++ b/studio/Dockerfile @@ -1,9 +1,11 @@ +# To be run in the root of the turbo monorepo +# NOTE: It's highly raccomended to use the new builder, Buildkit. https://docs.docker.com/build/buildkit/ ## USAGE: -# Build: docker build --target production -t supabase/studio:latest . +# Build: docker buildx build --target production -t supabase/studio:latest . # Run: docker run -p 3000:3000 supabase/studio # Deploy: docker push supabase/studio:latest # Clean build: -# docker build --target production --no-cache -t supabase/studio:latest . +# docker buildx build --target production --no-cache -t supabase/studio:latest . # docker builder prune FROM node:16-slim as base @@ -11,36 +13,53 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ python3 \ build-essential \ && rm -rf /var/lib/apt/lists/* +RUN npm i -g turbo -FROM base as builder -WORKDIR /usr/src/app -COPY package*.json ./ -COPY turbo.json . -COPY packages packages -COPY studio studio -# the following command is still unsupported for npm // https://github.com/vercel/turborepo/issues/1830 -# RUN npm i -g turbo -# RUN turbo prune --scope=studio --docker -# RUN npm clean-install && npm cache clean --force +# Calculate if dependencies are outdated +FROM base as turbo +WORKDIR /app +COPY . . +# Prune unneeded dependencies with turbo (from apps/ for example) +RUN turbo prune --scope=studio --docker + +# Install dev dependencies (only if needed) +FROM base as deps +WORKDIR /app +COPY --from=turbo /app/out/json/ . +COPY --from=turbo /app/out/package-lock.json ./ +RUN npm clean-install && npm cache clean --force + +FROM deps as deps-prod +RUN npm prune --production +# Remove packages dependencies (not needed in production) +RUN rm -rf /app/packages + +# builder contains dependencies and source code not compiled +FROM deps as builder +WORKDIR /app +COPY --from=turbo /app/out/full ./ ENTRYPOINT ["docker-entrypoint.sh"] FROM builder as dev -COPY --from=builder /usr/src/app ./ -RUN npm clean-install && npm cache clean --force EXPOSE 8082 CMD ["npx", "turbo", "run", "dev", "--filter=studio"] +# Compile NextJS FROM builder as productionprep WORKDIR /app -COPY --from=builder /usr/src/app ./ -RUN npm clean-install \ - && npm cache clean --force \ - && npx turbo run build --scope=studio --include-dependencies --no-deps \ - && rm -rf ./studio/.next/cache/webpack \ - && npm prune --production +RUN npx turbo run build --scope=studio --include-dependencies --no-deps \ + && rm -rf ./studio/.next/cache/webpack +# Copy only compiled code and dependencies FROM node:16-slim as production -COPY --from=productionprep /app /app +# Copy package(s).json and package-lock.json +COPY --from=turbo /app/out/json /app +# Copy all dependencies for production +COPY --from=deps-prod /app /app +# Copy necessary files +COPY --from=turbo /app/out/full/studio /app/studio +COPY --from=productionprep /app/studio/.next /app/studio/.next WORKDIR /app/studio EXPOSE 3000 +HEALTHCHECK --interval=5s --timeout=5s --retries=3 CMD node -e "require('http').get('http://localhost:3000/api/profile', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})" CMD ["npm", "run", "start"] diff --git a/studio/components/grid/components/editor/SelectEditor.tsx b/studio/components/grid/components/editor/SelectEditor.tsx index f980583fad0..9c65b367e11 100644 --- a/studio/components/grid/components/editor/SelectEditor.tsx +++ b/studio/components/grid/components/editor/SelectEditor.tsx @@ -1,10 +1,10 @@ -import { Listbox } from '@supabase/ui' +import { Select } from 'ui' import { EditorProps } from '@supabase/react-data-grid' import { useTrackedState } from 'components/grid/store' interface SelectEditorProps extends EditorProps { - options: { label: string; value: string }[] + options: { label: string; _value: string }[] } export function SelectEditor({ @@ -19,11 +19,12 @@ export function SelectEditor({ const value = row[column.key as keyof TRow] as unknown as string - function onChange(value: string) { - if (!value || value == '') { + function onChange(event: any) { + debugger + if (!event.target.value || event.target.value == '') { onRowChange({ ...row, [column.key]: null }, true) } else { - onRowChange({ ...row, [column.key]: value }, true) + onRowChange({ ...row, [column.key]: event.target.value }, true) } } @@ -32,7 +33,7 @@ export function SelectEditor({ } return ( - ({ onChange={onChange} onBlur={onBlur} > - - NULL - - {options.map(({ label, value }) => ( - + NULL + {options.map(({ label, _value }) => ( + {label} - + ))} - + ) } diff --git a/studio/components/interfaces/Auth/EmailTemplates/EmailTemplates.tsx b/studio/components/interfaces/Auth/EmailTemplates/EmailTemplates.tsx index 0e86b1247a5..a982880a845 100644 --- a/studio/components/interfaces/Auth/EmailTemplates/EmailTemplates.tsx +++ b/studio/components/interfaces/Auth/EmailTemplates/EmailTemplates.tsx @@ -1,4 +1,4 @@ -import { Tabs } from 'ui' +import { Button, IconExternalLink, Tabs } from 'ui' import { observer } from 'mobx-react-lite' import { TEMPLATES_SCHEMAS } from 'stores/authConfig/schema' @@ -8,10 +8,20 @@ import TemplateEditor from './TemplateEditor' const EmailTemplates = observer(() => { return (
- + + apiWrapper(req, res, handler, { withAuth: true }) + +async function handler(req: NextApiRequest, res: NextApiResponse) { + const { method } = req + + switch (method) { + case 'GET': + return handleGetAll(req, res) + default: + res.setHeader('Allow', ['GET']) + res.status(405).json({ error: { message: `Method ${method} Not Allowed` } }) + } +} + +const handleGetAll = async (req: NextApiRequest, res: NextApiResponse) => { + const headers = constructHeaders(req.headers) + let response = await get(`${PG_META_URL}/foreign-tables`, { + headers, + }) + if (response.error) { + return res.status(400).json({ error: response.error }) + } + return res.status(200).json(response) +} diff --git a/studio/pages/api/pg-meta/[ref]/views.ts b/studio/pages/api/pg-meta/[ref]/views.ts new file mode 100644 index 00000000000..aa205be3019 --- /dev/null +++ b/studio/pages/api/pg-meta/[ref]/views.ts @@ -0,0 +1,32 @@ +import { NextApiRequest, NextApiResponse } from 'next' + +import apiWrapper from 'lib/api/apiWrapper' +import { get } from 'lib/common/fetch' +import { constructHeaders } from 'lib/api/apiHelpers' +import { PG_META_URL } from 'lib/constants' + +export default (req: NextApiRequest, res: NextApiResponse) => + apiWrapper(req, res, handler, { withAuth: true }) + +async function handler(req: NextApiRequest, res: NextApiResponse) { + const { method } = req + + switch (method) { + case 'GET': + return handleGetAll(req, res) + default: + res.setHeader('Allow', ['GET']) + res.status(405).json({ error: { message: `Method ${method} Not Allowed` } }) + } +} + +const handleGetAll = async (req: NextApiRequest, res: NextApiResponse) => { + const headers = constructHeaders(req.headers) + let response = await get(`${PG_META_URL}/views`, { + headers, + }) + if (response.error) { + return res.status(400).json({ error: response.error }) + } + return res.status(200).json(response) +} diff --git a/studio/pages/new/index.tsx b/studio/pages/new/index.tsx index 201c9d7a8d0..fb8c96b2bb2 100644 --- a/studio/pages/new/index.tsx +++ b/studio/pages/new/index.tsx @@ -128,7 +128,7 @@ const Wizard: NextPageWithLayout = () => { { {orgKind == 'COMPANY' ? (