From 00baa36c4f5ace4cf469914d8894ca69179cfa69 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Fri, 14 Oct 2022 20:46:40 +0200 Subject: [PATCH 01/15] updates for opensearch --- .../_blog/2022-10-14-postgres-full-text-search-vs-the-rest.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/_blog/2022-10-14-postgres-full-text-search-vs-the-rest.mdx b/apps/www/_blog/2022-10-14-postgres-full-text-search-vs-the-rest.mdx index 458a397fecf..d14a3b671cb 100644 --- a/apps/www/_blog/2022-10-14-postgres-full-text-search-vs-the-rest.mdx +++ b/apps/www/_blog/2022-10-14-postgres-full-text-search-vs-the-rest.mdx @@ -332,7 +332,7 @@ There's a _lot_ of tuning left undone here, but a few points stand out right awa - Postgres FTS is quite close performance-wise to many other solutions, at least in their default configuration. - Only Typesense and MeiliSearch properly handled mis-spellings (the "suprman" query). - Typesense was relatively strict with matches compared to other engines. -- OpenSearch was _very_ fast with ingest, but also failed with the misspelling out of the box. +- OpenSearch was _very_ fast with ingest, but the default configuration doesn't index misspellings. - In-memory SQLite is by far the fastest, and PG isn't too far behind for this small data set. ## Feedback from the other products From d2d327b796e1f79f54a754137c0074af1060d156 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Fri, 14 Oct 2022 16:40:51 -0230 Subject: [PATCH 02/15] Chore/fix docs build (#9566) Cast spec before switching on it --- apps/reference/generator/api.ts | 2 +- apps/reference/generator/cli.ts | 3 +-- apps/reference/generator/config.ts | 2 +- apps/reference/generator/sdk.ts | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/reference/generator/api.ts b/apps/reference/generator/api.ts index b15086938e7..602842c4014 100644 --- a/apps/reference/generator/api.ts +++ b/apps/reference/generator/api.ts @@ -10,7 +10,7 @@ export default async function gen( apiUrl: string ) { const specRaw = fs.readFileSync(inputFileName, 'utf8') - const spec = JSON.parse(specRaw) + const spec = JSON.parse(specRaw) as any // console.log('spec', spec) switch (spec.openapi || spec.swagger) { diff --git a/apps/reference/generator/cli.ts b/apps/reference/generator/cli.ts index d9708f0752d..9d554feb79f 100644 --- a/apps/reference/generator/cli.ts +++ b/apps/reference/generator/cli.ts @@ -6,8 +6,7 @@ import template from './templates/CliTemplate' import type { CliSpec } from './types/CliSpec' export default async function gen(inputFileName: string, outputDir: string) { - const spec = yaml.load(fs.readFileSync(inputFileName, 'utf8')) - // console.log('spec', spec) + const spec = yaml.load(fs.readFileSync(inputFileName, 'utf8')) as any switch (spec.clispec) { case '001': diff --git a/apps/reference/generator/config.ts b/apps/reference/generator/config.ts index 690a77dc833..378857b998c 100644 --- a/apps/reference/generator/config.ts +++ b/apps/reference/generator/config.ts @@ -12,7 +12,7 @@ type Section = { } export default async function gen(inputFileName: string, outputDir: string) { - const spec = yaml.load(fs.readFileSync(inputFileName, 'utf8')) + const spec = yaml.load(fs.readFileSync(inputFileName, 'utf8')) as any // console.log('spec', spec) switch (spec.configspec) { diff --git a/apps/reference/generator/sdk.ts b/apps/reference/generator/sdk.ts index ee290d16d2f..2adb10c3898 100644 --- a/apps/reference/generator/sdk.ts +++ b/apps/reference/generator/sdk.ts @@ -6,7 +6,7 @@ import { writeToDisk } from './helpers' import type { SdkSpec } from './types/SdkSpec' export default async function gen(inputFileName: string, outputDir: string) { - const spec = yaml.load(fs.readFileSync(inputFileName, 'utf8')) + const spec = yaml.load(fs.readFileSync(inputFileName, 'utf8')) as any // console.log('spec', spec) switch (spec.sdkspec) { From 9e1f5fdfddecbd3e63b462754bc4451ce93a32a3 Mon Sep 17 00:00:00 2001 From: Maulik Date: Sat, 15 Oct 2022 01:56:38 +0530 Subject: [PATCH 03/15] fix: add nginx env variables for kong (#9386) add nginx env variables for kong --- docker/docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index af69fb28459..8c14bd4d2b8 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -38,6 +38,8 @@ services: # https://github.com/supabase/cli/issues/14 KONG_DNS_ORDER: LAST,A,CNAME KONG_PLUGINS: request-transformer,cors,key-auth,acl + KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k + KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k volumes: - ./volumes/api:/var/lib/kong From e44e7033fe8a1b077cf9a30178eabfb44998828c Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 14 Oct 2022 20:32:26 -0700 Subject: [PATCH 04/15] Update prisma.mdx Change "step 3" to "step 1". --- apps/reference/docs/guides/integrations/prisma.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/reference/docs/guides/integrations/prisma.mdx b/apps/reference/docs/guides/integrations/prisma.mdx index 1915b3a8b9f..d376db45fd3 100644 --- a/apps/reference/docs/guides/integrations/prisma.mdx +++ b/apps/reference/docs/guides/integrations/prisma.mdx @@ -62,7 +62,7 @@ This project comes with TypeScript configured and has the following structure. ### Configuring the project to use PostgreSQL Go ahead and delete the `prisma/dev.db` file because we will be switching to PostgreSQL. -Next, inside the `prisma/.env` file, update the value of the `DATABASE_URL` variable to the connection string you got in **step 3**. The URL might look as follows: +Next, inside the `prisma/.env` file, update the value of the `DATABASE_URL` variable to the connection string you got in **step 1**. The URL might look as follows: ```env # prisma/.env From 9a2e68ddecdbabaf436d47ad2f562b79b6771918 Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 14 Oct 2022 21:09:17 -0700 Subject: [PATCH 05/15] Fix step 4 -> step 4 --- apps/reference/docs/guides/integrations/prisma.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/reference/docs/guides/integrations/prisma.mdx b/apps/reference/docs/guides/integrations/prisma.mdx index d376db45fd3..e7f8d517ccc 100644 --- a/apps/reference/docs/guides/integrations/prisma.mdx +++ b/apps/reference/docs/guides/integrations/prisma.mdx @@ -122,7 +122,7 @@ This will create a `prisma/migrations` folder inside your `prisma` directory and If you’re working in a serverless environment (for example Node.js functions hosted on AWS Lambda, Vercel or Netlify Functions), you need to set up [connection pooling](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#serverless-environments-faas) using a tool like [PgBouncer](https://www.pgbouncer.org/). That’s because every function invocation may result in a [new connection to the database](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#the-serverless-challenge). Supabase [supports connection management using PgBouncer](https://supabase.io/blog/2021/04/02/supabase-pgbouncer#what-is-connection-pooling) and are enabled by default. Go to the **Database** page from the sidebar in the Supabase dashboard and navigate to **connection pool** settings ![Connection pool settings](/img/guides/integrations/prisma/w0oowg8vq435ob5c3gf0.png) -When running migrations you need to use the non pooled connection URL (like the one we used in **step 4**). However, when deploying your app, you’ll use the pooled connection URL and add the `?pgbouncer=true` flag to the PostgreSQL connection URL. To minimize the number of concurrent connections, setting the `connection_limit` to `1` is also recommended. So the URL might look as follows: +When running migrations you need to use the non pooled connection URL (like the one we used in **step 1**). However, when deploying your app, you’ll use the pooled connection URL and add the `?pgbouncer=true` flag to the PostgreSQL connection URL. To minimize the number of concurrent connections, setting the `connection_limit` to `1` is also recommended. So the URL might look as follows: ```env # prisma/.env From 77660bebbd6496a674bd0289e03dbaee127b1479 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Sat, 15 Oct 2022 13:05:05 +0000 Subject: [PATCH 06/15] Update the angular user management quickstart (#9509) * Updae the angular user management quickstart * Update repository references * Update apps/reference/docs/guides/with-angular.mdx Co-authored-by: dng * Update apps/reference/docs/guides/with-angular.mdx Co-authored-by: dng * Update apps/reference/docs/guides/with-angular.mdx Co-authored-by: dng * Update apps/reference/docs/guides/with-angular.mdx Co-authored-by: dng Co-authored-by: dng --- apps/reference/docs/guides/with-angular.mdx | 529 ++++++++++++-------- 1 file changed, 322 insertions(+), 207 deletions(-) diff --git a/apps/reference/docs/guides/with-angular.mdx b/apps/reference/docs/guides/with-angular.mdx index 93389bfc2df..2d4dc88df31 100644 --- a/apps/reference/docs/guides/with-angular.mdx +++ b/apps/reference/docs/guides/with-angular.mdx @@ -33,7 +33,7 @@ If you want to do it yourself, let's get started! ### GitHub -Whenever you get stuck at any point, take a look at [this repo](https://github.com/angular-supa/supabase-angular-user-management). +Whenever you get stuck at any point, take a look at [this repo](https://github.com/supabase/examples/tree/main/supabase-js-v2/user-management/angular-user-management). ## Project set up @@ -180,281 +180,365 @@ export const environment = { Now that we have the API credentials in place, let's create a **SupabaseService** with `ng g s supabase` to initialize the Supabase client and implement functions to communicate with the Supabase API. ```ts title="src/app/supabase.service.ts" -import { Injectable } from '@angular/core' +import { Injectable } from '@angular/core'; import { AuthChangeEvent, + AuthSession, createClient, Session, SupabaseClient, -} from '@supabase/supabase-js' -import { environment } from '../environments/environment' + User, +} from '@supabase/supabase-js'; +import { environment } from 'src/environments/environment'; +import { Database } from 'src/schema'; export interface Profile { - username: string - website: string - avatar_url: string + id?: string; + username: string; + website: string; + avatar_url: string; } @Injectable({ providedIn: 'root', }) export class SupabaseService { - private supabase: SupabaseClient + private supabase: SupabaseClient; + _session: AuthSession | null = null; constructor() { this.supabase = createClient( environment.supabaseUrl, environment.supabaseKey - ) - } - - get user() { - return this.supabase.auth.user() + ); } get session() { - return this.supabase.auth.session() + this.supabase.auth.getSession().then(({ data }) => { + this._session = data.session; + }) + return this._session; } - get profile() { + profile(user: User) { return this.supabase .from('profiles') .select(`username, website, avatar_url`) - .eq('id', this.user?.id) - .single() + .eq('id', user.id) + .single(); } authChanges( callback: (event: AuthChangeEvent, session: Session | null) => void ) { - return this.supabase.auth.onAuthStateChange(callback) + return this.supabase.auth.onAuthStateChange(callback); } signIn(email: string) { - return this.supabase.auth.signIn({ email }) + return this.supabase.auth.signInWithOtp({ email }); } signOut() { - return this.supabase.auth.signOut() + return this.supabase.auth.signOut(); } updateProfile(profile: Profile) { const update = { ...profile, - id: this.user?.id, updated_at: new Date(), - } + }; - return this.supabase.from('profiles').upsert(update, { - returning: 'minimal', // Don't return the value after inserting - }) + return this.supabase.from('profiles').upsert(update); } downLoadImage(path: string) { - return this.supabase.storage.from('avatars').download(path) + return this.supabase.storage.from('avatars').download(path); } uploadAvatar(filePath: string, file: File) { - return this.supabase.storage.from('avatars').upload(filePath, file) + return this.supabase.storage.from('avatars').upload(filePath, file); } } ``` -And one optional step is to update the CSS file `src/index.css` to make the app look nice. -You can find the full contents of this file [here](https://raw.githubusercontent.com/angular-supa/supabase-angular-user-management/main/src/styles.css). +Optionally, update [src/styles.css](https://raw.githubusercontent.com/supabase/examples/main/supabase-js-v2/user-management/angular-user-management/src/styles.css) to style the app. ### Set up a Login component Let's set up an Angular component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords. Create an **AuthComponent** with `ng g c auth` Angular CLI command. -```ts title="src/app/auth.component.ts" -import { Component } from '@angular/core' -import { SupabaseService } from './supabase.service' +```ts title="src/app/auth/auth.component.ts" +import { Component, OnInit } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; +import { SupabaseService } from '../supabase.service'; @Component({ selector: 'app-auth', - template: ` -
-
-

