From 50eefb1c2c8aca0353374fe1a4ce8c8b5bfa304c Mon Sep 17 00:00:00 2001 From: maharshi Date: Tue, 25 Apr 2023 02:58:47 +0530 Subject: [PATCH 001/112] Update rss.tsx fixes issue #13381 --- apps/www/lib/rss.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/www/lib/rss.tsx b/apps/www/lib/rss.tsx index c0409f8393d..2ae792e8648 100644 --- a/apps/www/lib/rss.tsx +++ b/apps/www/lib/rss.tsx @@ -13,7 +13,7 @@ const generateRssItem = (post: any): string => { ${post.title} https://supabase.com${post.path} ${post.description} - ${dayjs(post.date).utc().format('ddd, DD MMM YYYY HH:hh:mm ZZ')} + ${dayjs(post.date).utc().format('ddd, DD MMM YYYY HH:mm:ss [GMT]')} ` } @@ -33,7 +33,7 @@ export const generateRss = (posts: any[], authorID?: string): string => { en ${dayjs(posts[0].date) .utc() - .format('ddd, DD MMM YYYY HH:hh:mm ZZ')} + .format('ddd, DD MMM YYYY HH:mm:ss [GMT]')} ${posts.map(generateRssItem).join('')} @@ -49,7 +49,7 @@ export const generateRss = (posts: any[], authorID?: string): string => { en ${dayjs(posts[0].date) .utc() - .format('ddd, DD MMM YYYY HH:hh:mm ZZ')} + .format('ddd, DD MMM YYYY HH:mm:ss [GMT]')} ${posts.map(generateRssItem).join('')} From c122d0dd1ee9244dfda86c1fe805869afabb0b2f Mon Sep 17 00:00:00 2001 From: Thiago Resende Date: Sun, 14 May 2023 10:23:36 -0300 Subject: [PATCH 002/112] add public general availability status in README.pt-br.md --- i18n/README.pt-br.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/README.pt-br.md b/i18n/README.pt-br.md index bea72a069ba..30ec01c7eab 100644 --- a/i18n/README.pt-br.md +++ b/i18n/README.pt-br.md @@ -41,7 +41,7 @@ Para ver como Contribuir, visite [Primeiros Passos](../DEVELOPERS.md) - [x] Alfa: Nós estamos testando Supabase com um grupo fechado de clientes. - [x] Alfa Público: Qualquer um pode se registrar em [app.supabase.com](https://app.supabase.com). Porém seja flexível com a gente, ainda existem alguns obstáculos. - [x] Beta Público: Estável o suficiente para a maioria dos casos não empresariais. -- [ ] Público: Pronto para produção. +- [ ] Público: Pronto para produção. [[status](https://supabase.com/docs/guides/getting-started/features#feature-status)] Estamos atualmente na fase Beta Público. Siga as "entregas" (_releases_) deste repositório para receber uma notificação quando houver uma grande atualização (_major releases_). From d7b192508db549d193b8e7ec29a4fca72ceb3bd8 Mon Sep 17 00:00:00 2001 From: Isaiah Hamilton Date: Mon, 15 May 2023 10:55:06 -0400 Subject: [PATCH 003/112] remove extra space --- i18n/README.pt-br.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/README.pt-br.md b/i18n/README.pt-br.md index 30ec01c7eab..7c7b80e5fea 100644 --- a/i18n/README.pt-br.md +++ b/i18n/README.pt-br.md @@ -41,7 +41,7 @@ Para ver como Contribuir, visite [Primeiros Passos](../DEVELOPERS.md) - [x] Alfa: Nós estamos testando Supabase com um grupo fechado de clientes. - [x] Alfa Público: Qualquer um pode se registrar em [app.supabase.com](https://app.supabase.com). Porém seja flexível com a gente, ainda existem alguns obstáculos. - [x] Beta Público: Estável o suficiente para a maioria dos casos não empresariais. -- [ ] Público: Pronto para produção. [[status](https://supabase.com/docs/guides/getting-started/features#feature-status)] +- [ ] Público: Pronto para produção. [[status](https://supabase.com/docs/guides/getting-started/features#feature-status)] Estamos atualmente na fase Beta Público. Siga as "entregas" (_releases_) deste repositório para receber uma notificação quando houver uma grande atualização (_major releases_). From eb64cf1ab9379813cf46a76bc0e7be620fafcecc Mon Sep 17 00:00:00 2001 From: mvozbranyuk Date: Wed, 22 Feb 2023 18:26:05 +0300 Subject: [PATCH 004/112] partners slug moved to integrations and experts pages --- apps/www/pages/partners/experts/[slug].tsx | 244 ++++++++++++++++++ .../pages/partners/integrations/[slug].tsx | 244 ++++++++++++++++++ 2 files changed, 488 insertions(+) create mode 100644 apps/www/pages/partners/experts/[slug].tsx create mode 100644 apps/www/pages/partners/integrations/[slug].tsx diff --git a/apps/www/pages/partners/experts/[slug].tsx b/apps/www/pages/partners/experts/[slug].tsx new file mode 100644 index 00000000000..e06148d892e --- /dev/null +++ b/apps/www/pages/partners/experts/[slug].tsx @@ -0,0 +1,244 @@ +import {marked} from 'marked' +import {GetStaticPaths, GetStaticProps} from 'next' +import {NextSeo} from 'next-seo' +import Image from 'next/image' +import Link from 'next/link' +import {Swiper, SwiperSlide} from 'swiper/react' +import 'swiper/swiper.min.css' +import {IconChevronLeft, IconExternalLink} from 'ui' +import DefaultLayout from '~/components/Layouts/Default' +import SectionContainer from '~/components/Layouts/SectionContainer' +import supabase from '~/lib/supabase' +import {Partner} from '~/types/partners' +import Error404 from '../../404' + +function Partner({ partner }: { partner: Partner }) { + if (!partner) return + return ( + <> + + + + +
+ {/* Back button */} + + + + Back + + + +
+ {partner.title} +

+ {partner.title} +

+
+ +
+ + {partner.images.map((image: any, i: number) => { + return ( + +
+ {partner.title} +
+
+ ) + })} +
+
+ +
+
+

+ Overview +

