Compare commits
80 Commits
@nhost/das
...
@nhost/rea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
472559276c | ||
|
|
2cdb13b3ef | ||
|
|
a41124c5e0 | ||
|
|
6ecffa81ae | ||
|
|
ea7b102c07 | ||
|
|
e9daf92830 | ||
|
|
9e4ad76e7f | ||
|
|
0fd65db563 | ||
|
|
146fbb84b9 | ||
|
|
b51c18fedb | ||
|
|
a5305e6b56 | ||
|
|
aa88ef2e5c | ||
|
|
ee6b3c9ac8 | ||
|
|
79fd86acc5 | ||
|
|
c2cbeddcb8 | ||
|
|
62b2de59d4 | ||
|
|
2a760593db | ||
|
|
9288873ce8 | ||
|
|
47014be8e3 | ||
|
|
49719f7a84 | ||
|
|
b3b64a3b74 | ||
|
|
3a56c12df4 | ||
|
|
5b15a4f235 | ||
|
|
83303017c3 | ||
|
|
e0739a5883 | ||
|
|
0a5a841cc8 | ||
|
|
3309835f06 | ||
|
|
32b221f944 | ||
|
|
e8a99badb8 | ||
|
|
1ea6e01963 | ||
|
|
958dec5dfe | ||
|
|
09257fbfb2 | ||
|
|
61e3497a13 | ||
|
|
a7b4e5606d | ||
|
|
34d77c9db1 | ||
|
|
4f1efd28a6 | ||
|
|
07a45fde0e | ||
|
|
9d0380eef3 | ||
|
|
ce3ec36b0a | ||
|
|
b62a9d19b5 | ||
|
|
c1472079c5 | ||
|
|
dd36971798 | ||
|
|
6199c1c555 | ||
|
|
f41fdc12af | ||
|
|
fc419ffa4d | ||
|
|
b7c102e876 | ||
|
|
873fc36e61 | ||
|
|
29743f0b71 | ||
|
|
d904ca2bbf | ||
|
|
80b22724de | ||
|
|
80e49f4459 | ||
|
|
b3d5ead508 | ||
|
|
77dcb8c964 | ||
|
|
3488da9dfd | ||
|
|
0e68a1fdfd | ||
|
|
8797b2bd17 | ||
|
|
5ef0b31573 | ||
|
|
86e5e0fb50 | ||
|
|
c2d589dd29 | ||
|
|
4b807d8134 | ||
|
|
ccdabb707f | ||
|
|
364bc87fd3 | ||
|
|
cc02902cbb | ||
|
|
0e838b9406 | ||
|
|
37ebf7d8e2 | ||
|
|
e23af24bdd | ||
|
|
90eb53cf19 | ||
|
|
7e516d7630 | ||
|
|
0861e41e70 | ||
|
|
057e7e2572 | ||
|
|
5a4e237a29 | ||
|
|
c7501c70ae | ||
|
|
6a45c1abad | ||
|
|
660d339e14 | ||
|
|
3dca08595d | ||
|
|
7c501c4e4f | ||
|
|
5e1d5b737c | ||
|
|
1d04ad6306 | ||
|
|
a4fa5f6f59 | ||
|
|
7e973d568a |
3
.github/workflows/ci.yaml
vendored
3
.github/workflows/ci.yaml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
BUILD: 'all'
|
||||
- name: Check if the pnpm lockfile changed
|
||||
id: changed-lockfile
|
||||
uses: tj-actions/changed-files@v36
|
||||
uses: tj-actions/changed-files@v37
|
||||
with:
|
||||
files: pnpm-lock.yaml
|
||||
# * Determine a pnpm filter argument for packages that have been modified.
|
||||
@@ -146,6 +146,7 @@ jobs:
|
||||
run: echo "NHOST_TEST_DASHBOARD_URL=https://${{ steps.fetch-dashboard-preview-url.outputs.preview_url }}" >> $GITHUB_ENV
|
||||
# * Run the `ci` script of the current package of the matrix. Dependencies build is cached by Turborepo
|
||||
- name: Run e2e tests
|
||||
timeout-minutes: 7
|
||||
run: pnpm --filter="${{ matrix.package.name }}" run e2e
|
||||
- id: file-name
|
||||
if: ${{ failure() }}
|
||||
|
||||
103
DEVELOPERS.md
103
DEVELOPERS.md
@@ -1,16 +1,37 @@
|
||||
# Developer guide
|
||||
# Developer Guide
|
||||
|
||||
## Requirements
|
||||
|
||||
- This repository works with **Node 16**
|
||||
### Node.js v18
|
||||
|
||||
- We use [pnpm](https://pnpm.io/) as a package manager to speed up development and builds, and as a basis for our monorepo. You need to make sure it's installed on your machine. There are [several ways to install it](https://pnpm.io/installation), but the easiest way is with `npm`:
|
||||
_⚠️ Node.js v16 is also supported for the time being but support will be dropped in the near future_.
|
||||
|
||||
### [pnpm](https://pnpm.io/) package manager
|
||||
|
||||
The easiest way to install `pnpm` if it's not installed on your machine yet is to use `npm`:
|
||||
|
||||
```sh
|
||||
$ npm install -g pnpm
|
||||
```
|
||||
|
||||
- Our tests and examples use the Nhost CLI, to run the backend services locally. You can follow the installation instructions in [our documentation](https://docs.nhost.io/get-started/cli-workflow/install-cli).
|
||||
### [Nhost CLI](https://docs.nhost.io/cli)
|
||||
|
||||
- The CLI is primarily used for running the E2E tests
|
||||
- Please refer to the [installation guide](https://docs.nhost.io/get-started/cli-workflow/install-cli) if you have not installed it yet
|
||||
|
||||
## File Structure
|
||||
|
||||
The repository is organized as a monorepo, with the following structure (only relevant folders are shown):
|
||||
|
||||
```
|
||||
assets/ # Assets used in the README
|
||||
config/ # Configuration files for the monorepo
|
||||
dashboard/ # Dashboard
|
||||
docs/ # Documentation website
|
||||
examples/ # Example projects
|
||||
packages/ # Core packages
|
||||
integrations/ # These are packages that rely on the core packages
|
||||
```
|
||||
|
||||
## Get started
|
||||
|
||||
@@ -31,25 +52,25 @@ $ pnpm install
|
||||
|
||||
### Development
|
||||
|
||||
Although package references are correctly updated on the fly for TypeScript, example projects won't
|
||||
see the changes because they are depending on the build output. To fix this, you can run packages
|
||||
in development mode.
|
||||
Although package references are correctly updated on the fly for TypeScript, example projects and the dashboard won't see the changes because they are depending on the build output. To fix this, you can run packages in development mode.
|
||||
|
||||
Running packages in development mode is as simple as:
|
||||
Running packages in development mode from the root folder is as simple as:
|
||||
|
||||
```sh
|
||||
$ pnpm dev
|
||||
```
|
||||
|
||||
Our packages are linked together using [PNPM's workspace](https://pnpm.io/workspaces) feature. Vite automatically detects changes in the dependencies and rebuilds everything, so that the changes are immediately reflected in the other packages.
|
||||
Our packages are linked together using [PNPM's workspace](https://pnpm.io/workspaces) feature. Next.js and Vite automatically detect changes in the dependencies and rebuild everything, so the changes will be reflected in the examples and the dashboard.
|
||||
|
||||
### Use examples
|
||||
**Note:** It's possible that Next.js or Vite throw an error when you run `pnpm dev`. Restarting the process should fix it.
|
||||
|
||||
### Use Examples
|
||||
|
||||
Examples are a great way to test your changes in practice. Make sure you've `pnpm dev` running in your terminal and then run an example.
|
||||
|
||||
Let's follow the instructions to run [react-apollo example](https://github.com/nhost/nhost/blob/main/examples/react-apollo/README.md).
|
||||
|
||||
## Run the documentation website locally
|
||||
## Edit Documentation
|
||||
|
||||
The easier way to contribute to our documentation is to go to the `docs` folder and follow the [instructions to start local development](https://github.com/nhost/nhost/blob/main/docs/README.md):
|
||||
|
||||
@@ -60,9 +81,9 @@ $ pnpm install
|
||||
$ pnpm start
|
||||
```
|
||||
|
||||
## Run test suites
|
||||
## Run Test Suites
|
||||
|
||||
### Unit tests
|
||||
### Unit Tests
|
||||
|
||||
You can run the unit tests with the following command from the repository root:
|
||||
|
||||
@@ -70,7 +91,7 @@ You can run the unit tests with the following command from the repository root:
|
||||
$ pnpm test
|
||||
```
|
||||
|
||||
### End-to-end tests
|
||||
### E2E Tests
|
||||
|
||||
Each package that defines end-to-end tests embeds their own Nhost configuration, that will be automatically when running the tests. As a result, you must make sure you are not running the Nhost CLI before running the tests.
|
||||
|
||||
@@ -83,24 +104,60 @@ $ pnpm e2e
|
||||
## Changesets
|
||||
|
||||
If you've made changes to the packages, you must describe those changes so that they can be reflected in the next release.
|
||||
We use [changesets](https://github.com/changesets/changesets) to support our versioning and release workflows. When you submit a pull request, a bot checks if some changesets are present, and if not, it directs you to add them.
|
||||
We use [changesets](https://github.com/changesets/changesets) to support our versioning and release workflows. When you submit a pull request, a bot checks if changesets are present, and if not, it asks you to add them.
|
||||
|
||||
The most comprehensive way to add a changeset is to run the following command in the repository root:
|
||||
To create a changeset, run the following command from the repository root:
|
||||
|
||||
```sh
|
||||
$ pnpm changeset
|
||||
```
|
||||
|
||||
This will create a file in the `.changeset` directory. You can edit it to give more details about the change you just made.
|
||||
This command will guide you through the process of creating a changeset. It will create a file in the `.changeset` directory.
|
||||
|
||||
You can take a look at the changeset documentation: [How to add a changeset](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).
|
||||
|
||||
## Committing changes
|
||||
### Selecting the Version
|
||||
|
||||
You'll notice that `git commit` takes a few seconds to run. We set a commit hook that scans the changes in the code, automatically generates documentation from the inline [TSDoc](https://tsdoc.org/) annotations, and adds these generated documentation files to the commit. They automatically update the [reference documentation](https://docs.nhost.io/reference).
|
||||
When you create a changeset, you will be asked to select the version of the package that you are bumping. The versioning scheme is as follows:
|
||||
|
||||
- **major**
|
||||
- For breaking changes (e.g: changing the function signature, etc.)
|
||||
- Should be avoided as much as possible as it will require users to update their code. Instead, consider supporting both the old and the new API simultaneously for a while.
|
||||
- For example: `v1.5.8` -> `v2.0.0`
|
||||
- **minor**
|
||||
- For new features (e.g: adding a new page to the dashboard, etc.)
|
||||
- For example: `v1.5.8` -> `v1.6.0`
|
||||
- **patch**
|
||||
- For bug fixes (e.g: fixing a typo, etc.)
|
||||
- For example: `v1.5.8` -> `v1.5.9`
|
||||
|
||||
<!-- ## Good practices
|
||||
- lint
|
||||
- prettier
|
||||
- documentation -->
|
||||
### Writing Good Changesets
|
||||
|
||||
A concise summary that describes the changes should be added to each PR. This summary will be used as the changeset description.
|
||||
|
||||
The following structure is used for describing changes:
|
||||
|
||||
- **The type of the change**:
|
||||
|
||||
- fix
|
||||
- feat
|
||||
- chore
|
||||
- docs
|
||||
|
||||
- **The scope of the change** (_broader scopes (e.g: dashboard, hasura-storage-js, etc.) are not recommended as GitHub Releases already contain which project is being bumped_):
|
||||
|
||||
- projects
|
||||
- deployments
|
||||
- deps
|
||||
- etc.
|
||||
|
||||
- **A short summary of the changes that were made**
|
||||
|
||||
**Examples:**
|
||||
|
||||
- `fix(deployments): use correct timestamp for deployment details`
|
||||
- `chore(deps): bump @types/react to v18.2.8`
|
||||
- `feat(secrets): enable secrets`
|
||||
- etc.
|
||||
|
||||
You can always take a look at examples of changesets in the [GitHub Releases section](https://github.com/nhost/nhost/releases).
|
||||
|
||||
@@ -2,13 +2,14 @@ import '@fontsource/inter';
|
||||
import '@fontsource/inter/500.css';
|
||||
import '@fontsource/inter/700.css';
|
||||
import { CssBaseline, ThemeProvider } from '@mui/material';
|
||||
import { NhostClient, NhostProvider } from '@nhost/nextjs';
|
||||
import { NhostApolloProvider } from '@nhost/react-apollo';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { Buffer } from 'buffer';
|
||||
import { initialize, mswDecorator } from 'msw-storybook-addon';
|
||||
import { RouterContext } from 'next/dist/shared/lib/router-context';
|
||||
import { createTheme } from '../src/components/ui/v2/createTheme';
|
||||
import '../src/styles/globals.css';
|
||||
import createTheme from '../src/theme/createTheme';
|
||||
|
||||
global.Buffer = Buffer;
|
||||
|
||||
@@ -56,5 +57,10 @@ export const decorators = [
|
||||
<Story />
|
||||
</NhostApolloProvider>
|
||||
),
|
||||
(Story) => (
|
||||
<NhostProvider nhost={new NhostClient({ subdomain: 'local' })}>
|
||||
<Story />
|
||||
</NhostProvider>
|
||||
),
|
||||
mswDecorator,
|
||||
];
|
||||
|
||||
@@ -1,5 +1,60 @@
|
||||
# @nhost/dashboard
|
||||
|
||||
## 0.17.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react-apollo@5.0.30
|
||||
- @nhost/nextjs@1.13.32
|
||||
|
||||
## 0.17.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ea7b102c0: fix(pat): highlight expired tokens
|
||||
|
||||
## 0.17.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b3b64a3b7: chore(deps): bump `@types/react` to `v18.2.14` and `@types/react-dom` to `v18.2.6`
|
||||
- 32b221f94: chore(deps): bump `graphiql` to `v3`
|
||||
- 3a56c12df: chore(deps): bump `turbo` to `v1.10.6`
|
||||
- Updated dependencies [b3b64a3b7]
|
||||
- @nhost/react-apollo@5.0.29
|
||||
- @nhost/nextjs@1.13.31
|
||||
|
||||
## 0.17.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f41fdc12a: chore(deps): bump `turbo` to `1.10.5`
|
||||
- 6199c1c55: fix(projects): don't redirect to 404 page
|
||||
- Updated dependencies [07a45fde0]
|
||||
- @nhost/react-apollo@5.0.28
|
||||
- @nhost/nextjs@1.13.30
|
||||
|
||||
## 0.17.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 80b22724d: chore(deps): bump `@types/react` to `v18.2.13`, `@types/react-dom` to `v18.2.6` and `@storybook/testing-library` to `v0.2.0`
|
||||
|
||||
## 0.17.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cc02902cb: chore(docs): update environment variable documentation
|
||||
|
||||
## 0.17.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 660d339e1: fix(storybook): don't break storybook
|
||||
- 660d339e1: fix(tests): prevent warnings during tests
|
||||
- @nhost/react-apollo@5.0.27
|
||||
- @nhost/nextjs@1.13.29
|
||||
|
||||
## 0.17.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -3,7 +3,7 @@ RUN apk add --no-cache libc6-compat
|
||||
RUN apk update
|
||||
WORKDIR /app
|
||||
|
||||
RUN yarn global add turbo@1.10.3
|
||||
RUN yarn global add turbo@1.10.6
|
||||
COPY . .
|
||||
RUN turbo prune --scope="@nhost/dashboard" --docker
|
||||
|
||||
|
||||
@@ -3,10 +3,26 @@
|
||||
This is the Nhost Dashboard, a web application that allows you to manage your Nhost projects.
|
||||
To get started, you need to have an Nhost project. If you don't have one, you can [create a project here](https://app.nhost.io).
|
||||
|
||||
First, install the dependencies:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
Then, build the packages that are used by the Nhost Dashboard:
|
||||
|
||||
```bash
|
||||
pnpm -w build
|
||||
```
|
||||
|
||||
Finally, run the development server:
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) to see the result in your browser.
|
||||
|
||||
## Environment
|
||||
|
||||
### Setup Environment Variables
|
||||
@@ -54,6 +70,12 @@ Components are documented using [Storybook](https://storybook.js.org/). To run S
|
||||
pnpm storybook
|
||||
```
|
||||
|
||||
By default, Storybook will run on port `6006`. You can change this by passing the `--port` flag:
|
||||
|
||||
```bash
|
||||
pnpm storybook --port 6007
|
||||
```
|
||||
|
||||
### General Environment Variables
|
||||
|
||||
| Name | Description |
|
||||
@@ -110,15 +132,19 @@ pnpm storybook
|
||||
| `@typescript-eslint/naming-convention` | Enforces a consistent naming convention. |
|
||||
| `no-restricted-imports` | Enforces absolute imports and consistent import paths for components from `src/components/ui` folder. |
|
||||
|
||||
### End-to-End Tests
|
||||
### Unit Tests
|
||||
|
||||
End-to-end tests are written using [Playwright](https://playwright.dev/). To run the tests, run the following command:
|
||||
Unit tests are written using [Vitest](https://vitest.dev/). To run the tests, run the following command:
|
||||
|
||||
```bash
|
||||
pnpm e2e
|
||||
pnpm test
|
||||
```
|
||||
|
||||
Most of the tests require access to the Nhost test user. To run these tests, you need to set the following environment variables in `.env.test`:
|
||||
### End-to-End Tests
|
||||
|
||||
Most of the end-to-end tests require access to an Nhost test user and a live project. You can register a user and create a test project on the [Nhost Dashboard](https://app.nhost.io/).
|
||||
|
||||
Next, you need to create a project. Create a `.env.test` file with the following variables:
|
||||
|
||||
```
|
||||
NHOST_TEST_DASHBOARD_URL=<test_dashboard_url>
|
||||
@@ -128,3 +154,20 @@ NHOST_TEST_WORKSPACE_NAME=<test_workspace_name>
|
||||
NHOST_TEST_PROJECT_NAME=<test_project_name>
|
||||
NHOST_TEST_PROJECT_ADMIN_SECRET=<test_project_admin_secret>
|
||||
```
|
||||
|
||||
**Required Variables**:
|
||||
|
||||
- `NHOST_TEST_DASHBOARD_URL`: The URL to run the tests against (e.g: http://localhost:3000 or https://staging.app.nhost.io)
|
||||
- `NHOST_TEST_USER_EMAIL`: Email address of the test user that owns the test project
|
||||
- `NHOST_TEST_USER_PASSWORD`: Password of the test user that owns the test project
|
||||
- `NHOST_TEST_WORKSPACE_NAME`: Name of the workspace that contains the test project
|
||||
- `NHOST_TEST_PROJECT_NAME`: Name of the test project
|
||||
- `NHOST_TEST_PROJECT_ADMIN_SECRET`: Admin secret of the test project
|
||||
|
||||
Make sure to copy the workspace and project information from the [Nhost Dashboard](https://app.nhost.io/).
|
||||
|
||||
End-to-end tests are written using [Playwright](https://playwright.dev/). To run the tests, run the following command:
|
||||
|
||||
```bash
|
||||
pnpm e2e
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "0.17.11",
|
||||
"version": "0.17.18",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
@@ -11,11 +11,11 @@
|
||||
"lint": "next lint --max-warnings 0",
|
||||
"test": "vitest",
|
||||
"codegen": "graphql-codegen --config graphql.config.yaml --errors-only",
|
||||
"nhost:dev": "nhost up",
|
||||
"format": "prettier --write \"src/**/*.{js,ts,tsx,jsx,json,md}\" --plugin-search-dir=.",
|
||||
"storybook": "start-storybook -p 6006 -s public",
|
||||
"build-storybook": "build-storybook",
|
||||
"e2e": "npx playwright@1.34.0 install --with-deps && playwright test"
|
||||
"install-browsers": "pnpm dlx playwright@1.31.0 install --with-deps",
|
||||
"e2e": "pnpm install-browsers && pnpm dlx playwright@1.31.0 test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.7.10",
|
||||
@@ -26,7 +26,7 @@
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@fontsource/inter": "^5.0.0",
|
||||
"@fontsource/roboto-mono": "^5.0.0",
|
||||
"@graphiql/react": "^0.17.0",
|
||||
"@graphiql/react": "^0.18.0",
|
||||
"@graphiql/toolkit": "^0.8.2",
|
||||
"@headlessui/react": "^1.6.5",
|
||||
"@heroicons/react": "^1.0.6",
|
||||
@@ -49,7 +49,7 @@
|
||||
"clsx": "^1.2.1",
|
||||
"date-fns": "^2.29.3",
|
||||
"generate-password": "^1.7.0",
|
||||
"graphiql": "^2.4.0",
|
||||
"graphiql": "^3.0.0",
|
||||
"graphql": "^16.6.0",
|
||||
"graphql-request": "^6.0.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
@@ -87,7 +87,7 @@
|
||||
"@graphql-codegen/typescript-operations": "^3.0.0",
|
||||
"@graphql-codegen/typescript-react-apollo": "^3.3.1",
|
||||
"@next/bundle-analyzer": "^12.3.1",
|
||||
"@playwright/test": "^1.34.0",
|
||||
"@playwright/test": "1.31.0",
|
||||
"@storybook/addon-actions": "^6.5.14",
|
||||
"@storybook/addon-essentials": "^6.5.14",
|
||||
"@storybook/addon-interactions": "^6.5.14",
|
||||
@@ -96,7 +96,7 @@
|
||||
"@storybook/builder-webpack5": "^6.5.14",
|
||||
"@storybook/manager-webpack5": "^6.5.14",
|
||||
"@storybook/react": "^6.5.14",
|
||||
"@storybook/testing-library": "^0.0.13",
|
||||
"@storybook/testing-library": "^0.2.0",
|
||||
"@testing-library/dom": "^9.0.0",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
@@ -104,8 +104,8 @@
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/pluralize": "^0.0.29",
|
||||
"@types/react": "18.2.12",
|
||||
"@types/react-dom": "18.2.5",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@types/react-table": "^7.7.12",
|
||||
"@types/testing-library__jest-dom": "^5.14.5",
|
||||
"@types/validator": "^13.7.10",
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import { styled } from '@mui/material';
|
||||
import { textClasses } from '@/components/ui/v2/Text';
|
||||
import { getTypographyUtilityClass, styled } from '@mui/material';
|
||||
import type { ListItemTextProps as MaterialListItemTextProps } from '@mui/material/ListItemText';
|
||||
import MaterialListItemText, {
|
||||
listItemTextClasses,
|
||||
listItemTextClasses as materialListItemTextClasses,
|
||||
} from '@mui/material/ListItemText';
|
||||
import clsx from 'clsx';
|
||||
|
||||
export interface ListItemTextProps extends MaterialListItemTextProps {}
|
||||
|
||||
const listItemTextClasses = {
|
||||
...materialListItemTextClasses,
|
||||
warning: getTypographyUtilityClass('colorWarning'),
|
||||
};
|
||||
|
||||
const StyledListItemText = styled(MaterialListItemText)(({ theme }) => ({
|
||||
color: theme.palette.text.primary,
|
||||
display: 'grid',
|
||||
@@ -16,6 +23,9 @@ const StyledListItemText = styled(MaterialListItemText)(({ theme }) => ({
|
||||
[`&.${listItemTextClasses.root}`]: {
|
||||
margin: 0,
|
||||
},
|
||||
[`&.${listItemTextClasses.warning}`]: {
|
||||
color: theme.palette.warning.dark,
|
||||
},
|
||||
[`& > .${listItemTextClasses.primary}`]: {
|
||||
fontWeight: 500,
|
||||
textOverflow: 'ellipsis',
|
||||
@@ -29,8 +39,23 @@ const StyledListItemText = styled(MaterialListItemText)(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
function ListItemText({ children, ...props }: ListItemTextProps) {
|
||||
return <StyledListItemText {...props}>{children}</StyledListItemText>;
|
||||
function ListItemText({
|
||||
children,
|
||||
color = 'primary',
|
||||
className,
|
||||
...props
|
||||
}: ListItemTextProps) {
|
||||
return (
|
||||
<StyledListItemText
|
||||
className={clsx(
|
||||
color === 'warning' && textClasses.colorWarning,
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</StyledListItemText>
|
||||
);
|
||||
}
|
||||
|
||||
ListItemText.displayName = 'NhostListItemText';
|
||||
|
||||
@@ -18,7 +18,7 @@ export type TextProps<
|
||||
*
|
||||
* @default 'primary'
|
||||
*/
|
||||
color?: 'primary' | 'secondary' | 'disabled' | 'error';
|
||||
color?: 'primary' | 'secondary' | 'disabled' | 'error' | 'warning';
|
||||
/**
|
||||
* The component used for the root node.
|
||||
*/
|
||||
@@ -31,6 +31,7 @@ const textClasses = {
|
||||
colorSecondary: getTypographyUtilityClass('colorSecondary'),
|
||||
colorDisabled: getTypographyUtilityClass('colorDisabled'),
|
||||
colorError: getTypographyUtilityClass('colorError'),
|
||||
colorWarning: getTypographyUtilityClass('colorWarning'),
|
||||
};
|
||||
|
||||
const StyledTypography = styled(MaterialTypography)<TextProps>(({ theme }) => ({
|
||||
@@ -50,6 +51,9 @@ const StyledTypography = styled(MaterialTypography)<TextProps>(({ theme }) => ({
|
||||
[`&.${textClasses.colorError}`]: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
[`&.${textClasses.colorWarning}`]: {
|
||||
color: theme.palette.warning.dark,
|
||||
},
|
||||
}));
|
||||
|
||||
function Text<
|
||||
@@ -70,6 +74,7 @@ function Text<
|
||||
color === 'secondary' && textClasses.colorSecondary,
|
||||
color === 'disabled' && textClasses.colorDisabled,
|
||||
color === 'error' && textClasses.colorError,
|
||||
color === 'warning' && textClasses.colorWarning,
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { IconProps } from '@/components/ui/v2/icons';
|
||||
import { SvgIcon } from '@/components/ui/v2/icons/SvgIcon';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
function WarningIcon(props: IconProps, ref: ForwardedRef<SVGSVGElement>) {
|
||||
return (
|
||||
<SvgIcon
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
aria-label="Warning"
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M8 5.5V9.5"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M7.135 2.49904L1.63648 11.9986C1.5485 12.1506 1.5021 12.3231 1.50195 12.4987C1.50181 12.6743 1.54792 12.8469 1.63565 12.999C1.72338 13.1512 1.84964 13.2776 2.00172 13.3654C2.15379 13.4533 2.32633 13.4995 2.50196 13.4995H13.499C13.6746 13.4995 13.8472 13.4533 13.9992 13.3654C14.1513 13.2776 14.2776 13.1512 14.3653 12.999C14.453 12.8469 14.4991 12.6743 14.499 12.4987C14.4988 12.3231 14.4524 12.1506 14.3645 11.9986L8.86594 2.49904C8.7781 2.34728 8.6519 2.22129 8.49999 2.1337C8.34809 2.04611 8.17582 2 8.00047 2C7.82512 2 7.65285 2.04611 7.50095 2.1337C7.34904 2.22129 7.22284 2.34728 7.135 2.49904V2.49904Z"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8 12C8.41421 12 8.75 11.6642 8.75 11.25C8.75 10.8358 8.41421 10.5 8 10.5C7.58579 10.5 7.25 10.8358 7.25 11.25C7.25 11.6642 7.58579 12 8 12Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
|
||||
WarningIcon.displayName = 'NhostWarningIcon';
|
||||
|
||||
export default forwardRef(WarningIcon);
|
||||
@@ -0,0 +1 @@
|
||||
export { default as WarningIcon } from './WarningIcon';
|
||||
@@ -9,9 +9,11 @@ import { Dropdown } from '@/components/ui/v2/Dropdown';
|
||||
import { IconButton } from '@/components/ui/v2/IconButton';
|
||||
import { DotsVerticalIcon } from '@/components/ui/v2/icons/DotsVerticalIcon';
|
||||
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
|
||||
import { WarningIcon } from '@/components/ui/v2/icons/WarningIcon';
|
||||
import { List } from '@/components/ui/v2/List';
|
||||
import { ListItem } from '@/components/ui/v2/ListItem';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
||||
import { CreatePATForm } from '@/features/account/settings/components/CreatePATForm';
|
||||
import { getToastStyleProps } from '@/utils/constants/settings';
|
||||
import { getServerError } from '@/utils/getServerError';
|
||||
@@ -133,69 +135,91 @@ export default function PATSettings() {
|
||||
<Box className="grid grid-flow-row gap-2">
|
||||
{availablePersonalAccessTokens.length > 0 && (
|
||||
<List>
|
||||
{availablePersonalAccessTokens.map((pat, index) => (
|
||||
<Fragment key={pat.id}>
|
||||
<ListItem.Root
|
||||
className="grid grid-cols-3 gap-2 px-4 pr-12"
|
||||
secondaryAction={
|
||||
<Dropdown.Root>
|
||||
<Dropdown.Trigger
|
||||
asChild
|
||||
hideChevron
|
||||
className="absolute right-4 top-1/2 -translate-y-1/2"
|
||||
>
|
||||
<IconButton
|
||||
variant="borderless"
|
||||
color="secondary"
|
||||
disabled={maintenanceActive}
|
||||
aria-label={`More options for ${pat.name}`}
|
||||
{availablePersonalAccessTokens.map((pat, index) => {
|
||||
const tokenHasExpired = new Date(pat.expiresAt) < new Date();
|
||||
|
||||
return (
|
||||
<Fragment key={pat.id}>
|
||||
<ListItem.Root
|
||||
className="grid grid-cols-3 gap-2 px-4 pr-12"
|
||||
secondaryAction={
|
||||
<Dropdown.Root>
|
||||
<Dropdown.Trigger
|
||||
asChild
|
||||
hideChevron
|
||||
className="absolute right-4 top-1/2 -translate-y-1/2"
|
||||
>
|
||||
<DotsVerticalIcon />
|
||||
</IconButton>
|
||||
</Dropdown.Trigger>
|
||||
<IconButton
|
||||
variant="borderless"
|
||||
color="secondary"
|
||||
disabled={maintenanceActive}
|
||||
aria-label={`More options for ${pat.name}`}
|
||||
>
|
||||
<DotsVerticalIcon />
|
||||
</IconButton>
|
||||
</Dropdown.Trigger>
|
||||
|
||||
<Dropdown.Content
|
||||
menu
|
||||
PaperProps={{ className: 'w-32' }}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<Dropdown.Item onClick={() => handleConfirmDelete(pat)}>
|
||||
<Text className="font-medium" color="error">
|
||||
Delete
|
||||
</Text>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Content>
|
||||
</Dropdown.Root>
|
||||
}
|
||||
>
|
||||
<ListItem.Text className="truncate">{pat.name}</ListItem.Text>
|
||||
<Dropdown.Content
|
||||
menu
|
||||
PaperProps={{ className: 'w-32' }}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<Dropdown.Item
|
||||
onClick={() => handleConfirmDelete(pat)}
|
||||
>
|
||||
<Text className="font-medium" color="error">
|
||||
Delete
|
||||
</Text>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Content>
|
||||
</Dropdown.Root>
|
||||
}
|
||||
>
|
||||
<ListItem.Text
|
||||
className="truncate"
|
||||
color={tokenHasExpired ? 'warning' : 'primary'}
|
||||
>
|
||||
<span className="mr-2">{pat.name}</span>
|
||||
{tokenHasExpired && (
|
||||
<Tooltip title="This personal access token is expired.">
|
||||
<WarningIcon className="h-4 w-4" />
|
||||
</Tooltip>
|
||||
)}
|
||||
</ListItem.Text>
|
||||
|
||||
<Text className="truncate">
|
||||
{new Date(pat.expiresAt).toLocaleDateString()}
|
||||
</Text>
|
||||
<Text
|
||||
className="truncate"
|
||||
color={tokenHasExpired ? 'warning' : 'primary'}
|
||||
>
|
||||
{new Date(pat.expiresAt).toLocaleDateString()}
|
||||
</Text>
|
||||
|
||||
<Text className="truncate">
|
||||
{new Date(pat.createdAt).toLocaleDateString()}
|
||||
</Text>
|
||||
</ListItem.Root>
|
||||
<Text
|
||||
className="truncate"
|
||||
color={tokenHasExpired ? 'warning' : 'primary'}
|
||||
>
|
||||
{new Date(pat.createdAt).toLocaleDateString()}
|
||||
</Text>
|
||||
</ListItem.Root>
|
||||
|
||||
<Divider
|
||||
component="li"
|
||||
className={twMerge(
|
||||
index === availablePersonalAccessTokens.length - 1
|
||||
? '!mt-4'
|
||||
: '!my-4',
|
||||
)}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
<Divider
|
||||
component="li"
|
||||
className={twMerge(
|
||||
index === availablePersonalAccessTokens.length - 1
|
||||
? '!mt-4'
|
||||
: '!my-4',
|
||||
)}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
)}
|
||||
|
||||
|
||||
@@ -66,7 +66,10 @@ export default function WebAuthnSettings() {
|
||||
config: {
|
||||
auth: {
|
||||
method: {
|
||||
webauthn: values,
|
||||
webauthn: {...values,
|
||||
relyingParty: {
|
||||
name: currentProject.name,
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/v2/Button';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import hasuraMetadataQuery from '@/tests/msw/mocks/rest/hasuraMetadataQuery';
|
||||
import tableQuery from '@/tests/msw/mocks/rest/tableQuery';
|
||||
import tokenQuery from '@/tests/msw/mocks/rest/tokenQuery';
|
||||
import type { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
@@ -34,7 +35,7 @@ const defaultParameters = {
|
||||
},
|
||||
},
|
||||
msw: {
|
||||
handlers: [tableQuery, hasuraMetadataQuery],
|
||||
handlers: [tokenQuery, tableQuery, hasuraMetadataQuery],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -187,11 +187,11 @@ export default async function fetchTable({
|
||||
const queryError = responseData as QueryError;
|
||||
const schemaNotFound =
|
||||
POSTGRESQL_ERROR_CODES.SCHEMA_NOT_FOUND ===
|
||||
queryError.internal.error.status_code;
|
||||
queryError.internal?.error?.status_code;
|
||||
|
||||
const tableNotFound =
|
||||
POSTGRESQL_ERROR_CODES.TABLE_NOT_FOUND ===
|
||||
queryError.internal.error.status_code;
|
||||
queryError.internal?.error?.status_code;
|
||||
|
||||
if (schemaNotFound || tableNotFound) {
|
||||
return {
|
||||
@@ -203,7 +203,7 @@ export default async function fetchTable({
|
||||
}
|
||||
|
||||
if (
|
||||
queryError.internal.error.status_code ===
|
||||
queryError.internal?.error?.status_code ===
|
||||
POSTGRESQL_ERROR_CODES.COLUMNS_NOT_FOUND
|
||||
) {
|
||||
return {
|
||||
@@ -214,7 +214,7 @@ export default async function fetchTable({
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(queryError.internal.error.message);
|
||||
throw new Error(queryError.internal?.error?.message);
|
||||
}
|
||||
|
||||
if ('error' in responseData) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import tokenQuery from '@/tests/msw/mocks/rest/tokenQuery';
|
||||
import { render, screen, waitFor } from '@/tests/testUtils';
|
||||
import { graphql } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
@@ -5,6 +6,7 @@ import { beforeAll, expect, test } from 'vitest';
|
||||
import HasuraCorsDomainSettings from './HasuraCorsDomainSettings';
|
||||
|
||||
const server = setupServer(
|
||||
tokenQuery,
|
||||
graphql.query('GetHasuraSettings', (_req, res, ctx) =>
|
||||
res(
|
||||
ctx.data({
|
||||
@@ -15,7 +17,14 @@ const server = setupServer(
|
||||
version: 'v2.25.1-ce',
|
||||
settings: {
|
||||
corsDomain: ['*'],
|
||||
enableAllowList: false,
|
||||
enableRemoteSchemaPermissions: false,
|
||||
enableConsole: false,
|
||||
devMode: false,
|
||||
enabledAPIs: [],
|
||||
},
|
||||
logs: [],
|
||||
events: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -56,7 +65,14 @@ test('should enable switch by default when CORS domain is set to one or more dom
|
||||
version: 'v2.25.1-ce',
|
||||
settings: {
|
||||
corsDomain: ['https://example.com', 'https://*.example.com'],
|
||||
enableAllowList: false,
|
||||
enableRemoteSchemaPermissions: false,
|
||||
enableConsole: false,
|
||||
devMode: false,
|
||||
enabledAPIs: [],
|
||||
},
|
||||
logs: [],
|
||||
events: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Project, Workspace } from '@/types/application';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import { getHasuraAdminSecret } from '@/utils/env';
|
||||
import { GetWorkspaceAndProjectDocument } from '@/utils/__generated__/graphql';
|
||||
import { useNhostClient, useUserData } from '@nhost/nextjs';
|
||||
import { useAuthenticationStatus, useNhostClient } from '@nhost/nextjs';
|
||||
import type { RefetchOptions } from '@tanstack/react-query';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/router';
|
||||
@@ -34,14 +34,24 @@ export interface UseCurrentWorkspaceAndProjectReturnType {
|
||||
|
||||
export default function useCurrentWorkspaceAndProject(): UseCurrentWorkspaceAndProjectReturnType {
|
||||
const client = useNhostClient();
|
||||
const user = useUserData();
|
||||
const isPlatform = useIsPlatform();
|
||||
const { isAuthenticated, isLoading: isAuthLoading } =
|
||||
useAuthenticationStatus();
|
||||
|
||||
const {
|
||||
query: { workspaceSlug, appSlug },
|
||||
isReady,
|
||||
isReady: isRouterReady,
|
||||
} = useRouter();
|
||||
|
||||
const isWorkspaceSlugAvailable = Boolean(workspaceSlug);
|
||||
|
||||
const shouldFetchWorkspaceAndProject =
|
||||
isPlatform &&
|
||||
isRouterReady &&
|
||||
isWorkspaceSlugAvailable &&
|
||||
isAuthenticated &&
|
||||
!isAuthLoading;
|
||||
|
||||
// We can't use the hook exported by the codegen here because there are cases
|
||||
// where it doesn't target the Nhost backend, but the currently active project
|
||||
// instead.
|
||||
@@ -59,7 +69,7 @@ export default function useCurrentWorkspaceAndProject(): UseCurrentWorkspaceAndP
|
||||
}),
|
||||
{
|
||||
keepPreviousData: true,
|
||||
enabled: isPlatform && isReady && !!workspaceSlug && !!user,
|
||||
enabled: shouldFetchWorkspaceAndProject,
|
||||
// multiple components are relying on this query, so we don't want to
|
||||
// refetch it too often - kind of a hack, should be improved later
|
||||
staleTime: 1000,
|
||||
@@ -142,7 +152,7 @@ export default function useCurrentWorkspaceAndProject(): UseCurrentWorkspaceAndP
|
||||
return {
|
||||
currentWorkspace,
|
||||
currentProject,
|
||||
loading: response ? false : isFetching,
|
||||
loading: response ? false : isFetching || isAuthLoading,
|
||||
error: response?.error
|
||||
? new Error(error?.message || 'Unknown error occurred.')
|
||||
: null,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ProjectFragment } from '@/utils/__generated__/graphql';
|
||||
import {
|
||||
getAuthServiceUrl,
|
||||
getDatabaseServiceUrl,
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
getStorageServiceUrl,
|
||||
isPlatform,
|
||||
} from '@/utils/env';
|
||||
import type { ProjectFragment } from '@/utils/__generated__/graphql';
|
||||
|
||||
export type NhostService =
|
||||
| 'auth'
|
||||
|
||||
@@ -123,7 +123,7 @@ export default function SystemEnvironmentVariableSettings() {
|
||||
return (
|
||||
<SettingsContainer
|
||||
title="System Environment Variables"
|
||||
description="Environment Variables are key-value pairs configured outside your source code. They are used to store environment-specific values such as API keys."
|
||||
description="System environment variables are automatically generated from the configuration file and your project's subdomain and region."
|
||||
docsLink="https://docs.nhost.io/platform/environment-variables#system-environment-variables"
|
||||
rootClassName="gap-0"
|
||||
className="mt-2 mb-2.5 px-0"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mockApplication, mockWorkspace } from '@/tests/mocks';
|
||||
import tokenQuery from '@/tests/msw/mocks/rest/tokenQuery';
|
||||
import { queryClient, render, screen } from '@/tests/testUtils';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
@@ -34,6 +35,7 @@ vi.mock('next/router', () => ({
|
||||
}));
|
||||
|
||||
const server = setupServer(
|
||||
tokenQuery,
|
||||
rest.get('https://local.graphql.nhost.run/v1', (_req, res, ctx) =>
|
||||
res(ctx.status(200)),
|
||||
),
|
||||
@@ -135,6 +137,7 @@ test('should render an empty state when GitHub is connected, but there are no de
|
||||
|
||||
test('should render a list of deployments', async () => {
|
||||
server.use(
|
||||
tokenQuery,
|
||||
rest.post('https://local.graphql.nhost.run/v1', async (_req, res, ctx) => {
|
||||
const { operationName } = await _req.json();
|
||||
|
||||
@@ -194,6 +197,7 @@ test('should render a list of deployments', async () => {
|
||||
|
||||
test('should disable redeployments if a deployment is already in progress', async () => {
|
||||
server.use(
|
||||
tokenQuery,
|
||||
rest.post('https://local.graphql.nhost.run/v1', async (req, res, ctx) => {
|
||||
const { operationName } = await req.json();
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
resourcesUpdatedQuery,
|
||||
} from '@/tests/msw/mocks/graphql/resourceSettingsQuery';
|
||||
import updateConfigMutation from '@/tests/msw/mocks/graphql/updateConfigMutation';
|
||||
import tokenQuery from '@/tests/msw/mocks/rest/tokenQuery';
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
@@ -35,6 +36,7 @@ vi.mock('next/router', () => ({
|
||||
}));
|
||||
|
||||
const server = setupServer(
|
||||
tokenQuery,
|
||||
resourcesAvailableQuery,
|
||||
getProPlanOnlyQuery,
|
||||
getWorkspaceAndProjectQuery,
|
||||
|
||||
@@ -88,6 +88,7 @@ export const mockWorkspace: Workspace = {
|
||||
slug: 'test-workspace',
|
||||
workspaceMembers: [],
|
||||
projects: [mockApplication],
|
||||
creatorUserId: '1',
|
||||
};
|
||||
|
||||
export const mockSession: NhostSession = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { rest } from 'msw';
|
||||
|
||||
const hasuraMetadataQuery = rest.post(
|
||||
'http://localhost:8080/v1/metadata',
|
||||
'https://local.hasura.nhost.run/v1/metadata',
|
||||
(_req, res, ctx) =>
|
||||
res(
|
||||
ctx.delay(250),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { rest } from 'msw';
|
||||
|
||||
const tableQuery = rest.post(
|
||||
'http://localhost:8080/v2/query',
|
||||
'https://local.hasura.nhost.run/v2/query',
|
||||
async (req, res, ctx) => {
|
||||
const body = await req.json();
|
||||
|
||||
|
||||
32
dashboard/src/tests/msw/mocks/rest/tokenQuery.ts
Normal file
32
dashboard/src/tests/msw/mocks/rest/tokenQuery.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import type { NhostSession } from '@nhost/nextjs';
|
||||
import { rest } from 'msw';
|
||||
|
||||
const tokenQuery = rest.post(
|
||||
`https://local.auth.nhost.run/v1/token`,
|
||||
(_req, res, ctx) =>
|
||||
res(
|
||||
ctx.json<NhostSession>({
|
||||
accessToken: faker.datatype.string(40),
|
||||
refreshToken: faker.datatype.uuid(),
|
||||
accessTokenExpiresIn: 900,
|
||||
user: {
|
||||
id: faker.datatype.uuid(),
|
||||
createdAt: faker.date.past().toUTCString(),
|
||||
displayName: `${faker.name.firstName()} ${faker.name.lastName()}`,
|
||||
avatarUrl: faker.internet.avatar(),
|
||||
locale: 'en',
|
||||
isAnonymous: false,
|
||||
emailVerified: true,
|
||||
defaultRole: 'user',
|
||||
roles: ['user', 'me'],
|
||||
phoneNumber: null,
|
||||
phoneNumberVerified: false,
|
||||
activeMfaType: null,
|
||||
metadata: {},
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
export default tokenQuery;
|
||||
@@ -32,13 +32,13 @@ process.env = {
|
||||
NODE_ENV: 'development',
|
||||
NEXT_PUBLIC_NHOST_PLATFORM: 'false',
|
||||
NEXT_PUBLIC_ENV: 'dev',
|
||||
NEXT_PUBLIC_NHOST_AUTH_URL: 'https://localdev.nhost.run/v1/auth',
|
||||
NEXT_PUBLIC_NHOST_FUNCTIONS_URL: 'https://localdev.nhost.run/v1/functions',
|
||||
NEXT_PUBLIC_NHOST_GRAPHQL_URL: 'https://localdev.nhost.run/v1/graphql',
|
||||
NEXT_PUBLIC_NHOST_STORAGE_URL: 'https://localdev.nhost.run/v1/storage',
|
||||
NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL: 'http://localhost:9695',
|
||||
NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL: 'http://localhost:9693',
|
||||
NEXT_PUBLIC_NHOST_HASURA_API_URL: 'http://localhost:8080',
|
||||
NEXT_PUBLIC_NHOST_AUTH_URL: 'https://local.auth.nhost.run/v1',
|
||||
NEXT_PUBLIC_NHOST_FUNCTIONS_URL: 'https://local.functions.nhost.run/v1',
|
||||
NEXT_PUBLIC_NHOST_GRAPHQL_URL: 'https://local.graphql.nhost.run/v1',
|
||||
NEXT_PUBLIC_NHOST_STORAGE_URL: 'https://local.storage.nhost.run/v1',
|
||||
NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL: 'https://local.hasura.nhost.run',
|
||||
NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL: 'https://local.hasura.nhost.run',
|
||||
NEXT_PUBLIC_NHOST_HASURA_API_URL: 'https://local.hasura.nhost.run',
|
||||
};
|
||||
|
||||
export const queryClient = new QueryClient({
|
||||
|
||||
@@ -33,5 +33,5 @@
|
||||
"incremental": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules", ".nhost", "nhost", "functions"]
|
||||
"exclude": ["node_modules", ".nhost", "nhost", "functions", "playwright.config.ts"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @nhost/docs
|
||||
|
||||
## 0.3.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a5305e6b5: docs: update old URLs to the new format
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cc02902cb: chore(docs): update environment variable documentation
|
||||
|
||||
## 0.3.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -63,8 +63,8 @@ Follow this guide to sign in users with Apple.
|
||||
- Click the checkbox to enable "Sign in with Apple".
|
||||
- Click **Configure** next to "Sign in with Apple".
|
||||
- Make sure your newly created Bundle ID is selected under Primary App ID.
|
||||
- Add your base auth domain under "Domains and Subdomains". E.g. `<subdomain>.nhost.run`.
|
||||
- Add the full callback URL under "Return URLs". E.g. `https://<subdomain>.nhost.run/v1/auth/signin/provider/apple/callback`.
|
||||
- Add your base auth domain under "Domains and Subdomains". E.g. `<subdomain>.auth.<region>.nhost.run`.
|
||||
- Add the full callback URL under "Return URLs". E.g. `https://<subdomain>.auth.<region>.nhost.run/v1/signin/provider/apple/callback`.
|
||||
- Click **Next**.
|
||||
- Click **Done**.
|
||||
- Click **Continue** in the top right corner.
|
||||
|
||||
@@ -138,13 +138,21 @@ export default server
|
||||
|
||||
Add `STRIPE_SECRET_KEY` as an environment variable.
|
||||
|
||||
If you're using Nhost, add `STRIPE_SECRET_KEY` to `.env.development` like this:
|
||||
If you're using Nhost, add `STRIPE_SECRET_KEY` to `nhost.toml` like this:
|
||||
|
||||
```
|
||||
[[ global.environment ]]
|
||||
name=STRIPE_SECRET_KEY
|
||||
value='{{ secrets.STRIPE_SECRET_KEY }}'
|
||||
```
|
||||
|
||||
And then add to your `.secrets` file:
|
||||
|
||||
```
|
||||
STRIPE_SECRET_KEY=sk_test_***
|
||||
```
|
||||
|
||||
And add the production key (`sk_live_***`) to [environment variables](/platform/environment-variables) in the Nhost dashboard.
|
||||
In production set your secret with your stripe production key (`sk_live_***`) in the Nhost dashboard.
|
||||
|
||||
Learn more about [Stripe API keys](https://stripe.com/docs/keys#obtain-api-keys).
|
||||
|
||||
|
||||
@@ -6,10 +6,6 @@ image: /img/og/platform/environment-variables.png
|
||||
|
||||
Environment Variables are key-value pairs configured outside your source code. They are used to store environment-specific values such as API keys.
|
||||
|
||||
You can manage your project's Environment Variables in Nhost Dashboard under **Variables**. When you define a new variable, you can set one value for **production** and one for **development**.
|
||||
|
||||

|
||||
|
||||
Environment Variables are available for:
|
||||
|
||||
- [Hasura GraphQL Engine](/graphql)
|
||||
@@ -17,13 +13,44 @@ Environment Variables are available for:
|
||||
|
||||
When an Environment Variable has updated the changes happen immediately for Hasura GraphQL Engine. For Serverless Functions, a new deployment via [Git](/platform/git) is required.
|
||||
|
||||
## Custom Environment Variables
|
||||
|
||||
You can manage your project's Environment Variables in the Nhost Dashboard or by using the configuration file.
|
||||
|
||||
### Dashboard
|
||||
|
||||

|
||||
|
||||
Environment Variables can be managed in the Nhost Dashboard under **Settings** → **Environment Variables**.
|
||||
|
||||
### Configuration File
|
||||
|
||||
Environment Variables can also be managed by adding new `[[global.environment]]` sections to the `nhost.toml` file.
|
||||
|
||||
```toml
|
||||
[global]
|
||||
[[global.environment]]
|
||||
name = 'MY_ENV_VAR'
|
||||
value = '<first-value>'
|
||||
|
||||
[[global.environment]]
|
||||
name = 'MY_OTHER_ENV_VAR'
|
||||
value = '<second-value>'
|
||||
|
||||
# ... omitted for brevity
|
||||
```
|
||||
|
||||
These environment variables will also be available on the Nhost Dashboard after committing and pushing the changes to your Git repository.
|
||||
|
||||
## System Environment Variables
|
||||
|
||||
System Environment Variables are automatically available in production and during local development. The following system Environment Variables are available:
|
||||
System environment variables are automatically generated from the configuration file and your project's subdomain and region.
|
||||
|
||||
The following system environment variables are available:
|
||||
|
||||
- `NHOST_ADMIN_SECRET`
|
||||
- `NHOST_WEBHOOK_SECRET`
|
||||
- `NHOST_BACKEND_URL` ([deprecated](https://github.com/nhost/nhost/discussions/1319))
|
||||
- ~~`NHOST_BACKEND_URL`~~ ([deprecated](https://github.com/nhost/nhost/discussions/1319))
|
||||
- `NHOST_SUBDOMAIN`
|
||||
- `NHOST_REGION`
|
||||
- `NHOST_HASURA_URL`
|
||||
@@ -33,7 +60,9 @@ System Environment Variables are automatically available in production and durin
|
||||
- `NHOST_FUNCTIONS_URL`
|
||||
- `NHOST_JWT_SECRET`
|
||||
|
||||
Example values:
|
||||
`NHOST_ADMIN_SECRET`, `NHOST_WEBHOOK_SECRET` and `NHOST_JWT_SECRET` are populated with values from the configuration file. The rest of the system environment variables are populated with values from your project's subdomain and region.
|
||||
|
||||
**Example values**:
|
||||
|
||||
```text
|
||||
NHOST_ADMIN_SECRET=e7w36ag287qn5qry795f6ymm57qgvqup
|
||||
@@ -58,10 +87,3 @@ NHOST_FUNCTIONS_URL=https://abc123abc.functions.eu-central-1.nhost.run/v1
|
||||
|
||||
NHOST_JWT_SECRET={"type": "HS256", "key": "vumpbe2w2mgaqj5yqfp7dvxu6kywtvsgb68ejpdaqxerea8jwrsszdp2dhkjxsh4df69pzm3ja6ukedx8ja43zdt6q9kgbgg2w9vh2sedeppukud9a2qzy29v3afdn7m"}
|
||||
```
|
||||
|
||||
## Development Environment Variables
|
||||
|
||||
When developing locally using the [CLI](/cli), Environment Variables set in `.env.development` are available in your local environment. There are two ways to manage them:
|
||||
|
||||
1. Edit the `.env.development` file manually.
|
||||
2. Add development Environment Variables in the Nhost Dashboard and use `nhost env pull` to sync them. This way, your team members will also have access to the same Environment Variables.
|
||||
|
||||
@@ -48,7 +48,7 @@ Learn more about [`upload()`](/reference/javascript/storage/upload).
|
||||
<TabItem value="http" label="HTTP" default>
|
||||
|
||||
```http
|
||||
POST /v1/storage/files HTTP/1.1
|
||||
POST https://local.storage.nhost.run/v1/files HTTP/1.1
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -80,7 +80,7 @@ Learn more about [`getPublicUrl()`](/reference/javascript/storage/get-public-url
|
||||
<TabItem value="http" label="HTTP" default>
|
||||
|
||||
```http
|
||||
GET /v1/storage/files/{file_id} HTTP/1.1
|
||||
GET https://local.storage.nhost.run/v1/files/{file_id} HTTP/1.1
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -109,7 +109,7 @@ Learn more about [`getPresignedUrl()`](/reference/javascript/storage/get-presign
|
||||
<TabItem value="http" label="HTTP" default>
|
||||
|
||||
```http
|
||||
GET /v1/storage/files/{file_id}/presignedurl HTTP/1.1
|
||||
GET https://local.storage.nhost.run/v1/files/{file_id}/presignedurl HTTP/1.1
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -132,7 +132,7 @@ Learn more about [`delete()`](/reference/javascript/storage/delete).
|
||||
<TabItem value="http" label="HTTP" default>
|
||||
|
||||
```http
|
||||
DELETE /v1/storage/files/{file_id} HTTP/1.1
|
||||
DELETE https://local.storage.nhost.run/v1/files/{file_id} HTTP/1.1
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -193,7 +193,7 @@ Image Transformation works on both public and pre-signed URLs.
|
||||
**Example**: Transform an image to 500px width (`?w=500`):
|
||||
|
||||
```text
|
||||
https://[subdomain].nhost.run/v1/storage/files/08e6fa32-0880-4d0e-a832-278198acb363?w=500
|
||||
https://[subdomain].storage.[region].nhost.run/v1/files/08e6fa32-0880-4d0e-a832-278198acb363?w=500
|
||||
```
|
||||
|
||||
## Example: CRM System
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/docs",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @nhost-examples/codegen-react-apollo
|
||||
|
||||
## 0.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b3b64a3b7: chore(deps): bump `@types/react` to `v18.2.14` and `@types/react-dom` to `v18.2.6`
|
||||
- Updated dependencies [b3b64a3b7]
|
||||
- @nhost/react-apollo@5.0.29
|
||||
- @nhost/react@2.0.25
|
||||
|
||||
## 0.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-apollo",
|
||||
"version": "0.1.9",
|
||||
"version": "0.1.10",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"codegen": "graphql-codegen",
|
||||
@@ -29,8 +29,8 @@
|
||||
"@graphql-typed-document-node/core": "^3.1.1",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@types/node": "^18.11.9",
|
||||
"@types/react": "^18.0.34",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@vitejs/plugin-react": "^3.0.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"postcss": "^8.4.19",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @nhost-examples/codegen-react-query
|
||||
|
||||
## 0.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b3b64a3b7: chore(deps): bump `@types/react` to `v18.2.14` and `@types/react-dom` to `v18.2.6`
|
||||
- Updated dependencies [b3b64a3b7]
|
||||
- @nhost/react@2.0.25
|
||||
|
||||
## 0.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-query",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.11",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"codegen": "graphql-codegen",
|
||||
@@ -30,8 +30,8 @@
|
||||
"@graphql-typed-document-node/core": "^3.1.1",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/react": "^18.0.34",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"eslint": "^8.23.0",
|
||||
"postcss": "^8.4.20",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @nhost-examples/react-urql
|
||||
|
||||
## 0.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b3b64a3b7: chore(deps): bump `@types/react` to `v18.2.14` and `@types/react-dom` to `v18.2.6`
|
||||
- Updated dependencies [b3b64a3b7]
|
||||
- @nhost/react-urql@2.0.26
|
||||
- @nhost/react@2.0.25
|
||||
|
||||
## 0.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-urql",
|
||||
"private": true,
|
||||
"version": "0.0.6",
|
||||
"version": "0.0.7",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
@@ -23,8 +23,8 @@
|
||||
"@graphql-typed-document-node/core": "^3.1.1",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/react": "^18.0.34",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@vitejs/plugin-react": "^3.0.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"postcss": "^8.4.19",
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @nhost-examples/nextjs
|
||||
|
||||
## 0.1.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b3b64a3b7: chore(deps): bump `@types/react` to `v18.2.14` and `@types/react-dom` to `v18.2.6`
|
||||
- Updated dependencies [b3b64a3b7]
|
||||
- @nhost/react-apollo@5.0.29
|
||||
- @nhost/react@2.0.25
|
||||
- @nhost/nextjs@1.13.31
|
||||
|
||||
## 0.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
1
examples/nextjs/nhost/metadata/backend_configs.yaml
Normal file
1
examples/nextjs/nhost/metadata/backend_configs.yaml
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
1
examples/nextjs/nhost/metadata/metrics_config.yaml
Normal file
1
examples/nextjs/nhost/metadata/metrics_config.yaml
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
1
examples/nextjs/nhost/metadata/opentelemetry.yaml
Normal file
1
examples/nextjs/nhost/metadata/opentelemetry.yaml
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/nextjs",
|
||||
"version": "0.1.11",
|
||||
"version": "0.1.12",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -32,7 +32,7 @@
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^12.2.5",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/react": "18.0.34",
|
||||
"@types/react": "^18.2.14",
|
||||
"@xstate/inspect": "^0.6.2",
|
||||
"eslint-config-next": "12.0.10",
|
||||
"typescript": "^4.8.2",
|
||||
|
||||
4
examples/node-storage/.gitignore
vendored
4
examples/node-storage/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
.secrets.nhost
|
||||
.secrets
|
||||
.nhost
|
||||
cat.jpg
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost-examples/node-storage
|
||||
|
||||
## 0.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2cdb13b3e: fix(upload): allow specifying `id` and `name` only when not using `form-data`
|
||||
- @nhost/nhost-js@2.2.11
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
6
examples/node-storage/nhost/metadata/actions.yaml
Normal file
6
examples/node-storage/nhost/metadata/actions.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
actions: []
|
||||
custom_types:
|
||||
enums: []
|
||||
input_objects: []
|
||||
objects: []
|
||||
scalars: []
|
||||
1
examples/node-storage/nhost/metadata/allow_list.yaml
Normal file
1
examples/node-storage/nhost/metadata/allow_list.yaml
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
1
examples/node-storage/nhost/metadata/api_limits.yaml
Normal file
1
examples/node-storage/nhost/metadata/api_limits.yaml
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
1
examples/node-storage/nhost/metadata/cron_triggers.yaml
Normal file
1
examples/node-storage/nhost/metadata/cron_triggers.yaml
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,14 @@
|
||||
- name: default
|
||||
kind: postgres
|
||||
configuration:
|
||||
connection_info:
|
||||
database_url:
|
||||
from_env: HASURA_GRAPHQL_DATABASE_URL
|
||||
isolation_level: read-committed
|
||||
pool_settings:
|
||||
connection_lifetime: 600
|
||||
idle_timeout: 180
|
||||
max_connections: 50
|
||||
retries: 1
|
||||
use_prepared_statements: true
|
||||
tables: "!include default/tables/tables.yaml"
|
||||
@@ -0,0 +1,23 @@
|
||||
table:
|
||||
name: provider_requests
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
id:
|
||||
custom_name: id
|
||||
options:
|
||||
custom_name: options
|
||||
custom_column_names:
|
||||
id: id
|
||||
options: options
|
||||
custom_name: authProviderRequests
|
||||
custom_root_fields:
|
||||
delete: deleteAuthProviderRequests
|
||||
delete_by_pk: deleteAuthProviderRequest
|
||||
insert: insertAuthProviderRequests
|
||||
insert_one: insertAuthProviderRequest
|
||||
select: authProviderRequests
|
||||
select_aggregate: authProviderRequestsAggregate
|
||||
select_by_pk: authProviderRequest
|
||||
update: updateAuthProviderRequests
|
||||
update_by_pk: updateAuthProviderRequest
|
||||
@@ -0,0 +1,28 @@
|
||||
table:
|
||||
name: providers
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
id:
|
||||
custom_name: id
|
||||
custom_column_names:
|
||||
id: id
|
||||
custom_name: authProviders
|
||||
custom_root_fields:
|
||||
delete: deleteAuthProviders
|
||||
delete_by_pk: deleteAuthProvider
|
||||
insert: insertAuthProviders
|
||||
insert_one: insertAuthProvider
|
||||
select: authProviders
|
||||
select_aggregate: authProvidersAggregate
|
||||
select_by_pk: authProvider
|
||||
update: updateAuthProviders
|
||||
update_by_pk: updateAuthProvider
|
||||
array_relationships:
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: provider_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
@@ -0,0 +1,26 @@
|
||||
table:
|
||||
name: refresh_token_types
|
||||
schema: auth
|
||||
is_enum: true
|
||||
configuration:
|
||||
column_config: {}
|
||||
custom_column_names: {}
|
||||
custom_name: authRefreshTokenTypes
|
||||
custom_root_fields:
|
||||
delete: deleteAuthRefreshTokenTypes
|
||||
delete_by_pk: deleteAuthRefreshTokenType
|
||||
insert: insertAuthRefreshTokenTypes
|
||||
insert_one: insertAuthRefreshTokenType
|
||||
select: authRefreshTokenTypes
|
||||
select_aggregate: authRefreshTokenTypesAggregate
|
||||
select_by_pk: authRefreshTokenType
|
||||
update: updateAuthRefreshTokenTypes
|
||||
update_by_pk: updateAuthRefreshTokenType
|
||||
array_relationships:
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: type
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
@@ -0,0 +1,55 @@
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
expires_at:
|
||||
custom_name: expiresAt
|
||||
refresh_token_hash:
|
||||
custom_name: refreshTokenHash
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
created_at: createdAt
|
||||
expires_at: expiresAt
|
||||
refresh_token_hash: refreshTokenHash
|
||||
user_id: userId
|
||||
custom_name: authRefreshTokens
|
||||
custom_root_fields:
|
||||
delete: deleteAuthRefreshTokens
|
||||
delete_by_pk: deleteAuthRefreshToken
|
||||
insert: insertAuthRefreshTokens
|
||||
insert_one: insertAuthRefreshToken
|
||||
select: authRefreshTokens
|
||||
select_aggregate: authRefreshTokensAggregate
|
||||
select_by_pk: authRefreshToken
|
||||
update: updateAuthRefreshTokens
|
||||
update_by_pk: updateAuthRefreshToken
|
||||
object_relationships:
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- expires_at
|
||||
- metadata
|
||||
- type
|
||||
- user_id
|
||||
filter:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
_and:
|
||||
- user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
- type:
|
||||
_eq: pat
|
||||
@@ -0,0 +1,35 @@
|
||||
table:
|
||||
name: roles
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
role:
|
||||
custom_name: role
|
||||
custom_column_names:
|
||||
role: role
|
||||
custom_name: authRoles
|
||||
custom_root_fields:
|
||||
delete: deleteAuthRoles
|
||||
delete_by_pk: deleteAuthRole
|
||||
insert: insertAuthRoles
|
||||
insert_one: insertAuthRole
|
||||
select: authRoles
|
||||
select_aggregate: authRolesAggregate
|
||||
select_by_pk: authRole
|
||||
update: updateAuthRoles
|
||||
update_by_pk: updateAuthRole
|
||||
array_relationships:
|
||||
- name: userRoles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: role
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: usersByDefaultRole
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: default_role
|
||||
table:
|
||||
name: users
|
||||
schema: auth
|
||||
@@ -0,0 +1,48 @@
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
access_token:
|
||||
custom_name: accessToken
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
id:
|
||||
custom_name: id
|
||||
provider_id:
|
||||
custom_name: providerId
|
||||
provider_user_id:
|
||||
custom_name: providerUserId
|
||||
refresh_token:
|
||||
custom_name: refreshToken
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
access_token: accessToken
|
||||
created_at: createdAt
|
||||
id: id
|
||||
provider_id: providerId
|
||||
provider_user_id: providerUserId
|
||||
refresh_token: refreshToken
|
||||
updated_at: updatedAt
|
||||
user_id: userId
|
||||
custom_name: authUserProviders
|
||||
custom_root_fields:
|
||||
delete: deleteAuthUserProviders
|
||||
delete_by_pk: deleteAuthUserProvider
|
||||
insert: insertAuthUserProviders
|
||||
insert_one: insertAuthUserProvider
|
||||
select: authUserProviders
|
||||
select_aggregate: authUserProvidersAggregate
|
||||
select_by_pk: authUserProvider
|
||||
update: updateAuthUserProviders
|
||||
update_by_pk: updateAuthUserProvider
|
||||
object_relationships:
|
||||
- name: provider
|
||||
using:
|
||||
foreign_key_constraint_on: provider_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
@@ -0,0 +1,36 @@
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
id:
|
||||
custom_name: id
|
||||
role:
|
||||
custom_name: role
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
created_at: createdAt
|
||||
id: id
|
||||
role: role
|
||||
user_id: userId
|
||||
custom_name: authUserRoles
|
||||
custom_root_fields:
|
||||
delete: deleteAuthUserRoles
|
||||
delete_by_pk: deleteAuthUserRole
|
||||
insert: insertAuthUserRoles
|
||||
insert_one: insertAuthUserRole
|
||||
select: authUserRoles
|
||||
select_aggregate: authUserRolesAggregate
|
||||
select_by_pk: authUserRole
|
||||
update: updateAuthUserRoles
|
||||
update_by_pk: updateAuthUserRole
|
||||
object_relationships:
|
||||
- name: roleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: role
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
@@ -0,0 +1,33 @@
|
||||
table:
|
||||
name: user_security_keys
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
credential_id:
|
||||
custom_name: credentialId
|
||||
credential_public_key:
|
||||
custom_name: credentialPublicKey
|
||||
id:
|
||||
custom_name: id
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
credential_id: credentialId
|
||||
credential_public_key: credentialPublicKey
|
||||
id: id
|
||||
user_id: userId
|
||||
custom_name: authUserSecurityKeys
|
||||
custom_root_fields:
|
||||
delete: deleteAuthUserSecurityKeys
|
||||
delete_by_pk: deleteAuthUserSecurityKey
|
||||
insert: insertAuthUserSecurityKeys
|
||||
insert_one: insertAuthUserSecurityKey
|
||||
select: authUserSecurityKeys
|
||||
select_aggregate: authUserSecurityKeysAggregate
|
||||
select_by_pk: authUserSecurityKey
|
||||
update: updateAuthUserSecurityKeys
|
||||
update_by_pk: updateAuthUserSecurityKey
|
||||
object_relationships:
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
@@ -0,0 +1,122 @@
|
||||
table:
|
||||
name: users
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
active_mfa_type:
|
||||
custom_name: activeMfaType
|
||||
avatar_url:
|
||||
custom_name: avatarUrl
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
default_role:
|
||||
custom_name: defaultRole
|
||||
disabled:
|
||||
custom_name: disabled
|
||||
display_name:
|
||||
custom_name: displayName
|
||||
email:
|
||||
custom_name: email
|
||||
email_verified:
|
||||
custom_name: emailVerified
|
||||
id:
|
||||
custom_name: id
|
||||
is_anonymous:
|
||||
custom_name: isAnonymous
|
||||
last_seen:
|
||||
custom_name: lastSeen
|
||||
locale:
|
||||
custom_name: locale
|
||||
new_email:
|
||||
custom_name: newEmail
|
||||
otp_hash:
|
||||
custom_name: otpHash
|
||||
otp_hash_expires_at:
|
||||
custom_name: otpHashExpiresAt
|
||||
otp_method_last_used:
|
||||
custom_name: otpMethodLastUsed
|
||||
password_hash:
|
||||
custom_name: passwordHash
|
||||
phone_number:
|
||||
custom_name: phoneNumber
|
||||
phone_number_verified:
|
||||
custom_name: phoneNumberVerified
|
||||
ticket:
|
||||
custom_name: ticket
|
||||
ticket_expires_at:
|
||||
custom_name: ticketExpiresAt
|
||||
totp_secret:
|
||||
custom_name: totpSecret
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
webauthn_current_challenge:
|
||||
custom_name: currentChallenge
|
||||
custom_column_names:
|
||||
active_mfa_type: activeMfaType
|
||||
avatar_url: avatarUrl
|
||||
created_at: createdAt
|
||||
default_role: defaultRole
|
||||
disabled: disabled
|
||||
display_name: displayName
|
||||
email: email
|
||||
email_verified: emailVerified
|
||||
id: id
|
||||
is_anonymous: isAnonymous
|
||||
last_seen: lastSeen
|
||||
locale: locale
|
||||
new_email: newEmail
|
||||
otp_hash: otpHash
|
||||
otp_hash_expires_at: otpHashExpiresAt
|
||||
otp_method_last_used: otpMethodLastUsed
|
||||
password_hash: passwordHash
|
||||
phone_number: phoneNumber
|
||||
phone_number_verified: phoneNumberVerified
|
||||
ticket: ticket
|
||||
ticket_expires_at: ticketExpiresAt
|
||||
totp_secret: totpSecret
|
||||
updated_at: updatedAt
|
||||
webauthn_current_challenge: currentChallenge
|
||||
custom_name: users
|
||||
custom_root_fields:
|
||||
delete: deleteUsers
|
||||
delete_by_pk: deleteUser
|
||||
insert: insertUsers
|
||||
insert_one: insertUser
|
||||
select: users
|
||||
select_aggregate: usersAggregate
|
||||
select_by_pk: user
|
||||
update: updateUsers
|
||||
update_by_pk: updateUser
|
||||
object_relationships:
|
||||
- name: defaultRoleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: default_role
|
||||
array_relationships:
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
- name: roles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: securityKeys
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_security_keys
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
@@ -0,0 +1,49 @@
|
||||
table:
|
||||
name: buckets
|
||||
schema: storage
|
||||
configuration:
|
||||
column_config:
|
||||
cache_control:
|
||||
custom_name: cacheControl
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
download_expiration:
|
||||
custom_name: downloadExpiration
|
||||
id:
|
||||
custom_name: id
|
||||
max_upload_file_size:
|
||||
custom_name: maxUploadFileSize
|
||||
min_upload_file_size:
|
||||
custom_name: minUploadFileSize
|
||||
presigned_urls_enabled:
|
||||
custom_name: presignedUrlsEnabled
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
custom_column_names:
|
||||
cache_control: cacheControl
|
||||
created_at: createdAt
|
||||
download_expiration: downloadExpiration
|
||||
id: id
|
||||
max_upload_file_size: maxUploadFileSize
|
||||
min_upload_file_size: minUploadFileSize
|
||||
presigned_urls_enabled: presignedUrlsEnabled
|
||||
updated_at: updatedAt
|
||||
custom_name: buckets
|
||||
custom_root_fields:
|
||||
delete: deleteBuckets
|
||||
delete_by_pk: deleteBucket
|
||||
insert: insertBuckets
|
||||
insert_one: insertBucket
|
||||
select: buckets
|
||||
select_aggregate: bucketsAggregate
|
||||
select_by_pk: bucket
|
||||
update: updateBuckets
|
||||
update_by_pk: updateBucket
|
||||
array_relationships:
|
||||
- name: files
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bucket_id
|
||||
table:
|
||||
name: files
|
||||
schema: storage
|
||||
@@ -0,0 +1,51 @@
|
||||
table:
|
||||
name: files
|
||||
schema: storage
|
||||
configuration:
|
||||
column_config:
|
||||
bucket_id:
|
||||
custom_name: bucketId
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
etag:
|
||||
custom_name: etag
|
||||
id:
|
||||
custom_name: id
|
||||
is_uploaded:
|
||||
custom_name: isUploaded
|
||||
mime_type:
|
||||
custom_name: mimeType
|
||||
name:
|
||||
custom_name: name
|
||||
size:
|
||||
custom_name: size
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
uploaded_by_user_id:
|
||||
custom_name: uploadedByUserId
|
||||
custom_column_names:
|
||||
bucket_id: bucketId
|
||||
created_at: createdAt
|
||||
etag: etag
|
||||
id: id
|
||||
is_uploaded: isUploaded
|
||||
mime_type: mimeType
|
||||
name: name
|
||||
size: size
|
||||
updated_at: updatedAt
|
||||
uploaded_by_user_id: uploadedByUserId
|
||||
custom_name: files
|
||||
custom_root_fields:
|
||||
delete: deleteFiles
|
||||
delete_by_pk: deleteFile
|
||||
insert: insertFiles
|
||||
insert_one: insertFile
|
||||
select: files
|
||||
select_aggregate: filesAggregate
|
||||
select_by_pk: file
|
||||
update: updateFiles
|
||||
update_by_pk: updateFile
|
||||
object_relationships:
|
||||
- name: bucket
|
||||
using:
|
||||
foreign_key_constraint_on: bucket_id
|
||||
@@ -0,0 +1,11 @@
|
||||
- "!include auth_provider_requests.yaml"
|
||||
- "!include auth_providers.yaml"
|
||||
- "!include auth_refresh_token_types.yaml"
|
||||
- "!include auth_refresh_tokens.yaml"
|
||||
- "!include auth_roles.yaml"
|
||||
- "!include auth_user_providers.yaml"
|
||||
- "!include auth_user_roles.yaml"
|
||||
- "!include auth_user_security_keys.yaml"
|
||||
- "!include auth_users.yaml"
|
||||
- "!include storage_buckets.yaml"
|
||||
- "!include storage_files.yaml"
|
||||
@@ -0,0 +1 @@
|
||||
disabled_for_roles: []
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
1
examples/node-storage/nhost/metadata/metrics_config.yaml
Normal file
1
examples/node-storage/nhost/metadata/metrics_config.yaml
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
1
examples/node-storage/nhost/metadata/network.yaml
Normal file
1
examples/node-storage/nhost/metadata/network.yaml
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
1
examples/node-storage/nhost/metadata/opentelemetry.yaml
Normal file
1
examples/node-storage/nhost/metadata/opentelemetry.yaml
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
1
examples/node-storage/nhost/metadata/remote_schemas.yaml
Normal file
1
examples/node-storage/nhost/metadata/remote_schemas.yaml
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
1
examples/node-storage/nhost/metadata/rest_endpoints.yaml
Normal file
1
examples/node-storage/nhost/metadata/rest_endpoints.yaml
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
1
examples/node-storage/nhost/metadata/version.yaml
Normal file
1
examples/node-storage/nhost/metadata/version.yaml
Normal file
@@ -0,0 +1 @@
|
||||
version: 3
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/node-storage",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"description": "This is an example of how to use the Storage with Node.js",
|
||||
"main": "src/index.mjs",
|
||||
@@ -14,9 +14,12 @@
|
||||
"@nhost/nhost-js": "*",
|
||||
"dotenv": "^16.1.3",
|
||||
"form-data": "^4.0.0",
|
||||
"node-fetch": "^3.3.0"
|
||||
"fs-extra": "^11.1.1",
|
||||
"node-fetch": "^3.3.0",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.15.11"
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/uuid": "^9.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
44
examples/node-storage/pnpm-lock.yaml
generated
44
examples/node-storage/pnpm-lock.yaml
generated
@@ -10,14 +10,23 @@ dependencies:
|
||||
form-data:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
fs-extra:
|
||||
specifier: ^11.1.1
|
||||
version: 11.1.1
|
||||
node-fetch:
|
||||
specifier: ^3.3.0
|
||||
version: 3.3.0
|
||||
uuid:
|
||||
specifier: ^9.0.0
|
||||
version: 9.0.0
|
||||
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^18.15.11
|
||||
version: 18.15.11
|
||||
'@types/uuid':
|
||||
specifier: ^9.0.1
|
||||
version: 9.0.1
|
||||
|
||||
packages:
|
||||
|
||||
@@ -32,6 +41,10 @@ packages:
|
||||
resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
|
||||
dev: true
|
||||
|
||||
/@types/uuid@9.0.1:
|
||||
resolution: {integrity: sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==}
|
||||
dev: true
|
||||
|
||||
/asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
dev: false
|
||||
@@ -92,6 +105,27 @@ packages:
|
||||
fetch-blob: 3.2.0
|
||||
dev: false
|
||||
|
||||
/fs-extra@11.1.1:
|
||||
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
|
||||
engines: {node: '>=14.14'}
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
jsonfile: 6.1.0
|
||||
universalify: 2.0.0
|
||||
dev: false
|
||||
|
||||
/graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
dev: false
|
||||
|
||||
/jsonfile@6.1.0:
|
||||
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||
dependencies:
|
||||
universalify: 2.0.0
|
||||
optionalDependencies:
|
||||
graceful-fs: 4.2.11
|
||||
dev: false
|
||||
|
||||
/jwt-decode@3.1.2:
|
||||
resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==}
|
||||
dev: false
|
||||
@@ -142,6 +176,16 @@ packages:
|
||||
engines: {node: '>=4'}
|
||||
dev: false
|
||||
|
||||
/universalify@2.0.0:
|
||||
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
dev: false
|
||||
|
||||
/uuid@9.0.0:
|
||||
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/web-streams-polyfill@3.2.1:
|
||||
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { NhostClient } from '@nhost/nhost-js'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
/**
|
||||
* Create a new Nhost client.
|
||||
|
||||
@@ -1,62 +1,12 @@
|
||||
import dotenv from 'dotenv'
|
||||
import FormData from 'form-data'
|
||||
import fetch from 'node-fetch'
|
||||
import { createClient } from './client.mjs'
|
||||
import { uploadFile } from './uploadFile.mjs'
|
||||
import { uploadFormData } from './uploadFormData.mjs'
|
||||
|
||||
dotenv.config()
|
||||
async function uploadFiles() {
|
||||
await uploadFormData()
|
||||
|
||||
const client = createClient()
|
||||
console.info('-----')
|
||||
|
||||
async function uploadImage() {
|
||||
try {
|
||||
// Download image from remote URL
|
||||
const response = await fetch(
|
||||
'https://hips.hearstapps.com/hmg-prod/images/cute-cat-photos-1593441022.jpg?crop=1.00xw:0.753xh;0,0.153xh&resize=1200:*'
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Image not found!')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer()
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append('file[]', Buffer.from(arrayBuffer), 'cat.jpg')
|
||||
|
||||
// Upload file to Nhost Storage
|
||||
const { error: uploadError, fileMetadata } = await client.storage.upload({
|
||||
formData,
|
||||
headers: { ...formData.getHeaders() }
|
||||
})
|
||||
|
||||
if (uploadError) {
|
||||
console.error(uploadError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
console.info(`File has been uploaded successfully!`)
|
||||
|
||||
const uploadedFile =
|
||||
'processedFiles' in fileMetadata ? fileMetadata.processedFiles[0] : fileMetadata
|
||||
|
||||
console.info(`ID: ${uploadedFile?.id}`)
|
||||
|
||||
// Generate a presigned URL for the uploaded file
|
||||
const { error: presignError, presignedUrl: blurredImage } =
|
||||
await client.storage.getPresignedUrl({ fileId: uploadedFile.id })
|
||||
|
||||
if (presignError) {
|
||||
console.error(presignError)
|
||||
return
|
||||
}
|
||||
|
||||
console.info(`Presigned URL: ${blurredImage.url}`)
|
||||
} catch (error) {
|
||||
console.error(error.message)
|
||||
}
|
||||
await uploadFile()
|
||||
}
|
||||
|
||||
uploadImage()
|
||||
uploadFiles()
|
||||
|
||||
70
examples/node-storage/src/uploadFile.mjs
Normal file
70
examples/node-storage/src/uploadFile.mjs
Normal file
@@ -0,0 +1,70 @@
|
||||
import fs from 'fs'
|
||||
import fetch from 'node-fetch'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { createClient } from './client.mjs'
|
||||
|
||||
const client = createClient()
|
||||
|
||||
export async function uploadFile() {
|
||||
console.info('Uploading a Single File Directly...')
|
||||
|
||||
try {
|
||||
// Download image from remote URL
|
||||
const response = await fetch(
|
||||
'https://hips.hearstapps.com/hmg-prod/images/cute-cat-photos-1593441022.jpg?crop=1.00xw:0.753xh;0,0.153xh&resize=1200:*'
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`[file]`, 'Image not found!')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer()
|
||||
|
||||
const fileBuffer = Buffer.from(arrayBuffer)
|
||||
const fileName = 'cat.jpg'
|
||||
|
||||
fs.writeFile(fileName, fileBuffer, async (err) => {
|
||||
if (err) {
|
||||
console.error(`[file]`, err)
|
||||
return
|
||||
}
|
||||
|
||||
const file = fs.createReadStream(fileName)
|
||||
const customFileId = uuidv4()
|
||||
|
||||
const { error: uploadError, fileMetadata } = await client.storage.upload({
|
||||
file,
|
||||
id: customFileId
|
||||
})
|
||||
|
||||
if (uploadError) {
|
||||
console.error(`[file]`, uploadError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
console.info(`[file]`, `File has been uploaded successfully!`)
|
||||
|
||||
console.info(`[file]`, `ID: ${fileMetadata?.id}`)
|
||||
console.info(`[file]`, `Matches custom ID: ${fileMetadata?.id === customFileId}`)
|
||||
|
||||
// Generate a presigned URL for the uploaded file
|
||||
const { error: presignError, presignedUrl: image } = await client.storage.getPresignedUrl({
|
||||
fileId: fileMetadata.id
|
||||
})
|
||||
|
||||
if (presignError) {
|
||||
console.error(`[file]`, presignError)
|
||||
return
|
||||
}
|
||||
|
||||
console.info(`[file]`, `Presigned URL: ${image.url}`)
|
||||
})
|
||||
|
||||
// Upload file to Nhost Storage
|
||||
} catch (error) {
|
||||
console.error(`[file]`, error.message)
|
||||
}
|
||||
}
|
||||
90
examples/node-storage/src/uploadFormData.mjs
Normal file
90
examples/node-storage/src/uploadFormData.mjs
Normal file
@@ -0,0 +1,90 @@
|
||||
import FormData from 'form-data'
|
||||
import fetch from 'node-fetch'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { createClient } from './client.mjs'
|
||||
|
||||
const client = createClient()
|
||||
|
||||
export async function uploadFormData() {
|
||||
console.info('Uploading 2 Files via Form Data...')
|
||||
|
||||
try {
|
||||
// Download image from remote URL
|
||||
const response = await fetch(
|
||||
'https://hips.hearstapps.com/hmg-prod/images/cute-cat-photos-1593441022.jpg?crop=1.00xw:0.753xh;0,0.153xh&resize=1200:*'
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`[form-data]`, 'Image not found!')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer()
|
||||
|
||||
const customValues = [
|
||||
{
|
||||
id: uuidv4(),
|
||||
name: 'cat1.jpg'
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
name: 'cat2.jpg'
|
||||
}
|
||||
]
|
||||
|
||||
const formData = new FormData()
|
||||
|
||||
formData.append('file[]', Buffer.from(arrayBuffer), customValues[0].name)
|
||||
formData.append('metadata[]', JSON.stringify({ id: customValues[0].id }))
|
||||
formData.append('file[]', Buffer.from(arrayBuffer), customValues[1].name)
|
||||
formData.append('metadata[]', JSON.stringify({ id: customValues[1].id }))
|
||||
|
||||
// Upload files to Nhost Storage
|
||||
const { error: uploadError, fileMetadata } = await client.storage.upload({
|
||||
formData,
|
||||
headers: { ...formData.getHeaders() }
|
||||
})
|
||||
|
||||
if (uploadError) {
|
||||
console.error(`[form-data]`, uploadError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (fileMetadata.processedFiles.length !== 2) {
|
||||
console.error(
|
||||
`[form-data]`,
|
||||
`Expected 2 files to be uploaded, but got ${fileMetadata.processedFiles.length}`
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
fileMetadata?.processedFiles.map(async (processedFile, index) => {
|
||||
console.info(`[form-data]`, `File has been uploaded successfully!`)
|
||||
|
||||
console.info(`[form-data]`, `ID: ${processedFile.id}`)
|
||||
console.info(
|
||||
`[form-data]`,
|
||||
`Matches custom ID: ${customValues.some(({ id }) => processedFile.id === id)}`
|
||||
)
|
||||
|
||||
// Generate a presigned URL for the uploaded file
|
||||
const { error: presignError, presignedUrl: image } = await client.storage.getPresignedUrl({
|
||||
fileId: processedFile.id
|
||||
})
|
||||
|
||||
if (presignError) {
|
||||
console.error(`[form-data]`, presignError)
|
||||
return
|
||||
}
|
||||
|
||||
console.info(`[form-data]`, `Presigned URL: ${image.url}`)
|
||||
})
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`[form-data]`, error.message)
|
||||
}
|
||||
}
|
||||
11
examples/node-storage/tsconfig.json
Normal file
11
examples/node-storage/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": false
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
# @nhost-examples/react-apollo
|
||||
|
||||
## 0.1.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b3b64a3b7: chore(deps): bump `@types/react` to `v18.2.14` and `@types/react-dom` to `v18.2.6`
|
||||
- Updated dependencies [b3b64a3b7]
|
||||
- @nhost/react-apollo@5.0.29
|
||||
- @nhost/react@2.0.25
|
||||
|
||||
## 0.1.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -7,8 +7,6 @@ configuration:
|
||||
custom_name: createdAt
|
||||
expires_at:
|
||||
custom_name: expiresAt
|
||||
refresh_token:
|
||||
custom_name: refreshToken
|
||||
refresh_token_hash:
|
||||
custom_name: refreshTokenHash
|
||||
type:
|
||||
@@ -18,7 +16,6 @@ configuration:
|
||||
custom_column_names:
|
||||
created_at: createdAt
|
||||
expires_at: expiresAt
|
||||
refresh_token: refreshToken
|
||||
refresh_token_hash: refreshTokenHash
|
||||
type: type
|
||||
user_id: userId
|
||||
@@ -44,6 +41,7 @@ select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- expires_at
|
||||
- metadata
|
||||
|
||||
1
examples/react-apollo/nhost/metadata/metrics_config.yaml
Normal file
1
examples/react-apollo/nhost/metadata/metrics_config.yaml
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
1
examples/react-apollo/nhost/metadata/opentelemetry.yaml
Normal file
1
examples/react-apollo/nhost/metadata/opentelemetry.yaml
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/react-apollo",
|
||||
"version": "0.1.13",
|
||||
"version": "0.1.14",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.7.14",
|
||||
@@ -22,8 +22,9 @@
|
||||
"scripts": {
|
||||
"dev": "vite --host localhost --port 3000",
|
||||
"generate": "graphql-codegen --config graphql.config.yaml",
|
||||
"install-browsers": "pnpm dlx playwright@1.31.0 install --with-deps",
|
||||
"e2e": "pnpm e2e:start-backend && pnpm e2e:test",
|
||||
"e2e:test": "npx playwright@1.34.0 install --with-deps && playwright test; nhost down",
|
||||
"e2e:test": "pnpm install-browsers && pnpm dlx playwright@1.31.0 test; nhost down",
|
||||
"e2e:start-backend": "cp .secrets.example .secrets && nhost up",
|
||||
"e2e:start-ui": "run-s build preview",
|
||||
"build": "vite build",
|
||||
@@ -51,10 +52,10 @@
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
"@graphql-codegen/cli": "^2.16.5",
|
||||
"@nuintun/qrcode": "^3.3.2",
|
||||
"@playwright/test": "^1.34.0",
|
||||
"@playwright/test": "1.31.0",
|
||||
"@types/pngjs": "^6.0.1",
|
||||
"@types/react": "^18.2.6",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@types/totp-generator": "^0.0.4",
|
||||
"@vitejs/plugin-react": "^3.1.0",
|
||||
"@xstate/inspect": "^0.6.5",
|
||||
|
||||
15
examples/react-apollo/pnpm-lock.yaml
generated
15
examples/react-apollo/pnpm-lock.yaml
generated
@@ -58,8 +58,8 @@ devDependencies:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.2
|
||||
'@playwright/test':
|
||||
specifier: ^1.34.0
|
||||
version: 1.34.0
|
||||
specifier: 1.31.0
|
||||
version: 1.31.0
|
||||
'@types/pngjs':
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.1
|
||||
@@ -1327,13 +1327,13 @@ packages:
|
||||
webcrypto-core: 1.7.7
|
||||
dev: true
|
||||
|
||||
/@playwright/test@1.34.0:
|
||||
resolution: {integrity: sha512-GIALJVODOIrMflLV54H3Cow635OfrTwOu24ZTDyKC66uchtFX2NcCRq83cLdakMjZKYK78lODNLQSYBj2OgaTw==}
|
||||
/@playwright/test@1.31.0:
|
||||
resolution: {integrity: sha512-Ys5s/06Dg9g3zAIdCIb/UOBYim3U7Zjb3DvC6XBtnRmnglH5O47iwYzmtxXu9fhSyzI2Jn28apkXIOD81GgCdw==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@types/node': 20.2.5
|
||||
playwright-core: 1.34.0
|
||||
playwright-core: 1.31.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
dev: true
|
||||
@@ -3039,9 +3039,10 @@ packages:
|
||||
engines: {node: '>=8.6'}
|
||||
dev: true
|
||||
|
||||
/playwright-core@1.34.0:
|
||||
resolution: {integrity: sha512-fMUY1+iR6kYbJF/EsOOqzBA99ZHXbw9sYPNjwA4X/oV0hVF/1aGlWYBGPVUEqxBkGANDKMziYoOdKGU5DIP5Gg==}
|
||||
/playwright-core@1.31.0:
|
||||
resolution: {integrity: sha512-/KquBjS5DcASCh8cGeNVHuC0kyb7c9plKTwaKxgOGtxT7+DZO2fjmFvPDBSXslEIK5CeOO/2kk5rOCktFXKEdA==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/pngjs@7.0.0:
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @nhost-examples/react-gqty
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b3b64a3b7: chore(deps): bump `@types/react` to `v18.2.14` and `@types/react-dom` to `v18.2.6`
|
||||
- Updated dependencies [b3b64a3b7]
|
||||
- @nhost/react@2.0.25
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/react-gqty",
|
||||
"private": true,
|
||||
"version": "0.0.8",
|
||||
"version": "0.0.9",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -20,8 +20,8 @@
|
||||
"devDependencies": {
|
||||
"@gqty/cli": "3.3.0-alpha-d8cdbf6.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@types/react": "^18.0.34",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@vitejs/plugin-react": "^3.0.0",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"postcss": "^8.4.18",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/seed-data-storage
|
||||
|
||||
## 0.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a5305e6b5: docs: update old URLs to the new format
|
||||
|
||||
## 0.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -22,8 +22,8 @@ The database and storage have now been seeded successfully.
|
||||
|
||||
You can now try to fetch the seeded files:
|
||||
|
||||
- [http://localhost:1337/v1/storage/files/3d62252d-8db2-4b2b-ba63-f2ef64af4267](http://localhost:1337/v1/storage/files/3d62252d-8db2-4b2b-ba63-f2ef64af4267)
|
||||
- [http://localhost:1337/v1/storage/files/039f89ef-f151-418f-b2db-83c94fbf0fa5](http://localhost:1337/v1/storage/files/039f89ef-f151-418f-b2db-83c94fbf0fa5)
|
||||
- [https://local.storage.nhost.run/v1/files/3d62252d-8db2-4b2b-ba63-f2ef64af4267](https://local.storage.nhost.run/v1/files/3d62252d-8db2-4b2b-ba63-f2ef64af4267)
|
||||
- [https://local.storage.nhost.run/v1/files/039f89ef-f151-418f-b2db-83c94fbf0fa5](https://local.storage.nhost.run/v1/files/039f89ef-f151-418f-b2db-83c94fbf0fa5)
|
||||
|
||||
And make a GraphQL request:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/seed-data-storage",
|
||||
"private": true,
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"scripts": {
|
||||
"seed-storage": "./seed-storage.sh"
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ jq -c '.[]' input.json | while read i; do
|
||||
-H "Content-Type: multipart/form-data" \
|
||||
-H "x-hasura-admin-secret: nhost-admin-secret" \
|
||||
-F "file=@$path" \
|
||||
http://localhost:1337/v1/storage/files/$id
|
||||
https://local.storage.nhost.run/v1/files/$id
|
||||
done
|
||||
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
buildInputs = with pkgs; [
|
||||
nhost
|
||||
nodejs_18
|
||||
nodePackages.pnpm
|
||||
# nodePackages.pnpm
|
||||
] ++ buildInputs ++ nativeBuildInputs;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# @nhost/apollo
|
||||
|
||||
## 5.2.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@2.2.11
|
||||
|
||||
## 5.2.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 07a45fde0: chore(deps): bump `graphql` to `v16.7.1`
|
||||
- Updated dependencies [07a45fde0]
|
||||
- @nhost/nhost-js@2.2.10
|
||||
|
||||
## 5.2.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5a4e237a2]
|
||||
- @nhost/nhost-js@2.2.9
|
||||
|
||||
## 5.2.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/apollo",
|
||||
"version": "5.2.10",
|
||||
"version": "5.2.13",
|
||||
"description": "Nhost Apollo Client library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -64,7 +64,7 @@
|
||||
"@nhost/nhost-js": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"graphql": "16.6.0",
|
||||
"graphql": "16.7.1",
|
||||
"graphql-ws": "^5.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/google-translation
|
||||
|
||||
## 0.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a5305e6b5: docs: update old URLs to the new format
|
||||
|
||||
## 0.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -110,7 +110,7 @@ Learn more about the [Nhost CLI](https://docs.nhost.io/platform/cli).
|
||||
|
||||
Test the Google Translation GraphQL API in the browser:
|
||||
|
||||
[http://localhost:1337/v1/functions/graphql/google-translation](http://localhost:1337/v1/functions/graphql/google-translation)
|
||||
[https://local.functions.nhost.run/v1/graphql/google-translation](https://local.functions.nhost.run/v1/graphql/google-translation)
|
||||
|
||||
### Remote Schema
|
||||
|
||||
@@ -119,7 +119,7 @@ Add the Google Translation GraphQL API as a Remote Schema in Hasura.
|
||||
**URL**
|
||||
|
||||
```
|
||||
{{NHOST_BACKEND_URL}}/v1/functions/graphql/google-translation
|
||||
https://local.functions.nhost.run/v1/graphql/google-translation
|
||||
```
|
||||
|
||||
**Headers**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/google-translation",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"description": "Google Translation GraphQL API",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user