diff --git a/.github/workflows/avoid-typos.yml b/.github/workflows/avoid-typos.yml index cd8d982f4aa..1cbe64238ac 100644 --- a/.github/workflows/avoid-typos.yml +++ b/.github/workflows/avoid-typos.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code. - uses: actions/checkout@v1 + uses: actions/checkout@v3 - name: misspell uses: reviewdog/action-misspell@v1 with: diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 462e45092a9..371aa1b881e 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Run prettier run: |- npx prettier -c 'apps/**/*.{js,jsx,ts,tsx,css,md,json}' @@ -41,7 +41,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Run prettier run: |- npx prettier -c 'i18n/**/*.{js,jsx,ts,tsx,css,md,json}' diff --git a/.github/workflows/studio-build.yml b/.github/workflows/studio-build.yml index 35d1f97b8b1..e2fb6aa46e6 100644 --- a/.github/workflows/studio-build.yml +++ b/.github/workflows/studio-build.yml @@ -18,9 +18,9 @@ jobs: # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'npm' diff --git a/.github/workflows/studio-tests.yml b/.github/workflows/studio-tests.yml index 72a84549568..11fda1e766c 100644 --- a/.github/workflows/studio-tests.yml +++ b/.github/workflows/studio-tests.yml @@ -24,9 +24,9 @@ jobs: # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'npm' diff --git a/apps/docs/pages/guides/auth/social-login.mdx b/apps/docs/pages/guides/auth/social-login.mdx index 1c950411577..8864a1b6dc9 100644 --- a/apps/docs/pages/guides/auth/social-login.mdx +++ b/apps/docs/pages/guides/auth/social-login.mdx @@ -43,6 +43,12 @@ Supabase supports a suite of social providers. Follow these guides to configure ))} + +## Provider Tokens + +Once the OAuth flow completes, Supabase Auth will sign your user in. You will receive a copy of the provider token used in the OAuth flow in case you need to use it further. For example, you can use the Google provider token to access Google APIs on behalf of your user. + +Provider tokens are intentionally not stored in your project's database, however. This is because provider tokens give access to potentially sensitive user data in third-party systems. Different applications have different needs, and one application's OAuth scopes may be significantly more permissive than another. If you do want to use the provider token outside of the browser that completed the OAuth flow, you will have to send it manually to a secure server under your control. export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/functions/examples/upstash-redis.mdx b/apps/docs/pages/guides/functions/examples/upstash-redis.mdx index bbe58dd4dd1..77411c5b1d8 100644 --- a/apps/docs/pages/guides/functions/examples/upstash-redis.mdx +++ b/apps/docs/pages/guides/functions/examples/upstash-redis.mdx @@ -73,7 +73,7 @@ serve(async (_req) => { ```bash supabase start -supabase functions serve upstash-redis-counter --no-verify-jwt --env-file supabase/functions/upstash-redis-counter/.env +supabase functions serve --no-verify-jwt --env-file supabase/functions/upstash-redis-counter/.env ``` Navigate to http://localhost:54321/functions/v1/upstash-redis-counter. diff --git a/apps/docs/pages/guides/functions/local-development.mdx b/apps/docs/pages/guides/functions/local-development.mdx index e342abe9b14..254cad70044 100644 --- a/apps/docs/pages/guides/functions/local-development.mdx +++ b/apps/docs/pages/guides/functions/local-development.mdx @@ -10,7 +10,7 @@ You can run your Edge Function locally using [`supabase functions serve`](/docs/ ```bash supabase start # start the supabase stack -supabase functions serve hello-world # start the Function watcher +supabase functions serve # start the Functions watcher ``` The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server. @@ -20,7 +20,7 @@ The `functions serve` command has hot-reloading capabilities. It will watch for While serving your local Edge Function, you can invoke it using curl: ```bash -curl --request POST 'http://localhost:54321/functions/v1/hello-world' \ +curl --request POST 'http://localhost:54321/functions/v1/function-name' \ --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \ --header 'Content-Type: application/json' \ --data '{ "name":"Functions" }' @@ -37,7 +37,7 @@ const supabase = createClient( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' ) -const { data, error } = await supabase.functions.invoke('hello-world', { +const { data, error } = await supabase.functions.invoke('function-name', { body: { name: 'Functions' }, }) ``` diff --git a/apps/docs/pages/guides/functions/secrets.mdx b/apps/docs/pages/guides/functions/secrets.mdx index 3d60a239a8b..3487fc94d79 100644 --- a/apps/docs/pages/guides/functions/secrets.mdx +++ b/apps/docs/pages/guides/functions/secrets.mdx @@ -46,7 +46,7 @@ console.log(Deno.env.get('MY_NAME')) Now we can invoke our function locally, by serving it with our new `.env.local` file: ```bash -supabase functions serve hello-world --env-file ./supabase/.env.local +supabase functions serve --env-file ./supabase/.env.local ``` When the function starts you should see the name “Yoda” output to the terminal. diff --git a/apps/www/_blog/2023-02-16-case-study-happyteams.mdx b/apps/www/_blog/2023-02-16-case-study-happyteams.mdx new file mode 100644 index 00000000000..cd7366094c0 --- /dev/null +++ b/apps/www/_blog/2023-02-16-case-study-happyteams.mdx @@ -0,0 +1,58 @@ +--- +title: HappyTeams unlocks better performance and reduces cost with Supabase +description: How a bootstrapped startup migrated from Heroku to Supabase in 30 minutes and never looked back +author: rory_wilding +image: 2023-02-16-case-study-happyteams/case-study-happyteams.png +thumb: 2023-02-16-case-study-happyteams/case-study-happyteams.png +tags: + - case-study +date: '2023-02-16' +toc_depth: 3 +--- + +## The Challenge  + +> Finding a painless and hands-free database experience to support growth + +[HappyTeams](https://happyteams.io/) is a growing startup with an ambitious vision. The company is bootstrapped, so technical resources are scarce. Devs need to do more with less. This means staying laser-focused on delivering features, rather than database timeouts or performance issues. + +The company launched its app on Heroku Postgres. But one year in, they were experiencing scaling issues. They needed to handle thousands of users while also shipping new features and building out integrations. Issues with database slowness and concurrent connections were holding them back. + +Michael Dever, the founder and CTO of HappyTeams, realized it was time for a change. A large overhaul of the app in early 2022 presented the opportunity to do so: "When you're in that first sprint of building a product, you're kind of locked with that first database decision you make. When we did a rebuild, it was time to look at something like Supabase." + +![HappyTeams Home Page](/images/blog/2023-02-16-case-study-happyteams/happyteams-home-page.png) + +## Why Supabase? + +There were four main factors that led HappyTeams to switch from Heroku Postgres to Supabase: + +1. **Better performance**. The team was experiencing uneven performance with Heroku, which was sluggish at times. Supabase was consistently fast. + +2. **Enabling a large number of direct connections and connection pooling with PgBouncer**. HappyTeams was constantly hitting limits on the number of database connections. They could solve this problem with Supabase's [flexible plans](https://supabase.com/pricing) and [support for PgBouncer](https://supabase.com/blog/supabase-pgbouncer). + +3. **Quality of support, documentation, and onboarding**: While Heroku provided no support at the tier they were on, the Supabase team was readily available: "Responses were powerful and they answered all my questions, even on a Sunday. That just further sold the product for me." He was also impressed with the [documentation](https://supabase.com/docs) on the Supabase website and the onboarding process. + +4. **Lower costs**. Moving to Supabase reduced HappyTeams's costs: The base plan comes with everything they need to scale the product. + + + Great documentation, great product, great pricing. It sells itself, really. + + +## Supabase provides a fast and reliable production database + +After a 30 minute migration, HappyTeams can iterate quickly, add new features, and improve their product based on customer feedback + +Once they made the decision, migrating from Heroku to Supabase was simple. HappyTeams completed their database migration in less than an hour using pg_dump and pg_restore. + +Performance is no longer an issue. "Supabase just worked", says Michael. Pooling connections using PgBouncer was effortless - solving a recurring pain point for HappyTeams. + +"Supabase is so fast. Way, way, way faster than our Heroku Postgres database, which I think was located in the same region." + +Today, Supabase is an integral part of HappyTeams's tech stack. It has allowed the company to move fast and rapidly release new features - with minimal tech resources devoted to their backend infrastructure. + +Thanks to Supabase's ease of use and reliability, HappyTeams can iterate quickly based on feature requests and deliver more value to their user base. They are planning to expand their usage, exploring functionality such as [Edge Functions](https://supabase.com/edge-functions) and the [API client](https://supabase.com/docs/guides/api). + + + When we needed to add a comments feature to our app, we could get it done in just two days. + Supabase is really like a dream come true for a developer. + diff --git a/apps/www/public/images/blog/2023-02-16-case-study-happyteams/case-study-happyteams.png b/apps/www/public/images/blog/2023-02-16-case-study-happyteams/case-study-happyteams.png new file mode 100644 index 00000000000..4158a47c534 Binary files /dev/null and b/apps/www/public/images/blog/2023-02-16-case-study-happyteams/case-study-happyteams.png differ diff --git a/apps/www/public/images/blog/2023-02-16-case-study-happyteams/happyteams-home-page.png b/apps/www/public/images/blog/2023-02-16-case-study-happyteams/happyteams-home-page.png new file mode 100644 index 00000000000..9f06f368ef6 Binary files /dev/null and b/apps/www/public/images/blog/2023-02-16-case-study-happyteams/happyteams-home-page.png differ diff --git a/apps/www/public/images/blog/avatars/michael-dever-happyteams.jpeg b/apps/www/public/images/blog/avatars/michael-dever-happyteams.jpeg new file mode 100644 index 00000000000..e534ae31b57 Binary files /dev/null and b/apps/www/public/images/blog/avatars/michael-dever-happyteams.jpeg differ diff --git a/apps/www/public/rss.xml b/apps/www/public/rss.xml index 166ea62a3bd..cd5b5dca072 100644 --- a/apps/www/public/rss.xml +++ b/apps/www/public/rss.xml @@ -5,9 +5,16 @@ https://supabase.com Latest news from Supabase en - Mon, 13 Feb 2023 16:04:00 +0000 + Wed, 15 Feb 2023 16:04:00 +0000 + https://supabase.com/blog/case-study-happyteams + HappyTeams unlocks better performance and reduces cost with Supabase + https://supabase.com/blog/case-study-happyteams + How a bootstrapped startup migrated from Heroku to Supabase in 30 minutes and never looked back + Wed, 15 Feb 2023 16:04:00 +0000 + + https://supabase.com/blog/flutter-real-time-multiplayer-game How to build a real-time multiplayer game with Flutter Flame https://supabase.com/blog/flutter-real-time-multiplayer-game diff --git a/docker/.gitignore b/docker/.gitignore index 33c83313c67..a1e9dc61e05 100644 --- a/docker/.gitignore +++ b/docker/.gitignore @@ -2,3 +2,4 @@ volumes/db/data volumes/storage .env test.http +docker-compose.override.yml diff --git a/examples/edge-functions/README.md b/examples/edge-functions/README.md index f32e56e2357..47e624783a9 100644 --- a/examples/edge-functions/README.md +++ b/examples/edge-functions/README.md @@ -6,23 +6,17 @@ ## Example Functions -The function examples are located in [`./supabase/functions`](./supabase/functions): - -- [`browser-with-cors`](./supabase/functions/browser-with-cors/index.ts): Handle CORS headers for function invocations from browser environments. -- [`select-from-table-with-auth-rls`](./supabase/functions/select-from-table-with-auth-rls/index.ts): Retrieve data from an authenticated user via RLS. -- [`send-email-smtp`](./supabase/functions/send-email-smtp/index.ts): Send an email using SMTP credentials. -- [`stripe-webhooks`](./supabase/functions/stripe-webhooks/index.ts): Handle Stripe Webhooks. -- [`telegram-bot`](./supabase/functions/telegram-bot/index.ts): Webhook handler for Telegram bots using [grammY](https://grammy.dev/). +We're constantly adding new Function Examples, [check our docs](https://supabase.com/docs/guides/functions#examples) for a complete list! ## Develop locally - Run `supabase start` (make sure your Docker daemon is running.) -- Run `mv ./supabase/.env.local.example ./supabase/.env.local` to rename the local `.env` file. -- Set the required variables to run the desired edge functions in the `.env.local` file. -- Run `supabase functions serve your-function-name --env-file ./supabase/.env.local` +- Run `cp ./supabase/.env.local.example ./supabase/.env.local` to create your local `.env` file. +- Set the required variables for the corresponding edge functions in the `.env.local` file. +- Run `supabase functions serve --env-file ./supabase/.env.local --no-verify-jwt` - Run the CURL command in the example function, or use the [invoke method](https://supabase.com/docs/reference/javascript/invoke) on the Supabase client or use the test client [app](./app/). -## Test +## Test Client This example includes a create-react-app in the [`./app/`](./app/) directory which you can use as a sort of postman to make test requests both locally and to your deployed functions. @@ -45,10 +39,13 @@ Note: when testing locally, the select dropdown doesn't have any effect, and inv - Link your project - Within your project root run `supabase link --project-ref your-project-ref` - Set up your secrets + - Run `supabase secrets set --env-file ./supabase/.env.local` to set the environment variables. - + (This is assuming your local and production secrets are the same. The recommended way is to create a separate `.env` file for storing production secrets, and then use it to set the environment variables while deploying.) + - You can run `supabase secrets list` to check that it worked and also to see what other env vars are set by default. + - Deploy the function - Within your project root run `supabase functions deploy your-function-name` - In your [`./app/.env`](./app/.env) file remove the `SUPA_FUNCTION_LOCALHOST` variable and restart your Expo app. diff --git a/examples/edge-functions/edge-functions.code-workspace b/examples/edge-functions/edge-functions.code-workspace index 80054980ded..d3e694fd61c 100644 --- a/examples/edge-functions/edge-functions.code-workspace +++ b/examples/edge-functions/edge-functions.code-workspace @@ -18,6 +18,7 @@ "node_modules/": true, "app/": true, "supabase/functions/": true - } + }, + "deno.importMap": "./supabase/functions/import_map.json" } } diff --git a/examples/edge-functions/supabase/.env.local.example b/examples/edge-functions/supabase/.env.local.example index 4456221dc6c..0031244f9f3 100644 --- a/examples/edge-functions/supabase/.env.local.example +++ b/examples/edge-functions/supabase/.env.local.example @@ -1,3 +1,21 @@ +# cloudflare-turnstile +CLOUDFLARE_TURNSTILE_SECRET_KEY=your_secret_key + +# discord-bot +DISCORD_PUBLIC_KEY= + +# location +IPINFO_TOKEN="your https://ipinfo.io token" + +# openai +OPENAI_API_KEY="" + +# postgres-on-the-edge +DATABASE_URL= + +# puppeteer +PUPPETEER_BROWSERLESS_IO_TOKEN= + # send-email-smtp SMTP_HOSTNAME="your.hostname.com" SMTP_PORT="2587" @@ -5,13 +23,14 @@ SMTP_USERNAME="your_username" SMTP_PASSWORD="your_password" SMTP_FROM="no-reply@example.com" -# telegram-bot -TELEGRAM_BOT_TOKEN="get it from https://t.me/BotFather" -FUNCTION_SECRET="random secret" - # stripe-webhooks STRIPE_API_KEY="" STRIPE_WEBHOOK_SIGNING_SECRET="" -# openai -OPENAI_API_KEY="" +# telegram-bot +TELEGRAM_BOT_TOKEN="get it from https://t.me/BotFather" +FUNCTION_SECRET="random secret" + +# upstash-redis-counter +UPSTASH_REDIS_REST_URL= +UPSTASH_REDIS_REST_TOKEN= diff --git a/examples/edge-functions/supabase/functions/.vscode/settings.json b/examples/edge-functions/supabase/functions/.vscode/settings.json index e40716fdd7e..1d800e858b2 100644 --- a/examples/edge-functions/supabase/functions/.vscode/settings.json +++ b/examples/edge-functions/supabase/functions/.vscode/settings.json @@ -1,5 +1,5 @@ { "deno.enable": true, - "deno.lint": true, - "deno.unstable": true + "deno.unstable": true, + "deno.importMap": "./import_map.json" } diff --git a/examples/edge-functions/supabase/functions/browser-with-cors/index.ts b/examples/edge-functions/supabase/functions/browser-with-cors/index.ts index 1ab04de4e1f..d022ef6035f 100644 --- a/examples/edge-functions/supabase/functions/browser-with-cors/index.ts +++ b/examples/edge-functions/supabase/functions/browser-with-cors/index.ts @@ -2,7 +2,7 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' +import { serve } from 'std/server' import { corsHeaders } from '../_shared/cors.ts' console.log(`Function "browser-with-cors" up and running!`) diff --git a/examples/edge-functions/supabase/functions/cloudflare-turnstile/README.md b/examples/edge-functions/supabase/functions/cloudflare-turnstile/README.md index 4d8bc5008cd..3ec3bf986bc 100644 --- a/examples/edge-functions/supabase/functions/cloudflare-turnstile/README.md +++ b/examples/edge-functions/supabase/functions/cloudflare-turnstile/README.md @@ -2,6 +2,10 @@ Turnstile is Cloudflare's CAPTCHA alternative: https://developers.cloudflare.com/turnstile/get-started/ +## Watch the Video Tutorial + +[![video tutorial](https://img.youtube.com/vi/OwW0znboh60/0.jpg)](https://www.youtube.com/watch?v=OwW0znboh60) + ## Setup - Follow these steps to set up a new site: https://developers.cloudflare.com/turnstile/get-started/ diff --git a/examples/edge-functions/supabase/functions/cloudflare-turnstile/index.ts b/examples/edge-functions/supabase/functions/cloudflare-turnstile/index.ts index 5d831d18ab7..02e440f3460 100644 --- a/examples/edge-functions/supabase/functions/cloudflare-turnstile/index.ts +++ b/examples/edge-functions/supabase/functions/cloudflare-turnstile/index.ts @@ -2,7 +2,7 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' +import { serve } from 'std/server' import { corsHeaders } from '../_shared/cors.ts' console.log(`Function "cloudflare-turnstile" up and running!`) @@ -55,7 +55,7 @@ serve(async (req) => { }) // To invoke: -// curl -i --location --request POST 'http://localhost:54321/functions/v1/' \ +// curl -i --location --request POST 'http://localhost:54321/functions/v1/cloudflare-turnstile' \ // --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \ // --header 'Content-Type: application/json' \ // --data '{"token":"cf-turnstile-response"}' diff --git a/examples/edge-functions/supabase/functions/discord-bot/README.md b/examples/edge-functions/supabase/functions/discord-bot/README.md index 5e9ad79f29b..7d66deb798c 100644 --- a/examples/edge-functions/supabase/functions/discord-bot/README.md +++ b/examples/edge-functions/supabase/functions/discord-bot/README.md @@ -5,6 +5,10 @@ - https://deno.com/deploy/docs/tutorial-discord-slash - https://discord.com/developers/docs/interactions/application-commands#slash-commands +## Watch the Video Tutorial + +[![video tutorial](https://img.youtube.com/vi/J24Bvo_m7DM/0.jpg)](https://www.youtube.com/watch?v=J24Bvo_m7DM) + ## Create an application on Discord Developer Portal 1. Go to [https://discord.com/developers/applications](https://discord.com/developers/applications) (login using your discord account if required). @@ -59,6 +63,6 @@ Open Discord, type `/Promise` and press **Enter**. ## Run locally ```bash -supabase functions serve discord-bot --no-verify-jwt --env-file ./supabase/functions/discord-bot/.env +supabase functions serve discord-bot --no-verify-jwt --env-file ./supabase/.env.local ngrok http 54321 ``` diff --git a/examples/edge-functions/supabase/functions/discord-bot/index.ts b/examples/edge-functions/supabase/functions/discord-bot/index.ts index 140bb30b767..abbf5985f2d 100644 --- a/examples/edge-functions/supabase/functions/discord-bot/index.ts +++ b/examples/edge-functions/supabase/functions/discord-bot/index.ts @@ -5,10 +5,10 @@ // Sift is a small routing library that abstracts away details like starting a // listener on a port, and provides a simple function (serve) that has an API // to invoke a function for a specific path. -import { json, serve, validateRequest } from 'https://deno.land/x/sift@0.6.0/mod.ts' +import { json, serve, validateRequest } from 'sift' // TweetNaCl is a cryptography library that we use to verify requests // from Discord. -import nacl from 'https://cdn.skypack.dev/tweetnacl@v1.0.3?dts' +import nacl from 'nacl' enum DiscordCommandType { Ping = 1, diff --git a/examples/edge-functions/supabase/functions/file-upload-storage/index.ts b/examples/edge-functions/supabase/functions/file-upload-storage/index.ts index 5593c0b6581..208619a7b49 100644 --- a/examples/edge-functions/supabase/functions/file-upload-storage/index.ts +++ b/examples/edge-functions/supabase/functions/file-upload-storage/index.ts @@ -1,8 +1,8 @@ // This example shows how to use Edge Functions to read incoming multipart/form-data request, // and write files to Supabase Storage and other fields to a database table. -import { Application, Router } from 'https://deno.land/x/oak/mod.ts' -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' +import { Application } from 'oak' +import { createClient } from '@supabase/supabase-js' const MB = 1024 * 1024 @@ -25,9 +25,9 @@ app.use(async (ctx) => { const supabaseClient = createClient( // Supabase API URL - env var exported by default. - Deno.env.get('SUPABASE_URL') ?? '', + Deno.env.get('SUPABASE_URL')!, // Supabase API ANON KEY - env var exported by default. - Deno.env.get('SUPABASE_ANON_KEY') ?? '' + Deno.env.get('SUPABASE_ANON_KEY')! ) //upload image to Storage diff --git a/examples/edge-functions/supabase/functions/get-tshirt-competition/README.md b/examples/edge-functions/supabase/functions/get-tshirt-competition/README.md index 8d0bcb0dd72..b83a049526c 100644 --- a/examples/edge-functions/supabase/functions/get-tshirt-competition/README.md +++ b/examples/edge-functions/supabase/functions/get-tshirt-competition/README.md @@ -15,9 +15,11 @@ Good luck trying to GET a T-Shirt! ### Serve this function locally ```bash -supabase functions serve --no-verify-jwt get-tshirt-competition +supabase functions serve --no-verify-jwt ``` +Navigate to http://localhost:54321/functions/v1/get-tshirt-competition?email=testr@test.de&twitter=thorwebdev&size=2XL&answer=20 + ### Deploy this function ```bash diff --git a/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts b/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts index 01e03605441..76b09b974c9 100644 --- a/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts +++ b/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts @@ -2,8 +2,8 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' +import { serve } from 'std/server' +import { createClient } from '@supabase/supabase-js' import { corsHeaders } from '../_shared/cors.ts' console.log(`Function "get-tshirt-competition" up and running!`) diff --git a/examples/edge-functions/supabase/functions/github-action-deploy/index.ts b/examples/edge-functions/supabase/functions/github-action-deploy/index.ts index 2dc629d0cf8..423445b1608 100644 --- a/examples/edge-functions/supabase/functions/github-action-deploy/index.ts +++ b/examples/edge-functions/supabase/functions/github-action-deploy/index.ts @@ -2,23 +2,18 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from "https://deno.land/std@0.131.0/http/server.ts"; +import { serve } from 'std/server' -console.log("Hello from Functions!"); +console.log('Hello from Functions!') -serve(async (req) => { - const { name } = await req.json(); +serve((_req) => { const data = { message: `I was deployed via GitHub Actions!`, - }; + } return new Response(JSON.stringify(data), { - headers: { "Content-Type": "application/json" }, - }); -}); + headers: { 'Content-Type': 'application/json' }, + }) +}) -// To invoke: -// curl -i --location --request POST 'http://localhost:54321/functions/v1/' \ -// --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \ -// --header 'Content-Type: application/json' \ -// --data '{"name":"Functions"}' +// To invoke: http://localhost:54321/functions/v1/github-action-deploy diff --git a/examples/edge-functions/supabase/functions/import_map.json b/examples/edge-functions/supabase/functions/import_map.json new file mode 100644 index 00000000000..be4b4d84118 --- /dev/null +++ b/examples/edge-functions/supabase/functions/import_map.json @@ -0,0 +1,20 @@ +{ + "imports": { + "denomailer": "https://deno.land/x/denomailer@0.12.0/mod.ts", + "nacl": "https://cdn.skypack.dev/tweetnacl@v1.0.3?dts", + "oak": "https://deno.land/x/oak@v11.1.0/mod.ts", + "og_edge": "https://deno.land/x/og_edge@0.0.4/mod.ts", + "openai": "https://esm.sh/openai@3.1.0", + "grammy": "https://deno.land/x/grammy@v1.8.3/mod.ts", + "react": "https://esm.sh/react@18.2.0", + "std/server": "https://deno.land/std@0.177.0/http/server.ts", + "stripe": "https://esm.sh/stripe@11.1.0?target=deno", + "sift": "https://deno.land/x/sift@0.6.0/mod.ts", + "@supabase/supabase-js": "https://esm.sh/@supabase/supabase-js@2.7.1", + "postgres": "https://deno.land/x/postgres@v0.14.2/mod.ts", + "puppeteer": "https://deno.land/x/puppeteer@16.2.0/mod.ts", + "React": "https://esm.sh/react@18.2.0?deno-std=0.177.0", + "upstash_redis": "https://deno.land/x/upstash_redis@v1.19.3/mod.ts", + "xhr_polyfill": "https://deno.land/x/xhr@0.3.0/mod.ts" + } +} diff --git a/examples/edge-functions/supabase/functions/location/README.md b/examples/edge-functions/supabase/functions/location/README.md index b2c0bacceb7..de3010f4440 100644 --- a/examples/edge-functions/supabase/functions/location/README.md +++ b/examples/edge-functions/supabase/functions/location/README.md @@ -4,6 +4,14 @@ This example shows how you can get user location based on the IP provided in X-F You will need to signup for an account in https://ipinfo.io and provide it as `IPINFO_TOKEN` environment variable ([learn how to set environment variables to your functions](https://supabase.com/docs/guides/functions#secrets-and-environment-variables)). +## Develop locally + +```bash +supabase functions serve --env-file ./supabase/.env.local --no-verify-jwt +``` + +Navigate to http://localhost:54321/functions/v1/location + ## Deploy ```bash diff --git a/examples/edge-functions/supabase/functions/location/index.ts b/examples/edge-functions/supabase/functions/location/index.ts index c9df62475af..cda85ab7837 100644 --- a/examples/edge-functions/supabase/functions/location/index.ts +++ b/examples/edge-functions/supabase/functions/location/index.ts @@ -2,20 +2,29 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from "https://deno.land/std@0.131.0/http/server.ts" +import { serve } from 'std/server' function ips(req: Request) { - return req.headers.get("x-forwarded-for")?.split(/\s*,\s*/); + return req.headers.get('x-forwarded-for')?.split(/\s*,\s*/) } serve(async (req) => { - const clientIps = ips(req) || ['']; - const res = await fetch(`https://ipinfo.io/${clientIps[0]}?token=${Deno.env.get('IPINFO_TOKEN')}`, { - headers: { 'Content-Type': 'application/json'}}); - const { city, country } = await res.json(); - - return new Response( - JSON.stringify(`You're accessing from ${city}, ${country}`), - { headers: { "Content-Type": "application/json" } }, + const clientIps = ips(req) || [''] + const res = await fetch( + `https://ipinfo.io/${clientIps[0]}?token=${Deno.env.get('IPINFO_TOKEN')}`, + { + headers: { 'Content-Type': 'application/json' }, + } ) + if (res.ok) { + const { city, country } = await res.json() + + return new Response(JSON.stringify(`You're accessing from ${city}, ${country}`), { + headers: { 'Content-Type': 'application/json' }, + }) + } else { + return new Response(await res.text(), { + status: 400, + }) + } }) diff --git a/examples/edge-functions/supabase/functions/oak-server/README.md b/examples/edge-functions/supabase/functions/oak-server/README.md index b1db6166231..ed2795cfc71 100644 --- a/examples/edge-functions/supabase/functions/oak-server/README.md +++ b/examples/edge-functions/supabase/functions/oak-server/README.md @@ -5,7 +5,7 @@ This example shows how you can write functions using Oak server middleware (http ## Run locally ```bash -supabase functions serve oak-server +supabase functions serve --no-verify-jwt ``` Use cURL or Postman to make a POST request to http://localhost:54321/functions/v1/oak-server. @@ -20,5 +20,5 @@ Use cURL or Postman to make a POST request to http://localhost:54321/functions/v ## Deploy ```bash -supabase functions deploy oak-server +supabase functions deploy oak-server --no-verify-jwt ``` diff --git a/examples/edge-functions/supabase/functions/oak-server/index.ts b/examples/edge-functions/supabase/functions/oak-server/index.ts index 9be189be65a..965da32e4a0 100644 --- a/examples/edge-functions/supabase/functions/oak-server/index.ts +++ b/examples/edge-functions/supabase/functions/oak-server/index.ts @@ -1,4 +1,4 @@ -import { Application, Router } from 'https://deno.land/x/oak/mod.ts' +import { Application, Router } from 'oak' const router = new Router() router diff --git a/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/README.md b/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/README.md index 2adb83328c7..05158fd957e 100644 --- a/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/README.md +++ b/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/README.md @@ -9,7 +9,7 @@ Generate Open Graph images with Deno and Supabase Edge Functions and cache the g ## Run locally ```bash -supabase functions serve og-image-with-storage-cdn --no-verify-jwt +supabase functions serve --no-verify-jwt ``` Navigate to http://localhost:54321/functions/v1/og-image-with-storage-cdn?ticketNumber=3524&username=thorwebdev&name=Thor%20%E9%9B%B7%E7%A5%9E%20Schaeff diff --git a/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/handler.tsx b/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/handler.tsx index 80ef24e2ccd..202684370ea 100644 --- a/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/handler.tsx +++ b/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/handler.tsx @@ -1,6 +1,6 @@ -import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0' -import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts' -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' +import React from 'React' +import { ImageResponse } from 'og_edge' +import { createClient } from '@supabase/supabase-js' import { corsHeaders } from '../_shared/cors.ts' const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw6' @@ -213,7 +213,7 @@ export async function handler(req: Request) { }) if (error) throw error - return generatedImage + return await fetch(`${STORAGE_URL}/tickets/${username}.png?v=3`) } catch (error) { return new Response(JSON.stringify({ error: error.message }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, diff --git a/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/index.ts b/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/index.ts index 5b2cf3da0b9..0cf64b6e552 100644 --- a/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/index.ts +++ b/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/index.ts @@ -2,7 +2,7 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.140.0/http/server.ts' +import { serve } from 'std/server' import { handler } from './handler.tsx' diff --git a/examples/edge-functions/supabase/functions/openai/README.md b/examples/edge-functions/supabase/functions/openai/README.md index 4433aa6abb1..33894bacb4f 100644 --- a/examples/edge-functions/supabase/functions/openai/README.md +++ b/examples/edge-functions/supabase/functions/openai/README.md @@ -9,7 +9,7 @@ cp supabase/.env.local.example supabase/.env.local ## Run locally ```bash -supabase functions serve --no-verify-jwt --env-file ./supabase/.env.local openai +supabase functions serve --env-file ./supabase/.env.local --no-verify-jwt ``` Use cURL or Postman to make a POST request to http://localhost:54321/functions/v1/openai. diff --git a/examples/edge-functions/supabase/functions/openai/index.ts b/examples/edge-functions/supabase/functions/openai/index.ts index 5bab2eafbb2..a7418d73e19 100644 --- a/examples/edge-functions/supabase/functions/openai/index.ts +++ b/examples/edge-functions/supabase/functions/openai/index.ts @@ -1,6 +1,6 @@ -import 'https://deno.land/x/xhr@0.3.0/mod.ts' -import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' -import { CreateCompletionRequest } from 'https://esm.sh/openai@3.1.0' +import 'xhr_polyfill' +import { serve } from 'std/server' +import { CreateCompletionRequest } from 'openai' serve(async (req) => { const { query } = await req.json() diff --git a/examples/edge-functions/supabase/functions/opengraph/README.md b/examples/edge-functions/supabase/functions/opengraph/README.md index 9242524394e..866f222465a 100644 --- a/examples/edge-functions/supabase/functions/opengraph/README.md +++ b/examples/edge-functions/supabase/functions/opengraph/README.md @@ -8,7 +8,7 @@ Generate Open Graph images with Deno and Supabase Edge Functions, no framework n ## Run locally ```bash -supabase functions serve opengraph --no-verify-jwt +supabase functions serve --no-verify-jwt ``` Navigate to http://localhost:54321/functions/v1/opengraph diff --git a/examples/edge-functions/supabase/functions/opengraph/handler.tsx b/examples/edge-functions/supabase/functions/opengraph/handler.tsx index ef64dc4b8e9..d1bbcd5b2c2 100644 --- a/examples/edge-functions/supabase/functions/opengraph/handler.tsx +++ b/examples/edge-functions/supabase/functions/opengraph/handler.tsx @@ -1,5 +1,5 @@ -import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0' -import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts' +import React from 'React' +import { ImageResponse } from 'og_edge' export function handler(req: Request) { return new ImageResponse( diff --git a/examples/edge-functions/supabase/functions/opengraph/index.ts b/examples/edge-functions/supabase/functions/opengraph/index.ts index 5a7c90237cf..0a928de50a7 100644 --- a/examples/edge-functions/supabase/functions/opengraph/index.ts +++ b/examples/edge-functions/supabase/functions/opengraph/index.ts @@ -2,7 +2,7 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.140.0/http/server.ts' +import { serve } from 'std/server' import { handler } from './handler.tsx' diff --git a/examples/edge-functions/supabase/functions/postgres-on-the-edge/index.ts b/examples/edge-functions/supabase/functions/postgres-on-the-edge/index.ts index 6ce14f6990d..871d24679fe 100644 --- a/examples/edge-functions/supabase/functions/postgres-on-the-edge/index.ts +++ b/examples/edge-functions/supabase/functions/postgres-on-the-edge/index.ts @@ -1,5 +1,5 @@ -import * as postgres from 'https://deno.land/x/postgres@v0.14.2/mod.ts' -import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' +import * as postgres from 'postgres' +import { serve } from 'std/server' // Get the connection string from the environment variable "DATABASE_URL" const databaseUrl = Deno.env.get('DATABASE_URL')! diff --git a/examples/edge-functions/supabase/functions/puppeteer/README.md b/examples/edge-functions/supabase/functions/puppeteer/README.md index 353df27fd05..57e99a74781 100644 --- a/examples/edge-functions/supabase/functions/puppeteer/README.md +++ b/examples/edge-functions/supabase/functions/puppeteer/README.md @@ -4,6 +4,14 @@ This example shows how you can use Puppeteer and a headless-browser to generate Since Edge Functions cannot run a Headless Browser instance due to resource constraints, you will need to use a hosted browser service like https://browserless.io. +## Develop locally + +```bash +supabase functions serve --env-file ./supabase/.env.local --no-verify-jwt +``` + +Navigate to http://localhost:54321/functions/v1/puppeteer + ## Deploy ```bash diff --git a/examples/edge-functions/supabase/functions/puppeteer/index.ts b/examples/edge-functions/supabase/functions/puppeteer/index.ts index 7eac140f29f..72efb56a723 100644 --- a/examples/edge-functions/supabase/functions/puppeteer/index.ts +++ b/examples/edge-functions/supabase/functions/puppeteer/index.ts @@ -1,11 +1,14 @@ -import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' -import puppeteer from 'https://deno.land/x/puppeteer@16.2.0/mod.ts' +import { serve } from 'std/server' +import puppeteer from 'puppeteer' serve(async (req) => { try { + console.log(`wss://chrome.browserless.io?token=${Deno.env.get('PUPPETEER_BROWSERLESS_IO_KEY')}`) // Visit browserless.io to get your free API token const browser = await puppeteer.connect({ - browserWSEndpoint: 'wss://chrome.browserless.io?token=YOUR_API_TOKEN', + browserWSEndpoint: `wss://chrome.browserless.io?token=${Deno.env.get( + 'PUPPETEER_BROWSERLESS_IO_KEY' + )}`, }) const page = await browser.newPage() @@ -17,7 +20,7 @@ serve(async (req) => { return new Response(screenshot, { headers: { 'Content-Type': 'image/png' } }) } catch (e) { console.error(e) - return new Response(JSON.stringify(`Error occurred when generating the screenshot`), { + return new Response(JSON.stringify({ error: e.message }), { headers: { 'Content-Type': 'application/json' }, status: 500, }) diff --git a/examples/edge-functions/supabase/functions/read-storage/index.ts b/examples/edge-functions/supabase/functions/read-storage/index.ts index d7247b800dc..c32ccfd91bc 100644 --- a/examples/edge-functions/supabase/functions/read-storage/index.ts +++ b/examples/edge-functions/supabase/functions/read-storage/index.ts @@ -2,8 +2,8 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' +import { serve } from 'std/server' +import { createClient } from '@supabase/supabase-js' const corsHeaders = { 'Access-Control-Allow-Origin': '*', diff --git a/examples/edge-functions/supabase/functions/restful-tasks/index.ts b/examples/edge-functions/supabase/functions/restful-tasks/index.ts index 67c36ed0a98..eea5219a074 100644 --- a/examples/edge-functions/supabase/functions/restful-tasks/index.ts +++ b/examples/edge-functions/supabase/functions/restful-tasks/index.ts @@ -2,8 +2,8 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' -import { createClient, SupabaseClient } from 'https://esm.sh/@supabase/supabase-js@2' +import { serve } from 'std/server' +import { createClient, SupabaseClient } from '@supabase/supabase-js' const corsHeaders = { 'Access-Control-Allow-Origin': '*', diff --git a/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts b/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts index f9dedb16a7d..72b95575cb9 100644 --- a/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts +++ b/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts @@ -2,8 +2,8 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' +import { serve } from 'std/server' +import { createClient } from '@supabase/supabase-js' import { corsHeaders } from '../_shared/cors.ts' console.log(`Function "select-from-table-with-auth-rls" up and running!`) diff --git a/examples/edge-functions/supabase/functions/send-email-smtp/README.md b/examples/edge-functions/supabase/functions/send-email-smtp/README.md index 02e6d5a6b08..68b7dbad82b 100644 --- a/examples/edge-functions/supabase/functions/send-email-smtp/README.md +++ b/examples/edge-functions/supabase/functions/send-email-smtp/README.md @@ -13,4 +13,4 @@ Note: `SMTP_PORT` must be a port other than `25`, `465`, and `587` as Deno Deplo ## Test locally - `cp ./supabase/.env.local.example ./supabase/.env.local` -- `supabase functions serve send-email-smtp --env-file ./supabase/.env.local` \ No newline at end of file +- `supabase functions serve --env-file ./supabase/.env.local` diff --git a/examples/edge-functions/supabase/functions/send-email-smtp/index.ts b/examples/edge-functions/supabase/functions/send-email-smtp/index.ts index 4958b8fa153..d0abf117129 100644 --- a/examples/edge-functions/supabase/functions/send-email-smtp/index.ts +++ b/examples/edge-functions/supabase/functions/send-email-smtp/index.ts @@ -2,14 +2,14 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' -import { SmtpClient } from 'https://deno.land/x/denomailer@0.12.0/mod.ts' +import { serve } from 'std/server' +import { SmtpClient } from 'denomailer' const smtp = new SmtpClient() console.log(`Function "send-email-smtp" up and running!`) -serve(async (req) => { +serve(async (_req) => { await smtp.connect({ hostname: Deno.env.get('SMTP_HOSTNAME')!, port: Number(Deno.env.get('SMTP_PORT')!), @@ -24,7 +24,7 @@ serve(async (req) => { subject: `Hello from Supabase Edge Functions`, content: `Hello Functions \o/`, }) - } catch (error: any) { + } catch (error) { return new Response(error.message, { status: 500 }) } @@ -41,7 +41,7 @@ serve(async (req) => { }) // To invoke: -// curl -i --location --request POST 'http://localhost:54321/functions/v1/' \ +// curl -i --location --request POST 'http://localhost:54321/functions/v1/send-email-smtp' \ // --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \ // --header 'Content-Type: application/json' \ // --data '{"name":"Functions"}' diff --git a/examples/edge-functions/supabase/functions/streams/README.md b/examples/edge-functions/supabase/functions/streams/README.md index 52cd230a364..1be0ec27b24 100644 --- a/examples/edge-functions/supabase/functions/streams/README.md +++ b/examples/edge-functions/supabase/functions/streams/README.md @@ -3,7 +3,7 @@ ## Run locally ```bash -supabase functions serve --no-verify-jwt streams +supabase functions serve --no-verify-jwt ``` Use cURL or Postman to make a GET request to http://localhost:54321/functions/v1/streams. diff --git a/examples/edge-functions/supabase/functions/streams/index.ts b/examples/edge-functions/supabase/functions/streams/index.ts index 15d3c2ace8e..dafbf83c7d7 100644 --- a/examples/edge-functions/supabase/functions/streams/index.ts +++ b/examples/edge-functions/supabase/functions/streams/index.ts @@ -1,4 +1,4 @@ -import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' +import { serve } from 'std/server' const msg = new TextEncoder().encode('data: hello\r\n\r\n') diff --git a/examples/edge-functions/supabase/functions/stripe-webhooks/README.md b/examples/edge-functions/supabase/functions/stripe-webhooks/README.md index 2d5a7a24d5d..337352277fb 100644 --- a/examples/edge-functions/supabase/functions/stripe-webhooks/README.md +++ b/examples/edge-functions/supabase/functions/stripe-webhooks/README.md @@ -9,7 +9,7 @@ Also check out our full Stripe Payments examples for [React Native (Expo)](https ## Test locally - Terminal 1: - - `supabase functions serve --no-verify-jwt stripe-webhooks --env-file ./supabase/.env.local` + - `supabase functions serve --no-verify-jwt --env-file ./supabase/.env.local` - Terminal 2: - `stripe listen --forward-to localhost:54321/functions/v1/` - Terminal 3 (optional): diff --git a/examples/edge-functions/supabase/functions/stripe-webhooks/index.ts b/examples/edge-functions/supabase/functions/stripe-webhooks/index.ts index 6b159a2a5b4..a0bbb7dc958 100644 --- a/examples/edge-functions/supabase/functions/stripe-webhooks/index.ts +++ b/examples/edge-functions/supabase/functions/stripe-webhooks/index.ts @@ -2,20 +2,21 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.132.0/http/server.ts' +import { serve } from 'std/server' -// esm.sh is used to compile stripe-node to be compatible with ES modules. -import Stripe from 'https://esm.sh/stripe@10.13.0?target=deno&no-check&deno-std=0.132.0' +// Import via bare specifier thanks to the import_map.json file. +import Stripe from 'stripe' -const stripe = Stripe(Deno.env.get('STRIPE_API_KEY'), { +const stripe = new Stripe(Deno.env.get('STRIPE_API_KEY') as string, { // This is needed to use the Fetch API rather than relying on the Node http // package. + apiVersion: '2022-11-15', httpClient: Stripe.createFetchHttpClient(), }) // This is needed in order to use the Web Crypto API in Deno. const cryptoProvider = Stripe.createSubtleCryptoProvider() -console.log(`Function "stripe-webhooks" up and running!`) +console.log('Hello from Stripe Webhook!') serve(async (request) => { const signature = request.headers.get('Stripe-Signature') @@ -27,8 +28,8 @@ serve(async (request) => { try { receivedEvent = await stripe.webhooks.constructEventAsync( body, - signature, - Deno.env.get('STRIPE_WEBHOOK_SIGNING_SECRET'), + signature!, + Deno.env.get('STRIPE_WEBHOOK_SIGNING_SECRET')!, undefined, cryptoProvider ) @@ -36,25 +37,5 @@ serve(async (request) => { return new Response(err.message, { status: 400 }) } console.log(`🔔 Event received: ${receivedEvent.id}`) - - // Secondly, we use this event to query the Stripe API in order to avoid - // handling any forged event. If available, we use the idempotency key. - const requestOptions = - receivedEvent.request && receivedEvent.request.idempotency_key - ? { - idempotencyKey: receivedEvent.request.idempotency_key, - } - : {} - - let retrievedEvent - try { - retrievedEvent = await stripe.events.retrieve(receivedEvent.id, requestOptions) - } catch (err) { - return new Response(err.message, { status: 400 }) - } - - return new Response(JSON.stringify({ id: retrievedEvent.id, status: 'ok' }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }) + return new Response(JSON.stringify({ ok: true }), { status: 200 }) }) diff --git a/examples/edge-functions/supabase/functions/telegram-bot/index.ts b/examples/edge-functions/supabase/functions/telegram-bot/index.ts index eab5aed9fd6..ecaa9916327 100644 --- a/examples/edge-functions/supabase/functions/telegram-bot/index.ts +++ b/examples/edge-functions/supabase/functions/telegram-bot/index.ts @@ -2,34 +2,28 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from "https://deno.land/std@0.131.0/http/server.ts" +import { serve } from 'std/server' console.log(`Function "telegram-bot" up and running!`) -import { Bot, webhookCallback } from "https://deno.land/x/grammy@v1.8.3/mod.ts"; +import { Bot, webhookCallback } from 'grammy' -const bot = new Bot(Deno.env.get('TELEGRAM_BOT_TOKEN') || ''); +const bot = new Bot(Deno.env.get('TELEGRAM_BOT_TOKEN') || '') -bot.command("start", (ctx) => ctx.reply("Welcome! Up and running.")); +bot.command('start', (ctx) => ctx.reply('Welcome! Up and running.')) bot.command('ping', (ctx) => ctx.reply(`Pong! ${new Date()} ${Date.now()}`)) -const handleUpdate = webhookCallback(bot, "std/http"); +const handleUpdate = webhookCallback(bot, 'std/http') serve(async (req) => { try { - const url = new URL(req.url); - if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET')) + const url = new URL(req.url) + if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET')) return new Response('not allowed', { status: 405 }) - return await handleUpdate(req); + return await handleUpdate(req) } catch (err) { - console.error(err); + console.error(err) } }) - -// To invoke: -// curl -i --location --request POST 'http://localhost:54321/functions/v1/' \ -// --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \ -// --header 'Content-Type: application/json' \ -// --data '{"name":"Functions"}' diff --git a/examples/edge-functions/supabase/functions/upstash-redis-counter/.env.example b/examples/edge-functions/supabase/functions/upstash-redis-counter/.env.example deleted file mode 100644 index d0785c19978..00000000000 --- a/examples/edge-functions/supabase/functions/upstash-redis-counter/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -UPSTASH_REDIS_REST_URL= -UPSTASH_REDIS_REST_TOKEN= \ No newline at end of file diff --git a/examples/edge-functions/supabase/functions/upstash-redis-counter/README.md b/examples/edge-functions/supabase/functions/upstash-redis-counter/README.md index 3d33a9e7dd7..dd854b5a07b 100644 --- a/examples/edge-functions/supabase/functions/upstash-redis-counter/README.md +++ b/examples/edge-functions/supabase/functions/upstash-redis-counter/README.md @@ -9,7 +9,7 @@ Create a Redis database using the [Upstash Console](https://console.upstash.com/ Select the `Global` type to minimize the latency from all edge locations. Copy the `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` to your .env file. You'll find them under **Details > REST API > .env**. ```bash -cp supabase/functions/upstash-redis-counter/.env.example supabase/functions/upstash-redis-counter/.env +cp supabase/.env.local.example supabase/.env.local ``` ## Run locally @@ -18,7 +18,7 @@ Make sure you have the latest version of the [Supabase CLI installed](https://su ```bash supabase start -supabase functions serve upstash-redis-counter --no-verify-jwt --env-file supabase/functions/upstash-redis-counter/.env +supabase functions serve --no-verify-jwt --env-file supabase/.env.local ``` Navigate to http://localhost:54321/functions/v1/upstash-redis-counter. @@ -27,5 +27,5 @@ Navigate to http://localhost:54321/functions/v1/upstash-redis-counter. ```bash supabase functions deploy upstash-redis-counter --no-verify-jwt -supabase secrets set --env-file supabase/functions/upstash-redis-counter/.env +supabase secrets set --env-file supabase/.env.local ``` diff --git a/examples/edge-functions/supabase/functions/upstash-redis-counter/index.ts b/examples/edge-functions/supabase/functions/upstash-redis-counter/index.ts index 911424209c9..bf630d0287f 100644 --- a/examples/edge-functions/supabase/functions/upstash-redis-counter/index.ts +++ b/examples/edge-functions/supabase/functions/upstash-redis-counter/index.ts @@ -2,8 +2,8 @@ // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. -import { serve } from 'https://deno.land/std@0.177.0/http/server.ts' -import { Redis } from 'https://deno.land/x/upstash_redis@v1.19.3/mod.ts' +import { serve } from 'std/server' +import { Redis } from 'upstash_redis' console.log(`Function "upstash-redis-counter" up and running!`) diff --git a/package-lock.json b/package-lock.json index 9944995b450..ad7da916061 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,6 @@ "requires": true, "packages": { "": { - "name": "supabase", "version": "0.0.0", "license": "Apache-2.0", "workspaces": [ @@ -46167,7 +46166,7 @@ "@sinclair/typebox": "^0.25.1", "pg": "^8.7.1", "pg-format": "^1.0.4", - "pgsql-parser": "13.4.0", + "pgsql-parser": "^13.3.0", "postgres-array": "^3.0.1", "prettier": "^2.6.0", "prettier-plugin-sql": "^0.12.1" diff --git a/spec/cli_v1_commands--old.yaml b/spec/cli_v1_commands--old.yaml index d7af310d127..f3671ceb1ae 100644 --- a/spec/cli_v1_commands--old.yaml +++ b/spec/cli_v1_commands--old.yaml @@ -380,12 +380,12 @@ commands: ``` - id: supabase-functions-serve title: supabase functions serve - summary: Serve a Function locally + summary: Serve Functions locally tags: [] links: [] usage: |- ```sh - supabase functions serve [flags] + supabase functions serve [flags] ``` subcommands: [] options: |- diff --git a/spec/cli_v1_commands.yaml b/spec/cli_v1_commands.yaml index c060b75634e..daf13552001 100644 --- a/spec/cli_v1_commands.yaml +++ b/spec/cli_v1_commands.yaml @@ -761,10 +761,10 @@ commands: flags: [] - id: supabase-functions-serve title: supabase functions serve - summary: Serve a Function locally + summary: Serve all functions locally tags: [] links: [] - usage: supabase functions serve [flags] + usage: supabase functions serve [flags] subcommands: [] flags: - id: env-file diff --git a/spec/common-cli-sections.json b/spec/common-cli-sections.json index 8e5dff89916..127c10a5318 100644 --- a/spec/common-cli-sections.json +++ b/spec/common-cli-sections.json @@ -239,7 +239,7 @@ }, { "id": "supabase-functions-serve", - "title": "Serve a function", + "title": "Serve functions locally", "slug": "supabase-functions-serve", "type": "cli-command" }, diff --git a/spec/supabase_js_v2.yml b/spec/supabase_js_v2.yml index 26c48fec458..e59459ce89a 100644 --- a/spec/supabase_js_v2.yml +++ b/spec/supabase_js_v2.yml @@ -2258,6 +2258,9 @@ functions: ``` hideCodeBlock: true isSpotlight: true + description: | + When using [reserved words](https://www.postgresql.org/docs/current/sql-keywords-appendix.html) for column names you need + to add double quotes e.g. `.gt('"order"', 2)` - id: gte title: gte() $ref: '@supabase/postgrest-js.PostgrestFilterBuilder.gte' diff --git a/studio/pages/project/[ref]/reports/database.tsx b/studio/pages/project/[ref]/reports/database.tsx index 253fcc54f6d..20ba14361d2 100644 --- a/studio/pages/project/[ref]/reports/database.tsx +++ b/studio/pages/project/[ref]/reports/database.tsx @@ -1,10 +1,14 @@ import dayjs from 'dayjs' import { observer } from 'mobx-react-lite' import { FC, useEffect, useState } from 'react' -import { IconArrowRight } from 'ui' +import { Badge, Button, IconArrowRight, IconExternalLink } from 'ui' import { useParams, useStore } from 'hooks' -import { TIME_PERIODS_INFRA, USAGE_APPROACHING_THRESHOLD } from 'lib/constants' +import { + PRICING_TIER_PRODUCT_IDS, + TIME_PERIODS_INFRA, + USAGE_APPROACHING_THRESHOLD, +} from 'lib/constants' import { formatBytes } from 'lib/helpers' import { NextPageWithLayout } from 'types' @@ -71,6 +75,10 @@ const DatabaseUsage: FC = () => { const egressIsApproaching = databaseSizeUsageRatio >= USAGE_APPROACHING_THRESHOLD const egressIsExceeded = databaseSizeUsageRatio >= 1 + const subscriptionTier = ui.selectedProject?.subscription_tier + + const isPaidTier = subscriptionTier !== PRICING_TIER_PRODUCT_IDS.FREE + return ( <>
@@ -94,6 +102,25 @@ const DatabaseUsage: FC = () => { labelTop={databaseSizeLimit > 0 ? formatBytes(databaseSizeLimit) : ''} />
+ +
+
+ {usage?.disk_volume_size_gb && ( + Disk Size: {usage.disk_volume_size_gb} GB + )} + + {isPaidTier && Auto-Scaling} +
+ + +