+ + {partner.video && ( +
+ -
+## Encryption Keys -As we mentioned, the Vault uses pgsodium's Transparent Column Encryption (TCE) to store secrets in an authenticated encrypted form. There are some details around that you may be curious about, what does authenticated mean, and where are encryption keys store? This section explains those details. - -### Authenticated Encryption with Associated Data - -The first important feature of TCE is that it uses an [Authenticated Encryption with Associated Data]() encryption algorithm (based on libsodium). - -### Encryption key location - -**Authenticated Encryption** means that in addition to the data being encrypted, it is also signed so that it cannot be forged. You can guarantee that the data was encrypted by someone you trust, which you wouldn't get with encryption alone. The decryption function verifies that the signature is valid _before decrypting the value_. - -**Associated Data** means that you can include any other columns from the same row as part of the signature computation. This doesn't encrypt those other columns - rather it ensures that your encrypted value is only associated with columns from that row. If an attacker were to copy an encrypted value from another row to the current one, the signature would be rejected (assuming you used a unique column in the associated data). - -Another important feature of pgsodium is that the encryption keys are never stored in the database alongside the encrypted data. Instead, only a **Key ID** is stored, which is a reference to the key that is only accessible outside of SQL. Even if an attacker can capture a dump of your entire database, they will see only encrypted data and key IDs, _never the raw key itself_. - -This is an important safety precaution - there is little value in storing the encryption key in the database itself as this would be like locking your front door but leaving the key in the lock! Storing the key outside the database fixes this issue. - -Where are the keys stored? Supabase creates and manages the root keys (from which all key IDs are derived) in our secured backend systems. We keep this root key safe and separate from your data. You remain in control of your keys - a separate API endpoint is available that you can use to access the key if you want to decrypt your data outside of Supabase. +These are keys used to encrypt data inside your database. You can create different Encryption Keys for different purposes, for example: one for encrypting user-data, and another for application-data. Each key is encrypted itself using a root encryption key that lives outside of the database. See **[Encryption key location](#encryption-key-location)** for more details. ## Using the Vault -Using the vault is as simple as `INSERT`ing data into the -`vault.secret` table. +You can manage secrets and encryption keys from the UI or using SQL. + +### Adding secrets + +There is also a handy function for creating secrets called `vault.create_secret()`: ```sql -postgres=> insert into vault.secrets (secret) values ('s3kre3t_k3y') returning *; --[ RECORD 1 ]------------------------------------------------------------- -id | d91596b8-1047-446c-b9c0-66d98af6d001 -name | -description | -secret | S02eXS9BBY+kE3r621IS8beAytEEtj+dDHjs9/0AoMy7HTbog+ylxcS22A== -key_id | 7f5ad44b-6bd5-4c99-9f68-4b6c7486f927 -nonce | \x3aa2e92f9808e496aa4163a59304b895 -created_at | 2022-12-14 02:29:21.3625+00 -updated_at | 2022-12-14 02:29:21.3625+00 -``` - -There is also a handy function for creating secrets called -`vault.create_secret()`: - -```sql -postgres=> select vault.create_secret('another_s3kre3t'); --[ RECORD 1 ]-+------------------------------------- -create_secret | c9b00867-ca8b-44fc-a81d-d20b8169be17 - +select vault.create_secret('my_s3kre3t'); ``` The function returns the UUID of the new secret. -## Name and Description - -Secrets can also have an optional _unique_ name, or an optional description. These are also arguments to `vault.create_secret()`: +
+Show Result ```sql -postgres=> select vault.create_secret('another_s3kre3t', 'unique_name', 'This is the description'); -[ RECORD 1 ]-+------------------------------------- -create_secret | 7095d222-efe5-4cd5-b5c6-5755b451e223 +create_secret | c9b00867-ca8b-44fc-a81d-d20b8169be17 +``` -postgres=> select * from vault.secrets where id = '7095d222-efe5-4cd5-b5c6-5755b451e223'; +
+ +Secrets can also have an optional _unique_ name and an optional description. These are also arguments to `vault.create_secret()`: + +```sql +select vault.create_secret('another_s3kre3t', 'unique_name', 'This is the description'); +``` + +
+Show Result + +```sql -[ RECORD 1 ]----------------------------------------------------------------- id | 7095d222-efe5-4cd5-b5c6-5755b451e223 name | unique_name @@ -115,12 +80,49 @@ created_at | 2022-12-14 02:34:23.85159+00 updated_at | 2022-12-14 02:34:23.85159+00 ``` -## Querying Data from the Vault +
+ +Alternatively, you can create a secret by `insert`ing data into the `vault.secret` table: + +{/* prettier-ignore */} +```sql +insert into vault.secrets (secret) +values ('s3kre3t_k3y') returning *; +``` + +
+Show Result + +```sql +-[ RECORD 1 ]------------------------------------------------------------- +id | d91596b8-1047-446c-b9c0-66d98af6d001 +name | +description | +secret | S02eXS9BBY+kE3r621IS8beAytEEtj+dDHjs9/0AoMy7HTbog+ylxcS22A== +key_id | 7f5ad44b-6bd5-4c99-9f68-4b6c7486f927 +nonce | \x3aa2e92f9808e496aa4163a59304b895 +created_at | 2022-12-14 02:29:21.3625+00 +updated_at | 2022-12-14 02:29:21.3625+00 +``` + +
+ +### Viewing secrets If you look in the `vault.secrets` table, you will see that your data is stored encrypted. To decrypt the data, there is an automatically created view `vault.decrypted_secrets`. This view will decrypt secret data on the fly: +{/* prettier-ignore */} +```sql +select * +from vault.decrypted_secrets +order by created_at desc +limit 3; +``` + +
+Show Result + ```sql -postgres=> select * from vault.decrypted_secrets order by created_at desc limit 3; -[ RECORD 1 ]----+----------------------------------------------------------------- id | 7095d222-efe5-4cd5-b5c6-5755b451e223 name | unique_name @@ -153,17 +155,30 @@ created_at | 2022-12-14 02:29:21.3625+00 updated_at | 2022-12-14 02:29:21.3625+00 ``` +
+ Notice how this view has a `decrypted_secret` column that contains the decrypted secrets. Views are not stored on disk, they are only run at query time, so the secret remains encrypted on disk, and in any backup dumps or replication streams. You should ensure that you protect access to this view with the appropriate SQL privilege settings at all times, as anyone that has access to the view has access to decrypted secrets. -## Updating Secrets +### Updating Secrets A secret can be updated with the `vault.update_secret()` function, this function makes updating secrets easy, just provide the secret UUID as the first argument, and then an updated secret, updated optional unique name, or updated description: ```sql -postgres=> select vault.update_secret('7095d222-efe5-4cd5-b5c6-5755b451e223', 'n3w_upd@ted_s3kret', - 'updated_unique_name', 'This is the updated description'); +select + vault.update_secret( + '7095d222-efe5-4cd5-b5c6-5755b451e223', + 'n3w_upd@ted_s3kret', + 'updated_unique_name', + 'This is the updated description' + ); +``` + +
+Show Result + +```sql -[ RECORD 1 ]-+- update_secret | @@ -180,6 +195,38 @@ created_at | 2022-12-14 02:34:23.85159+00 updated_at | 2022-12-14 02:51:13.938396+00 ``` +
+ +## Deep Dive on How The Vault works + +
+ +
+ +As we mentioned, the Vault uses pgsodium's Transparent Column Encryption (TCE) to store secrets in an authenticated encrypted form. There are some details around that you may be curious about, what does authenticated mean, and where are encryption keys store? This section explains those details. + +### Authenticated Encryption with Associated Data + +The first important feature of TCE is that it uses an [Authenticated Encryption with Associated Data]() encryption algorithm (based on libsodium). + +### Encryption key location + +**Authenticated Encryption** means that in addition to the data being encrypted, it is also signed so that it cannot be forged. You can guarantee that the data was encrypted by someone you trust, which you wouldn't get with encryption alone. The decryption function verifies that the signature is valid _before decrypting the value_. + +**Associated Data** means that you can include any other columns from the same row as part of the signature computation. This doesn't encrypt those other columns - rather it ensures that your encrypted value is only associated with columns from that row. If an attacker were to copy an encrypted value from another row to the current one, the signature would be rejected (assuming you used a unique column in the associated data). + +Another important feature of pgsodium is that the encryption keys are never stored in the database alongside the encrypted data. Instead, only a **Key ID** is stored, which is a reference to the key that is only accessible outside of SQL. Even if an attacker can capture a dump of your entire database, they will see only encrypted data and key IDs, _never the raw key itself_. + +This is an important safety precaution - there is little value in storing the encryption key in the database itself as this would be like locking your front door but leaving the key in the lock! Storing the key outside the database fixes this issue. + +Where are the keys stored? Supabase creates and manages the root keys (from which all key IDs are derived) in our secured backend systems. We keep this root key safe and separate from your data. You remain in control of your keys - a separate API endpoint is available that you can use to access the key if you want to decrypt your data outside of Supabase. + ## Internal Details To encrypt data, you need a _key id_. You can use the default key id created automatically for every project, or create your own key ids Using the `pgsodium.create_key()` function. Key ids are used to internally derive the encryption key used to encrypt secrets in the vault. Vault users typically do not have access to the key itself, only the key id. @@ -209,7 +256,7 @@ And then restart your project from the dashboard to enable that change. In the future we are researching various ways to refine the way statement logging interacts with sensitive columns. -## See also +## Resources - Read more about Supabase Vault in the [blog post](https://supabase.com/blog/vault-now-in-beta) - [Supabase Vault on GitHub](https://github.com/supabase/vault) From 5c789829157d695f41a908081c50713575543e9b Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Wed, 31 May 2023 23:57:49 +0200 Subject: [PATCH 100/112] Update apps/docs/pages/guides/database/column-encryption.mdx Co-authored-by: Greg Richardson --- apps/docs/pages/guides/database/column-encryption.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/pages/guides/database/column-encryption.mdx b/apps/docs/pages/guides/database/column-encryption.mdx index 5539bfc0a37..262aee6762b 100644 --- a/apps/docs/pages/guides/database/column-encryption.mdx +++ b/apps/docs/pages/guides/database/column-encryption.mdx @@ -11,7 +11,7 @@ export const meta = { Supabase provides a secure method for encrypting columns using [Vault](/docs/guides/database/vault), our Postgres secrets manager. Vault is Postgres extension with an integrated UI intended to act as a secure global secrets management for you project. -Vault enables an advance feature called Transparent Column Encryption (TCE) which provides a safe way to encrypt your data so that it doesn't leak into logs and backups. TCE provides a safe way to encrypt your data so that it doesn't leak into logs and backups. It can also provide row-level authenticated encryption. +Vault enables an advanced feature called Transparent Column Encryption (TCE) which provides a safe way to encrypt your data so that it doesn't leak into logs and backups. It can also provide row-level authenticated encryption. ## Encrypting columns From ae2aca65f27d62f5edadfbc2158b71711d50f6f3 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Thu, 1 Jun 2023 11:26:28 +0800 Subject: [PATCH 101/112] chore: add react tooltip to ui package dependencies --- package-lock.json | 42 ++++++++++++++++++++++++++++++++++++++++ packages/ui/package.json | 5 +++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2436954f16f..6a7a6d58773 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45761,6 +45761,7 @@ "react-feather": "^2.0.10", "react-markdown": "^8.0.3", "react-syntax-highlighter": "^15.5.0", + "react-tooltip": "^4.2.17", "remark-gfm": "^3.0.1", "sse.js": "^0.6.1", "tailwind-merge": "^1.10.0", @@ -46316,6 +46317,30 @@ "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==", "dev": true }, + "packages/ui/node_modules/react-tooltip": { + "version": "4.2.17", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.17.tgz", + "integrity": "sha512-LzwUbQYzeRyrjbuuCbYUB0dlJpFPGPwigWS052umr1QulcF5N4czabDiWJ+Y585Q7ijvMFuBbeOvnI/azoTusw==", + "dependencies": { + "prop-types": "^15.7.2", + "uuid": "^7.0.3" + }, + "engines": { + "npm": ">=6.13" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "packages/ui/node_modules/react-tooltip/node_modules/uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "packages/ui/node_modules/sql-formatter": { "version": "12.2.0", "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-12.2.0.tgz", @@ -80424,6 +80449,7 @@ "react-feather": "^2.0.10", "react-markdown": "^8.0.3", "react-syntax-highlighter": "^15.5.0", + "react-tooltip": "^4.2.17", "remark-gfm": "^3.0.1", "sql-formatter": "^12.2.0", "sse.js": "^0.6.1", @@ -80804,6 +80830,22 @@ "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==", "dev": true }, + "react-tooltip": { + "version": "4.2.17", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.17.tgz", + "integrity": "sha512-LzwUbQYzeRyrjbuuCbYUB0dlJpFPGPwigWS052umr1QulcF5N4czabDiWJ+Y585Q7ijvMFuBbeOvnI/azoTusw==", + "requires": { + "prop-types": "^15.7.2", + "uuid": "^7.0.3" + }, + "dependencies": { + "uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" + } + } + }, "sql-formatter": { "version": "12.2.0", "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-12.2.0.tgz", diff --git a/packages/ui/package.json b/packages/ui/package.json index ce7073b768d..3ffe1a62329 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -46,6 +46,7 @@ "react-feather": "^2.0.10", "react-markdown": "^8.0.3", "react-syntax-highlighter": "^15.5.0", + "react-tooltip": "^4.2.17", "remark-gfm": "^3.0.1", "sse.js": "^0.6.1", "tailwind-merge": "^1.10.0", @@ -53,7 +54,6 @@ "tailwindcss-radix": "^1.6.0" }, "devDependencies": { - "config": "*", "@babel/preset-env": "^7.21.4", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.4", @@ -62,13 +62,14 @@ "@storybook/addon-styling": "^1.0.1", "@storybook/react": "^7.0.5", "@storybook/react-webpack5": "^7.0.5", - "@tokens-studio/sd-transforms": "^0.8.5", "@testing-library/jest-dom": "^5.16.5", + "@tokens-studio/sd-transforms": "^0.8.5", "@types/common-tags": "^1.8.1", "@types/react": "^17.0.37", "@types/react-copy-to-clipboard": "^5.0.4", "@types/react-dom": "^17.0.11", "@types/react-syntax-highlighter": "^15.5.6", + "config": "*", "copyfiles": "^2.4.1", "glob": "^8.1.0", "sql-formatter": "^12.2.0", From 2ca87c5733812040ef1fdeb3475a013756fadc6d Mon Sep 17 00:00:00 2001 From: Long Hoang <1732217+loong@users.noreply.github.com> Date: Thu, 1 Jun 2023 11:43:27 +0800 Subject: [PATCH 102/112] Update apps/docs/pages/guides/platform/compute-add-ons.mdx Co-authored-by: Greg Richardson --- apps/docs/pages/guides/platform/compute-add-ons.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/pages/guides/platform/compute-add-ons.mdx b/apps/docs/pages/guides/platform/compute-add-ons.mdx index 4c478bfa21c..d517b4822f5 100644 --- a/apps/docs/pages/guides/platform/compute-add-ons.mdx +++ b/apps/docs/pages/guides/platform/compute-add-ons.mdx @@ -58,7 +58,7 @@ Smaller compute instances can burst up to their largest throughput and IOPS for If you need consistent disk performance, choose the 4XL or larger compute add-on which has the same baseline and maximum disk throughput and IOPS. -If you're unsure of how much throughput or IOPS your application requires, you can load test your project and inspect these [metrics in the Dashboard](https://app.supabase.com/project/_/reports). If the `Daily Disk IO Budget % Remaining` stat is less than 100%, it indicates that your workload has burst beyond the baseline IO throughput during the day. If this metric drops to zero, the workload has used up all the burst IO throughput minutes during the day and is running at the baseline performance. These projects are good candidates for upgrading to a larger compute add with higher baseline throughput. +If you're unsure of how much throughput or IOPS your application requires, you can load test your project and inspect these [metrics in the Dashboard](https://app.supabase.com/project/_/reports). If the `Daily Disk IO Budget % Remaining` stat is less than 100%, it indicates that your workload has burst beyond the baseline IO throughput during the day. If this metric drops to zero, the workload has used up all the burst IO throughput minutes during the day and is running at the baseline performance. These projects are good candidates for upgrading to a larger compute add-on with higher baseline throughput. export const Page = ({ children }) => From 3b34def423c08cacc35aa6e3dbd5967fee412754 Mon Sep 17 00:00:00 2001 From: Long Hoang <1732217+loong@users.noreply.github.com> Date: Thu, 1 Jun 2023 11:45:20 +0800 Subject: [PATCH 103/112] Update disk mbps --- apps/www/data/PricingAddOnTable.json | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/apps/www/data/PricingAddOnTable.json b/apps/www/data/PricingAddOnTable.json index 67f2de2ff39..6ff2c90314c 100644 --- a/apps/www/data/PricingAddOnTable.json +++ b/apps/www/data/PricingAddOnTable.json @@ -15,10 +15,10 @@ { "key": "cpu", "title": "CPU", "value": "2-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": false }, { "key": "memory", "title": "Memory", "value": "1 GB" }, - { "key": "maxDiskIO", "title": "Disk IO Bandwidth up to", "value": "2,606 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "2,085 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk IO Bandwidth", + "title": "Baseline Disk Mbps", "value": "87 Mbps" }, { @@ -40,10 +40,10 @@ { "key": "cpu", "title": "CPU", "value": "2-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": false }, { "key": "memory", "title": "Memory", "value": "2 GB" }, - { "key": "maxDiskIO", "title": "Maximum Disk IO Bandwidth", "value": "2,606 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "2,085 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk IO Bandwidth", + "title": "Baseline Disk Mbps", "value": "174 Mbps" }, { @@ -65,10 +65,10 @@ { "key": "cpu", "title": "CPU", "value": "2-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": false }, { "key": "memory", "title": "Memory", "value": "4 GB" }, - { "key": "maxDiskIO", "title": "Maximum Disk IO Bandwidth", "value": "2,606 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "2,085 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk IO Bandwidth", + "title": "Baseline Disk Mbps", "value": "347 Mbps" }, { @@ -90,10 +90,10 @@ { "key": "cpu", "title": "CPU", "value": "2-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": true }, { "key": "memory", "title": "Memory", "value": "8 GB" }, - { "key": "maxDiskIO", "title": "Maximum Disk IO Bandwidth", "value": "4,750 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "4,750 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk IO Bandwidth", + "title": "Baseline Disk Mbps", "value": "630 Mbps" }, { @@ -115,10 +115,10 @@ { "key": "cpu", "title": "CPU", "value": "4-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": true }, { "key": "memory", "title": "Memory", "value": "16 GB" }, - { "key": "maxDiskIO", "title": "Maximum Disk IO Bandwidth", "value": "4,750 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "4,750 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk IO Bandwidth", + "title": "Baseline Disk Mbps", "value": "1,188 Mbps" }, { @@ -140,10 +140,10 @@ { "key": "cpu", "title": "CPU", "value": "8-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": true }, { "key": "memory", "title": "Memory", "value": "32 GB" }, - { "key": "maxDiskIO", "title": "Maximum Disk IO Bandwidth", "value": "4,750 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "4,750 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk IO Bandwidth", + "title": "Baseline Disk Mbps", "value": "2,375 Mbps" }, { @@ -165,10 +165,10 @@ { "key": "cpu", "title": "CPU", "value": "16-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": true }, { "key": "memory", "title": "Memory", "value": "64 GB" }, - { "key": "maxDiskIO", "title": "Maximum Disk IO Bandwidth", "value": "4,750 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "4,750 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk IO Bandwidth", + "title": "Baseline Disk Mbps", "value": "4,750 Mbps" }, { @@ -190,10 +190,10 @@ { "key": "cpu", "title": "CPU", "value": "32-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": true }, { "key": "memory", "title": "Memory", "value": "128 GB" }, - { "key": "maxDiskIO", "title": "Maximum Disk IO Bandwidth", "value": "9,500 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "9,500 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk IO Bandwidth", + "title": "Baseline Disk Mbps", "value": "9,500 Mbps" }, { @@ -217,12 +217,12 @@ { "key": "memory", "title": "Memory", "value": "192 GB" }, { "key": "maxDiskIO", - "title": "Maximum Disk IO Bandwidth", + "title": "Max Disk Mbps", "value": "14,250 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk IO Bandwidth", + "title": "Baseline Disk Mbps", "value": "14,250 Mbps" }, { @@ -246,12 +246,12 @@ { "key": "memory", "title": "Memory", "value": "256 GB" }, { "key": "maxDiskIO", - "title": "Maximum Disk IO Bandwidth", + "title": "Max Disk Mbps", "value": "19,000 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk IO Bandwidth", + "title": "Baseline Disk Mbps", "value": "19,000 Mbps" }, { From 66455d5ebb8b161c568bd7a53f84c79667907cb9 Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Thu, 1 Jun 2023 12:49:48 +0800 Subject: [PATCH 104/112] feat: add docs for deploy all edge functions. --- .../pages/guides/functions/cicd-workflow.mdx | 13 +++++++++++-- apps/docs/pages/guides/functions/quickstart.mdx | 17 +++++++++++++++++ examples/edge-functions/README.md | 13 +++++++++++-- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/apps/docs/pages/guides/functions/cicd-workflow.mdx b/apps/docs/pages/guides/functions/cicd-workflow.mdx index a1836c20b53..4cd57580e9e 100644 --- a/apps/docs/pages/guides/functions/cicd-workflow.mdx +++ b/apps/docs/pages/guides/functions/cicd-workflow.mdx @@ -24,7 +24,7 @@ jobs: env: SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} - PROJECT_ID: zdtdtxajzydjqzuktnqx + PROJECT_ID: your-project-id steps: - uses: actions/checkout@v3 @@ -33,7 +33,16 @@ jobs: with: version: 1.0.0 - - run: supabase functions deploy your-function-name --project-ref $PROJECT_ID + - run: supabase functions deploy --project-ref $PROJECT_ID +``` + +Since Supabase CLI [v1.62.0](https://github.com/supabase/cli/releases/tag/v1.62.0) you can deploy all functions with a single command. + +Individual function configuration like [JWT verification](/docs/reference/cli/config#functions.function_name.verify_jwt) and [import map location](/docs/reference/cli/config#functions.function_name.import_map) can be set via the `config.toml` file. + +```toml +[functions.hello-world] +verify_jwt = false ```
diff --git a/apps/docs/pages/guides/functions/quickstart.mdx b/apps/docs/pages/guides/functions/quickstart.mdx index be7537510fd..a01448526ab 100644 --- a/apps/docs/pages/guides/functions/quickstart.mdx +++ b/apps/docs/pages/guides/functions/quickstart.mdx @@ -41,6 +41,8 @@ This creates a function stub in your `supabase` folder at `./functions/hello-wor ## Deploy to production +### Deploy a specific function + ```bash supabase functions deploy hello-world ``` @@ -56,6 +58,21 @@ If you want to use Edge Functions to handle webhooks (e.g. [Stripe payment webho +### Deploy all functions + +```bash +supabase functions deploy +``` + +Since Supabase CLI [v1.62.0](https://github.com/supabase/cli/releases/tag/v1.62.0) you can deploy all functions with a single command. This is useful for example when [deploying with GitHub Actions](/docs/guides/functions/cicd-workflow). + +Individual function configuration like [JWT verification](/docs/reference/cli/config#functions.function_name.verify_jwt) and [import map location](/docs/reference/cli/config#functions.function_name.import_map) can be set via the `config.toml` file. + +```toml +[functions.hello-world] +verify_jwt = false +``` + ## Invoking remote functions You can invoke Edge Functions using curl: diff --git a/examples/edge-functions/README.md b/examples/edge-functions/README.md index 47e624783a9..c80b938b6e9 100644 --- a/examples/edge-functions/README.md +++ b/examples/edge-functions/README.md @@ -81,7 +81,7 @@ jobs: env: SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} - PROJECT_ID: zdtdtxajzydjqzuktnqx + PROJECT_ID: your-project-id steps: - uses: actions/checkout@v3 @@ -90,7 +90,16 @@ jobs: with: version: 1.0.0 - - run: supabase functions deploy your-function-name --project-ref $PROJECT_ID + - run: supabase functions deploy --project-ref $PROJECT_ID +``` + +Since Supabase CLI [v1.62.0](https://github.com/supabase/cli/releases/tag/v1.62.0) you can deploy all functions with a single command. + +Individual function configuration like [JWT verification](/docs/reference/cli/config#functions.function_name.verify_jwt) and [import map location](/docs/reference/cli/config#functions.function_name.import_map) can be set via the `config.toml` file. + +```toml +[functions.hello-world] +verify_jwt = false ``` ## 👁⚡️👁 From 0877df6fbb5e5026e2ec102510c8dd6ce53439b3 Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Thu, 1 Jun 2023 12:58:01 +0800 Subject: [PATCH 105/112] chore: use latest cli version. --- apps/docs/pages/guides/functions/cicd-workflow.mdx | 2 +- .../guides/functions/examples/github-actions.mdx | 13 +++++++++++-- .../edge-functions/.github/workflows/deploy.yaml | 2 +- examples/edge-functions/README.md | 2 +- .../functions/github-action-deploy/README.md | 2 +- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/docs/pages/guides/functions/cicd-workflow.mdx b/apps/docs/pages/guides/functions/cicd-workflow.mdx index 4cd57580e9e..4827bde8473 100644 --- a/apps/docs/pages/guides/functions/cicd-workflow.mdx +++ b/apps/docs/pages/guides/functions/cicd-workflow.mdx @@ -31,7 +31,7 @@ jobs: - uses: supabase/setup-cli@v1 with: - version: 1.0.0 + version: latest - run: supabase functions deploy --project-ref $PROJECT_ID ``` diff --git a/apps/docs/pages/guides/functions/examples/github-actions.mdx b/apps/docs/pages/guides/functions/examples/github-actions.mdx index 94777f4366d..7baf600b40d 100644 --- a/apps/docs/pages/guides/functions/examples/github-actions.mdx +++ b/apps/docs/pages/guides/functions/examples/github-actions.mdx @@ -40,9 +40,18 @@ jobs: - uses: supabase/setup-cli@v1 with: - version: 1.0.0 + version: latest - - run: supabase functions deploy github-action-deploy --project-ref $PROJECT_ID + - run: supabase functions deploy --project-ref $PROJECT_ID +``` + +Since Supabase CLI [v1.62.0](https://github.com/supabase/cli/releases/tag/v1.62.0) you can deploy all functions with a single command. + +Individual function configuration like [JWT verification](/docs/reference/cli/config#functions.function_name.verify_jwt) and [import map location](/docs/reference/cli/config#functions.function_name.import_map) can be set via the `config.toml` file. + +```toml +[functions.hello-world] +verify_jwt = false ``` export const Page = ({ children }) => diff --git a/examples/edge-functions/.github/workflows/deploy.yaml b/examples/edge-functions/.github/workflows/deploy.yaml index 1bb40de48ef..054fe9b8e40 100644 --- a/examples/edge-functions/.github/workflows/deploy.yaml +++ b/examples/edge-functions/.github/workflows/deploy.yaml @@ -19,6 +19,6 @@ jobs: - uses: supabase/setup-cli@v1 with: - version: 1.0.0 + version: latest - run: supabase functions deploy github-action-deploy --project-ref $PROJECT_ID diff --git a/examples/edge-functions/README.md b/examples/edge-functions/README.md index c80b938b6e9..e0f895d4ca1 100644 --- a/examples/edge-functions/README.md +++ b/examples/edge-functions/README.md @@ -88,7 +88,7 @@ jobs: - uses: supabase/setup-cli@v1 with: - version: 1.0.0 + version: latest - run: supabase functions deploy --project-ref $PROJECT_ID ``` diff --git a/examples/edge-functions/supabase/functions/github-action-deploy/README.md b/examples/edge-functions/supabase/functions/github-action-deploy/README.md index 01a7fd0d7d1..a9d91966077 100644 --- a/examples/edge-functions/supabase/functions/github-action-deploy/README.md +++ b/examples/edge-functions/supabase/functions/github-action-deploy/README.md @@ -26,7 +26,7 @@ jobs: - uses: supabase/setup-cli@v1 with: - version: 1.0.0 + version: latest - run: supabase functions deploy github-action-deploy --project-ref $PROJECT_ID ``` From d58a752793973c672a32a772eafa34d078143516 Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Thu, 1 Jun 2023 14:31:47 +0800 Subject: [PATCH 106/112] fix: typo. --- .../Navigation/NavigationMenu/NavigationMenu.constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 747abf433a3..7720cbbe8b8 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -727,7 +727,7 @@ export const functions: NavMenuConstant = { url: undefined, items: [ { name: 'Developing Functions locally', url: '/guides/functions/local-development' }, - { name: 'Deploying with Git', url: '/guides/functions/cicd-workflow' }, + { name: 'Deploying with GitHub', url: '/guides/functions/cicd-workflow' }, { name: 'Managing Secrets and Environment Variables', url: '/guides/functions/secrets' }, { name: 'Integrating With Supabase Auth', url: '/guides/functions/auth' }, { From e39fbcb1b66ef98778a5ef35111beeba8bf348a3 Mon Sep 17 00:00:00 2001 From: Long Hoang <1732217+loong@users.noreply.github.com> Date: Thu, 1 Jun 2023 14:40:40 +0800 Subject: [PATCH 107/112] Fix max and baseline disk througput --- studio/components/interfaces/Billing/AddOns/AddOns.utils.ts | 2 +- .../interfaces/BillingV2/Subscription/AddOns/AddOns.tsx | 6 +++--- .../interfaces/BillingV2/Usage/Usage.constants.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts b/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts index 3abaaaacee3..11c6b267632 100644 --- a/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts +++ b/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts @@ -33,7 +33,7 @@ export const formatComputeSizes = (addons: SubscriptionAddon[]) => { metadata: { default_price_id: undefined, supabase_prod_id: 'addon_instance_micro', - features: '2-core ARM (shared) • 1GB memory • 2,606Mbps Disk IO', + features: '2-core ARM (shared) • 1GB memory • 2,085Mbps Disk Throughput', }, prices: [ { diff --git a/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx b/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx index 6065c97c467..b4a6de0995c 100644 --- a/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx +++ b/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx @@ -221,7 +221,7 @@ const AddOns = ({}: AddOnsProps) => {

- Disk IO Bandwidth max burst + Max Disk Throughput

{

- {computeInstanceSpecs?.max_disk_io_mbs?.toLocaleString() ?? '2,606'} Mbps + {computeInstanceSpecs?.max_disk_io_mbs?.toLocaleString() ?? '2,085'} Mbps

@@ -240,7 +240,7 @@ const AddOns = ({}: AddOnsProps) => {

- Baseline Disk IO Bandwidth + Baseline Disk Throughput

Date: Thu, 1 Jun 2023 15:26:00 +0800 Subject: [PATCH 108/112] Say Bandwidth instead of mbps as mbps already stated as unit --- apps/www/data/PricingAddOnTable.json | 42 ++++++++++++++-------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/www/data/PricingAddOnTable.json b/apps/www/data/PricingAddOnTable.json index 6ff2c90314c..23b617c339f 100644 --- a/apps/www/data/PricingAddOnTable.json +++ b/apps/www/data/PricingAddOnTable.json @@ -5,7 +5,7 @@ "For projects that need dedicated compute", "For scaling number of connections", "Additional CPU & RAM options", - "Additional Disk IO Bandwidth" + "Additional Disk Bandwidth" ], "rows": [ { @@ -15,10 +15,10 @@ { "key": "cpu", "title": "CPU", "value": "2-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": false }, { "key": "memory", "title": "Memory", "value": "1 GB" }, - { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "2,085 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Bandwidth", "value": "2,085 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk Mbps", + "title": "Baseline Disk Bandwidth", "value": "87 Mbps" }, { @@ -40,10 +40,10 @@ { "key": "cpu", "title": "CPU", "value": "2-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": false }, { "key": "memory", "title": "Memory", "value": "2 GB" }, - { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "2,085 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Bandwidth", "value": "2,085 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk Mbps", + "title": "Baseline Disk Bandwidth", "value": "174 Mbps" }, { @@ -65,10 +65,10 @@ { "key": "cpu", "title": "CPU", "value": "2-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": false }, { "key": "memory", "title": "Memory", "value": "4 GB" }, - { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "2,085 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Bandwidth", "value": "2,085 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk Mbps", + "title": "Baseline Disk Bandwidth", "value": "347 Mbps" }, { @@ -90,10 +90,10 @@ { "key": "cpu", "title": "CPU", "value": "2-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": true }, { "key": "memory", "title": "Memory", "value": "8 GB" }, - { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "4,750 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Bandwidth", "value": "4,750 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk Mbps", + "title": "Baseline Disk Bandwidth", "value": "630 Mbps" }, { @@ -115,10 +115,10 @@ { "key": "cpu", "title": "CPU", "value": "4-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": true }, { "key": "memory", "title": "Memory", "value": "16 GB" }, - { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "4,750 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Bandwidth", "value": "4,750 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk Mbps", + "title": "Baseline Disk Bandwidth", "value": "1,188 Mbps" }, { @@ -140,10 +140,10 @@ { "key": "cpu", "title": "CPU", "value": "8-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": true }, { "key": "memory", "title": "Memory", "value": "32 GB" }, - { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "4,750 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Bandwidth", "value": "4,750 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk Mbps", + "title": "Baseline Disk Bandwidth", "value": "2,375 Mbps" }, { @@ -165,10 +165,10 @@ { "key": "cpu", "title": "CPU", "value": "16-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": true }, { "key": "memory", "title": "Memory", "value": "64 GB" }, - { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "4,750 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Bandwidth", "value": "4,750 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk Mbps", + "title": "Baseline Disk Bandwidth", "value": "4,750 Mbps" }, { @@ -190,10 +190,10 @@ { "key": "cpu", "title": "CPU", "value": "32-core ARM" }, { "key": "dedicated", "title": "Dedicated", "value": true }, { "key": "memory", "title": "Memory", "value": "128 GB" }, - { "key": "maxDiskIO", "title": "Max Disk Mbps", "value": "9,500 Mbps" }, + { "key": "maxDiskIO", "title": "Max Disk Bandwidth", "value": "9,500 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk Mbps", + "title": "Baseline Disk Bandwidth", "value": "9,500 Mbps" }, { @@ -217,12 +217,12 @@ { "key": "memory", "title": "Memory", "value": "192 GB" }, { "key": "maxDiskIO", - "title": "Max Disk Mbps", + "title": "Max Disk Bandwidth", "value": "14,250 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk Mbps", + "title": "Baseline Disk Bandwidth", "value": "14,250 Mbps" }, { @@ -246,12 +246,12 @@ { "key": "memory", "title": "Memory", "value": "256 GB" }, { "key": "maxDiskIO", - "title": "Max Disk Mbps", + "title": "Max Disk Bandwidth", "value": "19,000 Mbps" }, { "key": "baseDiskIO", - "title": "Baseline Disk Mbps", + "title": "Baseline Disk Bandwidth", "value": "19,000 Mbps" }, { From 68426bcd086dfe448239eca5b8c405ee856f1925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Gr=C3=BCneberg?= Date: Wed, 31 May 2023 17:12:04 +0200 Subject: [PATCH 109/112] chore: improve usage v2 page --- .../pages/guides/platform/performance.mdx | 2 +- .../pages/guides/platform/troubleshooting.mdx | 2 +- .../BillingV2/Subscription/AddOns/AddOns.tsx | 4 +- .../Subscription/Tier/EnterpriseCard.tsx | 2 +- .../interfaces/BillingV2/Usage/Activity.tsx | 13 ++--- .../interfaces/BillingV2/Usage/Bandwidth.tsx | 13 ++--- .../BillingV2/Usage/Infrastructure.tsx | 38 +++++-------- .../BillingV2/Usage/SizeAndCounts.tsx | 16 +++--- .../BillingV2/Usage/Usage.constants.ts | 56 ++++++++++--------- .../interfaces/BillingV2/Usage/Usage.tsx | 3 +- .../BillingV2/Usage/UsageBarChart.tsx | 43 +------------- .../data/analytics/infra-monitoring-query.ts | 3 +- 12 files changed, 78 insertions(+), 117 deletions(-) diff --git a/apps/docs/pages/guides/platform/performance.mdx b/apps/docs/pages/guides/platform/performance.mdx index 609a303d26f..8dbe236cc52 100644 --- a/apps/docs/pages/guides/platform/performance.mdx +++ b/apps/docs/pages/guides/platform/performance.mdx @@ -124,7 +124,7 @@ Queries which are the most time consuming are not necessarily bad, you may have Generally for most applications a small percentage of data is accessed more regularly than the rest. To make sure that your regularly accessed data is available, Postgres tracks your data access patterns and keeps this in its [shared_buffers](https://www.postgresql.org/docs/15/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY) cache. -Applications with lower cache hit rates generally perform more poorly since they have to hit the disk to get results rather than serving them from memory. Very poor hit rates can also cause you to burst past your [Disk I/O limits](https://supabase.com/docs/guides/platform/compute-add-ons#disk-io-bandwidth) causing significant performance issues. +Applications with lower cache hit rates generally perform more poorly since they have to hit the disk to get results rather than serving them from memory. Very poor hit rates can also cause you to burst past your [Disk I/O limits](https://supabase.com/docs/guides/platform/compute-add-ons#disk-throughput-and-iops) causing significant performance issues. You can view your cache and index hit rate by executing the following query: diff --git a/apps/docs/pages/guides/platform/troubleshooting.mdx b/apps/docs/pages/guides/platform/troubleshooting.mdx index 5ac27b8b71d..5e4b244192b 100644 --- a/apps/docs/pages/guides/platform/troubleshooting.mdx +++ b/apps/docs/pages/guides/platform/troubleshooting.mdx @@ -31,7 +31,7 @@ Some common solutions for this issue are: - [Using fewer Postgres connections](../platform/performance#configuring-clients-to-use-fewer-connections) can reduce the amount of resources needed on the project. - [Restarting](https://app.supabase.com/project/_/settings/general) the database. This only temporarily solves the issue by terminating any ongoing workloads that might be tying up your compute resources. -If your [Daily Disk IO budget](../platform/compute-add-ons#disk-io-bandwidth) has been drained, you will need to either wait for it to be replenished the next day, or upgrade to a larger compute add-on to increase the budget available to your project. +If your [Daily Disk IO budget](../platform/compute-add-ons#disk-throughput-and-iops) has been drained, you will need to either wait for it to be replenished the next day, or upgrade to a larger compute add-on to increase the budget available to your project. ## Unable to connect to your Supabase Project diff --git a/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx b/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx index b66ef1fbe3c..86352c7d1e4 100644 --- a/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx +++ b/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx @@ -221,7 +221,7 @@ const AddOns = ({}: AddOnsProps) => {

{computeInstanceSpecs?.connections_pooler ?? 200}

- +

@@ -240,7 +240,7 @@ const AddOns = ({}: AddOnsProps) => {

- +

diff --git a/studio/components/interfaces/BillingV2/Subscription/Tier/EnterpriseCard.tsx b/studio/components/interfaces/BillingV2/Subscription/Tier/EnterpriseCard.tsx index f087cf8687d..f3ef23e63be 100644 --- a/studio/components/interfaces/BillingV2/Subscription/Tier/EnterpriseCard.tsx +++ b/studio/components/interfaces/BillingV2/Subscription/Tier/EnterpriseCard.tsx @@ -39,7 +39,7 @@ const EnterpriseCard = ({ plan, isCurrentPlan, isTeamTierEnabled }: EnterpriseCa diff --git a/studio/components/interfaces/BillingV2/Usage/Activity.tsx b/studio/components/interfaces/BillingV2/Usage/Activity.tsx index a5e7ef7af2a..987f30d3fff 100644 --- a/studio/components/interfaces/BillingV2/Usage/Activity.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Activity.tsx @@ -176,7 +176,7 @@ const Activity = ({ projectRef }: ActivityProps) => {

-

{attribute.name} quota usage

+

{attribute.name} usage

{!usageBasedBilling && usageRatio >= 1 ? (
{ )}
-

Used

+

+ {attribute.chartPrefix || 'Used '}in period +

{(usageMeta?.usage ?? 0).toLocaleString()}

{usageMeta.limit > 0 && ( @@ -254,7 +256,7 @@ const Activity = ({ projectRef }: ActivityProps) => {
-

{attribute.name} over time

+

{attribute.name} per day

{attribute.chartDescription.split('\n').map((paragraph, idx) => (

{paragraph} @@ -269,15 +271,12 @@ const Activity = ({ projectRef }: ActivityProps) => {

) : ( 0} - name={attribute.name} + name={`${attribute.chartPrefix || ''}${attribute.name}`} unit={attribute.unit} attribute={attribute.attribute} data={chartData} - yLimit={usageMeta?.limit ?? 0} yLeftMargin={chartMeta[attribute.key].margin} yFormatter={(value) => ChartYFormatterCompactNumber(value, attribute.unit)} - quotaWarningType={isFreeTier || isProTier ? 'danger' : 'warning'} /> )} diff --git a/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx b/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx index 1cfa638840e..4e1c17070e7 100644 --- a/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx @@ -101,7 +101,7 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => {
-

{attribute.name} quota usage

+

{attribute.name} usage

{!usageBasedBilling && usageRatio >= 1 ? (
{

-

Used

+

+ {attribute.chartPrefix || 'Used '}in period +

{formatBytes(usageMeta?.usage ?? 0)}

{usageMeta?.limit > 0 && ( @@ -168,7 +170,7 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => {
-

{attribute.name} over time

+

{attribute.name} per day

{attribute.chartDescription.split('\n').map((paragraph, idx) => (

{paragraph} @@ -183,15 +185,12 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => {

) : ( 0} - name={attribute.name} + name={`${attribute.chartPrefix || ''}${attribute.name}`} unit={attribute.unit} attribute={attribute.attribute} data={chartData} - yLimit={usageMeta?.limit ?? 0} yLeftMargin={chartMeta[attribute.key].margin} yFormatter={(value) => ChartYFormatterCompactNumber(value, attribute.unit)} - quotaWarningType={isFreeTier || isProTier ? 'danger' : 'warning'} /> )} diff --git a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx index 1dd2d1cc2ed..70477e64f4b 100644 --- a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx @@ -77,17 +77,16 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { const { data: ioBudgetData, isLoading: isLoadingIoBudgetData } = useInfraMonitoringQuery({ projectRef, - attribute: 'disk_io_budget', + attribute: 'disk_io_consumption', interval, startDate, endDate, dateFormat, }) - const currentDayIoBudget = Number( - ioBudgetData?.data.find((x) => x.periodStartFormatted === dayjs().format('DD MMM'))?.[ - 'disk_io_budget' - ] ?? 100 + const highestIoBudgetConsumption = Math.max( + ...(ioBudgetData?.data || []).map((x) => Number(x.disk_io_consumption) ?? 0), + 0 ) const chartMeta: { [key: string]: { data: DataPoint[]; isLoading: boolean } } = { @@ -99,7 +98,7 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { isLoading: isLoadingMemoryUsageData, data: memoryUsageData?.data ?? [], }, - disk_io_budget: { + disk_io_consumption: { isLoading: isLoadingIoBudgetData, data: ioBudgetData?.data ?? [], }, @@ -116,14 +115,14 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { return (
- {attribute.key === 'disk_io_budget' && ( + {attribute.key === 'disk_io_consumption' && ( <> - {currentDayIoBudget <= 0 ? ( + {highestIoBudgetConsumption >= 100 ? (

- Your workload has used up all the burst IO throughput minutes during the day - and is running at the baseline performance. If you need consistent disk - performance, consider upgrading to a larger compute add-on. + Your workload has used up all the burst IO throughput minutes and ran at the + baseline performance. If you need consistent disk performance, consider + upgrading to a larger compute add-on.

@@ -133,7 +132,7 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => {
- ) : currentDayIoBudget <= 20 ? ( + ) : highestIoBudgetConsumption >= 80 ? (

Your workload is about to use up all the burst IO throughput minutes during @@ -185,24 +184,17 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { )}

- {attribute.key === 'disk_io_budget' ? ( -

IO Budget remaining

+ {attribute.key === 'disk_io_consumption' ? ( +

IO consumed per day

) : (

Max{' '} {attribute.name} {' '} - usage + per {interval === '1d' ? 'day' : 'hour'}

)} - - - - -
{attribute.chartDescription.split('\n').map((paragraph, idx) => ( @@ -219,7 +211,7 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => {
) : chartData.length ? ( {
-

{attribute.name} quota usage

+

{attribute.name} usage

{!usageBasedBilling && usageRatio >= 1 ? (
{

-

Used

+

+ {attribute.chartPrefix || 'Used '}in period +

{attribute.unit === 'bytes' ? formatBytes(usageMeta?.usage ?? 0) @@ -217,7 +219,10 @@ const SizeAndCounts = ({ projectRef }: SizeAndCountsProps) => { )}

-

{attribute.name} over time

+

+ {attribute.chartPrefix || ''} + {attribute.name} per day +

{attribute.chartDescription.split('\n').map((paragraph, idx) => (

{paragraph} @@ -232,15 +237,12 @@ const SizeAndCounts = ({ projectRef }: SizeAndCountsProps) => {

) : ( 0} - name={attribute.name} + name={`${attribute.chartPrefix || ''}${attribute.name}`} unit={attribute.unit} attribute={attribute.attribute} data={chartData} - yLimit={usageMeta?.limit ?? 0} yLeftMargin={chartMeta[attribute.key].margin} yFormatter={(value) => ChartYFormatterCompactNumber(value, attribute.unit)} - quotaWarningType={isFreeTier || isProTier ? 'danger' : 'warning'} /> )} diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts b/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts index 4fad750de7a..03fdf26c6cd 100644 --- a/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts +++ b/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts @@ -11,6 +11,7 @@ export interface CategoryAttribute { key: string // Property from project usage attribute: string // For querying against stats-daily / infra-monitoring name: string + chartPrefix?: string unit: 'bytes' | 'absolute' | 'percentage' links?: { name: string @@ -29,7 +30,7 @@ export const USAGE_CATEGORIES: { { key: 'infra', name: 'Infrastructure', - description: 'Usage statistics related to your Postgres server', + description: 'Usage statistics related to your server instance', attributes: [ { anchor: 'cpu', @@ -63,19 +64,19 @@ export const USAGE_CATEGORIES: { ], }, { - anchor: 'disk_io_budget', - key: 'disk_io_budget', - attribute: 'disk_io_budget', + anchor: 'disk_io', + key: 'disk_io_consumption', + attribute: 'disk_io_consumption', name: 'Disk IO bandwidth', unit: 'percentage', links: [ { name: 'Documentation', - url: 'https://supabase.com/docs/guides/platform/compute-add-ons#disk-io-bandwidth', + url: 'https://supabase.com/docs/guides/platform/compute-add-ons#disk-throughput-and-iops', }, ], description: - 'SSD Disks are attached to your servers and the disk performance of your workload is determined by the Disk IO bandwidth of this connection.', + 'SSD Disks are attached to your servers. The disk performance of your workload is determined by the Disk IO bandwidth of this connection.', chartDescription: '', }, ], @@ -92,8 +93,8 @@ export const USAGE_CATEGORIES: { name: 'Database egress', unit: 'bytes', description: - 'Contains any outgoing traffic (egress) from your database\nBilling is based on the total sum of egress in GB throughout your billing period.', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + 'Contains any outgoing traffic (egress) from your database.\nBilling is based on the total sum of egress in GB throughout your billing period.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, { anchor: 'storageEgress', @@ -103,7 +104,7 @@ export const USAGE_CATEGORIES: { unit: 'bytes', description: 'All requests to download/view your storage items go through our CDN. We sum up all outgoing traffic (egress) for storage related requests through our CDN. We do not differentiate between cache and no cache hits.\nBilling is based on the total amount of egress in GB throughout your billing period.', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, ], }, @@ -117,6 +118,7 @@ export const USAGE_CATEGORIES: { key: 'db_size', attribute: 'total_db_size_bytes', name: 'Database size', + chartPrefix: 'Average ', unit: 'bytes', description: 'Billing is based on the average daily database size in GB throughout the billing period.', @@ -126,27 +128,29 @@ export const USAGE_CATEGORIES: { url: 'https://supabase.com/docs/guides/platform/database-size', }, ], - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, { anchor: 'storageSize', key: 'storage_size', attribute: 'total_storage_size_bytes', name: 'Storage size', + chartPrefix: 'Max ', unit: 'bytes', description: - 'Sum of all objects in your storage buckets\nBilling is based on the average size in GB throughout your billing period', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + 'Sum of all objects in your storage buckets.\nBilling is based on the average size in GB throughout your billing period', + chartDescription: 'The data shown here refreshes every 24 hours.', }, { anchor: 'funcCount', key: 'func_count', attribute: 'total_func_count', name: 'Edge function count', + chartPrefix: 'Max ', unit: 'absolute', description: - 'Number of serverless functions in your project\nBilling is based on the maximum amount of functions at any point in time throughout your billing period', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + 'Number of serverless functions in your project.\nBilling is based on the maximum amount of functions at any point in time throughout your billing period', + chartDescription: 'The data shown here refreshes every 24 hours.', }, ], }, @@ -159,23 +163,23 @@ export const USAGE_CATEGORIES: { anchor: 'mau', key: 'monthly_active_users', attribute: 'total_auth_billing_period_mau', - name: 'Monthly active users', + name: 'Monthly Active Users', unit: 'absolute', description: - 'Users who log in or refresh their token\nBilling is based on the sum of distinct users requesting your API throughout the billing period. Resets every billing cycle.', + 'Users who log in or refresh their token count towards MAU.\nBilling is based on the sum of distinct users requesting your API throughout the billing period. Resets every billing cycle.', chartDescription: - 'The data shown here is refreshed over a period of 24 hours and resets at the beginning of every billing period.', + 'The data shown here is refreshed over a period of 24 hours and resets at the beginning of every billing period.\nThe data points are relative to the beginning of your billing period.', }, { anchor: 'mauSso', key: 'monthly_active_sso_users', attribute: 'total_auth_billing_period_sso_mau', - name: 'Monthly active single sign-on users', + name: 'Monthly Active SSO Users', unit: 'absolute', description: - 'SSO users who log in or refresh their token\nBilling is based on the sum of distinct Single Sign-On users requesting your API throughout the billing period. Resets every billing cycle.', + 'SSO users who log in or refresh their token count towards SSO MAU.\nBilling is based on the sum of distinct Single Sign-On users requesting your API throughout the billing period. Resets every billing cycle.', chartDescription: - 'The data shown here is refreshed over a period of 24 hours and resets at the beginning of every billing period.', + 'The data shown here is refreshed over a period of 24 hours and resets at the beginning of every billing period.\nThe data points are relative to the beginning of your billing period.', }, { anchor: 'storageImageTransformations', @@ -185,7 +189,8 @@ export const USAGE_CATEGORIES: { unit: 'absolute', description: 'We distinctly count all images that were transformed in the billing period, ignoring any transformations. If you transform one image with different transformations, it only counts as one.\nBilling is based on the unique count of (origin) images that used transformations throughout the billing period. Resets every billing cycle.', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + chartDescription: + 'The data shown here refreshes every 24 hours.\nThe data points are relative to the beginning of your billing period.', }, { anchor: 'functionInvocations', @@ -195,7 +200,7 @@ export const USAGE_CATEGORIES: { unit: 'absolute', description: 'Every single serverless function invocation independent of response status is counted.\nBilling is based on the sum of all invocations throughout your billing period.', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, { anchor: 'realtimeMessageCount', @@ -205,17 +210,18 @@ export const USAGE_CATEGORIES: { unit: 'absolute', description: "Count of messages going through Realtime. If you do a database change and 5 clients listen to that change via Realtime, that's 5 messages. If you broadcast a message and 4 clients listen to that, that's 5 messages (1 message sent, 4 received).\nBilling is based on the total amount of messages throughout your billing period.", - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, { anchor: 'realtimePeakConnection', key: 'realtime_peak_connection', attribute: 'total_realtime_peak_connection', name: 'Realtime peak connections', + chartPrefix: 'Max ', unit: 'absolute', description: - 'Total number of successful connections (not connection attempts)\nBilling is based on the maximum amount of concurrent peak connections throughout your billing period.', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + 'Total number of successful connections (not connection attempts).\nBilling is based on the maximum amount of concurrent peak connections throughout your billing period.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, ], }, diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.tsx b/studio/components/interfaces/BillingV2/Usage/Usage.tsx index 12196d3279c..d96a78e9292 100644 --- a/studio/components/interfaces/BillingV2/Usage/Usage.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Usage.tsx @@ -57,7 +57,8 @@ const Usage = () => { const subscriptionTierId = subscription?.tier?.supabase_prod_id const usageBillingEnabled = - subscriptionTierId === PRICING_TIER_PRODUCT_IDS.FREE || PRICING_TIER_PRODUCT_IDS.PRO + subscriptionTierId !== PRICING_TIER_PRODUCT_IDS.FREE && + subscriptionTierId !== PRICING_TIER_PRODUCT_IDS.PRO const scrollTo = (id: 'infra' | 'bandwidth' | 'sizeCount' | 'activity') => { switch (id) { diff --git a/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx b/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx index 50551b14dfc..681a8f2b322 100644 --- a/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx +++ b/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx @@ -4,7 +4,6 @@ import { CartesianGrid, Cell, ComposedChart, - ReferenceLine, ResponsiveContainer, Tooltip, XAxis, @@ -21,11 +20,9 @@ export interface UsageBarChartProps { name: string // Used within the tooltip attribute: string unit: 'bytes' | 'absolute' | 'percentage' - hasQuota?: boolean yLimit?: number yLeftMargin?: number yFormatter?: (value: number | string) => string - quotaWarningType?: 'warning' | 'danger' } const UsageBarChart = ({ @@ -33,23 +30,14 @@ const UsageBarChart = ({ name, attribute, unit, - hasQuota = false, yLimit, yLeftMargin = 10, yFormatter, - quotaWarningType = 'warning', }: UsageBarChartProps) => { const yMin = 0 // We can consider passing this as a prop if there's a use case in the future - const yMax = (yLimit ?? 0) * Y_DOMAIN_CEILING_MULTIPLIER - const quotaWarningClass = quotaWarningType === 'warning' ? 'fill-amber-900' : 'fill-red-900' const yDomain = [yMin, yLimit ?? 0] - const ticks = - yLimit !== undefined && hasQuota - ? [yMin, (yMax - yMin) * 0.25, (yMax - yMin) * 0.5, (yMax - yMin) * 0.75, yMax].map((x) => - Math.ceil(x) - ) - : undefined + return (
@@ -62,7 +50,6 @@ const UsageBarChart = ({ axisLine={false} tickLine={{ stroke: 'none' }} domain={yDomain} - ticks={ticks} tickFormatter={yFormatter} /> {data.map((entry) => { - return ( - = yLimit && hasQuota - ? quotaWarningClass - : 'fill-scale-1200' - } - /> - ) + return })} - {hasQuota && yLimit && ( - ( - -
- INCLUDED USAGE -
-
- )} - /> - )}
diff --git a/studio/data/analytics/infra-monitoring-query.ts b/studio/data/analytics/infra-monitoring-query.ts index 9e6429756f6..fbc79aa22ff 100644 --- a/studio/data/analytics/infra-monitoring-query.ts +++ b/studio/data/analytics/infra-monitoring-query.ts @@ -8,7 +8,7 @@ import { analyticsKeys } from './keys' export type InfraMonitoringVariables = { projectRef?: string - attribute: 'cpu_usage' | 'disk_io_budget' | 'ram_usage' + attribute: 'cpu_usage' | 'disk_io_budget' | 'ram_usage' | 'disk_io_consumption' startDate?: string endDate?: string interval?: '1m' | '5m' | '10m' | '30m' | '1h' | '1d' @@ -78,6 +78,7 @@ export const useInfraMonitoringQuery = ( }), } as TData }, + staleTime: 1000 * 60, // default good for a minute ...options, } ) From 2a6930883843f3add5d5c2c09fc36bdb2835915e Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Thu, 1 Jun 2023 15:24:12 +0800 Subject: [PATCH 110/112] fix: disable logs explorer gracefully --- .../interfaces/Settings/Logs/Logs.constants.ts | 3 --- .../NavigationBar/NavigationBar.utils.tsx | 17 ++++++----------- .../[ref]/analytics/endpoints/[name].ts | 5 +++++ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/studio/components/interfaces/Settings/Logs/Logs.constants.ts b/studio/components/interfaces/Settings/Logs/Logs.constants.ts index f798a345554..0d65ccf9c74 100644 --- a/studio/components/interfaces/Settings/Logs/Logs.constants.ts +++ b/studio/components/interfaces/Settings/Logs/Logs.constants.ts @@ -1,5 +1,4 @@ import dayjs from 'dayjs' -import { IS_PLATFORM } from 'lib/constants' import { DatetimeHelper, FilterTableSet, LogTemplate } from '.' export const LOGS_EXPLORER_DOCS_URL = @@ -666,5 +665,3 @@ export const TIER_QUERY_LIMITS: { TEAM: { text: '28 days', value: 28, unit: 'day', promptUpgrade: true }, ENTERPRISE: { text: '90 days', value: 90, unit: 'day', promptUpgrade: false }, } - -export const SHOW_O11Y = IS_PLATFORM || process.env.NEXT_PUBLIC_ENABLE_LOGS == 'true' diff --git a/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx b/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx index 314f5d16569..20904d97129 100644 --- a/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx +++ b/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx @@ -13,7 +13,6 @@ import SVG from 'react-inlinesvg' import { ProjectBase } from 'types' import { Route } from 'components/ui/ui.types' import { BASE_PATH, IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' -import { SHOW_O11Y } from 'components/interfaces/Settings/Logs' export const generateToolRoutes = (ref?: string, project?: ProjectBase): Route[] => { const isProjectBuilding = project?.status === PROJECT_STATUS.COMING_UP @@ -97,16 +96,12 @@ export const generateOtherRoutes = (ref?: string, project?: ProjectBase): Route[ }, ] : []), - ...(SHOW_O11Y - ? [ - { - key: 'logs', - label: 'Logs', - icon: , - link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/logs/explorer`), - }, - ] - : []), + { + key: 'logs', + label: 'Logs', + icon: , + link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/logs/explorer`), + }, { key: 'api', label: 'API Docs', diff --git a/studio/pages/api/projects/[ref]/analytics/endpoints/[name].ts b/studio/pages/api/projects/[ref]/analytics/endpoints/[name].ts index 2b7ab5ec113..b9ab5791bea 100644 --- a/studio/pages/api/projects/[ref]/analytics/endpoints/[name].ts +++ b/studio/pages/api/projects/[ref]/analytics/endpoints/[name].ts @@ -10,6 +10,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { switch (method) { case 'GET': + if (process.env.NEXT_PUBLIC_ENABLE_LOGS !== 'true') { + return res + .status(400) + .json({ error: { message: '[analytics] is not enabled in supabase/config.toml' } }) + } const missingEnvVars = [ process.env.LOGFLARE_API_KEY ? null : 'LOGFLARE_API_KEY', process.env.LOGFLARE_URL ? null : 'LOGFLARE_URL', From 97b3325e9880aa272c5e61c1ab9187adf6aa3c5b Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Thu, 1 Jun 2023 11:07:44 +0200 Subject: [PATCH 111/112] Add customers visual animation --- .../components/Sections/TimedTabsSection.tsx | 2 +- .../products/vector/CustomersVisual.tsx | 1521 +++++++++-------- .../products/vector/SecureAndScalableImg.tsx | 2 +- 3 files changed, 791 insertions(+), 734 deletions(-) diff --git a/apps/www/components/Sections/TimedTabsSection.tsx b/apps/www/components/Sections/TimedTabsSection.tsx index 6864e52b32f..532a70fb177 100644 --- a/apps/www/components/Sections/TimedTabsSection.tsx +++ b/apps/www/components/Sections/TimedTabsSection.tsx @@ -25,7 +25,7 @@ const Tab = ({ isActive, label, paragraph, onClick, progress, intervalDuration } aria-selected={isActive} role="tab" > -
+
{isActive && ( { const { isDarkMode } = useTheme() + const ref = useRef() + const isInView = useInView(ref, { margin: '-25%', once: true }) + + const initial = INITIAL_BOTTOM + const animate = getAnimation({}) return ( - - +
- - - - - - - - - - - - + - - - - - - - - - - - - + + + + + + + {/* center green main line */} + + + + + + + + {/* top green */} + + + + + + + {/* logo bg rays */} + + {/* Vector logo */} + + {/* logo side archs */} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + + - - - - - + + - - - - - + + - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + {/* bg overlays */} + + + + + + + {/* coordinates */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) } diff --git a/apps/www/components/products/vector/SecureAndScalableImg.tsx b/apps/www/components/products/vector/SecureAndScalableImg.tsx index d4e2f66bdb7..3dc3e3718a8 100644 --- a/apps/www/components/products/vector/SecureAndScalableImg.tsx +++ b/apps/www/components/products/vector/SecureAndScalableImg.tsx @@ -184,7 +184,7 @@ const SecureAndScalableImg = ({ isHovered }: { isHovered: boolean }) => { {/* main shield behind glow */} From cfe850e179966a9e21aab49a489a3d7bae76534e Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Thu, 1 Jun 2023 12:24:14 +0200 Subject: [PATCH 112/112] Darken and improve higlights cards --- .../components/Sections/HighlightCards.tsx | 8 ++- .../products/vector/DeployGlobally.tsx | 29 +++------- .../products/vector/OpenAIImage.tsx | 50 +++++------------- .../products/vector/PGvectorImg.tsx | 16 ++---- .../products/vector/SecureAndScalableImg.tsx | 6 --- .../product/vector/openai-logo-dark.png | Bin 0 -> 31748 bytes .../product/vector/openai-logo-light.png | Bin 0 -> 32763 bytes 7 files changed, 31 insertions(+), 78 deletions(-) create mode 100644 apps/www/public/images/product/vector/openai-logo-dark.png create mode 100644 apps/www/public/images/product/vector/openai-logo-light.png diff --git a/apps/www/components/Sections/HighlightCards.tsx b/apps/www/components/Sections/HighlightCards.tsx index 8a33f528b94..9e2493492a2 100644 --- a/apps/www/components/Sections/HighlightCards.tsx +++ b/apps/www/components/Sections/HighlightCards.tsx @@ -44,8 +44,14 @@ const HighlightCard = ({ highlight, index }: { highlight: Highlight; index: numb onMouseLeave={() => setIsHovered(false)} animate={isInView ? animate : initial} > - +
+
{highlight.image && } {highlight.svg && highlight.svg}
diff --git a/apps/www/components/products/vector/DeployGlobally.tsx b/apps/www/components/products/vector/DeployGlobally.tsx index 49148a8e209..2077b88c6a7 100644 --- a/apps/www/components/products/vector/DeployGlobally.tsx +++ b/apps/www/components/products/vector/DeployGlobally.tsx @@ -11,14 +11,11 @@ function getRandomNumber(min: number, max: number) { const DeployGlobally = ({ isHovered }: { isHovered: boolean }) => { const { isDarkMode } = useTheme() const ref = React.useRef() - const states = ['off', 'medium', 'high'] + const states = ['1', '2', '3'] const colors = { green: isDarkMode ? '#1CF7C3' : '#00B99F', gray: isDarkMode ? '#151918' : '#D3D3D3', } - const indices = [ - 3, 11, 14, 23, 27, 31, 35, 42, 48, 55, 62, 67, 71, 81, 83, 91, 98, 100, 103, 107, 110, 115, - ] const transitionDuration = 250 React.useEffect(() => { @@ -27,21 +24,15 @@ const DeployGlobally = ({ isHovered }: { isHovered: boolean }) => { const circles = [...ref.current?.querySelectorAll('circle')] const interval = setInterval(() => { - circles?.map((circle, index: any) => { - const isMatch = indices.includes(index) - - if (!isMatch) { - return - } - + circles?.map((circle) => { const nextState = states[Math.floor(Math.random() * states.length)] const currentState = circle.dataset.state const pulse = Math.random() > 0.2 && - ((currentState === 'off' && nextState === 'high') || - (currentState === 'off' && nextState === 'medium') || - (currentState === 'medium' && nextState === 'high')) + ((currentState === '1' && nextState === '3') || + (currentState === '1' && nextState === '2') || + (currentState === '2' && nextState === '3')) if (pulse) { const delay = getRandomNumber(40, 90) @@ -59,8 +50,8 @@ const DeployGlobally = ({ isHovered }: { isHovered: boolean }) => { ) } - if (currentState === 'high' && nextState === 'medium' && pulse) { - circle.dataset.state = 'off' + if (currentState === '3' && nextState === '2' && pulse) { + circle.dataset.state = '1' } else { circle.dataset.state = nextState } @@ -75,12 +66,6 @@ const DeployGlobally = ({ isHovered }: { isHovered: boolean }) => { return ( <> -
{ return ( <> -
+
+ OpenAi logo +
{ rx="24" fill="url(#paint5_radial_4346_99959)" /> - - { - - - - - - - - { return ( <> -
{ fillRule="evenodd" clipRule="evenodd" d="M183.411 113.532C182.915 112.029 181.615 110.983 179.934 110.731C179.141 110.613 178.234 110.663 177.159 110.885C175.287 111.271 173.898 111.418 172.884 111.447C176.711 104.986 179.822 97.6186 181.613 90.6834C184.51 79.4696 182.962 74.361 181.153 72.0501C176.367 65.934 169.383 62.6482 160.958 62.5475C156.464 62.4926 152.518 63.38 150.46 64.018C148.543 63.6801 146.483 63.4913 144.32 63.4563C140.268 63.3917 136.687 64.2751 133.627 66.0908C131.933 65.5181 129.215 64.7106 126.075 64.1952C118.691 62.9829 112.739 63.9276 108.386 67.0028C103.115 70.7263 100.671 77.1956 101.123 86.2311C101.267 89.0999 102.871 97.8281 105.398 106.106C106.85 110.864 108.399 114.815 110.001 117.85C112.272 122.155 114.703 124.69 117.432 125.6C118.961 126.109 121.74 126.466 124.663 124.032C125.033 124.481 125.527 124.927 126.183 125.341C127.016 125.866 128.035 126.295 129.052 126.55C132.719 127.466 136.153 127.237 139.082 125.952C139.1 126.473 139.114 126.971 139.126 127.401C139.146 128.098 139.165 128.782 139.19 129.421C139.364 133.745 139.658 137.107 140.53 139.459C140.577 139.588 140.642 139.785 140.71 139.994C141.145 141.326 141.872 143.554 143.721 145.3C145.637 147.108 147.954 147.662 150.076 147.662C151.14 147.662 152.155 147.523 153.046 147.332C156.221 146.651 159.826 145.615 162.434 141.901C164.9 138.39 166.099 133.103 166.316 124.771C166.344 124.535 166.37 124.31 166.395 124.096C166.411 123.951 166.429 123.803 166.446 123.656L167.027 123.707L167.177 123.717C170.41 123.864 174.364 123.178 176.792 122.051C178.711 121.16 184.859 117.914 183.411 113.532ZM135.912 88.7858C135.046 88.6652 134.261 88.7769 133.864 89.0773C133.641 89.2465 133.572 89.4424 133.553 89.5773C133.504 89.9345 133.754 90.3294 133.908 90.5331C134.343 91.1098 134.978 91.5061 135.607 91.5935C135.699 91.6061 135.789 91.6122 135.879 91.6122C136.929 91.6122 137.882 90.7953 137.967 90.1923C138.072 89.4372 136.975 88.9338 135.912 88.7858ZM164.612 88.8077H164.612C164.529 88.2158 163.476 88.047 162.476 88.1859C161.478 88.3251 160.51 88.7756 160.591 89.3688C160.656 89.8302 161.488 90.6178 162.475 90.6176C162.558 90.6176 162.642 90.6121 162.727 90.6003C163.385 90.5091 163.868 90.0911 164.097 89.8501C164.447 89.4831 164.649 89.0737 164.612 88.8077ZM181.083 114.159C180.716 113.05 179.537 112.693 177.576 113.098C171.756 114.299 169.671 113.467 168.987 112.963C173.511 106.071 177.233 97.7399 179.241 89.9666C180.192 86.2845 180.717 82.8651 180.76 80.0781C180.807 77.0185 180.287 74.7707 179.212 73.3974C174.879 67.8613 168.521 64.8917 160.823 64.8101C155.532 64.7507 151.061 66.105 150.194 66.4857C148.369 66.0318 146.379 65.7532 144.213 65.7176C140.241 65.6534 136.807 66.6044 133.964 68.5428C132.729 68.0834 129.538 66.9878 125.635 66.3592C118.888 65.2727 113.526 66.0958 109.7 68.8066C105.134 72.0412 103.026 77.8232 103.435 85.9915C103.572 88.7399 105.138 97.194 107.609 105.291C110.862 115.947 114.398 121.979 118.118 123.22C118.553 123.366 119.056 123.467 119.609 123.467C120.966 123.467 122.63 122.855 124.361 120.774C127.237 117.315 129.922 114.416 130.911 113.363C132.374 114.148 133.98 114.586 135.624 114.63C135.627 114.673 135.631 114.716 135.635 114.759C135.305 115.15 135.035 115.493 134.804 115.785C133.666 117.231 133.429 117.531 129.764 118.286C128.721 118.501 125.952 119.072 125.912 121.013C125.867 123.133 129.184 124.024 129.562 124.118C130.88 124.448 132.148 124.611 133.358 124.611C136.301 124.611 138.891 123.643 140.96 121.772C140.897 129.332 141.212 136.782 142.12 139.051C142.863 140.909 144.679 145.451 150.416 145.45C151.258 145.45 152.185 145.352 153.204 145.134C159.191 143.85 161.791 141.204 162.797 135.37C163.335 132.252 164.259 124.807 164.693 120.813C165.61 121.099 166.79 121.23 168.066 121.23C170.727 121.23 173.798 120.664 175.724 119.77C177.887 118.766 181.791 116.3 181.083 114.159ZM166.823 87.1724C166.803 88.3514 166.641 89.4217 166.469 90.5391C166.284 91.7407 166.093 92.983 166.045 94.4912C165.997 95.9588 166.18 97.4848 166.358 98.9603C166.716 101.941 167.083 105.009 165.66 108.037C165.439 107.644 165.226 107.216 165.03 106.741C164.853 106.313 164.469 105.624 163.938 104.671C161.869 100.961 157.025 92.2735 159.505 88.7285C160.243 87.6731 162.118 86.5885 166.823 87.1724ZM161.119 67.1982C168.016 67.3506 173.471 69.9306 177.334 74.8662C180.297 78.6521 177.034 95.8783 167.591 110.739C167.497 110.621 167.402 110.501 167.304 110.378C167.265 110.329 167.225 110.279 167.185 110.229C169.625 106.199 169.148 102.211 168.723 98.6758C168.549 97.225 168.384 95.8546 168.426 94.5675C168.469 93.2029 168.649 92.033 168.824 90.9015C169.038 89.5071 169.256 88.0645 169.196 86.3636C169.241 86.1853 169.259 85.9745 169.236 85.7243C169.082 84.0932 167.22 79.2124 163.425 74.7942C161.349 72.378 158.321 69.6737 154.188 67.8498C155.966 67.4813 158.397 67.1377 161.119 67.1982ZM122.53 119.25C120.622 121.543 119.305 121.104 118.872 120.959C116.05 120.018 112.775 114.053 109.888 104.594C107.39 96.4093 105.93 88.1792 105.814 85.8713C105.45 78.5726 107.219 73.4858 111.073 70.7523C117.346 66.3038 127.659 68.9664 131.803 70.3169C131.744 70.3756 131.682 70.4306 131.623 70.4902C124.822 77.358 124.984 89.0916 125 89.8087C125 90.0855 125.023 90.4773 125.055 91.0163C125.172 92.9896 125.39 96.6627 124.808 100.822C124.267 104.688 125.459 108.471 128.076 111.202C128.347 111.485 128.63 111.75 128.923 112C127.757 113.248 125.225 116.007 122.53 119.25ZM129.794 109.558C127.684 107.356 126.726 104.294 127.165 101.156C127.779 96.7618 127.552 92.9349 127.43 90.8789C127.413 90.5913 127.398 90.3392 127.389 90.1402C128.383 89.2595 132.986 86.7926 136.269 87.5449C137.768 87.8882 138.68 88.9082 139.06 90.6631C141.024 99.748 139.32 103.535 137.951 106.577C137.668 107.204 137.402 107.797 137.174 108.41L136.998 108.884C136.551 110.082 136.135 111.196 135.877 112.254C133.635 112.247 131.453 111.289 129.794 109.558ZM130.14 121.806C129.485 121.643 128.896 121.359 128.551 121.123C128.839 120.987 129.353 120.802 130.244 120.619C134.556 119.731 135.221 119.105 136.675 117.259C137.009 116.835 137.387 116.355 137.91 115.771C137.91 115.771 137.91 115.77 137.911 115.77C138.69 114.898 139.047 115.045 139.693 115.314C140.217 115.531 140.728 116.187 140.935 116.91C141.032 117.251 141.143 117.9 140.783 118.404C137.745 122.656 133.319 122.602 130.14 121.806ZM152.7 142.809C147.426 143.94 145.558 141.248 144.328 138.172C143.533 136.185 143.143 127.229 143.42 117.338C143.424 117.206 143.405 117.079 143.369 116.959C143.337 116.729 143.289 116.495 143.221 116.26C142.809 114.821 141.806 113.617 140.602 113.118C140.123 112.92 139.245 112.556 138.19 112.826C138.415 111.899 138.805 110.852 139.228 109.719L139.406 109.242C139.606 108.704 139.856 108.147 140.122 107.558C141.555 104.374 143.517 100.013 141.387 90.162C140.589 86.4723 137.925 84.6706 133.886 85.0889C131.464 85.3395 129.249 86.3165 128.144 86.8767C127.907 86.9971 127.689 87.1133 127.486 87.2267C127.795 83.5089 128.96 76.5613 133.318 72.1655C136.062 69.398 139.717 68.0313 144.17 68.1049C152.944 68.2486 158.57 72.7512 161.745 76.5032C164.481 79.7364 165.963 82.9932 166.554 84.7497C162.107 84.2977 159.083 85.1755 157.55 87.3672C154.215 92.1346 159.375 101.388 161.854 105.835C162.309 106.65 162.702 107.355 162.825 107.654C163.633 109.611 164.678 110.918 165.442 111.872C165.675 112.164 165.903 112.447 166.075 112.695C164.728 113.083 162.309 113.98 162.53 118.464C162.352 120.714 161.087 131.248 160.445 134.97C159.597 139.887 157.788 141.719 152.7 142.809ZM174.718 117.608C173.341 118.248 171.036 118.727 168.847 118.83C166.429 118.943 165.198 118.559 164.909 118.323C164.772 115.529 165.813 115.237 166.913 114.928C167.086 114.879 167.255 114.832 167.418 114.775C167.519 114.857 167.63 114.939 167.752 115.019C169.695 116.302 173.162 116.44 178.056 115.43C178.073 115.426 178.091 115.423 178.109 115.419C177.449 116.036 176.32 116.865 174.718 117.608Z" - fill="url(#paint13_linear_4346_99807)" - fillOpacity="0.5" + fill="url(#pgLogo)" + fillOpacity="1" /> { - - + + { return ( <> -
v!;EevD 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 literal 0 HcmV?d00001 diff --git a/apps/www/public/images/product/vector/openai-logo-light.png b/apps/www/public/images/product/vector/openai-logo-light.png new file mode 100644 index 0000000000000000000000000000000000000000..8fa3ca5a2418fc607cc563c5305bee674c0e1f38 GIT binary patch literal 32763 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

c{=*c&wT=&o} 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 S{imD&0000vMr|o literal 0 HcmV?d00001