From 283374bfb3898fed7a344126bd90a9fa76062888 Mon Sep 17 00:00:00 2001 From: Jonas Braus Date: Wed, 29 Jan 2025 21:50:10 +0100 Subject: [PATCH] test --- supabase/.dockerignore | 19 + supabase/.misspell-fixer.ignore | 3 + supabase/.npmrc | 1 + supabase/.nvmrc | 1 + supabase/.prettierignore | 20 + supabase/.prettierrc | 16 + supabase/.vale.ini | 14 + supabase/CONTRIBUTING.md | 35 + supabase/DEVELOPERS.md | 214 + supabase/LICENSE | 201 + supabase/Makefile | 48 + supabase/README.md | 287 + supabase/SECURITY.md | 1 + supabase/docker/.env | 123 + supabase/docker/README.md | 3 + supabase/docker/dev/data.sql | 48 + supabase/docker/dev/docker-compose.dev.yml | 34 + supabase/docker/docker-compose.s3.yml | 94 + supabase/docker/docker-compose.yml | 533 + supabase/docker/reset.sh | 44 + supabase/docker/volumes/api/kong.yml | 241 + supabase/docker/volumes/db/_supabase.sql | 3 + supabase/docker/volumes/db/init/data.sql | 0 supabase/docker/volumes/db/jwt.sql | 5 + supabase/docker/volumes/db/logs.sql | 6 + supabase/docker/volumes/db/pooler.sql | 6 + supabase/docker/volumes/db/realtime.sql | 4 + supabase/docker/volumes/db/roles.sql | 8 + supabase/docker/volumes/db/webhooks.sql | 208 + .../docker/volumes/functions/hello/index.ts | 16 + .../docker/volumes/functions/main/index.ts | 94 + supabase/docker/volumes/logs/vector.yml | 232 + supabase/docker/volumes/pooler/pooler.exs | 30 + supabase/package.json | 63 + supabase/pnpm-lock.yaml | 26921 ++++++++++++++++ supabase/pnpm-workspace.yaml | 11 + supabase/supa-mdx-lint.config.toml | 14 + supabase/tsconfig.json | 6 + supabase/turbo.json | 130 + 39 files changed, 29737 insertions(+) create mode 100644 supabase/.dockerignore create mode 100644 supabase/.misspell-fixer.ignore create mode 100644 supabase/.npmrc create mode 100644 supabase/.nvmrc create mode 100644 supabase/.prettierignore create mode 100644 supabase/.prettierrc create mode 100644 supabase/.vale.ini create mode 100644 supabase/CONTRIBUTING.md create mode 100644 supabase/DEVELOPERS.md create mode 100644 supabase/LICENSE create mode 100644 supabase/Makefile create mode 100644 supabase/README.md create mode 120000 supabase/SECURITY.md create mode 100644 supabase/docker/.env create mode 100644 supabase/docker/README.md create mode 100644 supabase/docker/dev/data.sql create mode 100644 supabase/docker/dev/docker-compose.dev.yml create mode 100644 supabase/docker/docker-compose.s3.yml create mode 100644 supabase/docker/docker-compose.yml create mode 100755 supabase/docker/reset.sh create mode 100644 supabase/docker/volumes/api/kong.yml create mode 100644 supabase/docker/volumes/db/_supabase.sql create mode 100755 supabase/docker/volumes/db/init/data.sql create mode 100644 supabase/docker/volumes/db/jwt.sql create mode 100644 supabase/docker/volumes/db/logs.sql create mode 100644 supabase/docker/volumes/db/pooler.sql create mode 100644 supabase/docker/volumes/db/realtime.sql create mode 100644 supabase/docker/volumes/db/roles.sql create mode 100644 supabase/docker/volumes/db/webhooks.sql create mode 100644 supabase/docker/volumes/functions/hello/index.ts create mode 100644 supabase/docker/volumes/functions/main/index.ts create mode 100644 supabase/docker/volumes/logs/vector.yml create mode 100644 supabase/docker/volumes/pooler/pooler.exs create mode 100644 supabase/package.json create mode 100644 supabase/pnpm-lock.yaml create mode 100644 supabase/pnpm-workspace.yaml create mode 100644 supabase/supa-mdx-lint.config.toml create mode 100644 supabase/tsconfig.json create mode 100644 supabase/turbo.json diff --git a/supabase/.dockerignore b/supabase/.dockerignore new file mode 100644 index 0000000..15e4fbe --- /dev/null +++ b/supabase/.dockerignore @@ -0,0 +1,19 @@ +# Root folder of this turbo monorepo +# Doesn't send useless context to Docker daemon +.git +.github +about +docker +examples +i18n +**/node_modules +apps/docs/spec +supabase +tests + +**/.next +.vercel +**/.env.* + +.dockerignore +apps/studio/Dockerfile diff --git a/supabase/.misspell-fixer.ignore b/supabase/.misspell-fixer.ignore new file mode 100644 index 0000000..1b26bc4 --- /dev/null +++ b/supabase/.misspell-fixer.ignore @@ -0,0 +1,3 @@ +^./i18n +^./packages/api-types +^./apps/www/lib/redirects.js \ No newline at end of file diff --git a/supabase/.npmrc b/supabase/.npmrc new file mode 100644 index 0000000..4fd0219 --- /dev/null +++ b/supabase/.npmrc @@ -0,0 +1 @@ +engine-strict=true \ No newline at end of file diff --git a/supabase/.nvmrc b/supabase/.nvmrc new file mode 100644 index 0000000..2edeafb --- /dev/null +++ b/supabase/.nvmrc @@ -0,0 +1 @@ +20 \ No newline at end of file diff --git a/supabase/.prettierignore b/supabase/.prettierignore new file mode 100644 index 0000000..4c3f5a3 --- /dev/null +++ b/supabase/.prettierignore @@ -0,0 +1,20 @@ +.expo +.next +node_modules +package-lock.json +docker* +apps/**/out +# prettier-plugin-sql-cst only supports sqlite syntax +**/supabase/migrations/*.sql +apps/www/schema.sql +apps/www/public/images/* +apps/docs/**/generated/* +apps/docs/examples +examples/slack-clone/nextjs-slack-clone/full-schema.sql +# ignore files with custom js formatting +apps/studio/public +apps/**/.turbo +apps/docs/CONTRIBUTING.md +apps/design-system/__registry__ +packages/icons/__registry__ +packages/icons/src/icons/*.ts diff --git a/supabase/.prettierrc b/supabase/.prettierrc new file mode 100644 index 0000000..58dc164 --- /dev/null +++ b/supabase/.prettierrc @@ -0,0 +1,16 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "printWidth": 100, + "endOfLine": "lf", + "sqlKeywordCase": "lower", + "plugins": ["prettier-plugin-sql-cst"], + "overrides": [ + { + "files": "**/*.json", + "options": { "parser": "json" } + } + ] +} diff --git a/supabase/.vale.ini b/supabase/.vale.ini new file mode 100644 index 0000000..1ff9438 --- /dev/null +++ b/supabase/.vale.ini @@ -0,0 +1,14 @@ +StylesPath = './vale/styles' +MinAlertLevel = 'suggestion' + +Vocab = Supabase + +[formats] +mdx = md + +[*] +BasedOnStyles = Vale, Custom +BlockIgnores = (?s) *(.*?), \ +(?s) *(export\s+const\s+\S+\s*=\s*\[.*?\]), \ +(?s) *() +TokenIgnores = (href\s*([=:])\s*['"].*?\1), \ diff --git a/supabase/CONTRIBUTING.md b/supabase/CONTRIBUTING.md new file mode 100644 index 0000000..b5d3188 --- /dev/null +++ b/supabase/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# CONTRIBUTING.md + +Thank you for contributing to Supabase! We’re a big, exciting open source project and we’d love to have you contribute! Here’s some resources and guidance to help you get started: + +[1. Getting Started](#getting-started) +[2. Issues](#issues) +[3. Pull Requests](#pull-requests) + +## Getting Started + +To ensure a positive and inclusive environment, please read our [code of conduct](https://github.com/supabase/.github/blob/main/CODE_OF_CONDUCT.md) before contributing. For help setting up the code in this repo, please follow our [DEVELOPERS.md](https://github.com/supabase/supabase/blob/master/DEVELOPERS.md) file. For the [docs](https://supabase.com/docs) site, follow this [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md) guide. + +## Issues + +If you find a bug, please create an Issue and we’ll triage it. + +- Please search [existing Issues](https://github.com/supabase/supabase/issues) before creating a new one. +- Please include a clear description of the problem along with steps to reproduce it. Exact steps with screenshots and urls really help here. + +## Pull Requests + +We actively welcome your Pull Requests! A couple of things to keep in mind before you submit: + +- If you’re fixing an Issue, make sure someone else hasn’t already created a PR fixing the same issue. Likewise, make sure to link your PR to the related Issue(s). +- We will always try to accept the first viable PR that resolves the Issue. +- If you're new, we encourage you to take a look at issues tagged with [good first issue](https://github.com/supabase/supabase/labels/good%20first%20issue). +- If you’re submitting a new feature, make sure you have opened a [Discussion](https://github.com/orgs/supabase/discussions/new/choose) to discuss the new feature before opening a PR. We’d love to accept your hard work, but unfortunately if a feature hasn’t gone through a proper design process, your PR will be closed. +- Please use the PR message template and provide detailed context for quicker review. PRs without clear problem statements will be closed. + +Prior to submitting your PR, please conduct the following pre-flight checks: + +- Run `npm run build` locally to ensure that your code builds successfully without having to wait on us to approve Vercel Preview deploys. +- Ensure that the Prettier tests run successfully on your PR. + +Running these before you create the PR will help reduce back and forth with the team. diff --git a/supabase/DEVELOPERS.md b/supabase/DEVELOPERS.md new file mode 100644 index 0000000..26f4013 --- /dev/null +++ b/supabase/DEVELOPERS.md @@ -0,0 +1,214 @@ +# Developing Supabase + +- [Developing Supabase](#developing-supabase) + + - [Getting started](#getting-started) + - [Install dependencies](#install-dependencies) + - [Local development](#local-development) + - [Fork the repo](#fork-the-repo) + - [Clone the repo](#clone-the-repo) + - [Install dependencies](#install-dependencies-1) + - [Running sites individually](#running-sites-individually) + - [Shared components](#shared-components) + - [Installing packages](#installing-packages) + - [Running Docker for Supabase Studio](#running-docker-for-supabase-studio) + - [Prerequsites](#prerequsites) + - [Get Started](#get-started) + - [Create a pull request](#create-a-pull-request) + - [Issue assignment](#issue-assignment) + - [Common tasks](#common-tasks) + - [Add a redirect](#add-a-redirect) + - [Federated docs](#federated-docs) + - [Community channels](#community-channels) + - [Contributors](#contributors) + +- [Common tasks](#common-tasks) + - [Add a redirect](#add-a-redirect) +- [Community channels](#community-channels) + +## Getting started + +Thank you for your interest in [Supabase](https://supabase.com) and your willingness to contribute! + +To ensure a positive and inclusive environment, please read our [code of conduct](https://github.com/supabase/.github/blob/main/CODE_OF_CONDUCT.md). We encourage you to explore the existing [issues](https://github.com/supabase/supabase/issues) to see how you can make a meaningful impact. This document will help you setup your development environment. + +### Install dependencies + +You will need to install and configure the following dependencies on your machine to build [Supabase](https://supabase.com): + +- [Git](http://git-scm.com/) +- [Node.js v20.x (LTS)](http://nodejs.org) +- [pnpm](https://pnpm.io/) version 9.x.x or higher +- [make](https://www.gnu.org/software/make/) or the equivalent to `build-essentials` for your OS +- [Docker](https://docs.docker.com/get-docker/) (to run studio locally) + +## Local development + +This repo uses [Turborepo](https://turborepo.org/docs). + +All of our apps are in this [Turborepo](https://turborepo.org/docs), which make it easy to share packages and config between projects. + +### Fork the repo + +To contribute code to [Supabase](https://supabase.com), you must fork the [Supabase repo](https://github.com/supabase/supabase). + +### Clone the repo + +1. Clone your GitHub forked repo: + + ```sh + git clone https://github.com//supabase.git + ``` + +2. Go to the Supabase directory: + ```sh + cd supabase + ``` + +### Install dependencies + +1. Install the dependencies in the root of the repo. + + ```sh + pnpm install # install dependencies + ``` + +2. Copy the example `.env.local.example` to `.env.local` + + ```sh + cp apps/www/.env.local.example apps/www/.env.local + ``` + +3. After that you can run the apps simultaneously with the following. + ```sh + pnpm dev # start all the applications + ``` + +Then visit, and edit, any of the following sites: + +| Site | Directory | Scope name | Description | Local development server | +| -------------------------------------------------------- | -------------- | ---------- | --------------------------------------------- | -------------------------- | +| [supabase.com](https://supabase.com) | `/apps/www` | www | The main website | http://localhost:3000 | +| [supabase.com/dashboard](https://supabase.com/dashboard) | `/apps/studio` | studio | Studio dashboard (requires Docker, see below) | http://localhost:8082 | +| [supabase.com/docs](https://supabase.com/docs) | `/apps/docs` | docs | Guides and Reference (Next.js based) | http://localhost:3001/docs | + +#### Running sites individually + +You can run any of the sites individually by using the scope name. For example: + +```sh +pnpm dev:www +``` + +Note: Particularly for `www` make sure you have copied `apps/www/.env.local.example` to `apps/www/.env.local` + +#### Shared components + +The monorepo has a set of shared components under `/packages`: + +- `/packages/ai-commands`: Helpers/Commands for AI related functions +- `/packages/common`: Common React components, shared between all sites +- `/packages/config`: All shared config +- `/packages/shared-data`: Shared data that can be used across all apps +- `/packages/tsconfig`: Shared Typescript settings +- `/packages/ui`: Common UI components + +#### Installing packages + +Installing a package in a specific workspace requires you to move to the workspace and then run the install command. + +For example: + +1. `cd apps/studio`: move to the `studio` workspace +2. `pnpm add react`: installs `react` into `studio` workspace + + +--- + +## Running Docker for Supabase Studio + +To run Studio locally, you'll need to setup Docker in addition to your NextJS frontend. + +#### Prerequsites + +First, make sure you have the Docker installed on your device. You can download and install it from [here](https://docs.docker.com/get-docker/). + +#### Get Started + +1. Navigate to the `docker` directory in your forked repo + + ```sh + cd docker + ``` + +2. Copy the example `env` file + + ```sh + cp .env.example .env + ``` + +3. Run docker + + ```sh + docker compose up + ``` + +This command initializes the containers specified in the `docker-compose.yml` file. It might take a few moments to complete, depending on your computer and internet connection. + +Once the `docker compose up` process completes, you should have your local version of Supabase up and running within Docker containers. You can access it at `http://localhost:8082`. + +Remember to keep the Docker application open as long as you're working with your local Supabase instance. + +## Create a pull request + +After making any changes, open a pull request. Once you submit your pull request, the Supabase team will review it with you. + +Once your PR has been merged, you will be proudly listed as a contributor in the [contributor chart](https://github.com/supabase/supabase/graphs/contributors)! + +## Issue assignment + +We don't have a process for assigning issues to contributors. Please feel free to jump into any issues in this repo that you are able to help with. Our intention is to encourage anyone to help without feeling burdened by an assigned task. Life can sometimes get in the way, and we don't want to leave contributors feeling obligated to complete issues when they may have limited time or unexpected commitments. + +We also recognize that not having a process can sometimes lead to competing or duplicate PRs. There's no perfect solution here. We encourage you to communicate early and often on an Issue to indicate that you're actively working on it. If you see that an Issue already has a PR, try working with that author instead of drafting your own. + +We review PRs in the order of their submission. We try to accept the earliest one that is closest to being ready to merge. + +--- + +## Common tasks + +### Add a redirect + +Create a new entry in the [`redirects.js`](https://github.com/supabase/supabase/blob/master/apps/www/lib/redirects.js) file in our main site. + +--- + +### Federated docs + +We support "federating" docs, meaning doc content can come directly from external repos other than [`supabase/supabase`](https://github.com/supabase/supabase). + +- It's great for things like client libs who have their own set of docs that we don't want to duplicate on the official Supabase docs (eg. [`supabase/vecs`](https://github.com/supabase/vecs)). +- No duplication or manual steps required - fetches and generates automatically as part of the docs build pipeline +- It's flexible - you can "embed" external docs nearly anywhere at any level in Supabase docs, but they will feel native +- If you are maintaining a repo containing docs that you think could also live in Supabase docs, feel free to create an issue and we can work together to integrate + +Federated docs work using Next.js's build pipeline. We use `getStaticProps()` to fetch remote documentation (ie. markdown) at build time which is processed and passed to the respective page within the docs. + +See the [Vecs Python source code](https://github.com/supabase/supabase/blob/master/apps/docs/pages/guides/ai/python/%5Bslug%5D.tsx) to see how we do this for [`supabase/vecs`](https://github.com/supabase/vecs). Use this as a starting point for federating other docs. + +Some things to consider: + +- Links will often need to be transformed. For example if you are bringing in external markdown content, they may contain relative links that may not translate 1-to-1 after rendering in the Supabase docs. Use the [Link Transform](https://github.com/supabase/supabase/blob/master/apps/docs/lib/mdx/plugins/rehypeLinkTransform.ts) rehype plugin to transform links. +- External markdown may contain syntax extensions that Supabase docs don't understand by default (eg. [mkdocs-material extensions](https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown)). We've built a few remark plugins to support these extensions (eg. [MkDocs Admonition](https://github.com/supabase/supabase/blob/master/apps/docs/lib/mdx/plugins/remarkAdmonition.ts)). If there is a markdown extension that you need that isn't built yet, feel free to open an issue and we can work together to create it. + +--- + +## Community channels + +If you get stuck somewhere or have any questions, join our [Discord Community Server](https://discord.supabase.com/) or the [Github Discussions](https://github.com/supabase/supabase/discussions). We are here to help! + +## Contributors + + + contributors + diff --git a/supabase/LICENSE b/supabase/LICENSE new file mode 100644 index 0000000..49ea124 --- /dev/null +++ b/supabase/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 Supabase + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/supabase/Makefile b/supabase/Makefile new file mode 100644 index 0000000..4984751 --- /dev/null +++ b/supabase/Makefile @@ -0,0 +1,48 @@ +REPO_DIR=$(shell pwd) + +help: + @echo "\SCRIPTS\n" + @echo "make github.contributors # pull a list of all contributors" + @echo "make github.issues # pull a list of all issue creators" + @echo "make github.repos # pull a list of our repos" + @echo "make github.traction # get a history of stargazers for our individual repos" + +github.contributors.%: + curl -sS https://api.github.com/repos/supabase/$*/contributors \ + | jq -r 'map_values({ username: .login }) \ + | unique \ + | sort_by(.username)' \ + > $(REPO_DIR)/web/src/data/contributors/$*.json + +.PHONY: github.rcontributorsepos +github.contributors: \ + github.contributors.supabase \ + github.contributors.supabase-js \ + github.contributors.supabase-py \ + github.contributors.supabase-flutter \ + github.contributors.supabase-dart + +github.issues: + curl -sS https://api.github.com/repos/supabase/supabase/issues \ + | jq -r 'map_values({username: .user.login, avatar_url: .user.avatar_url}) \ + | unique \ + | sort_by(.username)' \ + > $(REPO_DIR)/web/src/data/contributors/issues.json + +.PHONY: github.repos +github.repos: \ + github.repos.supabase \ + github.repos.realtime \ + github.repos.postgres \ + github.repos.postgres-meta + +github.repos.%: + curl -sS https://api.github.com/repos/supabase/$* \ + > $(REPO_DIR)/web/src/data/repos/$*.json + +github.traction: + cd "$(REPO_DIR)"/web && \ + npm run traction + +dev: + vercel dev --listen 8080 --local-config vercel-local.json \ No newline at end of file diff --git a/supabase/README.md b/supabase/README.md new file mode 100644 index 0000000..3a165a2 --- /dev/null +++ b/supabase/README.md @@ -0,0 +1,287 @@ +

