Compare commits
22 Commits
@nhost/goo
...
@nhost/rea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35010353c7 | ||
|
|
aff059ec71 | ||
|
|
713d53cfc0 | ||
|
|
e0ab6d9a37 | ||
|
|
7baee8a9cc | ||
|
|
3db2999f60 | ||
|
|
3c4dd55045 | ||
|
|
92b434e840 | ||
|
|
13d359602f | ||
|
|
0d8d0eb10f | ||
|
|
ed9df85778 | ||
|
|
41617b970a | ||
|
|
c5c904b716 | ||
|
|
7db095fe92 | ||
|
|
f33e07b191 | ||
|
|
017f1a6c7b | ||
|
|
93957c8af3 | ||
|
|
2505b2e26b | ||
|
|
5f4b4d2acc | ||
|
|
71a8ce4446 | ||
|
|
5ef5189898 | ||
|
|
791b7295fb |
@@ -1,5 +1,45 @@
|
||||
# @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
|
||||
|
||||
## 1.7.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 0d8d0eb: Update docs and dashboard references
|
||||
|
||||
## 1.6.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react-apollo@9.0.0
|
||||
- @nhost/nextjs@2.1.2
|
||||
|
||||
## 1.6.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react-apollo@8.0.1
|
||||
- @nhost/nextjs@2.1.1
|
||||
|
||||
## 1.6.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 5ef5189: fix: update `@apollo/client` to `3.9.4` to fix a cache bug
|
||||
|
||||
## 1.6.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -4,7 +4,6 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||
});
|
||||
const { version } = require('./package.json');
|
||||
|
||||
|
||||
const cspHeader = `
|
||||
default-src 'self' *.nhost.run ws://*.nhost.run nhost.run ws://nhost.run;
|
||||
script-src 'self' 'unsafe-eval' 'unsafe-inline' cdn.segment.com js.stripe.com;
|
||||
@@ -19,7 +18,7 @@ const cspHeader = `
|
||||
frame-src 'self' js.stripe.com;
|
||||
block-all-mixed-content;
|
||||
upgrade-insecure-requests;
|
||||
`
|
||||
`;
|
||||
|
||||
module.exports = withBundleAnalyzer({
|
||||
reactStrictMode: true,
|
||||
@@ -41,7 +40,7 @@ module.exports = withBundleAnalyzer({
|
||||
headers: [
|
||||
{
|
||||
key: 'X-Frame-Options',
|
||||
value: 'SAMEORIGIN'
|
||||
value: 'SAMEORIGIN',
|
||||
},
|
||||
{
|
||||
key: 'Content-Security-Policy',
|
||||
@@ -49,7 +48,7 @@ module.exports = withBundleAnalyzer({
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
];
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "1.6.6",
|
||||
"version": "1.8.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
@@ -19,7 +19,7 @@
|
||||
"e2e": "pnpm install-browsers && pnpm playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.9.1",
|
||||
"@apollo/client": "^3.9.4",
|
||||
"@codemirror/lang-sql": "^6.5.5",
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"@emotion/react": "^11.11.3",
|
||||
@@ -106,7 +106,7 @@
|
||||
"@storybook/addon-postcss": "^2.0.0",
|
||||
"@storybook/builder-webpack5": "^6.5.16",
|
||||
"@storybook/manager-webpack5": "^6.5.16",
|
||||
"@storybook/react": "^6.5.16",
|
||||
"@storybook/react": "^7.6.15",
|
||||
"@storybook/testing-library": "^0.2.2",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -130,7 +130,7 @@ export default function AllowedEmailDomainsSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication#allowed-emails-and-domains"
|
||||
docsLink="https://docs.nhost.io/guides/auth/overview#allowed-emails-and-domains"
|
||||
switchId="enabled"
|
||||
showSwitch
|
||||
className={twMerge(
|
||||
|
||||
@@ -105,7 +105,7 @@ export default function AllowedRedirectURLsSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication#allowed-redirect-urls"
|
||||
docsLink="https://docs.nhost.io/guides/auth/overview#allowed-redirect-urls"
|
||||
className="grid grid-flow-row px-4 lg:grid-cols-5"
|
||||
>
|
||||
<Input
|
||||
|
||||
@@ -141,7 +141,7 @@ export default function AppleProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication/sign-in-with-apple"
|
||||
docsLink="https://docs.nhost.io/guides/auth/social/sign-in-apple"
|
||||
docsTitle="how to sign in users with Apple"
|
||||
icon={
|
||||
theme.palette.mode === 'dark'
|
||||
|
||||
@@ -136,7 +136,7 @@ export default function BlockedEmailSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication#blocked-emails-and-domains"
|
||||
docsLink="https://docs.nhost.io/guides/auth/overview#allowed-emails-and-domains"
|
||||
switchId="enabled"
|
||||
showSwitch
|
||||
className={twMerge(
|
||||
|
||||
@@ -99,7 +99,7 @@ export default function ClientURLSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication#client-url"
|
||||
docsLink="https://docs.nhost.io/guides/auth/overview#client-url"
|
||||
className="grid grid-flow-row lg:grid-cols-5"
|
||||
>
|
||||
<Input
|
||||
|
||||
@@ -89,7 +89,7 @@ export default function DisableNewUsersSettings() {
|
||||
<SettingsContainer
|
||||
title="Disable New Users"
|
||||
description="If set, newly registered users are disabled and won't be able to sign in."
|
||||
docsLink="https://docs.nhost.io/authentication#disable-new-users"
|
||||
docsLink="https://docs.nhost.io/guides/auth/overview#disable-new-users"
|
||||
switchId="disabled"
|
||||
showSwitch
|
||||
slotProps={{
|
||||
|
||||
@@ -111,7 +111,7 @@ export default function DiscordProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/platform/authentication/sign-in-with-discord"
|
||||
docsLink="https://docs.nhost.io/guides/auth/social/sign-in-discord"
|
||||
docsTitle="how to sign in users with Discord"
|
||||
icon="/assets/brands/discord.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -104,7 +104,7 @@ export default function EmailAndPasswordSettings() {
|
||||
<SettingsContainer
|
||||
title="Email and Password"
|
||||
description="Allow users to sign in with email and password."
|
||||
docsLink="https://docs.nhost.io/authentication/sign-in-with-email-and-password"
|
||||
docsLink="https://docs.nhost.io/guides/auth/sign-in-email-password"
|
||||
docsTitle="how to sign in users with email and password"
|
||||
className="grid grid-flow-row"
|
||||
showSwitch
|
||||
|
||||
@@ -111,7 +111,7 @@ export default function FacebookProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/platform/authentication/sign-in-with-facebook"
|
||||
docsLink="https://docs.nhost.io/guides/auth/social/sign-in-facebook"
|
||||
docsTitle="how to sign in users with Facebook"
|
||||
icon="/assets/brands/facebook.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -113,7 +113,7 @@ export default function GitHubProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/platform/authentication/sign-in-with-github"
|
||||
docsLink="https://docs.nhost.io/guides/auth/social/sign-in-github"
|
||||
docsTitle="how to sign in users with GitHub"
|
||||
icon={
|
||||
theme.palette.mode === 'dark'
|
||||
|
||||
@@ -111,7 +111,7 @@ export default function GoogleProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/platform/authentication/sign-in-with-google"
|
||||
docsLink="https://docs.nhost.io/guides/auth/social/sign-in-google"
|
||||
docsTitle="how to sign in users with Google"
|
||||
icon="/assets/brands/google.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -113,7 +113,7 @@ export default function GravatarSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication#gravatar"
|
||||
docsLink="https://docs.nhost.io/guides/auth/overview#gravatar"
|
||||
switchId="enabled"
|
||||
showSwitch
|
||||
className={twMerge(
|
||||
|
||||
@@ -111,7 +111,7 @@ export default function LinkedInProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/platform/authentication/sign-in-with-linkedin"
|
||||
docsLink="https://docs.nhost.io/guides/auth/social/sign-in-linkedin"
|
||||
docsTitle="how to sign in users with LinkedIn"
|
||||
icon="/assets/brands/linkedin.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -102,7 +102,7 @@ export default function MFASettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication#multi-factor-authentication"
|
||||
docsLink="https://docs.nhost.io/guides/auth/overview#multi-factor-authentication"
|
||||
switchId="enabled"
|
||||
showSwitch
|
||||
className={twMerge(
|
||||
|
||||
@@ -97,7 +97,7 @@ export default function MagicLinkSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication/sign-in-with-magic-link"
|
||||
docsLink="https://docs.nhost.io/guides/auth/sign-in-magic-link"
|
||||
docsTitle="how to sign in users with Magic Link"
|
||||
switchId="enabled"
|
||||
showSwitch
|
||||
|
||||
@@ -137,7 +137,7 @@ export default function SMSSettings() {
|
||||
}}
|
||||
switchId="enabled"
|
||||
showSwitch
|
||||
docsLink="https://docs.nhost.io/authentication/sign-in-with-phone-number-sms"
|
||||
docsLink="https://docs.nhost.io/guides/auth/sign-in-phone-number"
|
||||
docsTitle="how to sign in users with a phone number (SMS)"
|
||||
className={twMerge(
|
||||
'grid grid-flow-col grid-cols-2 grid-rows-4 gap-x-3 gap-y-4 px-4 py-2',
|
||||
|
||||
@@ -111,7 +111,7 @@ export default function SpotifyProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/platform/authentication/sign-in-with-spotify"
|
||||
docsLink="https://docs.nhost.io/guides/auth/social/sign-in-spotify"
|
||||
docsTitle="how to sign in users with Spotify"
|
||||
icon="/assets/brands/spotify.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -113,7 +113,7 @@ export default function TwitchProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/platform/authentication/sign-in-with-twitch"
|
||||
docsLink="https://docs.nhost.io/guides/auth/social/sign-in-twitch"
|
||||
docsTitle="how to sign in users with Twitch"
|
||||
icon={
|
||||
theme.palette.mode === 'dark'
|
||||
|
||||
@@ -101,7 +101,7 @@ export default function WebAuthnSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication/sign-in-with-security-keys"
|
||||
docsLink="https://docs.nhost.io/guides/auth/sign-in-webauthn"
|
||||
docsTitle="how to sign in users with security keys"
|
||||
switchId="enabled"
|
||||
showSwitch
|
||||
|
||||
@@ -137,7 +137,7 @@ export default function WorkOsProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/authentication/sign-in-with-workos"
|
||||
docsLink="https://docs.nhost.io/guides/auth/social/sign-in-workos"
|
||||
docsTitle="how to sign in users with WorkOS"
|
||||
icon="/assets/brands/workos.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@ function Plan({ planName, price, setPlan, planId, selectedPlanId }: any) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="my-4 grid w-full grid-flow-col items-center justify-between gap-2 px-1"
|
||||
className="grid items-center justify-between w-full grid-flow-col gap-2 px-1 my-4"
|
||||
onClick={setPlan}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div className="grid grid-flow-row gap-y-0.5">
|
||||
<div className="grid grid-flow-col items-center justify-start gap-2">
|
||||
<div className="grid items-center justify-start grid-flow-col gap-2">
|
||||
<Checkbox
|
||||
onChange={setPlan}
|
||||
checked={selectedPlanId === planId}
|
||||
@@ -40,7 +40,7 @@ function Plan({ planName, price, setPlan, planId, selectedPlanId }: any) {
|
||||
<Text
|
||||
variant="h3"
|
||||
component="p"
|
||||
className="self-center text-left font-medium"
|
||||
className="self-center font-medium text-left"
|
||||
>
|
||||
Upgrade to {planName}
|
||||
</Text>
|
||||
@@ -156,7 +156,7 @@ export function ChangePlanModalWithData({ app, plans, close }: any) {
|
||||
|
||||
if (pollingCurrentProject) {
|
||||
return (
|
||||
<Box className="mx-auto w-full max-w-xl rounded-lg p-6 text-left">
|
||||
<Box className="w-full max-w-xl p-6 mx-auto text-left rounded-lg">
|
||||
<div className="flex flex-col">
|
||||
<div className="mx-auto">
|
||||
<Image
|
||||
@@ -179,7 +179,7 @@ export function ChangePlanModalWithData({ app, plans, close }: any) {
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
className="mx-auto mt-4 w-full max-w-sm"
|
||||
className="w-full max-w-sm mx-auto mt-4"
|
||||
onClick={() => {
|
||||
if (close) {
|
||||
close();
|
||||
@@ -196,7 +196,7 @@ export function ChangePlanModalWithData({ app, plans, close }: any) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Box className="w-full max-w-xl rounded-lg p-6 text-left">
|
||||
<Box className="w-full max-w-xl p-6 text-left rounded-lg">
|
||||
<BaseDialog
|
||||
open={showPaymentModal}
|
||||
onClose={() => setShowPaymentModal(false)}
|
||||
@@ -241,7 +241,7 @@ export function ChangePlanModalWithData({ app, plans, close }: any) {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-2 grid grid-flow-row gap-2">
|
||||
<div className="grid grid-flow-row gap-2 mt-2">
|
||||
<Button
|
||||
onClick={handleChangePlanClick}
|
||||
disabled={!selectedPlan}
|
||||
|
||||
@@ -138,7 +138,7 @@ export default function PermissionVariableSettings() {
|
||||
<SettingsContainer
|
||||
title="Permission Variables"
|
||||
description="Permission variables are used to define permission rules in the GraphQL API."
|
||||
docsLink="https://docs.nhost.io/graphql/permissions"
|
||||
docsLink="https://docs.nhost.io/guides/api/permissions#permission-variables"
|
||||
rootClassName="gap-0"
|
||||
className="my-2 px-0"
|
||||
slotProps={{ submitButton: { className: 'hidden' } }}
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function ResourcesFormFooter() {
|
||||
<Text>
|
||||
Learn more about{' '}
|
||||
<Link
|
||||
href="https://docs.nhost.io/platform/compute"
|
||||
href="https://docs.nhost.io/platform/compute-resources"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
underline="hover"
|
||||
|
||||
@@ -169,7 +169,7 @@ export default function RoleSettings() {
|
||||
<SettingsContainer
|
||||
title="Default Allowed Roles"
|
||||
description="Default Allowed Roles are roles users get automatically when they sign up."
|
||||
docsLink="https://docs.nhost.io/authentication/users#allowed-roles"
|
||||
docsLink="https://docs.nhost.io/guides/auth/users#allowed-roles"
|
||||
rootClassName="gap-0"
|
||||
className={twMerge(
|
||||
'my-2 px-0',
|
||||
|
||||
158
dashboard/src/pages/_/_/[...slug].tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@@ -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',
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# @nhost/docs
|
||||
|
||||
## 2.5.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 0d8d0eb: Update docs and dashboard references
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 41617b9: feat: added elevated permissions docs
|
||||
- 7db095f: chore: added a note about disk performance and CDN information
|
||||
|
||||
## 2.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 791b729: fix: remove auth method
|
||||
|
||||
## 2.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
92
docs/guides/auth/elevated-permissions.mdx
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: Elevated Permissions
|
||||
sidebarTitle: Elevated Permissions
|
||||
description: Require extra permissions to perform critical operations
|
||||
icon: unlock
|
||||
---
|
||||
|
||||
In some scenarios you may want to add an extra layer of security to perform certain actions or view certain data. For instance, you may wish to allow users to view their profile information after authenticating but you may want to require users to confirm changes to their profile by performing an extra validation step with a [security key](sign-in-webauthn).
|
||||
|
||||

|
||||
|
||||
This is accomplished by adding the claim `x-hasura-auth-elevated: $user-id` to the access token in response to the extra security challenge. With this new claim in mind you can start writing permissions that require this extra step.
|
||||
|
||||
It is important to keep in mind the claim is added to the access token so, for as long as you have that access token, you will have elevated access. Once the access token is renewed (due to expiration or any other reason), the claim will be lost and a new security challenge will be required to add it back.
|
||||
|
||||
# API Endpoints and SDK components
|
||||
|
||||
To implement this functionality [auth](/product/authentication) [0.26.0](https://github.com/nhost/hasura-auth/releases/tag/0.26.0) introduces the new endpoint [/elevate/webauthn](/reference/auth/elevate-webauthn). This endpoint works the same way as [/signin/webauthn](/reference/auth/sign-in-using-email-via-fido2-webauthn-authentication) but it requires an Authorization header with a valid token to add the elevated claim to.
|
||||
|
||||
In addition, a few methods and components have also been added to simplify it's usage:
|
||||
|
||||
- [nhost.auth.elevateEmailSecurityKey](/reference/javascript/auth/elevate-email-security-key)
|
||||
- [React - useElevateSecurityKeyEmail](/reference/react/use-elevate-security-key-email)
|
||||
- [Next.js - useElevateSecurityKeyEmail](/reference/nextjs/use-elevate-security-key-email)
|
||||
- [Vue - useElevateSecurityKeyEmail](/reference/vue/use-elevate-security-key-email)
|
||||
|
||||
# Protecting Hasura data
|
||||
|
||||
You can use the claim `x-hasura-auth-elevated` in exactly the same way you would normally use `X-Hasura-User-Id` for added security. For instance, the following permissions would allow users to see their data without any extra security:
|
||||
|
||||

|
||||
|
||||
While the following permissions would require them first to elevate their access in order to update them:
|
||||
|
||||

|
||||
|
||||
# Protecting Auth data
|
||||
|
||||
Some user information needs to be changed via hasura-auth's API rather than via graphql mutations. These endpoints are:
|
||||
|
||||
- [/user/password](/reference/auth/set-a-new-password) for changing passwords
|
||||
- [/user/email/change](/reference/auth/change-the-current-users-email) for changing emails
|
||||
- [/user/mfa](/reference/auth/activatedeactivate-multi-factor-authentication) for enabling or disabling MFA
|
||||
- [/user/webauthn/add](/reference/auth/initialize-adding-of-a-new-webauthn-security-key-device-browser) for adding security keys
|
||||
- [/pat](/reference/auth/create-personal-access-token-pat) for PAT creation
|
||||
|
||||
To protect these endpoints and require the elevated claim you can use the following configuration option:
|
||||
|
||||
```toml
|
||||
[auth.elevatedPrivileges]
|
||||
mode = 'required'
|
||||
```
|
||||
|
||||
The mode can be one of `disabled` (default), `required`, `recommended`.
|
||||
|
||||
In `disabled` mode the elevated claim isn't required in any of the options above. This is the default behavior.
|
||||
|
||||
In `required` mode, all of the endpoints above will require an access token with the elevated claim. There is only one exception to this rule. If the user has no security key, adding the first security key won't require the elevated claim. However, as the rest of the endpoints do require it, the user won't be able to perform any changes until it adds a security key and gets an access token with the elevated claim. Adding extra security keys after the first one has been added will require the elevated claim. Removing security keys always requires the elevated claim.
|
||||
|
||||
In `recommended` mode, the elevated claim is only required if the user has a security key configured. If a user doesn't have a security key the elevated claim won't be required to perform the actions described above. If the user has one or more keys the elevated claims will be required. This mode provides flexibility for the users to choose if they want the extra security or not.
|
||||
|
||||
<Warning>
|
||||
If you are allowing users to perform changes to auth data directly by performing graphql mutations (i.e. deleting security keys), don't forget to update the permissions to match the desired behavior.
|
||||
</Warning>
|
||||
|
||||
## Example
|
||||
|
||||
To demonstrate this functionality we have implemented them in our [react-apollo](https://react-apollo.example.nhost.io) ([source](https://github.com/nhost/nhost/tree/main/examples/react-apollo)) and [vue-apollo](https://vue-apollo.example.nhost.io) ([source](https://github.com/nhost/nhost/tree/main/examples/vue-apollo)) examples.
|
||||
|
||||
### Secret Notes
|
||||
|
||||
To demonstrate how the elevated claim can work for permissions you can check the "Secret Notes" example. In this example, we are allowing users to see their secret notes by giving the `select` permissions `notes.user_id eq X-Hasura-User-Id`
|
||||
|
||||

|
||||
|
||||
However, we are requiring elevated permissions to `insert`, `update` and `delete` with the permissions `notes.user_id eq x-hasura-auth-elevated`. In our example we automatically initiate the elevation process if the claim isn't already present:
|
||||
|
||||

|
||||
|
||||
Note that after elevating permissions the secret note is added and the elevated claim persists until there is a token refreshed.
|
||||
|
||||

|
||||
|
||||
### Updating profile information
|
||||
|
||||
In addition, to demonstrate the new `auth.elevatedPrivileges` setting, we have set it to `required` in this example requiring elevated access to perform certain changes. For instance, if you try to change your password you will first have to elevate your access:
|
||||
|
||||

|
||||
|
||||
After elevating access the password is changed:
|
||||
|
||||

|
||||
111
docs/guides/auth/overview.mdx
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Learn about Nhost Auth
|
||||
icon: hand-wave
|
||||
---
|
||||
|
||||
Nhost Auth is a ready-to-use authentication service seamlessly integrated with the [GraphQL API](/product/graphql) and its [Permission System](/guides/api/permissions) from Hasura. This allows you to easily add user authentication to your application without having to build and maintain your own authentication system.
|
||||
|
||||
## Supported Methods
|
||||
|
||||
<CardGroup cols={4}>
|
||||
<Card title="Email and Password" icon="square-1" href="/guides/auth/sign-in-email-password">
|
||||
</Card>
|
||||
<Card title="Magic Link" icon="square-2" href="/guides/auth/sign-in-magic-link">
|
||||
</Card>
|
||||
<Card title="Phone Number (SMS)" icon="square-3" href="/guides/auth/sign-in-phone-number">
|
||||
</Card>
|
||||
<Card title="Security Keys (WebAuthn)" icon="square-4" href="/guides/auth/sign-in-webauthn">
|
||||
</Card>
|
||||
<Card title="Elevated Permissions" icon="square-5" href="/guides/auth/elevated-permissions">
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### OAuth Providers
|
||||
|
||||
<CardGroup cols={4}>
|
||||
<Card title="Apple" icon="square-1" href="/guides/auth/social/sign-in-apple">
|
||||
</Card>
|
||||
<Card title="Discord" icon="square-2" href="/guides/auth/social/sign-in-discord">
|
||||
</Card>
|
||||
<Card title="Facebook" icon="square-3" href="/guides/auth/social/sign-in-facebook">
|
||||
</Card>
|
||||
<Card title="GitHub" icon="square-4" href="/guides/auth/social/sign-in-github">
|
||||
</Card>
|
||||
<Card title="Google" icon="square-5" href="/guides/auth/social/sign-in-google">
|
||||
</Card>
|
||||
<Card title="Linkedin" icon="square-6" href="/guides/auth/social/sign-in-linkedin">
|
||||
</Card>
|
||||
<Card title="Spotify" icon="square-7" href="/guides/auth/social/sign-in-spotify">
|
||||
</Card>
|
||||
<Card title="Twitch" icon="square-8" href="/guides/auth/social/sign-in-twitch">
|
||||
</Card>
|
||||
<Card title="WorkOS" icon="square-9" href="/guides/auth/social/sign-in-workos">
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Client URL
|
||||
|
||||
Client URL is the URL of your frontend application. The Client URL is used to redirect the user in certain auth workflows like signing in or resetting a password.
|
||||
|
||||
## Allowed Redirect URLs
|
||||
|
||||
Allowed Redirect URLs are the URLs of your frontend application that users are allowed to be redirected to on specific auth workflows. This is useful if you have multiple applications using the same Nhost backend or if you want to redirect users to a specific URL after certain authentication workflows.
|
||||
|
||||
As an example, for a staging project, you can set the Client URL to `https://staging.example.com` and Allowed Redirect URLs to `https://*.vercel.app`. This way, the user can be redirected to any Vercel deployment of your frontend application.
|
||||
|
||||
## Allowed Emails and Domains
|
||||
|
||||
Allowed Emails and Domains are used to restrict what email adresses and domains are valid when signing up and signing in.
|
||||
|
||||
If both allowed emails and allowed domains are set a user can only sign up if their email address matches one of the allowed emails or one of the allowed domains.
|
||||
|
||||
## Blocked Emails and Domains
|
||||
|
||||
Blocked Emails and Domains are used to block specific email addresses and domains from signing up and signing in.
|
||||
|
||||
Note that even if a user's email address matches any allowed email or domain, they will still be blocked if their email address matches any blocked email or domain.
|
||||
|
||||
## Multi-factor Authentication
|
||||
|
||||
By enabling Multi-Factor Authentication (MFA), you can allow users to verify their identity using a second factor during the sign-in process. We currently support Authenticator Apps (TOTP) for MFA.
|
||||
|
||||
A user can enable MFA for their account by scanning a QR code with their Authenticator App. After that, they will be prompted to enter a code generated by their Authenticator App during the sign-in process.
|
||||
|
||||
## Gravatar
|
||||
|
||||
If Gravatar is enabled, Nhost Auth will use the user's email address to fetch their Gravatar profile picture. If the user doesn't have a Gravatar profile picture, a default image will be used.
|
||||
|
||||
There are two options for Gravatars.
|
||||
|
||||
<Steps>
|
||||
<Step title="Default Image">
|
||||
|
||||
If the user doesn't have a Gravatar profile picture, a default image will be used. You can choose between the following options:
|
||||
|
||||
- `404`: Do not load any image if none is associated with the email hash, instead return an HTTP 404 (File Not Found) response.
|
||||
- `mp`: (mystery-person) a simple, cartoon-style silhouetted outline of a person (does not vary by email hash).
|
||||
- `identicon`: a geometric pattern based on an email hash.
|
||||
- `monsterid`: a generated 'monster' with different colors, faces, etc.
|
||||
- `wavatar`: generated faces with differing features and backgrounds.
|
||||
- `retro`: awesome generated, 8-bit arcade-style pixelated faces.
|
||||
- `robohash`: a generated robot with different colors, faces, etc.
|
||||
- `blank`: a transparent PNG image.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Rating">
|
||||
|
||||
Gravatar images are rated by default. You can choose between the following options:
|
||||
|
||||
- `g`: suitable for display on all websites with any audience type.
|
||||
- `pg`: may contain rude gestures, provocatively dressed individuals, lesser swear words or mild violence.
|
||||
- `r`: may contain such things as harsh profanity, intense violence, nudity, or hard drug use.
|
||||
- `x`: may contain hardcore sexual imagery or extremely disturbing violence.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Disable New Users
|
||||
|
||||
If set, newly registered users are disabled and won't be able to sign in. This is useful if you want to manually approve new users before they can sign in.
|
||||
155
docs/guides/auth/users.mdx
Normal file
@@ -0,0 +1,155 @@
|
||||
---
|
||||
title: Users
|
||||
description: Learn about Users managed by Nhost Auth
|
||||
icon: users
|
||||
---
|
||||
|
||||
## Creating Users
|
||||
|
||||
Users are created using the sign-up or sign-in flows described under [Supported Methods](/guides/auth/overview#supported-methods).
|
||||
|
||||
- **Avoid** creating users directly via GraphQL or the database, unless you are [importing users](#import-users) from an external system.
|
||||
- **Avoid** modifying the database schema for the `auth.users` table.
|
||||
- **Avoid** modifying the GraphQL root queries or fields for any of the tables in the `auth` schema.
|
||||
|
||||
You're allowed to:
|
||||
|
||||
- Add and remove your GraphQL relationships for the `users` table and other tables in the `auth` schema.
|
||||
- Create, edit and delete permissions for the `users` table and other tables in the `auth` schema.
|
||||
|
||||
## Roles
|
||||
|
||||
Each user has one **default role** and a list of **allowed roles**. These roles are used to resolve permissions for requests to [GraphQL](/graphql/permissions) and [Storage](/storage#permissions).
|
||||
|
||||
When the user makes a request, only one role is used to resolve permissions. The default role is used if no role is explicitly specified. Users can only make requests using the default role or one of the allowed roles.
|
||||
|
||||
### Default Role
|
||||
|
||||
The default role is used when no role is specified in the request. By default, users' default role is `user`.
|
||||
|
||||
You can change what the default role for new users should be at **Settings -> Roles and Permissions**.
|
||||
|
||||
### Allowed Roles
|
||||
|
||||
Allowed roles are roles the user is allowed to use when making a request. Usually, you would change the role from `user` (the default role) to some other role because you want to use a different role to resolve permissions for a particular request.
|
||||
|
||||
By default, users have two allowed roles:
|
||||
|
||||
- `user` (default)
|
||||
- `me`
|
||||
|
||||
You can change the default role for new users at **Settings -> Roles and Permissions**.
|
||||
|
||||
#### Assign Allowed Roles
|
||||
|
||||
It's possible to give users a subset of allowed roles during signup.
|
||||
|
||||
**Example:** Only set the `user` role (exclude the `me` role) for the user's allowed roles:
|
||||
|
||||
```js
|
||||
await nhost.auth.signUp({
|
||||
email: 'joe@example.com',
|
||||
password: 'secret-password'
|
||||
options: {
|
||||
allowedRoles: ['user']
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Set Role for GraphQL Requests
|
||||
|
||||
When no role is specified, the user's default role will be used:
|
||||
|
||||
```js
|
||||
await nhost.graphql.request(QUERY, {})
|
||||
```
|
||||
|
||||
If you want to make a GraphQL request using a specific role, you can do so by using the `x-hasura-role` header, like this:
|
||||
|
||||
```js
|
||||
await nhost.graphql.request(
|
||||
QUERY,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
'x-hasura-role': 'me'
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
If the request is not part of the user's allowed roles, the request will fail.
|
||||
|
||||
## Metadata
|
||||
|
||||
You can store custom information about the user in the `metadata` column of the `users` table. The `metadata` column is of type JSONB so any JSON data can be stored.
|
||||
|
||||
**Example:** Add metadata to a user during sign-up:
|
||||
|
||||
```js
|
||||
await nhost.auth.signUp({
|
||||
email: 'joe@example.com',
|
||||
password: 'secret-password',
|
||||
options: {
|
||||
metadata: {
|
||||
birthYear: 1989,
|
||||
town: 'Stockholm',
|
||||
likes: ['Postgres', 'GraphQL', 'Hasura', 'Authentication', 'Storage', 'Serverless Functions']
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Get User Information using GraphQL
|
||||
|
||||
**Example:** Get all users.
|
||||
|
||||
```graphql
|
||||
query {
|
||||
users {
|
||||
id
|
||||
displayName
|
||||
email
|
||||
metadata
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example:** Get a single user.
|
||||
|
||||
```graphql
|
||||
query {
|
||||
user(id: "<user-id>") {
|
||||
id
|
||||
displayName
|
||||
email
|
||||
metadata
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Import Users
|
||||
|
||||
If you have users in a different system, you can import them into Nhost. When importing users you should insert the users directly into the database instead of using the authentication endpoints (`/signup/email-password`) to avoid sending unnecessary transactional emails.
|
||||
|
||||
### GraphQL
|
||||
|
||||
Make a GraphQL request to insert a user like this:
|
||||
|
||||
```graphql
|
||||
mutation insertUser($user: users_insert_input!) {
|
||||
insertUser(object: $user) {
|
||||
id
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### SQL
|
||||
|
||||
Connect directly to the database and insert a user like this:
|
||||
|
||||
```sql
|
||||
INSERT INTO auth.users (id, email, display_name, password_hash, ..) VALUES ('<user-id>', '<email>', '<display-name>', '<password-hash>', ..);
|
||||
```
|
||||
|
||||
Passwords are hashed using [bcrypt](https://en.wikipedia.org/wiki/Bcrypt).
|
||||
@@ -12,78 +12,41 @@ Postgres configuration can be tweaked to customize the **runtime behavior**, **p
|
||||
Postgres.
|
||||
</Warning>
|
||||
|
||||
### Available Settings
|
||||
|
||||
The following `CUE` schema contains all postgres settings available in `nhost.toml`.
|
||||
|
||||
```cue schema
|
||||
#Postgres: {
|
||||
version: string | *"14.6-20230705-1"
|
||||
|
||||
// Resources for the service, optional
|
||||
resources?: #Resources & {
|
||||
replicas: 1
|
||||
}
|
||||
|
||||
// postgres settings of the same name in camelCase, optional
|
||||
settings?: {
|
||||
jit: "off" | "on" | *"on"
|
||||
maxConnections: int32 | *100
|
||||
sharedBuffers: string | *"128MB"
|
||||
effectiveCacheSize: string | *"4GB"
|
||||
maintenanceWorkMem: string | *"64MB"
|
||||
checkpointCompletionTarget: number | *0.9
|
||||
walBuffers: int32 | *-1
|
||||
defaultStatisticsTarget: int32 | *100
|
||||
randomPageCost: number | *4.0
|
||||
effectiveIOConcurrency: int32 | *1
|
||||
workMem: string | *"4MB"
|
||||
hugePages: string | *"try"
|
||||
minWalSize: string | *"80MB"
|
||||
maxWalSize: string | *"1GB"
|
||||
maxWorkerProcesses: int32 | *8
|
||||
maxParallelWorkersPerGather: int32 | *2
|
||||
maxParallelWorkers: int32 | *8
|
||||
maxParallelMaintenanceWorkers: int32 | *2
|
||||
walLevel: string | *"replica"
|
||||
maxWalSenders: int32 | *10
|
||||
maxReplicationSlots: int32 | *10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Example
|
||||
|
||||
To configure your Postgres instance, simply add the relevant settings under `[postgres.settings]` in your project's `nhost.toml` file.
|
||||
|
||||
```toml nhost.toml
|
||||
[postgres]
|
||||
version = '14.6-20230925-1'
|
||||
version = '14-20230312-1'
|
||||
|
||||
[postgres.resources.compute]
|
||||
cpu = 1000
|
||||
memory = 2048
|
||||
cpu = 2000
|
||||
memory = 4096
|
||||
|
||||
[postgres.resources.storage]
|
||||
capacity = 20
|
||||
|
||||
[postgres.settings]
|
||||
jit = "off"
|
||||
jit = 'off'
|
||||
maxConnections = 100
|
||||
sharedBuffers = '256MB'
|
||||
effectiveCacheSize = '768MB'
|
||||
sharedBuffers = '128MB'
|
||||
effectiveCacheSize = '4GB'
|
||||
maintenanceWorkMem = '64MB'
|
||||
checkpointCompletionTarget = 0.9
|
||||
walBuffers = -1
|
||||
walBuffers = '-1'
|
||||
defaultStatisticsTarget = 100
|
||||
randomPageCost = 1.1
|
||||
effectiveIOConcurrency = 200
|
||||
workMem = '1310kB'
|
||||
hugePages = 'off'
|
||||
randomPageCost = 4.0
|
||||
effectiveIOConcurrency = 1
|
||||
workMem = '4MB'
|
||||
hugePages = 'try'
|
||||
minWalSize = '80MB'
|
||||
maxWalSize = '1GB'
|
||||
maxWorkerProcesses = 8
|
||||
maxParallelWorkersPerGather = 2
|
||||
maxParallelWorkers = 8
|
||||
maxParallelMaintenanceWorkers = 2
|
||||
walLevel = "replica"
|
||||
walLevel = 'replica'
|
||||
maxWalSenders = 10
|
||||
maxReplicationSlots = 10
|
||||
```
|
||||
|
||||
@@ -16,6 +16,8 @@ In case your Postgres service is not meeting your performance expectations, you
|
||||
|
||||
4. Evaluate the usage of indexes in your database. Identify queries that could benefit from additional indexes and strategically add them to improve query performance.
|
||||
|
||||
5. Increase the disk size to increase [disk performance](/platform/compute-resources#disk-performance). Keep in mind increasing the disk size isn't reversible and increasing the memory of the service may yield better results. This is mostly useful when your data is very volatile and the postgres cache can't work effectively. Only attempt to increase disk for performance reasons if your reads and writes are very high and increasing memory isn't effective.
|
||||
|
||||
By implementing these steps, you can effectively address performance concerns and enhance the overall performance of your Postgres service.
|
||||
|
||||
## Upgrade to our latest postgres image
|
||||
|
||||
28
docs/guides/storage/cdn.mdx
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: CDN
|
||||
description: Serving files lightning fast
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
The [storage](/product/storage) service integrates with a CDN service to cache files and serve them close to users. This leads to faster response times and lower load on the backend service. You can read our initial [announcement](https://nhost.io/blog/launching-nhost-cdn-nhost-storage-is-now-blazing-fast) for some general information about what a CDN is and the performance gains.
|
||||
|
||||
# Security
|
||||
|
||||
The primary function of a CDN is to deliver cached files quickly to users without relying on the backend. While this is effective for public files, we need to handle non-public files differently to ensure that only users with proper permissions can access those files. Instead of serving the file directly, a conditional fetch is performed, including the user's Authorization header. This allows the backend service to verify the user's permissions. By conducting this conditional check, the backend service only needs to confirm to the CDN that the file can be served to the specific user, eliminating the need to serve the file itself.
|
||||
|
||||
# Cache invalidation
|
||||
|
||||
If a file is modified or deleted, we instruct the CDN to immediately invalidate the cached files. This is done automatically by the storage service and requires no special handling.
|
||||
|
||||
# Maximizing HITs
|
||||
|
||||
In CDN terminology, a HIT occurs when a file is found in the cache and can be served to the user. Conversely, a MISS happens when the file is not yet cached and requires a round trip to the backend to retrieve it before it can be served to the user.
|
||||
|
||||
To lower response times and backend load, we want to maximize HITs as much as possible. To do that here are some notes and recommendations:
|
||||
|
||||
1. Links with different query arguments are treated as different files, even if they all point to the same underlying file.
|
||||
2. Due to (1), if you are using the image manipulation feature, each set of options (i.e. different sizes or qualities), will be treated as different files. Having too many different combinations may increase the number of MISSes and be counterproductive.
|
||||
3. Only use presigned URLs if you really must to and reuse if possible. Due to (1) as well, each presigned URL is different, which means they are treated by the CDN as different files, even if they all point to the same file.
|
||||
4. If possible, always prefer public files
|
||||
5. Authenticated files are your second best option. You can use [nhost.storage.download](/reference/javascript/storage/download) to download private files.
|
||||
6. If you are hosting large files, don't be afraid of using the [range header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range). The service should be able to cache and serve partial files too.
|
||||
@@ -0,0 +1,24 @@
|
||||
sequenceDiagram
|
||||
Note over User: User logins as usual
|
||||
User->>App: I want to login, please
|
||||
App->>Backend: /signin/...
|
||||
Backend->>App: Session{...}
|
||||
App->>User: Here is your session
|
||||
|
||||
Note over User: Actions that don't require elevated permissions work as usual
|
||||
User->>App: I want to see my profile data
|
||||
App->>Backend: query getProfileData { ... }
|
||||
Backend->>App: data { ... }
|
||||
App->>User: Here is your profile
|
||||
|
||||
Note over User: Action that requires elevated permissions starts here
|
||||
User->>App: I want to change my address to X
|
||||
App->>Backend: /elevate/webauthn
|
||||
Backend->>App: SecurityChallenge{ ... }
|
||||
App->>User: SecurityChallenge{ ... }
|
||||
User->>App: SecurityChallengeResponse{ ... }
|
||||
App->>+Backend: SecurityChallengeResponse{ ... }
|
||||
Backend->>App: SessionWithElevatedClaim{ ... }
|
||||
App->>Backend: mutation updateAddress { ... }
|
||||
Backend->>App: success { ... }
|
||||
App->>User: Your address has been changed
|
||||
BIN
docs/images/guides/auth/elevated-permissions/overview.png
Normal file
|
After Width: | Height: | Size: 232 KiB |
BIN
docs/images/guides/auth/elevated-permissions/password.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 878 KiB |
|
After Width: | Height: | Size: 872 KiB |
|
After Width: | Height: | Size: 691 KiB |
|
After Width: | Height: | Size: 798 KiB |
BIN
docs/images/guides/auth/elevated-permissions/select.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
docs/images/guides/auth/elevated-permissions/update.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
@@ -111,6 +111,8 @@
|
||||
{
|
||||
"group": "Authentication",
|
||||
"pages": [
|
||||
"guides/auth/overview",
|
||||
"guides/auth/users",
|
||||
{
|
||||
"group": "Social Sign In",
|
||||
"icon": "at",
|
||||
@@ -130,6 +132,7 @@
|
||||
"guides/auth/sign-in-magic-link",
|
||||
"guides/auth/sign-in-phone-number",
|
||||
"guides/auth/sign-in-webauthn",
|
||||
"guides/auth/elevated-permissions",
|
||||
"guides/auth/email-templates"
|
||||
]
|
||||
},
|
||||
@@ -139,7 +142,7 @@
|
||||
},
|
||||
{
|
||||
"group": "Storage",
|
||||
"pages": ["guides/storage/overview", "guides/storage/antivirus"]
|
||||
"pages": ["guides/storage/overview", "guides/storage/cdn", "guides/storage/antivirus"]
|
||||
},
|
||||
{
|
||||
"group": "Functions",
|
||||
@@ -193,7 +196,9 @@
|
||||
"reference/auth/sign-up-using-email-via-fido2-webauthn-authentication",
|
||||
"reference/auth/verfiy-fido2-webauthn-authentication-and-complete-signup",
|
||||
"reference/auth/sign-in-using-email-via-fido2-webauthn-authentication",
|
||||
"reference/auth/verfiy-fido2-webauthn-authentication-using-public-key-cryptography"
|
||||
"reference/auth/verfiy-fido2-webauthn-authentication-using-public-key-cryptography",
|
||||
"reference/auth/elevate-webauthn",
|
||||
"reference/auth/elevate-webauthn-verify"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -224,7 +229,8 @@
|
||||
"reference/auth/get-user-information",
|
||||
"reference/auth/refresh-the-oauth-access-tokens-of-a-given-user-you-must-be-an-admin-to-perform-this-operation",
|
||||
"reference/auth/initialize-adding-of-a-new-webauthn-security-key-device-browser",
|
||||
"reference/auth/verfiy-adding-of-a-new-webauth-security-key-device-browser"
|
||||
"reference/auth/verfiy-adding-of-a-new-webauth-security-key-device-browser",
|
||||
"reference/auth/create-personal-access-token-pat"
|
||||
]
|
||||
},
|
||||
"reference/auth/sign-out"
|
||||
@@ -291,7 +297,6 @@
|
||||
"group": "Auth",
|
||||
"pages": [
|
||||
"reference/javascript/auth/hasura-auth-client",
|
||||
"reference/javascript/auth/add-security-key",
|
||||
"reference/javascript/auth/change-email",
|
||||
"reference/javascript/auth/change-password",
|
||||
"reference/javascript/auth/create-pat",
|
||||
@@ -312,7 +317,9 @@
|
||||
"reference/javascript/auth/sign-in",
|
||||
"reference/javascript/auth/sign-in-pat",
|
||||
"reference/javascript/auth/sign-out",
|
||||
"reference/javascript/auth/sign-up"
|
||||
"reference/javascript/auth/sign-up",
|
||||
"reference/javascript/auth/add-security-key",
|
||||
"reference/javascript/auth/elevate-email-security-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -380,6 +387,7 @@
|
||||
"reference/react/use-sign-out",
|
||||
"reference/react/use-sign-up-email-password",
|
||||
"reference/react/use-sign-up-email-security-key-email",
|
||||
"reference/react/use-elevate-security-key-email",
|
||||
"reference/react/use-user-avatar-url",
|
||||
"reference/react/use-user-data",
|
||||
"reference/react/use-user-default-role",
|
||||
@@ -426,6 +434,7 @@
|
||||
"reference/nextjs/use-sign-out",
|
||||
"reference/nextjs/use-sign-up-email-password",
|
||||
"reference/nextjs/use-sign-up-email-security-key-email",
|
||||
"reference/nextjs/use-elevate-security-key-email",
|
||||
"reference/nextjs/use-user-avatar-url",
|
||||
"reference/nextjs/use-user-data",
|
||||
"reference/nextjs/use-user-default-role",
|
||||
@@ -472,7 +481,11 @@
|
||||
"reference/vue/use-user-id",
|
||||
"reference/vue/use-user-is-anonymous",
|
||||
"reference/vue/use-user-locale",
|
||||
"reference/vue/use-user-roles"
|
||||
"reference/vue/use-user-roles",
|
||||
"reference/vue/use-add-security-key",
|
||||
"reference/vue/use-elevate-security-key-email",
|
||||
"reference/vue/use-sign-in-email-security-key",
|
||||
"reference/vue/use-sign-up-email-security-key"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/docs",
|
||||
"version": "2.3.0",
|
||||
"version": "2.5.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "mintlify dev"
|
||||
|
||||
@@ -4,7 +4,7 @@ description: "Allocate CPU and Memory to your backend infrastructure"
|
||||
icon: server
|
||||
---
|
||||
|
||||
Compute resources are the fundamental units that represent the processing power and memory available to your projects. The primary compute resources are vCPU and RAM.
|
||||
Compute resources are the fundamental units that represent the processing power and memory available to your projects. The primary compute resources are vCPU and RAM.
|
||||
|
||||
This documentation outlines the key aspects of compute resources in the context of the Nhost Cloud Platform.
|
||||
|
||||
@@ -40,7 +40,7 @@ Projects on the pro tier have a total of 2 shared vCPUs and 2 GiB of RAM spread
|
||||
|
||||
## Dedicated Compute
|
||||
|
||||
For production workloads where latency is essential or consistent performance is non-negotiable, we strongly suggest the use of dedicated resources.
|
||||
For production workloads where latency is essential or consistent performance is non-negotiable, we strongly suggest the use of dedicated resources.
|
||||
|
||||
<Note>Compute/Dedicated resources are only available on the Pro plan</Note>
|
||||
|
||||
@@ -72,3 +72,21 @@ To setup dedicated resources for your project, you can either use the Dashboard
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Disk Performance
|
||||
|
||||
Services may require a disk provisioned to store data. For instance, [postgres](/guides/database/configuring-postgres#configuration-example) comes with a disk provisioned by default and [Nhost Run](/product/run) may [too](/guides/run/resources#storage). For these cases we provisioned SSD disks with the following performance:
|
||||
|
||||
- Baseline: 3000 IOPS
|
||||
- Baseline: 125Mbps of thoughput
|
||||
- Every 50GB: +350 IOPS and +15Mbps of throughput
|
||||
|
||||
For example, the following disk sizes will have the following performance:
|
||||
|
||||
| Size | IOPS | Throughput |
|
||||
| ---- | ---- | ---------- |
|
||||
| 1 | 3000| 125 |
|
||||
| 10 | 3000| 125 |
|
||||
| 49 | 3000| 125 |
|
||||
| 50 | 3350| 140 |
|
||||
| 100 | 3700 | 155 |
|
||||
| 300 | 5100 | 215 |
|
||||
|
||||
@@ -21,7 +21,7 @@ export default (req: Request, res: Response) => {
|
||||
Variables created are available to all services, including Run Services and Functions
|
||||
</Note>
|
||||
|
||||
## Add Environment Variables
|
||||
## Adding Environment Variables
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Config">
|
||||
@@ -39,3 +39,43 @@ value = "Nhost is Awesome!"
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## System Environment Variables
|
||||
|
||||
System environment variables are generated and managed by Nhost. The following variables are available:
|
||||
|
||||
- `NHOST_ADMIN_SECRET`
|
||||
- `NHOST_WEBHOOK_SECRET`
|
||||
- `NHOST_SUBDOMAIN`
|
||||
- `NHOST_REGION`
|
||||
- `NHOST_HASURA_URL`
|
||||
- `NHOST_AUTH_URL`
|
||||
- `NHOST_GRAPHQL_URL`
|
||||
- `NHOST_STORAGE_URL`
|
||||
- `NHOST_FUNCTIONS_URL`
|
||||
- `NHOST_JWT_SECRET`
|
||||
|
||||
`NHOST_ADMIN_SECRET`, `NHOST_WEBHOOK_SECRET` and `NHOST_JWT_SECRET` are populated with values from their corresponding secrets.
|
||||
|
||||
**Example values**:
|
||||
|
||||
```text
|
||||
NHOST_ADMIN_SECRET={{ secrets.HASURA_GRAPHQL_ADMIN_SECRET }}
|
||||
|
||||
NHOST_WEBHOOK_SECRET={{ secrets.NHOST_WEBHOOK_SECRET }}
|
||||
|
||||
NHOST_SUBDOMAIN=abv123abc
|
||||
|
||||
NHOST_REGION=eu-central-1
|
||||
|
||||
NHOST_HASURA_URL=https://abc123abc.hasura.eu-central-1.nhost.run/console
|
||||
|
||||
NHOST_AUTH_URL=https://abc123abc.auth.eu-central-1.nhost.run/v1
|
||||
|
||||
NHOST_GRAPHQL_URL=https://abc123abc.graphql.eu-central-1.nhost.run/v1
|
||||
|
||||
NHOST_STORAGE_URL=https://abc123abc.storage.eu-central-1.nhost.run/v1
|
||||
|
||||
NHOST_FUNCTIONS_URL=https://abc123abc.functions.eu-central-1.nhost.run/v1
|
||||
|
||||
NHOST_JWT_SECRET={"key": "{{ secrets.HASURA_GRAPHQL_JWT_SECRET }}", "type": "HS256" }
|
||||
```
|
||||
|
||||
@@ -19,6 +19,8 @@ Combined with a powerful **Permission Rules** system, Nhost Auth offers everythi
|
||||
</Card>
|
||||
<Card title="Security Keys (WebAuthn)" icon="square-4" href="../guides/auth/sign-in-webauthn">
|
||||
</Card>
|
||||
<Card title="Elevated Permissions" icon="square-5" href="../guides/auth/elevated-permissions">
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### OAuth Providers
|
||||
|
||||
@@ -4,13 +4,18 @@ description: Store and Serve large files
|
||||
icon: file
|
||||
---
|
||||
|
||||
Use Nhost Storage to store and retrieve large files such as videos, images, large documents, or any other objects.
|
||||
Use Nhost Storage to store and retrieve large files such as videos, images, large documents, or any other objects.
|
||||
|
||||
Nhost Storage includes a built-in image optimizer, so you can resize and compress your media files on the fly.
|
||||
|
||||
### CDN
|
||||
|
||||
Serve your assets with a global CDN to reduce latency.
|
||||
<CardGroup cols={4}>
|
||||
<Card title="Overview" icon="square-1" href="/guides/storage/overview">
|
||||
</Card>
|
||||
<Card title="CDN" icon="square-2" href="/guides/storage/cdn">
|
||||
</Card>
|
||||
<Card title="Antivirus" icon="square-3" href="/guides/storage/antivirus">
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
|
||||
### Additional Resources
|
||||
|
||||
3
docs/reference/auth/create-personal-access-token-pat.mdx
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
openapi: post /pat
|
||||
---
|
||||
4
docs/reference/auth/elevate-webauthn-verify.mdx
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
openapi: post /elevate/webauthn/verify
|
||||
sidebarTitle: Elevate Verify
|
||||
---
|
||||
4
docs/reference/auth/elevate-webauthn.mdx
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
openapi: post /elevate/webauthn
|
||||
sidebarTitle: Elevate
|
||||
---
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: elevateEmailSecurityKey()
|
||||
sidebarTitle: elevateEmailSecurityKey()
|
||||
---
|
||||
|
||||
Use `nhost.auth.elevateEmailSecurityKey` to get a temporary elevated auth permissions to run sensitive operations.
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">email</span>** <span className="optional-status">required</span> <code>string</code>
|
||||
|
||||
user email
|
||||
|
||||
---
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: ElevateWithSecurityKeyHandlerResult
|
||||
sidebarTitle: ElevateWithSecurityKeyHandlerResult
|
||||
description: No description provided.
|
||||
---
|
||||
|
||||
# `ElevateWithSecurityKeyHandlerResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">elevated</span>** <span className="optional-status">required</span> <code>boolean</code>
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> <code>boolean</code>
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> <code>null | [`AuthErrorPayload`](/reference/javascript/auth/types/auth-error-payload)</code>
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> <code>boolean</code>
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
@@ -16,7 +16,7 @@ instance of `NhostClient` that is ready to use on the server side (signed in or
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">params</span>** <span className="optional-status">required</span> <code>string | Partial<Pick<NhostReactClientConstructorParams, "subdomain" | "region" | "authUrl" | "graphqlUrl" | "storageUrl" | "functionsUrl">></code>
|
||||
**<span className="parameter-name">params</span>** <span className="optional-status">required</span> <code>Partial<Pick<NhostReactClientConstructorParams, "subdomain" | "region" | "authUrl" | "graphqlUrl" | "storageUrl" | "functionsUrl">></code>
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">params</span>** <span className="optional-status">required</span> <code>string | Partial<Pick<NhostReactClientConstructorParams, "subdomain" | "region" | "authUrl" | "graphqlUrl" | "storageUrl" | "functionsUrl">></code>
|
||||
**<span className="parameter-name">params</span>** <span className="optional-status">required</span> <code>Partial<Pick<NhostReactClientConstructorParams, "subdomain" | "region" | "authUrl" | "graphqlUrl" | "storageUrl" | "functionsUrl">></code>
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: NhostClient
|
||||
sidebarTitle: NhostClient
|
||||
description: No description provided.
|
||||
---
|
||||
|
||||
@@ -21,7 +20,6 @@ description: No description provided.
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>authUrl</span> | <code>string</code> | | |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>region</span> | <code>string</code> | | Project region (e.g. `eu-central-1`) Project region is not required during local development (when `subdomain` is `localhost`) |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>subdomain</span> | <code>string</code> | | Project subdomain (e.g. `ieingiwnginwnfnegqwvdqwdwq`) Use `localhost` during local development |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>backendUrl</span> | <code>string</code> | | Nhost backend URL Will be deprecated in a future release. Please look at 'subdomain' and 'region' instead. |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>devTools</span> | <code>boolean</code> | | Activate devTools e.g. the ability to connect to the xstate inspector |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>autoSignIn</span> | <code>boolean</code> | | When set to true, will parse the url on startup to check if it contains a refresh token to start the session with |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>autoRefreshToken</span> | <code>boolean</code> | | When set to true, will automatically refresh token before it expires |
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: BackendUrl
|
||||
sidebarTitle: BackendUrl
|
||||
description: No description provided.
|
||||
---
|
||||
|
||||
# `BackendUrl`
|
||||
|
||||
```ts
|
||||
type BackendUrl = () => { backendUrl: string }
|
||||
```
|
||||
@@ -47,13 +47,6 @@ Use `localhost` during local development
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">backendUrl</span>** <span className="optional-status">optional</span> <code>string</code>
|
||||
|
||||
Nhost backend URL
|
||||
Will be deprecated in a future release. Please look at 'subdomain' and 'region' instead.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">devTools</span>** <span className="optional-status">optional</span> <code>boolean</code>
|
||||
|
||||
Activate devTools e.g. the ability to connect to the xstate inspector
|
||||
|
||||
@@ -62,13 +62,6 @@ Activate devTools e.g. the ability to connect to the xstate inspector
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">backendUrl</span>** <span className="optional-status">optional</span> <code>string</code>
|
||||
|
||||
Nhost backend URL
|
||||
Will be deprecated in a future release. Please look at 'subdomain' and 'region' instead.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">subdomain</span>** <span className="optional-status">optional</span> <code>string</code>
|
||||
|
||||
Project subdomain (e.g. `ieingiwnginwnfnegqwvdqwdwq`)
|
||||
|
||||
14
docs/reference/nextjs/use-elevate-security-key-email.mdx
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: useElevateSecurityKeyEmail()
|
||||
sidebarTitle: useElevateSecurityKeyEmail()
|
||||
---
|
||||
|
||||
Use the hook `useElevateSecurityKeyEmail` to elevate the user auth permission in order to perform sensitive operations
|
||||
|
||||
```tsx
|
||||
const { elevateEmailSecurityKey, elevated } = useElevateSecurityKeyEmail()
|
||||
|
||||
console.log({ elevated })
|
||||
|
||||
await elevateEmailSecurityKey('joe@example.com')
|
||||
```
|
||||
@@ -3,7 +3,7 @@ title: useSignUpEmailSecurityKeyEmail()
|
||||
sidebarTitle: useSignUpEmailSecurityKeyEmail()
|
||||
---
|
||||
|
||||
Use the hook `useSignUpEmailSecurityKey` to sign up a user with security key and an email using the WebAuthn API.
|
||||
Use the hook `useSignUpEmailSecurityKeyEmail` to sign up a user with security key and an email using the WebAuthn API.
|
||||
|
||||
```tsx
|
||||
const {
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignUpEmailSecurityKey()
|
||||
} = useSignUpEmailSecurityKeyEmail()
|
||||
|
||||
console.log({ needsEmailVerification, isLoading, isSuccess, isError, error })
|
||||
|
||||
|
||||
@@ -11,18 +11,9 @@ info:
|
||||
servers:
|
||||
- url: https://local.auth.nhost.run/v1
|
||||
description: API Server
|
||||
security:
|
||||
- AdminSecret: []
|
||||
- BearerAuth: []
|
||||
|
||||
components:
|
||||
|
||||
securitySchemes:
|
||||
AdminSecret:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-Hasura-Admin-Secret
|
||||
description: Hasura Admin Secret
|
||||
BearerAuth:
|
||||
scheme: bearer
|
||||
type: http
|
||||
@@ -59,6 +50,35 @@ components:
|
||||
userVerification: "preferred"
|
||||
rpId: "react-apollo.example.nhost.io"
|
||||
|
||||
ElevateWebauthnSessionPayload:
|
||||
type: object
|
||||
properties:
|
||||
challenge:
|
||||
type: string
|
||||
allowCredentials:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
timeout:
|
||||
type: integer
|
||||
userVerification:
|
||||
type: string
|
||||
rpId:
|
||||
type: string
|
||||
example:
|
||||
challenge: "KOGeoAfC2nrZ_SluhmU5RYYMvBsRDvzghjERGdXbbfQ"
|
||||
allowCredentials:
|
||||
- id: "zCnsWvxgtMrOCeX6eA_yqQ"
|
||||
type: "public-key"
|
||||
timeout: 60000
|
||||
userVerification: "preferred"
|
||||
rpId: "react-apollo.example.nhost.io"
|
||||
|
||||
WebauthnSessionPayload:
|
||||
type: object
|
||||
properties:
|
||||
@@ -761,7 +781,69 @@ components:
|
||||
example: john.smith@nhost.io
|
||||
format: email
|
||||
type: string
|
||||
credential:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
rawId:
|
||||
type: string
|
||||
response:
|
||||
type: object
|
||||
properties:
|
||||
authenticatorData:
|
||||
type: string
|
||||
clientDataJSON:
|
||||
type: string
|
||||
signature:
|
||||
type: string
|
||||
userHandle:
|
||||
type: string
|
||||
format: uuid
|
||||
type:
|
||||
type: string
|
||||
clientExtensionResults:
|
||||
type: object
|
||||
authenticatorAttachment:
|
||||
type: string
|
||||
example:
|
||||
email: "nuno@nhost.io"
|
||||
credential:
|
||||
id: "zCnsWvxgtMrOCeX6eA_yqQ"
|
||||
rawId: "zCnsWvxgtMrOCeX6eA_yqQ"
|
||||
response:
|
||||
authenticatorData: "0RE6Bmg2J-FxNrC8136ZQSeTWKWtdni_Lpfv5XR4bDsdAAAAAA"
|
||||
clientDataJSON: "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiTkNSSVRVU1pjeFE1ZTFhdUtUcXVlNnA4R0ZacHdxUS1kZzM4bnlWa3NCRSIsIm9yaWdpbiI6Imh0dHBzOi8vcmVhY3QtYXBvbGxvLmV4YW1wbGUubmhvc3QuaW8ifQ"
|
||||
signature: "MEUCIQDRXq3aY-gXWsuYJZhOzqqn6UpoRQfcPdNLP7hpZ7IdvQIgX5rY6TomkYUtqydu-w88fW7KeFm-0oE-5jTdLNHg9zw"
|
||||
userHandle: "8881037a-8495-48ef-8a04-ebbdb69415db"
|
||||
type: "public-key"
|
||||
clientExtensionResults: {}
|
||||
authenticatorAttachment: "platform"
|
||||
required:
|
||||
- email
|
||||
- credential
|
||||
|
||||
ElevateWebauthnSchema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
email:
|
||||
description: A valid email
|
||||
example: john.smith@nhost.io
|
||||
format: email
|
||||
type: string
|
||||
required:
|
||||
- email
|
||||
|
||||
ElevateVerifyWebauthnSchema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
properties:
|
||||
email:
|
||||
description: A valid email
|
||||
example: john.smith@nhost.io
|
||||
format: email
|
||||
type: string
|
||||
credential:
|
||||
type: object
|
||||
properties:
|
||||
@@ -1288,6 +1370,20 @@ components:
|
||||
example: 0.17.1
|
||||
type: string
|
||||
|
||||
CreatePATSchema:
|
||||
type: object
|
||||
properties:
|
||||
expiresAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: The expiration date and time of the personal access token
|
||||
example:
|
||||
metadata:
|
||||
type: object
|
||||
description: Additional metadata associated with the personal access token
|
||||
additionalProperties: true
|
||||
required:
|
||||
- expiresAt
|
||||
|
||||
paths:
|
||||
/healthz:
|
||||
@@ -1459,7 +1555,6 @@ paths:
|
||||
tags:
|
||||
- Authentication
|
||||
|
||||
|
||||
/signin/anonymous:
|
||||
post:
|
||||
description: 'Sign In a user anonymously'
|
||||
@@ -1524,7 +1619,6 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DisabledEndpointError'
|
||||
description: The feature is not activated
|
||||
security: []
|
||||
summary: Sign In TOTP
|
||||
tags:
|
||||
- Authentication
|
||||
@@ -1753,7 +1847,7 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SignInWebauthnSessionPayload'
|
||||
description: The payload is invalid
|
||||
description: Signed in successfully
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
@@ -1812,6 +1906,82 @@ paths:
|
||||
tags:
|
||||
- Authentication
|
||||
|
||||
/elevate/webauthn:
|
||||
post:
|
||||
description: Elevate access for an already signed in user using FIDO2 Webauthn
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ElevateWebauthnSchema'
|
||||
description: ''
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ElevateWebauthnSessionPayload'
|
||||
description: Elevated Webauthn successfully
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/InvalidRequestError'
|
||||
description: The payload is invalid
|
||||
'404':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DisabledEndpointError'
|
||||
description: The feature is not activated
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Elevate WebAuthn
|
||||
tags:
|
||||
- Authentication
|
||||
|
||||
/elevate/webauthn/verify:
|
||||
post:
|
||||
description: Verify FIDO2 Webauthn authentication using public-key cryptography
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ElevateVerifyWebauthnSchema'
|
||||
description: ''
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SessionPayload'
|
||||
description: Access elevated successfully
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/InvalidRequestError'
|
||||
description: The payload is invalid
|
||||
'401':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UnauthorizedError'
|
||||
description: Invalid email or password, or user is not verified
|
||||
'404':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DisabledEndpointError'
|
||||
description: The feature is not activated
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Elevate WebAuthn Verify
|
||||
tags:
|
||||
- Authentication
|
||||
|
||||
/mfa/totp/generate:
|
||||
get:
|
||||
parameters: []
|
||||
@@ -1965,7 +2135,7 @@ paths:
|
||||
description: User is not authenticated
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Get user information
|
||||
summary: Get User Information
|
||||
tags:
|
||||
- User management
|
||||
|
||||
@@ -2140,7 +2310,7 @@ paths:
|
||||
description: User is not authenticated
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Set a new password
|
||||
summary: Set New Password
|
||||
tags:
|
||||
- User management
|
||||
|
||||
@@ -2278,6 +2448,42 @@ paths:
|
||||
tags:
|
||||
- User management
|
||||
|
||||
/pat:
|
||||
post:
|
||||
deprecated: false
|
||||
parameters: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreatePATSchema'
|
||||
description: ''
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Session'
|
||||
description: User successfully authenticated
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/InvalidRequestError'
|
||||
description: The payload format is invalid
|
||||
'401':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UnauthenticatedUserError'
|
||||
description: User is not authenticated
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Create Personal Access Token (PAT)
|
||||
tags:
|
||||
- User management
|
||||
|
||||
/verify:
|
||||
get:
|
||||
deprecated: false
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: NhostClient
|
||||
sidebarTitle: NhostClient
|
||||
description: No description provided.
|
||||
---
|
||||
|
||||
@@ -21,7 +20,6 @@ description: No description provided.
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>authUrl</span> | <code>string</code> | | |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>region</span> | <code>string</code> | | Project region (e.g. `eu-central-1`) Project region is not required during local development (when `subdomain` is `localhost`) |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>subdomain</span> | <code>string</code> | | Project subdomain (e.g. `ieingiwnginwnfnegqwvdqwdwq`) Use `localhost` during local development |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>backendUrl</span> | <code>string</code> | | Nhost backend URL Will be deprecated in a future release. Please look at 'subdomain' and 'region' instead. |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>devTools</span> | <code>boolean</code> | | Activate devTools e.g. the ability to connect to the xstate inspector |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>autoSignIn</span> | <code>boolean</code> | | When set to true, will parse the url on startup to check if it contains a refresh token to start the session with |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>autoRefreshToken</span> | <code>boolean</code> | | When set to true, will automatically refresh token before it expires |
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: BackendUrl
|
||||
sidebarTitle: BackendUrl
|
||||
description: No description provided.
|
||||
---
|
||||
|
||||
# `BackendUrl`
|
||||
|
||||
```ts
|
||||
type BackendUrl = () => { backendUrl: string }
|
||||
```
|
||||
@@ -47,13 +47,6 @@ Use `localhost` during local development
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">backendUrl</span>** <span className="optional-status">optional</span> <code>string</code>
|
||||
|
||||
Nhost backend URL
|
||||
Will be deprecated in a future release. Please look at 'subdomain' and 'region' instead.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">devTools</span>** <span className="optional-status">optional</span> <code>boolean</code>
|
||||
|
||||
Activate devTools e.g. the ability to connect to the xstate inspector
|
||||
|
||||
14
docs/reference/react/use-elevate-security-key-email.mdx
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: useElevateSecurityKeyEmail()
|
||||
sidebarTitle: useElevateSecurityKeyEmail()
|
||||
---
|
||||
|
||||
Use the hook `useElevateSecurityKeyEmail` to elevate the user auth permission in order to perform sensitive operations
|
||||
|
||||
```tsx
|
||||
const { elevateEmailSecurityKey, elevated } = useElevateSecurityKeyEmail()
|
||||
|
||||
console.log({ elevated })
|
||||
|
||||
await elevateEmailSecurityKey('joe@example.com')
|
||||
```
|
||||
@@ -3,7 +3,7 @@ title: useSignUpEmailSecurityKeyEmail()
|
||||
sidebarTitle: useSignUpEmailSecurityKeyEmail()
|
||||
---
|
||||
|
||||
Use the hook `useSignUpEmailSecurityKey` to sign up a user with security key and an email using the WebAuthn API.
|
||||
Use the hook `useSignUpEmailSecurityKeyEmail` to sign up a user with security key and an email using the WebAuthn API.
|
||||
|
||||
```tsx
|
||||
const {
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignUpEmailSecurityKey()
|
||||
} = useSignUpEmailSecurityKeyEmail()
|
||||
|
||||
console.log({ needsEmailVerification, isLoading, isSuccess, isError, error })
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: NhostClient
|
||||
sidebarTitle: NhostClient
|
||||
description: No description provided.
|
||||
---
|
||||
|
||||
@@ -14,10 +13,13 @@ description: No description provided.
|
||||
|
||||
| Property | Type | Required | Notes |
|
||||
| :----------------------------------------------------------------------------------------------------- | :----------------------------- | :------: | :--------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>functionsUrl</span> | <code>string</code> | | |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>storageUrl</span> | <code>string</code> | | |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>graphqlUrl</span> | <code>string</code> | | |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>authUrl</span> | <code>string</code> | | |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>adminSecret</span> | <code>string</code> | | When set, the admin secret is sent as a header, `x-hasura-admin-secret`, for all requests to GraphQL, Storage, and Serverless Functions. |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>region</span> | <code>string</code> | | Project region (e.g. `eu-central-1`) Project region is not required during local development (when `subdomain` is `localhost`) |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>subdomain</span> | <code>string</code> | | Project subdomain (e.g. `ieingiwnginwnfnegqwvdqwdwq`) Use `localhost` during local development |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>backendUrl</span> | <code>string</code> | | Nhost backend URL Will be deprecated in a future release. Please look at 'subdomain' and 'region' instead. |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>devTools</span> | <code>boolean</code> | | Activate devTools e.g. the ability to connect to the xstate inspector |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>autoSignIn</span> | <code>boolean</code> | | When set to true, will parse the url on startup to check if it contains a refresh token to start the session with |
|
||||
| <span className="parameter-name"><span className="light-grey">params.</span>autoRefreshToken</span> | <code>boolean</code> | | When set to true, will automatically refresh token before it expires |
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: AddSecuritKeyComposableResult
|
||||
sidebarTitle: AddSecuritKeyComposableResult
|
||||
description: No description provided.
|
||||
---
|
||||
|
||||
# `AddSecuritKeyComposableResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> <code>Ref<null | ErrorPayload<any>></code>
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isLoading</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` when the action is executing, `false` when it finished its execution.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">add</span>** <span className="optional-status">required</span> <code>AddSecurityKeyHandler</code>
|
||||
|
||||
Add a security key to the current user with the WebAuthn API
|
||||
|
||||
---
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: BackendUrl
|
||||
sidebarTitle: BackendUrl
|
||||
description: No description provided.
|
||||
---
|
||||
|
||||
# `BackendUrl`
|
||||
|
||||
```ts
|
||||
type BackendUrl = () => { backendUrl: string }
|
||||
```
|
||||
@@ -10,6 +10,22 @@ description: No description provided.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">functionsUrl</span>** <span className="optional-status">optional</span> <code>string</code>
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">storageUrl</span>** <span className="optional-status">optional</span> <code>string</code>
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">graphqlUrl</span>** <span className="optional-status">optional</span> <code>string</code>
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">authUrl</span>** <span className="optional-status">optional</span> <code>string</code>
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">adminSecret</span>** <span className="optional-status">optional</span> <code>string</code>
|
||||
|
||||
When set, the admin secret is sent as a header, `x-hasura-admin-secret`,
|
||||
@@ -31,13 +47,6 @@ Use `localhost` during local development
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">backendUrl</span>** <span className="optional-status">optional</span> <code>string</code>
|
||||
|
||||
Nhost backend URL
|
||||
Will be deprecated in a future release. Please look at 'subdomain' and 'region' instead.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">devTools</span>** <span className="optional-status">optional</span> <code>boolean</code>
|
||||
|
||||
Activate devTools e.g. the ability to connect to the xstate inspector
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: SignInSecurityKeyPasswordlessHookResult
|
||||
sidebarTitle: SignInSecurityKeyPasswordlessHookResult
|
||||
description: No description provided.
|
||||
---
|
||||
|
||||
# `SignInSecurityKeyPasswordlessHookResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> <code>Ref<null | AuthErrorPayload></code>
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isLoading</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` when the action is executing, `false` when it finished its execution.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">user</span>** <span className="optional-status">required</span> <code>Ref<null | User></code>
|
||||
|
||||
User information
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">accessToken</span>** <span className="optional-status">required</span> <code>Ref<null | string></code>
|
||||
|
||||
Access token (JWT)
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">refreshToken</span>** <span className="optional-status">required</span> <code>Ref<null | string></code>
|
||||
|
||||
Access token (JWT)
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">needsEmailVerification</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an email is required to complete the action, and that a verification email has been sent to complete the action.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">signInEmailSecurityKey</span>** <span className="optional-status">required</span> <code>SignInSecurityKeyPasswordlessHandler</code>
|
||||
|
||||
---
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: SignUpSecurityKeyHookResult
|
||||
sidebarTitle: SignUpSecurityKeyHookResult
|
||||
description: No description provided.
|
||||
---
|
||||
|
||||
# `SignUpSecurityKeyHookResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> <code>Ref<null | AuthErrorPayload></code>
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isLoading</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` when the action is executing, `false` when it finished its execution.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">user</span>** <span className="optional-status">required</span> <code>Ref<null | User></code>
|
||||
|
||||
User information
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">accessToken</span>** <span className="optional-status">required</span> <code>Ref<null | string></code>
|
||||
|
||||
Access token (JWT)
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">refreshToken</span>** <span className="optional-status">required</span> <code>Ref<null | string></code>
|
||||
|
||||
Access token (JWT)
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">needsEmailVerification</span>** <span className="optional-status">required</span> <code>Ref<boolean></code>
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an email is required to complete the action, and that a verification email has been sent to complete the action.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">signUpEmailSecurityKey</span>** <span className="optional-status">required</span> <code>SignUpSecurityKeyHandler</code>
|
||||
|
||||
Used for a new user to sign up with a security key. Returns a promise with the current context
|
||||
|
||||
---
|
||||
16
docs/reference/vue/use-add-security-key.mdx
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: useAddSecurityKey()
|
||||
sidebarTitle: useAddSecurityKey()
|
||||
---
|
||||
|
||||
Use the composable `useAddSecurityKey` to add a WebAuthn security key.
|
||||
|
||||
```tsx
|
||||
const { add, isLoading, isSuccess, isError, error } = useAddSecurityKey()
|
||||
|
||||
const handleFormSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
await add('key nickname')
|
||||
}
|
||||
```
|
||||
16
docs/reference/vue/use-elevate-security-key-email.mdx
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: useElevateSecurityKeyEmail()
|
||||
sidebarTitle: useElevateSecurityKeyEmail()
|
||||
---
|
||||
|
||||
Use the composable `useElevateSecurityKeyEmail` to elevate the user auth permission in order to perform sensitive operations
|
||||
|
||||
```ts
|
||||
const { elevateEmailSecurityKey, elevated } = useElevateSecurityKeyEmail()
|
||||
|
||||
watchEffect(() => {
|
||||
console.log(elevated)
|
||||
})
|
||||
|
||||
await elevateEmailSecurityKey('joe@example.com')
|
||||
```
|
||||
25
docs/reference/vue/use-sign-in-email-security-key.mdx
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: useSignInEmailSecurityKey()
|
||||
sidebarTitle: useSignInEmailSecurityKey()
|
||||
---
|
||||
|
||||
Use the composable `useSignInEmailSecurityKey` to sign in a user using their email and a security key using the WebAuthn API.
|
||||
|
||||
```tsx
|
||||
const {
|
||||
signInEmailSecurityKey,
|
||||
needsEmailVerification,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignInEmailSecurityKey()
|
||||
|
||||
console.log({ needsEmailVerification, isLoading, isSuccess, isError, error })
|
||||
|
||||
const handleFormSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
await signInEmailSecurityKey('joe@example.com')
|
||||
}
|
||||
```
|
||||
33
docs/reference/vue/use-sign-up-email-security-key.mdx
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: useSignUpEmailSecurityKey()
|
||||
sidebarTitle: useSignUpEmailSecurityKey()
|
||||
---
|
||||
|
||||
Use the composable `useSignUpEmailSecurityKey` to sign up a user with security key and an email using the WebAuthn API.
|
||||
|
||||
```ts
|
||||
const {
|
||||
signUpEmailSecurityKey,
|
||||
needsEmailVerification,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignUpEmailSecurityKey()
|
||||
|
||||
console.log({ needsEmailVerification, isLoading, isSuccess, isError, error })
|
||||
|
||||
const handleFormSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
await signUpEmailSecurityKey('joe@example.com')
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">options</span>** <span className="optional-status">optional</span> <code>SignUpSecurityKeyOptions</code>
|
||||
|
||||
---
|
||||
@@ -1,5 +1,23 @@
|
||||
# @nhost-examples/cli
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.0.6
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.0.5
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.0.4
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/cli",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.7",
|
||||
"main": "src/index.mjs",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @nhost-examples/codegen-react-apollo
|
||||
|
||||
## 0.1.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react-apollo@9.0.1
|
||||
- @nhost/react@3.2.1
|
||||
|
||||
## 0.1.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [017f1a6]
|
||||
- @nhost/react@3.2.0
|
||||
- @nhost/react-apollo@9.0.0
|
||||
|
||||
## 0.1.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.1.1
|
||||
- @nhost/react-apollo@8.0.1
|
||||
|
||||
## 0.1.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-apollo",
|
||||
"version": "0.1.12",
|
||||
"version": "0.1.15",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"codegen": "graphql-codegen",
|
||||
@@ -15,7 +15,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.9.1",
|
||||
"@apollo/client": "^3.9.4",
|
||||
"@nhost/react": "workspace:^",
|
||||
"@nhost/react-apollo": "workspace:^",
|
||||
"clsx": "^1.2.1",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# @nhost-examples/codegen-react-query
|
||||
|
||||
## 0.1.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.2.1
|
||||
|
||||
## 0.1.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [017f1a6]
|
||||
- @nhost/react@3.2.0
|
||||
|
||||
## 0.1.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.1.1
|
||||
|
||||
## 0.1.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-query",
|
||||
"version": "0.1.13",
|
||||
"version": "0.1.16",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"codegen": "graphql-codegen",
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @nhost-examples/react-urql
|
||||
|
||||
## 0.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.2.1
|
||||
- @nhost/react-urql@6.0.1
|
||||
|
||||
## 0.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [017f1a6]
|
||||
- @nhost/react@3.2.0
|
||||
- @nhost/react-urql@6.0.0
|
||||
|
||||
## 0.0.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.1.1
|
||||
- @nhost/react-urql@5.0.1
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-urql",
|
||||
"private": true,
|
||||
"version": "0.0.9",
|
||||
"version": "0.0.12",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# Change all these variables before first creating the containers
|
||||
POSTGRES_PASSWORD=secret-pg-password-never-use-this-value
|
||||
HASURA_GRAPHQL_ADMIN_SECRET=nhost-admin-secret
|
||||
HASURA_GRAPHQL_ADMIN_SECRET=change-me
|
||||
# The jwt secret key can be generated with `openssl rand -hex 32`
|
||||
HASURA_GRAPHQL_JWT_SECRET='{"type":"HS256", "key":"5152fa850c02dc222631cca898ed1485821a70912a6e3649c49076912daa3b62182ba013315915d64f40cddfbb8b58eb5bd11ba225336a6af45bbae07ca873f3","issuer":"hasura-auth"}'
|
||||
STORAGE_ACCESS_KEY=storage-access-key-never-use-this-value
|
||||
STORAGE_SECRET_KEY=storage-secret-key-never-use-this-value
|
||||
|
||||
# The following HOST and URL env variables are separated since the HOST variables are used to define the allowed hosts to the traefik services in the docker-compose.yaml
|
||||
|
||||
# Public (proxy.my-nhost.com) or private (localhost) hostname for the Hasura API/proxy for the Nhost dashboard to send requests to
|
||||
PROXY_HOST=localhost
|
||||
# Change to https://${PROXY_HOST} if not using localhost
|
||||
PROXY_URL=http://${PROXY_HOST}:1337
|
||||
|
||||
# Public (example-url.my-nhost.com) or private (localhost) hostname for the Nhost dashboard for the proxy to allow requests from
|
||||
NHOST_HOST=localhost
|
||||
|
||||
# Environment variables for the Nhost Dashboard. See dashboard/.env.example for default values. The defualt values are changed to work with our traefik setup created with the docker-compose
|
||||
|
||||
# URL for the migrations API running from running `hasura-console`. If it needs to be publicly accessible, change it to your publicly-available URL (https://hasura-migrations.my-nhost.com)
|
||||
NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL=http://localhost:9693
|
||||
|
||||
# The following do not need to be changed unless you modified the docker-compose.yaml
|
||||
NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL=${PROXY_URL}
|
||||
NEXT_PUBLIC_NHOST_HASURA_API_URL=${PROXY_URL}
|
||||
NEXT_PUBLIC_NHOST_ADMIN_SECRET=${HASURA_GRAPHQL_ADMIN_SECRET}
|
||||
NEXT_PUBLIC_NHOST_AUTH_URL=${PROXY_URL}/v1/auth
|
||||
NEXT_PUBLIC_NHOST_GRAPHQL_URL=${PROXY_URL}/v1/graphql
|
||||
NEXT_PUBLIC_NHOST_STORAGE_URL=${PROXY_URL}/v1/storage
|
||||
NEXT_PUBLIC_NHOST_FUNCTIONS_URL=${PROXY_URL}/v1/functions
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @nhost-examples/docker-compose
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- aff059e: fix: timers
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- ed9df85: updated docker-compose.yaml and .env-example
|
||||
|
||||
## 0.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -8,9 +8,14 @@ services:
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.web.address=:1337"
|
||||
- "--entryPoints.admin.address=:3030"
|
||||
ports:
|
||||
# hasura/services
|
||||
- "1337:1337"
|
||||
# traefik interface
|
||||
- "9090:8080"
|
||||
# dashboard
|
||||
- "3030:3030"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
postgres:
|
||||
@@ -24,7 +29,7 @@ services:
|
||||
ports:
|
||||
- '5432:5432'
|
||||
graphql-engine:
|
||||
image: hasura/graphql-engine:v2.15.2
|
||||
image: hasura/graphql-engine:v2.35.1
|
||||
depends_on:
|
||||
- 'postgres'
|
||||
restart: always
|
||||
@@ -37,12 +42,19 @@ 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(`localhost`) && PathPrefix(`/`)"
|
||||
- "traefik.http.routers.hasura.rule=Host(`${PROXY_HOST}`, `localhost`) && PathPrefix(`/`)"
|
||||
- "traefik.http.routers.hasura.entrypoints=web"
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.16.2
|
||||
image: nhost/hasura-auth:0.24
|
||||
depends_on:
|
||||
- postgres
|
||||
- graphql-engine
|
||||
@@ -61,16 +73,16 @@ services:
|
||||
AUTH_SMTP_USER: user
|
||||
AUTH_SMTP_PASS: password
|
||||
AUTH_SMTP_SENDER: mail@example.com
|
||||
expose:
|
||||
expose:
|
||||
- 4000
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.middlewares.strip-auth.stripprefix.prefixes=/v1/auth"
|
||||
- "traefik.http.routers.auth.rule=Host(`localhost`) && PathPrefix(`/v1/auth`)"
|
||||
- "traefik.http.routers.auth.rule=Host(`${PROXY_HOST}`, `localhost`) && PathPrefix(`/v1/auth`)"
|
||||
- "traefik.http.routers.auth.middlewares=strip-auth@docker"
|
||||
- "traefik.http.routers.auth.entrypoints=web"
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.3.1
|
||||
image: nhost/hasura-storage:0.4.1
|
||||
depends_on:
|
||||
- postgres
|
||||
- graphql-engine
|
||||
@@ -79,7 +91,7 @@ services:
|
||||
expose:
|
||||
- 8000
|
||||
environment:
|
||||
PUBLIC_URL: http://localhost:${PROXY_PORT:-1337}
|
||||
PUBLIC_URL: ${PROXY_URL}
|
||||
HASURA_METADATA: 1
|
||||
HASURA_ENDPOINT: http://graphql-engine:8080/v1
|
||||
HASURA_GRAPHQL_ADMIN_SECRET: ${HASURA_GRAPHQL_ADMIN_SECRET}
|
||||
@@ -91,7 +103,7 @@ services:
|
||||
POSTGRES_MIGRATIONS_SOURCE: postgres://postgres:${POSTGRES_PASSWORD:-secretpgpassword}@postgres:5432/postgres?sslmode=disable
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.storage.rule=Host(`localhost`) && PathPrefix(`/v1/storage`)"
|
||||
- "traefik.http.routers.storage.rule=Host(`${PROXY_HOST}`, `localhost`) && PathPrefix(`/v1/storage`)"
|
||||
- "traefik.http.routers.storage.entrypoints=web"
|
||||
# Rewrite the path so it matches with the new storage API path introduced in hasura-storage 0.2
|
||||
- "traefik.http.middlewares.strip-suffix.replacepathregex.regex=^/v1/storage/(.*)"
|
||||
@@ -99,15 +111,15 @@ services:
|
||||
- "traefik.http.routers.storage.middlewares=strip-suffix@docker"
|
||||
command: serve
|
||||
functions:
|
||||
image: nhost/functions:0.1.8
|
||||
image: nhost/functions:1.0.0
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.middlewares.strip-functions.stripprefix.prefixes=/v1/functions"
|
||||
- "traefik.http.routers.functions.rule=Host(`localhost`) && PathPrefix(`/v1/functions`)"
|
||||
- "traefik.http.routers.functions.rule=Host(`${PROXY_HOST}`, `localhost`) && PathPrefix(`/v1/functions`)"
|
||||
- "traefik.http.routers.functions.middlewares=strip-functions@docker"
|
||||
- "traefik.http.routers.functions.entrypoints=web"
|
||||
restart: always
|
||||
expose:
|
||||
expose:
|
||||
- 3000
|
||||
volumes:
|
||||
- .:/opt/project
|
||||
@@ -135,13 +147,29 @@ 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
|
||||
dashboard:
|
||||
image: nhost/dashboard:0.7.4
|
||||
ports:
|
||||
- "3030:3000"
|
||||
image: nhost/dashboard:0.21.1
|
||||
environment:
|
||||
NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL: ${NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL}
|
||||
NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL: ${NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL}
|
||||
NEXT_PUBLIC_NHOST_HASURA_API_URL: ${NEXT_PUBLIC_NHOST_HASURA_API_URL}
|
||||
NEXT_PUBLIC_NHOST_ADMIN_SECRET: ${NEXT_PUBLIC_NHOST_ADMIN_SECRET}
|
||||
NEXT_PUBLIC_NHOST_AUTH_URL: ${NEXT_PUBLIC_NHOST_AUTH_URL}
|
||||
NEXT_PUBLIC_NHOST_GRAPHQL_URL: ${NEXT_PUBLIC_NHOST_GRAPHQL_URL}
|
||||
NEXT_PUBLIC_NHOST_STORAGE_URL: ${NEXT_PUBLIC_NHOST_STORAGE_URL}
|
||||
NEXT_PUBLIC_NHOST_FUNCTIONS_URL: ${NEXT_PUBLIC_NHOST_FUNCTIONS_URL}
|
||||
expose:
|
||||
- 3000
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.nhost.rule=Host(`${NHOST_HOST}`)"
|
||||
- "traefik.http.routers.nhost.entrypoints=admin"
|
||||
# If you would like to protect your dashboard with a username and password if it is publicly-facing, uncomment and fill in the following lines below according to the documentation at https://doc.traefik.io/traefik/middlewares/http/basicauth/
|
||||
#- "traefik.http.routers.nhost.middlewares=auth"
|
||||
#- "traefik.http.middlewares.auth.basicauth.users=
|
||||
volumes:
|
||||
functions_node_modules:
|
||||
|
||||
5
examples/docker-compose/functions/pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/docker-compose",
|
||||
"version": "0.0.7",
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"e2e": "vitest run"
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @nhost-examples/multi-tenant-one-to-many
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.0.6
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.0.5
|
||||
|
||||
## 2.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.0.4
|
||||
|
||||
## 2.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/multi-tenant-one-to-many",
|
||||
"private": true,
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.5",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {},
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# @nhost-examples/nextjs
|
||||
|
||||
## 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
|
||||
|
||||
- Updated dependencies [017f1a6]
|
||||
- @nhost/react@3.2.0
|
||||
- @nhost/react-apollo@9.0.0
|
||||
- @nhost/nextjs@2.1.2
|
||||
|
||||
## 0.1.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.1.1
|
||||
- @nhost/react-apollo@8.0.1
|
||||
- @nhost/nextjs@2.1.1
|
||||
|
||||
## 0.1.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||