Supabase + Angular

-

Sign in via magic link with your email below

-
- -
-
- -
-
-
- `, + templateUrl: './auth.component.html', + styleUrls: ['./auth.component.css'], }) -export class AuthComponent { - loading = false +export class AuthComponent implements OnInit { + loading = false; - constructor(private readonly supabase: SupabaseService) {} + signInForm = this.formBuilder.group({ + email: '', + }); - async handleLogin(input: string) { + constructor( + private readonly supabase: SupabaseService, + private readonly formBuilder: FormBuilder + ) {} + + ngOnInit(): void {} + + async onSubmit(): Promise { try { - this.loading = true - await this.supabase.signIn(input) - alert('Check your email for the login link!') + this.loading = true; + const email = this.signInForm.value.email as string; + const { error } = await this.supabase.signIn(email); + if (error) throw error; + alert('Check your email for the login link!'); } catch (error) { - alert(error.error_description || error.message) + if (error instanceof Error) { + alert(error.message); + } } finally { - this.loading = false + this.signInForm.reset(); + this.loading = false; } } } ``` +```html title="src/app/auth/auth.component.html" +
+
+

Supabase + Angular

+

Sign in via magic link with your email below

+
+
+ + +
+
+ +
+
+
+
-
- - -
-
- - -
-
- - -
- -
- -
- -
- -
- - `, + templateUrl: './account.component.html', + styleUrls: ['./account.component.css'], }) export class AccountComponent implements OnInit { - loading = false - profile: Profile | undefined + loading = false; + profile!: Profile; - @Input() session: Session | undefined + @Input() + session!: AuthSession; - constructor(private readonly supabase: SupabaseService) {} + updateProfileForm = this.formBuilder.group({ + username: '', + website: '', + avatar_url: '', + }); - ngOnInit() { - this.getProfile() + constructor( + private readonly supabase: SupabaseService, + private formBuilder: FormBuilder + ) {} + + async ngOnInit(): Promise { + await this.getProfile(); + + const { username, website, avatar_url } = this.profile; + this.updateProfileForm.patchValue({ + username, + website, + avatar_url, + }); } async getProfile() { try { - this.loading = true - let { data: profile, error, status } = await this.supabase.profile + this.loading = true; + const { user } = this.session; + let { data: profile, error, status } = await this.supabase.profile(user); if (error && status !== 406) { - throw error + throw error; } if (profile) { - this.profile = profile + this.profile = profile; } } catch (error) { - alert(error.message) + if (error instanceof Error) { + alert(error.message); + } } finally { - this.loading = false + this.loading = false; } } - async updateProfile( - username: string, - website: string, - avatar_url: string = '' - ) { + async updateProfile(): Promise { try { - this.loading = true - await this.supabase.updateProfile({ username, website, avatar_url }) + this.loading = true; + const { user } = this.session; + + const username = this.updateProfileForm.value.username as string; + const website = this.updateProfileForm.value.website as string; + const avatar_url = this.updateProfileForm.value.avatar_url as string; + + const { error } = await this.supabase.updateProfile({ + id: user.id, + username, + website, + avatar_url, + }); + if (error) throw error; } catch (error) { - alert(error.message) + if (error instanceof Error) { + alert(error.message); + } } finally { - this.loading = false + this.loading = false; } } async signOut() { - await this.supabase.signOut() + await this.supabase.signOut(); } } ``` +```html title="src/app/account/account.component.html" +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ +
+ +
+
+``` + ### Launch! Now that we have all the components in place, let's update **AppComponent**: ```ts title="src/app/app.component.ts" -import { Component, OnInit } from '@angular/core' -import { SupabaseService } from './supabase.service' +import { Component, OnInit } from '@angular/core'; +import { SupabaseService } from './supabase.service'; @Component({ selector: 'app-root', - template: ` -
- - - - -
- `, + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'], }) export class AppComponent implements OnInit { - session = this.supabase.session + title = 'angular-user-management'; + + session = this.supabase.session; constructor(private readonly supabase: SupabaseService) {} ngOnInit() { - this.supabase.authChanges((_, session) => (this.session = session)) + this.supabase.authChanges((_, session) => (this.session = session)); } } ``` +```html title="src/app/app.component.html" +
+ + + + +
+``` + +`app.module.ts` also needs to be modified to include the `ReactiveFormsModule` from the `@angular/forms` package. + +```ts title="src/app/app.module.ts" +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { AppComponent } from './app.component'; +import { AuthComponent } from './auth/auth.component'; +import { AccountComponent } from './account/account.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { AvatarComponent } from './avatar/avatar.component'; + +@NgModule({ + declarations: [ + AppComponent, + AuthComponent, + AccountComponent, + AvatarComponent, + ], + imports: [BrowserModule, ReactiveFormsModule], + providers: [], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + Once that's done, run this in a terminal window: ```bash @@ -474,110 +558,141 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma Let's create an avatar for the user so that they can upload a profile photo. Create an **AvatarComponent** with `ng g c avatar` Angular CLI command. -```ts title="src/app/avatar.component.ts" -import { Component, EventEmitter, Input, Output } from '@angular/core' -import { SupabaseService } from './supabase.service' -import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' +```ts title="src/app/avatar/avatar.component.ts" +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { SafeResourceUrl, DomSanitizer } from '@angular/platform-browser'; +import { SupabaseService } from '../supabase.service'; @Component({ selector: 'app-avatar', - template: ` -
- Avatar -
-
-
- - -
- `, + templateUrl: './avatar.component.html', + styleUrls: ['./avatar.component.css'], }) -export class AvatarComponent { - _avatarUrl: SafeResourceUrl | undefined - uploading = false +export class AvatarComponent implements OnInit { + _avatarUrl: SafeResourceUrl | undefined; + uploading = false; @Input() - set avatarUrl(url: string | undefined) { + set avatarUrl(url: string | null) { if (url) { - this.downloadImage(url) + this.downloadImage(url); } } - @Output() upload = new EventEmitter() + @Output() upload = new EventEmitter(); constructor( private readonly supabase: SupabaseService, private readonly dom: DomSanitizer ) {} + ngOnInit(): void {} + async downloadImage(path: string) { try { - const { data } = await this.supabase.downLoadImage(path) + const { data } = await this.supabase.downLoadImage(path); if (data instanceof Blob) { this._avatarUrl = this.dom.bypassSecurityTrustResourceUrl( URL.createObjectURL(data) - ) + ); } } catch (error) { - console.error('Error downloading image: ', error.message) + if (error instanceof Error) { + console.error('Error downloading image: ', error.message); + } } } async uploadAvatar(event: any) { try { - this.uploading = true + this.uploading = true; if (!event.target.files || event.target.files.length === 0) { - throw new Error('You must select an image to upload.') + throw new Error('You must select an image to upload.'); } - const file = event.target.files[0] - const fileExt = file.name.split('.').pop() - const fileName = `${Math.random()}.${fileExt}` - const filePath = `${fileName}` + const file = event.target.files[0]; + const fileExt = file.name.split('.').pop(); + const filePath = `${Math.random()}.${fileExt}`; - await this.supabase.uploadAvatar(filePath, file) - this.upload.emit(filePath) + await this.supabase.uploadAvatar(filePath, file); + this.upload.emit(filePath); } catch (error) { - alert(error.message) + if (error instanceof Error) { + alert(error.message); + } } finally { - this.uploading = false + this.uploading = false; } } } ``` +```html title="src/app/avatar/avatar.component.html" +
+ Avatar +
+
+
+ + +
+``` + ### Add the new widget And then we can add the widget on top of the **AccountComponent** html template: -```ts title="src/app/account.component.ts" -template: ` - - +```html title="src/app/account.component.html" +
+ + + +
+``` - -` +And add an `updateAvatar` function along with an `avatarUrl` getter to the **AccountComponent** typescript file: + +```ts title="src/app/account.component.ts" +@Component({ + selector: 'app-account', + templateUrl: './account.component.html', + styleUrls: ['./account.component.css'], +}) +export class AccountComponent implements OnInit { + // ... + get avatarUrl() { + return this.updateProfileForm.value.avatar_url as string; + } + + async updateAvatar(event: string): Promise { + this.updateProfileForm.patchValue({ + avatar_url: event, + }); + await this.updateProfile(); + } + // ... +} ``` ## Next steps From a722e559690f4521335a9fbab7dfa0407955922c Mon Sep 17 00:00:00 2001 From: Remi Rampin Date: Sun, 16 Oct 2022 14:41:00 -0400 Subject: [PATCH 07/15] Fix typo in blog post markup --- .../_blog/2022-10-14-postgres-full-text-search-vs-the-rest.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/_blog/2022-10-14-postgres-full-text-search-vs-the-rest.mdx b/apps/www/_blog/2022-10-14-postgres-full-text-search-vs-the-rest.mdx index d14a3b671cb..f96545c783a 100644 --- a/apps/www/_blog/2022-10-14-postgres-full-text-search-vs-the-rest.mdx +++ b/apps/www/_blog/2022-10-14-postgres-full-text-search-vs-the-rest.mdx @@ -206,7 +206,7 @@ We'll focus on the end-user-centric meaning of performance here (query speed). Postgres is capable of loading CSVs, but since we'll be using other search engines as well, let's convert to format that's much easier to use and process - [Newline Delimited JSON](https://dataprotocols.org/ndjson/). -After a few lines of code and one `[csv2ndjson.mjs` script](https://github.com/VADOSWARE/fts-benchmark/blob/main/src/util/csv2ndjson.mjs) later, we have a `movies.ndjson.json` full of JSON documents that we can easily ingest into any search engine (or other database for that matter!). +After a few lines of code and one [`csv2ndjson.mjs` script](https://github.com/VADOSWARE/fts-benchmark/blob/main/src/util/csv2ndjson.mjs) later, we have a `movies.ndjson.json` full of JSON documents that we can easily ingest into any search engine (or other database for that matter!). ### Inserting the data From 67dfc0823e2fb3d2e152355e9f85e4ca9c7423e7 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Mon, 17 Oct 2022 11:39:11 +0800 Subject: [PATCH 08/15] Update benchmark.tsx --- apps/www/components/CaseStudies/benchmark.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/www/components/CaseStudies/benchmark.tsx b/apps/www/components/CaseStudies/benchmark.tsx index 630e7b6e7cf..1e2a218bf1a 100644 --- a/apps/www/components/CaseStudies/benchmark.tsx +++ b/apps/www/components/CaseStudies/benchmark.tsx @@ -31,11 +31,6 @@ const Benchmark = () => {

- ) From b403c355eeda96640a88fe472205a656ca731200 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Mon, 17 Oct 2022 15:12:38 +0800 Subject: [PATCH 09/15] Update redirects.js --- apps/www/lib/redirects.js | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index 97e6e3bfc04..03cf6382e91 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -1415,4 +1415,50 @@ module.exports = [ // source: '/docs/reference/javascript/auth-api-updateuserbyid', // destination: '/docs/reference/javascript/auth-admin-updateuserbyid', // }, + + // v1 to v2 redirects + + // signIn method is now split into signInWithPassword ,signInWithPasswordless ,signInWithOAuth + // send traffic to v1 docs instead + { + permanent: true, + source: '/docs/reference/javascript/auth-signin', + destination: '/docs/reference/javascript/v1/auth-signin', + }, + // auth.session renamed to auth.getSession + // send to new v2 docs version + { + permanent: true, + source: '/docs/reference/javascript/auth-session', + destination: '/docs/reference/javascript/auth-getsession', + }, + // Removed sendMobileOTP in favor of signInWithOtp + // send traffic to new v2 docs instead + { + permanent: true, + source: '/docs/reference/javascript/auth-api-sendmobileotp', + destination: '/docs/reference/javascript/auth-signinwithotp', + }, + // Replaced update with updateUser + { + permanent: true, + source: '/docs/reference/javascript/auth-update', + destination: '/docs/reference/javascript/auth-updateuser', + }, + // realtime methods been replaced with new names + { + permanent: true, + source: '/docs/reference/javascript/removesubscription', + destination: '/docs/reference/javascript/v1/removesubscription', + }, + { + permanent: true, + source: '/docs/reference/javascript/removeallsubscriptions', + destination: '/docs/reference/javascript/v1/removeallsubscriptions', + }, + { + permanent: true, + source: '/docs/reference/javascript/getsubscriptions', + destination: '/docs/reference/javascript/v1/getsubscriptions', + }, ] From 434078ac59d6b0c15309b52da7fac54893f4d686 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Fri, 14 Oct 2022 23:38:39 +0800 Subject: [PATCH 10/15] chore: update cicd docs on resolving push error --- apps/reference/docs/guides/cli/cicd-workflow.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/reference/docs/guides/cli/cicd-workflow.mdx b/apps/reference/docs/guides/cli/cicd-workflow.mdx index 875d240c300..f0da979b5c0 100644 --- a/apps/reference/docs/guides/cli/cicd-workflow.mdx +++ b/apps/reference/docs/guides/cli/cicd-workflow.mdx @@ -358,12 +358,14 @@ ERROR: must be owner of table employees (SQLSTATE 42501); while executing migrat This is because tables created through Supabase dashboard are owned by `supabase_admin` role while the migration scripts executed through CLI are under `postgres` role. -One way to solve this is to grant `postgres` role additional privileges through the SQL Editor available on Supabase dashboard. For example, the following command grants postgres permissions to alter any table in the public schema. +One way to solve this is to reassign the owner of those tables to `postgres` role. For example, if your table is named `users` in the public schema, you can run the following command to reassign owner. ```sql -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO postgres; +ALTER TABLE users OWNER TO postgres; ``` +Apart from tables, you also need to reassign owner of other entities using their respective commands, including [types](https://www.postgresql.org/docs/current/sql-alterschema.html), [functions](https://www.postgresql.org/docs/current/sql-alterroutine.html), and [schemas](https://www.postgresql.org/docs/current/sql-altertype.html). + ### Rebasing new migrations Sometimes your teammate may merge a new migration file to git main branch, and now you need to rebase your local schema changes on top. From 7f355efc46974d5cc32b459452d5aa9d1c1b14ba Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Mon, 17 Oct 2022 15:24:03 +0800 Subject: [PATCH 11/15] chore: document graphql manual fix --- .../docs/guides/cli/cicd-workflow.mdx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apps/reference/docs/guides/cli/cicd-workflow.mdx b/apps/reference/docs/guides/cli/cicd-workflow.mdx index f0da979b5c0..07afed16df8 100644 --- a/apps/reference/docs/guides/cli/cicd-workflow.mdx +++ b/apps/reference/docs/guides/cli/cicd-workflow.mdx @@ -348,6 +348,24 @@ Once pushed, check that the migration version is up to date for both local and r supabase migration list ``` +### Permission denied on db remote commit + +If you have been using Supabase hosted projects for a long time, you might encounter the following permission error when executing `db remote commit`. + +```bash +Error: Error running pg_dump on remote database: pg_dump: error: query failed: ERROR: permission denied for table _type + +pg_dump: error: query was: LOCK TABLE "graphql"."_type" IN ACCESS SHARE MODE +``` + +To resolve this error, you need to grant `postgres` role permissions to `graphql` schema. You can do that by running the following query from Supabase dashboard's SQL Editor. + +```sql +grant all on all tables in schema graphql to postgres, anon, authenticated, service_role; +grant all on all functions in schema graphql to postgres, anon, authenticated, service_role; +grant all on all sequences in schema graphql to postgres, anon, authenticated, service_role; +``` + ### Permission denied on db push If you created a table through Supabase dashboard, and your new migration script contains `ALTER TABLE` statements, you might run into permission error when applying them on staging or production databases. From b7fca480186bdaea8f0a585c17c979db41a6f8f9 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Mon, 17 Oct 2022 16:49:51 +0800 Subject: [PATCH 12/15] chore: update cli reference docs --- spec/cli_v1_commands.yaml | 180 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 172 insertions(+), 8 deletions(-) diff --git a/spec/cli_v1_commands.yaml b/spec/cli_v1_commands.yaml index e3d03ff6373..f4b6ba415c2 100644 --- a/spec/cli_v1_commands.yaml +++ b/spec/cli_v1_commands.yaml @@ -1,7 +1,7 @@ clispec: '001' info: id: cli - version: 1.0.0 + version: 1.8.5 title: Supabase CLI language: sh source: https://github.com/supabase/cli @@ -11,8 +11,9 @@ info: Supabase CLI provides you with tools to develop your application locally, and deploy your application to the Supabase platform. options: |- ```sh - --debug output debug logs to stderr - -h, --help help for supabase + --debug output debug logs to stderr + -h, --help help for supabase + --workdir string path to a Supabase project directory ``` commands: - id: supabase-stop @@ -27,7 +28,8 @@ commands: subcommands: [] options: |- ```sh - -h, --help help for stop + --backup Backs up the current database before stopping. + -h, --help help for stop ``` - id: supabase-status title: supabase status @@ -299,7 +301,7 @@ commands: title: supabase gen types typescript summary: Generate types for TypeScript description: | - Generate types for TypeScript. Must specify either --local or --db-url + Generate types for TypeScript. Must specify one of --local, --linked, --project-id, or --db-url tags: [] links: [] usage: |- @@ -309,9 +311,12 @@ commands: subcommands: [] options: |- ```sh - --db-url string Generate types from a database url. - -h, --help help for typescript - --local Generate types from the local dev database. + --db-url string Generate types from a database url. + -h, --help help for typescript + --linked Generate types from the linked project. + --local Generate types from the local dev database. + --project-id string Generate types from a project ID. + --schema stringArray Schemas to generate types for. ``` - id: supabase-functions title: supabase functions @@ -399,6 +404,7 @@ commands: subcommands: - supabase-db-branch - supabase-db-diff + - supabase-db-lint - supabase-db-push - supabase-db-remote - supabase-db-reset @@ -478,6 +484,22 @@ commands: -h, --help help for push -p, --password string Password to your remote Postgres database. ``` + - id: supabase-db-lint + title: supabase db lint + summary: Checks local database for typing error + tags: [] + links: [] + usage: |- + ```sh + supabase db lint [flags] + ``` + subcommands: [] + options: |- + ```sh + -h, --help help for lint + --level string Error level to emit. (default "warning") + -s, --schema strings List of schema to include. (default [public]) + ``` - id: supabase-db-diff title: supabase db diff summary: Diffs the local database for schema changes @@ -567,3 +589,145 @@ commands: ```sh -h, --help help for create ``` + - id: supabase-completion + title: supabase completion + summary: Generate the autocompletion script for the specified shell + description: | + Generate the autocompletion script for supabase for the specified shell. + See each sub-command's help for details on how to use the generated script. + tags: [] + links: [] + subcommands: + - supabase-completion-bash + - supabase-completion-fish + - supabase-completion-powershell + - supabase-completion-zsh + options: |- + ```sh + -h, --help help for completion + ``` + - id: supabase-completion-zsh + title: supabase completion zsh + summary: Generate the autocompletion script for zsh + description: | + Generate the autocompletion script for the zsh shell. + + If shell completion is not already enabled in your environment you will need + to enable it. You can execute the following once: + + echo "autoload -U compinit; compinit" >> ~/.zshrc + + To load completions in your current shell session: + + source <(supabase completion zsh); compdef _supabase supabase + + To load completions for every new session, execute once: + + #### Linux: + + supabase completion zsh > "${fpath[1]}/_supabase" + + #### macOS: + + supabase completion zsh > $(brew --prefix)/share/zsh/site-functions/_supabase + + You will need to start a new shell for this setup to take effect. + tags: [] + links: [] + usage: |- + ```sh + supabase completion zsh [flags] + ``` + subcommands: [] + options: |- + ```sh + -h, --help help for zsh + --no-descriptions disable completion descriptions + ``` + - id: supabase-completion-powershell + title: supabase completion powershell + summary: Generate the autocompletion script for powershell + description: | + Generate the autocompletion script for powershell. + + To load completions in your current shell session: + + supabase completion powershell | Out-String | Invoke-Expression + + To load completions for every new session, add the output of the above command + to your powershell profile. + tags: [] + links: [] + usage: |- + ```sh + supabase completion powershell [flags] + ``` + subcommands: [] + options: |- + ```sh + -h, --help help for powershell + --no-descriptions disable completion descriptions + ``` + - id: supabase-completion-fish + title: supabase completion fish + summary: Generate the autocompletion script for fish + description: | + Generate the autocompletion script for the fish shell. + + To load completions in your current shell session: + + supabase completion fish | source + + To load completions for every new session, execute once: + + supabase completion fish > ~/.config/fish/completions/supabase.fish + + You will need to start a new shell for this setup to take effect. + tags: [] + links: [] + usage: |- + ```sh + supabase completion fish [flags] + ``` + subcommands: [] + options: |- + ```sh + -h, --help help for fish + --no-descriptions disable completion descriptions + ``` + - id: supabase-completion-bash + title: supabase completion bash + summary: Generate the autocompletion script for bash + description: | + Generate the autocompletion script for the bash shell. + + This script depends on the 'bash-completion' package. + If it is not installed already, you can install it via your OS's package manager. + + To load completions in your current shell session: + + source <(supabase completion bash) + + To load completions for every new session, execute once: + + #### Linux: + + supabase completion bash > /etc/bash_completion.d/supabase + + #### macOS: + + supabase completion bash > $(brew --prefix)/etc/bash_completion.d/supabase + + You will need to start a new shell for this setup to take effect. + tags: [] + links: [] + usage: |- + ```sh + supabase completion bash + ``` + subcommands: [] + options: |- + ```sh + -h, --help help for bash + --no-descriptions disable completion descriptions + ``` From 3d42d576ba4bc51286b039ee5c10eaaf43cac451 Mon Sep 17 00:00:00 2001 From: Ollie Halliday <22655069+Hallidayo@users.noreply.github.com> Date: Mon, 17 Oct 2022 12:16:30 +0100 Subject: [PATCH 13/15] chevron icon added to indiciate dropdown for multiselect --- studio/components/ui/MultiSelect/index.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/studio/components/ui/MultiSelect/index.tsx b/studio/components/ui/MultiSelect/index.tsx index c94cd640d65..77009bcd3d8 100644 --- a/studio/components/ui/MultiSelect/index.tsx +++ b/studio/components/ui/MultiSelect/index.tsx @@ -1,6 +1,14 @@ import { useRef, useEffect, useState, FormEvent, KeyboardEvent, ReactNode } from 'react' import { orderBy, filter, without } from 'lodash' -import { Popover, IconCheck, IconAlertCircle, IconSearch, IconPlus } from 'ui' +import { + Popover, + IconCheck, + IconAlertCircle, + IconSearch, + IconPlus, + IconArrowDown, + IconChevronDown, +} from 'ui' import { BadgeDisabled, BadgeSelected } from './Badges' @@ -223,7 +231,7 @@ export default function MultiSelect({ >
@@ -250,6 +258,9 @@ export default function MultiSelect({ ) } })} +
+ +
From e7d0f139640af16787bf7b1402c99795cd7a3dca Mon Sep 17 00:00:00 2001 From: Ollie Halliday <22655069+Hallidayo@users.noreply.github.com> Date: Mon, 17 Oct 2022 12:17:40 +0100 Subject: [PATCH 14/15] removed unneeded import --- studio/components/ui/MultiSelect/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/studio/components/ui/MultiSelect/index.tsx b/studio/components/ui/MultiSelect/index.tsx index 77009bcd3d8..085543693c7 100644 --- a/studio/components/ui/MultiSelect/index.tsx +++ b/studio/components/ui/MultiSelect/index.tsx @@ -6,7 +6,6 @@ import { IconAlertCircle, IconSearch, IconPlus, - IconArrowDown, IconChevronDown, } from 'ui' @@ -259,7 +258,7 @@ export default function MultiSelect({ } })}
- +
From dd544fbd2950bf306ebb50380fe3441602d88155 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Mon, 17 Oct 2022 21:16:21 +0800 Subject: [PATCH 15/15] Update security.mdx --- apps/www/pages/security.mdx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/www/pages/security.mdx b/apps/www/pages/security.mdx index 43867d08290..372502178a4 100644 --- a/apps/www/pages/security.mdx +++ b/apps/www/pages/security.mdx @@ -52,7 +52,7 @@ export const Section = ({ children, icon, img }) => ( ### SOC 2 -Supabase is SOC2 Type 1 compliant. Enterprise customers can request a copy of our SOC2 through our [Security Portal](https://security.supabase.com). +Supabase is SOC2 Type 1 compliant. Enterprise customers can request a copy of our SOC2 [here](https://supabase.com/legal/soc2).
@@ -126,8 +126,7 @@ In addition to internal security reviews, we use various tools to scan our code

Security Portal

-Access Supabase’s security and compliance documents on our Security Portal, such as -penetration testing and audit reports. +Access more of Supabase’s security on our Security Portal, such as penetration testing and audit reports.