Compare commits

...

10 Commits

Author SHA1 Message Date
github-actions[bot]
82728da100 chore: update versions (#2556)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/apollo@6.0.7

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost/react-apollo@9.0.2

### Patch Changes

-   @nhost/apollo@6.0.7
-   @nhost/react@3.2.2

## @nhost/react-urql@6.0.2

### Patch Changes

-   @nhost/react@3.2.2

## @nhost/graphql-js@0.1.7

### Patch Changes

- 2d68fee: fix: resolve an issue where unauthenticated graphql requests
are not sent

## @nhost/nextjs@2.1.4

### Patch Changes

-   @nhost/react@3.2.2

## @nhost/nhost-js@3.0.7

### Patch Changes

-   Updated dependencies [2d68fee]
    -   @nhost/graphql-js@0.1.7

## @nhost/react@3.2.2

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost/vue@2.2.2

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost/dashboard@1.8.1

### Patch Changes

-   @nhost/react-apollo@9.0.2
-   @nhost/nextjs@2.1.4

## @nhost-examples/cli@0.1.8

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost-examples/codegen-react-apollo@0.1.16

### Patch Changes

-   @nhost/react@3.2.2
-   @nhost/react-apollo@9.0.2

## @nhost-examples/codegen-react-query@0.1.17

### Patch Changes

-   @nhost/react@3.2.2

## @nhost-examples/codegen-react-urql@0.0.13

### Patch Changes

-   @nhost/react@3.2.2
-   @nhost/react-urql@6.0.2

## @nhost-examples/multi-tenant-one-to-many@2.0.6

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost-examples/nextjs@0.1.18

### Patch Changes

-   @nhost/react@3.2.2
-   @nhost/react-apollo@9.0.2
-   @nhost/nextjs@2.1.4

## @nhost-examples/node-storage@0.0.10

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost-examples/nextjs-server-components@0.2.4

### Patch Changes

-   @nhost/nhost-js@3.0.7

## @nhost-examples/react-apollo@0.3.2

### Patch Changes

-   @nhost/react@3.2.2
-   @nhost/react-apollo@9.0.2

## @nhost-examples/react-gqty@1.0.6

### Patch Changes

-   @nhost/react@3.2.2

## @nhost-examples/vue-apollo@0.2.3

### Patch Changes

-   @nhost/nhost-js@3.0.7
-   @nhost/apollo@6.0.7
-   @nhost/vue@2.2.2

## @nhost-examples/vue-quickstart@0.0.15

### Patch Changes

-   @nhost/apollo@6.0.7
-   @nhost/vue@2.2.2

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-23 21:14:11 +01:00
Hassan Ben Jobrane
2d68fee54c fix(graphql-js): allow graphql requests with no access token (#2555) 2024-02-23 21:09:45 +01:00
github-actions[bot]
35010353c7 chore: update versions (#2547)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/apollo@6.0.6

### Patch Changes

-   e0ab6d9: fix: add extra logic to check and wait for a valid JWT
    -   @nhost/nhost-js@3.0.6

## @nhost/react-apollo@9.0.1

### Patch Changes

-   Updated dependencies [e0ab6d9]
    -   @nhost/apollo@6.0.6
    -   @nhost/react@3.2.1

## @nhost/react-urql@6.0.1

### Patch Changes

-   @nhost/react@3.2.1

## @nhost/graphql-js@0.1.6

### Patch Changes

-   e0ab6d9: fix: add extra logic to check and wait for a valid JWT

## @nhost/hasura-auth-js@2.3.1

### Patch Changes

- 7baee8a: fix(hasura-auth-js): replace `jwt-decode` with `jose` for
decoding access tokens that works on both the browser and Node.js
-   e0ab6d9: fix: add extra logic to check and wait for a valid JWT

## @nhost/nextjs@2.1.3

### Patch Changes

-   @nhost/react@3.2.1

## @nhost/nhost-js@3.0.6

### Patch Changes

-   Updated dependencies [7baee8a]
-   Updated dependencies [e0ab6d9]
    -   @nhost/hasura-auth-js@2.3.1
    -   @nhost/graphql-js@0.1.6

## @nhost/react@3.2.1

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost/vue@2.2.1

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost/dashboard@1.8.0

### Minor Changes

- 713d53c: feat: add catch-all route for workspace/project - useful for
documentation

### Patch Changes

-   3db2999: fix: refresh table list after running SQL using the editor
- 3c4dd55: fix: handle `Error` objects properly in the `ErrorToast`
component
- 92b434e: fix: resolve an issue where the checkbox in the data-grid
header did not select all rows
    -   @nhost/react-apollo@9.0.1
    -   @nhost/nextjs@2.1.3

## @nhost-examples/cli@0.1.7

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost-examples/codegen-react-apollo@0.1.15

### Patch Changes

-   @nhost/react-apollo@9.0.1
-   @nhost/react@3.2.1

## @nhost-examples/codegen-react-query@0.1.16

### Patch Changes

-   @nhost/react@3.2.1

## @nhost-examples/codegen-react-urql@0.0.12

### Patch Changes

-   @nhost/react@3.2.1
-   @nhost/react-urql@6.0.1

## @nhost-examples/docker-compose@0.1.1

### Patch Changes

-   aff059e: fix: timers

## @nhost-examples/multi-tenant-one-to-many@2.0.5

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost-examples/nextjs@0.1.17

### Patch Changes

-   @nhost/react-apollo@9.0.1
-   @nhost/react@3.2.1
-   @nhost/nextjs@2.1.3

## @nhost-examples/node-storage@0.0.9

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost-examples/nextjs-server-components@0.2.3

### Patch Changes

-   @nhost/nhost-js@3.0.6

## @nhost-examples/react-apollo@0.3.1

### Patch Changes

-   @nhost/react-apollo@9.0.1
-   @nhost/react@3.2.1

## @nhost-examples/react-gqty@1.0.5

### Patch Changes

-   @nhost/react@3.2.1

## @nhost-examples/vue-apollo@0.2.2

### Patch Changes

-   Updated dependencies [e0ab6d9]
    -   @nhost/apollo@6.0.6
    -   @nhost/nhost-js@3.0.6
    -   @nhost/vue@2.2.1

## @nhost-examples/vue-quickstart@0.0.14

### Patch Changes

-   Updated dependencies [e0ab6d9]
    -   @nhost/apollo@6.0.6
    -   @nhost/vue@2.2.1

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-23 10:07:53 +01:00
David Barroso
aff059ec71 fix (examples/docker-compose): timers (#2553) 2024-02-23 10:04:20 +01:00
Nuno Pato
713d53cfc0 feat: dashboard: add catch-all route (#2545) 2024-02-22 17:59:24 -01:00
Hassan Ben Jobrane
e0ab6d9a37 fix: JWT expired bug (#2533)
fixes: https://github.com/nhost/nhost/issues/2348

---------

Co-authored-by: David Barroso <dbarrosop@dravetech.com>
2024-02-22 18:44:43 +01:00
Hassan Ben Jobrane
7baee8a9cc fix(hasura-auth-js): use jose instead of jwt-decode to decode the accessToken and get the Hasura claims (#2550)
fixes https://github.com/nhost/nhost/issues/2513
2024-02-21 11:17:03 +01:00
Hassan Ben Jobrane
3db2999f60 fix(dashboard): refresh table list after running a SQL stmt using the editor (#2549)
fixes https://github.com/nhost/projects/issues/52
2024-02-20 13:40:46 +01:00
Hassan Ben Jobrane
3c4dd55045 fix(dashboard): handle Error alongside ApolloError properly in the ErrorToast component (#2548)
fixes https://github.com/nhost/nhost/issues/2525
2024-02-20 12:16:26 +01:00
Hassan Ben Jobrane
92b434e840 fix: refactor DataGridHeader component to allow for selecting all rows (#2546)
fixes https://github.com/nhost/nhost/issues/2526
2024-02-19 17:30:59 +01:00
58 changed files with 686 additions and 93 deletions

View File

@@ -1,5 +1,26 @@
# @nhost/dashboard
## 1.8.1
### Patch Changes
- @nhost/react-apollo@9.0.2
- @nhost/nextjs@2.1.4
## 1.8.0
### Minor Changes
- 713d53c: feat: add catch-all route for workspace/project - useful for documentation
### Patch Changes
- 3db2999: fix: refresh table list after running SQL using the editor
- 3c4dd55: fix: handle `Error` objects properly in the `ErrorToast` component
- 92b434e: fix: resolve an issue where the checkbox in the data-grid header did not select all rows
- @nhost/react-apollo@9.0.1
- @nhost/nextjs@2.1.3
## 1.7.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/dashboard",
"version": "1.7.0",
"version": "1.8.1",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",

View File

@@ -96,45 +96,52 @@ export default function DataGridHeader<T extends object>({
}}
key={column.id}
>
<Dropdown.Trigger
className={twMerge(
'focus:outline-none motion-safe:transition-colors',
)}
disabled={
column.isDisabled ||
column.id === 'selection' ||
(column.disableSortBy && !onRemoveColumn)
}
hideChevron
>
{column.id === 'selection' ? (
<span
{...headerProps}
className="relative grid w-full grid-flow-col items-center justify-between p-2"
>
{column.render('Header')}
{allowSort && (
<Box component="span" sx={{ color: 'text.primary' }}>
{column.isSorted && !column.isSortedDesc && (
<ArrowUpIcon className="h-3 w-3" />
)}
{column.isSorted && column.isSortedDesc && (
<ArrowDownIcon className="h-3 w-3" />
)}
</Box>
)}
</span>
{allowResize && !column.disableResizing && (
) : (
<Dropdown.Trigger
className={twMerge(
'focus:outline-none motion-safe:transition-colors',
)}
disabled={
column.isDisabled || (column.disableSortBy && !onRemoveColumn)
}
hideChevron
>
<span
{...column.getResizerProps({
onClick: (event: Event) => event.stopPropagation(),
})}
className="absolute top-0 bottom-0 -right-0.5 z-10 h-full w-1.5 group-hover:bg-slate-900 group-hover:bg-opacity-20 group-active:bg-slate-900 group-active:bg-opacity-20 motion-safe:transition-colors"
/>
)}
</Dropdown.Trigger>
{...headerProps}
className="relative grid w-full grid-flow-col items-center justify-between p-2"
>
{column.render('Header')}
{allowSort && (
<Box component="span" sx={{ color: 'text.primary' }}>
{column.isSorted && !column.isSortedDesc && (
<ArrowUpIcon className="h-3 w-3" />
)}
{column.isSorted && column.isSortedDesc && (
<ArrowDownIcon className="h-3 w-3" />
)}
</Box>
)}
</span>
{allowResize && !column.disableResizing && (
<span
{...column.getResizerProps({
onClick: (event: Event) => event.stopPropagation(),
})}
className="absolute -right-0.5 bottom-0 top-0 z-10 h-full w-1.5 group-hover:bg-slate-900 group-hover:bg-opacity-20 group-active:bg-slate-900 group-active:bg-opacity-20 motion-safe:transition-colors"
/>
)}
</Dropdown.Trigger>
)}
<Dropdown.Content
menu

View File

@@ -5,7 +5,7 @@ import { XIcon } from '@/components/ui/v2/icons/XIcon';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { getToastBackgroundColor } from '@/utils/constants/settings';
import { copy } from '@/utils/copy';
import { type ApolloError } from '@apollo/client';
import { ApolloError } from '@apollo/client';
import { useUserData } from '@nhost/nextjs';
import { AnimatePresence, motion } from 'framer-motion';
import { useRouter } from 'next/router';
@@ -20,6 +20,43 @@ interface ErrorDetails {
error: any;
}
const getInternalErrorMessage = (
error: Error | ApolloError | undefined,
): string | null => {
if (!error) {
return null;
}
if (error instanceof ApolloError) {
const internalError = error.graphQLErrors?.[0]?.extensions?.internal as
| { error: { message: string } }
| undefined;
return internalError?.error?.message || null;
}
if (error instanceof Error) {
return error.message;
}
return null;
};
const errorToObject = (error: ApolloError | Error) => {
if (error instanceof ApolloError) {
return error;
}
if (error instanceof Error) {
return {
name: error.name,
message: error.message,
stack: error.stack,
};
}
return {};
};
export default function ErrorToast({
isVisible,
errorMessage,
@@ -28,7 +65,7 @@ export default function ErrorToast({
}: {
isVisible: boolean;
errorMessage: string;
error: ApolloError;
error: ApolloError | Error;
close: () => void;
}) {
const userData = useUserData();
@@ -43,19 +80,10 @@ export default function ErrorToast({
userId: userData?.id || 'local',
url: asPath,
},
error,
error: errorToObject(error),
};
const internalError = error?.graphQLErrors?.at(0)?.extensions?.internal as {
error: {
message: string;
};
};
const msg =
internalError?.error?.message ||
error?.graphQLErrors?.at(0).message ||
errorMessage;
const msg = getInternalErrorMessage(error) || errorMessage;
return (
<AnimatePresence>

View File

@@ -1,10 +1,12 @@
import { useDatabaseQuery } from '@/features/database/dataGrid/hooks/useDatabaseQuery';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { generateAppServiceUrl } from '@/features/projects/common/utils/generateAppServiceUrl';
import { getToastStyleProps } from '@/utils/constants/settings';
import { getHasuraAdminSecret } from '@/utils/env';
import { parseIdentifiersFromSQL } from '@/utils/sql';
import toast from 'react-hot-toast';
import { useRouter } from 'next/router';
import { useState } from 'react';
import toast from 'react-hot-toast';
export default function useRunSQL(
sqlCode: string,
@@ -22,6 +24,14 @@ export default function useRunSQL(
const [columns, setColumns] = useState<string[]>([]);
const [rows, setRows] = useState<string[][]>([[]]);
const router = useRouter();
const {
query: { dataSourceSlug },
} = router;
const { refetch } = useDatabaseQuery([dataSourceSlug as string]);
const appUrl = generateAppServiceUrl(
currentProject?.subdomain,
currentProject?.region,
@@ -269,6 +279,9 @@ export default function useRunSQL(
}
}
// refresh the table list after running the sql
await refetch();
setLoading(false);
};

View File

@@ -0,0 +1,158 @@
import { AuthenticatedLayout } from '@/components/layout/AuthenticatedLayout';
import { Container } from '@/components/layout/Container';
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { Input } from '@/components/ui/v2/Input';
import { List } from '@/components/ui/v2/List';
import { ListItem } from '@/components/ui/v2/ListItem';
import { Text } from '@/components/ui/v2/Text';
import {
useGetAllWorkspacesAndProjectsQuery,
type GetAllWorkspacesAndProjectsQuery,
} from '@/utils/__generated__/graphql';
import { Divider } from '@mui/material';
import { useUserData } from '@nhost/nextjs';
import debounce from 'lodash.debounce';
import Image from 'next/image';
import { useRouter } from 'next/router';
import type { ChangeEvent, ReactElement } from 'react';
import { Fragment, useEffect, useMemo, useState } from 'react';
type Workspace = Omit<
GetAllWorkspacesAndProjectsQuery['workspaces'][0],
'__typename'
>;
export default function SelectWorkspaceAndProject() {
const user = useUserData();
const router = useRouter();
const { data, loading } = useGetAllWorkspacesAndProjectsQuery({
skip: !user,
});
const workspaces: Workspace[] = data?.workspaces || [];
const projects = workspaces.flatMap((workspace) =>
workspace.projects.map((project) => ({
workspaceName: workspace.name,
projectName: project.name,
value: `${workspace.slug}/${project.slug}`,
})),
);
const [filter, setFilter] = useState('');
const handleFilterChange = useMemo(
() =>
debounce((event: ChangeEvent<HTMLInputElement>) => {
setFilter(event.target.value);
}, 200),
[],
);
useEffect(() => () => handleFilterChange.cancel(), [handleFilterChange]);
const goToProjectPage = async (project: {
workspaceName: string;
projectName: string;
value: string;
}) => {
const { slug } = router.query;
await router.push({
pathname: `/${project.value}/${
Array.isArray(slug) ? slug.join('/') : slug
}`,
});
};
const projectsToDisplay = filter
? projects.filter((project) =>
project.projectName.toLowerCase().includes(filter.toLowerCase()),
)
: projects;
if (loading) {
return (
<div className="flex w-full justify-center">
<ActivityIndicator
delay={500}
label="Loading workspaces and projects..."
/>
</div>
);
}
return (
<Container>
<div className="mx-auto grid max-w-[760px] grid-flow-row gap-4 py-6 sm:py-14">
<Text variant="h2" component="h1" className="">
Select a Project
</Text>
<div>
<div className="mb-2 flex w-full">
<Input
placeholder="Search..."
onChange={handleFilterChange}
fullWidth
autoFocus
/>
</div>
<RetryableErrorBoundary>
{projectsToDisplay.length === 0 ? (
<Box className="h-import py-2">
<Text variant="subtitle2">No results found.</Text>
</Box>
) : (
<List className="h-import overflow-y-auto">
{projectsToDisplay.map((project, index) => (
<Fragment key={project.value}>
<ListItem.Root
className="grid grid-flow-col justify-start gap-2 py-2.5"
secondaryAction={
<Button
variant="borderless"
color="primary"
onClick={() => goToProjectPage(project)}
>
Select
</Button>
}
>
<ListItem.Avatar>
<span className="inline-block h-6 w-6 overflow-hidden rounded-md">
<Image
src="/logos/new.svg"
alt="Nhost Logo"
width={24}
height={24}
/>
</span>
</ListItem.Avatar>
<ListItem.Text
primary={project.projectName}
secondary={`${project.workspaceName} / ${project.projectName}`}
/>
</ListItem.Root>
{index < projects.length - 1 && <Divider component="li" />}
</Fragment>
))}
</List>
)}
</RetryableErrorBoundary>
</div>
</div>
</Container>
);
}
SelectWorkspaceAndProject.getLayout = function getLayout(page: ReactElement) {
return (
<AuthenticatedLayout title="Select a Project">{page}</AuthenticatedLayout>
);
};

View File

@@ -29,6 +29,7 @@ import { Toaster } from 'react-hot-toast';
const emotionCache = createEmotionCache();
process.env = {
TEST_MODE: 'true',
NODE_ENV: 'development',
NEXT_PUBLIC_NHOST_PLATFORM: 'false',
NEXT_PUBLIC_ENV: 'dev',

View File

@@ -1,5 +1,17 @@
# @nhost-examples/cli
## 0.1.8
### Patch Changes
- @nhost/nhost-js@3.0.7
## 0.1.7
### Patch Changes
- @nhost/nhost-js@3.0.6
## 0.1.6
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/cli",
"version": "0.1.6",
"version": "0.1.8",
"main": "src/index.mjs",
"private": true,
"scripts": {

View File

@@ -1,5 +1,19 @@
# @nhost-examples/codegen-react-apollo
## 0.1.16
### Patch Changes
- @nhost/react@3.2.2
- @nhost/react-apollo@9.0.2
## 0.1.15
### Patch Changes
- @nhost/react-apollo@9.0.1
- @nhost/react@3.2.1
## 0.1.14
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/codegen-react-apollo",
"version": "0.1.14",
"version": "0.1.16",
"private": true,
"scripts": {
"codegen": "graphql-codegen",

View File

@@ -1,5 +1,17 @@
# @nhost-examples/codegen-react-query
## 0.1.17
### Patch Changes
- @nhost/react@3.2.2
## 0.1.16
### Patch Changes
- @nhost/react@3.2.1
## 0.1.15
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/codegen-react-query",
"version": "0.1.15",
"version": "0.1.17",
"private": true,
"scripts": {
"codegen": "graphql-codegen",

View File

@@ -1,5 +1,19 @@
# @nhost-examples/react-urql
## 0.0.13
### Patch Changes
- @nhost/react@3.2.2
- @nhost/react-urql@6.0.2
## 0.0.12
### Patch Changes
- @nhost/react@3.2.1
- @nhost/react-urql@6.0.1
## 0.0.11
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/codegen-react-urql",
"private": true,
"version": "0.0.11",
"version": "0.0.13",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",

View File

@@ -1,5 +1,11 @@
# @nhost-examples/docker-compose
## 0.1.1
### Patch Changes
- aff059e: fix: timers
## 0.1.0
### Minor Changes

View File

@@ -42,6 +42,13 @@ services:
HASURA_GRAPHQL_UNAUTHORIZED_ROLE: public
HASURA_GRAPHQL_LOG_LEVEL: debug
HASURA_GRAPHQL_ENABLE_CONSOLE: 'true'
healthcheck:
test:
- CMD-SHELL
- curl http://localhost:8080/healthz > /dev/null 2>&1
timeout: 60s
interval: 30s
start_period: 90s
labels:
- "traefik.enable=true"
- "traefik.http.routers.hasura.rule=Host(`${PROXY_HOST}`, `localhost`) && PathPrefix(`/`)"
@@ -66,7 +73,7 @@ services:
AUTH_SMTP_USER: user
AUTH_SMTP_PASS: password
AUTH_SMTP_SENDER: mail@example.com
expose:
expose:
- 4000
labels:
- "traefik.enable=true"
@@ -112,7 +119,7 @@ services:
- "traefik.http.routers.functions.middlewares=strip-functions@docker"
- "traefik.http.routers.functions.entrypoints=web"
restart: always
expose:
expose:
- 3000
volumes:
- .:/opt/project
@@ -140,7 +147,7 @@ services:
SMTP_SECURE: "${AUTH_SMTP_SECURE:-false}"
SMTP_SENDER: ${AUTH_SMTP_SENDER:-hbp@hbp.com}
ports:
- ${AUTH_SMTP_PORT:-1025}:1025
- ${AUTH_SMTP_PORT:-1025}:1025
- 8025:8025
volumes:
- ./data/mailhog:/maildir

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/docker-compose",
"version": "0.1.0",
"version": "0.1.1",
"private": true,
"scripts": {
"e2e": "vitest run"

View File

@@ -11,8 +11,12 @@ describe(
beforeAll(async () => {
// * Start docker compose
await promisifiedExec(
'docker compose -f docker-compose.yaml --env-file .env.example up --wait --quiet-pull'
'docker compose -f docker-compose.yaml --env-file .env.example up --wait --wait-timeout 300 --quiet-pull'
)
// we wait a bit extra because sometimes traefik takes a bit to configure the services
setTimeout(() => {}, 30000);
}, 5 * 60 * 1000)
afterAll(async () => {

View File

@@ -1,5 +1,17 @@
# @nhost-examples/multi-tenant-one-to-many
## 2.0.6
### Patch Changes
- @nhost/nhost-js@3.0.7
## 2.0.5
### Patch Changes
- @nhost/nhost-js@3.0.6
## 2.0.4
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/multi-tenant-one-to-many",
"private": true,
"version": "2.0.4",
"version": "2.0.6",
"description": "",
"main": "index.js",
"scripts": {},

View File

@@ -1,5 +1,21 @@
# @nhost-examples/nextjs
## 0.1.18
### Patch Changes
- @nhost/react@3.2.2
- @nhost/react-apollo@9.0.2
- @nhost/nextjs@2.1.4
## 0.1.17
### Patch Changes
- @nhost/react-apollo@9.0.1
- @nhost/react@3.2.1
- @nhost/nextjs@2.1.3
## 0.1.16
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/nextjs",
"version": "0.1.16",
"version": "0.1.18",
"private": true,
"scripts": {
"dev": "next dev",

View File

@@ -1,5 +1,17 @@
# @nhost-examples/node-storage
## 0.0.10
### Patch Changes
- @nhost/nhost-js@3.0.7
## 0.0.9
### Patch Changes
- @nhost/nhost-js@3.0.6
## 0.0.8
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/node-storage",
"version": "0.0.8",
"version": "0.0.10",
"private": true,
"description": "This is an example of how to use the Storage with Node.js",
"main": "src/index.mjs",

View File

@@ -1,5 +1,17 @@
# @nhost-examples/nextjs-server-components
## 0.2.4
### Patch Changes
- @nhost/nhost-js@3.0.7
## 0.2.3
### Patch Changes
- @nhost/nhost-js@3.0.6
## 0.2.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/nextjs-server-components",
"version": "0.2.2",
"version": "0.2.4",
"private": true,
"scripts": {
"dev": "next dev",

View File

@@ -1,5 +1,19 @@
# @nhost-examples/react-apollo
## 0.3.2
### Patch Changes
- @nhost/react@3.2.2
- @nhost/react-apollo@9.0.2
## 0.3.1
### Patch Changes
- @nhost/react-apollo@9.0.1
- @nhost/react@3.2.1
## 0.3.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/react-apollo",
"version": "0.3.0",
"version": "0.3.2",
"private": true,
"dependencies": {
"@apollo/client": "^3.9.4",

View File

@@ -1,5 +1,17 @@
# @nhost-examples/react-gqty
## 1.0.6
### Patch Changes
- @nhost/react@3.2.2
## 1.0.5
### Patch Changes
- @nhost/react@3.2.1
## 1.0.4
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/react-gqty",
"private": true,
"version": "1.0.4",
"version": "1.0.6",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,22 @@
# @nhost-examples/vue-apollo
## 0.2.3
### Patch Changes
- @nhost/nhost-js@3.0.7
- @nhost/apollo@6.0.7
- @nhost/vue@2.2.2
## 0.2.2
### Patch Changes
- Updated dependencies [e0ab6d9]
- @nhost/apollo@6.0.6
- @nhost/nhost-js@3.0.6
- @nhost/vue@2.2.1
## 0.2.1
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/vue-apollo",
"private": true,
"version": "0.2.1",
"version": "0.2.3",
"scripts": {
"dev": "vite",
"build": "vite build",

View File

@@ -1,5 +1,20 @@
# @nhost-examples/vue-quickstart
## 0.0.15
### Patch Changes
- @nhost/apollo@6.0.7
- @nhost/vue@2.2.2
## 0.0.14
### Patch Changes
- Updated dependencies [e0ab6d9]
- @nhost/apollo@6.0.6
- @nhost/vue@2.2.1
## 0.0.13
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/vue-quickstart",
"version": "0.0.13",
"version": "0.0.15",
"private": true,
"scripts": {
"build": "vite build",

View File

@@ -1,5 +1,18 @@
# @nhost/apollo
## 6.0.7
### Patch Changes
- @nhost/nhost-js@3.0.7
## 6.0.6
### Patch Changes
- e0ab6d9: fix: add extra logic to check and wait for a valid JWT
- @nhost/nhost-js@3.0.6
## 6.0.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/apollo",
"version": "6.0.5",
"version": "6.0.7",
"description": "Nhost Apollo Client library",
"license": "MIT",
"keywords": [
@@ -65,7 +65,8 @@
},
"dependencies": {
"graphql": "16.8.1",
"graphql-ws": "^5.14.3"
"graphql-ws": "^5.14.3",
"jwt-decode": "^3.1.2"
},
"devDependencies": {
"@apollo/client": "^3.9.4",

View File

@@ -12,6 +12,7 @@ import { setContext } from '@apollo/client/link/context'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions'
import { getMainDefinition } from '@apollo/client/utilities'
import { AuthContext, NhostClient } from '@nhost/nhost-js'
import jwtDecode, { JwtPayload } from 'jwt-decode'
import { createRestartableClient } from './ws'
const isBrowser = typeof window !== 'undefined'
@@ -58,25 +59,41 @@ export const createApolloClient = ({
let accessToken: AuthContext['accessToken'] | null = null
const isJwtValid = () => {
if (!accessToken?.value) {
return false
}
const marginInSeconds = 3
const marginInMilliseconds = marginInSeconds * 1000
let decodedToken: JwtPayload = jwtDecode(accessToken.value)
return decodedToken.exp! * 1000 > Date.now() - marginInMilliseconds
}
const isTokenValid = () =>
!!accessToken?.value && !!accessToken?.expiresAt && accessToken?.expiresAt > new Date()
!!accessToken?.value &&
!!accessToken?.expiresAt &&
accessToken?.expiresAt > new Date() &&
isJwtValid()
const isTokenValidOrNull = () => !accessToken || isTokenValid()
const awaitValidTokenOrNull = () => {
if (isTokenValidOrNull()) {
return
return Promise.resolve()
}
return new Promise((resolve) => {
// doing this as an interval to avoid race conditions.
const interval = setInterval(() => {
if (isTokenValidOrNull()) {
clearInterval(interval)
resolve(true)
}
}, 100)
})
const waitForValidToken = () => {
if (isTokenValidOrNull()) {
return Promise.resolve(true)
}
return new Promise((resolve) => {
setTimeout(() => waitForValidToken().then(resolve), 100)
})
}
return waitForValidToken()
}
const getAuthHeaders = async () => {

View File

@@ -1,5 +1,20 @@
# @nhost/react-apollo
## 9.0.2
### Patch Changes
- @nhost/apollo@6.0.7
- @nhost/react@3.2.2
## 9.0.1
### Patch Changes
- Updated dependencies [e0ab6d9]
- @nhost/apollo@6.0.6
- @nhost/react@3.2.1
## 9.0.0
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/react-apollo",
"version": "9.0.0",
"version": "9.0.2",
"description": "Nhost React Apollo client",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,17 @@
# @nhost/react-urql
## 6.0.2
### Patch Changes
- @nhost/react@3.2.2
## 6.0.1
### Patch Changes
- @nhost/react@3.2.1
## 6.0.0
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/react-urql",
"version": "6.0.0",
"version": "6.0.2",
"description": "Nhost React URQL client",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,17 @@
# @nhost/graphql-js
## 0.1.7
### Patch Changes
- 2d68fee: fix: resolve an issue where unauthenticated graphql requests are not sent
## 0.1.6
### Patch Changes
- e0ab6d9: fix: add extra logic to check and wait for a valid JWT
## 0.1.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/graphql-js",
"version": "0.1.5",
"version": "0.1.7",
"description": "Nhost GraphQL client",
"license": "MIT",
"keywords": [
@@ -54,7 +54,8 @@
},
"dependencies": {
"@graphql-typed-document-node/core": "^3.2.0",
"isomorphic-unfetch": "^3.1.0"
"isomorphic-unfetch": "^3.1.0",
"jwt-decode": "^3.1.2"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"

View File

@@ -12,6 +12,8 @@ import {
Variables
} from './types'
import jwtDecode, { JwtPayload } from 'jwt-decode'
/**
* @alias GraphQL
*/
@@ -28,6 +30,37 @@ export class NhostGraphqlClient {
this.adminSecret = adminSecret
}
private isAccessTokenValidOrNull = () => {
if (!this.accessToken) {
return true
}
try {
const decodedToken: JwtPayload = jwtDecode(this.accessToken)
return decodedToken.exp != null && decodedToken.exp * 1000 > Date.now()
} catch (error) {
console.error('Error decoding token:', error)
return false
}
}
private awaitForValidAccessTokenOrNull = async () => {
if (this.isAccessTokenValidOrNull()) {
return true
}
const waitForValidTokenOrNull = () => {
if (this.isAccessTokenValidOrNull()) {
return Promise.resolve(true)
}
return new Promise((resolve) => {
setTimeout(() => waitForValidTokenOrNull().then(resolve), 100)
})
}
return waitForValidTokenOrNull()
}
/**
* Use `nhost.graphql.request` to send a GraphQL request. For more serious GraphQL usage we recommend using a GraphQL client such as Apollo Client (https://www.apollographql.com/docs/react).
*
@@ -70,6 +103,12 @@ export class NhostGraphqlClient {
const { headers, ...otherOptions } = config || {}
const { query, operationName } = resolveRequestDocument(requestOptions.document)
if (!process.env.TEST_MODE) {
// We skip this while running unit tests because the accessToken is generated using faker
await this.awaitForValidAccessTokenOrNull()
}
try {
const response = await fetch(this.httpUrl, {
method: 'POST',

View File

@@ -1,5 +1,12 @@
# @nhost/hasura-auth-js
## 2.3.1
### Patch Changes
- 7baee8a: fix(hasura-auth-js): replace `jwt-decode` with `jose` for decoding access tokens that works on both the browser and Node.js
- e0ab6d9: fix: add extra logic to check and wait for a valid JWT
## 2.3.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/hasura-auth-js",
"version": "2.3.0",
"version": "2.3.1",
"description": "Hasura-auth client",
"license": "MIT",
"keywords": [
@@ -66,8 +66,8 @@
"dependencies": {
"@simplewebauthn/browser": "^6.2.2",
"fetch-ponyfill": "^7.1.0",
"jose": "^5.2.2",
"js-cookie": "^3.0.5",
"jwt-decode": "^3.1.2",
"xstate": "^4.38.3"
},
"devDependencies": {

View File

@@ -1,4 +1,4 @@
import jwt_decode from 'jwt-decode'
import * as jose from 'jose'
import { interpret } from 'xstate'
import {
EMAIL_NEEDS_VERIFICATION,
@@ -625,7 +625,7 @@ export class HasuraAuthClient {
public getDecodedAccessToken(): JWTClaims | null {
const jwt = this.getAccessToken()
if (!jwt) return null
return jwt_decode<JWTClaims>(jwt)
return jose.decodeJwt<JWTClaims>(jwt)
}
/**

View File

@@ -687,9 +687,16 @@ export const createAuthMachine = ({
isAutoRefreshDisabled: () => !autoRefreshToken,
refreshTimerShouldRefresh: (ctx) => {
const { expiresAt } = ctx.accessToken
if (!expiresAt) {
return false
}
// This happens when either the computer goes to sleep or when Chrome descides to suspend the tab
if (expiresAt.getTime() < Date.now()) {
return true
}
if (ctx.refreshTimer.lastAttempt) {
// * If the refresh timer reached the maximum number of attempts, we should not try again
if (ctx.refreshTimer.attempts > REFRESH_TOKEN_MAX_ATTEMPTS) {

View File

@@ -1,5 +1,17 @@
# @nhost/nextjs
## 2.1.4
### Patch Changes
- @nhost/react@3.2.2
## 2.1.3
### Patch Changes
- @nhost/react@3.2.1
## 2.1.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/nextjs",
"version": "2.1.2",
"version": "2.1.4",
"description": "Nhost NextJS library",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,21 @@
# @nhost/nhost-js
## 3.0.7
### Patch Changes
- Updated dependencies [2d68fee]
- @nhost/graphql-js@0.1.7
## 3.0.6
### Patch Changes
- Updated dependencies [7baee8a]
- Updated dependencies [e0ab6d9]
- @nhost/hasura-auth-js@2.3.1
- @nhost/graphql-js@0.1.6
## 3.0.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/nhost-js",
"version": "3.0.5",
"version": "3.0.7",
"description": "Nhost JavaScript SDK",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,17 @@
# @nhost/react
## 3.2.2
### Patch Changes
- @nhost/nhost-js@3.0.7
## 3.2.1
### Patch Changes
- @nhost/nhost-js@3.0.6
## 3.2.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/react",
"version": "3.2.0",
"version": "3.2.2",
"description": "Nhost React library",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,17 @@
# @nhost/vue
## 2.2.2
### Patch Changes
- @nhost/nhost-js@3.0.7
## 2.2.1
### Patch Changes
- @nhost/nhost-js@3.0.6
## 2.2.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/vue",
"version": "2.2.0",
"version": "2.2.2",
"description": "Nhost Vue library",
"license": "MIT",
"keywords": [

18
pnpm-lock.yaml generated
View File

@@ -1407,6 +1407,9 @@ importers:
graphql-ws:
specifier: ^5.14.3
version: 5.14.3(graphql@16.8.1)
jwt-decode:
specifier: ^3.1.2
version: 3.1.2
devDependencies:
'@apollo/client':
specifier: ^3.9.4
@@ -1575,6 +1578,9 @@ importers:
isomorphic-unfetch:
specifier: ^3.1.0
version: 3.1.0
jwt-decode:
specifier: ^3.1.2
version: 3.1.2
devDependencies:
'@nhost/docgen':
specifier: workspace:*
@@ -1591,12 +1597,12 @@ importers:
fetch-ponyfill:
specifier: ^7.1.0
version: 7.1.0
jose:
specifier: ^5.2.2
version: 5.2.2
js-cookie:
specifier: ^3.0.5
version: 3.0.5
jwt-decode:
specifier: ^3.1.2
version: 3.1.2
xstate:
specifier: ^4.38.3
version: 4.38.3
@@ -5938,7 +5944,7 @@ packages:
/@graphql-tools/utils@8.13.1(graphql@16.8.1):
resolution: {integrity: sha512-qIh9yYpdUFmctVqovwMdheVNJqFh+DQNWIhX87FJStfXYnmweBUDATok9fWPleKeFwxnW8IapKmY8m8toJEkAw==}
peerDependencies:
graphql: '>=16.8.1'
graphql: 16.8.1
dependencies:
graphql: 16.8.1
tslib: 2.6.2
@@ -19441,6 +19447,10 @@ packages:
resolution: {integrity: sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==}
dev: true
/jose@5.2.2:
resolution: {integrity: sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg==}
dev: false
/jpeg-js@0.4.4:
resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==}
dev: true