+ + +

+ +# Supabase + +[Supabase](https://supabase.com) is an open source Firebase alternative. We're building the features of Firebase using enterprise-grade open source tools. + +- [x] Hosted Postgres Database. [Docs](https://supabase.com/docs/guides/database) +- [x] Authentication and Authorization. [Docs](https://supabase.com/docs/guides/auth) +- [x] Auto-generated APIs. + - [x] REST. [Docs](https://supabase.com/docs/guides/api) + - [x] GraphQL. [Docs](https://supabase.com/docs/guides/graphql) + - [x] Realtime subscriptions. [Docs](https://supabase.com/docs/guides/realtime) +- [x] Functions. + - [x] Database Functions. [Docs](https://supabase.com/docs/guides/database/functions) + - [x] Edge Functions [Docs](https://supabase.com/docs/guides/functions) +- [x] File Storage. [Docs](https://supabase.com/docs/guides/storage) +- [x] AI + Vector/Embeddings Toolkit. [Docs](https://supabase.com/docs/guides/ai) +- [x] Dashboard + +![Supabase Dashboard](https://raw.githubusercontent.com/supabase/supabase/master/apps/www/public/images/github/supabase-dashboard.png) + +Watch "releases" of this repo to get notified of major updates. + +Watch this repo + +## Documentation + +For full documentation, visit [supabase.com/docs](https://supabase.com/docs) + +To see how to Contribute, visit [Getting Started](./DEVELOPERS.md) + +## Community & Support + +- [Community Forum](https://github.com/supabase/supabase/discussions). Best for: help with building, discussion about database best practices. +- [GitHub Issues](https://github.com/supabase/supabase/issues). Best for: bugs and errors you encounter using Supabase. +- [Email Support](https://supabase.com/docs/support#business-support). Best for: problems with your database or infrastructure. +- [Discord](https://discord.supabase.com). Best for: sharing your applications and hanging out with the community. + +## How it works + +Supabase is a combination of open source tools. We’re building the features of Firebase using enterprise-grade, open source products. If the tools and communities exist, with an MIT, Apache 2, or equivalent open license, we will use and support that tool. If the tool doesn't exist, we build and open source it ourselves. Supabase is not a 1-to-1 mapping of Firebase. Our aim is to give developers a Firebase-like developer experience using open source tools. + +**Architecture** + +Supabase is a [hosted platform](https://supabase.com/dashboard). You can sign up and start using Supabase without installing anything. +You can also [self-host](https://supabase.com/docs/guides/hosting/overview) and [develop locally](https://supabase.com/docs/guides/local-development). + +![Architecture](apps/docs/public/img/supabase-architecture.svg) + +- [Postgres](https://www.postgresql.org/) is an object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance. +- [Realtime](https://github.com/supabase/realtime) is an Elixir server that allows you to listen to PostgreSQL inserts, updates, and deletes using websockets. Realtime polls Postgres' built-in replication functionality for database changes, converts changes to JSON, then broadcasts the JSON over websockets to authorized clients. +- [PostgREST](http://postgrest.org/) is a web server that turns your PostgreSQL database directly into a RESTful API. +- [GoTrue](https://github.com/supabase/gotrue) is a JWT based API for managing users and issuing JWT tokens. +- [Storage](https://github.com/supabase/storage-api) provides a RESTful interface for managing Files stored in S3, using Postgres to manage permissions. +- [pg_graphql](http://github.com/supabase/pg_graphql/) a PostgreSQL extension that exposes a GraphQL API. +- [postgres-meta](https://github.com/supabase/postgres-meta) is a RESTful API for managing your Postgres, allowing you to fetch tables, add roles, and run queries, etc. +- [Kong](https://github.com/Kong/kong) is a cloud-native API gateway. + +#### Client libraries + +Our approach for client libraries is modular. Each sub-library is a standalone implementation for a single external system. This is one of the ways we support existing tools. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LanguageClientFeature-Clients (bundled in Supabase client)
SupabasePostgRESTGoTrueRealtimeStorageFunctions
⚡️ Official ⚡️
JavaScript (TypeScript)supabase-jspostgrest-jsgotrue-jsrealtime-jsstorage-jsfunctions-js
Fluttersupabase-flutterpostgrest-dartgotrue-dartrealtime-dartstorage-dartfunctions-dart
Swiftsupabase-swiftpostgrest-swiftauth-swiftrealtime-swiftstorage-swiftfunctions-swift
Pythonsupabase-pypostgrest-pygotrue-pyrealtime-pystorage-pyfunctions-py
💚 Community 💚
C#supabase-csharppostgrest-csharpgotrue-csharprealtime-csharpstorage-csharpfunctions-csharp
Go-postgrest-gogotrue-go-storage-gofunctions-go
Java--gotrue-java-storage-java-
Kotlinsupabase-ktpostgrest-ktauth-ktrealtime-ktstorage-ktfunctions-kt
Rubysupabase-rbpostgrest-rb----
Rust-postgrest-rs----
Godot Engine (GDScript)supabase-gdscriptpostgrest-gdscriptgotrue-gdscriptrealtime-gdscriptstorage-gdscriptfunctions-gdscript
+ + + + +## Badges + +![Made with Supabase](./apps/www/public/badge-made-with-supabase.svg) + +```md +[![Made with Supabase](https://supabase.com/badge-made-with-supabase.svg)](https://supabase.com) +``` + +```html + + Made with Supabase + +``` + +![Made with Supabase (dark)](./apps/www/public/badge-made-with-supabase-dark.svg) + +```md +[![Made with Supabase](https://supabase.com/badge-made-with-supabase-dark.svg)](https://supabase.com) +``` + +```html + + Made with Supabase + +``` + +## Translations + +- [Arabic | العربية](/i18n/README.ar.md) +- [Albanian / Shqip](/i18n/README.sq.md) +- [Bangla / বাংলা](/i18n/README.bn.md) +- [Bulgarian / Български](/i18n/README.bg.md) +- [Catalan / Català](/i18n/README.ca.md) +- [Czech / čeština](/i18n/README.cs.md) +- [Danish / Dansk](/i18n/README.da.md) +- [Dutch / Nederlands](/i18n/README.nl.md) +- [English](https://github.com/supabase/supabase) +- [Estonian / eesti keel](/i18n/README.et.md) +- [Finnish / Suomalainen](/i18n/README.fi.md) +- [French / Français](/i18n/README.fr.md) +- [German / Deutsch](/i18n/README.de.md) +- [Greek / Ελληνικά](/i18n/README.el.md) +- [Gujarati / ગુજરાતી](/i18n/README.gu.md) +- [Hebrew / עברית](/i18n/README.he.md) +- [Hindi / हिंदी](/i18n/README.hi.md) +- [Hungarian / Magyar](/i18n/README.hu.md) +- [Nepali / नेपाली](/i18n/README.ne.md) +- [Indonesian / Bahasa Indonesia](/i18n/README.id.md) +- [Italiano / Italian](/i18n/README.it.md) +- [Japanese / 日本語](/i18n/README.jp.md) +- [Korean / 한국어](/i18n/README.ko.md) +- [Lithuanian / lietuvių](/i18n/README.lt.md) +- [Latvian / latviski](/i18n/README.lv.md) +- [Malay / Bahasa Malaysia](/i18n/README.ms.md) +- [Norwegian (Bokmål) / Norsk (Bokmål)](/i18n/README.nb.md) +- [Persian / فارسی](/i18n/README.fa.md) +- [Polish / Polski](/i18n/README.pl.md) +- [Portuguese / Português](/i18n/README.pt.md) +- [Portuguese (Brazilian) / Português Brasileiro](/i18n/README.pt-br.md) +- [Romanian / Română](/i18n/README.ro.md) +- [Russian / Pусский](/i18n/README.ru.md) +- [Serbian / Srpski](/i18n/README.sr.md) +- [Sinhala / සිංහල](/i18n/README.si.md) +- [Slovak / slovenský](/i18n/README.sk.md) +- [Slovenian / Slovenščina](/i18n/README.sl.md) +- [Spanish / Español](/i18n/README.es.md) +- [Simplified Chinese / 简体中文](/i18n/README.zh-cn.md) +- [Swedish / Svenska](/i18n/README.sv.md) +- [Thai / ไทย](/i18n/README.th.md) +- [Traditional Chinese / 繁體中文](/i18n/README.zh-tw.md) +- [Turkish / Türkçe](/i18n/README.tr.md) +- [Ukrainian / Українська](/i18n/README.uk.md) +- [Vietnamese / Tiếng Việt](/i18n/README.vi-vn.md) +- [List of translations](/i18n/languages.md) diff --git a/supabase/SECURITY.md b/supabase/SECURITY.md new file mode 120000 index 0000000..65dd29a --- /dev/null +++ b/supabase/SECURITY.md @@ -0,0 +1 @@ +apps/docs/public/.well-known/security.txt \ No newline at end of file diff --git a/supabase/docker/.env b/supabase/docker/.env new file mode 100644 index 0000000..ee08d66 --- /dev/null +++ b/supabase/docker/.env @@ -0,0 +1,123 @@ +############ +# Secrets +# YOU MUST CHANGE THESE BEFORE GOING INTO PRODUCTION +############ + +POSTGRES_PASSWORD=your-super-secret-and-long-postgres-password +JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters-long +ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE +SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q +DASHBOARD_USERNAME=supabase +DASHBOARD_PASSWORD=dhbw&2025 +SECRET_KEY_BASE=UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq +VAULT_ENC_KEY=your-encryption-key-32-chars-min + + +############ +# Database - You can change these to any PostgreSQL database that has logical replication enabled. +############ + +POSTGRES_HOST=db +POSTGRES_DB=postgres +POSTGRES_PORT=5432 +# default user is postgres + + +############ +# Supavisor -- Database pooler +############ +POOLER_PROXY_PORT_TRANSACTION=6543 +POOLER_DEFAULT_POOL_SIZE=20 +POOLER_MAX_CLIENT_CONN=100 +POOLER_TENANT_ID=your-tenant-id + + +############ +# API Proxy - Configuration for the Kong Reverse proxy. +############ + +KONG_HTTP_PORT=8000 +KONG_HTTPS_PORT=8443 + + +############ +# API - Configuration for PostgREST. +############ + +PGRST_DB_SCHEMAS=public,storage,graphql_public + + +############ +# Auth - Configuration for the GoTrue authentication server. +############ + +## General +SITE_URL=http://localhost:3000 +ADDITIONAL_REDIRECT_URLS= +JWT_EXPIRY=3600 +DISABLE_SIGNUP=false +API_EXTERNAL_URL=http://localhost:8000 + +## Mailer Config +MAILER_URLPATHS_CONFIRMATION="/auth/v1/verify" +MAILER_URLPATHS_INVITE="/auth/v1/verify" +MAILER_URLPATHS_RECOVERY="/auth/v1/verify" +MAILER_URLPATHS_EMAIL_CHANGE="/auth/v1/verify" + +## Email auth +ENABLE_EMAIL_SIGNUP=true +ENABLE_EMAIL_AUTOCONFIRM=false +SMTP_ADMIN_EMAIL=admin@example.com +SMTP_HOST=supabase-mail +SMTP_PORT=2500 +SMTP_USER=fake_mail_user +SMTP_PASS=fake_mail_password +SMTP_SENDER_NAME=fake_sender +ENABLE_ANONYMOUS_USERS=false + +## Phone auth +ENABLE_PHONE_SIGNUP=true +ENABLE_PHONE_AUTOCONFIRM=true + + +############ +# Studio - Configuration for the Dashboard +############ + +STUDIO_DEFAULT_ORGANIZATION=Default Organization +STUDIO_DEFAULT_PROJECT=Default Project + +STUDIO_PORT=3000 +# replace if you intend to use Studio outside of localhost +SUPABASE_PUBLIC_URL=http://localhost:8000 + +# Enable webp support +IMGPROXY_ENABLE_WEBP_DETECTION=true + +# Add your OpenAI API key to enable SQL Editor Assistant +OPENAI_API_KEY= + + +############ +# Functions - Configuration for Functions +############ +# NOTE: VERIFY_JWT applies to all functions. Per-function VERIFY_JWT is not supported yet. +FUNCTIONS_VERIFY_JWT=false + + +############ +# Logs - Configuration for Logflare +# Please refer to https://supabase.com/docs/reference/self-hosting-analytics/introduction +############ + +LOGFLARE_LOGGER_BACKEND_API_KEY=your-super-secret-and-long-logflare-key + +# Change vector.toml sinks to reflect this change +LOGFLARE_API_KEY=your-super-secret-and-long-logflare-key + +# Docker socket location - this value will differ depending on your OS +DOCKER_SOCKET_LOCATION=/var/run/docker.sock + +# Google Cloud Project details +GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID +GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER diff --git a/supabase/docker/README.md b/supabase/docker/README.md new file mode 100644 index 0000000..9ab215b --- /dev/null +++ b/supabase/docker/README.md @@ -0,0 +1,3 @@ +# Supabase Docker + +This is a minimal Docker Compose setup for self-hosting Supabase. Follow the steps [here](https://supabase.com/docs/guides/hosting/docker) to get started. diff --git a/supabase/docker/dev/data.sql b/supabase/docker/dev/data.sql new file mode 100644 index 0000000..2328004 --- /dev/null +++ b/supabase/docker/dev/data.sql @@ -0,0 +1,48 @@ +create table profiles ( + id uuid references auth.users not null, + updated_at timestamp with time zone, + username text unique, + avatar_url text, + website text, + + primary key (id), + unique(username), + constraint username_length check (char_length(username) >= 3) +); + +alter table profiles enable row level security; + +create policy "Public profiles are viewable by the owner." + on profiles for select + using ( auth.uid() = id ); + +create policy "Users can insert their own profile." + on profiles for insert + with check ( auth.uid() = id ); + +create policy "Users can update own profile." + on profiles for update + using ( auth.uid() = id ); + +-- Set up Realtime +begin; + drop publication if exists supabase_realtime; + create publication supabase_realtime; +commit; +alter publication supabase_realtime add table profiles; + +-- Set up Storage +insert into storage.buckets (id, name) +values ('avatars', 'avatars'); + +create policy "Avatar images are publicly accessible." + on storage.objects for select + using ( bucket_id = 'avatars' ); + +create policy "Anyone can upload an avatar." + on storage.objects for insert + with check ( bucket_id = 'avatars' ); + +create policy "Anyone can update an avatar." + on storage.objects for update + with check ( bucket_id = 'avatars' ); diff --git a/supabase/docker/dev/docker-compose.dev.yml b/supabase/docker/dev/docker-compose.dev.yml new file mode 100644 index 0000000..ca19a0a --- /dev/null +++ b/supabase/docker/dev/docker-compose.dev.yml @@ -0,0 +1,34 @@ +version: "3.8" + +services: + studio: + build: + context: .. + dockerfile: studio/Dockerfile + target: dev + ports: + - 8082:8082 + mail: + container_name: supabase-mail + image: inbucket/inbucket:3.0.3 + ports: + - '2500:2500' # SMTP + - '9000:9000' # web interface + - '1100:1100' # POP3 + auth: + environment: + - GOTRUE_SMTP_USER= + - GOTRUE_SMTP_PASS= + meta: + ports: + - 5555:8080 + db: + restart: 'no' + volumes: + # Always use a fresh database when developing + - /var/lib/postgresql/data + # Seed data should be inserted last (alphabetical order) + - ./dev/data.sql:/docker-entrypoint-initdb.d/seed.sql + storage: + volumes: + - /var/lib/storage diff --git a/supabase/docker/docker-compose.s3.yml b/supabase/docker/docker-compose.s3.yml new file mode 100644 index 0000000..043691a --- /dev/null +++ b/supabase/docker/docker-compose.s3.yml @@ -0,0 +1,94 @@ +services: + + minio: + image: minio/minio + ports: + - '9000:9000' + - '9001:9001' + environment: + MINIO_ROOT_USER: supa-storage + MINIO_ROOT_PASSWORD: secret1234 + command: server --console-address ":9001" /data + healthcheck: + test: [ "CMD", "curl", "-f", "http://minio:9000/minio/health/live" ] + interval: 2s + timeout: 10s + retries: 5 + volumes: + - ./volumes/storage:/data:z + + minio-createbucket: + image: minio/mc + depends_on: + minio: + condition: service_healthy + entrypoint: > + /bin/sh -c " + /usr/bin/mc alias set supa-minio http://minio:9000 supa-storage secret1234; + /usr/bin/mc mb supa-minio/stub; + exit 0; + " + + storage: + container_name: supabase-storage + image: supabase/storage-api:v1.11.13 + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + rest: + condition: service_started + imgproxy: + condition: service_started + minio: + condition: service_healthy + healthcheck: + test: + [ + "CMD", + "wget", + "--no-verbose", + "--tries=1", + "--spider", + "http://localhost:5000/status" + ] + timeout: 5s + interval: 5s + retries: 3 + restart: unless-stopped + environment: + ANON_KEY: ${ANON_KEY} + SERVICE_KEY: ${SERVICE_ROLE_KEY} + POSTGREST_URL: http://rest:3000 + PGRST_JWT_SECRET: ${JWT_SECRET} + DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} + FILE_SIZE_LIMIT: 52428800 + STORAGE_BACKEND: s3 + GLOBAL_S3_BUCKET: stub + GLOBAL_S3_ENDPOINT: http://minio:9000 + GLOBAL_S3_PROTOCOL: http + GLOBAL_S3_FORCE_PATH_STYLE: true + AWS_ACCESS_KEY_ID: supa-storage + AWS_SECRET_ACCESS_KEY: secret1234 + AWS_DEFAULT_REGION: stub + FILE_STORAGE_BACKEND_PATH: /var/lib/storage + TENANT_ID: stub + # TODO: https://github.com/supabase/storage-api/issues/55 + REGION: stub + ENABLE_IMAGE_TRANSFORMATION: "true" + IMGPROXY_URL: http://imgproxy:5001 + volumes: + - ./volumes/storage:/var/lib/storage:z + + imgproxy: + container_name: supabase-imgproxy + image: darthsim/imgproxy:v3.8.0 + healthcheck: + test: [ "CMD", "imgproxy", "health" ] + timeout: 5s + interval: 5s + retries: 3 + environment: + IMGPROXY_BIND: ":5001" + IMGPROXY_USE_ETAG: "true" + IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION} diff --git a/supabase/docker/docker-compose.yml b/supabase/docker/docker-compose.yml new file mode 100644 index 0000000..36b272f --- /dev/null +++ b/supabase/docker/docker-compose.yml @@ -0,0 +1,533 @@ +# Usage +# Start: docker compose up +# With helpers: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up +# Stop: docker compose down +# Destroy: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans +# Reset everything: ./reset.sh + +name: supabase + +services: + + studio: + container_name: supabase-studio + image: supabase/studio:20250113-83c9420 + restart: unless-stopped + healthcheck: + test: + [ + "CMD", + "node", + "-e", + "fetch('http://studio:3000/api/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})" + ] + timeout: 10s + interval: 5s + retries: 3 + depends_on: + analytics: + condition: service_healthy + environment: + STUDIO_PG_META_URL: http://meta:8080 + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + + DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION} + DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT} + OPENAI_API_KEY: ${OPENAI_API_KEY:-} + + SUPABASE_URL: http://kong:8000 + SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} + SUPABASE_ANON_KEY: ${ANON_KEY} + SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} + AUTH_JWT_SECRET: ${JWT_SECRET} + + LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + LOGFLARE_URL: http://analytics:4000 + NEXT_PUBLIC_ENABLE_LOGS: true + # Comment to use Big Query backend for analytics + NEXT_ANALYTICS_BACKEND_PROVIDER: postgres + # Uncomment to use Big Query backend for analytics + # NEXT_ANALYTICS_BACKEND_PROVIDER: bigquery + + kong: + container_name: supabase-kong + image: kong:2.8.1 + restart: unless-stopped + ports: + - ${KONG_HTTP_PORT}:8000/tcp + - ${KONG_HTTPS_PORT}:8443/tcp + volumes: + # https://github.com/supabase/supabase/issues/12661 + - ./volumes/api/kong.yml:/home/kong/temp.yml:ro + depends_on: + analytics: + condition: service_healthy + environment: + KONG_DATABASE: "off" + KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml + # https://github.com/supabase/cli/issues/14 + KONG_DNS_ORDER: LAST,A,CNAME + KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth + KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k + KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k + SUPABASE_ANON_KEY: ${ANON_KEY} + SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} + DASHBOARD_USERNAME: ${DASHBOARD_USERNAME} + DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD} + # https://unix.stackexchange.com/a/294837 + entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start' + + auth: + container_name: supabase-auth + image: supabase/gotrue:v2.167.0 + restart: unless-stopped + healthcheck: + test: + [ + "CMD", + "wget", + "--no-verbose", + "--tries=1", + "--spider", + "http://localhost:9999/health" + ] + timeout: 5s + interval: 5s + retries: 3 + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + analytics: + condition: service_healthy + environment: + GOTRUE_API_HOST: 0.0.0.0 + GOTRUE_API_PORT: 9999 + API_EXTERNAL_URL: ${API_EXTERNAL_URL} + + GOTRUE_DB_DRIVER: postgres + GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} + + GOTRUE_SITE_URL: ${SITE_URL} + GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS} + GOTRUE_DISABLE_SIGNUP: ${DISABLE_SIGNUP} + + GOTRUE_JWT_ADMIN_ROLES: service_role + GOTRUE_JWT_AUD: authenticated + GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated + GOTRUE_JWT_EXP: ${JWT_EXPIRY} + GOTRUE_JWT_SECRET: ${JWT_SECRET} + + GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP} + GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS} + GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM} + + # Uncomment to bypass nonce check in ID Token flow. Commonly set to true when using Google Sign In on mobile. + # GOTRUE_EXTERNAL_SKIP_NONCE_CHECK: true + + # GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true + # GOTRUE_SMTP_MAX_FREQUENCY: 1s + GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL} + GOTRUE_SMTP_HOST: ${SMTP_HOST} + GOTRUE_SMTP_PORT: ${SMTP_PORT} + GOTRUE_SMTP_USER: ${SMTP_USER} + GOTRUE_SMTP_PASS: ${SMTP_PASS} + GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME} + GOTRUE_MAILER_URLPATHS_INVITE: ${MAILER_URLPATHS_INVITE} + GOTRUE_MAILER_URLPATHS_CONFIRMATION: ${MAILER_URLPATHS_CONFIRMATION} + GOTRUE_MAILER_URLPATHS_RECOVERY: ${MAILER_URLPATHS_RECOVERY} + GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: ${MAILER_URLPATHS_EMAIL_CHANGE} + + GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP} + GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM} + # Uncomment to enable custom access token hook. Please see: https://supabase.com/docs/guides/auth/auth-hooks for full list of hooks and additional details about custom_access_token_hook + + # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED: "true" + # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI: "pg-functions://postgres/public/custom_access_token_hook" + # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS: "" + + # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED: "true" + # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/mfa_verification_attempt" + + # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_ENABLED: "true" + # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/password_verification_attempt" + + # GOTRUE_HOOK_SEND_SMS_ENABLED: "false" + # GOTRUE_HOOK_SEND_SMS_URI: "pg-functions://postgres/public/custom_access_token_hook" + # GOTRUE_HOOK_SEND_SMS_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n" + + # GOTRUE_HOOK_SEND_EMAIL_ENABLED: "false" + # GOTRUE_HOOK_SEND_EMAIL_URI: "http://host.docker.internal:54321/functions/v1/email_sender" + # GOTRUE_HOOK_SEND_EMAIL_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n" + + rest: + container_name: supabase-rest + image: postgrest/postgrest:v12.2.0 + restart: unless-stopped + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + analytics: + condition: service_healthy + environment: + PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} + PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} + PGRST_DB_ANON_ROLE: anon + PGRST_JWT_SECRET: ${JWT_SECRET} + PGRST_DB_USE_LEGACY_GUCS: "false" + PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET} + PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY} + command: + [ + "postgrest" + ] + + realtime: + # This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain + container_name: realtime-dev.supabase-realtime + image: supabase/realtime:v2.34.7 + restart: unless-stopped + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + analytics: + condition: service_healthy + healthcheck: + test: + [ + "CMD", + "curl", + "-sSfL", + "--head", + "-o", + "/dev/null", + "-H", + "Authorization: Bearer ${ANON_KEY}", + "http://localhost:4000/api/tenants/realtime-dev/health" + ] + timeout: 5s + interval: 5s + retries: 3 + environment: + PORT: 4000 + DB_HOST: ${POSTGRES_HOST} + DB_PORT: ${POSTGRES_PORT} + DB_USER: supabase_admin + DB_PASSWORD: ${POSTGRES_PASSWORD} + DB_NAME: ${POSTGRES_DB} + DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime' + DB_ENC_KEY: supabaserealtime + API_JWT_SECRET: ${JWT_SECRET} + SECRET_KEY_BASE: ${SECRET_KEY_BASE} + ERL_AFLAGS: -proto_dist inet_tcp + DNS_NODES: "''" + RLIMIT_NOFILE: "10000" + APP_NAME: realtime + SEED_SELF_HOST: true + RUN_JANITOR: true + + # To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up + storage: + container_name: supabase-storage + image: supabase/storage-api:v1.14.5 + restart: unless-stopped + volumes: + - ./volumes/storage:/var/lib/storage:z + healthcheck: + test: + [ + "CMD", + "wget", + "--no-verbose", + "--tries=1", + "--spider", + "http://storage:5000/status" + ] + timeout: 5s + interval: 5s + retries: 3 + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + rest: + condition: service_started + imgproxy: + condition: service_started + environment: + ANON_KEY: ${ANON_KEY} + SERVICE_KEY: ${SERVICE_ROLE_KEY} + POSTGREST_URL: http://rest:3000 + PGRST_JWT_SECRET: ${JWT_SECRET} + DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} + FILE_SIZE_LIMIT: 52428800 + STORAGE_BACKEND: file + FILE_STORAGE_BACKEND_PATH: /var/lib/storage + TENANT_ID: stub + # TODO: https://github.com/supabase/storage-api/issues/55 + REGION: stub + GLOBAL_S3_BUCKET: stub + ENABLE_IMAGE_TRANSFORMATION: "true" + IMGPROXY_URL: http://imgproxy:5001 + + imgproxy: + container_name: supabase-imgproxy + image: darthsim/imgproxy:v3.8.0 + restart: unless-stopped + volumes: + - ./volumes/storage:/var/lib/storage:z + healthcheck: + test: + [ + "CMD", + "imgproxy", + "health" + ] + timeout: 5s + interval: 5s + retries: 3 + environment: + IMGPROXY_BIND: ":5001" + IMGPROXY_LOCAL_FILESYSTEM_ROOT: / + IMGPROXY_USE_ETAG: "true" + IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION} + + meta: + container_name: supabase-meta + image: supabase/postgres-meta:v0.84.2 + restart: unless-stopped + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + analytics: + condition: service_healthy + environment: + PG_META_PORT: 8080 + PG_META_DB_HOST: ${POSTGRES_HOST} + PG_META_DB_PORT: ${POSTGRES_PORT} + PG_META_DB_NAME: ${POSTGRES_DB} + PG_META_DB_USER: supabase_admin + PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD} + + functions: + container_name: supabase-edge-functions + image: supabase/edge-runtime:v1.66.5 + restart: unless-stopped + volumes: + - ./volumes/functions:/home/deno/functions:Z + depends_on: + analytics: + condition: service_healthy + environment: + JWT_SECRET: ${JWT_SECRET} + SUPABASE_URL: http://kong:8000 + SUPABASE_ANON_KEY: ${ANON_KEY} + SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY} + SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} + # TODO: Allow configuring VERIFY_JWT per function. This PR might help: https://github.com/supabase/cli/pull/786 + VERIFY_JWT: "${FUNCTIONS_VERIFY_JWT}" + command: + [ + "start", + "--main-service", + "/home/deno/functions/main" + ] + + analytics: + container_name: supabase-analytics + image: supabase/logflare:1.4.0 + restart: unless-stopped + ports: + - 4000:4000 + # Uncomment to use Big Query backend for analytics + # volumes: + # - type: bind + # source: ${PWD}/gcloud.json + # target: /opt/app/rel/logflare/bin/gcloud.json + # read_only: true + healthcheck: + test: + [ + "CMD", + "curl", + "http://localhost:4000/health" + ] + timeout: 5s + interval: 5s + retries: 10 + depends_on: + db: + # Disable this if you are using an external Postgres database + condition: service_healthy + environment: + LOGFLARE_NODE_HOST: 127.0.0.1 + DB_USERNAME: supabase_admin + DB_DATABASE: _supabase + DB_HOSTNAME: ${POSTGRES_HOST} + DB_PORT: ${POSTGRES_PORT} + DB_PASSWORD: ${POSTGRES_PASSWORD} + DB_SCHEMA: _analytics + LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + LOGFLARE_SINGLE_TENANT: true + LOGFLARE_SUPABASE_MODE: true + LOGFLARE_MIN_CLUSTER_SIZE: 1 + + # Comment variables to use Big Query backend for analytics + POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase + POSTGRES_BACKEND_SCHEMA: _analytics + LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true + # Uncomment to use Big Query backend for analytics + # GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID} + # GOOGLE_PROJECT_NUMBER: ${GOOGLE_PROJECT_NUMBER} + + # Comment out everything below this point if you are using an external Postgres database + db: + container_name: supabase-db + image: supabase/postgres:15.8.1.020 + restart: unless-stopped + volumes: + - ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z + # Must be superuser to create event trigger + - ./volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z + # Must be superuser to alter reserved role + - ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z + # Initialize the database settings with JWT_SECRET and JWT_EXP + - ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z + # PGDATA directory is persisted between restarts + - ./volumes/db/data:/var/lib/postgresql/data:Z + # Changes required for internal supabase data such as _analytics + - ./volumes/db/_supabase.sql:/docker-entrypoint-initdb.d/migrations/97-_supabase.sql:Z + # Changes required for Analytics support + - ./volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z + # Changes required for Pooler support + - ./volumes/db/pooler.sql:/docker-entrypoint-initdb.d/migrations/99-pooler.sql:Z + # Use named volume to persist pgsodium decryption key between restarts + - db-config:/etc/postgresql-custom + healthcheck: + test: + [ + "CMD", + "pg_isready", + "-U", + "postgres", + "-h", + "localhost" + ] + interval: 5s + timeout: 5s + retries: 10 + depends_on: + vector: + condition: service_healthy + environment: + POSTGRES_HOST: /var/run/postgresql + PGPORT: ${POSTGRES_PORT} + POSTGRES_PORT: ${POSTGRES_PORT} + PGPASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + PGDATABASE: ${POSTGRES_DB} + POSTGRES_DB: ${POSTGRES_DB} + JWT_SECRET: ${JWT_SECRET} + JWT_EXP: ${JWT_EXPIRY} + command: + [ + "postgres", + "-c", + "config_file=/etc/postgresql/postgresql.conf", + "-c", + "log_min_messages=fatal" # prevents Realtime polling queries from appearing in logs + ] + + vector: + container_name: supabase-vector + image: timberio/vector:0.28.1-alpine + restart: unless-stopped + volumes: + - ./volumes/logs/vector.yml:/etc/vector/vector.yml:ro + - ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock:ro + healthcheck: + test: + [ + "CMD", + "wget", + "--no-verbose", + "--tries=1", + "--spider", + "http://vector:9001/health" + ] + timeout: 5s + interval: 5s + retries: 3 + environment: + LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + command: + [ + "--config", + "/etc/vector/vector.yml" + ] + + # Update the DATABASE_URL if you are using an external Postgres database + supavisor: + container_name: supabase-pooler + image: supabase/supavisor:1.1.56 + restart: unless-stopped + ports: + - ${POSTGRES_PORT}:5432 + - ${POOLER_PROXY_PORT_TRANSACTION}:6543 + volumes: + - ./volumes/pooler/pooler.exs:/etc/pooler/pooler.exs:ro + healthcheck: + test: + [ + "CMD", + "curl", + "-sSfL", + "--head", + "-o", + "/dev/null", + "http://127.0.0.1:4000/api/health" + ] + interval: 10s + timeout: 5s + retries: 5 + depends_on: + db: + condition: service_healthy + analytics: + condition: service_healthy + environment: + PORT: 4000 + POSTGRES_PORT: ${POSTGRES_PORT} + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/_supabase + CLUSTER_POSTGRES: true + SECRET_KEY_BASE: ${SECRET_KEY_BASE} + VAULT_ENC_KEY: ${VAULT_ENC_KEY} + API_JWT_SECRET: ${JWT_SECRET} + METRICS_JWT_SECRET: ${JWT_SECRET} + REGION: local + ERL_AFLAGS: -proto_dist inet_tcp + POOLER_TENANT_ID: ${POOLER_TENANT_ID} + POOLER_DEFAULT_POOL_SIZE: ${POOLER_DEFAULT_POOL_SIZE} + POOLER_MAX_CLIENT_CONN: ${POOLER_MAX_CLIENT_CONN} + POOLER_POOL_MODE: transaction + command: + [ + "/bin/sh", + "-c", + "/app/bin/migrate && /app/bin/supavisor eval \"$$(cat /etc/pooler/pooler.exs)\" && /app/bin/server" + ] + + dashboard: + container_name: mensa-dashboard + image: registry.gitlab.com/brausjonas/mensaappdashboard + restart: unless-stopped + ports: + - 8051:8051 + +volumes: + db-config: diff --git a/supabase/docker/reset.sh b/supabase/docker/reset.sh new file mode 100755 index 0000000..d5f3a41 --- /dev/null +++ b/supabase/docker/reset.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +echo "WARNING: This will remove all containers and container data, and will reset the .env file. This action cannot be undone!" +read -p "Are you sure you want to proceed? (y/N) " -n 1 -r +echo # Move to a new line +if [[ ! $REPLY =~ ^[Yy]$ ]] +then + echo "Operation cancelled." + exit 1 +fi + +echo "Stopping and removing all containers..." +docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans + +echo "Cleaning up bind-mounted directories..." +BIND_MOUNTS=( + "./volumes/db/data" +) + +for DIR in "${BIND_MOUNTS[@]}"; do + if [ -d "$DIR" ]; then + echo "Deleting $DIR..." + rm -rf "$DIR" + else + echo "Directory $DIR does not exist. Skipping bind mount deletion step..." + fi +done + +echo "Resetting .env file..." +if [ -f ".env" ]; then + echo "Removing existing .env file..." + rm -f .env +else + echo "No .env file found. Skipping .env removal step..." +fi + +if [ -f ".env.example" ]; then + echo "Copying .env.example to .env..." + cp .env.example .env +else + echo ".env.example file not found. Skipping .env reset step..." +fi + +echo "Cleanup complete!" \ No newline at end of file diff --git a/supabase/docker/volumes/api/kong.yml b/supabase/docker/volumes/api/kong.yml new file mode 100644 index 0000000..7abf425 --- /dev/null +++ b/supabase/docker/volumes/api/kong.yml @@ -0,0 +1,241 @@ +_format_version: '2.1' +_transform: true + +### +### Consumers / Users +### +consumers: + - username: DASHBOARD + - username: anon + keyauth_credentials: + - key: $SUPABASE_ANON_KEY + - username: service_role + keyauth_credentials: + - key: $SUPABASE_SERVICE_KEY + +### +### Access Control List +### +acls: + - consumer: anon + group: anon + - consumer: service_role + group: admin + +### +### Dashboard credentials +### +basicauth_credentials: + - consumer: DASHBOARD + username: $DASHBOARD_USERNAME + password: $DASHBOARD_PASSWORD + +### +### API Routes +### +services: + ## Open Auth routes + - name: auth-v1-open + url: http://auth:9999/verify + routes: + - name: auth-v1-open + strip_path: true + paths: + - /auth/v1/verify + plugins: + - name: cors + - name: auth-v1-open-callback + url: http://auth:9999/callback + routes: + - name: auth-v1-open-callback + strip_path: true + paths: + - /auth/v1/callback + plugins: + - name: cors + - name: auth-v1-open-authorize + url: http://auth:9999/authorize + routes: + - name: auth-v1-open-authorize + strip_path: true + paths: + - /auth/v1/authorize + plugins: + - name: cors + + ## Secure Auth routes + - name: auth-v1 + _comment: 'GoTrue: /auth/v1/* -> http://auth:9999/*' + url: http://auth:9999/ + routes: + - name: auth-v1-all + strip_path: true + paths: + - /auth/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + + ## Secure REST routes + - name: rest-v1 + _comment: 'PostgREST: /rest/v1/* -> http://rest:3000/*' + url: http://rest:3000/ + routes: + - name: rest-v1-all + strip_path: true + paths: + - /rest/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: true + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + + ## Secure GraphQL routes + - name: graphql-v1 + _comment: 'PostgREST: /graphql/v1/* -> http://rest:3000/rpc/graphql' + url: http://rest:3000/rpc/graphql + routes: + - name: graphql-v1-all + strip_path: true + paths: + - /graphql/v1 + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: true + - name: request-transformer + config: + add: + headers: + - Content-Profile:graphql_public + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + + ## Secure Realtime routes + - name: realtime-v1-ws + _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' + url: http://realtime-dev.supabase-realtime:4000/socket + protocol: ws + routes: + - name: realtime-v1-ws + strip_path: true + paths: + - /realtime/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + - name: realtime-v1-rest + _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' + url: http://realtime-dev.supabase-realtime:4000/api + protocol: http + routes: + - name: realtime-v1-rest + strip_path: true + paths: + - /realtime/v1/api + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + ## Storage routes: the storage server manages its own auth + - name: storage-v1 + _comment: 'Storage: /storage/v1/* -> http://storage:5000/*' + url: http://storage:5000/ + routes: + - name: storage-v1-all + strip_path: true + paths: + - /storage/v1/ + plugins: + - name: cors + + ## Edge Functions routes + - name: functions-v1 + _comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*' + url: http://functions:9000/ + routes: + - name: functions-v1-all + strip_path: true + paths: + - /functions/v1/ + plugins: + - name: cors + + ## Analytics routes + - name: analytics-v1 + _comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*' + url: http://analytics:4000/ + routes: + - name: analytics-v1-all + strip_path: true + paths: + - /analytics/v1/ + + ## Secure Database routes + - name: meta + _comment: 'pg-meta: /pg/* -> http://pg-meta:8080/*' + url: http://meta:8080/ + routes: + - name: meta-all + strip_path: true + paths: + - /pg/ + plugins: + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + + ## Protected Dashboard - catch all remaining routes + - name: dashboard + _comment: 'Studio: /* -> http://studio:3000/*' + url: http://studio:3000/ + routes: + - name: dashboard-all + strip_path: true + paths: + - / + plugins: + - name: cors + - name: basic-auth + config: + hide_credentials: true diff --git a/supabase/docker/volumes/db/_supabase.sql b/supabase/docker/volumes/db/_supabase.sql new file mode 100644 index 0000000..6236ae1 --- /dev/null +++ b/supabase/docker/volumes/db/_supabase.sql @@ -0,0 +1,3 @@ +\set pguser `echo "$POSTGRES_USER"` + +CREATE DATABASE _supabase WITH OWNER :pguser; diff --git a/supabase/docker/volumes/db/init/data.sql b/supabase/docker/volumes/db/init/data.sql new file mode 100755 index 0000000..e69de29 diff --git a/supabase/docker/volumes/db/jwt.sql b/supabase/docker/volumes/db/jwt.sql new file mode 100644 index 0000000..cfd3b16 --- /dev/null +++ b/supabase/docker/volumes/db/jwt.sql @@ -0,0 +1,5 @@ +\set jwt_secret `echo "$JWT_SECRET"` +\set jwt_exp `echo "$JWT_EXP"` + +ALTER DATABASE postgres SET "app.settings.jwt_secret" TO :'jwt_secret'; +ALTER DATABASE postgres SET "app.settings.jwt_exp" TO :'jwt_exp'; diff --git a/supabase/docker/volumes/db/logs.sql b/supabase/docker/volumes/db/logs.sql new file mode 100644 index 0000000..255c0f4 --- /dev/null +++ b/supabase/docker/volumes/db/logs.sql @@ -0,0 +1,6 @@ +\set pguser `echo "$POSTGRES_USER"` + +\c _supabase +create schema if not exists _analytics; +alter schema _analytics owner to :pguser; +\c postgres diff --git a/supabase/docker/volumes/db/pooler.sql b/supabase/docker/volumes/db/pooler.sql new file mode 100644 index 0000000..162c5b9 --- /dev/null +++ b/supabase/docker/volumes/db/pooler.sql @@ -0,0 +1,6 @@ +\set pguser `echo "$POSTGRES_USER"` + +\c _supabase +create schema if not exists _supavisor; +alter schema _supavisor owner to :pguser; +\c postgres diff --git a/supabase/docker/volumes/db/realtime.sql b/supabase/docker/volumes/db/realtime.sql new file mode 100644 index 0000000..4d4b9ff --- /dev/null +++ b/supabase/docker/volumes/db/realtime.sql @@ -0,0 +1,4 @@ +\set pguser `echo "$POSTGRES_USER"` + +create schema if not exists _realtime; +alter schema _realtime owner to :pguser; diff --git a/supabase/docker/volumes/db/roles.sql b/supabase/docker/volumes/db/roles.sql new file mode 100644 index 0000000..8f7161a --- /dev/null +++ b/supabase/docker/volumes/db/roles.sql @@ -0,0 +1,8 @@ +-- NOTE: change to your own passwords for production environments +\set pgpass `echo "$POSTGRES_PASSWORD"` + +ALTER USER authenticator WITH PASSWORD :'pgpass'; +ALTER USER pgbouncer WITH PASSWORD :'pgpass'; +ALTER USER supabase_auth_admin WITH PASSWORD :'pgpass'; +ALTER USER supabase_functions_admin WITH PASSWORD :'pgpass'; +ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass'; diff --git a/supabase/docker/volumes/db/webhooks.sql b/supabase/docker/volumes/db/webhooks.sql new file mode 100644 index 0000000..5837b86 --- /dev/null +++ b/supabase/docker/volumes/db/webhooks.sql @@ -0,0 +1,208 @@ +BEGIN; + -- Create pg_net extension + CREATE EXTENSION IF NOT EXISTS pg_net SCHEMA extensions; + -- Create supabase_functions schema + CREATE SCHEMA supabase_functions AUTHORIZATION supabase_admin; + GRANT USAGE ON SCHEMA supabase_functions TO postgres, anon, authenticated, service_role; + ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON TABLES TO postgres, anon, authenticated, service_role; + ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO postgres, anon, authenticated, service_role; + ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO postgres, anon, authenticated, service_role; + -- supabase_functions.migrations definition + CREATE TABLE supabase_functions.migrations ( + version text PRIMARY KEY, + inserted_at timestamptz NOT NULL DEFAULT NOW() + ); + -- Initial supabase_functions migration + INSERT INTO supabase_functions.migrations (version) VALUES ('initial'); + -- supabase_functions.hooks definition + CREATE TABLE supabase_functions.hooks ( + id bigserial PRIMARY KEY, + hook_table_id integer NOT NULL, + hook_name text NOT NULL, + created_at timestamptz NOT NULL DEFAULT NOW(), + request_id bigint + ); + CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); + CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); + COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.'; + CREATE FUNCTION supabase_functions.http_request() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + DECLARE + request_id bigint; + payload jsonb; + url text := TG_ARGV[0]::text; + method text := TG_ARGV[1]::text; + headers jsonb DEFAULT '{}'::jsonb; + params jsonb DEFAULT '{}'::jsonb; + timeout_ms integer DEFAULT 1000; + BEGIN + IF url IS NULL OR url = 'null' THEN + RAISE EXCEPTION 'url argument is missing'; + END IF; + + IF method IS NULL OR method = 'null' THEN + RAISE EXCEPTION 'method argument is missing'; + END IF; + + IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN + headers = '{"Content-Type": "application/json"}'::jsonb; + ELSE + headers = TG_ARGV[2]::jsonb; + END IF; + + IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN + params = '{}'::jsonb; + ELSE + params = TG_ARGV[3]::jsonb; + END IF; + + IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN + timeout_ms = 1000; + ELSE + timeout_ms = TG_ARGV[4]::integer; + END IF; + + CASE + WHEN method = 'GET' THEN + SELECT http_get INTO request_id FROM net.http_get( + url, + params, + headers, + timeout_ms + ); + WHEN method = 'POST' THEN + payload = jsonb_build_object( + 'old_record', OLD, + 'record', NEW, + 'type', TG_OP, + 'table', TG_TABLE_NAME, + 'schema', TG_TABLE_SCHEMA + ); + + SELECT http_post INTO request_id FROM net.http_post( + url, + payload, + params, + headers, + timeout_ms + ); + ELSE + RAISE EXCEPTION 'method argument % is invalid', method; + END CASE; + + INSERT INTO supabase_functions.hooks + (hook_table_id, hook_name, request_id) + VALUES + (TG_RELID, TG_NAME, request_id); + + RETURN NEW; + END + $function$; + -- Supabase super admin + DO + $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM pg_roles + WHERE rolname = 'supabase_functions_admin' + ) + THEN + CREATE USER supabase_functions_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION; + END IF; + END + $$; + GRANT ALL PRIVILEGES ON SCHEMA supabase_functions TO supabase_functions_admin; + GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA supabase_functions TO supabase_functions_admin; + GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA supabase_functions TO supabase_functions_admin; + ALTER USER supabase_functions_admin SET search_path = "supabase_functions"; + ALTER table "supabase_functions".migrations OWNER TO supabase_functions_admin; + ALTER table "supabase_functions".hooks OWNER TO supabase_functions_admin; + ALTER function "supabase_functions".http_request() OWNER TO supabase_functions_admin; + GRANT supabase_functions_admin TO postgres; + -- Remove unused supabase_pg_net_admin role + DO + $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_roles + WHERE rolname = 'supabase_pg_net_admin' + ) + THEN + REASSIGN OWNED BY supabase_pg_net_admin TO supabase_admin; + DROP OWNED BY supabase_pg_net_admin; + DROP ROLE supabase_pg_net_admin; + END IF; + END + $$; + -- pg_net grants when extension is already enabled + DO + $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_extension + WHERE extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; + END + $$; + -- Event trigger for pg_net + CREATE OR REPLACE FUNCTION extensions.grant_pg_net_access() + RETURNS event_trigger + LANGUAGE plpgsql + AS $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; + END; + $$; + COMMENT ON FUNCTION extensions.grant_pg_net_access IS 'Grants access to pg_net'; + DO + $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM pg_event_trigger + WHERE evtname = 'issue_pg_net_access' + ) THEN + CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end WHEN TAG IN ('CREATE EXTENSION') + EXECUTE PROCEDURE extensions.grant_pg_net_access(); + END IF; + END + $$; + INSERT INTO supabase_functions.migrations (version) VALUES ('20210809183423_update_grants'); + ALTER function supabase_functions.http_request() SECURITY DEFINER; + ALTER function supabase_functions.http_request() SET search_path = supabase_functions; + REVOKE ALL ON FUNCTION supabase_functions.http_request() FROM PUBLIC; + GRANT EXECUTE ON FUNCTION supabase_functions.http_request() TO postgres, anon, authenticated, service_role; +COMMIT; diff --git a/supabase/docker/volumes/functions/hello/index.ts b/supabase/docker/volumes/functions/hello/index.ts new file mode 100644 index 0000000..f1e20b9 --- /dev/null +++ b/supabase/docker/volumes/functions/hello/index.ts @@ -0,0 +1,16 @@ +// Follow this setup guide to integrate the Deno language server with your editor: +// 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.1/http/server.ts" + +serve(async () => { + return new Response( + `"Hello from Edge Functions!"`, + { headers: { "Content-Type": "application/json" } }, + ) +}) + +// To invoke: +// curl 'http://localhost:/functions/v1/hello' \ +// --header 'Authorization: Bearer ' diff --git a/supabase/docker/volumes/functions/main/index.ts b/supabase/docker/volumes/functions/main/index.ts new file mode 100644 index 0000000..a094010 --- /dev/null +++ b/supabase/docker/volumes/functions/main/index.ts @@ -0,0 +1,94 @@ +import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' +import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' + +console.log('main function started') + +const JWT_SECRET = Deno.env.get('JWT_SECRET') +const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true' + +function getAuthToken(req: Request) { + const authHeader = req.headers.get('authorization') + if (!authHeader) { + throw new Error('Missing authorization header') + } + const [bearer, token] = authHeader.split(' ') + if (bearer !== 'Bearer') { + throw new Error(`Auth header is not 'Bearer {token}'`) + } + return token +} + +async function verifyJWT(jwt: string): Promise { + const encoder = new TextEncoder() + const secretKey = encoder.encode(JWT_SECRET) + try { + await jose.jwtVerify(jwt, secretKey) + } catch (err) { + console.error(err) + return false + } + return true +} + +serve(async (req: Request) => { + if (req.method !== 'OPTIONS' && VERIFY_JWT) { + try { + const token = getAuthToken(req) + const isValidJWT = await verifyJWT(token) + + if (!isValidJWT) { + return new Response(JSON.stringify({ msg: 'Invalid JWT' }), { + status: 401, + headers: { 'Content-Type': 'application/json' }, + }) + } + } catch (e) { + console.error(e) + return new Response(JSON.stringify({ msg: e.toString() }), { + status: 401, + headers: { 'Content-Type': 'application/json' }, + }) + } + } + + const url = new URL(req.url) + const { pathname } = url + const path_parts = pathname.split('/') + const service_name = path_parts[1] + + if (!service_name || service_name === '') { + const error = { msg: 'missing function name in request' } + return new Response(JSON.stringify(error), { + status: 400, + headers: { 'Content-Type': 'application/json' }, + }) + } + + const servicePath = `/home/deno/functions/${service_name}` + console.error(`serving the request with ${servicePath}`) + + const memoryLimitMb = 150 + const workerTimeoutMs = 1 * 60 * 1000 + const noModuleCache = false + const importMapPath = null + const envVarsObj = Deno.env.toObject() + const envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]]) + + try { + const worker = await EdgeRuntime.userWorkers.create({ + servicePath, + memoryLimitMb, + workerTimeoutMs, + noModuleCache, + importMapPath, + envVars, + }) + return await worker.fetch(req) + } catch (e) { + const error = { msg: e.toString() } + return new Response(JSON.stringify(error), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }) + } +}) diff --git a/supabase/docker/volumes/logs/vector.yml b/supabase/docker/volumes/logs/vector.yml new file mode 100644 index 0000000..cce46df --- /dev/null +++ b/supabase/docker/volumes/logs/vector.yml @@ -0,0 +1,232 @@ +api: + enabled: true + address: 0.0.0.0:9001 + +sources: + docker_host: + type: docker_logs + exclude_containers: + - supabase-vector + +transforms: + project_logs: + type: remap + inputs: + - docker_host + source: |- + .project = "default" + .event_message = del(.message) + .appname = del(.container_name) + del(.container_created_at) + del(.container_id) + del(.source_type) + del(.stream) + del(.label) + del(.image) + del(.host) + del(.stream) + router: + type: route + inputs: + - project_logs + route: + kong: '.appname == "supabase-kong"' + auth: '.appname == "supabase-auth"' + rest: '.appname == "supabase-rest"' + realtime: '.appname == "supabase-realtime"' + storage: '.appname == "supabase-storage"' + functions: '.appname == "supabase-functions"' + db: '.appname == "supabase-db"' + # Ignores non nginx errors since they are related with kong booting up + kong_logs: + type: remap + inputs: + - router.kong + source: |- + req, err = parse_nginx_log(.event_message, "combined") + if err == null { + .timestamp = req.timestamp + .metadata.request.headers.referer = req.referer + .metadata.request.headers.user_agent = req.agent + .metadata.request.headers.cf_connecting_ip = req.client + .metadata.request.method = req.method + .metadata.request.path = req.path + .metadata.request.protocol = req.protocol + .metadata.response.status_code = req.status + } + if err != null { + abort + } + # Ignores non nginx errors since they are related with kong booting up + kong_err: + type: remap + inputs: + - router.kong + source: |- + .metadata.request.method = "GET" + .metadata.response.status_code = 200 + parsed, err = parse_nginx_log(.event_message, "error") + if err == null { + .timestamp = parsed.timestamp + .severity = parsed.severity + .metadata.request.host = parsed.host + .metadata.request.headers.cf_connecting_ip = parsed.client + url, err = split(parsed.request, " ") + if err == null { + .metadata.request.method = url[0] + .metadata.request.path = url[1] + .metadata.request.protocol = url[2] + } + } + if err != null { + abort + } + # Gotrue logs are structured json strings which frontend parses directly. But we keep metadata for consistency. + auth_logs: + type: remap + inputs: + - router.auth + source: |- + parsed, err = parse_json(.event_message) + if err == null { + .metadata.timestamp = parsed.time + .metadata = merge!(.metadata, parsed) + } + # PostgREST logs are structured so we separate timestamp from message using regex + rest_logs: + type: remap + inputs: + - router.rest + source: |- + parsed, err = parse_regex(.event_message, r'^(?P