2
0

🧑‍💻 Improve env variables type safety and management (#718)

Closes #679
This commit is contained in:
Baptiste Arnaud
2023-08-28 09:13:53 +02:00
committed by GitHub
parent a23a8c4456
commit 786e5cb582
148 changed files with 1550 additions and 1293 deletions

View File

@@ -1,64 +0,0 @@
---
sidebar_position: 3
---
import { SponsorButton } from '../../src/js/SponsorButton.jsx'
# Cloudron
:::note
The easiest way to get started with Typebot is with [the official managed service in the Cloud](https://app.typebot.io). You'll have high availability, backups, security, and maintenance all managed for you by me, Baptiste, Typebot's founder.
The cloud version can save a substantial amount of developer time and resources. For most sites this ends up being the best value option and the revenue goes to funding the maintenance and further development of Typebot. So youll be supporting open source software and getting a great service!
:::
## Requirements
You need a server with [Cloudron](https://www.cloudron.io/) installed and a machine with the [Cloudron CLI](https://docs.cloudron.io/packaging/cli/) installed.
## Installation
### 1. Download the compose file
On the machine that has the `cloudron` CLI, download the latest `CloudronManifest.json` file:
```sh
wget https://raw.githubusercontent.com/baptisteArno/typebot.io/latest/packages/cloudron/CloudronManifest.json
```
### 2. Install the app
Install the app:
```sh
cloudron install --image baptistearno/typebot-cloudron:latest
```
## Configuration
You can further configure the app by opening the app File Manager in Cloudron and edit the `env.sh` file.
There, you can add any environment variable you want, like:
```sh
export GITHUB_CLIENT_ID="your_github_client_id"
export GITHUB_CLIENT_SECRET="your_github_client_secret"
```
Then restart the app to apply the changes.
## Update
To update the app, run:
```sh
cloudron update --app the_name_of_your_app --image baptistearno/typebot-cloudron:latest
```
:::note
If you're self-hosting Typebot, [sponsoring me](https://github.com/sponsors/baptisteArno) is a great way to give back to the community and to contribute to the long-term sustainability of the project.
<SponsorButton />
Thank you for supporting independent creators of Free Open Source Software!
:::

View File

@@ -1,11 +1,11 @@
---
title: Builder
sidebar_position: 2
---
import { SponsorButton } from '../../../src/js/SponsorButton.jsx'
import { Asterix } from '../../../src/js/Asterix.jsx'
import { SponsorButton } from '../../src/js/SponsorButton.jsx'
import { Asterix } from '../../src/js/Asterix.jsx'
# Builder configuration
# Configuration
Parameters marked with <Asterix/> are required.
@@ -22,6 +22,7 @@ Parameters marked with <Asterix/> are required.
| DEFAULT_WORKSPACE_PLAN | FREE | Default workspace plan on user creation or when a user creates a new workspace. Possible values are `FREE`, `STARTER`, `PRO`, `LIFETIME`, `UNLIMITED`. The default plan for admin user is `UNLIMITED` |
| DISABLE_SIGNUP | false | Disable new user sign ups. Invited users are still able to sign up. |
| NEXT_PUBLIC_ONBOARDING_TYPEBOT_ID | | Typebot ID used for the onboarding. Onboarding page is skipped if not provided. |
| DEBUG | false | If enabled, the server will print valuable logs to debug config issues. |
## Email (Auth, notifications)

View File

@@ -1,4 +0,0 @@
{
"label": "Configuration",
"position": 2
}

View File

@@ -1,25 +0,0 @@
---
sidebar_position: 1
title: Overview
slug: /self-hosting/configuration
---
# Configuration
:::note
The easiest way to get started with Typebot is with [the official managed service in the Cloud](https://app.typebot.io). You'll have high availability, backups, security, and maintenance all managed for you by me, Baptiste, Typebot's founder.
The cloud version can save a substantial amount of developer time and resources. For most sites this ends up being the best value option and the revenue goes to funding the maintenance and further development of Typebot. So youll be supporting open source software and getting a great service!
:::
When running Typebot on your machine, the following configuration parameters can be supplied as environment variables.
Typebot is composed of 2 main applications:
- The builder, where you build your typebots
- The viewer, where your user answer the typebot
Both apps have their environment to configure properly:
- [Builder configuration](/self-hosting/configuration/builder)
- [Viewer configuration](/self-hosting/configuration/viewer)

View File

@@ -1,105 +0,0 @@
---
title: Viewer
---
import { SponsorButton } from '../../../src/js/SponsorButton.jsx'
import { Asterix } from '../../../src/js/Asterix.jsx'
# Viewer configuration
Parameters marked with <Asterix/> are required.
## General
| Parameter | Default | Description |
| --------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| DATABASE_URL <Asterix/> | | The database URL |
| ENCRYPTION_SECRET <Asterix/> | | A 256-bit key used to encrypt sensitive data. It is strongly recommended to [generate](https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx) a new one. The secret should be the same between builder and viewer. |
| NEXT_PUBLIC_VIEWER_URL <Asterix/> | | The viewer base URL. Should be the publicly accessible URL (i.e. `https://bot.domain.com`) |
| NEXTAUTH_URL <Asterix/> | | The builder base URL. Should be the publicly accessible URL (i.e. `https://typebot.domain.com`) |
| DEBUG | false | If enabled, the server will print valuable logs to debug config issues. |
## Emails (Notifications)
Used for sending email notifications and authentication
| Parameter | Default | Description |
| ------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SMTP_USERNAME | | SMTP username |
| SMTP_PASSWORD | | SMTP password |
| SMTP_HOST | | SMTP host. (i.e. `smtp.host.com`) |
| SMTP_PORT | 25 | SMTP port |
| SMTP_FROM | | From name and email (i.e. `'Typebot Notifications' <notifications@typebot.io>`) |
| SMTP_SECURE | false | If true the connection will use TLS when connecting to server. If false (the default) then TLS is used if server supports the STARTTLS extension. In most cases set this value to true if you are connecting to port 465. For port 587 or 25 keep it false |
## Google (Sheets)
Used when executing a Google Sheets block. Make sure to set the required scopes (`userinfo.email`, `spreadsheets`, `drive.readonly`) in your console
| Parameter | Default | Description |
| -------------------- | ------- | --------------------------------------------- |
| GOOGLE_CLIENT_ID | | The Client ID from the Google API Console |
| GOOGLE_CLIENT_SECRET | | The Client secret from the Google API Console |
### Configuration
https://console.developers.google.com/apis/credentials
The "Authorized redirect URIs" used when creating the credentials must include your full domain and end in the callback path:
- For production: https://{YOUR_DOMAIN}/api/credentials/google-sheets/callback
- For development: http://localhost:3000/api/credentials/google-sheets/callback
## S3 Storage (File upload input)
Used for the file upload input. It can be any S3 compatible object storage service (Minio, Digital Oceans Space, AWS S3...)
| Parameter | Default | Description |
| ------------- | ------- | -------------------------------------------------------------- |
| S3_ACCESS_KEY | | S3 access key. Also used to check if upload feature is enabled |
| S3_SECRET_KEY | | S3 secret key. |
| S3_BUCKET | typebot | Name of the bucket where assets will be uploaded in. |
| S3_PORT | | S3 Host port number |
| S3_ENDPOINT | | S3 endpoint (i.e. `s3.domain.com`). |
| S3_SSL | true | Use SSL when establishing the connection. |
| S3_REGION | | S3 region. |
Note that for AWS S3, your endpoint is usually: `s3.<S3_REGION>.amazonaws.com`
Your bucket must have the following policy that tells S3 to allow public read when an object is located under the public folder:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<BUCKET_NAME>/public/*"
}
]
}
```
You also need to configure CORS so that an object can be uploaded from the browser:
```json
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["PUT", "POST"],
"AllowedOrigins": ["*"],
"ExposeHeaders": ["ETag"]
}
]
```
:::note
If you're self-hosting Typebot, [sponsoring me](https://github.com/sponsors/baptisteArno) is a great way to give back to the community and to contribute to the long-term sustainability of the project.
<SponsorButton />
Thank you for supporting independent creators of Free Open Source Software!
:::

View File

@@ -73,6 +73,6 @@ Typebot is composed of 2 Next.js applications you need to deploy:
I've written guides on how to deploy Typebot using:
- [Docker](/self-hosting/docker)
- [Vercel](/self-hosting/vercel)
- [Manual](/self-hosting/manual)
- [Docker](/self-hosting/guides/docker)
- [Vercel](/self-hosting/guides/vercel)
- [Manual](/self-hosting/guides/manual)

View File

@@ -0,0 +1,4 @@
{
"label": "Guides",
"position": 3
}

View File

@@ -1,8 +1,8 @@
---
sidebar_position: 2
sidebar_position: 3
---
import { SponsorButton } from '../../src/js/SponsorButton.jsx'
import { SponsorButton } from '../../../src/js/SponsorButton.jsx'
# Docker
@@ -20,27 +20,23 @@ You need a server with Docker installed. If your server doesn't come with Docker
### 1. Download the compose file
On your server, download the latest `docker-compose.yml` file:
On your server, download the latest `docker-compose.yml` and the starter `.env` file:
```sh
wget https://raw.githubusercontent.com/baptisteArno/typebot.io/latest/docker-compose.yml
wget https://raw.githubusercontent.com/baptisteArno/typebot.io/latest/.env.example -O .env
```
### 2. Add the required configuration
The compose file has placeholders for the required parameters. To set the parameters you'll first need a random 32-character secret key which will be used to encrypt sensitive data. Here is a simple way to generate one:
1. You'll first need a random 32-character secret key which will be used to encrypt sensitive data. Here is a simple way to generate one:
```sh
openssl rand -base64 24 | tr -d '\n' ; echo
```
```sh
openssl rand -base64 24 | tr -d '\n' ; echo
```
Now edit `docker-compose.yml` and:
- Replace `<your-encryption-secret>` with the generated secret.
- Replace `<your-builder-url>` with the public URL of the builder (i.e. `https://typebot.domain.com:8080`).
- Replace `<your-viewer-url>` with the public URL of the viewer (i.e. `https://typebot.domain.com:8081`).
- Replace `<your-admin-email>` with the email address of the administrator.
- Configure at least one authentication provider (Email, Google, GitHub, Facebook or GitLab). More info here: [Configuration](https://docs.typebot.io/self-hosting/configuration).
2. Fill the `.env` file with your values.
3. Configure at least one authentication provider (Email, Google, GitHub, Facebook or GitLab). More info here: [Configuration](https://docs.typebot.io/self-hosting/configuration).
By default the compose file will pull the latest stable Typebot images: `baptistearno/typebot-builder:latest` and `baptistearno/typebot-viewer:latest`. You can decide to replace `latest` with a specific version or with `main` to get the latest modifications. You can find all the existing tags [here](https://hub.docker.com/r/baptistearno/typebot-builder/tags)
@@ -52,12 +48,13 @@ Once you've added your configuration to the compose file, you're ready to start
docker-compose up -d
```
When you run this command it does the following:
When you run this command, by default, it does the following:
- Create a database
- Run the migrations
- Start the builder on port 8080
- Start the viewer on port 8081
- All Typebot's data is stored in the `.typebot` folder in the current directory
You can now navigate to `http://typebot.domain.com:8080` and see the login screen. Login with the admin email to have access to a Team plan workspace automatically.
@@ -67,14 +64,33 @@ Typebot server itself does not perform SSL termination. It only runs on unencryp
Typebot is updated regularly, but it is up to you to apply these updates on your server. By virtue of using Docker, these updates are safe and easy to apply.
```sh
docker-compose down --remove-orphans
docker-compose pull typebot-builder
docker-compose pull typebot-viewer
docker-compose up -d
```
1. Pull the new images:
The self-hosted version is somewhat of a LTS, only getting the changes after they have been battle tested on the hosted version. If you want features as soon as they are available, consider becoming a hosted customer.
```sh
docker-compose pull typebot-builder
docker-compose pull typebot-viewer
```
Alternatively, you can pull specific versions:
```sh
docker-compose pull typebot-builder:1.0.0
docker-compose pull typebot-viewer:1.0.0
```
2. Stop the server:
```sh
docker-compose down
```
3. Start the server (with the new images):
```sh
docker-compose up -d
```
The self-hosted version is somewhat of a LTS, only getting the changes (~ once per month) after they have been battle tested on the cloud version. If you want features as soon as they are available, consider becoming a [cloud user](https://app.typebot.io).
## Optional extras
@@ -99,7 +115,7 @@ services:
restart: always
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- caddy-certificates:/data/caddy
- {$PWD}/.typebot/caddy-certificates:/data/caddy
ports:
- '80:80'
- '443:443'
@@ -118,10 +134,6 @@ services:
virtual.host: 'bot.domain.com' # change to your domain name
virtual.port: '3000'
virtual.tls-email: 'admin@example.com' # change to your email
volumes:
caddy-certificates:
driver: local
```
This config requires you to add the following DNS entry:
@@ -147,7 +159,7 @@ If you're already running a reverse proxy, the most important things to note are
### SMTP
I highly recommend using an external SMTP service. There are tons of options out there, including [SendInBlue](https://www.sendinblue.com/), [Mailgun](https://www.mailgun.com/) and [SendGrid](https://sendgrid.com/). It will avoid severe headaches 😅. Then, you will only need to add the required [SMTP configuration variables](/self-hosting/configuration/builder#email-auth-notifications).
I highly recommend using an external SMTP service. There are tons of options out there, including [SendInBlue](https://www.sendinblue.com/), [Mailgun](https://www.mailgun.com/) and [SendGrid](https://sendgrid.com/). It will avoid severe headaches 😅. Then, you will only need to add the required [SMTP configuration variables](/self-hosting/configuration#email-auth-notifications).
If, however, you don't want to, you can instantiate an SMTP server in the docker-compose file.
@@ -157,13 +169,13 @@ services:
mail:
image: bytemark/smtp
restart: always
typebot-builder:
environment:
- SMTP_HOST=mail
- NEXT_PUBLIC_SMTP_FROM=notifications@typebot.domain.com # change to your domain name
typebot-viewer:
- SMTP_HOST=mail
- NEXT_PUBLIC_SMTP_FROM=notifications@typebot.domain.com # change to your domain name
```
And add the following variables to your `.env` file:
```
SMTP_HOST=mail
NEXT_PUBLIC_SMTP_FROM=notifications@typebot.domain.com
```
You will probably need to make sure that `typebot.domain.com` has a valid SPF record and that your server IP has a rDNS set up.
@@ -186,7 +198,7 @@ services:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio123
volumes:
- s3_data:/data
- ${PWD}/.typebot/s3:/data
# This service just makes sure a bucket with the right policies is created
createbuckets:
image: minio/mc
@@ -200,21 +212,15 @@ services:
/usr/bin/mc anonymous set public minio/typebot/public;
exit 0;
"
typebot-builder:
environment:
- S3_ACCESS_KEY=minio
- S3_SECRET_KEY=minio123
- S3_BUCKET=typebot
- S3_ENDPOINT=storage.domain.com # change to your domain name
typebot-viewer:
environment:
- S3_ACCESS_KEY=minio
- S3_SECRET_KEY=minio123
- S3_BUCKET=typebot
- S3_ENDPOINT=storage.domain.com # change to your domain name
```
volumes:
s3_data:
And add the following variables to your `.env` file:
```
S3_ACCESS_KEY=minio
S3_SECRET_KEY=minio123
S3_BUCKET=typebot
S3_ENDPOINT=storage.domain.com
```
This config requires you to add the following DNS entry:
@@ -237,7 +243,7 @@ services:
restart: always
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- caddy-certificates:/data/caddy
- {$PWD}/.typebot/caddy-certificates:/data/caddy
ports:
- '80:80'
- '443:443'
@@ -248,7 +254,7 @@ services:
image: postgres:13
restart: always
volumes:
- db_data:/var/lib/postgresql/data
- {$PWD}/.typebot/database:/var/lib/postgresql/data
environment:
- POSTGRES_DB=typebot
- POSTGRES_PASSWORD=typebot
@@ -264,18 +270,8 @@ services:
extra_hosts:
- 'host.docker.internal:host-gateway'
# See https://docs.typebot.io/self-hosting/configuration for more configuration options
environment:
- DATABASE_URL=postgresql://postgres:typebot@typebot-db:5432/typebot
- NEXTAUTH_URL=https://typebot.domain.com
- NEXT_PUBLIC_VIEWER_URL=https://bot.domain.com
- ENCRYPTION_SECRET=K+Bar660Ofaec7v1jHC25tAn3l2b7c81
- ADMIN_EMAIL=baptiste.arnaud95@gmail.com
- SMTP_HOST=mail
- NEXT_PUBLIC_SMTP_FROM=notifications@typebot.domain.com
- S3_ACCESS_KEY=minio
- S3_SECRET_KEY=minio123
- S3_BUCKET=typebot
- S3_ENDPOINT=storage.domain.com
env_file:
- .env
typebot-viewer:
labels:
virtual.host: 'bot.domain.com' # change to your domain
@@ -284,16 +280,8 @@ services:
image: baptistearno/typebot-viewer:latest
restart: always
# See https://docs.typebot.io/self-hosting/configuration for more configuration options
environment:
- DATABASE_URL=postgresql://postgres:typebot@typebot-db:5432/typebot
- NEXT_PUBLIC_VIEWER_URL=https://bot.domain.com
- ENCRYPTION_SECRET=K+Bar660Ofaec7v1jHC25tAn3l2b7c81
- SMTP_HOST=mail
- NEXT_PUBLIC_SMTP_FROM=notifications@typebot.domain.com
- S3_ACCESS_KEY=minio
- S3_SECRET_KEY=minio123
- S3_BUCKET=typebot
- S3_ENDPOINT=storage.domain.com
env_file:
- .env
mail:
image: bytemark/smtp
restart: always
@@ -310,7 +298,7 @@ services:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio123
volumes:
- s3_data:/data
- {$PWD}/.typebot/s3:/data
# This service just make sure a bucket with the right policies is created
createbuckets:
image: minio/mc
@@ -324,11 +312,6 @@ services:
/usr/bin/mc anonymous set public minio/typebot/public;
exit 0;
"
volumes:
db_data:
s3_data:
caddy-certificates:
driver: local
```
:::note

View File

@@ -18,22 +18,18 @@ The cloud version can save a substantial amount of developer time and resources.
## Getting Started
1. Fork/clone the repository
1. Fork/clone the repository and checkout the latest stable version.
```sh
git clone git@github.com:<username>/typebot.io.git
cd typebot.io
git checkout latest
```
2. Setup environment variables by copying the example files and following the [configuration guide](/self-hosting/configuration) to fill in the missing values.
```sh
cd typebot.io
# check out the latest stable version or the one you want to use
git checkout latest
# copy the example env file
cp packages/prisma/.env.example packages/prisma/.env
cp apps/builder/.env.local.example apps/builder/.env.local
cp apps/viewer/.env.local.example apps/viewer/.env.local
cp .env.example .env
```
:::note
@@ -50,9 +46,6 @@ pnpm install
```sh
pnpm run build:apps
# or build them separately
pnpm run build:builder
pnpm run build:viewer
```
:::note

View File

@@ -1,10 +1,10 @@
# Using a Planetscale database
Typebot is also pluggable to a Planetscale database. But it means, you'll need to push schema changes manually, yourself.
Typebot is also pluggable to a Planetscale database. But it means, you'll need to push schema changes manually.
To do so, follow these instructions:
1. Copy `packages/prisma/.env.example` to `packages/prisma/.env` and replace `DATABASE_URL` with a development branch
1. Replace `DATABASE_URL` with a Planetscale development branch URL.
2. From the `packages/prisma` directory, run a the db push command: `pnpm run db:push`
3. Then, in Planetscale dashboard, or using their CLI, you can create a new deploy request from this development branch to your production branch.

View File

@@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 4
---
# Vercel
@@ -27,7 +27,7 @@ Fork the repository
5. Change the build command to:
```sh
cd ../.. && pnpm build:builder && pnpm db:migrate
cd ../.. && pnpm turbo build --filter=builder... && pnpm db:migrate
```
6. Add the required environment variables ([Check out the configuration guide](/self-hosting/configuration))
@@ -42,7 +42,7 @@ Fork the repository
5. Change the build command to:
```sh
cd ../.. && pnpm build:viewer && pnpm db:migrate
cd ../.. && pnpm pnpm turbo build --filter=viewer... && pnpm db:migrate
```
6. Add the required environment variables ([Check out the configuration guide](/self-hosting/configuration))

View File

@@ -6,4 +6,4 @@ You most likely forgot to set up an `ADMIN_EMAIL` variable or did not signed up
## I can't upload files
You need to add an [S3 configuration](./configuration/builder#s3-storage-media-uploads) to your project. If you are self-hosting with Docker, you can [add a S3 service to your docker-compose file](./docker#s3-storage).
You need to add an [S3 configuration](./configuration#s3-storage-media-uploads) to your project. If you are self-hosting with Docker, you can [add a S3 service to your docker-compose file](./guides/docker#s3-storage).

View File

@@ -2044,6 +2044,9 @@
},
"groupId": {
"type": "string"
},
"mergeResults": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -6326,6 +6329,9 @@
},
"groupId": {
"type": "string"
},
"mergeResults": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -10182,6 +10188,9 @@
},
"groupId": {
"type": "string"
},
"mergeResults": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -14173,6 +14182,9 @@
},
"groupId": {
"type": "string"
},
"mergeResults": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -18045,6 +18057,9 @@
},
"groupId": {
"type": "string"
},
"mergeResults": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -21971,6 +21986,9 @@
},
"groupId": {
"type": "string"
},
"mergeResults": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -25960,6 +25978,9 @@
},
"groupId": {
"type": "string"
},
"mergeResults": {
"type": "boolean"
}
},
"additionalProperties": false

View File

@@ -38,6 +38,15 @@
{
"type": "object",
"properties": {
"version": {
"type": "string",
"enum": [
"3",
"4",
"5"
],
"nullable": true
},
"id": {
"type": "string"
},
@@ -1621,6 +1630,9 @@
},
"groupId": {
"type": "string"
},
"mergeResults": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -3817,6 +3829,7 @@
}
},
"required": [
"version",
"id",
"groups",
"edges",

View File

@@ -13,7 +13,7 @@
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"update-search": "docker run -it --rm --env-file=.env -e \"CONFIG=$(cat docsearch-scrapper-config.json | jq -r tostring)\" algolia/docsearch-scraper",
"api:generate": "tsx --tsconfig ../builder/tsconfig.json ../builder/src/helpers/server/generateOpenApi.ts && tsx --tsconfig ../viewer/openapi.tsconfig.json ../viewer/src/helpers/server/generateOpenApi.ts"
"api:generate": "dotenv -e ./.env -e ../../.env -- tsx --tsconfig ../builder/tsconfig.json ../builder/src/helpers/server/generateOpenApi.ts && dotenv -e ./.env -e ../../.env -- tsx --tsconfig ../viewer/openapi.tsconfig.json ../viewer/src/helpers/server/generateOpenApi.ts"
},
"dependencies": {
"@docusaurus/core": "2.4.1",
@@ -46,6 +46,7 @@
"@algolia/client-search": "4.15.0",
"@docusaurus/types": "^2.3.1",
"@types/react": "18.0.28",
"dotenv-cli": "^7.2.1",
"tsx": "3.12.5",
"typescript": "4.9.5",
"webpack": "5.76.1"

12
apps/docs/vercel.json Normal file
View File

@@ -0,0 +1,12 @@
{
"rewrites": [
{
"source": "/self-hosting/configuration",
"destination": "/self-hosting/configuration"
},
{
"source": "/self-hosting/configuration/:path*",
"destination": "/self-hosting/configuration"
}
]
}