Compare commits
172 Commits
@nhost/rea
...
@nhost/cor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bebf9e1f2b | ||
|
|
2413c10283 | ||
|
|
0f7fbdab97 | ||
|
|
14e5fd63a6 | ||
|
|
2446913836 | ||
|
|
1f88a9f47a | ||
|
|
261e37cda4 | ||
|
|
5ee395ea8e | ||
|
|
828633ffc9 | ||
|
|
7b7527a5e6 | ||
|
|
620566fa4d | ||
|
|
4ce8b88d27 | ||
|
|
28d25e46de | ||
|
|
e0cfcafead | ||
|
|
12bc30daa3 | ||
|
|
7b5f00d10e | ||
|
|
58e1485c13 | ||
|
|
a64f1c4396 | ||
|
|
75a1428114 | ||
|
|
d82d830849 | ||
|
|
2def59fc6c | ||
|
|
64ceb2c6bf | ||
|
|
3ee007620c | ||
|
|
b9cf8172a0 | ||
|
|
32edfb4a9f | ||
|
|
848db9b672 | ||
|
|
3766921bcc | ||
|
|
5546052b2c | ||
|
|
c569b56d3d | ||
|
|
52ffa84adb | ||
|
|
b5ae438a8e | ||
|
|
fae05f7af2 | ||
|
|
380d7fc8ce | ||
|
|
94132bbc7f | ||
|
|
d87a9d7c79 | ||
|
|
be7756d4a2 | ||
|
|
ca5e335bff | ||
|
|
b9ed794f2b | ||
|
|
042dc7d27c | ||
|
|
db2df3d5b2 | ||
|
|
0b1cb628f2 | ||
|
|
912d95d153 | ||
|
|
76059f4738 | ||
|
|
011572f3ef | ||
|
|
b727b354dd | ||
|
|
a0682ed22e | ||
|
|
4d16306e56 | ||
|
|
b7861bbd36 | ||
|
|
e279805896 | ||
|
|
e3ebd9cb1b | ||
|
|
5bb928da2c | ||
|
|
ab06e96eac | ||
|
|
6e2aabbda0 | ||
|
|
e4ce235f38 | ||
|
|
e783b7478b | ||
|
|
06d2d2b0c7 | ||
|
|
656379e78b | ||
|
|
4156a9a61e | ||
|
|
0b72829274 | ||
|
|
6b0baab151 | ||
|
|
93f9d2d01d | ||
|
|
3fb3d4c282 | ||
|
|
ccba0b5015 | ||
|
|
62e331500d | ||
|
|
4104ddbcb6 | ||
|
|
43fc040a29 | ||
|
|
e472b2cb19 | ||
|
|
6570a940ee | ||
|
|
d3e97c87d6 | ||
|
|
36508c7930 | ||
|
|
709d364749 | ||
|
|
73eb2db159 | ||
|
|
86eb8903dc | ||
|
|
d665473074 | ||
|
|
58534c24f0 | ||
|
|
90a1c3b9e1 | ||
|
|
bdfa2b3053 | ||
|
|
2c5b31f27a | ||
|
|
d75fd747e0 | ||
|
|
a71b3aff59 | ||
|
|
8a8c67db92 | ||
|
|
13935ebdc4 | ||
|
|
357ba89d53 | ||
|
|
7e34805eb4 | ||
|
|
52782ee550 | ||
|
|
089d7fb0a2 | ||
|
|
9df131201e | ||
|
|
067d8a692c | ||
|
|
824060e7f6 | ||
|
|
0fe7b8f0fb | ||
|
|
78f096a738 | ||
|
|
4635a145c1 | ||
|
|
1446a8f13b | ||
|
|
ff75998e93 | ||
|
|
9cc044ca9f | ||
|
|
c65e44b8d6 | ||
|
|
9ec73b4c22 | ||
|
|
e4eda9e967 | ||
|
|
94b70e0ce9 | ||
|
|
d108dff4f4 | ||
|
|
7a8e771a72 | ||
|
|
f8fb4bbedd | ||
|
|
90e38b1cc5 | ||
|
|
9c04dad57c | ||
|
|
c6b1c33a8e | ||
|
|
65b36eff13 | ||
|
|
cac6088016 | ||
|
|
c192cb9503 | ||
|
|
94ff290264 | ||
|
|
aff80db515 | ||
|
|
6e2c991b2e | ||
|
|
78781ebfec | ||
|
|
f6093a619f | ||
|
|
63d6059981 | ||
|
|
022d49fb25 | ||
|
|
a8e6187360 | ||
|
|
276d6b10dd | ||
|
|
62461a2f20 | ||
|
|
81ec16d77b | ||
|
|
5a059c1e9e | ||
|
|
28bbde6142 | ||
|
|
05f01e45ec | ||
|
|
b1bd405a5e | ||
|
|
a295b5b1e6 | ||
|
|
eece559771 | ||
|
|
cd0e4d1908 | ||
|
|
7bf678df9f | ||
|
|
3bd1aa4d53 | ||
|
|
f3cca4997b | ||
|
|
0fd7a487d6 | ||
|
|
1bb032c1e7 | ||
|
|
2c97db68b5 | ||
|
|
714f2872ee | ||
|
|
65fc26a0e8 | ||
|
|
86a56f28c1 | ||
|
|
6e8abe28d6 | ||
|
|
39925ff5ca | ||
|
|
583a77ed0d | ||
|
|
e704831500 | ||
|
|
a8f82e8133 | ||
|
|
95948dd5b9 | ||
|
|
247b69c952 | ||
|
|
7d15b76402 | ||
|
|
b1ae65fd72 | ||
|
|
0063fd1840 | ||
|
|
743a7e6507 | ||
|
|
1687f7af04 | ||
|
|
078652861f | ||
|
|
39840cfd95 | ||
|
|
102c99e491 | ||
|
|
a3702a644e | ||
|
|
db65fea706 | ||
|
|
ffe9123b48 | ||
|
|
f112ea2115 | ||
|
|
4963153def | ||
|
|
940a36a68f | ||
|
|
77b109b3df | ||
|
|
15907d65e6 | ||
|
|
7d7d16fa71 | ||
|
|
3f39e48cbd | ||
|
|
97ade32869 | ||
|
|
8583af8290 | ||
|
|
a28193a6ba | ||
|
|
60d85e5a69 | ||
|
|
9d6c64430a | ||
|
|
519d1bf5cb | ||
|
|
5ffb0320b5 | ||
|
|
50d2413554 | ||
|
|
7d275aad90 | ||
|
|
6607e73cc2 | ||
|
|
b4bac161a5 | ||
|
|
37d15377c8 |
15
.github/workflows/contributors.yaml
vendored
Normal file
15
.github/workflows/contributors.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: Add contributors
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
contrib-readme-job:
|
||||
runs-on: ubuntu-latest
|
||||
name: A job to automate contrib in readme
|
||||
steps:
|
||||
- name: Contribute List
|
||||
uses: akhilmhdh/contributors-readme-action@v2.3.4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
215
README.md
215
README.md
@@ -1,8 +1,8 @@
|
||||
<div align="center">
|
||||
<img width="237" src="https://raw.githubusercontent.com/nhost/nhost/main/assets/logo.png"/>
|
||||

