Compare commits
32 Commits
@nhost/das
...
@nhost/rea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e838b9406 | ||
|
|
37ebf7d8e2 | ||
|
|
e23af24bdd | ||
|
|
90eb53cf19 | ||
|
|
7e516d7630 | ||
|
|
0861e41e70 | ||
|
|
057e7e2572 | ||
|
|
5a4e237a29 | ||
|
|
c7501c70ae | ||
|
|
6a45c1abad | ||
|
|
660d339e14 | ||
|
|
3dca08595d | ||
|
|
7c501c4e4f | ||
|
|
b9316bb668 | ||
|
|
5e1d5b737c | ||
|
|
bd4d0c2708 | ||
|
|
1d04ad6306 | ||
|
|
a4fa5f6f59 | ||
|
|
7e973d568a | ||
|
|
d81c52209b | ||
|
|
72744b3082 | ||
|
|
ff4efe2712 | ||
|
|
2982b90469 | ||
|
|
428a5df038 | ||
|
|
f79bf784b5 | ||
|
|
3b7449ac08 | ||
|
|
37bbfdb7ae | ||
|
|
eb570d2d09 | ||
|
|
c8c2a10b2d | ||
|
|
92c79eb2fb | ||
|
|
e70b45498d | ||
|
|
2e1ecfa731 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -62,3 +62,7 @@ todo.md
|
||||
|
||||
# Nhost CLI data
|
||||
.nhost
|
||||
|
||||
# Nix
|
||||
.envrc
|
||||
.direnv/
|
||||
|
||||
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,27 @@
|
||||
# @nhost/dashboard
|
||||
|
||||
## 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
|
||||
|
||||
- bd4d0c270: chore(dashboard):add postgres 14.6-20230613-1 to the version selector
|
||||
|
||||
## 0.17.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- c8c2a10b2: fix(database): don't break the password reset flow
|
||||
- e70b45498: chore(deps): bump `@types/react` to `v18.2.12` and `@types/react-dom` to `v18.2.5`
|
||||
|
||||
## 0.17.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -3,10 +3,20 @@
|
||||
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, run the development server:
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
## Environment
|
||||
|
||||
### Setup Environment Variables
|
||||
@@ -54,6 +64,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 +126,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 +148,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.9",
|
||||
"version": "0.17.12",
|
||||
"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",
|
||||
@@ -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",
|
||||
@@ -104,8 +104,8 @@
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/pluralize": "^0.0.29",
|
||||
"@types/react": "18.2.11",
|
||||
"@types/react-dom": "18.2.4",
|
||||
"@types/react": "18.2.12",
|
||||
"@types/react-dom": "18.2.5",
|
||||
"@types/react-table": "^7.7.12",
|
||||
"@types/testing-library__jest-dom": "^5.14.5",
|
||||
"@types/validator": "^13.7.10",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -31,6 +31,7 @@ export type DatabaseServiceVersionFormValues = Yup.InferType<
|
||||
>;
|
||||
|
||||
const AVAILABLE_POSTGRES_VERSIONS = [
|
||||
'14.6-20230613-1',
|
||||
'14.6-20230525',
|
||||
'14.6-20230406-2',
|
||||
'14.6-20230406-1',
|
||||
|
||||
@@ -6,14 +6,11 @@ import { Button } from '@/components/ui/v2/Button';
|
||||
import { CopyIcon } from '@/components/ui/v2/icons/CopyIcon';
|
||||
import { Input } from '@/components/ui/v2/Input';
|
||||
import { InputAdornment } from '@/components/ui/v2/InputAdornment';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { generateRandomDatabasePassword } from '@/features/database/common/utils/generateRandomDatabasePassword';
|
||||
import type { ResetDatabasePasswordFormValues } from '@/features/database/settings/utils/resetDatabasePasswordValidationSchema';
|
||||
import { resetDatabasePasswordValidationSchema } from '@/features/database/settings/utils/resetDatabasePasswordValidationSchema';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import {
|
||||
useResetPostgresPasswordMutation,
|
||||
useUpdateApplicationMutation,
|
||||
} from '@/generated/graphql';
|
||||
import { useResetDatabasePasswordMutation } from '@/generated/graphql';
|
||||
import { useLeaveConfirm } from '@/hooks/useLeaveConfirm';
|
||||
import { copy } from '@/utils/copy';
|
||||
import { discordAnnounce } from '@/utils/discordAnnounce';
|
||||
@@ -24,18 +21,10 @@ import { useUserData } from '@nhost/nextjs';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export interface ResetDatabasePasswordFormValues {
|
||||
/**
|
||||
* The new password to set for the database.
|
||||
*/
|
||||
databasePassword: string;
|
||||
}
|
||||
|
||||
export default function ResetDatabasePasswordSettings() {
|
||||
const [updateApplication] = useUpdateApplicationMutation();
|
||||
const [resetPassword, { loading: resetPasswordLoading }] =
|
||||
useResetDatabasePasswordMutation();
|
||||
const { maintenanceActive } = useUI();
|
||||
const [resetPostgresPasswordMutation, { loading: resetPasswordLoading }] =
|
||||
useResetPostgresPasswordMutation();
|
||||
const user = useUserData();
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
const { openAlertDialog } = useDialog();
|
||||
@@ -65,10 +54,7 @@ export default function ResetDatabasePasswordSettings() {
|
||||
function handleGenerateRandomPassword() {
|
||||
const newRandomDatabasePassword = generateRandomDatabasePassword();
|
||||
triggerToast(
|
||||
<Text>
|
||||
Random database password generated and copied to clipboard. Submit the
|
||||
form to save it.
|
||||
</Text>,
|
||||
'Random database password was generated and copied to clipboard. Submit the form to save it.',
|
||||
);
|
||||
copy(newRandomDatabasePassword);
|
||||
setValue('databasePassword', newRandomDatabasePassword, {
|
||||
@@ -80,18 +66,10 @@ export default function ResetDatabasePasswordSettings() {
|
||||
formValues: ResetDatabasePasswordFormValues,
|
||||
) {
|
||||
try {
|
||||
await resetPostgresPasswordMutation({
|
||||
variables: {
|
||||
appID: currentProject.id,
|
||||
newPassword: formValues.databasePassword,
|
||||
},
|
||||
});
|
||||
await updateApplication({
|
||||
await resetPassword({
|
||||
variables: {
|
||||
appId: currentProject.id,
|
||||
app: {
|
||||
postgresPassword: formValues.databasePassword,
|
||||
},
|
||||
newPassword: formValues.databasePassword,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export * from './ResetDatabasePasswordSettings';
|
||||
export { default as ResetDatabasePasswordSettings } from './ResetDatabasePasswordSettings';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
mutation ResetDatabasePassword($appId: String!, $newPassword: String!) {
|
||||
resetPostgresPassword(appID: $appId, newPassword: $newPassword)
|
||||
}
|
||||
@@ -16,4 +16,8 @@ export const resetDatabasePasswordValidationSchema = yup.object().shape({
|
||||
.minUppercase(1),
|
||||
});
|
||||
|
||||
export type ResetDatabasePasswordFormValues = yup.InferType<
|
||||
typeof resetDatabasePasswordValidationSchema
|
||||
>;
|
||||
|
||||
export default resetDatabasePasswordValidationSchema;
|
||||
|
||||
@@ -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: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
mutation resetPostgresPassword($appID: String!, $newPassword: String!) {
|
||||
resetPostgresPassword(appID: $appID, newPassword: $newPassword)
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
98
dashboard/src/utils/__generated__/graphql.ts
generated
98
dashboard/src/utils/__generated__/graphql.ts
generated
@@ -1746,7 +1746,6 @@ export type ConfigSystemConfigPostgres = {
|
||||
connectionString: ConfigSystemConfigPostgresConnectionString;
|
||||
database: Scalars['String'];
|
||||
enabled?: Maybe<Scalars['Boolean']>;
|
||||
password: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ConfigSystemConfigPostgresComparisonExp = {
|
||||
@@ -1756,7 +1755,6 @@ export type ConfigSystemConfigPostgresComparisonExp = {
|
||||
connectionString?: InputMaybe<ConfigSystemConfigPostgresConnectionStringComparisonExp>;
|
||||
database?: InputMaybe<ConfigStringComparisonExp>;
|
||||
enabled?: InputMaybe<ConfigBooleanComparisonExp>;
|
||||
password?: InputMaybe<ConfigStringComparisonExp>;
|
||||
};
|
||||
|
||||
export type ConfigSystemConfigPostgresConnectionString = {
|
||||
@@ -1795,14 +1793,12 @@ export type ConfigSystemConfigPostgresInsertInput = {
|
||||
connectionString: ConfigSystemConfigPostgresConnectionStringInsertInput;
|
||||
database: Scalars['String'];
|
||||
enabled?: InputMaybe<Scalars['Boolean']>;
|
||||
password: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ConfigSystemConfigPostgresUpdateInput = {
|
||||
connectionString?: InputMaybe<ConfigSystemConfigPostgresConnectionStringUpdateInput>;
|
||||
database?: InputMaybe<Scalars['String']>;
|
||||
enabled?: InputMaybe<Scalars['Boolean']>;
|
||||
password?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type ConfigSystemConfigUpdateInput = {
|
||||
@@ -2549,7 +2545,6 @@ export type Apps = {
|
||||
/** An object relationship */
|
||||
plan: Plans;
|
||||
planId: Scalars['uuid'];
|
||||
postgresPassword: Scalars['String'];
|
||||
providersUpdated?: Maybe<Scalars['Boolean']>;
|
||||
/** An object relationship */
|
||||
region: Regions;
|
||||
@@ -2787,7 +2782,6 @@ export type Apps_Bool_Exp = {
|
||||
paused?: InputMaybe<Boolean_Comparison_Exp>;
|
||||
plan?: InputMaybe<Plans_Bool_Exp>;
|
||||
planId?: InputMaybe<Uuid_Comparison_Exp>;
|
||||
postgresPassword?: InputMaybe<String_Comparison_Exp>;
|
||||
providersUpdated?: InputMaybe<Boolean_Comparison_Exp>;
|
||||
region?: InputMaybe<Regions_Bool_Exp>;
|
||||
regionId?: InputMaybe<Uuid_Comparison_Exp>;
|
||||
@@ -2859,7 +2853,6 @@ export type Apps_Insert_Input = {
|
||||
paused?: InputMaybe<Scalars['Boolean']>;
|
||||
plan?: InputMaybe<Plans_Obj_Rel_Insert_Input>;
|
||||
planId?: InputMaybe<Scalars['uuid']>;
|
||||
postgresPassword?: InputMaybe<Scalars['String']>;
|
||||
providersUpdated?: InputMaybe<Scalars['Boolean']>;
|
||||
region?: InputMaybe<Regions_Obj_Rel_Insert_Input>;
|
||||
regionId?: InputMaybe<Scalars['uuid']>;
|
||||
@@ -2886,7 +2879,6 @@ export type Apps_Max_Fields = {
|
||||
name?: Maybe<Scalars['String']>;
|
||||
nhostBaseFolder?: Maybe<Scalars['String']>;
|
||||
planId?: Maybe<Scalars['uuid']>;
|
||||
postgresPassword?: Maybe<Scalars['String']>;
|
||||
regionId?: Maybe<Scalars['uuid']>;
|
||||
repositoryProductionBranch?: Maybe<Scalars['String']>;
|
||||
slug?: Maybe<Scalars['String']>;
|
||||
@@ -2909,7 +2901,6 @@ export type Apps_Max_Order_By = {
|
||||
name?: InputMaybe<Order_By>;
|
||||
nhostBaseFolder?: InputMaybe<Order_By>;
|
||||
planId?: InputMaybe<Order_By>;
|
||||
postgresPassword?: InputMaybe<Order_By>;
|
||||
regionId?: InputMaybe<Order_By>;
|
||||
repositoryProductionBranch?: InputMaybe<Order_By>;
|
||||
slug?: InputMaybe<Order_By>;
|
||||
@@ -2933,7 +2924,6 @@ export type Apps_Min_Fields = {
|
||||
name?: Maybe<Scalars['String']>;
|
||||
nhostBaseFolder?: Maybe<Scalars['String']>;
|
||||
planId?: Maybe<Scalars['uuid']>;
|
||||
postgresPassword?: Maybe<Scalars['String']>;
|
||||
regionId?: Maybe<Scalars['uuid']>;
|
||||
repositoryProductionBranch?: Maybe<Scalars['String']>;
|
||||
slug?: Maybe<Scalars['String']>;
|
||||
@@ -2956,7 +2946,6 @@ export type Apps_Min_Order_By = {
|
||||
name?: InputMaybe<Order_By>;
|
||||
nhostBaseFolder?: InputMaybe<Order_By>;
|
||||
planId?: InputMaybe<Order_By>;
|
||||
postgresPassword?: InputMaybe<Order_By>;
|
||||
regionId?: InputMaybe<Order_By>;
|
||||
repositoryProductionBranch?: InputMaybe<Order_By>;
|
||||
slug?: InputMaybe<Order_By>;
|
||||
@@ -3017,7 +3006,6 @@ export type Apps_Order_By = {
|
||||
paused?: InputMaybe<Order_By>;
|
||||
plan?: InputMaybe<Plans_Order_By>;
|
||||
planId?: InputMaybe<Order_By>;
|
||||
postgresPassword?: InputMaybe<Order_By>;
|
||||
providersUpdated?: InputMaybe<Order_By>;
|
||||
region?: InputMaybe<Regions_Order_By>;
|
||||
regionId?: InputMaybe<Order_By>;
|
||||
@@ -3073,8 +3061,6 @@ export enum Apps_Select_Column {
|
||||
/** column name */
|
||||
PlanId = 'planId',
|
||||
/** column name */
|
||||
PostgresPassword = 'postgresPassword',
|
||||
/** column name */
|
||||
ProvidersUpdated = 'providersUpdated',
|
||||
/** column name */
|
||||
RegionId = 'regionId',
|
||||
@@ -3134,7 +3120,6 @@ export type Apps_Set_Input = {
|
||||
/** whether or not this app is paused */
|
||||
paused?: InputMaybe<Scalars['Boolean']>;
|
||||
planId?: InputMaybe<Scalars['uuid']>;
|
||||
postgresPassword?: InputMaybe<Scalars['String']>;
|
||||
providersUpdated?: InputMaybe<Scalars['Boolean']>;
|
||||
regionId?: InputMaybe<Scalars['uuid']>;
|
||||
repositoryProductionBranch?: InputMaybe<Scalars['String']>;
|
||||
@@ -3204,7 +3189,6 @@ export type Apps_Stream_Cursor_Value_Input = {
|
||||
/** whether or not this app is paused */
|
||||
paused?: InputMaybe<Scalars['Boolean']>;
|
||||
planId?: InputMaybe<Scalars['uuid']>;
|
||||
postgresPassword?: InputMaybe<Scalars['String']>;
|
||||
providersUpdated?: InputMaybe<Scalars['Boolean']>;
|
||||
regionId?: InputMaybe<Scalars['uuid']>;
|
||||
repositoryProductionBranch?: InputMaybe<Scalars['String']>;
|
||||
@@ -3259,8 +3243,6 @@ export enum Apps_Update_Column {
|
||||
/** column name */
|
||||
PlanId = 'planId',
|
||||
/** column name */
|
||||
PostgresPassword = 'postgresPassword',
|
||||
/** column name */
|
||||
ProvidersUpdated = 'providersUpdated',
|
||||
/** column name */
|
||||
RegionId = 'regionId',
|
||||
@@ -19053,6 +19035,14 @@ export type GetPostgresSettingsQueryVariables = Exact<{
|
||||
|
||||
export type GetPostgresSettingsQuery = { __typename?: 'query_root', systemConfig?: { __typename?: 'ConfigSystemConfig', postgres: { __typename?: 'ConfigSystemConfigPostgres', database: string } } | null, config?: { __typename: 'ConfigConfig', id: 'ConfigConfig', postgres?: { __typename?: 'ConfigPostgres', version?: string | null } | null } | null };
|
||||
|
||||
export type ResetDatabasePasswordMutationVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
newPassword: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ResetDatabasePasswordMutation = { __typename?: 'mutation_root', resetPostgresPassword: boolean };
|
||||
|
||||
export type GetHasuraSettingsQueryVariables = Exact<{
|
||||
appId: Scalars['uuid'];
|
||||
}>;
|
||||
@@ -19276,14 +19266,6 @@ export type GetCountriesQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
export type GetCountriesQuery = { __typename?: 'query_root', countries: Array<{ __typename?: 'countries', code: any, name: string }> };
|
||||
|
||||
export type ResetPostgresPasswordMutationVariables = Exact<{
|
||||
appID: Scalars['String'];
|
||||
newPassword: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ResetPostgresPasswordMutation = { __typename?: 'mutation_root', resetPostgresPassword: boolean };
|
||||
|
||||
export type DeploymentRowFragment = { __typename?: 'deployments', id: any, commitSHA: string, deploymentStartedAt?: any | null, deploymentEndedAt?: any | null, deploymentStatus?: string | null, commitUserName?: string | null, commitUserAvatarUrl?: string | null, commitMessage?: string | null };
|
||||
|
||||
export type ScheduledOrPendingDeploymentsSubSubscriptionVariables = Exact<{
|
||||
@@ -20128,6 +20110,38 @@ export type GetPostgresSettingsQueryResult = Apollo.QueryResult<GetPostgresSetti
|
||||
export function refetchGetPostgresSettingsQuery(variables: GetPostgresSettingsQueryVariables) {
|
||||
return { query: GetPostgresSettingsDocument, variables: variables }
|
||||
}
|
||||
export const ResetDatabasePasswordDocument = gql`
|
||||
mutation ResetDatabasePassword($appId: String!, $newPassword: String!) {
|
||||
resetPostgresPassword(appID: $appId, newPassword: $newPassword)
|
||||
}
|
||||
`;
|
||||
export type ResetDatabasePasswordMutationFn = Apollo.MutationFunction<ResetDatabasePasswordMutation, ResetDatabasePasswordMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useResetDatabasePasswordMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useResetDatabasePasswordMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useResetDatabasePasswordMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [resetDatabasePasswordMutation, { data, loading, error }] = useResetDatabasePasswordMutation({
|
||||
* variables: {
|
||||
* appId: // value for 'appId'
|
||||
* newPassword: // value for 'newPassword'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useResetDatabasePasswordMutation(baseOptions?: Apollo.MutationHookOptions<ResetDatabasePasswordMutation, ResetDatabasePasswordMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<ResetDatabasePasswordMutation, ResetDatabasePasswordMutationVariables>(ResetDatabasePasswordDocument, options);
|
||||
}
|
||||
export type ResetDatabasePasswordMutationHookResult = ReturnType<typeof useResetDatabasePasswordMutation>;
|
||||
export type ResetDatabasePasswordMutationResult = Apollo.MutationResult<ResetDatabasePasswordMutation>;
|
||||
export type ResetDatabasePasswordMutationOptions = Apollo.BaseMutationOptions<ResetDatabasePasswordMutation, ResetDatabasePasswordMutationVariables>;
|
||||
export const GetHasuraSettingsDocument = gql`
|
||||
query GetHasuraSettings($appId: uuid!) {
|
||||
config(appID: $appId, resolve: true) {
|
||||
@@ -21414,38 +21428,6 @@ export type GetCountriesQueryResult = Apollo.QueryResult<GetCountriesQuery, GetC
|
||||
export function refetchGetCountriesQuery(variables?: GetCountriesQueryVariables) {
|
||||
return { query: GetCountriesDocument, variables: variables }
|
||||
}
|
||||
export const ResetPostgresPasswordDocument = gql`
|
||||
mutation resetPostgresPassword($appID: String!, $newPassword: String!) {
|
||||
resetPostgresPassword(appID: $appID, newPassword: $newPassword)
|
||||
}
|
||||
`;
|
||||
export type ResetPostgresPasswordMutationFn = Apollo.MutationFunction<ResetPostgresPasswordMutation, ResetPostgresPasswordMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useResetPostgresPasswordMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useResetPostgresPasswordMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useResetPostgresPasswordMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [resetPostgresPasswordMutation, { data, loading, error }] = useResetPostgresPasswordMutation({
|
||||
* variables: {
|
||||
* appID: // value for 'appID'
|
||||
* newPassword: // value for 'newPassword'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useResetPostgresPasswordMutation(baseOptions?: Apollo.MutationHookOptions<ResetPostgresPasswordMutation, ResetPostgresPasswordMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<ResetPostgresPasswordMutation, ResetPostgresPasswordMutationVariables>(ResetPostgresPasswordDocument, options);
|
||||
}
|
||||
export type ResetPostgresPasswordMutationHookResult = ReturnType<typeof useResetPostgresPasswordMutation>;
|
||||
export type ResetPostgresPasswordMutationResult = Apollo.MutationResult<ResetPostgresPasswordMutation>;
|
||||
export type ResetPostgresPasswordMutationOptions = Apollo.BaseMutationOptions<ResetPostgresPasswordMutation, ResetPostgresPasswordMutationVariables>;
|
||||
export const ScheduledOrPendingDeploymentsSubDocument = gql`
|
||||
subscription ScheduledOrPendingDeploymentsSub($appId: uuid!) {
|
||||
deployments(
|
||||
|
||||
@@ -4,7 +4,7 @@ export default function getColor() {
|
||||
const colorPreference =
|
||||
typeof window !== 'undefined'
|
||||
? window.localStorage.getItem(COLOR_PREFERENCE_STORAGE_KEY)
|
||||
: 'light';
|
||||
: 'system';
|
||||
const prefersDarkMode =
|
||||
typeof window !== 'undefined'
|
||||
? window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
|
||||
@@ -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
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 @@
|
||||
{}
|
||||
@@ -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 @@
|
||||
{}
|
||||
@@ -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,7 +52,7 @@
|
||||
"@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",
|
||||
|
||||
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:
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
outputs = { self, nixpkgs, flake-utils, nix-filter }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
version = "v0.9.7";
|
||||
version = "v1.1.0";
|
||||
dist = {
|
||||
aarch64-darwin = rec {
|
||||
url = "https://github.com/nhost/cli/releases/download/${version}/cli-${version}-darwin-arm64.tar.gz";
|
||||
sha256 = "sha256-K0yKwsEsiR1JjsyWxxlo/hIrxzsmnwcw+KUZdT8/Rt4=";
|
||||
sha256 = "sha256-tF40CEkA357yzg2Gmc9ubjHJ5FlI9qQTdVdWNY/+f+Y=";
|
||||
};
|
||||
x86_64-linux = rec {
|
||||
url = "https://github.com/nhost/cli/releases/download/${version}/cli-${version}-linux-amd64.tar.gz";
|
||||
sha256 = "0llfk6fa6l7fcp6xpfx2kg3lzlzpdlkzl9lnficiq76s341slxbc";
|
||||
sha256 = "sha256-KLv06dI7A+5KGJ5F8xM1qC+oqHRmJ4kMaifLvaTFqak=";
|
||||
};
|
||||
};
|
||||
overlays = [
|
||||
@@ -124,9 +124,8 @@
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
nhost
|
||||
nodejs_16
|
||||
nodejs_18
|
||||
nodePackages.pnpm
|
||||
nhost
|
||||
] ++ buildInputs ++ nativeBuildInputs;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/apollo
|
||||
|
||||
## 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.11",
|
||||
"description": "Nhost Apollo Client library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/react-apollo
|
||||
|
||||
## 5.0.27
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/apollo@5.2.11
|
||||
- @nhost/react@2.0.23
|
||||
|
||||
## 5.0.26
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react-apollo",
|
||||
"version": "5.0.26",
|
||||
"version": "5.0.27",
|
||||
"description": "Nhost React Apollo client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/react-urql
|
||||
|
||||
## 2.0.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@2.0.23
|
||||
|
||||
## 2.0.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react-urql",
|
||||
"version": "2.0.23",
|
||||
"version": "2.0.24",
|
||||
"description": "Nhost React URQL client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/hasura-auth-js
|
||||
|
||||
## 2.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1d04ad630: chore(deps): use `fetch-ponyfill` instead of `isomorphic-unfetch`
|
||||
- 7e973d568: fix(tokens): prevent infinite token refresh when using custom expiration
|
||||
|
||||
## 2.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { faker } from '@faker-js/faker'
|
||||
import fetch from 'isomorphic-unfetch'
|
||||
import fetchPonyfill from 'fetch-ponyfill'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { auth, getHtmlLink, mailhog } from './helpers'
|
||||
|
||||
const { fetch } = fetchPonyfill()
|
||||
|
||||
describe('emails', () => {
|
||||
afterEach(async () => {
|
||||
await auth.signOut()
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { load } from 'cheerio'
|
||||
import fetch from 'isomorphic-unfetch'
|
||||
import fetchPonyfill from 'fetch-ponyfill'
|
||||
import createMailhogClient from 'mailhog'
|
||||
import { expect } from 'vitest'
|
||||
import { HasuraAuthClient, SignUpParams } from '../src'
|
||||
|
||||
const { fetch } = fetchPonyfill()
|
||||
|
||||
const AUTH_BACKEND_URL = 'https://local.auth.nhost.run/v1'
|
||||
|
||||
const auth = new HasuraAuthClient({
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { faker } from '@faker-js/faker'
|
||||
import fetch from 'isomorphic-unfetch'
|
||||
import fetchPonyfill from 'fetch-ponyfill'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { auth, getHtmlLink, signUpAndInUser, signUpAndVerifyUser } from './helpers'
|
||||
|
||||
const { fetch } = fetchPonyfill()
|
||||
|
||||
describe('passwords', () => {
|
||||
afterEach(async () => {
|
||||
await auth.signOut()
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { faker } from '@faker-js/faker'
|
||||
import fetch from 'isomorphic-unfetch'
|
||||
import fetchPonyfill from 'fetch-ponyfill'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { USER_ALREADY_SIGNED_IN } from '../src'
|
||||
import { auth, getHtmlLink, signUpAndInUser, signUpAndVerifyUser } from './helpers'
|
||||
|
||||
const { fetch } = fetchPonyfill()
|
||||
|
||||
describe('sign-in', () => {
|
||||
afterEach(async () => {
|
||||
await auth.signOut()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/hasura-auth-js",
|
||||
"version": "2.1.6",
|
||||
"version": "2.1.7",
|
||||
"description": "Hasura-auth client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -65,7 +65,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@simplewebauthn/browser": "^6.0.0",
|
||||
"isomorphic-unfetch": "^3.1.0",
|
||||
"fetch-ponyfill": "^7.1.0",
|
||||
"js-cookie": "^3.0.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"xstate": "^4.33.5"
|
||||
@@ -76,6 +76,6 @@
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"mailhog": "^4.16.0",
|
||||
"msw": "^1.0.1"
|
||||
"msw": "^1.2.2"
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,6 @@ export const MIN_PASSWORD_LENGTH = 3
|
||||
* Minimum time in seconds between now and the JWT expiration time before the JWT is refreshed
|
||||
* For instance, if set to 60, the client will refresh the JWT one minute before it expires
|
||||
*/
|
||||
export const TOKEN_REFRESH_MARGIN = 300 // five minutes
|
||||
export const TOKEN_REFRESH_MARGIN_SECONDS = 60
|
||||
|
||||
export const REFRESH_TOKEN_MAX_ATTEMPTS = 5
|
||||
|
||||
@@ -10,6 +10,7 @@ export type AuthContext = {
|
||||
accessToken: {
|
||||
value: string | null
|
||||
expiresAt: Date | null
|
||||
expiresInSeconds: number | null
|
||||
}
|
||||
refreshTimer: {
|
||||
startedAt: Date | null
|
||||
@@ -30,7 +31,8 @@ export const INITIAL_MACHINE_CONTEXT: AuthContext = {
|
||||
mfa: null,
|
||||
accessToken: {
|
||||
value: null,
|
||||
expiresAt: null
|
||||
expiresAt: null,
|
||||
expiresInSeconds: 15
|
||||
},
|
||||
refreshTimer: {
|
||||
startedAt: null,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
NHOST_REFRESH_TOKEN_ID_KEY,
|
||||
NHOST_REFRESH_TOKEN_KEY,
|
||||
REFRESH_TOKEN_MAX_ATTEMPTS,
|
||||
TOKEN_REFRESH_MARGIN
|
||||
TOKEN_REFRESH_MARGIN_SECONDS
|
||||
} from '../../constants'
|
||||
import {
|
||||
CodifiedError,
|
||||
@@ -548,13 +548,15 @@ export const createAuthMachine = ({
|
||||
storageSetter(NHOST_JWT_EXPIRES_AT_KEY, nextRefresh.toISOString())
|
||||
return {
|
||||
value: accessToken,
|
||||
expiresAt: nextRefresh
|
||||
expiresAt: nextRefresh,
|
||||
expiresInSeconds: accessTokenExpiresIn
|
||||
}
|
||||
}
|
||||
storageSetter(NHOST_JWT_EXPIRES_AT_KEY, null)
|
||||
return {
|
||||
value: null,
|
||||
expiresAt: null
|
||||
expiresAt: null,
|
||||
expiresInSeconds: null
|
||||
}
|
||||
},
|
||||
refreshToken: (_, { data }) => {
|
||||
@@ -582,13 +584,15 @@ export const createAuthMachine = ({
|
||||
storageSetter(NHOST_JWT_EXPIRES_AT_KEY, nextRefresh.toISOString())
|
||||
return {
|
||||
value: accessToken,
|
||||
expiresAt: nextRefresh
|
||||
expiresAt: nextRefresh,
|
||||
expiresInSeconds: accessTokenExpiresIn
|
||||
}
|
||||
}
|
||||
storageSetter(NHOST_JWT_EXPIRES_AT_KEY, null)
|
||||
return {
|
||||
value: null,
|
||||
expiresAt: null
|
||||
expiresAt: null,
|
||||
expiresInSeconds: null
|
||||
}
|
||||
},
|
||||
refreshToken: (_, { data }) => {
|
||||
@@ -704,10 +708,23 @@ export const createAuthMachine = ({
|
||||
}
|
||||
}
|
||||
// * In any case, it's time to refresh when there's less than
|
||||
// * TOKEN_REFRESH_MARGIN seconds before the JWT exprires
|
||||
const expiresIn = expiresAt.getTime() - Date.now()
|
||||
const remaining = expiresIn - 1_000 * TOKEN_REFRESH_MARGIN
|
||||
return remaining <= 0
|
||||
// * TOKEN_REFRESH_MARGIN_SECONDS seconds before the JWT exprires
|
||||
const accessTokenExpirationTime = ctx.accessToken.expiresInSeconds
|
||||
|
||||
if (!accessTokenExpirationTime) {
|
||||
return false
|
||||
}
|
||||
|
||||
const expiresInMilliseconds = expiresAt.getTime() - Date.now()
|
||||
|
||||
// If the token expires in less time than the margin, we should use
|
||||
// a margin based on the token expiration time to avoid refreshing
|
||||
// the token infinitely
|
||||
const remainingMilliseconds =
|
||||
expiresInMilliseconds -
|
||||
1_000 * Math.min(TOKEN_REFRESH_MARGIN_SECONDS, accessTokenExpirationTime * 0.5)
|
||||
|
||||
return remainingMilliseconds <= 0
|
||||
},
|
||||
// * Untyped action payload. See https://github.com/statelyai/xstate/issues/3037
|
||||
/** Shoud retry to import the token on network error or any internal server error.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fetch from 'isomorphic-unfetch'
|
||||
import fetchPonyfill from 'fetch-ponyfill'
|
||||
import { NETWORK_ERROR_CODE } from '../errors'
|
||||
import { NullableErrorResponse } from '../types'
|
||||
|
||||
@@ -6,6 +6,8 @@ interface FetcResponse<T> extends NullableErrorResponse {
|
||||
data: T
|
||||
}
|
||||
|
||||
const { fetch } = fetchPonyfill()
|
||||
|
||||
const fetchWrapper = async <T>(
|
||||
url: string,
|
||||
method: 'GET' | 'POST',
|
||||
|
||||
@@ -6,7 +6,8 @@ export const contextWithUser: AuthContext = {
|
||||
...INITIAL_MACHINE_CONTEXT,
|
||||
accessToken: {
|
||||
value: faker.datatype.string(40),
|
||||
expiresAt: faker.date.future()
|
||||
expiresAt: faker.date.future(),
|
||||
expiresInSeconds: 15
|
||||
},
|
||||
refreshToken: {
|
||||
value: faker.datatype.uuid()
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
|
||||
import { interpret, InterpreterFrom } from 'xstate'
|
||||
import { InterpreterFrom, interpret } from 'xstate'
|
||||
import { waitFor } from 'xstate/lib/waitFor'
|
||||
import {
|
||||
AuthMachine,
|
||||
createAuthMachine,
|
||||
INVALID_REFRESH_TOKEN,
|
||||
NHOST_JWT_EXPIRES_AT_KEY,
|
||||
NHOST_REFRESH_TOKEN_KEY,
|
||||
TOKEN_REFRESH_MARGIN
|
||||
TOKEN_REFRESH_MARGIN_SECONDS,
|
||||
createAuthMachine
|
||||
} from '../src'
|
||||
import { BASE_URL } from './helpers/config'
|
||||
import {
|
||||
@@ -68,7 +68,8 @@ describe(`Time based token refresh`, () => {
|
||||
...contextWithUser,
|
||||
accessToken: {
|
||||
value: initialToken,
|
||||
expiresAt: initialExpiration
|
||||
expiresAt: initialExpiration,
|
||||
expiresInSeconds: 900
|
||||
}
|
||||
})
|
||||
|
||||
@@ -105,7 +106,7 @@ describe(`Time based token refresh`, () => {
|
||||
|
||||
test(`access token should always be refreshed when reaching the expiration margin`, async () => {
|
||||
// Fast forward to the initial expiration date
|
||||
vi.setSystemTime(new Date(initialExpiration.getTime() - TOKEN_REFRESH_MARGIN * 1000))
|
||||
vi.setSystemTime(new Date(initialExpiration.getTime() - TOKEN_REFRESH_MARGIN_SECONDS * 1000))
|
||||
|
||||
await waitFor(authServiceWithInitialSession, (state) =>
|
||||
state.matches({ authentication: { signedIn: { refreshTimer: { running: 'refreshing' } } } })
|
||||
@@ -126,7 +127,7 @@ describe(`Time based token refresh`, () => {
|
||||
|
||||
// Fast forward to the expiration date of the access token
|
||||
vi.setSystemTime(
|
||||
new Date(firstRefreshAccessTokenExpiration.getTime() - TOKEN_REFRESH_MARGIN * 1000)
|
||||
new Date(firstRefreshAccessTokenExpiration.getTime() - TOKEN_REFRESH_MARGIN_SECONDS * 1000)
|
||||
)
|
||||
|
||||
await waitFor(authServiceWithInitialSession, (state) =>
|
||||
@@ -148,7 +149,9 @@ describe(`Time based token refresh`, () => {
|
||||
|
||||
// Fast forward to a time when the access token is still valid, so nothing should be refreshed
|
||||
vi.setSystemTime(
|
||||
new Date(secondRefreshAccessTokenExpiration.getTime() - TOKEN_REFRESH_MARGIN * 5 * 1000)
|
||||
new Date(
|
||||
secondRefreshAccessTokenExpiration.getTime() - TOKEN_REFRESH_MARGIN_SECONDS * 5 * 1000
|
||||
)
|
||||
)
|
||||
|
||||
const thirdRefreshState = await waitFor(authServiceWithInitialSession, (state) =>
|
||||
@@ -165,7 +168,7 @@ describe(`Time based token refresh`, () => {
|
||||
})
|
||||
|
||||
test(`token should be refreshed every N seconds based on the refresh interval`, async () => {
|
||||
const refreshIntervalTime = faker.datatype.number({ min: 800, max: 900 })
|
||||
const refreshIntervalTime = faker.datatype.number({ min: 1000, max: 1500 })
|
||||
|
||||
const authMachineWithInitialSession = createAuthMachine({
|
||||
backendUrl: BASE_URL,
|
||||
@@ -178,7 +181,8 @@ describe(`Time based token refresh`, () => {
|
||||
...contextWithUser,
|
||||
accessToken: {
|
||||
value: initialToken,
|
||||
expiresAt: initialExpiration
|
||||
expiresAt: initialExpiration,
|
||||
expiresInSeconds: 900
|
||||
}
|
||||
})
|
||||
|
||||
@@ -249,6 +253,7 @@ describe('General and disabled auto-sign in', () => {
|
||||
const user = { ...fakeUser }
|
||||
const accessToken = faker.datatype.string(40)
|
||||
const refreshToken = faker.datatype.uuid()
|
||||
const accessTokenExpiresIn = 900
|
||||
|
||||
server.use(authTokenNetworkErrorHandler)
|
||||
|
||||
@@ -257,13 +262,16 @@ describe('General and disabled auto-sign in', () => {
|
||||
data: {
|
||||
session: {
|
||||
accessToken,
|
||||
accessTokenExpiresIn: 0,
|
||||
accessTokenExpiresIn,
|
||||
refreshToken,
|
||||
user
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Fast forward to a time when the access token is already expired
|
||||
vi.setSystemTime(Date.now() + accessTokenExpiresIn * 1.5 * 1000)
|
||||
|
||||
const state = await waitFor(authService, (state) => state.context.refreshTimer.attempts > 0)
|
||||
expect(state.context.refreshTimer.attempts).toBeGreaterThan(0)
|
||||
}, 8000)
|
||||
|
||||
@@ -2,7 +2,7 @@ import { faker } from '@faker-js/faker'
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, expect, test } from 'vitest'
|
||||
import { interpret } from 'xstate'
|
||||
import { waitFor } from 'xstate/lib/waitFor'
|
||||
import { AuthClient, createSendVerificationEmailMachine, INVALID_EMAIL_ERROR } from '../src'
|
||||
import { AuthClient, INVALID_EMAIL_ERROR, createSendVerificationEmailMachine } from '../src'
|
||||
import { BASE_URL } from './helpers/config'
|
||||
import {
|
||||
sendVerificationEmailInternalErrorHandler,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/hasura-storage-js
|
||||
|
||||
## 2.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1d04ad630: chore(deps): use `fetch-ponyfill` instead of `isomorphic-unfetch`
|
||||
|
||||
## 2.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/hasura-storage-js",
|
||||
"version": "2.1.5",
|
||||
"version": "2.1.6",
|
||||
"description": "Hasura-storage client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/nextjs
|
||||
|
||||
## 1.13.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@2.0.23
|
||||
|
||||
## 1.13.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/nextjs",
|
||||
"version": "1.13.28",
|
||||
"version": "1.13.29",
|
||||
"description": "Nhost NextJS library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
AuthMachine,
|
||||
NHOST_REFRESH_TOKEN_KEY,
|
||||
NhostClient,
|
||||
NhostReactClientConstructorParams,
|
||||
NhostSession,
|
||||
NHOST_REFRESH_TOKEN_KEY,
|
||||
VanillaNhostClient
|
||||
} from '@nhost/react'
|
||||
import Cookies from 'js-cookie'
|
||||
@@ -12,9 +12,11 @@ import { StateFrom } from 'xstate'
|
||||
import { waitFor } from 'xstate/lib/waitFor'
|
||||
import { NHOST_SESSION_KEY } from './utils'
|
||||
|
||||
export type CreateServerSideClientParams = Pick<
|
||||
NhostReactClientConstructorParams,
|
||||
'subdomain' | 'region' | 'authUrl' | 'functionsUrl' | 'graphqlUrl' | 'storageUrl'
|
||||
export type CreateServerSideClientParams = Partial<
|
||||
Pick<
|
||||
NhostReactClientConstructorParams,
|
||||
'subdomain' | 'region' | 'authUrl' | 'functionsUrl' | 'graphqlUrl' | 'storageUrl'
|
||||
>
|
||||
>
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @nhost/nhost-js
|
||||
|
||||
## 2.2.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 5a4e237a2: fix(deps): pass access token to clients on sign in
|
||||
- Updated dependencies [1d04ad630]
|
||||
- Updated dependencies [7e973d568]
|
||||
- @nhost/hasura-auth-js@2.1.7
|
||||
- @nhost/hasura-storage-js@2.1.6
|
||||
|
||||
## 2.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/nhost-js",
|
||||
"version": "2.2.8",
|
||||
"version": "2.2.9",
|
||||
"description": "Nhost JavaScript SDK",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -52,12 +52,19 @@ export class NhostClient {
|
||||
this.functions = createFunctionsClient({ adminSecret, ...urlParams })
|
||||
this.graphql = createGraphqlClient({ adminSecret, ...urlParams })
|
||||
|
||||
this.auth.onAuthStateChanged((_event, session) => {
|
||||
if (_event === 'SIGNED_OUT') {
|
||||
this.auth.onAuthStateChanged((event, session) => {
|
||||
if (event === 'SIGNED_OUT') {
|
||||
this.storage.setAccessToken(undefined)
|
||||
this.functions.setAccessToken(undefined)
|
||||
this.graphql.setAccessToken(undefined)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const accessToken = session?.accessToken
|
||||
this.storage.setAccessToken(accessToken)
|
||||
this.functions.setAccessToken(accessToken)
|
||||
this.graphql.setAccessToken(accessToken)
|
||||
})
|
||||
|
||||
// * Update access token for clients, including when signin in
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/react
|
||||
|
||||
## 2.0.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5a4e237a2]
|
||||
- @nhost/nhost-js@2.2.9
|
||||
|
||||
## 2.0.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react",
|
||||
"version": "2.0.22",
|
||||
"version": "2.0.23",
|
||||
"description": "Nhost React library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/vue
|
||||
|
||||
## 1.13.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5a4e237a2]
|
||||
- @nhost/nhost-js@2.2.9
|
||||
|
||||
## 1.13.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/vue",
|
||||
"version": "1.13.28",
|
||||
"version": "1.13.29",
|
||||
"description": "Nhost Vue library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
989
pnpm-lock.yaml
generated
989
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user