Compare commits
21 Commits
@nhost/rea
...
@nhost/rea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7a3136086 | ||
|
|
b5642586a4 | ||
|
|
cadc8f8864 | ||
|
|
1dc2bce05a | ||
|
|
65588268f6 | ||
|
|
a6b15bb387 | ||
|
|
7b8f64ab25 | ||
|
|
451b62d641 | ||
|
|
08a37aae7c | ||
|
|
679c32cb5a | ||
|
|
25bc2bee67 | ||
|
|
a05d7585a3 | ||
|
|
89f823fdce | ||
|
|
6f4d465f54 | ||
|
|
aafbaa8d25 | ||
|
|
0f7b31497f | ||
|
|
1965fc85d6 | ||
|
|
a8a7c32ec1 | ||
|
|
e8232cdfbb | ||
|
|
066489e3d4 | ||
|
|
de10d84cc1 |
26
README.md
26
README.md
@@ -264,6 +264,13 @@ Here are some ways of contributing to making Nhost better:
|
|||||||
<sub><b>Mustafa Hanif</b></sub>
|
<sub><b>Mustafa Hanif</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/nbourdin">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/5602476?v=4" width="100;" alt="nbourdin"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Nicolas Bourdin</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/timpratim">
|
<a href="https://github.com/timpratim">
|
||||||
<img src="https://avatars.githubusercontent.com/u/32492961?v=4" width="100;" alt="timpratim"/>
|
<img src="https://avatars.githubusercontent.com/u/32492961?v=4" width="100;" alt="timpratim"/>
|
||||||
@@ -291,15 +298,15 @@ Here are some ways of contributing to making Nhost better:
|
|||||||
<br />
|
<br />
|
||||||
<sub><b>Anders Kjær Damgaard</b></sub>
|
<sub><b>Anders Kjær Damgaard</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td></tr>
|
||||||
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/Sonichigo">
|
<a href="https://github.com/Sonichigo">
|
||||||
<img src="https://avatars.githubusercontent.com/u/53110238?v=4" width="100;" alt="Sonichigo"/>
|
<img src="https://avatars.githubusercontent.com/u/53110238?v=4" width="100;" alt="Sonichigo"/>
|
||||||
<br />
|
<br />
|
||||||
<sub><b>Animesh Pathak</b></sub>
|
<sub><b>Animesh Pathak</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td></tr>
|
</td>
|
||||||
<tr>
|
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/rustyb">
|
<a href="https://github.com/rustyb">
|
||||||
<img src="https://avatars.githubusercontent.com/u/53086?v=4" width="100;" alt="rustyb"/>
|
<img src="https://avatars.githubusercontent.com/u/53086?v=4" width="100;" alt="rustyb"/>
|
||||||
@@ -334,15 +341,15 @@ Here are some ways of contributing to making Nhost better:
|
|||||||
<br />
|
<br />
|
||||||
<sub><b>Hoang Do</b></sub>
|
<sub><b>Hoang Do</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td></tr>
|
||||||
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/MelodicCrypter">
|
<a href="https://github.com/MelodicCrypter">
|
||||||
<img src="https://avatars.githubusercontent.com/u/18341500?v=4" width="100;" alt="MelodicCrypter"/>
|
<img src="https://avatars.githubusercontent.com/u/18341500?v=4" width="100;" alt="MelodicCrypter"/>
|
||||||
<br />
|
<br />
|
||||||
<sub><b>Hugh Caluscusin</b></sub>
|
<sub><b>Hugh Caluscusin</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td></tr>
|
</td>
|
||||||
<tr>
|
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/jladuval">
|
<a href="https://github.com/jladuval">
|
||||||
<img src="https://avatars.githubusercontent.com/u/1935359?v=4" width="100;" alt="jladuval"/>
|
<img src="https://avatars.githubusercontent.com/u/1935359?v=4" width="100;" alt="jladuval"/>
|
||||||
@@ -364,13 +371,6 @@ Here are some ways of contributing to making Nhost better:
|
|||||||
<sub><b>Max Reynolds</b></sub>
|
<sub><b>Max Reynolds</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
|
||||||
<a href="https://github.com/nbourdin">
|
|
||||||
<img src="https://avatars.githubusercontent.com/u/5602476?v=4" width="100;" alt="nbourdin"/>
|
|
||||||
<br />
|
|
||||||
<sub><b>Nicolas Bourdin</b></sub>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/ghoshnirmalya">
|
<a href="https://github.com/ghoshnirmalya">
|
||||||
<img src="https://avatars.githubusercontent.com/u/6391763?v=4" width="100;" alt="ghoshnirmalya"/>
|
<img src="https://avatars.githubusercontent.com/u/6391763?v=4" width="100;" alt="ghoshnirmalya"/>
|
||||||
|
|||||||
@@ -9,6 +9,14 @@ Follow this guide to sign in users with Magic Link, also called passwordless ema
|
|||||||
|
|
||||||
The Magic Link sign-in method enables you to sign in users to your app using an email address, without requiring a password.
|
The Magic Link sign-in method enables you to sign in users to your app using an email address, without requiring a password.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Enable the Magic Link sign-in method in the Nhost dashboard under **Users** -> **Login settings** -> **Magic Link**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Sign In
|
||||||
|
|
||||||
To sign in users with Magic Link is a two-step process:
|
To sign in users with Magic Link is a two-step process:
|
||||||
|
|
||||||
1. Send a Magic Link to the user's email address.
|
1. Send a Magic Link to the user's email address.
|
||||||
@@ -21,3 +29,5 @@ nhost.auth.signIn({
|
|||||||
email: 'joe@example.com'
|
email: 'joe@example.com'
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you want to change the email for your magic link emails, you can do so by changing the [email templates](/platform/authentication/email-templates).
|
||||||
@@ -7,22 +7,53 @@ image: /img/og/platform/sign-in-with-phone-number-sms.png
|
|||||||
|
|
||||||
Follow this guide to sign in users with a phone number (SMS).
|
Follow this guide to sign in users with a phone number (SMS).
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
You need a [Twilio account](https://www.twilio.com/try-twilio) to use this feature because all SMS are sent through Twilio.
|
||||||
|
|
||||||
|
Enable the Phone Number (SMS) sign-in method in the Nhost dashboard under **Users** -> **Login settings** -> **Passwordless SMS**.
|
||||||
|
|
||||||
|
You need to insert the following settings in the Nhost dashboard from Twilio:
|
||||||
|
|
||||||
|
- Account SID
|
||||||
|
- Auth Token
|
||||||
|
- Messaging Service SID (or a Twilio phone number)
|
||||||
|
|
||||||
|
<video width="99%" autoPlay muted loop controls="true" style={{marginBottom: '15px'}}>
|
||||||
|
<source src="/videos/enable-sms-sign-in.mp4" type="video/mp4" />
|
||||||
|
</video>
|
||||||
|
|
||||||
|
|
||||||
## Sign In
|
## Sign In
|
||||||
|
|
||||||
To sign in users with a phone number is a two-step process:
|
To sign in users with a phone number is a two-step process:
|
||||||
|
|
||||||
1. Send a one-time password (OTP) to the user's phone number.
|
1. Send a one-time password (OTP) to the user's phone number.
|
||||||
2. The user uses the OTP to sign in
|
2. The user uses the OTP to sign in.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Step 1: Send OTP to the user's phone number
|
// Step 1: Send OTP to the user's phone number
|
||||||
await nhost.auth.signIn({
|
await nhost.auth.signIn({
|
||||||
phoneNumber: '0011233213123'
|
phoneNumber: '+11233213123'
|
||||||
})
|
})
|
||||||
|
|
||||||
// Step 2: Sign in user using their phone number and OTP
|
// Step 2: Sign in user using their phone number and OTP
|
||||||
await nhost.auth.signIn({
|
await nhost.auth.signIn({
|
||||||
phoneNumber: '0011233213123'
|
phoneNumber: '+11233213123'
|
||||||
|
// highlight-next-line
|
||||||
otp: '123456',
|
otp: '123456',
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The first time a user signs in using a phone number, the user is created. That means you don't need to sign up the user before signin in the user.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
|
||||||
|
Phone numbers should start with `+` (not `00`) to follow the [E.164 formatting standard](https://en.wikipedia.org/wiki/E.164).
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## Other SMS Providers
|
||||||
|
|
||||||
|
We only support Twilio for now. If you want support for another SMS provider, please create an issue on [GitHub](https://github.com/nhost/nhost).
|
||||||
@@ -15,7 +15,7 @@ The following things are deployed:
|
|||||||
- Serverless Functions
|
- Serverless Functions
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
Settings in `nhost/config.yaml` are **not** deployed. That menas you need to manually sync settings between local and remote environments between the CLI and Nhost Cloud.
|
Settings in `nhost/config.yaml` are **not** deployed. That means you need to manually sync settings between local and remote environments between the CLI and Nhost Cloud.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Connecting a GitHub repository
|
## Connecting a GitHub repository
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
---
|
|
||||||
title: 'Storage'
|
|
||||||
sidebar_position: 7
|
|
||||||
image: /img/og/platform/storage.png
|
|
||||||
---
|
|
||||||
|
|
||||||
Nhost stores and serves files of any type in your backend.
|
|
||||||
|
|
||||||
The metadata for files hosted on Nhost is available in the `storage.files` table of your database, and thus is also accessible in the GraphQL API. This allows you to fetch a list of files to display in your frontend, for example.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Buckets
|
|
||||||
|
|
||||||
Buckets are used to organize files and group permissions for files. Buckets are stored in the `storage.buckets` table in your database, and accessible via `buckets` in your GraphQL API.
|
|
||||||
|
|
||||||
Buckets are a way to segment permissions for file type, minimum and maximum file size, cache control, download expiration, and if pre-signed URLs are allowed.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Permissions
|
|
||||||
|
|
||||||
Upload, read and delete permissions for files are managed through Hasura's permission system, just like other permissions.
|
|
||||||
|
|
||||||
### Upload
|
|
||||||
|
|
||||||
To upload a file, a user must have the `insert` permission to the `storage.files` table. The following columns must be checked:
|
|
||||||
|
|
||||||
- `id`
|
|
||||||
- `bucket_id`
|
|
||||||
- `name`
|
|
||||||
- `mime_type`
|
|
||||||
|
|
||||||
### Select
|
|
||||||
|
|
||||||
To read and download a file, a user must have the `select` permission to the `storage.files` table. Only the `id` column is required, but as a best practice you should allow reading all columns.
|
|
||||||
|
|
||||||
### Delete
|
|
||||||
|
|
||||||
To delete a file, a user must have the `delete` permission to the `storage.files` table.
|
|
||||||
|
|
||||||
> Updating an existing file is not supported. Delete and upload a new file instead.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Accessing files
|
|
||||||
|
|
||||||
To access a file, make an HTTP GET request to `/v1/storage/files/{id}` with the `Authorization` header set with the access token returned by Nhost Auth. Alternatively, you can create a pre-signed URL by making an HTTP GET request to `/v1/storage/files/{id}/presignedurl` with the `Authorization` header set with the access token returned by Nhost Auth.
|
|
||||||
|
|
||||||
If the file is publicly accessible, there is no need to set the `Authorization` header. The file is publicly accessible if the permission for the `public` role is set to allow reading the file metadata in Hasura for the `storage.files` table.
|
|
||||||
|
|
||||||
### Pre-signed URL
|
|
||||||
|
|
||||||
A pre-signed URL is a unique URL that is used to access the file. Anyone with the pre-signed URL can access the file without exceptions. The URL is generated by Nhost Storage API and is valid for a limited time. The time limit of the pre-signed URL is determined by the `download_expiration` column in the bucket where the file is.
|
|
||||||
|
|
||||||
Using pre-signed URL is a good way to share files to people who don't have access to the file directly, or to access the file without setting the `Authorization` headers, for example when displaying the file in an `img` tag.
|
|
||||||
|
|
||||||
## Example with GraphQL
|
|
||||||
|
|
||||||
Let's say we're building a CRM and we want to store files for each customer.
|
|
||||||
|
|
||||||
In our CRM, we have the following two tables:
|
|
||||||
|
|
||||||
- `customers`
|
|
||||||
- `id`
|
|
||||||
- `name`
|
|
||||||
- `address`
|
|
||||||
- `customer_files`
|
|
||||||
- `id`
|
|
||||||
- `customer_id` (foreign key to `customers.id`)
|
|
||||||
- `file_id` (foreign key to `storage.files.id`)
|
|
||||||
|
|
||||||
We also have created relationships between `customers` and `customer_files` and between `customer_files` and `storage.files`.
|
|
||||||
|
|
||||||
We can now query the data with the following query:
|
|
||||||
|
|
||||||
```graphql
|
|
||||||
query {
|
|
||||||
customer {
|
|
||||||
# customer table
|
|
||||||
id
|
|
||||||
name
|
|
||||||
customer_files {
|
|
||||||
# customer_files table
|
|
||||||
id
|
|
||||||
file {
|
|
||||||
# storage.files table
|
|
||||||
id
|
|
||||||
name
|
|
||||||
size
|
|
||||||
mime_type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
To upload a file, this is what we do;
|
|
||||||
|
|
||||||
1. Upload a file
|
|
||||||
2. Get the `id` of the file. The server returns this.
|
|
||||||
3. Insert the `id` of the file together with the customer's id into the `customer_files` table.
|
|
||||||
244
docs/docs/platform/storage.mdx
Normal file
244
docs/docs/platform/storage.mdx
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
---
|
||||||
|
title: 'Storage'
|
||||||
|
sidebar_position: 7
|
||||||
|
image: /img/og/platform/storage.png
|
||||||
|
---
|
||||||
|
|
||||||
|
import Tabs from '@theme/Tabs'
|
||||||
|
import TabItem from '@theme/TabItem'
|
||||||
|
|
||||||
|
Nhost Storage enables you to let your users upload and download files. Nhost Storage is integrated with the [GraphQL API](/platform/graphql) and its permission system from Hasura.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
Files can be of any type, such as images, documents, videos, etc.
|
||||||
|
|
||||||
|
File metadata is stored in your database in the `files` table in the `storage` schema. This means that file metadata is available in your GraphQL API, which makes it easy to:
|
||||||
|
|
||||||
|
- Read file metadata via GraphQL.
|
||||||
|
- Manage file permissions (in Hasura).
|
||||||
|
- Create GraphQL relationships between files and your database tables.
|
||||||
|
|
||||||
|
:::warning
|
||||||
|
Don't modify the database schema, nor GraphQL root fields in any of the tables in the `storage` schema.
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
You're allowed to add and modify the following:
|
||||||
|
|
||||||
|
- GraphQL Relationships
|
||||||
|
- Permissions
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
### Upload File
|
||||||
|
|
||||||
|
When a file is uploaded, the file metadata is inserted into the `storage.files` table and a file `id` is returned. The file's `id` is how you reference and access the file. It's recommended to create your own table to store the uploaded file `id`, to access the file in the future.
|
||||||
|
|
||||||
|
<Tabs groupId="http-sdk">
|
||||||
|
<TabItem value="js" label="JavaScript">
|
||||||
|
|
||||||
|
```js
|
||||||
|
await nhost.storage.upload({ file })
|
||||||
|
```
|
||||||
|
|
||||||
|
Learn more about [`upload()`](/reference/javascript/storage/upload).
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="http" label="HTTP" default>
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /v1/storage/files HTTP/1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
### Download File
|
||||||
|
|
||||||
|
There are two ways to download a file:
|
||||||
|
|
||||||
|
- Public URL.
|
||||||
|
- Pre-signed URL.
|
||||||
|
|
||||||
|
#### Public URL
|
||||||
|
|
||||||
|
Public URLs are available for both unauthenticated and authenticated users. Permissions are checked for every file request. To get a public URL for a file, you would normally use the `public` role to set **select** permissions.
|
||||||
|
|
||||||
|
<Tabs groupId="http-sdk">
|
||||||
|
<TabItem value="js" label="JavaScript">
|
||||||
|
|
||||||
|
```js
|
||||||
|
await nhost.storage.getPublicUrl({
|
||||||
|
fileId: '<File-ID>'
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Learn more about [`getPublicUrl()`](/reference/javascript/storage/get-public-url).
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="http" label="HTTP" default>
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v1/storage/files/{file_id} HTTP/1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
#### Pre-signed URL
|
||||||
|
|
||||||
|
Pre-signed URLs work a bit differently from public URLs.
|
||||||
|
|
||||||
|
The permission check only happens when the user requests a pre-signed URL. Once a pre-signed URL is generated, anyone with the pre-signed URL can download the file.
|
||||||
|
|
||||||
|
Pre-signed URLs expire, and stop work, after a set amount of time. By default, for files in the `default` bucket, the expiration time is 30 seconds. You can change the expiration time for pre-signed URLs by changing the `download_expiration` (in seconds) field on the bucket where the file is located.
|
||||||
|
|
||||||
|
<Tabs groupId="http-sdk">
|
||||||
|
<TabItem value="js" label="JavaScript">
|
||||||
|
|
||||||
|
```js
|
||||||
|
await nhost.storage.getPresignedUrl({
|
||||||
|
fileId: '<File-ID>'
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Learn more about [`getPresignedUrl()`](/reference/javascript/storage/get-presigned-url).
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="http" label="HTTP" default>
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v1/storage/files/{file_id}/presignedurl HTTP/1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
### Delete File
|
||||||
|
|
||||||
|
Delete a file and the file metadata in the database.
|
||||||
|
|
||||||
|
<Tabs groupId="http-sdk">
|
||||||
|
<TabItem value="js" label="JavaScript">
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { error } = await nhost.storage.delete({ fileId: 'uuid' })
|
||||||
|
```
|
||||||
|
|
||||||
|
Learn more about [`delete()`](/reference/javascript/storage/delete).
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="http" label="HTTP" default>
|
||||||
|
|
||||||
|
```http
|
||||||
|
DELETE /v1/storage/files/{file_id} HTTP/1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## Buckets
|
||||||
|
|
||||||
|
Buckets are used to organize files and group permissions for files. Buckets are stored in the `storage.buckets` table in your database, and accessible via `buckets` in your GraphQL API.
|
||||||
|
|
||||||
|
For each bucket, you can specify file permissions for the following properties:
|
||||||
|
|
||||||
|
- MIME type.
|
||||||
|
- Minimum size.
|
||||||
|
- Maximum size.
|
||||||
|
- Cache control.
|
||||||
|
- Allow pre-signed URLs.
|
||||||
|
- Download expiration (for pre-signed URLs).
|
||||||
|
|
||||||
|
There is a default bucket (`default`) that is used if no bucket is specified during file upload. It's not possible to delete the `default` bucket.
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
|
||||||
|
Permissions to upload, download, and delete files are managed through Hasura's permission system on the `storage.files` table.
|
||||||
|
|
||||||
|
### Upload
|
||||||
|
|
||||||
|
To upload a file, a user must have the **`insert` permission** to the `storage.files` table. The following columns must be allowed to insert:
|
||||||
|
|
||||||
|
- `id`
|
||||||
|
- `bucket_id`
|
||||||
|
- `name`
|
||||||
|
- `mime_type`
|
||||||
|
|
||||||
|
### Download
|
||||||
|
|
||||||
|
To download a file, a user must have the **`select` permission** to the `storage.files` table. Only the `id` column is required, but we recommend allowing to select all columns.
|
||||||
|
|
||||||
|
### Delete
|
||||||
|
|
||||||
|
To delete a file, a user must have the **`delete` permission** to the `storage.files` table.
|
||||||
|
|
||||||
|
Updating an existing file is not supported. If you need to update a file, delete the file and upload a new file.
|
||||||
|
|
||||||
|
Just deleting the file metadata in the `storage.files` table does **not** delete the actual file. Always delete files via Nhost Storage. This way, both the file metadata and the actual file are deleted.
|
||||||
|
|
||||||
|
## Image Transformation
|
||||||
|
|
||||||
|
Images can be transformed, on the fly, by adding query parameters. The following query parameters are available:
|
||||||
|
|
||||||
|
- `w` - Width of the image in pixels.
|
||||||
|
- `h` - Height of the image in pixels.
|
||||||
|
|
||||||
|
Image Transformation works on both public and pre-signed URLs.
|
||||||
|
|
||||||
|
**Example**: Transform an image to 500px width (`?w=500`):
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://[subdomain].nhost.run/v1/storage/files/08e6fa32-0880-4d0e-a832-278198acb363?w=500
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example: CRM System
|
||||||
|
|
||||||
|
Let's say you want to build a CRM system and you want to store files for customers. This is one way how you could do that.
|
||||||
|
|
||||||
|
Start with, you would have two tables:
|
||||||
|
|
||||||
|
1. `customers` - Customer data.
|
||||||
|
2. `customer_files` - What file belongs to what customer.
|
||||||
|
|
||||||
|
```text
|
||||||
|
- customers
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- address
|
||||||
|
customer_files
|
||||||
|
- id
|
||||||
|
- customer_id (Foreign Key to `customers.id`)
|
||||||
|
- file_id (Foreign Key to `storage.files.id`)
|
||||||
|
```
|
||||||
|
|
||||||
|
You would also create [Hasura Relationships](https://hasura.io/docs/latest/graphql/core/databases/postgres/schema/table-relationships/index/) (GraphQL relationships) between between `customers` and `customer_files` and between `customer_files` and `storage.files`.
|
||||||
|
|
||||||
|
With the two tables and GraphQL relationships in place, you can query customers and the customer's files like this:
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
query {
|
||||||
|
customers { # customers table
|
||||||
|
id
|
||||||
|
name
|
||||||
|
customer_files { # customer_files tabel
|
||||||
|
id
|
||||||
|
file { # storage.files table
|
||||||
|
id
|
||||||
|
name
|
||||||
|
size
|
||||||
|
mimeType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The file upload process would be as follows:
|
||||||
|
|
||||||
|
1. Upload a file.
|
||||||
|
2. Get the returned file id.
|
||||||
|
3. Insert the file `id` and the customer's `id` into the `customer_files` table.
|
||||||
|
|
||||||
|
This would allow you to upload and download files belonging to specific customers in your CRM system.
|
||||||
@@ -26,7 +26,7 @@ const config = {
|
|||||||
favicon: 'img/favicon.png',
|
favicon: 'img/favicon.png',
|
||||||
organizationName: 'nhost',
|
organizationName: 'nhost',
|
||||||
projectName: 'docs',
|
projectName: 'docs',
|
||||||
|
plugins: [require.resolve("docusaurus-plugin-image-zoom")],
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
'classic',
|
'classic',
|
||||||
@@ -180,6 +180,16 @@ const config = {
|
|||||||
apiKey: 'a76361eaed8ebcd4cf5d9ae2f0c9e746',
|
apiKey: 'a76361eaed8ebcd4cf5d9ae2f0c9e746',
|
||||||
indexName: 'nhost',
|
indexName: 'nhost',
|
||||||
contextualSearch: false
|
contextualSearch: false
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
selector: '.markdown :not(em) > img',
|
||||||
|
config: {
|
||||||
|
// options you can specify via https://github.com/francoischalifour/medium-zoom#usage
|
||||||
|
background: {
|
||||||
|
light: 'rgb(255, 255, 255)',
|
||||||
|
dark: 'rgb(50, 50, 50)'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"@docusaurus/preset-classic": "2.0.0-beta.20",
|
"@docusaurus/preset-classic": "2.0.0-beta.20",
|
||||||
"@mdx-js/react": "^1.6.22",
|
"@mdx-js/react": "^1.6.22",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.1.1",
|
||||||
|
"docusaurus-plugin-image-zoom": "^0.1.1",
|
||||||
"mdx-mermaid": "^1.2.1",
|
"mdx-mermaid": "^1.2.1",
|
||||||
"mermaid": "^8.14.0",
|
"mermaid": "^8.14.0",
|
||||||
"prism-react-renderer": "^1.3.1",
|
"prism-react-renderer": "^1.3.1",
|
||||||
|
|||||||
BIN
docs/static/img/platform/authentication/sign-in-methods/magic-link/magic-link-setup.png
vendored
Normal file
BIN
docs/static/img/platform/authentication/sign-in-methods/magic-link/magic-link-setup.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 667 KiB |
BIN
docs/static/videos/enable-sms-sign-in.mp4
vendored
Normal file
BIN
docs/static/videos/enable-sms-sign-in.mp4
vendored
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
import { FaFacebook, FaGithub, FaGoogle } from 'react-icons/fa'
|
import { FaFacebook, FaGithub, FaGoogle } from 'react-icons/fa'
|
||||||
|
|
||||||
import { useProviderLink } from '@nhost/react'
|
import { useProviderLink } from '@nhost/nextjs'
|
||||||
|
|
||||||
import AuthLink from './AuthLink'
|
import AuthLink from './AuthLink'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @nhost/apollo
|
# @nhost/apollo
|
||||||
|
|
||||||
|
## 0.5.13
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- @nhost/nhost-js@1.2.4
|
||||||
|
|
||||||
## 0.5.12
|
## 0.5.12
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/apollo",
|
"name": "@nhost/apollo",
|
||||||
"version": "0.5.12",
|
"version": "0.5.13",
|
||||||
"description": "Nhost Apollo Client library",
|
"description": "Nhost Apollo Client library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# @nhost/core
|
# @nhost/core
|
||||||
|
|
||||||
|
## 0.6.5
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 08a37aae: correct rewriting options when `clientUrl` is not available
|
||||||
|
The client URL is set to `window.location.origin`, so it can rewrite redirection urls that are passed on to authenticaion methods. However, `clientUrl` is set to `''` when running on the server side. This fix then avoid raising an error when trying to rewrite `redirectTo` on non-browser environment, and forces `useProviderLink` to be rendered on the client side.
|
||||||
|
|
||||||
## 0.6.4
|
## 0.6.4
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/core",
|
"name": "@nhost/core",
|
||||||
"version": "0.6.4",
|
"version": "0.6.5",
|
||||||
"description": "Nhost core client library",
|
"description": "Nhost core client library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -17,21 +17,39 @@ export const encodeQueryParameters = (baseUrl: string, parameters?: Record<strin
|
|||||||
else return baseUrl
|
else return baseUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
export const rewriteRedirectTo = <T extends RedirectOption>(clientUrl: string, options?: T) => {
|
/**
|
||||||
|
* Transform options that include a redirectTo property so the
|
||||||
|
* redirect url is absolute, given a base clientUrl.
|
||||||
|
* If no client url is given, any relative redirectUrl is removed while
|
||||||
|
* the other options are sent as-is.
|
||||||
|
* @param clientUrl base client url
|
||||||
|
* @param options
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const rewriteRedirectTo = <T extends RedirectOption>(
|
||||||
|
clientUrl?: string,
|
||||||
|
options?: T
|
||||||
|
): (Omit<T, 'redirectTo'> & { redirectTo?: string }) | undefined => {
|
||||||
if (!options?.redirectTo) {
|
if (!options?.redirectTo) {
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
const { redirectTo, ...otherOptions } = options
|
||||||
|
// * If the clientUrl is not defined, we can't rewrite the redirectTo
|
||||||
|
if (!clientUrl) {
|
||||||
|
// * If redirectTo is a relative path, we therefore pull it out of the options
|
||||||
|
if (redirectTo.startsWith('/')) {
|
||||||
|
return otherOptions
|
||||||
|
} else {
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
}
|
||||||
const baseClientUrl = new URL(clientUrl)
|
const baseClientUrl = new URL(clientUrl)
|
||||||
const clientParams = Object.fromEntries(new URLSearchParams(baseClientUrl.search))
|
const clientParams = Object.fromEntries(new URLSearchParams(baseClientUrl.search))
|
||||||
const url = new URL(
|
const url = new URL(redirectTo.startsWith('/') ? baseClientUrl.origin + redirectTo : redirectTo)
|
||||||
options.redirectTo.startsWith('/')
|
|
||||||
? baseClientUrl.origin + options.redirectTo
|
|
||||||
: options.redirectTo
|
|
||||||
)
|
|
||||||
const additionalParams = new URLSearchParams(url.search)
|
const additionalParams = new URLSearchParams(url.search)
|
||||||
let combinedParams = Object.fromEntries(additionalParams)
|
let combinedParams = Object.fromEntries(additionalParams)
|
||||||
|
|
||||||
if (options.redirectTo.startsWith('/')) {
|
if (redirectTo.startsWith('/')) {
|
||||||
combinedParams = { ...clientParams, ...combinedParams }
|
combinedParams = { ...clientParams, ...combinedParams }
|
||||||
}
|
}
|
||||||
let pathName = baseClientUrl.pathname
|
let pathName = baseClientUrl.pathname
|
||||||
@@ -39,7 +57,7 @@ export const rewriteRedirectTo = <T extends RedirectOption>(clientUrl: string, o
|
|||||||
pathName += url.pathname.slice(1)
|
pathName += url.pathname.slice(1)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...options,
|
...otherOptions,
|
||||||
redirectTo: encodeQueryParameters(url.origin + pathName, combinedParams)
|
redirectTo: encodeQueryParameters(url.origin + pathName, combinedParams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,18 @@ test(`should not add redirectTo when none is given`, async () => {
|
|||||||
expect(rewriteRedirectTo('https://frontend.com')).toBeUndefined()
|
expect(rewriteRedirectTo('https://frontend.com')).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(`should remove redirectTo when it's relative and no clientUrl is given`, async () => {
|
||||||
|
const options: RedirectOption = { redirectTo: '/index' }
|
||||||
|
expect(rewriteRedirectTo('', options)).toEqual({})
|
||||||
|
expect(rewriteRedirectTo(undefined, options)).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`should preserve options when redirectTo is not a relative url and no clientUrl is given`, async () => {
|
||||||
|
const options: RedirectOption = { redirectTo: 'https://frontend.com' }
|
||||||
|
expect(rewriteRedirectTo('', options)).toEqual(options)
|
||||||
|
expect(rewriteRedirectTo(undefined, options)).toEqual(options)
|
||||||
|
})
|
||||||
|
|
||||||
test(`should append redirectTo with the clientUrl prefix`, async () => {
|
test(`should append redirectTo with the clientUrl prefix`, async () => {
|
||||||
const options: RedirectOption = { redirectTo: '/index' }
|
const options: RedirectOption = { redirectTo: '/index' }
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# @nhost/hasura-auth-js
|
# @nhost/hasura-auth-js
|
||||||
|
|
||||||
|
## 1.1.14
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [08a37aae]
|
||||||
|
- @nhost/core@0.6.5
|
||||||
|
|
||||||
## 1.1.13
|
## 1.1.13
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/hasura-auth-js",
|
"name": "@nhost/hasura-auth-js",
|
||||||
"version": "1.1.13",
|
"version": "1.1.14",
|
||||||
"description": "Hasura-auth client",
|
"description": "Hasura-auth client",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
# @nhost/nextjs
|
# @nhost/nextjs
|
||||||
|
|
||||||
|
## 1.2.13
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 08a37aae: correct rewriting options when `clientUrl` is not available
|
||||||
|
The client URL is set to `window.location.origin`, so it can rewrite redirection urls that are passed on to authenticaion methods. However, `clientUrl` is set to `''` when running on the server side. This fix then avoid raising an error when trying to rewrite `redirectTo` on non-browser environment, and forces `useProviderLink` to be rendered on the client side.
|
||||||
|
- Updated dependencies [08a37aae]
|
||||||
|
- @nhost/core@0.6.5
|
||||||
|
- @nhost/react@0.7.13
|
||||||
|
- @nhost/nhost-js@1.2.4
|
||||||
|
|
||||||
## 1.2.12
|
## 1.2.12
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/nextjs",
|
"name": "@nhost/nextjs",
|
||||||
"version": "1.2.12",
|
"version": "1.2.13",
|
||||||
"description": "Nhost NextJS library",
|
"description": "Nhost NextJS library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @nhost/nhost-js
|
# @nhost/nhost-js
|
||||||
|
|
||||||
|
## 1.2.4
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- @nhost/hasura-auth-js@1.1.14
|
||||||
|
|
||||||
## 1.2.3
|
## 1.2.3
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/nhost-js",
|
"name": "@nhost/nhost-js",
|
||||||
"version": "1.2.3",
|
"version": "1.2.4",
|
||||||
"description": "Nhost JavaScript SDK",
|
"description": "Nhost JavaScript SDK",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# @nhost/react-apollo
|
# @nhost/react-apollo
|
||||||
|
|
||||||
|
## 4.2.15
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [08a37aae]
|
||||||
|
- @nhost/react@0.7.13
|
||||||
|
- @nhost/apollo@0.5.13
|
||||||
|
|
||||||
## 4.2.14
|
## 4.2.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/react-apollo",
|
"name": "@nhost/react-apollo",
|
||||||
"version": "4.2.14",
|
"version": "4.2.15",
|
||||||
"description": "Nhost React Apollo client",
|
"description": "Nhost React Apollo client",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# @nhost/react-auth
|
# @nhost/react-auth
|
||||||
|
|
||||||
|
## 3.0.11
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [08a37aae]
|
||||||
|
- @nhost/react@0.7.13
|
||||||
|
- @nhost/hasura-auth-js@1.1.14
|
||||||
|
|
||||||
## 3.0.10
|
## 3.0.10
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/react-auth",
|
"name": "@nhost/react-auth",
|
||||||
"version": "3.0.10",
|
"version": "3.0.11",
|
||||||
"description": "Nhost React client",
|
"description": "Nhost React client",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
# @nhost/react
|
# @nhost/react
|
||||||
|
|
||||||
|
## 0.7.13
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 08a37aae: correct rewriting options when `clientUrl` is not available
|
||||||
|
The client URL is set to `window.location.origin`, so it can rewrite redirection urls that are passed on to authenticaion methods. However, `clientUrl` is set to `''` when running on the server side. This fix then avoid raising an error when trying to rewrite `redirectTo` on non-browser environment, and forces `useProviderLink` to be rendered on the client side.
|
||||||
|
- Updated dependencies [08a37aae]
|
||||||
|
- @nhost/core@0.6.5
|
||||||
|
- @nhost/nhost-js@1.2.4
|
||||||
|
|
||||||
## 0.7.12
|
## 0.7.12
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/react",
|
"name": "@nhost/react",
|
||||||
"version": "0.7.12",
|
"version": "0.7.13",
|
||||||
"description": "Nhost React library",
|
"description": "Nhost React library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useContext, useMemo } from 'react'
|
import { useContext, useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { encodeQueryParameters, Provider, ProviderOptions, rewriteRedirectTo } from '@nhost/core'
|
import { encodeQueryParameters, Provider, ProviderOptions, rewriteRedirectTo } from '@nhost/core'
|
||||||
|
|
||||||
@@ -29,13 +29,25 @@ import { NhostReactContext } from './provider'
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export const useProviderLink = (options?: ProviderOptions) => {
|
export const useProviderLink = (options?: ProviderOptions) => {
|
||||||
|
/**
|
||||||
|
* @internal When using Nextjs or any SSR framework, nhost.auth.client.clientUrl will be set to `undefined`
|
||||||
|
* as its value is set to window.location.origin.
|
||||||
|
* This is because the request context is not available when setting up the client `new NhostClient()` outside of
|
||||||
|
* the React/Nextjs context.
|
||||||
|
*/
|
||||||
|
const [isSSR, setIsSSR] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsSSR(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
const nhost = useContext(NhostReactContext)
|
const nhost = useContext(NhostReactContext)
|
||||||
|
|
||||||
return new Proxy({} as Record<Provider, string>, {
|
return new Proxy({} as Record<Provider, string>, {
|
||||||
get(_, provider: string) {
|
get(_, provider: string) {
|
||||||
return encodeQueryParameters(
|
return encodeQueryParameters(
|
||||||
`${nhost.auth.client.backendUrl}/signin/provider/${provider}`,
|
`${nhost.auth.client.backendUrl}/signin/provider/${provider}`,
|
||||||
rewriteRedirectTo(nhost.auth.client.clientUrl, options as any)
|
rewriteRedirectTo(isSSR ? undefined : nhost.auth.client.clientUrl, options as any)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# @nhost/vue
|
# @nhost/vue
|
||||||
|
|
||||||
|
## 0.1.6
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [08a37aae]
|
||||||
|
- @nhost/core@0.6.5
|
||||||
|
- @nhost/nhost-js@1.2.4
|
||||||
|
|
||||||
## 0.1.5
|
## 0.1.5
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/vue",
|
"name": "@nhost/vue",
|
||||||
"version": "0.1.5",
|
"version": "0.1.6",
|
||||||
"description": "Nhost Vue library",
|
"description": "Nhost Vue library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -95,6 +95,7 @@ importers:
|
|||||||
'@tsconfig/docusaurus': ^1.0.5
|
'@tsconfig/docusaurus': ^1.0.5
|
||||||
'@types/swagger-ui-react': ^4.1.1
|
'@types/swagger-ui-react': ^4.1.1
|
||||||
clsx: ^1.1.1
|
clsx: ^1.1.1
|
||||||
|
docusaurus-plugin-image-zoom: ^0.1.1
|
||||||
mdx-mermaid: ^1.2.1
|
mdx-mermaid: ^1.2.1
|
||||||
mermaid: ^8.14.0
|
mermaid: ^8.14.0
|
||||||
prism-react-renderer: ^1.3.1
|
prism-react-renderer: ^1.3.1
|
||||||
@@ -108,6 +109,7 @@ importers:
|
|||||||
'@docusaurus/preset-classic': 2.0.0-beta.20_66c8c36abcfdbeab71528ce4b75c41f7
|
'@docusaurus/preset-classic': 2.0.0-beta.20_66c8c36abcfdbeab71528ce4b75c41f7
|
||||||
'@mdx-js/react': 1.6.22_react@17.0.2
|
'@mdx-js/react': 1.6.22_react@17.0.2
|
||||||
clsx: 1.1.1
|
clsx: 1.1.1
|
||||||
|
docusaurus-plugin-image-zoom: 0.1.1
|
||||||
mdx-mermaid: 1.2.2_mermaid@8.14.0+react@17.0.2
|
mdx-mermaid: 1.2.2_mermaid@8.14.0+react@17.0.2
|
||||||
mermaid: 8.14.0
|
mermaid: 8.14.0
|
||||||
prism-react-renderer: 1.3.1_react@17.0.2
|
prism-react-renderer: 1.3.1_react@17.0.2
|
||||||
@@ -9697,6 +9699,12 @@ packages:
|
|||||||
resolution: {integrity: sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=}
|
resolution: {integrity: sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/docusaurus-plugin-image-zoom/0.1.1:
|
||||||
|
resolution: {integrity: sha512-cJXo5TKh9OR1gE4B5iS5ovLWYYDFwatqRm00iXFPOaShZG99l5tgkDKgbQPAwSL9wg4I+wz3aMwkOtDhMIpKDQ==}
|
||||||
|
dependencies:
|
||||||
|
medium-zoom: 1.0.6
|
||||||
|
dev: false
|
||||||
|
|
||||||
/dom-converter/0.2.0:
|
/dom-converter/0.2.0:
|
||||||
resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==}
|
resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -14628,6 +14636,10 @@ packages:
|
|||||||
resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=}
|
resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
/medium-zoom/1.0.6:
|
||||||
|
resolution: {integrity: sha512-UdiUWfvz9fZMg1pzf4dcuqA0W079o0mpqbTnOz5ip4VGYX96QjmbM+OgOU/0uOzAytxC0Ny4z+VcYQnhdifimg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/memfs/3.4.1:
|
/memfs/3.4.1:
|
||||||
resolution: {integrity: sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==}
|
resolution: {integrity: sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==}
|
||||||
engines: {node: '>= 4.0.0'}
|
engines: {node: '>= 4.0.0'}
|
||||||
|
|||||||
Reference in New Issue
Block a user