|
||||
|
||||
<br />
|
||||
<br />
|
||||
<div align="center">
|
||||
|
||||
# Nhost
|
||||
|
||||
<a href="https://docs.nhost.io/get-started">Quickstart</a>
|
||||
<span> • </span>
|
||||
@@ -20,9 +20,9 @@
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
**Nhost is a serverless backend for web and mobile apps** built with the following things in mind:
|
||||
**Nhost is an open-source GraphQL backend,** built with the following things in mind:
|
||||
|
||||
- Open Source
|
||||
- Open-Source
|
||||
- Developer Productivity
|
||||
- SQL
|
||||
- GraphQL
|
||||
@@ -110,7 +110,7 @@ First and foremost: **Star and watch this repository** to stay up-to-date.
|
||||
|
||||
Also, follow Nhost on [GitHub Discussions](https://github.com/nhost/nhost/discussions), our [Blog](https://nhost.io/blog), and on [Twitter](https://twitter.com/nhostio). You can chat with the team and other members on [Discord](https://discord.com/invite/9V7Qb2U) and follow our tutorials and other video material at [YouTube](https://www.youtube.com/channel/UCJ7irtvV9Y0EQMxpabb6ntg?view_as=subscriber).
|
||||
|
||||
## Nhost is Open Source
|
||||
### Nhost is Open Source
|
||||
|
||||
This repository, and most of our other open source projects, are licensed under the MIT license.
|
||||
|
||||
@@ -122,6 +122,203 @@ Here are some ways of contributing to making Nhost better:
|
||||
- Join our [Discord](https://discord.com/invite/9V7Qb2U) and connect with other members to share and learn from.
|
||||
- Send a pull request to any of our [open source repositories](https://github.com/nhost) on Github. Check our [contribution guide](https://github.com/nhost/nhost/blob/main/CONTRIBUTING.md) for more details about how to contribute. We're looking forward to your contribution!
|
||||
|
||||
## Security
|
||||
### Contributors
|
||||
|
||||
If you discover a security vulnerability within Nhost, please e-mail [security@nhost.io](mailto:security@nhost.io). All security vulnerabilities will be promptly addressed.
|
||||
<!-- readme: contributors -start -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/plmercereau">
|
||||
<img src="https://avatars.githubusercontent.com/u/24897252?v=4" width="100;" alt="plmercereau"/>
|
||||
<br />
|
||||
<sub><b>Pilou</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/elitan">
|
||||
<img src="https://avatars.githubusercontent.com/u/331818?v=4" width="100;" alt="elitan"/>
|
||||
<br />
|
||||
<sub><b>Johan Eliasson</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/szilarddoro">
|
||||
<img src="https://avatars.githubusercontent.com/u/310881?v=4" width="100;" alt="szilarddoro"/>
|
||||
<br />
|
||||
<sub><b>Szilárd Dóró</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/nunopato">
|
||||
<img src="https://avatars.githubusercontent.com/u/1523504?v=4" width="100;" alt="nunopato"/>
|
||||
<br />
|
||||
<sub><b>Nuno Pato</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/subatuba21">
|
||||
<img src="https://avatars.githubusercontent.com/u/34824571?v=4" width="100;" alt="subatuba21"/>
|
||||
<br />
|
||||
<sub><b>Subha Das</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/guicurcio">
|
||||
<img src="https://avatars.githubusercontent.com/u/20285232?v=4" width="100;" alt="guicurcio"/>
|
||||
<br />
|
||||
<sub><b>Guido Curcio</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/sebagudelo">
|
||||
<img src="https://avatars.githubusercontent.com/u/43288271?v=4" width="100;" alt="sebagudelo"/>
|
||||
<br />
|
||||
<sub><b>Sebagudelo</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/mrinalwahal">
|
||||
<img src="https://avatars.githubusercontent.com/u/9859731?v=4" width="100;" alt="mrinalwahal"/>
|
||||
<br />
|
||||
<sub><b>Mrinal Wahal</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/gdangelo">
|
||||
<img src="https://avatars.githubusercontent.com/u/4352286?v=4" width="100;" alt="gdangelo"/>
|
||||
<br />
|
||||
<sub><b>Grégory D'Angelo</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/FuzzyReason">
|
||||
<img src="https://avatars.githubusercontent.com/u/62517920?v=4" width="100;" alt="FuzzyReason"/>
|
||||
<br />
|
||||
<sub><b>Vadim Smirnov</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/subhendukundu">
|
||||
<img src="https://avatars.githubusercontent.com/u/20059141?v=4" width="100;" alt="subhendukundu"/>
|
||||
<br />
|
||||
<sub><b>Subhendu Kundu</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/chrtze">
|
||||
<img src="https://avatars.githubusercontent.com/u/3797215?v=4" width="100;" alt="chrtze"/>
|
||||
<br />
|
||||
<sub><b>Christopher Möller</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/hajek-raven">
|
||||
<img src="https://avatars.githubusercontent.com/u/7288737?v=4" width="100;" alt="hajek-raven"/>
|
||||
<br />
|
||||
<sub><b>Filip Hájek</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jerryjappinen">
|
||||
<img src="https://avatars.githubusercontent.com/u/1101002?v=4" width="100;" alt="jerryjappinen"/>
|
||||
<br />
|
||||
<sub><b>Jerry Jäppinen</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/mustafa-hanif">
|
||||
<img src="https://avatars.githubusercontent.com/u/30019262?v=4" width="100;" alt="mustafa-hanif"/>
|
||||
<br />
|
||||
<sub><b>Mustafa Hanif</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/timpratim">
|
||||
<img src="https://avatars.githubusercontent.com/u/32492961?v=4" width="100;" alt="timpratim"/>
|
||||
<br />
|
||||
<sub><b>Pratim</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Savinvadim1312">
|
||||
<img src="https://avatars.githubusercontent.com/u/16936043?v=4" width="100;" alt="Savinvadim1312"/>
|
||||
<br />
|
||||
<sub><b>Savin Vadim</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ahmic">
|
||||
<img src="https://avatars.githubusercontent.com/u/13452362?v=4" width="100;" alt="ahmic"/>
|
||||
<br />
|
||||
<sub><b>Amir Ahmic</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/akd-io">
|
||||
<img src="https://avatars.githubusercontent.com/u/30059155?v=4" width="100;" alt="akd-io"/>
|
||||
<br />
|
||||
<sub><b>Anders Kjær Damgaard</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rustyb">
|
||||
<img src="https://avatars.githubusercontent.com/u/53086?v=4" width="100;" alt="rustyb"/>
|
||||
<br />
|
||||
<sub><b>Colin Broderick</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/dohomi">
|
||||
<img src="https://avatars.githubusercontent.com/u/489221?v=4" width="100;" alt="dohomi"/>
|
||||
<br />
|
||||
<sub><b>Dominic Garms</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/GavanWilhite">
|
||||
<img src="https://avatars.githubusercontent.com/u/2085119?v=4" width="100;" alt="GavanWilhite"/>
|
||||
<br />
|
||||
<sub><b>Gavan Wilhite</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/alveshelio">
|
||||
<img src="https://avatars.githubusercontent.com/u/8176422?v=4" width="100;" alt="alveshelio"/>
|
||||
<br />
|
||||
<sub><b>Helio Alves</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/nkhdo">
|
||||
<img src="https://avatars.githubusercontent.com/u/26102306?v=4" width="100;" alt="nkhdo"/>
|
||||
<br />
|
||||
<sub><b>Hoang Do</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ghoshnirmalya">
|
||||
<img src="https://avatars.githubusercontent.com/u/6391763?v=4" width="100;" alt="ghoshnirmalya"/>
|
||||
<br />
|
||||
<sub><b>Nirmalya Ghosh</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/quentin-decre">
|
||||
<img src="https://avatars.githubusercontent.com/u/1137511?v=4" width="100;" alt="quentin-decre"/>
|
||||
<br />
|
||||
<sub><b>Quentin Decré</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/komninoschat">
|
||||
<img src="https://avatars.githubusercontent.com/u/29049104?v=4" width="100;" alt="komninoschat"/>
|
||||
<br />
|
||||
<sub><b>Komninos</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
</table>
|
||||
<!-- readme: contributors -end -->
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": ["next", "prettier"],
|
||||
"rules": {
|
||||
"@next/next/no-img-element": "off",
|
||||
"import/no-default-export": "off",
|
||||
"react/self-closing-comp": "warn",
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"react/no-unescaped-entities": "off",
|
||||
"react/prop-types": "off",
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
"no-console": "warn"
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"settings": { "react": { "version": "detect" } }
|
||||
}
|
||||
22
docs/.gitignore
vendored
22
docs/.gitignore
vendored
@@ -1,2 +1,20 @@
|
||||
!lib
|
||||
!.prettierignore
|
||||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
node_modules/
|
||||
.next/
|
||||
11
docs/.prettierrc.json
Normal file
11
docs/.prettierrc.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"endOfLine": "auto",
|
||||
"semi": true
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
# nhost-documentation
|
||||
|
||||
## null
|
||||
### Patch Changes
|
||||
|
||||
- 03562af: Build in CommonJS and ESM instead of UMD and ESM as the UMD bundle generated by the default Vite lib build mode doesn't work with NodeJS
|
||||
@@ -1,48 +1,37 @@
|
||||
# Nhost Documentation
|
||||
# Nhost Docs
|
||||
|
||||
## Get started
|
||||
This documentation describes how to build, start and test the documentation locally.
|
||||
|
||||
From the **root** of the `nhost/nhost` repository:
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
pnpm run clean:all
|
||||
pnpm i
|
||||
cd docs
|
||||
pnpm run dev
|
||||
$ pnpm i
|
||||
```
|
||||
|
||||
### Local Development
|
||||
|
||||
## Structure
|
||||
|
||||
The `order.ts` file contains the main order for the entire structure of `posts`. The keys are `categories` and the values are `subcategories` in which contains the order the posts.
|
||||
|
||||
```
|
||||
export const orderTwo = {
|
||||
'get-started': {
|
||||
'quick-start': ['index', 'schema', 'javascript-client', 'permissions'],
|
||||
authentication: ['index'],
|
||||
'cli-workflow': ['index', 'workflow-setup', 'install-cli', 'local-changes', 'metadata-and-serverless-functions'],
|
||||
upgrade: ['index']
|
||||
},
|
||||
platform: {
|
||||
database: ['index', 'permissions', 'graphql'],
|
||||
authentication: ['index', 'user-management', 'sign-in-methods', 'social-login', 'email-templates'],
|
||||
storage: ['index'],
|
||||
'serverless-functions': ['index', 'event-triggers'],
|
||||
nhost: ['index', 'environment-variables', 'github-integration', 'local-development']
|
||||
},
|
||||
reference: {
|
||||
sdk: ['index', 'graphql', 'authentication', 'storage', 'functions'],
|
||||
react: ['index', 'hooks', 'protecting-routes', 'apollo'],
|
||||
nextjs: ['index', 'configuration', 'protecting-routes', ],
|
||||
cli: ['index'],
|
||||
'hasura-auth': ['index', 'installation', 'configuration', 'environment-variables', 'schema', 'api-reference']
|
||||
}
|
||||
};
|
||||
```bash
|
||||
$ pnpm start
|
||||
```
|
||||
|
||||
Metadata such as the `title` of the file that appears on the nav is on the frontmatter of each markdown file. The file name becomes the final url. Each top-level folder appears on the header as main navigation, each subfolder becomes a main subcategory of the nav and posts are included under each subcategory.
|
||||
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
||||
|
||||
In order to create a new file you place it its proper subcategory and modify the category on the `order.ts` file such as `sdk: ["javascript-sdk", "react-auth", "react-apollo"],` -> `sdk: ["javascript-sdk", "react-auth", "vue"]`
|
||||
### Build
|
||||
|
||||
Each subCategory e.g. `reference` or `tutorials` has an `index.mdx` file. If a new subcategory is added, a file has to be created for it.
|
||||
```bash
|
||||
$ pnpm build
|
||||
```
|
||||
|
||||
This command generates static content into the `build` directory and can be served using any static contents hosting service.
|
||||
|
||||
### Serve
|
||||
|
||||
```bash
|
||||
$ pnpm serve
|
||||
```
|
||||
|
||||
This command serves the static content from the `build` directory.
|
||||
|
||||
### Contributing
|
||||
|
||||
All pull requests are greatly appreciated! See our [contributing guide](https://github.com/nhost/nhost/blob/main/CONTRIBUTING.md) to get started.
|
||||
|
||||
3
docs/babel.config.js
Normal file
3
docs/babel.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
import Text from '@/components/ui/Text'
|
||||
import { motion } from 'framer-motion'
|
||||
import { useRouter } from 'next/dist/client/router'
|
||||
import React, { useState } from 'react'
|
||||
import createKebabCase from '../utils/createKebabCase'
|
||||
import Permalink from './icons/Permalink'
|
||||
|
||||
export interface AnchorLinkProps {
|
||||
children?: any
|
||||
id?: string
|
||||
size?: 'tiny' | 'small' | 'normal' | 'large' | 'big' | 'heading'
|
||||
className?: string
|
||||
}
|
||||
|
||||
export default function AnchorLink({ children, id, size, className }: AnchorLinkProps) {
|
||||
const {
|
||||
query: { category, subcategory, post }
|
||||
} = useRouter()
|
||||
const [showPermaLink, setShowPermalink] = useState(false)
|
||||
|
||||
const isQuoted = typeof children !== 'string'
|
||||
|
||||
return (
|
||||
<div
|
||||
id={
|
||||
id
|
||||
? children.split('/')[1]
|
||||
: createKebabCase(
|
||||
`#${isQuoted ? (children.props ? children.props.children : children) : children}`
|
||||
)
|
||||
}
|
||||
className={className}
|
||||
onMouseOver={() => setShowPermalink(true)}
|
||||
onMouseLeave={() => setShowPermalink(false)}
|
||||
>
|
||||
<span id={createKebabCase(`${children}`)} className={'flex flex-row relative'}>
|
||||
{showPermaLink ? (
|
||||
<motion.span
|
||||
className="absolute self-center w-4 h-4 align-middle -left-5"
|
||||
onClick={() => {
|
||||
navigator.clipboard
|
||||
.writeText(
|
||||
`https://docs.nhost.io/${category}/${subcategory}/${post}/${
|
||||
id
|
||||
? id
|
||||
: createKebabCase(
|
||||
`#${
|
||||
isQuoted
|
||||
? children.props
|
||||
? children.props.children
|
||||
: children
|
||||
: children
|
||||
}`
|
||||
)
|
||||
}`
|
||||
)
|
||||
.catch((e) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(e)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Permalink className="w-4 h-4" />
|
||||
</motion.span>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<Text
|
||||
variant="a"
|
||||
href={createKebabCase(
|
||||
`#${isQuoted ? (children.props ? children.props.children : children) : children}`
|
||||
)}
|
||||
color="greyscaleDark"
|
||||
className="font-medium break-all"
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import React from 'react'
|
||||
import { useState } from 'react'
|
||||
|
||||
import Check from './icons/Check'
|
||||
import Copy from './icons/Copy'
|
||||
|
||||
export default function Command({ children }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
return (
|
||||
<div className="my-1 flex-row inline-flex self-center text-xs bg-gray-50 pl-2 pr-1.5 text-gray-900 font-mono leading-6 py-0.25 border border-gray-200 rounded-md">
|
||||
<span className="text-verydark mr-1.5 self-center">$</span>
|
||||
{children}
|
||||
<button
|
||||
className="ml-1.5 self-center inline-block cursor-pointer"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(children).catch((e) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(e)
|
||||
})
|
||||
setCopied(true)
|
||||
setTimeout(() => {
|
||||
setCopied(false)
|
||||
}, 1000)
|
||||
}}
|
||||
>
|
||||
{/* <Tooltip text={"Copied!"}> */}
|
||||
{copied ? (
|
||||
<Check className="w-3.5 h-3.5 mr-0.5 text-greenDark transition-colors self-center" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4 text-gray-500 transition-colors hover:text-gray-900" />
|
||||
)}
|
||||
{/* </Tooltip> */}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export function Container({ children }) {
|
||||
return (
|
||||
<div className="mx-10 px-2 sm:px-10 md:px-20 lg:px-0 flex flex-row md:max-w-container pb-20 md:mx-auto mt-8 lg:space-x-20">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import markdownStyles from '@/styles/markdown-styles.module.css'
|
||||
import { DOCS_GITHUB_ENDPOINT } from '@/utils/constants'
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
import { useRouter } from 'next/dist/client/router'
|
||||
import React from 'react'
|
||||
|
||||
import GithubIcon from './icons/GithubIcon'
|
||||
import Button from './ui/Button/Button'
|
||||
import Text from './ui/Text/Text'
|
||||
|
||||
function getGithubLink(category, subcategory, post) {
|
||||
if (post) return `${DOCS_GITHUB_ENDPOINT}${category}/${subcategory}/${post}.mdx`
|
||||
else if (subcategory) return `${DOCS_GITHUB_ENDPOINT}${category}/${subcategory}/index.mdx`
|
||||
else {
|
||||
return `${DOCS_GITHUB_ENDPOINT}${category}/index.mdx`
|
||||
}
|
||||
}
|
||||
|
||||
export function Content({ mdxSource, components, frontmatter }) {
|
||||
const router = useRouter()
|
||||
return (
|
||||
<div className="flex flex-col w-full h-full mt-2">
|
||||
<div className="flex flex-row mb-4 place-content-between">
|
||||
<Text color="greyscaleDark" className="font-medium cursor-pointer" size="heading">
|
||||
{frontmatter.title}
|
||||
</Text>
|
||||
<div className="self-center hidden md:block">
|
||||
<Button
|
||||
Component="a"
|
||||
variant="secondary"
|
||||
className="invisible md:visible"
|
||||
href={getGithubLink(router.query.category, router.query.subcategory, router.query.post)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
type={null}
|
||||
>
|
||||
Edit This Page
|
||||
<GithubIcon className="w-3.5 h-3.5 ml-1.5 text-greyscaleDark self-center" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={markdownStyles['markdown']}>
|
||||
<MDXRemote {...mdxSource} components={components} lazy />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { withRouter } from 'next/router'
|
||||
import Link from 'next/link'
|
||||
import React, { Children } from 'react'
|
||||
|
||||
const CustomLink = ({ router, children, ...props }) => {
|
||||
const child = Children.only(children)
|
||||
|
||||
let className = child.props.className || ''
|
||||
const pathname = `/${router.query.category}/${router.query.post}`
|
||||
|
||||
if (pathname === props.href && props.activeClassName) {
|
||||
className = `${className} ${props.activeClassName}`.trim()
|
||||
}
|
||||
|
||||
delete props.activeClassName
|
||||
|
||||
// @ts-ignore
|
||||
return <Link {...props}>{React.cloneElement(child, { className })}</Link>
|
||||
}
|
||||
|
||||
export default withRouter(CustomLink)
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function Divider() {
|
||||
return <div className="divider mt-6 mb-4 order-2" />
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
import Button from '@/components/ui/Button'
|
||||
import siteLinks from '@/data/siteLinks.json'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Newsletter } from './Newsletter'
|
||||
|
||||
// import Input from './ui/Input/Input';
|
||||
export default function Footer() {
|
||||
const [email, setEmail] = useState('')
|
||||
|
||||
return (
|
||||
<div className="bg-verydark">
|
||||
<div className="max-w-mxcontainer px-5 mx-auto">
|
||||
<div className="flex flex-col pt-20">
|
||||
{/* Logo and CTA */}
|
||||
<div className="place-content-between flex flex-row">
|
||||
<div className="">
|
||||
<img
|
||||
src="/logos/nhost-footer-logo.svg"
|
||||
width={141.57}
|
||||
height={48}
|
||||
alt="Nhost white logo"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row self-center">
|
||||
<Button
|
||||
Component="a"
|
||||
variant="secondary"
|
||||
className="md:visible invisible mr-2 text-white cursor-pointer"
|
||||
href="mailto:hello@nhost.io"
|
||||
type={null}
|
||||
>
|
||||
Contact Us
|
||||
</Button>
|
||||
<Button
|
||||
Component="a"
|
||||
variant="primary"
|
||||
href="https://app.nhost.io"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="cursor-pointer"
|
||||
type={null}
|
||||
>
|
||||
<span className="md:block hidden">Sign up or Log in</span>
|
||||
<span className="md:hidden">Sign up</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/* All links */}
|
||||
{/* @FIX: space-x on the firSubscribest one. */}
|
||||
<div className="font-display md:flex-row flex flex-col mt-12">
|
||||
<div className="gap-14 md:grid-flow-col md:grid-cols-5 grid grid-flow-row grid-cols-1">
|
||||
{siteLinks.siteLinks.map((siteLink, i) => {
|
||||
return (
|
||||
<FooterLinks
|
||||
key={siteLink.text + i}
|
||||
title={siteLink.text}
|
||||
links={siteLink.links}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<Newsletter />
|
||||
{/* <Newsletter email={email} setEmail={setEmail} /> */}
|
||||
{/* Socials */}
|
||||
{/* @FIX: mt is 103px */}
|
||||
<div className="md:mx-0 place-content-between font-display md:flex-row flex flex-col pb-2 mx-auto mt-24">
|
||||
<div className="pb-2">
|
||||
<ul className="flex flex-row space-x-6">
|
||||
<li className="items-center self-center align-middle">
|
||||
<a href="https://github.com/nhost" target="_blank" rel="noreferrer">
|
||||
<img src="/logos/Github.svg" width={25} height={25} alt="Nhost on GitHub" />
|
||||
</a>
|
||||
</li>
|
||||
<li className="items-center self-center align-middle">
|
||||
<a href="https://twitter.com/nhostio" target="_blank" rel="noreferrer">
|
||||
<img src="/logos/Twitter.svg" width={25} height={25} alt="Nhost on Twitter" />
|
||||
</a>
|
||||
</li>
|
||||
<li className="items-center self-center align-middle">
|
||||
<a
|
||||
href="https://www.linkedin.com/company/nhost/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<img src="/logos/Linkedin.svg" width={25} height={25} alt="Nhost in LinkedIn" />
|
||||
</a>
|
||||
</li>
|
||||
<li className="items-center self-center align-middle">
|
||||
<a href="https://discord.com/invite/9V7Qb2U" target="_blank" rel="noreferrer">
|
||||
<img
|
||||
src="/logos/Discord.svg"
|
||||
width={25}
|
||||
height={25}
|
||||
alt="Nhost community on Discord"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="md:pt-0 md:space-y-0 md:flex-row flex flex-col pt-2 space-y-4 text-xs font-medium text-white">
|
||||
<a
|
||||
className="translucent self-center"
|
||||
href="https://nhost.io/privacy-policy"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
<a
|
||||
className="md:pl-6 translucent self-center"
|
||||
href="https://nhost.io/terms-of-service"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Terms of Service
|
||||
</a>
|
||||
|
||||
<a
|
||||
className="md:pl-6 translucent self-center"
|
||||
href="https://nhost.io"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
nhost.io 2022
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
interface FooterLinkProps {
|
||||
title: string
|
||||
links: Links[]
|
||||
}
|
||||
interface Links {
|
||||
name: string
|
||||
href: string
|
||||
}
|
||||
function FooterLinks({ title, links }: FooterLinkProps) {
|
||||
return (
|
||||
<div>
|
||||
{/* color */}
|
||||
<h1 className="font-medium text-gray-700 uppercase">{title}</h1>
|
||||
<ul className="mt-4 space-y-4">
|
||||
{links.map((link) => {
|
||||
return (
|
||||
<li key={link.name} className="text-white font-normal text-sm+ cursor-pointer">
|
||||
<Link href={link.href}>{link.name}</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
import { useNavData } from '@/components/NavDataContext'
|
||||
import { ArrowLeftIcon, MenuIcon } from '@heroicons/react/outline'
|
||||
import clsx from 'clsx'
|
||||
import { useRouter } from 'next/dist/client/router'
|
||||
import Link from 'next/link'
|
||||
import React, { MouseEvent, useEffect, useState } from 'react'
|
||||
import Button from '../components/ui/Button'
|
||||
import { Nav } from './Nav'
|
||||
|
||||
export default function Header() {
|
||||
const [mobileMenu, setMobileMenu] = useState(false)
|
||||
const router = useRouter()
|
||||
const GithubStarsCounter = () => {
|
||||
const repoUrl = `https://api.github.com/repos/nhost/nhost`
|
||||
const [count, setCount] = useState(null)
|
||||
const format = (n: number) => (n > 1000 ? `${(n / 1000).toFixed(1)}k` : n)
|
||||
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
const data = await fetch(repoUrl).then((res) => res.json())
|
||||
setCount(data.stargazers_count)
|
||||
})()
|
||||
}, [repoUrl])
|
||||
|
||||
return (
|
||||
<a
|
||||
className="text-base font-medium leading-snug flex flex-row items-center justify-center px-2.5 py-1.5 rounded opacity-50 hover:opacity-100 mr-8"
|
||||
href="https://github.com/nhost/nhost"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<img
|
||||
className="mr-2"
|
||||
src="/logos/Github2.svg"
|
||||
width={20}
|
||||
height={20}
|
||||
alt="Nhost on GitHub"
|
||||
/>
|
||||
{count === null ? 0 : format(count)}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
function handleMobileMenuOpen() {
|
||||
setMobileMenu(true)
|
||||
}
|
||||
|
||||
function handleMobileMenuClose() {
|
||||
setMobileMenu(false)
|
||||
}
|
||||
|
||||
if (mobileMenu) {
|
||||
return <MobileNav onClose={handleMobileMenuClose} />
|
||||
}
|
||||
|
||||
return (
|
||||
<header className="bg-white md:max-w-full menu-card rounded-md px-4 py-0.5 mx-2">
|
||||
<div className="md:max-w-header2 mx-auto font-display flex flex-row antialiased">
|
||||
<div className="flex flex-row w-full mx-auto place-content-between py-2">
|
||||
<div className="flex flex-row">
|
||||
<button
|
||||
className="md:hidden w-8 h-8 flex items-center justify-center cursor-pointer text-greyscaleDark"
|
||||
aria-label="Open menu"
|
||||
onClick={handleMobileMenuOpen}
|
||||
>
|
||||
<MenuIcon className="h-6 w-6" />
|
||||
</button>
|
||||
|
||||
<Link href="/get-started" passHref>
|
||||
<a className="hidden ml-3 sm:ml-0 self-center md:flex flex-row cursor-pointer">
|
||||
<img src="/images/nhost-docs.svg" width={110} height={35} alt="Nhost white logo" />
|
||||
<h1 className="self-center ml-6 font-medium text-greyscaleDark">DOCS</h1>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<div className="ml-20 hidden md:flex flex-row self-center ">
|
||||
<ul className="flex flex-row items-center self-center antialiased font-medium text-greyscaleGrey font-display">
|
||||
<Link href="/get-started" passHref={true}>
|
||||
<a
|
||||
className={clsx(
|
||||
'cursor-pointer text-base- self-center hover:text-greyscaleDark transition-colors duration-200 py-3',
|
||||
router.query.category === 'get-started' && 'text-greyscaleDark'
|
||||
)}
|
||||
>
|
||||
Get Started
|
||||
</a>
|
||||
</Link>
|
||||
<Link href="/platform" passHref={true}>
|
||||
<a
|
||||
className={clsx(
|
||||
'ml-12 cursor-pointer text-base- self-center hover:text-greyscaleDark transition-colors duration-200 py-3',
|
||||
router.query.category === 'platform' && 'text-greyscaleDark'
|
||||
)}
|
||||
>
|
||||
Platform
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<Link href="/reference" passHref={true}>
|
||||
<a
|
||||
className={clsx(
|
||||
'ml-12 cursor-pointer text-base- self-center hover:text-greyscaleDark transition-colors duration-200 py-3',
|
||||
router.query.category === 'reference' && 'text-greyscaleDark'
|
||||
)}
|
||||
>
|
||||
Reference
|
||||
</a>
|
||||
</Link>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden sm:flex self-center">
|
||||
<GithubStarsCounter />
|
||||
<Button
|
||||
className="self-center"
|
||||
variant="primary"
|
||||
href={'https://app.nhost.io'}
|
||||
Component="a"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
type={null}
|
||||
>
|
||||
Go to Nhost
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export type MobileNavProps = {
|
||||
onClose?: VoidFunction
|
||||
}
|
||||
|
||||
export function MobileNav({ onClose }: MobileNavProps) {
|
||||
const { getConvolutedNavByCategory } = useNavData()
|
||||
const router = useRouter()
|
||||
const [selectedMenuSlug, setSelectedMenuSlug] = useState<string | null>(null)
|
||||
const [selectedMenuName, setSelectedMenuName] = useState<string | null>(null)
|
||||
|
||||
function handleMenuSelect(event: MouseEvent<HTMLAnchorElement>, slug: string, name: string) {
|
||||
event.preventDefault()
|
||||
|
||||
setSelectedMenuSlug(slug)
|
||||
setSelectedMenuName(name)
|
||||
}
|
||||
|
||||
function clearMenuSelection() {
|
||||
setSelectedMenuSlug(null)
|
||||
setSelectedMenuName(null)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white menu-card rounded-lg px-4 pb-6 max-w-full mx-2">
|
||||
<div className="flex flex-col w-full py-3 mx-auto">
|
||||
<div className="grid grid-flow-col justify-between items-center">
|
||||
{!selectedMenuSlug && (
|
||||
<>
|
||||
<button
|
||||
className="w-8 h-8 flex items-center justify-center cursor-pointer text-greyscaleDark"
|
||||
aria-label="Close menu"
|
||||
onClick={onClose}
|
||||
>
|
||||
<MenuIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
<Link href="/get-started" passHref>
|
||||
<a
|
||||
className="ml-3 sm:ml-0 self-center flex flex-row cursor-pointer"
|
||||
onClick={onClose}
|
||||
>
|
||||
<img
|
||||
src="/images/nhost-docs.svg"
|
||||
width={110}
|
||||
height={35}
|
||||
alt="Nhost white logo"
|
||||
/>
|
||||
<h1 className="self-center ml-5 font-medium text-greyscaleDark">DOCS</h1>
|
||||
</a>
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedMenuSlug && (
|
||||
<button
|
||||
className="ml-2 h-8 grid grid-flow-col gap-2 items-center justify-center cursor-pointer text-greyscaleDark"
|
||||
aria-label="Go back to main menu"
|
||||
onClick={clearMenuSelection}
|
||||
>
|
||||
<ArrowLeftIcon className="h-4 w-4" aria-hidden="true" />{' '}
|
||||
<span className="font-medium text-base-">{selectedMenuName}</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Placeholder for making logo appear correctly in the middle */}
|
||||
<div className="w-8 h-8" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col py-6 mt-4 border-divide border-t border-b">
|
||||
{!selectedMenuSlug && (
|
||||
<ul className="flex flex-col font-medium text-greyscaleDark text-base- font-display space-y-4 text-left px-4">
|
||||
<li
|
||||
className={clsx(
|
||||
'cursor-pointer text-base- hover:text-greyscaleDark transition-colors duration-200 text-left ',
|
||||
router.query.category === 'get-started' && 'text-greyscaleDark'
|
||||
)}
|
||||
>
|
||||
<Link href="/get-started" passHref>
|
||||
<a
|
||||
className="block"
|
||||
onClick={(event) => handleMenuSelect(event, 'get-started', 'Get Started')}
|
||||
>
|
||||
Get Started
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li
|
||||
className={clsx(
|
||||
'cursor-pointer text-base- hover:text-greyscaleDark transition-colors duration-200 text-left',
|
||||
router.query.category === 'platform' && 'text-greyscaleDark'
|
||||
)}
|
||||
>
|
||||
<Link href="/platform">
|
||||
<a
|
||||
className="block"
|
||||
onClick={(event) => handleMenuSelect(event, 'platform', 'Platform')}
|
||||
>
|
||||
Platform
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li
|
||||
className={clsx(
|
||||
'cursor-pointer text-base- hover:text-greyscaleDark transition-colors duration-200',
|
||||
router.query.category === 'reference' && 'text-greyscaleDark'
|
||||
)}
|
||||
>
|
||||
<Link href="/reference">
|
||||
<a
|
||||
className="block"
|
||||
onClick={(event) => handleMenuSelect(event, 'reference', 'Reference')}
|
||||
>
|
||||
Reference
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{selectedMenuSlug && (
|
||||
<Nav
|
||||
category={selectedMenuSlug}
|
||||
categoryTitle={selectedMenuName}
|
||||
convolutedNav={getConvolutedNavByCategory(selectedMenuSlug)}
|
||||
onMenuSelected={onClose}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sm:flex self-center py-2">
|
||||
<Button
|
||||
className="self-center"
|
||||
variant="primary"
|
||||
href="https://app.nhost.io"
|
||||
Component="a"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
type={null}
|
||||
>
|
||||
Go to Nhost
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import Text from '@/components/ui/Text/Text'
|
||||
import createKebabCase from '@/utils/createKebabCase'
|
||||
import clsx from 'clsx'
|
||||
import { useRouter } from 'next/dist/client/router'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
export function HeadingsNavigation(props) {
|
||||
const {
|
||||
query: { category, subcategory, post }
|
||||
} = useRouter()
|
||||
|
||||
return (
|
||||
<div className="hidden xl:flex flex-col mt-10 sticky top-20 w-full h-full pb-12 pl-4">
|
||||
<Text className="font-medium" color="greyscaleDark" size="normal">
|
||||
On this page
|
||||
</Text>
|
||||
<ul className="space-y-2 mt-2 pl-1">
|
||||
{props.headings.map((heading) => {
|
||||
return (
|
||||
<Link
|
||||
passHref
|
||||
key={heading.name}
|
||||
href={`/${category}/${subcategory}/${post}#${createKebabCase(heading.name)}`}
|
||||
>
|
||||
<li
|
||||
className={clsx(
|
||||
'text-blue hover:text-darkBlue transition-all duration-300 cursor-pointer hover:translate-x-0.5 transform',
|
||||
heading.depth === 1 && 'text-sm font-medium',
|
||||
heading.depth === 2 && 'pl-3 text-sm font-normal',
|
||||
heading.depth === 3 && 'pl-7 text-xs font-normal'
|
||||
)}
|
||||
>
|
||||
{heading.name}
|
||||
</li>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import { lightNhostTheme } from '@/data/lightTheme'
|
||||
import { useState } from 'react'
|
||||
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter'
|
||||
import js from 'react-syntax-highlighter/dist/cjs/languages/hljs/javascript'
|
||||
import ts from 'react-syntax-highlighter/dist/cjs/languages/hljs/typescript'
|
||||
|
||||
import Check from '../icons/Check'
|
||||
import Copy from '../icons/Copy'
|
||||
|
||||
SyntaxHighlighter.registerLanguage('language-js', js)
|
||||
SyntaxHighlighter.registerLanguage('language-ts', ts)
|
||||
// TODO highlight JSX
|
||||
SyntaxHighlighter.registerLanguage('language-jsx', js)
|
||||
|
||||
export interface CodeEditorProps {
|
||||
code: string
|
||||
fileName: string
|
||||
className: string
|
||||
fixed: boolean
|
||||
gradient: boolean
|
||||
deploy: boolean
|
||||
url?: string
|
||||
children: any
|
||||
}
|
||||
|
||||
const CodeEditor = (props: CodeEditorProps) => {
|
||||
const { children, className } = props
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="relative min-w-full pb-0 my-4 rounded-md">
|
||||
<div className="absolute right-0">
|
||||
<button
|
||||
className="ml-1.5 self-center inline-block cursor-pointer rounded-md mt-2 mr-2"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(children).catch((e) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(e)
|
||||
})
|
||||
setCopied(true)
|
||||
setTimeout(() => {
|
||||
setCopied(false)
|
||||
}, 1000)
|
||||
}}
|
||||
>
|
||||
{/* <Tooltip text={"Copied!"}> */}
|
||||
{copied ? (
|
||||
<Check className="w-3.5 h-3.5 mr-0.5 text-greenDark transition-colors self-center" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4 text-gray-500 transition-colors hover:text-gray-900" />
|
||||
)}
|
||||
{/* </Tooltip> */}
|
||||
</button>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
language={className}
|
||||
style={lightNhostTheme}
|
||||
wrapLongLines={true}
|
||||
wrapLines={true}
|
||||
lineProps={{
|
||||
style: { wordBreak: 'break-all', whiteSpace: 'pre-wrap' }
|
||||
}}
|
||||
customStyle={{
|
||||
paddingLeft: '12px',
|
||||
fontSize: '13px'
|
||||
}}
|
||||
className="pt-2 rounded-md"
|
||||
showLineNumbers={false}
|
||||
>
|
||||
{children}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CodeEditor
|
||||
@@ -1,156 +0,0 @@
|
||||
import AnchorLink, { AnchorLinkProps } from '@/components/AnchorLink'
|
||||
import CodeComponent, { CodeEditorProps } from '@/components/MDX/CodeComponent'
|
||||
import Text, { TextProps } from '@/components/ui/Text'
|
||||
import clsx from 'clsx'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import React, { DetailedHTMLProps, HTMLProps, PropsWithChildren } from 'react'
|
||||
|
||||
import Command from '../Command'
|
||||
import Divider from '../Divider'
|
||||
import { Swagger } from '../Swagger'
|
||||
|
||||
function Note({ children }: PropsWithChildren<unknown>) {
|
||||
return (
|
||||
<div className="px-5 py-5 my-5 space-y-2 text-white rounded-md bg-verydark">
|
||||
<Text className="text-white">Note</Text>
|
||||
<Text className="text-white">{children}</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Video({
|
||||
src,
|
||||
...props
|
||||
}: DetailedHTMLProps<HTMLProps<HTMLSourceElement>, HTMLSourceElement>) {
|
||||
return (
|
||||
<div className="flex justify-center mx-10 my-8">
|
||||
<video width="800" controls>
|
||||
<source src={src} type="video/mp4" {...props} />
|
||||
</video>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const CustomLink = ({
|
||||
className,
|
||||
children,
|
||||
href,
|
||||
...props
|
||||
}: DetailedHTMLProps<HTMLProps<HTMLAnchorElement>, HTMLAnchorElement>) => {
|
||||
const isInternalLink = href && ['./', '../', '/', '#'].some((symbol) => href.startsWith(symbol))
|
||||
|
||||
if (isInternalLink) {
|
||||
return (
|
||||
<Link href={href} passHref>
|
||||
<a className={clsx('font-medium text-blue', className)} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
target="_blank"
|
||||
className={clsx('font-medium text-blue', className)}
|
||||
rel="noopener noreferrer"
|
||||
href={href}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
const components = {
|
||||
img: (props: DetailedHTMLProps<HTMLProps<HTMLImageElement>, HTMLImageElement>) => {
|
||||
return (
|
||||
<span className="block mx-10 my-10 ">
|
||||
<img src={props.src} alt={props.alt} className="mx-auto mt-2" />
|
||||
</span>
|
||||
)
|
||||
},
|
||||
Video,
|
||||
Image,
|
||||
Text,
|
||||
Note,
|
||||
code: (props: CodeEditorProps) => {
|
||||
if (props.className && props.className.includes('language')) {
|
||||
return <CodeComponent {...props} />
|
||||
} else {
|
||||
return <Command>{props.children}</Command>
|
||||
}
|
||||
},
|
||||
Divider,
|
||||
a: CustomLink,
|
||||
h1: (props: AnchorLinkProps) => {
|
||||
return (
|
||||
<>
|
||||
<Divider />
|
||||
<AnchorLink {...props} className="text-3xl cursor-pointer md:text-4xl" />
|
||||
</>
|
||||
)
|
||||
},
|
||||
h2: (props: AnchorLinkProps) => {
|
||||
return (
|
||||
<div className="mt-10">
|
||||
<AnchorLink {...props} className="cursor-pointer text-lg sm:text-xl md:text-2.5xl" />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
h3: (props: AnchorLinkProps) => {
|
||||
return (
|
||||
<div className="mt-8">
|
||||
<AnchorLink {...props} className="text-lg cursor-pointer" />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
h4: (props: AnchorLinkProps) => {
|
||||
return (
|
||||
<div className="mt-4">
|
||||
<AnchorLink {...props} className="font-bold cursor-pointer text-base-" />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
p: (props: TextProps) => {
|
||||
return (
|
||||
<Text
|
||||
variant="body"
|
||||
size="small"
|
||||
color="dark"
|
||||
className="my-2 antialiased leading-6"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
th: ({
|
||||
className,
|
||||
...props
|
||||
}: DetailedHTMLProps<HTMLProps<HTMLTableCellElement>, HTMLTableCellElement>) => {
|
||||
return <th className={clsx('font-display', className)} {...props} />
|
||||
},
|
||||
td: ({
|
||||
className,
|
||||
...props
|
||||
}: DetailedHTMLProps<HTMLProps<HTMLTableCellElement>, HTMLTableCellElement>) => {
|
||||
return <td className={clsx('font-display', className)} {...props} />
|
||||
},
|
||||
Swagger,
|
||||
Mermaid: ({ chart }) => {
|
||||
const [html, setHtml] = React.useState('')
|
||||
React.useEffect(() => {
|
||||
const start = async () => {
|
||||
const mermaid = (await import('mermaid')).default
|
||||
mermaid.mermaidAPI.render(uuid(), chart, (svgCode) => setHtml(svgCode))
|
||||
}
|
||||
start()
|
||||
}, [chart])
|
||||
return chart ? <div dangerouslySetInnerHTML={{ __html: html }} /> : null
|
||||
}
|
||||
}
|
||||
|
||||
let currentId = 0
|
||||
const uuid = () => `mermaid-${(currentId++).toString()}`
|
||||
|
||||
export default components
|
||||
@@ -1,3 +0,0 @@
|
||||
export function Main({ children }) {
|
||||
return <div className="flex flex-col w-full lg:min-w-body lg:w-body">{children}</div>
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import Text from '@/components/ui/Text'
|
||||
import clsx from 'clsx'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { ParsedUrlQuery } from 'querystring'
|
||||
import React, { MouseEvent } from 'react'
|
||||
import { fixTitle } from '../utils/fixTitle'
|
||||
import { NavItem } from './NavDataContext'
|
||||
|
||||
export type NavProps = {
|
||||
/**
|
||||
* Class name to apply to the wrapper element.
|
||||
*/
|
||||
className?: string
|
||||
/**
|
||||
* Category slug.
|
||||
*/
|
||||
category: string
|
||||
/**
|
||||
* The category title.
|
||||
*/
|
||||
categoryTitle: string
|
||||
/**
|
||||
* Convoluted navigation.
|
||||
*/
|
||||
convolutedNav: NavItem[]
|
||||
/**
|
||||
* Function to be called when a menu item is selected.
|
||||
*/
|
||||
onMenuSelected?: (event?: MouseEvent<HTMLAnchorElement, MouseEvent>) => void
|
||||
}
|
||||
|
||||
export function Nav({ className, onMenuSelected, ...props }: NavProps) {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<div className={clsx('lg:min-w-nav lg:w-nav flex-col space-y-5 antialiased mt-1', className)}>
|
||||
<div>
|
||||
<ul>
|
||||
<li
|
||||
className={clsx(
|
||||
'transition duration-300 ease-in-out rounded-md hover:text-black hover:bg-veryLightGray',
|
||||
router.query.category === props.category &&
|
||||
!router.query.subcategory &&
|
||||
!router.query.post &&
|
||||
'bg-veryLightGray'
|
||||
)}
|
||||
>
|
||||
<Link href={`/${props.category}`} passHref>
|
||||
<Text
|
||||
variant="a"
|
||||
color="greyscaleDark"
|
||||
size="normal"
|
||||
className={clsx(
|
||||
'block py-1.5 px-3 transition-colors duration-300 ease-in-out text-greyscaleDark hover:text-dark subpixel-antialiased',
|
||||
'font-medium'
|
||||
)}
|
||||
onClick={onMenuSelected}
|
||||
>
|
||||
{props.categoryTitle}
|
||||
</Text>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{props.convolutedNav.map((elem) => {
|
||||
const parentCategory = props.category.replace(' ', '-')
|
||||
|
||||
return (
|
||||
<div key={elem.category}>
|
||||
<Link href={`/${parentCategory}/${elem.category}/`} passHref>
|
||||
<Text
|
||||
variant="a"
|
||||
color="greyscaleGrey"
|
||||
size="normal"
|
||||
className="block px-3 py-px font-medium capitalize"
|
||||
onClick={onMenuSelected}
|
||||
>
|
||||
{/* Split */}
|
||||
{fixTitle(elem)}
|
||||
</Text>
|
||||
</Link>
|
||||
|
||||
<ul className="mt-1 space-y-1 ">
|
||||
{elem.posts.map((post) => {
|
||||
const pathToLink =
|
||||
post.fileName != 'index'
|
||||
? `/${parentCategory}/${elem.category}/${post.fileName}`
|
||||
: `/${parentCategory}/${elem.category}`
|
||||
|
||||
const shouldHighlight =
|
||||
router.query.subcategory === elem.category && router.query.post === post.fileName
|
||||
|
||||
const shouldHighlightSubcategories =
|
||||
!router.query.post &&
|
||||
post.fileName === 'index' &&
|
||||
elem.category === router.query.subcategory
|
||||
|
||||
return (
|
||||
<li
|
||||
className={clsx(
|
||||
'transition duration-300 ease-in-out rounded-md hover:text-black hover:bg-veryLightGray',
|
||||
(shouldHighlight || shouldHighlightSubcategories) && 'bg-veryLightGray'
|
||||
)}
|
||||
key={pathToLink}
|
||||
>
|
||||
<Link href={pathToLink} passHref>
|
||||
<Text
|
||||
variant="a"
|
||||
color="greyscaleDark"
|
||||
size="normal"
|
||||
className={clsx(
|
||||
'py-1.5 px-3 block transition-colors duration-300 ease-in-out text-greyscaleDark hover:text-dark subpixel-antialiased block',
|
||||
(shouldHighlight || shouldHighlightSubcategories) && 'font-medium'
|
||||
)}
|
||||
onClick={onMenuSelected}
|
||||
>
|
||||
{post.title}
|
||||
</Text>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { ParsedUrlQuery } from 'querystring'
|
||||
import { createContext, PropsWithChildren, useContext } from 'react'
|
||||
|
||||
export type Post = {
|
||||
/**
|
||||
* Title of the post.
|
||||
*/
|
||||
title: string
|
||||
/**
|
||||
* File name where the post is located.
|
||||
*/
|
||||
fileName: string
|
||||
/**
|
||||
* Order of posts.
|
||||
*/
|
||||
order: string[]
|
||||
}
|
||||
|
||||
export type NavItem = {
|
||||
/**
|
||||
* Slug of the category.
|
||||
*/
|
||||
category: string
|
||||
/**
|
||||
* List of posts in the category.
|
||||
*/
|
||||
posts: Post[]
|
||||
}
|
||||
|
||||
export type NavDataContextProps = {
|
||||
/**
|
||||
* Category slug.
|
||||
*/
|
||||
category: string
|
||||
/**
|
||||
* The category title.
|
||||
*/
|
||||
categoryTitle: string
|
||||
/**
|
||||
* Convoluted navigation.
|
||||
*/
|
||||
convolutedNav: NavItem[]
|
||||
/**
|
||||
* Available menu items for all categories.
|
||||
*/
|
||||
availableCategoryMenus: {
|
||||
/**
|
||||
* Slug of the category.
|
||||
*/
|
||||
slug: string
|
||||
/**
|
||||
* Menu items of the category.
|
||||
*/
|
||||
items: NavItem[]
|
||||
}[]
|
||||
}
|
||||
|
||||
export const NavDataContext = createContext<NavDataContextProps>(null)
|
||||
|
||||
export function NavDataProvider({ children, ...props }: PropsWithChildren<NavDataContextProps>) {
|
||||
return <NavDataContext.Provider value={props}>{children}</NavDataContext.Provider>
|
||||
}
|
||||
|
||||
export function useNavData() {
|
||||
const context = useContext(NavDataContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error(`"useNavData" must be used within a "NavDataProvider"`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the navigation items for the specified category.
|
||||
*
|
||||
* @param slug Slug of the category.
|
||||
* @returns All of the navigation items for the specified category.
|
||||
*/
|
||||
function getConvolutedNavByCategory(slug: string) {
|
||||
return (
|
||||
context.availableCategoryMenus.find(({ slug: category }) => category === slug)?.items ||
|
||||
context.convolutedNav
|
||||
)
|
||||
}
|
||||
|
||||
return { getConvolutedNavByCategory, ...context }
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import AnchorLink from './AnchorLink'
|
||||
import CustomLink from './CustomLink'
|
||||
import createKebabCase from '../utils/createKebabCase'
|
||||
import Text from '@/components/ui/Text'
|
||||
|
||||
export default function Nav({ headings }: { headings: any }) {
|
||||
return (
|
||||
<div className="flex flex-col space-y-5 mt-9">
|
||||
{headings.map((heading, index) => {
|
||||
return (
|
||||
<NavLink
|
||||
category={heading.category}
|
||||
post={heading.post}
|
||||
headings={heading.content}
|
||||
key={heading.category + index}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NavLink({ category, headings, post }) {
|
||||
const href = `/${category}/${post.toLowerCase()}`
|
||||
return (
|
||||
<div className="mt-10 font-display" key={category}>
|
||||
<CustomLink href={href} activeClassName="active" key={category}>
|
||||
<Text variant="body" size="small" className="capitalize cursor-pointer text-grayscale">
|
||||
{post.split('-').join(' ')}
|
||||
</Text>
|
||||
</CustomLink>
|
||||
<ul className="space-y-1">
|
||||
{headings.map((heading: { value: string }) => {
|
||||
return (
|
||||
<li className="py-1 capitalize rounded-sm" key={heading.value}>
|
||||
<AnchorLink
|
||||
id={`/${category}/${post.toLowerCase()}#${createKebabCase(heading.value)}`}
|
||||
>{`/${heading.value}`}</AnchorLink>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
import axios from 'axios'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useState } from 'react'
|
||||
|
||||
import Input from './ui/Input/Input'
|
||||
import Loading from './ui/Loading'
|
||||
|
||||
function NewsletterForm(props) {
|
||||
return (
|
||||
<div className="flex flex-row w-64 mt-5">
|
||||
<form
|
||||
className="grid grid-flow-row sm:grid-flow-col gap-4"
|
||||
onSubmit={(e) => props.subscribe(e)}
|
||||
>
|
||||
<Input
|
||||
color="dark"
|
||||
placeholder="Email address"
|
||||
value={props.email}
|
||||
onChange={props.setEmail}
|
||||
type="email"
|
||||
/>
|
||||
<button
|
||||
className="btn-subscribe font-display text-greyscaleDark font-medium cursor-pointer"
|
||||
disabled={!props.email}
|
||||
>
|
||||
{!props.loading ? 'Subscribe' : <Loading />}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NewsletterError({ errorMessage, retry }) {
|
||||
const formattedError = errorMessage.includes('already a list member')
|
||||
? errorMessage.split('.').slice(0, 2).join('.')
|
||||
: errorMessage
|
||||
return (
|
||||
<div className="grid grid-flow-row md:grid-flow-col gap-4 mt-5">
|
||||
<p className="text-white font-normal text-sm mt-2.5">{formattedError}.</p>
|
||||
<button
|
||||
className="btn-subscribe font-display text-greyscaleDark font-medium cursor-pointer"
|
||||
onClick={() => {
|
||||
retry()
|
||||
}}
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Newsletter() {
|
||||
const [email, setEmail] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
const [success, setSuccess] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handleSubmit = async function (e) {
|
||||
e.preventDefault()
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
const res = await axios.post('/api/add-email-to-newsletter', {
|
||||
email
|
||||
})
|
||||
if (!res.data.success) {
|
||||
setError(res.data.message)
|
||||
return
|
||||
}
|
||||
setSuccess(res.data.message)
|
||||
} catch (error) {
|
||||
setError(
|
||||
error.message ||
|
||||
"We've encountered an error while subscribing you. Try again in a few seconds"
|
||||
)
|
||||
} finally {
|
||||
setEmail('')
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (success) {
|
||||
let id = setInterval(() => {
|
||||
setSuccess('')
|
||||
}, 5000)
|
||||
return () => clearInterval(id)
|
||||
}
|
||||
}, [success])
|
||||
|
||||
return (
|
||||
<div className="font-display flex flex-col mt-16">
|
||||
<div className="md:px-0 w-full mx-auto">
|
||||
<h1 className="font-medium text-gray-700 uppercase">newsletter</h1>
|
||||
<p className="text-white font-normal text-sm+ mt-2.5">
|
||||
Platform updates and news on web and mobile development.
|
||||
</p>
|
||||
{error ? (
|
||||
<NewsletterError errorMessage={error} retry={() => setError('')} />
|
||||
) : !success ? (
|
||||
<NewsletterForm
|
||||
email={email}
|
||||
setEmail={setEmail}
|
||||
subscribe={handleSubmit}
|
||||
loading={loading}
|
||||
/>
|
||||
) : (
|
||||
<NewsletterSuccess success={success} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NewsletterSuccess({ success }) {
|
||||
return (
|
||||
<div className="flex flex-row mt-5">
|
||||
<p className="text-white font-normal text-sm mt-2.5">{success}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import Text from '@/components/ui/Text'
|
||||
import React from 'react'
|
||||
|
||||
import Github from '@/components/icons/Github'
|
||||
import { DOCS_GITHUB_ENDPOINT } from '@/utils/constants'
|
||||
|
||||
export function PostMetadata(props) {
|
||||
return (
|
||||
<div className="mt-3 flex flex-row border-t pt-6 place-content-between px-3">
|
||||
<div className="flex flex-row">
|
||||
<Github className="text-blue" />
|
||||
<a
|
||||
className="text-blue text-xs ml-2 self-center"
|
||||
href={`${DOCS_GITHUB_ENDPOINT}${props.category}/${props.subcategory}/${props.post}.mdx`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Edit this page on GitHub
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
{props.frontmatter.updatedAt ? (
|
||||
<div className="flex">
|
||||
<Text size="tiny">Last updated on {props.frontmatter.updatedAt}</Text>
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import ArrowLeft from '@/components/icons/ArrowLeft'
|
||||
import ArrowRight from '@/components/icons/ArrowRight'
|
||||
import Text from '@/components/ui/Text/Text'
|
||||
import { orderTwo } from '@/lib/order'
|
||||
import { useRouter } from 'next/dist/client/router'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
export function SubNavigation({ category, subcategory, post, convolutedNav }) {
|
||||
const router = useRouter()
|
||||
const indexOfSubcategory = Object.keys(orderTwo[category]).indexOf(subcategory)
|
||||
const indexOfPreviousPost = orderTwo[category][subcategory].indexOf(post) - 1
|
||||
let indexOfCurrentPost = orderTwo[category][subcategory].indexOf(post)
|
||||
const previousPost = orderTwo[category][subcategory][indexOfCurrentPost - 1]
|
||||
let indexOfNextPost = orderTwo[category][subcategory].indexOf(post) + 1
|
||||
|
||||
if (!router.query.post) indexOfCurrentPost++ && indexOfNextPost++
|
||||
|
||||
const nextPost = orderTwo[category][subcategory][indexOfCurrentPost + 1]
|
||||
|
||||
const pathLink = `/${category}/${subcategory}/${previousPost === 'index' ? '' : previousPost}`
|
||||
|
||||
return (
|
||||
<div className="flex flex-row mt-10 place-content-between px-2 antialiased">
|
||||
<Link href={pathLink} passHref>
|
||||
<Text variant="a" color="blue" className="font-medium cursor-pointer" size="small">
|
||||
{indexOfCurrentPost === 0 || !router.query.post ? (
|
||||
<></>
|
||||
) : (
|
||||
<div className="flex flex-row self-center hover:-translate-x-1 transform transition-transform duration-500">
|
||||
<ArrowLeft className="self-center mr-1" />
|
||||
{convolutedNav[indexOfSubcategory].posts[indexOfPreviousPost].title}
|
||||
</div>
|
||||
)}
|
||||
</Text>
|
||||
</Link>
|
||||
|
||||
<Link href={`/${category}/${subcategory}/${nextPost}`} passHref>
|
||||
<Text variant="a" size="small" color="blue" className="font-medium cursor-pointer">
|
||||
{nextPost ? (
|
||||
<div className="flex flex-row self-center hover:translate-x-1 transform transition-transform duration-500">
|
||||
{convolutedNav[indexOfSubcategory].posts[indexOfNextPost].title}
|
||||
<ArrowRight className="self-center ml-1" />
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Text>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import SwaggerUI from 'swagger-ui-react'
|
||||
import 'swagger-ui-react/swagger-ui.css'
|
||||
|
||||
const OperationsLayout = (props) => {
|
||||
const { getComponent } = props
|
||||
const Operations = getComponent('operations', true)
|
||||
let SvgAssets = getComponent('SvgAssets')
|
||||
return (
|
||||
<div className="swagger-ui">
|
||||
<SvgAssets />
|
||||
<Operations />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const OperationsLayoutPlugin = () => ({
|
||||
components: {
|
||||
OperationsLayout
|
||||
}
|
||||
})
|
||||
|
||||
export const Swagger: React.FC<{ spec: string }> = ({ spec }) => (
|
||||
<SwaggerUI
|
||||
url={`/openapi/${spec}`}
|
||||
plugins={[OperationsLayoutPlugin]}
|
||||
layout="OperationsLayout"
|
||||
supportedSubmitMethods={[]}
|
||||
/>
|
||||
)
|
||||
@@ -1,25 +0,0 @@
|
||||
export function Tooltip({ text, children, position = '-mx-20', color = '' }) {
|
||||
return (
|
||||
<div className="relative has-tooltip">
|
||||
{children}
|
||||
|
||||
<span
|
||||
className={`z-50 px-1.5 py-0.5 text-sm bg-verydark -my-12 -mx-9 text-white rounded-sm shadow-2xl border tooltip font-medium`}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
<svg
|
||||
className="absolute z-50 w-3 h-2 text-verydark transform tooltip -top-2 right-0.5"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 255 255"
|
||||
xmlSpace="preserve"
|
||||
>
|
||||
<polygon
|
||||
className="border border-white fill-current text-lightbrand"
|
||||
points="0,0 127.5,127.5 255,0"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import CaretRight from '@/components/icons/CaretRight'
|
||||
import Text from '@/components/ui/Text/Text'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
export function TopNavigation(props) {
|
||||
const category = props.category.split('-').join(' ')
|
||||
|
||||
function uppercaseEdgeCases(subcategory) {
|
||||
switch (subcategory) {
|
||||
case 'sdk':
|
||||
return 'SDK'
|
||||
case 'cli':
|
||||
return 'CLI'
|
||||
default:
|
||||
return subcategory
|
||||
}
|
||||
}
|
||||
|
||||
const subcategory = props.subcategory.split('-').join(' ')
|
||||
|
||||
return (
|
||||
<div className="flex flex-row w-full">
|
||||
<Link href={`/${props.category}`} passHref>
|
||||
<Text
|
||||
variant="a"
|
||||
color="grey"
|
||||
className="self-center font-medium capitalize transition-colors duration-200 cursor-pointer hover:text-greyscaleDark"
|
||||
size="normal"
|
||||
>
|
||||
{category}
|
||||
</Text>
|
||||
</Link>
|
||||
<CaretRight className="self-center text-greyscaleGrey mx-1" />
|
||||
<Link href={`/${props.category}/${props.subcategory}`} passHref>
|
||||
<Text
|
||||
color="grey"
|
||||
className="self-center font-medium capitalize transition-colors duration-200 cursor-pointer hover:text-greyscaleDark"
|
||||
size="normal"
|
||||
>
|
||||
{uppercaseEdgeCases(subcategory)}
|
||||
</Text>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import * as React from 'react'
|
||||
|
||||
function ArrowLeft(props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width={16} height={16} fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M13.5 8h-11M7 3.5L2.5 8 7 12.5"
|
||||
stroke="#0052CD"
|
||||
strokeWidth={1.5}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default ArrowLeft
|
||||
@@ -1,16 +0,0 @@
|
||||
import * as React from 'react'
|
||||
|
||||
function ArrowRight(props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width={16} height={16} fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M2.5 8h11M9 3.5L13.5 8 9 12.5"
|
||||
stroke="#0052CD"
|
||||
strokeWidth={1.5}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default ArrowRight
|
||||
@@ -1,11 +0,0 @@
|
||||
import * as React from 'react'
|
||||
|
||||
function CaretRight(props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width={16} height={16} fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M6 3l5 5-5 5" stroke="currentColor" strokeWidth={1.5} strokeLinejoin="round" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default CaretRight
|
||||
@@ -1,11 +0,0 @@
|
||||
import * as React from 'react'
|
||||
|
||||
function Check(props: any) {
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M13.5 4.5l-7 7L3 8" stroke="currentColor" strokeWidth={2} strokeLinejoin="round" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Check
|
||||
@@ -1,11 +0,0 @@
|
||||
import * as React from 'react'
|
||||
|
||||
function Check2(props: any) {
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M27 9L13 23l-7-7" stroke="#0D3777" strokeWidth={2} strokeLinejoin="round" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Check2
|
||||
@@ -1,20 +0,0 @@
|
||||
const Copy = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Copy
|
||||
@@ -1,22 +0,0 @@
|
||||
const Github = ({ ...props }) => {
|
||||
return (
|
||||
<div className="cursor-pointer">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
{...props}
|
||||
>
|
||||
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Github
|
||||
@@ -1,23 +0,0 @@
|
||||
import * as React from 'react'
|
||||
|
||||
function GithubIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width={14}
|
||||
height={14}
|
||||
viewBox="0 0 14 14"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M7 0a7 7 0 00-2.213 13.642c.35.065.478-.152.478-.337 0-.166-.006-.607-.01-1.19-1.947.422-2.357-.94-2.357-.94-.319-.808-.778-1.023-.778-1.023-.635-.434.048-.426.048-.426.703.05 1.073.722 1.073.722.624 1.07 1.638.76 2.037.581.063-.452.244-.76.444-.935-1.554-.177-3.188-.778-3.188-3.46 0-.764.273-1.39.72-1.878-.072-.177-.312-.89.07-1.853 0 0 .586-.188 1.924.718A6.705 6.705 0 017 3.385c.595.003 1.194.08 1.753.236 1.336-.906 1.923-.718 1.923-.718.382.964.142 1.676.07 1.853a2.7 2.7 0 01.72 1.878c0 2.69-1.638 3.281-3.197 3.454.251.216.475.644.475 1.297 0 .935-.009 1.69-.009 1.92 0 .187.127.405.482.337A7 7 0 007 0z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default GithubIcon
|
||||
@@ -1,20 +0,0 @@
|
||||
const Help = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Help
|
||||
@@ -1,36 +0,0 @@
|
||||
import * as React from 'react'
|
||||
|
||||
function Logo(props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg viewBox="0 0 95 32" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<g clipPath="url(#prefix__clip0)">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M50.657 15.218h4.792v-3.801h2.535v9.752h-2.535v-3.802h-4.792v3.802H48.21v-9.752h2.446v3.801zm16.996-3.801H63.74c-1.084 0-1.744.204-2.243.703-.497.496-.688 1.126-.688 2.238v3.874c0 1.112.19 1.74.688 2.238.497.498 1.157.703 2.243.703h3.913c1.084 0 1.744-.205 2.243-.703.5-.496.688-1.126.688-2.238v-3.874c0-1.112-.19-1.74-.688-2.239-.5-.498-1.159-.702-2.243-.702zm.483 6.872c0 .57-.19.73-.879.73h-3.121c-.688 0-.88-.16-.88-.73v-3.992c0-.571.192-.731.88-.731h3.121c.705 0 .88.145.88.73v3.993zm8.512-2.97h3.501c1.084 0 1.758.205 2.243.703.439.44.688 1.155.688 1.93v.586c0 .776-.25 1.476-.688 1.93-.485.498-1.145.703-2.243.703h-6.681v-2.15h6.271c.688 0 .88-.16.88-.73v-.292c0-.571-.192-.731-.88-.731h-3.5c-1.087 0-1.76-.207-2.243-.703-.439-.44-.689-1.154-.689-1.93v-.585c0-.776.248-1.477.689-1.93.485-.499 1.142-.703 2.242-.703h6.333v2.149h-5.923c-.688 0-.879.16-.879.73v.292c0 .572.191.732.88.732zm7.983-1.753v-2.15h9.963v2.15h-3.75v7.603h-2.448v-7.603H84.63z"
|
||||
fill="#21324B"
|
||||
/>
|
||||
<path
|
||||
d="M42.355 11.34h-3.913c-1.084 0-1.744.204-2.243.703-.497.496-.688 1.126-.688 2.236v6.888h2.447V14.22c0-.57.191-.73.88-.73h3.121c.704 0 .879.146.879.73v6.948h2.448V14.28c0-1.112-.191-1.74-.689-2.236-.497-.5-1.156-.705-2.242-.705z"
|
||||
fill="#0052CD"
|
||||
/>
|
||||
<g clipPath="url(#prefix__clip1)">
|
||||
<path
|
||||
d="M27.208 6.858L16.055.43a3.268 3.268 0 00-3.246 0 3.245 3.245 0 00-1.621 2.803v.839l-.727-.42a3.268 3.268 0 00-3.246 0A3.246 3.246 0 005.594 6.46v.838l-.727-.419a3.268 3.268 0 00-3.245 0A3.246 3.246 0 000 9.683v20.136a1.526 1.526 0 002.47 1.195L8 26.66l8.53 4.914a1.548 1.548 0 001.526 0c.47-.272.763-.776.763-1.319V18.14a5.595 5.595 0 00-2.797-4.835l-2.797-1.612V3.236a1.21 1.21 0 011.815-1.045l11.153 6.425a3.562 3.562 0 011.78 3.076v15.089c0 .43-.232.83-.605 1.045l-2.955 1.703V14.914a5.595 5.595 0 00-2.797-4.834L14.75 6.125v2.343l5.849 3.37a3.559 3.559 0 011.78 3.076v15.492c0 .54.292 1.047.763 1.319a1.549 1.549 0 001.526 0l3.719-2.143c1-.577 1.622-1.65 1.622-2.805V11.688a5.609 5.609 0 00-2.801-4.83zM15 15.062a3.559 3.559 0 011.78 3.077v11.24L9.718 25.31l2.267-1.782a3.214 3.214 0 001.236-2.542V14.04l1.78 1.023zm-3.813-2.197v8.117c0 .373-.169.72-.461.948l-8.693 6.84V9.68a1.209 1.209 0 011.814-1.045L5.594 9.64v14.39l2.033-1.6V6.458a1.209 1.209 0 011.815-1.045l1.745 1.004v4.102L9.155 9.347v2.345l2.034 1.173z"
|
||||
fill="#0052CD"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="prefix__clip0">
|
||||
<path fill="#fff" d="M0 0h94.582v32H0z" />
|
||||
</clipPath>
|
||||
<clipPath id="prefix__clip1">
|
||||
<path fill="#fff" d="M0 0h30.009v31.927H0z" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Logo
|
||||
@@ -1,15 +0,0 @@
|
||||
const Permalink = ({ ...props }) => {
|
||||
return (
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M5.878 10.12l4.243-4.242M9.06 11.182L7.293 12.95A3 3 0 013.05 8.707l1.768-1.768M11.182 9.06l1.768-1.768A3 3 0 008.707 3.05L6.939 4.818"
|
||||
stroke="#21324B"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Permalink
|
||||
@@ -1,21 +0,0 @@
|
||||
import * as React from 'react'
|
||||
|
||||
function Vector(props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width={260}
|
||||
height={117}
|
||||
viewBox="0 0 260 117"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M35.5 1h1V0h-1v1zM254 87l-5.773 10h11.546L254 87zM.5 2h2.188V0H.5v2zm6.563 0h4.375V0H7.061v2zm8.75 0h4.374V0h-4.375v2zm8.75 0h4.375V0h-4.375v2zm8.75 0H35.5V0h-2.188v2zM34.5 1v1.982h2V1h-2zm0 5.946v3.965h2V6.946h-2zm0 7.929v3.964h2v-3.964h-2zm0 7.929v3.964h2v-3.964h-2zm0 7.928v3.964h2v-3.964h-2zm0 7.929v3.964h2v-3.964h-2zm0 7.928v3.965h2v-3.965h-2zm0 7.929v3.964h2v-3.964h-2zm0 7.928v3.965h2v-3.965h-2zm0 7.929v3.964h2v-3.964h-2zm0 7.929v3.964h2v-3.964h-2zm0 7.928v3.964h2v-3.964h-2zm0 7.929v3.964h2v-3.964h-2zm0 7.928v3.965h2v-3.965h-2zm0 7.929V112h2v-1.982h-2zm0 1.982c0 .676.135 1.323.38 1.914l1.847-.766A2.985 2.985 0 0136.5 112h-2zm3.086 4.62c.59.245 1.238.38 1.914.38v-2a2.99 2.99 0 01-1.148-.227l-.766 1.847zm1.914.38h2.024v-2H39.5v2zm6.072 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.097 0h4.048v-2H69.86v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0H90.1v-2h-4.048v2zm8.096 0h4.048v-2H94.15v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.097 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.049v-2h-4.049v2zm8.097 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.049v-2h-4.049v2zm8.097 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0h4.048v-2h-4.048v2zm8.096 0H250v-2h-2.024v2zm2.024 0c.676 0 1.323-.135 1.914-.38l-.766-1.847A2.987 2.987 0 01250 115v2zm4.62-3.086c.245-.591.38-1.238.38-1.914h-2c0 .409-.081.796-.227 1.148l1.847.766zM255 112v-2.083h-2V112h2zm0-6.25v-4.167h-2v4.167h2zm0-8.333V93.25h-2v4.167h2z"
|
||||
fill="#C2CAD6"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Vector
|
||||
@@ -1,97 +0,0 @@
|
||||
.root {
|
||||
@apply font-display flex px-2 py-1.6;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
line-height: 22px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.primary {
|
||||
@apply bg-blue font-display flex font-medium text-white;
|
||||
font-size: 15px;
|
||||
border-radius: 4px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.danger {
|
||||
@apply py-2.5 px-2.5 text-red font-display font-medium;
|
||||
height: 36px;
|
||||
border-radius: 4px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border: 1px solid #c2cad6;
|
||||
}
|
||||
|
||||
.blue {
|
||||
@apply text-blue;
|
||||
}
|
||||
|
||||
.red {
|
||||
@apply text-red;
|
||||
}
|
||||
|
||||
.root:focus {
|
||||
@apply outline-none;
|
||||
}
|
||||
|
||||
.root[data-active] {
|
||||
@apply bg-accent-6;
|
||||
}
|
||||
|
||||
.loading {
|
||||
@apply bg-accent-1 text-accent-3 border-accent-2 cursor-not-allowed;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
@apply font-display text-greyscaleDark bg-white;
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
border: 1px solid #c2cad6;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.menu {
|
||||
@apply font-display text-greyscaleDark cursor-pointer;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
}
|
||||
.dark {
|
||||
@apply font-medium text-white;
|
||||
background: #21324b;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.dark .disabled {
|
||||
@apply font-medium text-white;
|
||||
background: #21324b;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.secondary .disabled {
|
||||
@apply font-medium;
|
||||
color: rgba(9, 34, 72, 0.4);
|
||||
}
|
||||
.disabled,
|
||||
.disabled:hover {
|
||||
@apply text-gray-400 cursor-not-allowed;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.small {
|
||||
width: 51px;
|
||||
height: 24px;
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
.transparent {
|
||||
@apply px-0 py-1 bg-transparent border-0;
|
||||
}
|
||||
.border {
|
||||
@apply px-2 py-1.5;
|
||||
border: 1px solid #c2cad6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import cn from 'classnames'
|
||||
import React, { ButtonHTMLAttributes, forwardRef, JSXElementConstructor, useRef } from 'react'
|
||||
import mergeRefs from 'react-merge-refs'
|
||||
|
||||
import s from './Button.module.css'
|
||||
|
||||
// import Loading from "../components/ui/Loading";
|
||||
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
href?: string
|
||||
className?: string
|
||||
variant?: 'primary' | 'dark' | 'secondary' | 'menu' | 'danger'
|
||||
color?: 'blue' | 'red'
|
||||
active?: boolean
|
||||
type?: 'submit' | 'reset' | 'button'
|
||||
Component?: string | JSXElementConstructor<any>
|
||||
width?: string | number
|
||||
loading?: boolean
|
||||
disabled?: boolean
|
||||
small?: boolean
|
||||
transparent?: boolean
|
||||
target?: string
|
||||
rel?: string
|
||||
onClick?: any
|
||||
border?: boolean
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const Button: React.FC<ButtonProps> = forwardRef((props, buttonRef) => {
|
||||
const {
|
||||
className,
|
||||
variant,
|
||||
children,
|
||||
active,
|
||||
width,
|
||||
small,
|
||||
href,
|
||||
color,
|
||||
border,
|
||||
loading = false,
|
||||
disabled = false,
|
||||
transparent = false,
|
||||
style = {},
|
||||
type = 'button',
|
||||
Component = 'button',
|
||||
...rest
|
||||
} = props
|
||||
const ref = useRef<typeof Component>(null)
|
||||
|
||||
const rootClassName = cn(
|
||||
s.root,
|
||||
{
|
||||
[s.primary]: variant === 'primary',
|
||||
[s.secondary]: variant === 'secondary',
|
||||
[s.menu]: variant === 'menu',
|
||||
[s.dark]: variant === 'dark',
|
||||
[s.danger]: variant === 'danger',
|
||||
[s.loading]: loading,
|
||||
[s.disabled]: disabled,
|
||||
[s.small]: small,
|
||||
[s.transparent]: transparent,
|
||||
[s.blue]: color === 'blue',
|
||||
[s.red]: color === 'red',
|
||||
[s.border]: border
|
||||
},
|
||||
className
|
||||
)
|
||||
|
||||
return (
|
||||
<Component
|
||||
aria-pressed={active}
|
||||
data-variant={variant}
|
||||
ref={mergeRefs([ref, buttonRef])}
|
||||
className={rootClassName}
|
||||
disabled={disabled}
|
||||
type={type}
|
||||
href={href}
|
||||
style={{
|
||||
width,
|
||||
...style
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
)
|
||||
})
|
||||
|
||||
export default Button
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './Button'
|
||||
@@ -1,31 +0,0 @@
|
||||
.root {
|
||||
@apply border-input text-dark focus:ring-dark focus:border-dark flex px-2 py-2 text-xs rounded-md shadow-sm;
|
||||
border: 1px solid #c2cad6;
|
||||
}
|
||||
|
||||
.root:focus {
|
||||
@apply outline-none;
|
||||
}
|
||||
|
||||
.dark {
|
||||
flex: none;
|
||||
width: 247px;
|
||||
height: 36px;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
color: white;
|
||||
|
||||
/* Translucent white/White light (20) */
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
|
||||
/* Inside Auto Layout */
|
||||
order: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.disabled,
|
||||
.disabled:hover {
|
||||
@apply text-accent-3 cursor-not-allowed;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import cn from 'classnames'
|
||||
import s from './Input.module.css'
|
||||
import React, { InputHTMLAttributes } from 'react'
|
||||
|
||||
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
className?: string
|
||||
onChange?: (...args: any[]) => any
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
type Color = 'dark' | 'light'
|
||||
|
||||
const Input: React.FC<InputProps> = (props) => {
|
||||
const { className, children, onChange, color, disabled, placeholder = '', ...rest } = props
|
||||
|
||||
const rootClassName = cn(
|
||||
s.root,
|
||||
{ [s.disabled]: disabled, [s.dark]: color === 'dark' },
|
||||
className
|
||||
)
|
||||
|
||||
const handleOnChange = (e: any) => {
|
||||
if (onChange) {
|
||||
onChange(e.target.value)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
className={rootClassName}
|
||||
onChange={handleOnChange}
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
placeholder={placeholder}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Input
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './Input'
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<svg
|
||||
className="w-5 h-5 text-dark animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
.body {
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.dark {
|
||||
@apply text-dark;
|
||||
}
|
||||
|
||||
.greyscaleDark {
|
||||
@apply text-greyscaleDark;
|
||||
}
|
||||
|
||||
.white {
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
.greyscaleGrey {
|
||||
@apply text-greyscaleGrey;
|
||||
}
|
||||
|
||||
.grey {
|
||||
@apply text-grayscale;
|
||||
}
|
||||
|
||||
.red {
|
||||
@apply text-red;
|
||||
}
|
||||
|
||||
.blue {
|
||||
@apply text-blue;
|
||||
}
|
||||
|
||||
.heading {
|
||||
@apply font-display;
|
||||
}
|
||||
|
||||
.pageHeading {
|
||||
@apply pt-1 pb-4 text-2xl font-bold leading-7 tracking-wide;
|
||||
}
|
||||
|
||||
.a {
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.tiny {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.normal {
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.large {
|
||||
font-size: 18px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.big {
|
||||
font-size: 26px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: 36px;
|
||||
line-height: 48px;
|
||||
}
|
||||
|
||||
.subHeading {
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sectionHeading {
|
||||
@apply text-lg font-medium font-display;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.item {
|
||||
font-family: Inter;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
import cn from 'classnames'
|
||||
import React, {
|
||||
CSSProperties,
|
||||
ForwardedRef,
|
||||
forwardRef,
|
||||
FunctionComponent,
|
||||
JSXElementConstructor
|
||||
} from 'react'
|
||||
import mergeRefs from 'react-merge-refs'
|
||||
import s from './Text.module.css'
|
||||
|
||||
export interface TextProps {
|
||||
variant?: Variant
|
||||
className?: string
|
||||
style?: CSSProperties
|
||||
children?: React.ReactNode | any
|
||||
color?: Color
|
||||
html?: string
|
||||
size?: Size
|
||||
target?: any
|
||||
rel?: any
|
||||
href?: string
|
||||
onClick?: () => any
|
||||
name?: any
|
||||
}
|
||||
|
||||
type Variant = 'heading' | 'body' | 'pageHeading' | 'sectionHeading' | 'item' | 'subHeading' | 'a'
|
||||
|
||||
type Size = 'tiny' | 'small' | 'normal' | 'large' | 'big' | 'heading'
|
||||
|
||||
type Color = 'dark' | 'grey' | 'blue' | 'greyscaleDark' | 'greyscaleGrey' | 'red' | 'white'
|
||||
|
||||
export const Text: FunctionComponent<TextProps> = forwardRef(function DefaultText(
|
||||
{
|
||||
style,
|
||||
className = '',
|
||||
variant = 'body',
|
||||
color,
|
||||
children,
|
||||
html,
|
||||
onClick,
|
||||
size,
|
||||
rel,
|
||||
href,
|
||||
target,
|
||||
name
|
||||
},
|
||||
ref: ForwardedRef<HTMLElement>
|
||||
) {
|
||||
const componentsMap: {
|
||||
[P in Variant]: React.ComponentType<any> | string
|
||||
} = {
|
||||
body: 'div',
|
||||
heading: 'h1',
|
||||
pageHeading: 'h1',
|
||||
sectionHeading: 'h2',
|
||||
subHeading: 'h3',
|
||||
item: 'p',
|
||||
a: 'a'
|
||||
}
|
||||
|
||||
const Component:
|
||||
| JSXElementConstructor<any>
|
||||
| React.ReactElement<any>
|
||||
| React.ComponentType<any>
|
||||
| string = componentsMap![variant!]
|
||||
|
||||
const htmlContentProps = html
|
||||
? {
|
||||
dangerouslySetInnerHTML: { __html: html }
|
||||
}
|
||||
: {}
|
||||
|
||||
const aProps =
|
||||
variant === 'a'
|
||||
? {
|
||||
rel,
|
||||
href,
|
||||
target
|
||||
}
|
||||
: {}
|
||||
|
||||
return (
|
||||
<Component
|
||||
ref={ref}
|
||||
className={cn(
|
||||
s.root,
|
||||
{
|
||||
[s.body]: variant === 'body',
|
||||
[s.a]: variant === 'a',
|
||||
[s.heading]: variant === 'heading',
|
||||
[s.pageHeading]: variant === 'pageHeading',
|
||||
[s.sectionHeading]: variant === 'sectionHeading',
|
||||
[s.subHeading]: variant === 'subHeading',
|
||||
[s.item]: variant === 'item',
|
||||
[s.dark]: color === 'dark',
|
||||
[s.greyscaleDark]: color === 'greyscaleDark',
|
||||
[s.grey]: color === 'grey',
|
||||
[s.blue]: color === 'blue',
|
||||
[s.tiny]: size === 'tiny',
|
||||
[s.small]: size === 'small',
|
||||
[s.normal]: size === 'normal',
|
||||
[s.large]: size === 'large',
|
||||
[s.big]: size === 'big',
|
||||
[s.heading]: size === 'heading',
|
||||
[s.greyscaleGrey]: color === 'greyscaleGrey',
|
||||
[s.red]: color === 'red',
|
||||
[s.white]: color === 'white'
|
||||
},
|
||||
className
|
||||
)}
|
||||
onClick={onClick}
|
||||
style={style}
|
||||
{...htmlContentProps}
|
||||
{...aProps}
|
||||
name={name}
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
)
|
||||
})
|
||||
|
||||
export default Text
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './Text'
|
||||
export { default } from './Text'
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './Button/Button'
|
||||
export * from './Text/Text'
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
title: 'The Nhost platform'
|
||||
---
|
||||
|
||||
This section:
|
||||
|
||||
### Database
|
||||
|
||||
- [Schema](/platform/database)
|
||||
- [Permissions](/platform/database/permissions)
|
||||
- [GraphQL](/platform/database/graphql)
|
||||
|
||||
### Authentication
|
||||
|
||||
- [Authentication overview](/platform/authentication)
|
||||
- [User management](/platform/authentication/user-management)
|
||||
- [Sign-in methods](/platform/authentication/sign-in-methods)
|
||||
- [Social login](/platform/authentication/social-login)
|
||||
- [Email templates](/platform/authentication/email-templates)
|
||||
|
||||
### Storage
|
||||
|
||||
- [File storage](/platform/storage)
|
||||
|
||||
### Serverless functions
|
||||
|
||||
- [Creating functions](/platform/serverless-functions)
|
||||
- [Event triggers](/platform/serverless-functions/event-triggers)
|
||||
|
||||
### Nhost
|
||||
|
||||
- [Environment variables](/platform/nhost/environment-variables)
|
||||
- [GitHub integration](/platform/nhost/github-integration)
|
||||
- [Local development](/platform/nhost/local-development)
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
title: 'API Reference'
|
||||
subtitle: 'Hasura Auth'
|
||||
---
|
||||
|
||||
<Swagger spec="hasura-auth.json" />
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
title: 'Reference'
|
||||
---
|
||||
|
||||
In this section:
|
||||
|
||||
### Nhost SDK
|
||||
|
||||
- [Overview](/reference/sdk)
|
||||
- [GraphQL](/reference/sdk/graphql)
|
||||
- [Authentication](/reference/sdk/authentication)
|
||||
- [Storage](/reference/sdk/storage)
|
||||
- [Functions](/reference/sdk/functions)
|
||||
|
||||
### React
|
||||
|
||||
- [Getting started](./reference/react)
|
||||
- [Hooks](./reference/react/hooks)
|
||||
- [Protecting routes](./reference/react/protecting-routes)
|
||||
- [Apollo GraphQL](./reference/react/apollo)
|
||||
|
||||
### Next.js
|
||||
|
||||
- [Introduction](./reference/nextjs)
|
||||
- [Configuration](./reference/nextjs/configuration)
|
||||
- [Protecting routes](./reference/nextjs/protecting-routes)
|
||||
|
||||
### Nhost CLI
|
||||
|
||||
- [CLI overview](/reference/cli)
|
||||
### Hasura Auth
|
||||
|
||||
- [Overview](./reference/hasura-auth)
|
||||
- [Installation](./reference/hasura-auth/installation)
|
||||
- [Configuration](./reference/hasura-auth/configuration)
|
||||
- [Environment variables](./reference/hasura-auth/environment-variables)
|
||||
- [API](./reference/hasura-auth/api-reference)
|
||||
- [Schema](./reference/hasura-auth/api-reference)
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
title: 'Protecting routes'
|
||||
---
|
||||
|
||||
Create a `auth-protected.js` file:
|
||||
|
||||
```jsx
|
||||
import { useRouter } from 'next/router'
|
||||
import { useAuthenticationStatus } from '@nhost/nextjs'
|
||||
|
||||
export function authProtected(Comp) {
|
||||
return function AuthProtected(props) {
|
||||
const router = useRouter()
|
||||
const { isLoading, isAuthenticated } = useAuthenticationStatus()
|
||||
|
||||
if (isLoading) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
router.push('/login')
|
||||
return null
|
||||
}
|
||||
|
||||
return <Comp {...props} />
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then wrap the Next.js page with `authProtected` to only allow signed in users to access the page.
|
||||
|
||||
```js
|
||||
import { authProtected } from '<some-path>/auth-protected'
|
||||
|
||||
function Index() {
|
||||
return <div>Only signed in users can access this page.</div>
|
||||
}
|
||||
|
||||
export default authProtected(Index)
|
||||
```
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
title: 'Protecting routes'
|
||||
---
|
||||
|
||||
## React Router
|
||||
|
||||
You can protect routes by creating an `AuthGate` component when using `@nhost/react` with [React Router](https://reactrouter.com/web/guides/quick-start).
|
||||
|
||||
```jsx
|
||||
import { Redirect } from 'react-router-dom'
|
||||
import { useAuthenticationStatus } from '@nhost/react'
|
||||
|
||||
export function AuthGate(children) {
|
||||
const { isLoading, isAuthenticated } = useAuthenticationStatus()
|
||||
|
||||
if (isLoading) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return <Redirect to="/login" />
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
```
|
||||
|
||||
Then, in your React Router, wrap the `AuthGate` component around the routes you want to protect:
|
||||
|
||||
```jsx
|
||||
<Router>
|
||||
<Switch>
|
||||
<Route path="/login">
|
||||
<Login />
|
||||
</Route>
|
||||
<Route path="/" exact>
|
||||
<AuthGate> // <--- Use AuthGate component like this
|
||||
<div>My protected dashboard</div>
|
||||
</AuthGate>
|
||||
</Route>
|
||||
</Switch>
|
||||
</Router>
|
||||
```
|
||||
@@ -1,112 +0,0 @@
|
||||
export const nhostTheme = {
|
||||
hljs: {
|
||||
display: 'block',
|
||||
background: '#F4F7F9',
|
||||
color: '#21324B'
|
||||
},
|
||||
'hljs-tag': {
|
||||
color: '#9C73DF'
|
||||
},
|
||||
'hljs-keyword': {
|
||||
color: '#9C73DF',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-selector-tag': {
|
||||
color: '#9C73DF',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-literal': {
|
||||
color: '#9C73DF',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-strong': {
|
||||
color: '#9C73DF'
|
||||
},
|
||||
'hljs-name': {
|
||||
color: '#9C73DF'
|
||||
},
|
||||
'hljs-code': {
|
||||
color: '#66d9ef'
|
||||
},
|
||||
'hljs-class .hljs-title': {
|
||||
color: 'red'
|
||||
},
|
||||
'hljs-attribute': {
|
||||
color: '#bf79db'
|
||||
},
|
||||
'hljs-symbol': {
|
||||
color: '#bf79db'
|
||||
},
|
||||
'hljs-regexp': {
|
||||
color: '#bf79db'
|
||||
},
|
||||
'hljs-link': {
|
||||
color: '#bf79db'
|
||||
},
|
||||
'hljs-string': {
|
||||
color: '#B7A590'
|
||||
},
|
||||
'hljs-bullet': {
|
||||
color: '#B7A590'
|
||||
},
|
||||
'hljs-subst': {
|
||||
color: '#B7A590'
|
||||
},
|
||||
'hljs-title': {
|
||||
color: '#B7A590',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-section': {
|
||||
color: '#B7A590',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-emphasis': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-type': {
|
||||
color: '#3ECF8E',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-built_in': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-builtin-name': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-selector-attr': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-selector-pseudo': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-addition': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-variable': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-template-tag': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-template-variable': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-comment': {
|
||||
color: '#75715e'
|
||||
},
|
||||
'hljs-quote': {
|
||||
color: '#75715e'
|
||||
},
|
||||
'hljs-deletion': {
|
||||
color: '#75715e'
|
||||
},
|
||||
'hljs-meta': {
|
||||
color: '#75715e'
|
||||
},
|
||||
'hljs-doctag': {
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-selector-id': {
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
export const lightNhostTheme = {
|
||||
hljs: {
|
||||
display: 'block',
|
||||
background: '#F4F7F9',
|
||||
color: '#21324B'
|
||||
},
|
||||
'hljs-tag': {
|
||||
color: '#9C73DF'
|
||||
},
|
||||
'hljs-keyword': {
|
||||
color: '#9C73DF',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-selector-tag': {
|
||||
color: '#9C73DF',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-literal': {
|
||||
color: '#9C73DF',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-strong': {
|
||||
color: '#9C73DF'
|
||||
},
|
||||
'hljs-name': {
|
||||
color: '#9C73DF'
|
||||
},
|
||||
'hljs-code': {
|
||||
color: '#66d9ef'
|
||||
},
|
||||
'hljs-class .hljs-title': {
|
||||
color: 'red'
|
||||
},
|
||||
'hljs-attribute': {
|
||||
color: '#bf79db'
|
||||
},
|
||||
'hljs-symbol': {
|
||||
color: '#bf79db'
|
||||
},
|
||||
'hljs-regexp': {
|
||||
color: '#bf79db'
|
||||
},
|
||||
'hljs-link': {
|
||||
color: '#bf79db'
|
||||
},
|
||||
'hljs-string': {
|
||||
color: '#B7A590'
|
||||
},
|
||||
'hljs-bullet': {
|
||||
color: '#B7A590'
|
||||
},
|
||||
'hljs-subst': {
|
||||
color: '#B7A590'
|
||||
},
|
||||
'hljs-title': {
|
||||
color: '#B7A590',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-section': {
|
||||
color: '#B7A590',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-emphasis': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-type': {
|
||||
color: '#3ECF8E',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-built_in': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-builtin-name': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-selector-attr': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-selector-pseudo': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-addition': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-variable': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-template-tag': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-template-variable': {
|
||||
color: '#3ECF8E'
|
||||
},
|
||||
'hljs-comment': {
|
||||
color: '#75715e'
|
||||
},
|
||||
'hljs-quote': {
|
||||
color: '#75715e'
|
||||
},
|
||||
'hljs-deletion': {
|
||||
color: '#75715e'
|
||||
},
|
||||
'hljs-meta': {
|
||||
color: '#75715e'
|
||||
},
|
||||
'hljs-doctag': {
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'hljs-selector-id': {
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"order": [
|
||||
{
|
||||
"id": "get-started",
|
||||
"name": "get-started",
|
||||
"description": "get-started",
|
||||
"pages": [
|
||||
{
|
||||
"route": "quickstart",
|
||||
"display": "Quick Start"
|
||||
},
|
||||
{
|
||||
"route": "app-development",
|
||||
"display": "App Development"
|
||||
},
|
||||
{
|
||||
"route": "workflow",
|
||||
"display": "Workflow"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
export interface Meta {
|
||||
order: CategoryLink[]
|
||||
}
|
||||
|
||||
export interface CategoryLink {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
pages: Page[]
|
||||
}
|
||||
|
||||
interface Page {
|
||||
route: string
|
||||
display: string
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"siteLinks": [
|
||||
{
|
||||
"text": "product",
|
||||
"links": [
|
||||
{
|
||||
"name": "Product",
|
||||
"href": "https://nhost.io/#product"
|
||||
},
|
||||
{
|
||||
"name": "Features",
|
||||
"href": "https://nhost.io/#features"
|
||||
},
|
||||
{
|
||||
"name": "Pricing",
|
||||
"href": "https://nhost.io/pricing"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "docs",
|
||||
"links": [
|
||||
{
|
||||
"name": "Get Started",
|
||||
"href": "/get-started"
|
||||
},
|
||||
{
|
||||
"name": "Platform",
|
||||
"href": "/platform"
|
||||
},
|
||||
{
|
||||
"name": "Reference",
|
||||
"href": "/reference"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "Frameworks",
|
||||
"links": [
|
||||
{
|
||||
"name": "API Reference",
|
||||
"href": "/reference"
|
||||
},
|
||||
{
|
||||
"name": "JS SDK",
|
||||
"href": "/reference/sdk"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "community",
|
||||
"links": [
|
||||
{
|
||||
"name": "GitHub",
|
||||
"href": "https://github.com/nhost"
|
||||
},
|
||||
{
|
||||
"name": "Discord",
|
||||
"href": "https://discord.com/invite/9V7Qb2U"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
export interface SiteLinks {
|
||||
siteLinks: SiteLink[]
|
||||
}
|
||||
|
||||
interface SiteLink {
|
||||
text: string
|
||||
links: string[]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: 'Authenticate users'
|
||||
slug: /get-started/authentication
|
||||
---
|
||||
|
||||
You defined `select` permissions for the `public` role in the previous section. You will now add `insert` and `create` permissions for authenticated users to secure your app's GraphQL API with authentication.
|
||||
@@ -12,7 +13,7 @@ You defined `select` permissions for the `public` role in the previous section.
|
||||
|
||||
Manually create a user by going to your app's **Users** tab (top menu) and clicking on **Add User**.
|
||||
|
||||

|
||||

|
||||
|
||||
You will now use that newly created user to make authenticated requests to the API.
|
||||
|
||||
@@ -23,20 +24,20 @@ You will now use that newly created user to make authenticated requests to the A
|
||||
Add the following code to sign in the new user and request the list of todos again:
|
||||
|
||||
```js
|
||||
import { NhostClient } from '@nhost/nhost-js'
|
||||
import { NhostClient } from '@nhost/nhost-js';
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: 'https://[app-subdomain].nhost.run'
|
||||
backendUrl: 'https://[app-subdomain].nhost.run',
|
||||
})(async () => {
|
||||
// Sign in user
|
||||
const signInResponse = await nhost.auth.signIn({
|
||||
email: 'joe@example.com',
|
||||
password: 'securepassword'
|
||||
})
|
||||
password: 'securepassword',
|
||||
});
|
||||
|
||||
// Handle sign-in error
|
||||
if (signInResponse.error) {
|
||||
throw signInResponse.error
|
||||
throw signInResponse.error;
|
||||
}
|
||||
|
||||
// Get todos
|
||||
@@ -49,10 +50,10 @@ const nhost = new NhostClient({
|
||||
is_completed
|
||||
}
|
||||
}
|
||||
`)
|
||||
`);
|
||||
|
||||
console.log(JSON.stringify(todos.data, null, 2))
|
||||
})()
|
||||
console.log(JSON.stringify(todos.data, null, 2));
|
||||
})();
|
||||
```
|
||||
|
||||
Why is the return value `null`? Because when making GraphQL requests as an authenticated user, the `user` role is assumed.
|
||||
@@ -67,7 +68,7 @@ Why is the return value `null`? Because when making GraphQL requests as an authe
|
||||
|
||||
We won't use the `public` role anymore, so let's remove all permission for that role.
|
||||
|
||||

|
||||

|
||||
|
||||
Now we'll add permissions for the `user` role.
|
||||
|
||||
@@ -79,12 +80,12 @@ First, we'll set the **Insert permission**.
|
||||
|
||||
A user can only insert `name` because all other columns will be set automatically. More specifically, `user_id` is set to the user's id making the request (`x-hasura-user-id`) and is configured in the `Column presets` section. See the image below.
|
||||
|
||||

|
||||

|
||||
|
||||
### Select permission
|
||||
|
||||
For **Select permission**, set a **custom check** so users can only select todos where `user_id` is the same as their user id. In other words: users are only allowed to select their own todos. See the image below.
|
||||
|
||||

|
||||

|
||||
|
||||
Now rerun the app. New todos are inserted, and only todos for the user are fetched and displayed. Your backend is successfully secured!
|
||||
4
docs/docs/get-started/cli-workflow/_category_.json
Normal file
4
docs/docs/get-started/cli-workflow/_category_.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "CLI",
|
||||
"position": 8
|
||||
}
|
||||
@@ -12,9 +12,9 @@ In the previous tutorials, we tested various parts of Nhost, such as:
|
||||
|
||||
All changes we did to our database and API happened directly in production of our Nhost app.
|
||||
|
||||
It’s not ideal for making changes in production because you might break things, which will affect all users of your app.
|
||||
It's not ideal for making changes in production because you might break things, which will affect all users of your app.
|
||||
|
||||
Instead, it’s recommended to make changes and test your app locally before deploying those changes to production.
|
||||
Instead, it's recommended to make changes and test your app locally before deploying those changes to production.
|
||||
|
||||
To do changes locally, we need to have a complete Nhost app running locally, which the Nhost CLI does.
|
||||
|
||||
@@ -26,8 +26,8 @@ The Nhost CLI matches your production application in a local environment, this w
|
||||
2. Push changes to GitHub.
|
||||
3. Nhost automatically applies changes to production.
|
||||
|
||||
## What you’ll learn in this guide:
|
||||
## What you'll learn in this guide:
|
||||
|
||||
- Use the Nhost CLI to create a local environment
|
||||
- Connect a GitHub repository with a Nhost app
|
||||
- Deploy local changes to production
|
||||
- Deploy local changes to production
|
||||
@@ -4,13 +4,13 @@ title: 'Install the CLI'
|
||||
|
||||
Install the Nhost CLI using the following command:
|
||||
|
||||
```sql
|
||||
```bash
|
||||
sudo curl -L https://raw.githubusercontent.com/nhost/cli/main/get.sh | bash
|
||||
```
|
||||
|
||||
Initialize a new Nhost App locally:
|
||||
|
||||
```sql
|
||||
```bash
|
||||
nhost init -n "nhost-example-app" && cd nhost-example-app
|
||||
```
|
||||
|
||||
@@ -28,10 +28,10 @@ git push -u origin main
|
||||
|
||||
Now go back to the **Nhost Console** and click **Deployments**. You just made a new deployment to your Nhost app!
|
||||
|
||||

|
||||

|
||||
|
||||
If you click on the deployment you can see that nothing was really deployed. That’s because we just made a change to the README file.
|
||||
|
||||

|
||||

|
||||
|
||||
Let's do some local backend changes!
|
||||
@@ -4,11 +4,13 @@ title: 'Local changes'
|
||||
|
||||
Start Nhost locally:
|
||||
|
||||
```sql
|
||||
```bash
|
||||
nhost dev
|
||||
```
|
||||
|
||||
>💡 Make sure you have [Docker](https://www.docker.com/get-started) installed on your computer. It’s required for Nhost to work.
|
||||
:::tip
|
||||
Make sure you have [Docker](https://www.docker.com/get-started) installed on your computer. It’s required for Nhost to work.
|
||||
:::
|
||||
|
||||
The `nhost dev` command will automatically start a complete Nhost environment locally on your computer using:
|
||||
|
||||
@@ -23,18 +25,22 @@ You use this local environment to do changes and testing before you deploy your
|
||||
|
||||
Running `nhost dev` also starts the Hasura Console.
|
||||
|
||||
:::tip
|
||||
It's important that you use the Hasura Console that is started automatically when you do changes. This way, changes are automatically tracked for you.
|
||||
:::
|
||||
|
||||
>💡 It’s important that you use the Hasura Console that is started automatically when you do changes. This way, changes are automatically tracked for you.
|
||||
|
||||

|
||||

|
||||
|
||||
In the Hasura Console, create a new table `customers` with two columns:
|
||||
|
||||
- id
|
||||
- name
|
||||
|
||||
<Video src="/videos/cli-workflow/hasura-create-customers-table.mp4">
|
||||
</Video>
|
||||
<video
|
||||
src="/videos/cli-workflow/hasura-create-customers-table.mp4"
|
||||
width="100%"
|
||||
controls
|
||||
/>
|
||||
|
||||
When we created the `customers` table there was also a migration created automatically. The migration was created at under `nhost/migrations/default`.
|
||||
|
||||
@@ -50,12 +56,11 @@ This database migration has only been applied locally, meaning, you created the
|
||||
|
||||
To apply the local change to production we need to commit the changes and push it to GitHub. Nhost will then automatically pick up the change in the repository and apply the changes.
|
||||
|
||||
<aside>
|
||||
💡 You can commit and push files in another terminal while still having `nhost dev` running.
|
||||
:::tip
|
||||
You can commit and push files in another terminal while still having `nhost dev` running.
|
||||
:::
|
||||
|
||||
</aside>
|
||||
|
||||
```sql
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "Initialized Nhost and added a customers table"
|
||||
git push
|
||||
@@ -63,14 +68,14 @@ git push
|
||||
|
||||
Head over to the **Deployments** tab in the **Nhost console** to see the deployment.
|
||||
|
||||

|
||||

|
||||
|
||||
Once the deployment finishes the `customers` table is created in production.
|
||||
|
||||

|
||||

|
||||
|
||||
We’ve now completed the recommended workflow with Nhost:
|
||||
We've now completed the recommended workflow with Nhost:
|
||||
|
||||
1. Develop locally using the Nhost CLI.
|
||||
2. Push changes to GitHub.
|
||||
3. Nhost deploys changes to production.
|
||||
3. Nhost deploys changes to production.
|
||||
@@ -2,6 +2,9 @@
|
||||
title: 'Metadata and Serverless Functions'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
In the previous section, we only created a new table; `customers`. Using the CLI you can also do changes to other parts of your backend.
|
||||
|
||||
There are three things the CLI and the GitHub integration track and applies to production:
|
||||
@@ -10,18 +13,18 @@ There are three things the CLI and the GitHub integration track and applies to p
|
||||
2. Hasura Metadata
|
||||
3. Serverless Functions
|
||||
|
||||
For this section, let’s do one change to the Hasura metadata and create one serverless function
|
||||
For this section, let's do one change to the Hasura metadata and create one serverless function
|
||||
|
||||
### Hasura Metadata
|
||||
|
||||
We’ll add permissions to the `users` table, making sure users can only see their own data. For this, go to the `auth` schema and click on the `users` table. then click on **Permissions** and enter a new role **user** and create a new **select** permission for that role**.**
|
||||
We'll add permissions to the `users` table, making sure users can only see their own data. For this, go to the `auth` schema and click on the `users` table. then click on **Permissions** and enter a new role **user** and create a new **select** permission for that role**.**
|
||||
|
||||
Create the permission **with custom check**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": {
|
||||
"_eq" : "X-Hasura-User-Id"
|
||||
"_eq": "X-Hasura-User-Id"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -36,12 +39,15 @@ Select the following columns:
|
||||
|
||||
Then click **Save permissions**.
|
||||
|
||||
<Video src="/videos/cli-workflow/hasura-user-permissions.mp4">
|
||||
</Video>
|
||||
<video
|
||||
src="/videos/cli-workflow/hasura-user-permissions.mp4"
|
||||
width="100%"
|
||||
controls
|
||||
/>
|
||||
|
||||
Now, let’s do a `git status` again to confirm the permission changes we did was tracked locally in your git repository.
|
||||
Now, let's do a `git status` again to confirm the permission changes we did was tracked locally in your git repository.
|
||||
|
||||

|
||||

|
||||
|
||||
We can now commit this change:
|
||||
|
||||
@@ -50,13 +56,13 @@ git add -A
|
||||
git commit -m "added permission for uses"
|
||||
```
|
||||
|
||||
Now let’s create a serverless function before we push all changes to GitHub so Nhost can deploy our changes.
|
||||
Now let's create a serverless function before we push all changes to GitHub so Nhost can deploy our changes.
|
||||
|
||||
### Serverless Function
|
||||
|
||||
A serverless function is a pieces of code written in JavaScript or TypeScript that take an HTTP request and returns a response.
|
||||
|
||||
Here’s an example:
|
||||
Here's an example:
|
||||
|
||||
```bash
|
||||
import { Request, Response } from 'express'
|
||||
@@ -68,25 +74,30 @@ export default (req: Request, res: Response) => {
|
||||
|
||||
Serverless functions are placed in the `functions/` folder of your repository. Every file will become its own endpoint.
|
||||
|
||||
Before we create our serverless function we’ll install `express`, which is a requirement for serverless functions to work.
|
||||
Before we create our serverless function we'll install `express`, which is a requirement for serverless functions to work.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm install express
|
||||
# or with yarn
|
||||
yarn add express
|
||||
npm install -d @types/node @types/express
|
||||
```
|
||||
|
||||
We’ll use TypeScript so we’ll install two type definitions too:
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
npm install -d @types/node @types/express
|
||||
# or with yarn
|
||||
yarn add express
|
||||
yarn add -D @types/node @types/express
|
||||
```
|
||||
|
||||
Then we’ll create a file `functions/time.ts`
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
In the file `time.ts` we’ll add the following code to create our serverless function:
|
||||
Then we'll create a file `functions/time.ts`
|
||||
|
||||
In the file `time.ts` we'll add the following code to create our serverless function:
|
||||
|
||||
```bash
|
||||
import { Request, Response } from 'express';
|
||||
@@ -98,11 +109,11 @@ export default (req: Request, res: Response) => {
|
||||
};
|
||||
```
|
||||
|
||||
We can now test the function locally. Locally, the backend URL is `http://localhost:1337`. Functions are under `/v1/functions`. And every function’s path and filename becomes an API endpoint.
|
||||
We can now test the function locally. Locally, the backend URL is `http://localhost:1337`. Functions are under `/v1/functions`. And every function's path and filename becomes an API endpoint.
|
||||
|
||||
This means our function `functions/time.ts` is at `http://localhost:1337/v1/functions/time`.
|
||||
|
||||
Let’s use curl to test our new function:
|
||||
Let's use curl to test our new function:
|
||||
|
||||
```bash
|
||||
curl http://localhost:1337/v1/functions/time
|
||||
@@ -116,9 +127,9 @@ curl http://localhost:1337/v1/functions/time\?name\=Johan
|
||||
Hello Johan! It's now: Sun, 06 Feb 2022 17:44:48 GMT
|
||||
```
|
||||
|
||||
Again, let’s use `git status` to see the changes we did to create our serverless function.
|
||||
Again, let's use `git status` to see the changes we did to create our serverless function.
|
||||
|
||||
Now let’s commit the changes and push them to GitHub.
|
||||
Now let's commit the changes and push them to GitHub.
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
@@ -128,32 +139,32 @@ git push
|
||||
|
||||
In the Nhost Console, click on the new deployment to see details.
|
||||
|
||||

|
||||

|
||||
|
||||
After Nhost has finished deploying your changes we can test them in production. First let’s confirm that the user permissions are applied.
|
||||
After Nhost has finished deploying your changes we can test them in production. First let's confirm that the user permissions are applied.
|
||||
|
||||

|
||||

|
||||
|
||||
Then, let’s confirm that the serverless function was deployed. Again, we’ll use curl:
|
||||
Then, let's confirm that the serverless function was deployed. Again, we'll use curl:
|
||||
|
||||
```bash
|
||||
curl https://your-backend-url.nhost.run/v1/functions/time\?name\=Johan
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
## Conclusion
|
||||
|
||||
In this tutorial we have installed the Nhost CLI and created a local Nhost environment to do local development and testing.
|
||||
|
||||
In the local environment we’ve made changes to our database, to Hasura’s metadata and created a serverless function.
|
||||
In the local environment we've made changes to our database, to Hasura's metadata and created a serverless function.
|
||||
|
||||
We’ve connected a GitHub repository and pushed our changes to GitHub.
|
||||
We've connected a GitHub repository and pushed our changes to GitHub.
|
||||
|
||||
We’ve seen Nhost automatically deploying our changes and we’ve verified that the changes were applied.
|
||||
We've seen Nhost automatically deploying our changes and we've verified that the changes were applied.
|
||||
|
||||
In summary, we’ve set up a productive environment using the recommended Nhost workflow:
|
||||
In summary, we've set up a productive environment using the recommended Nhost workflow:
|
||||
|
||||
1. Develop locally using the Nhost CLI.
|
||||
2. Push changes to GitHub.
|
||||
3. Nhost deploys changes to production.
|
||||
3. Nhost deploys changes to production.
|
||||
@@ -8,19 +8,24 @@ What follows is a detailed tutorial on how you setup Nhost for this workflow
|
||||
|
||||
Create a **new Nhost app** for this tutorial.
|
||||
|
||||
> It’s important that you create a **new** Nhost app for this guide instead of reusing an old Nhost app because we want to start with a clean Nhost app.
|
||||
:::tip
|
||||
It's important that you create a **new** Nhost app for this guide instead of reusing an old Nhost app because we want to start with a clean Nhost app.
|
||||
:::
|
||||
|
||||

|
||||

|
||||
|
||||
### Create new GitHub Repository
|
||||
|
||||
Create a new GitHub repository for your new Nhost app. The repo can be either private or public.
|
||||
|
||||

|
||||

|
||||
|
||||
## Connect GitHub Repository to Nhost App
|
||||
|
||||
In the Nhost Console, go to the dashboard of your Nhost app and click **Connect to GitHub**.
|
||||
|
||||
<Video src="/videos/cli-workflow/connect-github-repo.mp4">
|
||||
</Video>
|
||||
<video
|
||||
src="/videos/cli-workflow/connect-github-repo.mp4"
|
||||
width="100%"
|
||||
controls
|
||||
/>
|
||||
36
docs/docs/get-started/index.md
Normal file
36
docs/docs/get-started/index.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: 'Welcome to Nhost'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Nhost is an open-source, real-time, server-less backend platform for building reliable apps that scale with your business.
|
||||
|
||||
---
|
||||
|
||||
## Components
|
||||
|
||||
Nhost uses an opinionated set of open-source components.
|
||||
|
||||
#### Database
|
||||
|
||||
Your application gets its own PostgreSQL database, the world's most advanced relational database.
|
||||
|
||||
#### GraphQL API
|
||||
|
||||
Highly performant and real-time GraphQL API with Hasura.
|
||||
|
||||
#### Authentication and storage
|
||||
|
||||
User management & file storage seamlessly integrated with Hasura permissions.
|
||||
|
||||
#### Serverless functions
|
||||
|
||||
JavaScript and TypeScript functions run your custom code in the backend.
|
||||
|
||||
---
|
||||
|
||||
## Get started
|
||||
|
||||
Follow our [Quick start](/get-started/quick-start) guide to build your first app.
|
||||
|
||||
Check out [Nhost on GitHub](https://github.com/nhost/nhost). Give us a star, and feel free to open a discussion for any feature requests as well.
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: 'Create your app'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Let's create a simple todo-app using Nhost. In a todo-app, a user should be able to create list items for their account (CRUD) and not have anyone else see them (permissions).
|
||||
@@ -28,7 +29,7 @@ Press the **"New App"** button on the console's home page. Choose a name and pic
|
||||
|
||||
You'll be all set with the Default Workspace and the Free plan for now.
|
||||
|
||||

|
||||

|
||||
|
||||
Creating a new app takes around 20 seconds or so. During this time, Nhost sets up your app's entire backend and infrastructure.
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
title: 'JavaScript client'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
In the previous section, you used the Hasura Console to fetch a list of todos. Now, you will write a small JavaScript client to interact and retrieve todos from your Nhost app.
|
||||
|
||||
### Frontend frameworks
|
||||
@@ -14,25 +17,36 @@ In this guide, we'll keep the example simple. We're not using a frontend framewo
|
||||
|
||||
## Setup
|
||||
|
||||
> Make sure you have [Node.js](https://nodejs.org) and [npm](https://docs.npmjs.com/getting-started) installed.
|
||||
:::info
|
||||
|
||||
Create a new folder called `nhost-todos`, and initialize a new JavaScript app there:
|
||||
Make sure you have [Node.js](https://nodejs.org) and [npm](https://docs.npmjs.com/getting-started) or [Yarn](https://classic.yarnpkg.com/lang/en/docs/install) installed.
|
||||
|
||||
```sh
|
||||
:::
|
||||
|
||||
Create a new folder called `nhost-todos`, initialize a new JavaScript app there, and install the Nhost JavaScript SDK:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm init -y
|
||||
# or
|
||||
yarn add @nhost/nhost-js graphql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn init -y
|
||||
npm install @nhost/nhost-js graphql
|
||||
```
|
||||
|
||||
> You might have to edit the `package.json` file and add/change the `type` object to `module` (`"type": "module"`).
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Install Nhost JavaScript SDK:
|
||||
|
||||
```sh
|
||||
npm install @nhost/nhost-js
|
||||
# or
|
||||
yarn add @nhost/nhost-js
|
||||
```
|
||||
:::caution attention
|
||||
You might have to edit the `package.json` file and add/change the `type` object to `module` (`"type": "module"`).
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
@@ -43,18 +57,18 @@ In the new directory, create a file called `index.js`.
|
||||
Enter the following code into this file. It will initialize a new `NhostClient` that will interact with your backend:
|
||||
|
||||
```js
|
||||
import { NhostClient } from '@nhost/nhost-js'
|
||||
import { NhostClient } from '@nhost/nhost-js';
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: 'https://[app-subdomain].nhost.run' // replace this with the backend URL of your app
|
||||
})
|
||||
backendUrl: 'https://[app-subdomain].nhost.run', // replace this with the backend URL of your app
|
||||
});
|
||||
|
||||
console.log(nhost.graphql.getUrl())
|
||||
console.log(nhost.graphql.getUrl());
|
||||
```
|
||||
|
||||
Run the code in your terminal. You should see your app's GraphQL endpoint URL:
|
||||
|
||||
```sh
|
||||
```bash
|
||||
➜ node index.js
|
||||
|
||||
https://[app-subdomain].nhost.run/v1/graphql
|
||||
@@ -65,10 +79,10 @@ https://[app-subdomain].nhost.run/v1/graphql
|
||||
If you now add the following GraphQL query to the client, let's see what happens when you run the updated version:
|
||||
|
||||
```js
|
||||
import { NhostClient } from '@nhost/nhost-js'
|
||||
import { NhostClient } from '@nhost/nhost-js';
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: 'https://[app-subdomain].nhost.run'
|
||||
backendUrl: 'https://[app-subdomain].nhost.run',
|
||||
})(async () => {
|
||||
// nhost.graphql.request returns a promise, so we use await here
|
||||
const todos = await nhost.graphql.request(`
|
||||
@@ -80,14 +94,14 @@ const nhost = new NhostClient({
|
||||
is_completed
|
||||
}
|
||||
}
|
||||
`)
|
||||
`);
|
||||
|
||||
// Print todos to console
|
||||
console.log(JSON.stringify(todos.data, null, 2))
|
||||
})()
|
||||
console.log(JSON.stringify(todos.data, null, 2));
|
||||
})();
|
||||
```
|
||||
|
||||
```sh
|
||||
```bash
|
||||
➜ node index.js
|
||||
|
||||
null
|
||||
@@ -2,7 +2,7 @@
|
||||
title: 'Set permissions'
|
||||
---
|
||||
|
||||
In the previous section, you could fetch the todos because the **admin** role is enabled by default when using Hasura Console. When building your applications, you want to define permissions using **roles** that your users can assume when making requests.
|
||||
While using the Hasura Console, you could fetch the todos because the **admin** role is enabled by default but when building your applications with a client, you want to define permissions using **roles** that your users can assume when making requests.
|
||||
|
||||
Hasura supports role-based access control. You create rules for each role, table, and operation (select, insert, update and delete) that can check dynamic session variables, like user ID.
|
||||
|
||||
@@ -18,11 +18,11 @@ In Hasura Console, go to the **Data** tab, click on the **todos** table, then cl
|
||||
|
||||
Add the following permissions:
|
||||
|
||||

|
||||

|
||||
|
||||
Rerun the program. Now you see all todos.
|
||||
|
||||
```sh
|
||||
```bash
|
||||
➜ node index.js
|
||||
|
||||
{
|
||||
@@ -14,7 +14,7 @@ Go to the **Data** tab on your app's dashboard and select **Open Hasura**. Remem
|
||||
|
||||
The Hasura Console of your app's dedicated Hasura instance will open in a new tab. You can use Hasura Console to manage your app's schema, data, permissions, and event triggers.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -27,7 +27,7 @@ You should see all your database tables on the left-hand side of the screen. You
|
||||
|
||||
If you open the `auth` schema, you'll see that your app already has a `users` table, so you don't have to create one.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -44,7 +44,7 @@ In Hasura Console, go to the **data** tab, then click **Create Table**. Name thi
|
||||
|
||||
Using frequently used columns ensures the columns get the right name, type, and default value.
|
||||
|
||||

|
||||

|
||||
|
||||
### Add custom columns
|
||||
|
||||
@@ -55,7 +55,7 @@ Add two more columns manually:
|
||||
|
||||
Make sure to set the default value of `is_completed` to `false`.
|
||||
|
||||

|
||||

|
||||
|
||||
This is all we need! A new table will be created when you click **Add Table**.
|
||||
|
||||
@@ -65,7 +65,7 @@ This is all we need! A new table will be created when you click **Add Table**.
|
||||
|
||||
Go to the **Insert Row** tab to add some data to your database.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -10,22 +10,24 @@ Upgrading from Nhost v1 to v2 requires database schema and Hasura metadata chang
|
||||
|
||||
### Create a new Nhost v2 app locally
|
||||
|
||||
> Make sure you have the [Nhost CLI](/reference/cli) installed
|
||||
:::tip
|
||||
Make sure you have the [Nhost CLI](/reference/cli) installed
|
||||
:::
|
||||
|
||||
```sh
|
||||
```bash
|
||||
nhost init my-nhost-v2-app
|
||||
cd my-nhost-v2-app
|
||||
```
|
||||
|
||||
### Update config
|
||||
|
||||
Update `config: 3` to `config: 2` in `nhost/config.yaml`. This will update Hasura's configuration version, and we need to downgrade the version when we export migrations and metadata.
|
||||
Update `version: 3` to `version: 2` in `nhost/config.yaml`. This will update Hasura's configuration version, and we need to downgrade the version when we export migrations and metadata.
|
||||
|
||||
### Export current migrations and metadata from Nhost v1
|
||||
|
||||
Inside the `nhost/` folder of your app, run:
|
||||
|
||||
```sh
|
||||
```bash
|
||||
hasura migrate create init --from-server --endpoint=[v1-endpoint] --admin-secret=[v1-admin-secret]
|
||||
|
||||
hasura metadata export --endpoint=[v1-endpoint] --admin-secret=[v1-admin-secret]
|
||||
@@ -35,7 +37,9 @@ hasura metadata export --endpoint=[v1-endpoint] --admin-secret=[v1-admin-secret]
|
||||
|
||||
Make the following changes manually to your migrations.
|
||||
|
||||
> The migration file is located at `nhost/migrations/[timestamp]/up.sql`.
|
||||
:::tip
|
||||
The migration file is located at `nhost/migrations/[timestamp]/up.sql`.
|
||||
:::
|
||||
|
||||
- Add `OR REPLACE` after `CREATE` for the `public.set_current_timestamp_updated_at` function
|
||||
- Delete all `auth.*` tables and functions (if any).
|
||||
@@ -46,7 +50,9 @@ Make the following changes manually to your migrations.
|
||||
|
||||
Make the following changes manually to your metadata.
|
||||
|
||||
> The metadata is located at `nhost/metadata/tables.yaml`.
|
||||
:::tip
|
||||
The metadata is located at `nhost/metadata/tables.yaml`.
|
||||
:::
|
||||
|
||||
- Delete tracking all tables in the `auth` schema.
|
||||
- Delete tracking the `public.users` table.
|
||||
@@ -56,11 +62,13 @@ Make the following changes manually to your metadata.
|
||||
|
||||
Start Nhost locally using the [CLI](/reference/cli). From the root of your app, run:
|
||||
|
||||
```sh
|
||||
```bash
|
||||
nhost -d
|
||||
```
|
||||
|
||||
> Running Nhost applies your local database migrations and Hasura metadata.
|
||||
:::tip
|
||||
Running Nhost applies your local database migrations and Hasura metadata.
|
||||
:::
|
||||
|
||||
### Restart Auth and Storage containers
|
||||
|
||||
@@ -70,7 +78,7 @@ Open Docker UI and restart Hasura Auth and Hasura Storage. Restarting those cont
|
||||
|
||||
Delete the local migrations and metadata.
|
||||
|
||||
```
|
||||
```bash
|
||||
rm -rf nhost/migrations nhost/metadata
|
||||
```
|
||||
|
||||
@@ -80,13 +88,17 @@ Update `config: 2` to `config: 3` in `nhost/config.yaml`.
|
||||
|
||||
### Pull migrations and metadata from our local instance
|
||||
|
||||
> You can not use port `1337` in these requests. You have to use the specific port Huasra uses. Go to the Hasura Console under API and look what port Hasura is using under GraphQL Endpoint.
|
||||
In the `nhost/` folder, run the following command:
|
||||
|
||||
```
|
||||
```bash
|
||||
hasura migrate create init --from-server --endpoint=http://localhost:[hasura-port] --admin-secret=nhost-admin-secret
|
||||
hasura metadata export --from-server --endpoint=http://localhost:[hasura-port] --admin-secret=nhost-admin-secret
|
||||
hasura metadata export --endpoint=http://localhost:[hasura-port] --admin-secret=nhost-admin-secret
|
||||
```
|
||||
|
||||
:::warning
|
||||
You cannot use port `1337` in the commands above. You have to use the specific port Hasura uses. Go to the Hasura Console under API and look for the port Hasura is using under GraphQL Endpoint.
|
||||
:::
|
||||
|
||||
### Done
|
||||
|
||||
You now have a Nhost v2 project locally with correct migrations and metadata. Happy hacking!
|
||||
5
docs/docs/platform/_category_.json
Normal file
5
docs/docs/platform/_category_.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"label": "The Nhost Platform",
|
||||
"position": 1,
|
||||
"link": { "type": "generated-index", "slug": "/platform" }
|
||||
}
|
||||
5
docs/docs/platform/authentication/_category_.json
Normal file
5
docs/docs/platform/authentication/_category_.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"label": "Authentication",
|
||||
"position": 4,
|
||||
"link": { "id": "platform/authentication/index", "type": "doc" }
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: 'Email templates'
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
The following emails can be sent as part of the authentication flow:
|
||||
@@ -17,7 +18,7 @@ If you have developed custom email templates, you must make them available over
|
||||
|
||||
Go to **Users -> Login settings** and scroll down to **Custom email templates**, and set the URL to where your templates are located:
|
||||
|
||||

|
||||

|
||||
|
||||
You only need to define the base URL to point to your hosted templates. The UI will give you a hint about where Nhost will look for your actual template files.
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
---
|
||||
title: 'Overview'
|
||||
title: Authentication
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Nhost provides a ready-to-use authentication service, integrated with Nhost JavaScript SDK. This makes it easy to build login flows with multiple login methods.
|
||||
# Authentication
|
||||
|
||||
---
|
||||
Nhost provides a ready-to-use authentication service, integrated with Nhost JavaScript SDK. This makes it easy to build login flows with multiple sign-in methods.
|
||||
|
||||
## Getting started
|
||||
## Getting Started
|
||||
|
||||
Using [Nhost JavaScript SDK](/reference/sdk), sign up a new user:
|
||||
Sign up a user with the [Nhost JavaScript SDK](/reference/sdk):
|
||||
|
||||
```js
|
||||
import { NhostClient } from '@nhost/nhost-js'
|
||||
import { NhostClient } from '@nhost/nhost-js';
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: 'https://[app-subdomain].nhost.run'
|
||||
})
|
||||
backendUrl: 'https://[app-subdomain].nhost.run',
|
||||
});
|
||||
|
||||
await nhost.auth.signUp({
|
||||
email: 'joe@nhost.io',
|
||||
password: 'secret-password'
|
||||
})
|
||||
password: 'secret-password',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How it works
|
||||
|
||||
1. A user signs up and the user information is added to the `auth.users` table
|
||||
2. Nhost returns an access token (JWT token) with the user's information. The access token is cryptographically signed.
|
||||
1. A user signs up and the user information is added to the `auth.users` table.
|
||||
2. Nhost returns an [access token](#access-tokens) (JWT token) and the user's information.
|
||||
3. The user sends a request to the GraphQL API together with the access token.
|
||||
4. The GraphQL API reviews the access token to ensure the user is authorized to send the request.
|
||||
|
||||
Nhost's authentication service is integrated with your database. All users are saved in the app's database under the `auth` schema and can be accessed using GraphQL:
|
||||
Nhost's authentication service is integrated with your database. All users are stored in the app's database under the `auth` schema and can be accessed using GraphQL:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
@@ -45,13 +44,11 @@ query {
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tokens
|
||||
|
||||
Nhost authentication uses two tokens: Access tokens and refresh tokens.
|
||||
|
||||
[Nhost JavaScript SDK](/sdk/) automatically handles access and refresh tokens.
|
||||
[Nhost JavaScript SDK](/reference/sdk) automatically handles access and refresh tokens.
|
||||
|
||||
### Access tokens
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "OAuth providers",
|
||||
"position": 3
|
||||
}
|
||||
@@ -1,14 +1,23 @@
|
||||
---
|
||||
title: Sign In with Facebook
|
||||
title: Sign in with Facebook
|
||||
sidebar_position: 2
|
||||
slug: /platform/authentication/sign-in-with-facebook
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with Facebook with your Nhost App.
|
||||
|
||||

|
||||
<p align="center">
|
||||
<img
|
||||
alt="Facebook Sign In Preview"
|
||||
src="/img/social-providers/facebook-preview.svg"
|
||||
width={480}
|
||||
height={267}
|
||||
/>
|
||||
</p>
|
||||
|
||||
# Create Facebook account
|
||||
## Create Facebook account
|
||||
|
||||
- Create a new [Facebook account](https://www.facebook.com/) if you don’t have one already.
|
||||
- Create a new [Facebook account](https://www.facebook.com/) if you don't have one already.
|
||||
|
||||
## Create Facebook App
|
||||
|
||||
@@ -24,7 +33,7 @@ Follow this guide to sign in users with Facebook with your Nhost App.
|
||||
|
||||
- Click on Add Product in the left menu.
|
||||
- Click on Setup in the Facebook login card.
|
||||
- **Don’t** complete the quickstart. Instead, follow the next step.
|
||||
- **Don't** complete the quickstart. Instead, follow the next step.
|
||||
- Click on **Settings** under **Facebook Login** in the left menu.
|
||||
- Make sure **Embedded Browser OAuth Login** is set to **Yes**.
|
||||
- Fill in **Valid OAuth Redirect URIs** with your **OAuth Callback URL** from Nhost.
|
||||
@@ -43,7 +52,7 @@ To make sure we can fetch all user data (email, profile picture and name). For t
|
||||
## Configure Nhost
|
||||
|
||||
- Click **Settings** and then **Basic** in the left menu.
|
||||
- Copy and paste the **App ID (Client ID)** and **App secret (Client Secret)** from Facebook to your Nhost OAuth settings for Facebook. Make sure the [OAuth provider is enabled in Nhost](/platform/authentication/social-sign-in#enabling-social-sign-in).
|
||||
- Copy and paste the **App ID (Client ID)** and **App secret (Client Secret)** from Facebook to your Nhost OAuth settings for Facebook. Make sure the [OAuth provider is enabled in Nhost](/platform/authentication/social-sign-in#enabling-social-sign-in-provider).
|
||||
- Click the checkbox “**I have pasted the redirect URI into Facebook”**.
|
||||
- Click **Confirm settings**.
|
||||
|
||||
@@ -53,6 +62,6 @@ Use the [Nhost JavaScript client](/reference/sdk) to sign in users in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'facebook'
|
||||
})
|
||||
provider: 'facebook',
|
||||
});
|
||||
```
|
||||
@@ -1,14 +1,23 @@
|
||||
---
|
||||
title: Sign In with GitHub
|
||||
title: Sign in with GitHub
|
||||
sidebar_position: 3
|
||||
slug: /platform/authentication/sign-in-with-github
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with GitHub with your Nhost App.
|
||||
|
||||

|
||||
<p align="center">
|
||||
<img
|
||||
alt="GitHub Sign In Preview"
|
||||
src="/img/social-providers/github-preview.svg"
|
||||
width={480}
|
||||
height={267}
|
||||
/>
|
||||
</p>
|
||||
|
||||
# Create GitHub account
|
||||
## Create GitHub account
|
||||
|
||||
- Create a new [GitHub account](https://github.com/signup) if you don’t have one already.
|
||||
- Create a new [GitHub account](https://github.com/signup) if you don't have one already.
|
||||
|
||||
## Create GitHub OAuth App
|
||||
|
||||
@@ -28,7 +37,7 @@ Follow this guide to sign in users with GitHub with your Nhost App.
|
||||
## Configure Nhost
|
||||
|
||||
- Click Generate a new client secret to generate a OAuth client secret.
|
||||
- Copy and paste the **Client ID** and **Client Secret** from GitHub to your Nhost OAuth settings for GitHub. Make sure the [OAuth provider is enabled in Nhost](/platform/authentication/social-sign-in#enabling-social-sign-in).
|
||||
- Copy and paste the **Client ID** and **Client Secret** from GitHub to your Nhost OAuth settings for GitHub. Make sure the [OAuth provider is enabled in Nhost](/platform/authentication/social-sign-in#enabling-social-sign-in-provider).
|
||||
- Click the checkbox “**I have pasted the redirect URI into GitHub”**.
|
||||
- Click **Confirm settings**.
|
||||
|
||||
@@ -38,6 +47,6 @@ Use the [Nhost JavaScript client](/reference/sdk) to sign in users in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'github'
|
||||
})
|
||||
provider: 'github',
|
||||
});
|
||||
```
|
||||
@@ -1,10 +1,19 @@
|
||||
---
|
||||
title: Sign In with Google
|
||||
title: Sign in with Google
|
||||
sidebar_position: 1
|
||||
slug: /platform/authentication/sign-in-with-google
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with Google with your Nhost App.
|
||||
|
||||

|
||||
<p align="center">
|
||||
<img
|
||||
alt="Google Sign In Preview"
|
||||
src="/img/social-providers/google-preview.svg"
|
||||
width={480}
|
||||
height={267}
|
||||
/>
|
||||
</p>
|
||||
|
||||
## Sign up for Google
|
||||
|
||||
@@ -52,16 +61,16 @@ Follow this guide to sign in users with Google with your Nhost App.
|
||||
## Configure Nhost
|
||||
|
||||
- A modal appears with your Google Client ID and Client secret.
|
||||
- Copy and paste the **Client ID** and **Client Secret** from Google to your Nhost OAuth settings for Google. Make sure the [OAuth provider is enabled in Nhost](/platform/authentication/social-sign-in#enabling-social-sign-in).
|
||||
- Copy and paste the **Client ID** and **Client Secret** from Google to your Nhost OAuth settings for Google. Make sure the OAuth provider is enabled in Nhost.
|
||||
- Click the checkbox “**I have pasted the redirect URI into Google”**.
|
||||
- Click **Confirm settings**.
|
||||
|
||||
## Sign In users in your app
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/sdk) to sign in users in your app:
|
||||
Use the Nhost JavaScript client to sign in users in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'google'
|
||||
})
|
||||
provider: 'google',
|
||||
});
|
||||
```
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: 'Social Sign-In Providers'
|
||||
title: 'OAuth Providers'
|
||||
slug: /platform/authentication/social-sign-in
|
||||
---
|
||||
|
||||
Nhost Auth supports the following social sign-in providers:
|
||||
|
||||
- [GitHub](/platform/authentication/sign-in-with-github)
|
||||
- [Google](/platform/authentication/sign-in-with-google)
|
||||
- [Facebook](/platform/authentication/sign-in-with-facebook)
|
||||
- [GitHub](/platform/authentication/sign-in-with-github)
|
||||
- [LinkedIn](/platform/authentication/sign-in-with-linkedin)
|
||||
- [Spotify](/platform/authentication/sign-in-with-spotify)
|
||||
|
||||
@@ -28,8 +29,8 @@ Here's an example of how to implement sign-in with GitHub:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'github'
|
||||
})
|
||||
provider: 'github',
|
||||
});
|
||||
```
|
||||
|
||||
Users are redirected to your Nhost app's **client URL** by default. By default your Nhost app's client URL is set to `http://localhost:3000`. You can change the value of your client URL in the Nhost console by going to **Users** → **Login settings** → **Client URL**.
|
||||
@@ -1,10 +1,19 @@
|
||||
---
|
||||
title: Sign In with LinkedIn
|
||||
title: Sign in with LinkedIn
|
||||
sidebar_position: 4
|
||||
slug: /platform/authentication/sign-in-with-linkedin
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with LinkedIn with your Nhost App.
|
||||
|
||||

|
||||
<p align="center">
|
||||
<img
|
||||
alt="LinkedIn Sign In Preview"
|
||||
src="/img/social-providers/linkedin-preview.svg"
|
||||
width={480}
|
||||
height={267}
|
||||
/>
|
||||
</p>
|
||||
|
||||
## Create LinkedIn account
|
||||
|
||||
@@ -45,6 +54,6 @@ Use the [Nhost JavaScript client](/reference/sdk) to sign in users in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'linkedin'
|
||||
})
|
||||
provider: 'linkedin',
|
||||
});
|
||||
```
|
||||
@@ -1,12 +1,21 @@
|
||||
---
|
||||
title: Sign In with Spotify
|
||||
title: Sign in with Spotify
|
||||
sidebar_position: 5
|
||||
slug: /platform/authentication/sign-in-with-spotify
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with Spotify with your Nhost App.
|
||||
|
||||

|
||||
<p align="center">
|
||||
<img
|
||||
alt="Spotify Sign In Preview"
|
||||
src="/img/social-providers/spotify-preview.svg"
|
||||
width={480}
|
||||
height={267}
|
||||
/>
|
||||
</p>
|
||||
|
||||
# Create Spotify account
|
||||
## Create Spotify account
|
||||
|
||||
- Create a new [Spotify account](https://www.spotify.com/) if you don't have one already.
|
||||
|
||||
@@ -38,6 +47,6 @@ Use the [Nhost JavaScript client](/reference/sdk) to sign in users in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'spotify'
|
||||
})
|
||||
provider: 'spotify',
|
||||
});
|
||||
```
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: 'Sign-in methods'
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
Nhost supports a variety of sign-in methods:
|
||||
@@ -13,8 +14,8 @@ To sign in a user with email and password, the user must first sign up:
|
||||
```js
|
||||
await nhost.auth.signUp({
|
||||
email: 'joe@nhost.io',
|
||||
password: 'secret-password'
|
||||
})
|
||||
password: 'secret-password',
|
||||
});
|
||||
```
|
||||
|
||||
If you've turned on email verification in your app's **login settings**, a user will be sent a verification email upon signup. The user must click the verification link in the email before they can log in.
|
||||
@@ -24,8 +25,8 @@ Once a user has been signed up (and optionally verified), you can sign them in:
|
||||
```js
|
||||
await nhost.auth.signIn({
|
||||
email: 'joe@nhost.io',
|
||||
password: 'secret-password'
|
||||
})
|
||||
password: 'secret-password',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
@@ -42,8 +43,8 @@ Example in JavaScript:
|
||||
|
||||
```js
|
||||
await nhost.auth.signIn({
|
||||
email: 'joe@nhost.io'
|
||||
})
|
||||
email: 'joe@nhost.io',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
@@ -56,8 +57,8 @@ First, "sign in" the user with a phone number.
|
||||
|
||||
```js
|
||||
await nnhost.auth.signIn({
|
||||
phoneNumber: '+467610337135'
|
||||
})
|
||||
phoneNumber: '+467610337135',
|
||||
});
|
||||
```
|
||||
|
||||
This will create the user if the user does not already exist, and send a One Time Password (OTP) to the user's
|
||||
@@ -68,8 +69,8 @@ Use the OTP to finalize the sign-in:
|
||||
```js
|
||||
await nhost.auth.signIn({
|
||||
phoneNumber: '+467610337135',
|
||||
otp: 'otp-from-sms'
|
||||
})
|
||||
otp: 'otp-from-sms',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
@@ -78,7 +79,7 @@ await nhost.auth.signIn({
|
||||
|
||||
A user can be created anonymously. This is useful for offering a limited version of your application to your users without having them sign in first.
|
||||
|
||||
An anonymous user gets a user ID with the `anonymous` role. This role can be used to [set permissions in Hasura](/platform/database/permissions).
|
||||
An anonymous user gets a user ID with the `anonymous` role. This role can be used to [set permissions in Hasura](/platform/graphql/permissions).
|
||||
|
||||
### Deanonymize users
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: 'User management'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Users are saved in the database in the `auth.users` table and are accessible via the GraphQL API.
|
||||
@@ -66,7 +67,7 @@ If the user is not signed in, the `public` role will be used.
|
||||
When no request role is specified, the user's default role will be used:
|
||||
|
||||
```js
|
||||
await nhost.graphql.request(QUERY, {})
|
||||
await nhost.graphql.request(QUERY, {});
|
||||
```
|
||||
|
||||
Make a GraphQL request with the `me` role:
|
||||
@@ -77,10 +78,10 @@ await nhost.graphql.request(
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
'x-hasura-role': 'me'
|
||||
}
|
||||
}
|
||||
)
|
||||
'x-hasura-role': 'me',
|
||||
},
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
If the request is not part of the user's roles, the request will fail.
|
||||
4
docs/docs/platform/database/_category_.json
Normal file
4
docs/docs/platform/database/_category_.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Database",
|
||||
"position": 2
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: 'Schema'
|
||||
title: 'Database'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Every Nhost app comes with a Postgres database. Postgres is the world's most advanced open-source relational database and the most popular SQL database among developers. The database is hosted with Amazon RDS.
|
||||
4
docs/docs/platform/graphql/_category_.json
Normal file
4
docs/docs/platform/graphql/_category_.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "GraphQL",
|
||||
"position": 3
|
||||
}
|
||||
@@ -1,30 +1,49 @@
|
||||
---
|
||||
title: 'GraphQL'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
GraphQL is a query language for APIs. It provides a complete and understandable description of the data in your API.
|
||||
Every Nhost app has its own autogenerated GraphQL API. The GraphQL API is based on the tables and columns in the [Postgres database](/platform/database) and is instantly available. It's [Hasura GraphQL engine](https://github.com/hasura/graphql-engine) that powers the GraphQL API.
|
||||
|
||||
Every Nhost app comes with a GraphQL API, and you can connect to this API with any GraphQL client you like. Most often you'd use the client included in [Nhost SDK](/reference/sdk/graphql).
|
||||
The GraphQL API is available at: `https://[subdomain].nhost.run/v1/graphql`.
|
||||
|
||||
## What is GraphQL
|
||||
|
||||
GraphQL is a query language for APIs that prioritize developer experience. The GraphQL API can be used to both fetch (query) and modify (mutation) data. GraphQL is especially powerful for frontend developers who wants to build products fast.
|
||||
|
||||
### GraphQL clients for JavaScript
|
||||
|
||||
To interact with the GraphQL API it's recommended to use a GraphQL client:
|
||||
|
||||
- [Apollo Client](https://www.apollographql.com/docs/react/)
|
||||
- [URQL](https://formidable.com/open-source/urql/)
|
||||
- [React Query](https://react-query.tanstack.com/graphql)
|
||||
- [SWR](https://swr.vercel.app/docs/data-fetching#graphql)
|
||||
|
||||
It's also possible to use the built-in [GraphQL client](/reference/sdk/graphql) in the Nhost JavaScript client.
|
||||
|
||||
---
|
||||
|
||||
## GraphQL queries
|
||||
## GraphQL Query
|
||||
|
||||
You can read your app's data with a GraphQL query.
|
||||
A GraphQL query is used to fetch data from the database.
|
||||
|
||||
Here is a GraphQL query that selects title, body, and done for every row in your todos table.
|
||||
Here is a GraphQL query that selects `title`, `body`, and `isCompleted` for every row in a `todos` table.
|
||||
|
||||
**Example:**
|
||||
|
||||
```graphql
|
||||
query GetTodos {
|
||||
todos {
|
||||
title
|
||||
body
|
||||
done
|
||||
isCompleted
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -32,7 +51,7 @@ Response:
|
||||
"todos": [
|
||||
{
|
||||
"title": "Delete Firebase account",
|
||||
"body": "Migrate to nhost.io",
|
||||
"body": "Migrate to Nhost",
|
||||
"isCompleted": true
|
||||
}
|
||||
]
|
||||
@@ -42,7 +61,7 @@ Response:
|
||||
|
||||
#### Filtering and sorting
|
||||
|
||||
More complex queries utilize filters, limits, sorting and aggregation.
|
||||
GraphQL queries More complex queries utilize filters, limits, sorting and aggregation.
|
||||
|
||||
This GraphQL query selects all items in the todo table that aren't done, with the total number of comments and the last five comments:
|
||||
|
||||
@@ -97,6 +116,12 @@ Response:
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
Check out Hasura's documentation for full documentation for GraphQL queries.
|
||||
|
||||
[Hasura GraphQL queries](https://hasura.io/docs/latest/graphql/core/databases/postgres/queries/index/)
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## GraphQL mutations
|
||||
@@ -110,7 +135,13 @@ GraphQL mutation to insert data looks like this:
|
||||
```graphql
|
||||
mutation InsertTodo {
|
||||
insert_todos(
|
||||
objects: [{ title: "Delete Firebase account", body: "Migrate to nhost.io", done: false }]
|
||||
objects: [
|
||||
{
|
||||
title: "Delete Firebase account"
|
||||
body: "Migrate to nhost.io"
|
||||
done: false
|
||||
}
|
||||
]
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
@@ -147,7 +178,11 @@ Multiple rows can be inserted with an array as the objects property. This can be
|
||||
mutation InsertMultipleTodos {
|
||||
insert_todos(
|
||||
objects: [
|
||||
{ title: "Build the front end", body: "Mobile app or website first?", done: false }
|
||||
{
|
||||
title: "Build the front end"
|
||||
body: "Mobile app or website first?"
|
||||
done: false
|
||||
}
|
||||
{ title: "Launch 🚀", body: "That was easy", done: false }
|
||||
]
|
||||
) {
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: 'Permissions'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
The GraphQL API is protected by a role-based permission system based on access tokens. Permissions are handled on a per-table basis in Hasura Console.
|
||||
@@ -26,7 +27,7 @@ The default role for users is `user`.
|
||||
|
||||
## Select permissions
|
||||
|
||||

|
||||

|
||||
|
||||
One of the most common permission requirements is that logged-in users should only be able to read their own data. This is how it can be achieved with Hasura.
|
||||
|
||||
@@ -49,7 +50,7 @@ Note that if you add columns to your table table later, you must check new colum
|
||||
|
||||
## Insert permissions
|
||||
|
||||

|
||||

|
||||
|
||||
Here is a popular approach for insert permission for logged in users.
|
||||
|
||||
@@ -67,7 +68,7 @@ We also want every new record's `user_id` value to be set to the ID of the user
|
||||
|
||||
You can add extra permission variables in the Nhost console under **Users** and then **Roles and permissions**. These permission variables are then available when creating permissions for your GraphQL API in the Hasura console.
|
||||
|
||||

|
||||

|
||||
|
||||
As an example, let's say you add a new permission variable `x-hasura-organisation-id` with path `user.profile.organisation.id`. This means that Nhost Auth will get the value for `x-hasura-organisation-id` by internally generating the following GraphQL query:
|
||||
|
||||
11
docs/docs/platform/index.md
Normal file
11
docs/docs/platform/index.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: 'The Nhost Platform'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
- [Database](/platform/database)
|
||||
- [GraphQL](/platform/graphql)
|
||||
- [Authentication](/platform/authentication)
|
||||
- [Storage](/platform/storage)
|
||||
- [Serverless Functions](/platform/serverless-functions)
|
||||
- [Nhost](/platform/nhost)
|
||||
4
docs/docs/platform/nhost/_category_.json
Normal file
4
docs/docs/platform/nhost/_category_.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Nhost",
|
||||
"position": 7
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: 'Environment variables'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Environment variables are key-value pairs configured outside your source code. They are used to store environment-specific values such as API keys.
|
||||
@@ -17,7 +18,7 @@ System environment variables are automatically available in production and local
|
||||
|
||||
Example values:
|
||||
|
||||
```sh
|
||||
```bash
|
||||
NHOST_ADMIN_SECRET=e7w36ag287qn5qry795f6ymm57qgvqup
|
||||
NHOST_WEBHOOK_SECRET=ns3sfjgdw4y6zeqthwnnw347dzh8wyj4
|
||||
NHOST_JWT_SECRET={"type": "HS256", "key": "vumpbe2w2mgaqj5yqfp7dvxu6kywtvsgb68ejpdaqxerea8
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: 'GitHub integration'
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
You can connect your Nhost app to a GitHub repository. When you do this, any updates you push to your code will automatically be deployed.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user