Compare commits

...

15 Commits

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


# Releases
## @nhost/graphql-js@0.2.0

### Minor Changes

- 304065a: feat: add `setHeaders` method enabling global configuration
of storage, graphql, and functions client headers, alongside added
support for passing specific headers with individual calls

## @nhost/hasura-storage-js@2.5.0

### Minor Changes

- 304065a: feat: add `setHeaders` method enabling global configuration
of storage, graphql, and functions client headers, alongside added
support for passing specific headers with individual calls

## @nhost/nhost-js@3.1.0

### Minor Changes

- 304065a: feat: add `setHeaders` method enabling global configuration
of storage, graphql, and functions client headers, alongside added
support for passing specific headers with individual calls

### Patch Changes

-   Updated dependencies [68e0622]
-   Updated dependencies [304065a]
    -   @nhost/hasura-auth-js@2.4.2
    -   @nhost/hasura-storage-js@2.5.0
    -   @nhost/graphql-js@0.2.0

## @nhost/apollo@7.0.0

### Patch Changes

-   Updated dependencies [304065a]
    -   @nhost/nhost-js@3.1.0

## @nhost/react-apollo@11.0.2

### Patch Changes

-   @nhost/apollo@7.0.0
-   @nhost/react@3.4.2

## @nhost/react-urql@8.0.2

### Patch Changes

-   @nhost/react@3.4.2

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

### Patch Changes

- 68e0622: fix: resolved infinite loop occurring with requests to /token
when a user logs out in one tab while other tabs are open

## @nhost/nextjs@2.1.11

### Patch Changes

-   @nhost/react@3.4.2

## @nhost/react@3.4.2

### Patch Changes

-   Updated dependencies [304065a]
    -   @nhost/nhost-js@3.1.0

## @nhost/vue@2.5.2

### Patch Changes

-   Updated dependencies [304065a]
    -   @nhost/nhost-js@3.1.0

## @nhost/dashboard@1.15.0

### Minor Changes

-   a7bde37: feat: send metadata in the edit form

### Patch Changes

- 1bc615b: feat: improve error message handling in `ErrorToast`
component
    -   @nhost/react-apollo@11.0.2
    -   @nhost/nextjs@2.1.11

## @nhost/docs@2.10.3

### Patch Changes

-   a58c5cf: fix: broken link

## @nhost-examples/cli@0.3.2

### Patch Changes

-   Updated dependencies [304065a]
    -   @nhost/nhost-js@3.1.0

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

### Patch Changes

-   @nhost/react@3.4.2
-   @nhost/react-apollo@11.0.2

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

### Patch Changes

-   @nhost/react@3.4.2

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

### Patch Changes

-   @nhost/react@3.4.2
-   @nhost/react-urql@8.0.2

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

### Patch Changes

-   Updated dependencies [304065a]
    -   @nhost/nhost-js@3.1.0

## @nhost-examples/nextjs@0.3.2

### Patch Changes

-   @nhost/react@3.4.2
-   @nhost/react-apollo@11.0.2
-   @nhost/nextjs@2.1.11

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

### Patch Changes

-   Updated dependencies [304065a]
    -   @nhost/nhost-js@3.1.0

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

### Patch Changes

-   Updated dependencies [304065a]
    -   @nhost/nhost-js@3.1.0

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

### Patch Changes

-   @nhost/react@3.4.2
-   @nhost/react-apollo@11.0.2

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

### Patch Changes

-   @nhost/react@3.4.2

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

### Patch Changes

-   Updated dependencies [304065a]
    -   @nhost/nhost-js@3.1.0
    -   @nhost/apollo@7.0.0
    -   @nhost/vue@2.5.2

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

### Patch Changes

-   @nhost/apollo@7.0.0
-   @nhost/vue@2.5.2

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-13 13:52:42 +01:00
Hassan Ben Jobrane
304065ae22 feat: sdk: add setHeaders for setting global headers and make sure all client calls have a headers arg (#2697)
resolves https://github.com/nhost/nhost/issues/2696
2024-05-13 13:38:28 +01:00
Hassan Ben Jobrane
68e0622eb0 fix: broadcast signout event to all tabs (#2686)
fixes https://github.com/nhost/nhost/issues/2635
2024-05-09 11:20:39 +01:00
Hassan Ben Jobrane
70c6834636 chore: add e2e test project (#2688) 2024-05-08 12:52:11 +01:00
Johanna Blom
a7bde37bba feat (dashboard): send metadata in the edit form (#2689)
resolves #2684

---------

Co-authored-by: Hassan Ben Jobrane <hsanbenjobrane@gmail.com>
2024-05-08 13:36:03 +02:00
Hassan Ben Jobrane
1bc615beca fix(dashboard): improve error toast message handling (#2692) 2024-05-08 12:35:21 +01:00
David Barroso
a58c5cfc96 fix (docs): broken link (#2690) 2024-05-08 12:42:58 +02:00
Daniel Olofsson
c61228e45d Fixed typo in README.md (#2687) 2024-05-07 14:39:31 +02:00
github-actions[bot]
6cec04bd6f chore: update versions (#2680)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/dashboard@1.14.0

### Minor Changes

-   a448d7d: feat: allow configuring postmark and delete SMTP settings

## @nhost/docs@2.10.2

### Patch Changes

-   9480489: fix: update docs performance info

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-25 13:13:32 +01:00
David Barroso
a448d7d182 feat (dashboard): allow configuring postmark and deleting SMTP settings (#2678)
Co-authored-by: Hassan Ben Jobrane <hsanbenjobrane@gmail.com>
2024-04-25 12:58:47 +01:00
David Barroso
948048940e fix (docs): update docs performance info (#2679)
Co-authored-by: Nuno Pato <nunopato@gmail.com>
2024-04-25 13:50:45 +02:00
github-actions[bot]
5e91221d5a chore: update versions (#2672)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @nhost/dashboard@1.13.3

### Patch Changes

-   5924bc3: fix: include password in `GetSmtpSettings` query
- c5ad634: fix: resolved an issue where one-click install links were
broken on Safari
- 7278991: fix: update graphql auto-embeddings configuration to use
String type for model field

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-18 15:49:01 +01:00
Hassan Ben Jobrane
7278991a59 fix: dashboard: update graphql auto-embeddings configuration to use String type for model field (#2674) 2024-04-18 15:22:32 +01:00
Hassan Ben Jobrane
5924bc3248 fix: dashboard: include password in GetSmtpSettings query (#2673) 2024-04-18 15:14:02 +01:00
Hassan Ben Jobrane
c5ad634799 fix: dashboard: check for undefined router query on Safari when accessing base64config (#2671) 2024-04-18 10:04:18 +01:00
126 changed files with 1834 additions and 689 deletions

View File

@@ -1,5 +1,31 @@
# @nhost/dashboard
## 1.15.0
### Minor Changes
- a7bde37: feat: send metadata in the edit form
### Patch Changes
- 1bc615b: feat: improve error message handling in `ErrorToast` component
- @nhost/react-apollo@11.0.2
- @nhost/nextjs@2.1.11
## 1.14.0
### Minor Changes
- a448d7d: feat: allow configuring postmark and delete SMTP settings
## 1.13.3
### Patch Changes
- 5924bc3: fix: include password in `GetSmtpSettings` query
- c5ad634: fix: resolved an issue where one-click install links were broken on Safari
- 7278991: fix: update graphql auto-embeddings configuration to use String type for model field
## 1.13.2
### Patch Changes

View File

@@ -0,0 +1,2 @@
.secrets
.nhost

View File

@@ -0,0 +1 @@
version: 3

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Потвърдете смяната на вашия имейл</h2>
<p>Използвайте посочения линк, за да повърдите смяната на имейл:</p>
<p>
<a href="${link}">
Смени имейл
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Потвърждение за смяна на имейл

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Потвърдете вашия имейл</h2>
<p>Използвайте посочения линк, за да потвърдите вашия имейл:</p>
<p>
<a href="${link}">
Потвърдете имейл
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Потвърждаване на имейл

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Смяна на парола</h2>
<p>Използвайте посочения линк, за да смените вашата парола:</p>
<p>
<a href="${link}">
Смяна на парола
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Смяна на парола

View File

@@ -0,0 +1 @@
Вашият код е ${code}.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Магически линк за вход</h2>
<p>Използвайте посочения линк за защитен и бърз вход:</p>
<p>
<a href="${link}">
Вход
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Магически линк за вход

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Potvrzení změny emailové adresy</h2>
<p>Použijte tento odkaz k potvrzení změny emailové adresy:</p>
<p>
<a href="${link}">
Změnit email
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Změna vaší emailové adresy

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Ověření emailové adresy</h2>
<p>Použijte tento odkaz k ověření vaší emailové adresy:</p>
<p>
<a href="${link}">
Ověřit emailovou adresu
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Ověření vaší emailové adresy

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Obnova hesla</h2>
<p>Použijte tento odkaz k obnovení vašeho hesla:</p>
<p>
<a href="${link}">
Obnova hesla
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Obnova hesla

View File

@@ -0,0 +1 @@
Váš kód je ${code}.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Magický odkaz</h2>
<p>Použijte tento odkaz k bezpečnému přihlášení:</p>
<p>
<a href="${link}">
Přihlášení
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Bezpečný odkaz k přihlášení

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Confirm Email Change</h2>
<p>Use this link to confirm changing email:</p>
<p>
<a href="${link}">
Change email
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Change your email address

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Verify Email</h2>
<p>Use this link to verify your email:</p>
<p>
<a href="${link}">
Verify Email
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Verify your email

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Reset Password</h2>
<p>Use this link to reset your password:</p>
<p>
<a href="${link}">
Reset password
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Reset your password

View File

@@ -0,0 +1 @@
Your code is ${code}.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Magic Link</h2>
<p>Use this link to securely sign in:</p>
<p>
<a href="${link}">
Sign In
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Secure sign-in link

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Confirmar cambio de correo electrónico</h2>
<p>Utiliza el siguiente enlace para confirmar el cambio de correo:</p>
<p>
<a href="${link}">
Cambiar correo electrónico
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Cambiar dirección de correo electrónico

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Verificar correo electrónico</h2>
<p>Utilza el siguiente enlace para verificar tu correo:</p>
<p>
<a href="${link}">
Verificar correo electrónico
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Verifica tu correo electrónico

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Recuperar contraseña</h2>
<p>Utiliza el siguiente enlace para recuperar tu contraseña:</p>
<p>
<a href="${link}">
Recuperar contraseña
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Recuperar contraseña

View File

@@ -0,0 +1 @@
Tu código es ${code}.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Enlace mágico</h2>
<p>Utiliza este enlace para iniciar sesión de forma segura:</p>
<p>
<a href="${link}">
Iniciar sesión
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Enlace de acceso seguro

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Confirmer changement de courriel</h2>
<p>Utilisez ce lien pour confirmer le changement de courriel :</p>
<p>
<a href="${link}">
Changer courriel
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Changez votre adresse courriel

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>V&eacute;rifiez votre courriel</h2>
<p>Utilisez ce lien pour v&eacute;rifier votre courriel :</p>
<p>
<a href="${link}">
V&eacute;rifier courriel
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Vérifier votre courriel

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>R&eacute;initialiser votre mot de passe</h2>
<p>Utilisez ce lien pour r&eacute;initialiser votre mot de passe :</p>
<p>
<a href="${link}">
R&eacute;initialiser mot de passe
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Réinitialiser votre mot de passe

View File

@@ -0,0 +1 @@
Votre code est ${code}.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h2>Lien magique</h2>
<p>Utilisez ce lien pour vous connecter de fa&ccedil;on s&eacute;curisée :</p>
<p>
<a href="${link}">
Connexion
</a>
</p>
</body>
</html>

View File

@@ -0,0 +1 @@
Lien de connexion sécurisé

View File

@@ -0,0 +1,8 @@
${link},
${displayName},
${email},
${ticket},
${redirectTo},
${serverUrl},
${clientUrl},
${locale},

View File

@@ -0,0 +1 @@
${link}, ${displayName}, ${email}, ${ticket}, ${redirectTo}, ${serverUrl}, ${clientUrl}, ${locale}

View File

@@ -0,0 +1,151 @@
[global]
[hasura]
version = 'v2.33.4-ce'
adminSecret = '{{ secrets.HASURA_GRAPHQL_ADMIN_SECRET }}'
webhookSecret = '{{ secrets.NHOST_WEBHOOK_SECRET }}'
[[hasura.jwtSecrets]]
type = 'HS256'
key = '{{ secrets.HASURA_GRAPHQL_JWT_SECRET }}'
[hasura.settings]
corsDomain = ['*']
devMode = true
enableAllowList = false
enableConsole = true
enableRemoteSchemaPermissions = false
enabledAPIs = ['metadata', 'graphql', 'pgdump', 'config']
liveQueriesMultiplexedRefetchInterval = 1000
stringifyNumericTypes = false
[hasura.logs]
level = 'warn'
[hasura.events]
httpPoolSize = 100
[functions]
[functions.node]
version = 18
[auth]
version = '0.24.1'
[auth.elevatedPrivileges]
mode = 'disabled'
[auth.redirections]
clientUrl = 'http://localhost:3000'
[auth.signUp]
enabled = true
disableNewUsers = false
[auth.user]
[auth.user.roles]
default = 'user'
allowed = ['user', 'me']
[auth.user.locale]
default = 'en'
allowed = ['en']
[auth.user.gravatar]
enabled = true
default = 'blank'
rating = 'g'
[auth.user.email]
[auth.user.emailDomains]
[auth.session]
[auth.session.accessToken]
expiresIn = 900
[auth.session.refreshToken]
expiresIn = 2592000
[auth.method]
[auth.method.anonymous]
enabled = false
[auth.method.emailPasswordless]
enabled = false
[auth.method.emailPassword]
hibpEnabled = false
emailVerificationRequired = true
passwordMinLength = 9
[auth.method.smsPasswordless]
enabled = false
[auth.method.oauth]
[auth.method.oauth.apple]
enabled = false
[auth.method.oauth.azuread]
tenant = 'common'
enabled = false
[auth.method.oauth.bitbucket]
enabled = false
[auth.method.oauth.discord]
enabled = false
[auth.method.oauth.facebook]
enabled = false
[auth.method.oauth.github]
enabled = false
[auth.method.oauth.gitlab]
enabled = false
[auth.method.oauth.google]
enabled = false
[auth.method.oauth.linkedin]
enabled = false
[auth.method.oauth.spotify]
enabled = false
[auth.method.oauth.strava]
enabled = false
[auth.method.oauth.twitch]
enabled = false
[auth.method.oauth.twitter]
enabled = false
[auth.method.oauth.windowslive]
enabled = false
[auth.method.oauth.workos]
enabled = false
[auth.method.webauthn]
enabled = false
[auth.method.webauthn.attestation]
timeout = 60000
[auth.totp]
enabled = false
[postgres]
version = '14.6-20240129-1'
[provider]
[storage]
version = '0.6.0'
[observability]
[observability.grafana]
adminPassword = '{{ secrets.GRAFANA_ADMIN_PASSWORD }}'

View File

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

View File

@@ -29,10 +29,10 @@ const getInternalErrorMessage = (
if (error.name === 'ApolloError') {
// @ts-ignore
const internalError = error.graphQLErrors?.[0]?.extensions?.internal as {
error: { message: string };
};
return internalError?.error?.message || null;
const graphqlError = error.graphQLErrors?.[0];
const graphqlExtensionsError = graphqlError?.extensions?.internal
?.error as { message: string };
return graphqlError.message || graphqlExtensionsError?.message || null;
}
if (error instanceof Error) {

View File

@@ -0,0 +1,143 @@
import { ApplyLocalSettingsDialog } from '@/components/common/ApplyLocalSettingsDialog';
import { useDialog } from '@/components/common/DialogProvider';
import { useUI } from '@/components/common/UIProvider';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { Text } from '@/components/ui/v2/Text';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
import {
GetSmtpSettingsDocument,
useUpdateConfigMutation,
} from '@/utils/__generated__/graphql';
import { useState } from 'react';
import { twMerge } from 'tailwind-merge';
function ConfirmDeleteSMTPSettingsModal({
close,
onDelete,
}: {
onDelete?: () => Promise<any>;
close: () => void;
}) {
const onClickDelete = async () => {
await onDelete();
close();
};
return (
<Box className={twMerge('w-full rounded-lg p-6 text-left')}>
<div className="grid grid-flow-row gap-4">
<Text variant="h3" component="h2">
Delete SMTP Settings?
</Text>
<Text>This will reset all your SMTP and Postmark settings.</Text>
<div className="grid grid-flow-row gap-2">
<Button color="error" onClick={onClickDelete}>
Delete
</Button>
<Button variant="outlined" color="secondary" onClick={close}>
Cancel
</Button>
</div>
</div>
</Box>
);
}
export default function DeleteSMTPSettings() {
const { openDialog, closeDialog } = useDialog();
const isPlatform = useIsPlatform();
const localMimirClient = useLocalMimirClient();
const { maintenanceActive } = useUI();
const [loading, setLoading] = useState(false);
const { currentProject } = useCurrentWorkspaceAndProject();
const [updateConfig] = useUpdateConfigMutation({
refetchQueries: [GetSmtpSettingsDocument],
...(!isPlatform ? { client: localMimirClient } : {}),
});
const deleteSMTPSettings = async () => {
const updateConfigPromise = updateConfig({
variables: {
appId: currentProject.id,
config: {
provider: {
smtp: null,
},
},
},
});
setLoading(true);
await execPromiseWithErrorToast(
async () => {
await updateConfigPromise;
if (!isPlatform) {
openDialog({
title: 'Apply your changes',
component: <ApplyLocalSettingsDialog />,
props: {
PaperProps: {
className: 'max-w-2xl',
},
},
});
}
},
{
loadingMessage: 'SMTP settings are being deleted...',
successMessage: 'SMTP settings have been deleted successfully.',
errorMessage:
'An error occurred while trying to delete the SMTP settings.',
},
);
setLoading(false);
};
const confirmDeleteSMTPSettings = async () => {
openDialog({
component: (
<ConfirmDeleteSMTPSettingsModal
close={closeDialog}
onDelete={deleteSMTPSettings}
/>
),
});
};
return (
<SettingsContainer
title="Delete SMTP Settings"
description="Delete SMTP settings and revert to default values"
className="px-0"
slotProps={{
submitButton: { className: 'hidden' },
footer: { className: 'hidden' },
}}
>
<Box className="grid grid-flow-row border-t-1">
<Button
color="error"
className="mx-4 mt-4 justify-self-end"
onClick={confirmDeleteSMTPSettings}
disabled={loading || maintenanceActive}
loading={loading}
>
Delete
</Button>
</Box>
</SettingsContainer>
);
}

View File

@@ -0,0 +1 @@
export { default as DeleteSTMPSettings } from './DeleteSMTPSettings';

View File

@@ -0,0 +1,149 @@
import { ApplyLocalSettingsDialog } from '@/components/common/ApplyLocalSettingsDialog';
import { useDialog } from '@/components/common/DialogProvider';
import { useUI } from '@/components/common/UIProvider';
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { Input } from '@/components/ui/v2/Input';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
import {
GetSmtpSettingsDocument,
useGetSmtpSettingsQuery,
useUpdateConfigMutation,
} from '@/utils/__generated__/graphql';
import { yupResolver } from '@hookform/resolvers/yup';
import { FormProvider, useForm } from 'react-hook-form';
import * as yup from 'yup';
const validationSchema = yup
.object({
sender: yup.string().label('SMTP Sender').email().required(),
password: yup.string().label('Password').required(),
})
.required();
export type PostmarkFormValues = yup.InferType<typeof validationSchema>;
export default function PostmarkSettings() {
const { openDialog } = useDialog();
const isPlatform = useIsPlatform();
const { maintenanceActive } = useUI();
const localMimirClient = useLocalMimirClient();
const { currentProject } = useCurrentWorkspaceAndProject();
const { data } = useGetSmtpSettingsQuery({
variables: { appId: currentProject?.id },
...(!isPlatform ? { client: localMimirClient } : {}),
});
const { sender, password } = data?.config?.provider?.smtp || {};
const [updateConfig] = useUpdateConfigMutation({
refetchQueries: [GetSmtpSettingsDocument],
...(!isPlatform ? { client: localMimirClient } : {}),
});
const form = useForm<PostmarkFormValues>({
reValidateMode: 'onSubmit',
resolver: yupResolver(validationSchema),
defaultValues: {
password: '',
sender: '',
},
values: {
password: password || '',
sender: sender || '',
},
mode: 'onSubmit',
criteriaMode: 'all',
});
const {
register,
formState: { errors, isDirty, isSubmitting },
} = form;
const handleEditPostmarkSettings = async (values: PostmarkFormValues) => {
const updateConfigPromise = updateConfig({
variables: {
appId: currentProject.id,
config: {
provider: {
smtp: { method: 'LOGIN', host: 'postmark', ...values },
},
},
},
});
await execPromiseWithErrorToast(
async () => {
await updateConfigPromise;
if (!isPlatform) {
openDialog({
title: 'Apply your changes',
component: <ApplyLocalSettingsDialog />,
props: {
PaperProps: {
className: 'max-w-2xl',
},
},
});
}
},
{
loadingMessage: 'Postmark settings are being updated...',
successMessage: 'Postmark settings have been updated successfully.',
errorMessage:
'An error occurred while trying to update your Postmark settings.',
},
);
};
return (
<FormProvider {...form}>
<Form onSubmit={handleEditPostmarkSettings}>
<SettingsContainer
title="Postmark Settings"
description="Configure postmark's native integration to send emails from your email domain."
submitButtonText="Save"
className="grid grid-cols-9 gap-4"
slotProps={{
submitButton: {
disabled: !isDirty || maintenanceActive,
loading: isSubmitting,
},
}}
>
<Input
{...register('sender')}
id="sender"
name="sender"
label="From Email"
placeholder="noreply@nhost.app"
className="lg:col-span-4"
hideEmptyHelperText
fullWidth
error={Boolean(errors.sender)}
helperText={errors.sender?.message}
/>
<Input
{...register('password')}
id="password"
label="Password"
type="password"
placeholder="Enter password"
className="lg:col-span-5"
hideEmptyHelperText
fullWidth
error={Boolean(errors.password)}
helperText={errors.password?.message}
/>
</SettingsContainer>
</Form>
</FormProvider>
);
}

View File

@@ -0,0 +1,2 @@
export * from './PostmarkSettings';
export { default as PostmarkSettings } from './PostmarkSettings';

View File

@@ -0,0 +1,238 @@
import { ApplyLocalSettingsDialog } from '@/components/common/ApplyLocalSettingsDialog';
import { useDialog } from '@/components/common/DialogProvider';
import { useUI } from '@/components/common/UIProvider';
import { ControlledCheckbox } from '@/components/form/ControlledCheckbox';
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { Input } from '@/components/ui/v2/Input';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
import {
GetSmtpSettingsDocument,
useGetSmtpSettingsQuery,
useUpdateConfigMutation,
} from '@/utils/__generated__/graphql';
import { yupResolver } from '@hookform/resolvers/yup';
import { FormProvider, useForm } from 'react-hook-form';
import { type Optional } from 'utility-types';
import * as yup from 'yup';
const smtpValidationSchema = yup
.object({
secure: yup.bool().label('SMTP Secure'),
host: yup
.string()
.label('SMTP Host')
.matches(
/((https?):\/\/)?(www\.)?[a-z0-9]+(\.[a-z]{2,}){1,3}(#[a-zA-Z0-9#]+?)*\/?(\?[a-zA-Z0-9-_]+=[a-zA-Z0-9-%]+&?)?$/,
'SMTP Host must be a valid URL',
)
.required(),
port: yup
.number()
.typeError('The SMTP port should contain only numbers.')
.required(),
user: yup.string().label('Username').required(),
password: yup.string().label('Password'),
method: yup.string().required(),
sender: yup.string().label('SMTP Sender').email().required(),
})
.required();
export type SmtpFormValues = yup.InferType<typeof smtpValidationSchema>;
export default function SMTPSettings() {
const { maintenanceActive } = useUI();
const { openDialog } = useDialog();
const isPlatform = useIsPlatform();
const localMimirClient = useLocalMimirClient();
const { currentProject } = useCurrentWorkspaceAndProject();
const { data } = useGetSmtpSettingsQuery({
variables: { appId: currentProject?.id },
...(!isPlatform ? { client: localMimirClient } : {}),
});
const { secure, host, port, user, method, sender, password } =
data?.config?.provider?.smtp || {};
const form = useForm<Optional<SmtpFormValues, 'password'>>({
reValidateMode: 'onSubmit',
resolver: yupResolver(smtpValidationSchema),
defaultValues: {
secure: false,
host: '',
port: undefined,
user: '',
password: '',
method: '',
sender: '',
},
values: {
secure: secure || false,
host: host || '',
port,
user: user || '',
password: password || '',
method: method || '',
sender: sender || '',
},
mode: 'onSubmit',
criteriaMode: 'all',
});
const {
register: registerSmtp,
formState: { errors, isDirty, isSubmitting },
} = form;
const [updateConfig] = useUpdateConfigMutation({
refetchQueries: [GetSmtpSettingsDocument],
...(!isPlatform ? { client: localMimirClient } : {}),
});
const handleEditSMTPSettings = async (values: SmtpFormValues) => {
const { password: newPassword, ...valuesWithoutPassword } = values;
const updateConfigPromise = updateConfig({
variables: {
appId: currentProject.id,
config: {
provider: {
smtp: newPassword ? values : valuesWithoutPassword,
},
},
},
});
await execPromiseWithErrorToast(
async () => {
await updateConfigPromise;
if (!isPlatform) {
openDialog({
title: 'Apply your changes',
component: <ApplyLocalSettingsDialog />,
props: {
PaperProps: {
className: 'max-w-2xl',
},
},
});
}
},
{
loadingMessage: 'SMTP settings are being updated...',
successMessage: 'SMTP settings have been updated successfully.',
errorMessage:
'An error occurred while trying to update the SMTP settings.',
},
);
};
return (
<FormProvider {...form}>
<Form onSubmit={handleEditSMTPSettings}>
<SettingsContainer
title="SMTP Settings"
description="Configure your SMTP settings to send emails from your email domain."
submitButtonText="Save"
className="grid grid-cols-9 gap-4"
slotProps={{
submitButton: {
disabled: !isDirty || maintenanceActive,
loading: isSubmitting,
},
}}
>
<Input
{...registerSmtp('sender')}
id="sender"
name="sender"
label="From Email"
placeholder="noreply@nhost.app"
className="lg:col-span-4"
hideEmptyHelperText
fullWidth
error={Boolean(errors.sender)}
helperText={errors.sender?.message}
/>
<Input
{...registerSmtp('host')}
id="host"
name="host"
label="SMTP Host"
className="lg:col-span-4"
placeholder="e.g. smtp.sendgrid.net"
hideEmptyHelperText
fullWidth
error={Boolean(errors.host)}
helperText={errors.host?.message}
/>
<Input
{...registerSmtp('port')}
id="port"
name="port"
label="Port"
type="number"
placeholder="587"
className="lg:col-span-1"
hideEmptyHelperText
fullWidth
error={Boolean(errors.port)}
helperText={errors.port?.message}
/>
<Input
{...registerSmtp('user')}
id="user"
label="SMTP Username"
placeholder="SMTP Username"
className="lg:col-span-4"
hideEmptyHelperText
fullWidth
error={Boolean(errors.user)}
helperText={errors.user?.message}
/>
<Input
{...registerSmtp('password')}
id="password"
label="SMTP Password"
type="password"
placeholder="Enter SMTP password"
className="lg:col-span-5"
hideEmptyHelperText
fullWidth
error={Boolean(errors.password)}
helperText={errors.password?.message}
/>
<Input
{...registerSmtp('method')}
id="method"
name="method"
label="SMTP Auth Method"
placeholder="LOGIN"
hideEmptyHelperText
className="lg:col-span-4"
fullWidth
error={Boolean(errors.method)}
helperText={errors.method?.message}
/>
<ControlledCheckbox
name="secure"
id="secure"
label="Use SSL"
className="lg:col-span-9"
/>
</SettingsContainer>
</Form>
</FormProvider>
);
}

View File

@@ -0,0 +1,2 @@
export * from './SMTPSettings';
export { default as SMTPSettings } from './SMTPSettings';

View File

@@ -31,9 +31,10 @@ import { yupResolver } from '@hookform/resolvers/yup';
import { useTheme } from '@mui/material';
import { format } from 'date-fns';
import kebabCase from 'just-kebab-case';
import debounce from 'lodash.debounce';
import Image from 'next/image';
import type { RemoteAppUser } from 'pages/[workspaceSlug]/[appSlug]/users';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import * as Yup from 'yup';
@@ -76,6 +77,21 @@ export const EditUserFormValidationSchema = Yup.object({
locale: Yup.string(),
defaultRole: Yup.string(),
roles: Yup.array().of(Yup.boolean()),
metadata: Yup.string().test(
'is-valid-json',
'Metadata must be valid JSON or empty',
(value) => {
if (value === '') {
return true;
} // Allow empty string as valid input
try {
JSON.parse(value);
return true;
} catch (error) {
return false;
}
},
),
});
export type EditUserFormValues = Yup.InferType<
@@ -116,14 +132,55 @@ export default function EditUserForm({
locale: user.locale,
defaultRole: user.defaultRole,
roles: roles.map((role) => Object.values(role)[0]),
metadata: user?.metadata ? JSON.stringify(user.metadata, null, 2) : '',
},
});
const {
register,
setError,
clearErrors,
formState: { errors, dirtyFields, isSubmitting, isValidating },
} = form;
const handleMetadataError = useMemo(() => {
const debouncedSetError = debounce((value) => {
try {
JSON.parse(value);
// Only set an error if JSON parsing fails
} catch (error) {
setError('metadata', {
type: 'manual',
message: 'Invalid JSON format',
});
}
}, 500);
return {
call: debouncedSetError,
cancel: debouncedSetError.cancel, // lodash debounce provides a cancel method to stop the delayed function
};
}, [setError]);
const handleMetadataChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
if (value === '') {
clearErrors('metadata'); // Clear errors when the input is explicitly cleared
handleMetadataError.cancel(); // Cancel any debounced error checks
} else {
try {
JSON.parse(value);
clearErrors('metadata'); // Clear errors when valid JSON is entered
handleMetadataError.cancel(); // Cancel pending debounced error checks
} catch (error) {
handleMetadataError.call(value); // Call the debounced error setter
}
}
},
[clearErrors, handleMetadataError],
);
const isDirty = Object.keys(dirtyFields).length > 0;
useEffect(() => {
@@ -467,6 +524,28 @@ export default function EditUserForm({
</div>
</Box>
)}
<Box component="section" className="grid grid-flow-row gap-8 p-6">
<Input
{...register('metadata', { onChange: handleMetadataChange })}
id="metadata"
label="Metadata"
variant="inline"
hideEmptyHelperText
error={!!errors.metadata}
fullWidth
multiline
inputProps={{
className: 'resize-y min-h-[130px]',
}}
helperText={
errors.metadata
? errors.metadata.message
: 'Enter valid JSON. This can be a number, boolean, array, or object.'
}
maxRows={100}
autoComplete="off"
/>
</Box>
</Box>
<Box className="grid w-full flex-shrink-0 snap-end grid-flow-col justify-between gap-3 place-self-end border-t-1 p-2">

View File

@@ -113,6 +113,9 @@ export default function UsersBody({ users, onSubmit }: UsersBodyProps) {
phoneNumber: values.phoneNumber,
phoneNumberVerified: values.phoneNumberVerified,
locale: values.locale,
...(values?.metadata !== undefined && values.metadata !== ''
? { metadata: JSON.parse(values.metadata) }
: { metadata: null }),
},
},
});

View File

@@ -12,6 +12,7 @@ query GetSmtpSettings($appId: uuid!) {
secure
sender
user
password
}
}
}

View File

@@ -1,6 +1,6 @@
mutation insertGraphiteAutoEmbeddingsConfiguration(
$name: String
$model: embedding_model_enum
$model: String
$schemaName: String
$tableName: String
$columnName: String

View File

@@ -1,7 +1,7 @@
mutation updateGraphiteAutoEmbeddingsConfiguration(
$id: uuid!
$name: String
$model: embedding_model_enum
$model: String
$schemaName: String
$tableName: String
$columnName: String

View File

@@ -11,6 +11,7 @@ fragment RemoteAppGetUsers on users {
defaultRole
lastSeen
locale
metadata
roles {
id
role

View File

@@ -1,100 +1,35 @@
import { ApplyLocalSettingsDialog } from '@/components/common/ApplyLocalSettingsDialog';
import { useDialog } from '@/components/common/DialogProvider';
import { useUI } from '@/components/common/UIProvider';
import { ControlledCheckbox } from '@/components/form/ControlledCheckbox';
import { Form } from '@/components/form/Form';
import { Container } from '@/components/layout/Container';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { SettingsLayout } from '@/components/layout/SettingsLayout';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Input } from '@/components/ui/v2/Input';
import { Option } from '@/components/ui/v2/Option';
import { Select } from '@/components/ui/v2/Select';
import DeleteSMTPSettings from '@/features/authentication/settings/components/DeleteSMTPSettings/DeleteSMTPSettings';
import { PostmarkSettings } from '@/features/authentication/settings/components/PostmarkSettings';
import { SMTPSettings } from '@/features/authentication/settings/components/SMTPSettings';
import { UpgradeNotification } from '@/features/projects/common/components/UpgradeNotification';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
import {
GetSmtpSettingsDocument,
useGetSmtpSettingsQuery,
useUpdateConfigMutation,
} from '@/utils/__generated__/graphql';
import { yupResolver } from '@hookform/resolvers/yup';
import type { ReactElement } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import type { Optional } from 'utility-types';
import * as yup from 'yup';
const smtpValidationSchema = yup
.object({
secure: yup.bool().label('SMTP Secure'),
host: yup
.string()
.label('SMTP Host')
.matches(
/((https?):\/\/)?(www.)?[a-z0-9]+(\.[a-z]{2,}){1,3}(#?\/?[a-zA-Z0-9#]+)*\/?(\?[a-zA-Z0-9-_]+=[a-zA-Z0-9-%]+&?)?$/,
'SMTP Host must be a valid URL',
)
.required(),
port: yup
.number()
.typeError('The SMTP port should contain only numbers.')
.required(),
user: yup.string().label('Username').required(),
password: yup.string().label('Password'),
method: yup.string().required(),
sender: yup.string().label('SMTP Sender').email().required(),
})
.required();
export type SmtpFormValues = yup.InferType<typeof smtpValidationSchema>;
import { useGetSmtpSettingsQuery } from '@/utils/__generated__/graphql';
import { useEffect, useState, type ReactElement } from 'react';
export default function SMTPSettingsPage() {
const { openDialog } = useDialog();
const isPlatform = useIsPlatform();
const { maintenanceActive } = useUI();
const localMimirClient = useLocalMimirClient();
const { currentProject } = useCurrentWorkspaceAndProject();
const [mode, setMode] = useState('postmark');
const { data, loading, error } = useGetSmtpSettingsQuery({
variables: { appId: currentProject?.id },
...(!isPlatform ? { client: localMimirClient } : {}),
});
const { secure, host, port, user, method, sender } =
data?.config?.provider?.smtp || {};
const { host } = data?.config?.provider?.smtp || {};
const form = useForm<Optional<SmtpFormValues, 'password'>>({
reValidateMode: 'onSubmit',
resolver: yupResolver(smtpValidationSchema),
defaultValues: {
secure: false,
host: '',
port: undefined,
user: '',
method: '',
sender: '',
},
values: {
secure: secure || false,
host: host || '',
port,
user: user || '',
method: method || '',
sender: sender || '',
},
mode: 'onSubmit',
criteriaMode: 'all',
});
const {
register,
formState: { errors, isDirty, isSubmitting },
} = form;
const [updateConfig] = useUpdateConfigMutation({
refetchQueries: [GetSmtpSettingsDocument],
...(!isPlatform ? { client: localMimirClient } : {}),
});
useEffect(() => {
setMode(host !== 'postmark' ? 'smtp' : 'postmark');
}, [host]);
if (isPlatform && currentProject?.plan?.isFree) {
return (
@@ -121,151 +56,29 @@ export default function SMTPSettingsPage() {
throw error;
}
const handleEditSMTPSettings = async (values: SmtpFormValues) => {
const { password, ...valuesWithoutPassword } = values;
const updateConfigPromise = updateConfig({
variables: {
appId: currentProject.id,
config: {
provider: {
smtp: password ? values : valuesWithoutPassword,
},
},
},
});
await execPromiseWithErrorToast(
async () => {
await updateConfigPromise;
if (!isPlatform) {
openDialog({
title: 'Apply your changes',
component: <ApplyLocalSettingsDialog />,
props: {
PaperProps: {
className: 'max-w-2xl',
},
},
});
}
},
{
loadingMessage: 'SMTP settings are being updated...',
successMessage: 'SMTP settings have been updated successfully.',
errorMessage:
'An error occurred while trying to update the SMTP settings.',
},
);
};
return (
<Container
className="grid max-w-5xl grid-flow-row gap-4 bg-transparent"
rootClassName="bg-transparent"
>
<FormProvider {...form}>
<Form onSubmit={handleEditSMTPSettings}>
<SettingsContainer
title="SMTP Settings"
description="Configure your SMTP settings to send emails from your email domain."
submitButtonText="Save"
className="grid grid-cols-9 gap-4"
slotProps={{
submitButton: {
disabled: !isDirty || maintenanceActive,
loading: isSubmitting,
},
}}
>
<Input
{...register('sender')}
id="sender"
name="sender"
label="From Email"
placeholder="noreply@nhost.app"
className="lg:col-span-4"
hideEmptyHelperText
fullWidth
error={Boolean(errors.sender)}
helperText={errors.sender?.message}
/>
<Select
slotProps={{
popper: { disablePortal: false, className: 'z-[10000]' },
}}
value={mode}
onChange={(_, value) => setMode(value as string)}
fullWidth
>
<Option key="smtp" value="smtp">
SMTP
</Option>
<Option key="postmark" value="postmark">
Postmark
</Option>
</Select>
<Input
{...register('host')}
id="host"
name="host"
label="SMTP Host"
className="lg:col-span-4"
placeholder="e.g. smtp.sendgrid.net"
hideEmptyHelperText
fullWidth
error={Boolean(errors.host)}
helperText={errors.host?.message}
/>
<Input
{...register('port')}
id="port"
name="port"
label="Port"
type="number"
placeholder="587"
className="lg:col-span-1"
hideEmptyHelperText
fullWidth
error={Boolean(errors.port)}
helperText={errors.port?.message}
/>
<Input
{...register('user')}
id="user"
label="SMTP Username"
placeholder="SMTP Username"
className="lg:col-span-4"
hideEmptyHelperText
fullWidth
error={Boolean(errors.user)}
helperText={errors.user?.message}
/>
<Input
{...register('password')}
id="password"
label="SMTP Password"
type="password"
placeholder="Enter SMTP password"
className="lg:col-span-5"
hideEmptyHelperText
fullWidth
error={Boolean(errors.password)}
helperText={errors.password?.message}
/>
<Input
{...register('method')}
id="method"
name="method"
label="SMTP Auth Method"
placeholder="LOGIN"
hideEmptyHelperText
className="lg:col-span-4"
fullWidth
error={Boolean(errors.method)}
helperText={errors.method?.message}
/>
<ControlledCheckbox
name="secure"
id="secure"
label="Use SSL"
className="lg:col-span-9"
/>
</SettingsContainer>
</Form>
</FormProvider>
{mode === 'postmark' ? <PostmarkSettings /> : <SMTPSettings />}
<DeleteSMTPSettings />
</Container>
);
}

View File

@@ -82,7 +82,11 @@ export default function SelectWorkspaceAndProject() {
);
useEffect(() => {
checkConfigFromQuery(router.query?.config as string);
const config = router.query?.config as string;
if (config) {
checkConfigFromQuery(router.query?.config as string);
}
}, [checkConfigFromQuery, router.query]);
const goToServices = async (project: {
@@ -133,7 +137,7 @@ export default function SelectWorkspaceAndProject() {
if (loading) {
return (
<div className="flex w-full justify-center">
<div className="flex justify-center w-full">
<ActivityIndicator
delay={500}
label="Loading workspaces and projects..."
@@ -156,7 +160,7 @@ export default function SelectWorkspaceAndProject() {
/>
<div>
<div className="mb-2 flex w-full">
<div className="flex w-full mb-2">
<Input
placeholder="Search..."
onChange={handleFilterChange}
@@ -166,11 +170,11 @@ export default function SelectWorkspaceAndProject() {
</div>
<RetryableErrorBoundary>
{projectsToDisplay.length === 0 ? (
<Box className="h-import py-2">
<Box className="py-2 h-import">
<Text variant="subtitle2">No results found.</Text>
</Box>
) : (
<List className="h-import overflow-y-auto">
<List className="overflow-y-auto h-import">
{projectsToDisplay.map((project, index) => (
<Fragment key={project.value}>
<ListItem.Root
@@ -186,7 +190,7 @@ export default function SelectWorkspaceAndProject() {
}
>
<ListItem.Avatar>
<span className="inline-block h-6 w-6 overflow-hidden rounded-md">
<span className="inline-block w-6 h-6 overflow-hidden rounded-md">
<Image
src="/logos/new.svg"
alt="Nhost Logo"

View File

@@ -16,9 +16,7 @@ export type Scalars = {
bigint: any;
bytea: any;
citext: any;
embedding_model_enum: any;
jsonb: any;
timestamp: any;
timestamptz: any;
timestampz: any;
uuid: any;
@@ -1547,9 +1545,9 @@ export type AuthUserProviders_Bool_Exp = {
export enum AuthUserProviders_Constraint {
/** unique or primary key constraint on columns "id" */
UserProvidersPkey = 'user_providers_pkey',
/** unique or primary key constraint on columns "provider_id", "provider_user_id" */
/** unique or primary key constraint on columns "provider_user_id", "provider_id" */
UserProvidersProviderIdProviderUserIdKey = 'user_providers_provider_id_provider_user_id_key',
/** unique or primary key constraint on columns "provider_id", "user_id" */
/** unique or primary key constraint on columns "user_id", "provider_id" */
UserProvidersUserIdProviderIdKey = 'user_providers_user_id_provider_id_key'
}
@@ -2684,19 +2682,6 @@ export enum Cursor_Ordering {
Desc = 'DESC'
}
/** Boolean expression to compare columns of type "embedding_model_enum". All fields are combined with logical 'AND'. */
export type Embedding_Model_Enum_Comparison_Exp = {
_eq?: InputMaybe<Scalars['embedding_model_enum']>;
_gt?: InputMaybe<Scalars['embedding_model_enum']>;
_gte?: InputMaybe<Scalars['embedding_model_enum']>;
_in?: InputMaybe<Array<Scalars['embedding_model_enum']>>;
_is_null?: InputMaybe<Scalars['Boolean']>;
_lt?: InputMaybe<Scalars['embedding_model_enum']>;
_lte?: InputMaybe<Scalars['embedding_model_enum']>;
_neq?: InputMaybe<Scalars['embedding_model_enum']>;
_nin?: InputMaybe<Array<Scalars['embedding_model_enum']>>;
};
/** columns and relationships of "storage.files" */
export type Files = {
__typename?: 'files';
@@ -3297,7 +3282,7 @@ export type GraphiteAutoEmbeddingsConfiguration = {
createdAt: Scalars['timestamptz'];
id: Scalars['uuid'];
lastRun?: Maybe<Scalars['timestamptz']>;
model: Scalars['embedding_model_enum'];
model: Scalars['String'];
mutation?: Maybe<Scalars['String']>;
name: Scalars['String'];
query?: Maybe<Scalars['String']>;
@@ -3337,7 +3322,7 @@ export type GraphiteAutoEmbeddingsConfiguration_Bool_Exp = {
createdAt?: InputMaybe<Timestamptz_Comparison_Exp>;
id?: InputMaybe<Uuid_Comparison_Exp>;
lastRun?: InputMaybe<Timestamptz_Comparison_Exp>;
model?: InputMaybe<Embedding_Model_Enum_Comparison_Exp>;
model?: InputMaybe<String_Comparison_Exp>;
mutation?: InputMaybe<String_Comparison_Exp>;
name?: InputMaybe<String_Comparison_Exp>;
query?: InputMaybe<String_Comparison_Exp>;
@@ -3352,7 +3337,7 @@ export enum GraphiteAutoEmbeddingsConfiguration_Constraint {
AutoEmbeddingsConfigurationNameKey = 'auto_embeddings_configuration_name_key',
/** unique or primary key constraint on columns "id" */
AutoEmbeddingsConfigurationPkey = 'auto_embeddings_configuration_pkey',
/** unique or primary key constraint on columns "table_name", "column_name", "schema_name" */
/** unique or primary key constraint on columns "column_name", "schema_name", "table_name" */
AutoEmbeddingsConfigurationSchemaNameTableNameColumnKey = 'auto_embeddings_configuration_schema_name_table_name_column_key'
}
@@ -3362,7 +3347,7 @@ export type GraphiteAutoEmbeddingsConfiguration_Insert_Input = {
createdAt?: InputMaybe<Scalars['timestamptz']>;
id?: InputMaybe<Scalars['uuid']>;
lastRun?: InputMaybe<Scalars['timestamptz']>;
model?: InputMaybe<Scalars['embedding_model_enum']>;
model?: InputMaybe<Scalars['String']>;
mutation?: InputMaybe<Scalars['String']>;
name?: InputMaybe<Scalars['String']>;
query?: InputMaybe<Scalars['String']>;
@@ -3378,7 +3363,7 @@ export type GraphiteAutoEmbeddingsConfiguration_Max_Fields = {
createdAt?: Maybe<Scalars['timestamptz']>;
id?: Maybe<Scalars['uuid']>;
lastRun?: Maybe<Scalars['timestamptz']>;
model?: Maybe<Scalars['embedding_model_enum']>;
model?: Maybe<Scalars['String']>;
mutation?: Maybe<Scalars['String']>;
name?: Maybe<Scalars['String']>;
query?: Maybe<Scalars['String']>;
@@ -3394,7 +3379,7 @@ export type GraphiteAutoEmbeddingsConfiguration_Min_Fields = {
createdAt?: Maybe<Scalars['timestamptz']>;
id?: Maybe<Scalars['uuid']>;
lastRun?: Maybe<Scalars['timestamptz']>;
model?: Maybe<Scalars['embedding_model_enum']>;
model?: Maybe<Scalars['String']>;
mutation?: Maybe<Scalars['String']>;
name?: Maybe<Scalars['String']>;
query?: Maybe<Scalars['String']>;
@@ -3471,7 +3456,7 @@ export type GraphiteAutoEmbeddingsConfiguration_Set_Input = {
createdAt?: InputMaybe<Scalars['timestamptz']>;
id?: InputMaybe<Scalars['uuid']>;
lastRun?: InputMaybe<Scalars['timestamptz']>;
model?: InputMaybe<Scalars['embedding_model_enum']>;
model?: InputMaybe<Scalars['String']>;
mutation?: InputMaybe<Scalars['String']>;
name?: InputMaybe<Scalars['String']>;
query?: InputMaybe<Scalars['String']>;
@@ -3494,7 +3479,7 @@ export type GraphiteAutoEmbeddingsConfiguration_Stream_Cursor_Value_Input = {
createdAt?: InputMaybe<Scalars['timestamptz']>;
id?: InputMaybe<Scalars['uuid']>;
lastRun?: InputMaybe<Scalars['timestamptz']>;
model?: InputMaybe<Scalars['embedding_model_enum']>;
model?: InputMaybe<Scalars['String']>;
mutation?: InputMaybe<Scalars['String']>;
name?: InputMaybe<Scalars['String']>;
query?: InputMaybe<Scalars['String']>;
@@ -3898,10 +3883,6 @@ export type Mutation_Root = {
deleteVirus?: Maybe<Virus>;
/** delete data from the table: "storage.virus" */
deleteViruses?: Maybe<Virus_Mutation_Response>;
/** delete data from the table: "todos" */
delete_todos?: Maybe<Todos_Mutation_Response>;
/** delete single row from the table: "todos" */
delete_todos_by_pk?: Maybe<Todos>;
graphite?: Maybe<GraphiteMutation>;
/** insert a single row into the table: "auth.providers" */
insertAuthProvider?: Maybe<AuthProviders>;
@@ -3955,10 +3936,6 @@ export type Mutation_Root = {
insertVirus?: Maybe<Virus>;
/** insert data into the table: "storage.virus" */
insertViruses?: Maybe<Virus_Mutation_Response>;
/** insert data into the table: "todos" */
insert_todos?: Maybe<Todos_Mutation_Response>;
/** insert a single row into the table: "todos" */
insert_todos_one?: Maybe<Todos>;
/** update single row of the table: "auth.providers" */
updateAuthProvider?: Maybe<AuthProviders>;
/** update single row of the table: "auth.provider_requests" */
@@ -4033,12 +4010,6 @@ export type Mutation_Root = {
update_buckets_many?: Maybe<Array<Maybe<Buckets_Mutation_Response>>>;
/** update multiples rows of table: "storage.files" */
update_files_many?: Maybe<Array<Maybe<Files_Mutation_Response>>>;
/** update data of the table: "todos" */
update_todos?: Maybe<Todos_Mutation_Response>;
/** update single row of the table: "todos" */
update_todos_by_pk?: Maybe<Todos>;
/** update multiples rows of table: "todos" */
update_todos_many?: Maybe<Array<Maybe<Todos_Mutation_Response>>>;
/** update multiples rows of table: "auth.users" */
update_users_many?: Maybe<Array<Maybe<Users_Mutation_Response>>>;
/** update multiples rows of table: "storage.virus" */
@@ -4304,18 +4275,6 @@ export type Mutation_RootDeleteVirusesArgs = {
};
/** mutation root */
export type Mutation_RootDelete_TodosArgs = {
where: Todos_Bool_Exp;
};
/** mutation root */
export type Mutation_RootDelete_Todos_By_PkArgs = {
id: Scalars['uuid'];
};
/** mutation root */
export type Mutation_RootInsertAuthProviderArgs = {
object: AuthProviders_Insert_Input;
@@ -4498,20 +4457,6 @@ export type Mutation_RootInsertVirusesArgs = {
};
/** mutation root */
export type Mutation_RootInsert_TodosArgs = {
objects: Array<Todos_Insert_Input>;
on_conflict?: InputMaybe<Todos_On_Conflict>;
};
/** mutation root */
export type Mutation_RootInsert_Todos_OneArgs = {
object: Todos_Insert_Input;
on_conflict?: InputMaybe<Todos_On_Conflict>;
};
/** mutation root */
export type Mutation_RootUpdateAuthProviderArgs = {
_set?: InputMaybe<AuthProviders_Set_Input>;
@@ -4816,26 +4761,6 @@ export type Mutation_RootUpdate_Files_ManyArgs = {
};
/** mutation root */
export type Mutation_RootUpdate_TodosArgs = {
_set?: InputMaybe<Todos_Set_Input>;
where: Todos_Bool_Exp;
};
/** mutation root */
export type Mutation_RootUpdate_Todos_By_PkArgs = {
_set?: InputMaybe<Todos_Set_Input>;
pk_columns: Todos_Pk_Columns_Input;
};
/** mutation root */
export type Mutation_RootUpdate_Todos_ManyArgs = {
updates: Array<Todos_Updates>;
};
/** mutation root */
export type Mutation_RootUpdate_Users_ManyArgs = {
updates: Array<Users_Updates>;
@@ -4944,12 +4869,6 @@ export type Query_Root = {
graphiteAutoEmbeddingsConfigurationAggregate: GraphiteAutoEmbeddingsConfiguration_Aggregate;
/** fetch data from the table: "graphite.auto_embeddings_configuration" */
graphiteAutoEmbeddingsConfigurations: Array<GraphiteAutoEmbeddingsConfiguration>;
/** fetch data from the table: "todos" */
todos: Array<Todos>;
/** fetch aggregated fields from the table: "todos" */
todos_aggregate: Todos_Aggregate;
/** fetch data from the table: "todos" using primary key columns */
todos_by_pk?: Maybe<Todos>;
/** fetch data from the table: "auth.users" using primary key columns */
user?: Maybe<Users>;
/** fetch data from the table: "auth.users" */
@@ -5264,29 +5183,6 @@ export type Query_RootGraphiteAutoEmbeddingsConfigurationsArgs = {
};
export type Query_RootTodosArgs = {
distinct_on?: InputMaybe<Array<Todos_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
order_by?: InputMaybe<Array<Todos_Order_By>>;
where?: InputMaybe<Todos_Bool_Exp>;
};
export type Query_RootTodos_AggregateArgs = {
distinct_on?: InputMaybe<Array<Todos_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
order_by?: InputMaybe<Array<Todos_Order_By>>;
where?: InputMaybe<Todos_Bool_Exp>;
};
export type Query_RootTodos_By_PkArgs = {
id: Scalars['uuid'];
};
export type Query_RootUserArgs = {
id: Scalars['uuid'];
};
@@ -5438,14 +5334,6 @@ export type Subscription_Root = {
graphiteAutoEmbeddingsConfigurationStream: Array<GraphiteAutoEmbeddingsConfiguration>;
/** fetch data from the table: "graphite.auto_embeddings_configuration" */
graphiteAutoEmbeddingsConfigurations: Array<GraphiteAutoEmbeddingsConfiguration>;
/** fetch data from the table: "todos" */
todos: Array<Todos>;
/** fetch aggregated fields from the table: "todos" */
todos_aggregate: Todos_Aggregate;
/** fetch data from the table: "todos" using primary key columns */
todos_by_pk?: Maybe<Todos>;
/** fetch data from the table in a streaming manner: "todos" */
todos_stream: Array<Todos>;
/** fetch data from the table: "auth.users" using primary key columns */
user?: Maybe<Users>;
/** fetch data from the table: "auth.users" */
@@ -5855,36 +5743,6 @@ export type Subscription_RootGraphiteAutoEmbeddingsConfigurationsArgs = {
};
export type Subscription_RootTodosArgs = {
distinct_on?: InputMaybe<Array<Todos_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
order_by?: InputMaybe<Array<Todos_Order_By>>;
where?: InputMaybe<Todos_Bool_Exp>;
};
export type Subscription_RootTodos_AggregateArgs = {
distinct_on?: InputMaybe<Array<Todos_Select_Column>>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
order_by?: InputMaybe<Array<Todos_Order_By>>;
where?: InputMaybe<Todos_Bool_Exp>;
};
export type Subscription_RootTodos_By_PkArgs = {
id: Scalars['uuid'];
};
export type Subscription_RootTodos_StreamArgs = {
batch_size: Scalars['Int'];
cursor: Array<InputMaybe<Todos_Stream_Cursor_Input>>;
where?: InputMaybe<Todos_Bool_Exp>;
};
export type Subscription_RootUserArgs = {
id: Scalars['uuid'];
};
@@ -5944,19 +5802,6 @@ export type Subscription_RootVirusesAggregateArgs = {
where?: InputMaybe<Virus_Bool_Exp>;
};
/** Boolean expression to compare columns of type "timestamp". All fields are combined with logical 'AND'. */
export type Timestamp_Comparison_Exp = {
_eq?: InputMaybe<Scalars['timestamp']>;
_gt?: InputMaybe<Scalars['timestamp']>;
_gte?: InputMaybe<Scalars['timestamp']>;
_in?: InputMaybe<Array<Scalars['timestamp']>>;
_is_null?: InputMaybe<Scalars['Boolean']>;
_lt?: InputMaybe<Scalars['timestamp']>;
_lte?: InputMaybe<Scalars['timestamp']>;
_neq?: InputMaybe<Scalars['timestamp']>;
_nin?: InputMaybe<Array<Scalars['timestamp']>>;
};
/** Boolean expression to compare columns of type "timestamptz". All fields are combined with logical 'AND'. */
export type Timestamptz_Comparison_Exp = {
_eq?: InputMaybe<Scalars['timestamptz']>;
@@ -5970,208 +5815,6 @@ export type Timestamptz_Comparison_Exp = {
_nin?: InputMaybe<Array<Scalars['timestamptz']>>;
};
/** columns and relationships of "todos" */
export type Todos = {
__typename?: 'todos';
/** An object relationship */
attachment?: Maybe<Files>;
createdAt: Scalars['timestamp'];
done: Scalars['Boolean'];
file_id?: Maybe<Scalars['uuid']>;
id: Scalars['uuid'];
title: Scalars['String'];
updatedAt: Scalars['timestamptz'];
/** An object relationship */
user: Users;
user_id: Scalars['uuid'];
};
/** aggregated selection of "todos" */
export type Todos_Aggregate = {
__typename?: 'todos_aggregate';
aggregate?: Maybe<Todos_Aggregate_Fields>;
nodes: Array<Todos>;
};
/** aggregate fields of "todos" */
export type Todos_Aggregate_Fields = {
__typename?: 'todos_aggregate_fields';
count: Scalars['Int'];
max?: Maybe<Todos_Max_Fields>;
min?: Maybe<Todos_Min_Fields>;
};
/** aggregate fields of "todos" */
export type Todos_Aggregate_FieldsCountArgs = {
columns?: InputMaybe<Array<Todos_Select_Column>>;
distinct?: InputMaybe<Scalars['Boolean']>;
};
/** Boolean expression to filter rows from the table "todos". All fields are combined with a logical 'AND'. */
export type Todos_Bool_Exp = {
_and?: InputMaybe<Array<Todos_Bool_Exp>>;
_not?: InputMaybe<Todos_Bool_Exp>;
_or?: InputMaybe<Array<Todos_Bool_Exp>>;
attachment?: InputMaybe<Files_Bool_Exp>;
createdAt?: InputMaybe<Timestamp_Comparison_Exp>;
done?: InputMaybe<Boolean_Comparison_Exp>;
file_id?: InputMaybe<Uuid_Comparison_Exp>;
id?: InputMaybe<Uuid_Comparison_Exp>;
title?: InputMaybe<String_Comparison_Exp>;
updatedAt?: InputMaybe<Timestamptz_Comparison_Exp>;
user?: InputMaybe<Users_Bool_Exp>;
user_id?: InputMaybe<Uuid_Comparison_Exp>;
};
/** unique or primary key constraints on table "todos" */
export enum Todos_Constraint {
/** unique or primary key constraint on columns "id" */
TodosPkey = 'todos_pkey'
}
/** input type for inserting data into table "todos" */
export type Todos_Insert_Input = {
attachment?: InputMaybe<Files_Obj_Rel_Insert_Input>;
createdAt?: InputMaybe<Scalars['timestamp']>;
done?: InputMaybe<Scalars['Boolean']>;
file_id?: InputMaybe<Scalars['uuid']>;
id?: InputMaybe<Scalars['uuid']>;
title?: InputMaybe<Scalars['String']>;
updatedAt?: InputMaybe<Scalars['timestamptz']>;
user?: InputMaybe<Users_Obj_Rel_Insert_Input>;
user_id?: InputMaybe<Scalars['uuid']>;
};
/** aggregate max on columns */
export type Todos_Max_Fields = {
__typename?: 'todos_max_fields';
createdAt?: Maybe<Scalars['timestamp']>;
file_id?: Maybe<Scalars['uuid']>;
id?: Maybe<Scalars['uuid']>;
title?: Maybe<Scalars['String']>;
updatedAt?: Maybe<Scalars['timestamptz']>;
user_id?: Maybe<Scalars['uuid']>;
};
/** aggregate min on columns */
export type Todos_Min_Fields = {
__typename?: 'todos_min_fields';
createdAt?: Maybe<Scalars['timestamp']>;
file_id?: Maybe<Scalars['uuid']>;
id?: Maybe<Scalars['uuid']>;
title?: Maybe<Scalars['String']>;
updatedAt?: Maybe<Scalars['timestamptz']>;
user_id?: Maybe<Scalars['uuid']>;
};
/** response of any mutation on the table "todos" */
export type Todos_Mutation_Response = {
__typename?: 'todos_mutation_response';
/** number of rows affected by the mutation */
affected_rows: Scalars['Int'];
/** data from the rows affected by the mutation */
returning: Array<Todos>;
};
/** on_conflict condition type for table "todos" */
export type Todos_On_Conflict = {
constraint: Todos_Constraint;
update_columns?: Array<Todos_Update_Column>;
where?: InputMaybe<Todos_Bool_Exp>;
};
/** Ordering options when selecting data from "todos". */
export type Todos_Order_By = {
attachment?: InputMaybe<Files_Order_By>;
createdAt?: InputMaybe<Order_By>;
done?: InputMaybe<Order_By>;
file_id?: InputMaybe<Order_By>;
id?: InputMaybe<Order_By>;
title?: InputMaybe<Order_By>;
updatedAt?: InputMaybe<Order_By>;
user?: InputMaybe<Users_Order_By>;
user_id?: InputMaybe<Order_By>;
};
/** primary key columns input for table: todos */
export type Todos_Pk_Columns_Input = {
id: Scalars['uuid'];
};
/** select columns of table "todos" */
export enum Todos_Select_Column {
/** column name */
CreatedAt = 'createdAt',
/** column name */
Done = 'done',
/** column name */
FileId = 'file_id',
/** column name */
Id = 'id',
/** column name */
Title = 'title',
/** column name */
UpdatedAt = 'updatedAt',
/** column name */
UserId = 'user_id'
}
/** input type for updating data in table "todos" */
export type Todos_Set_Input = {
createdAt?: InputMaybe<Scalars['timestamp']>;
done?: InputMaybe<Scalars['Boolean']>;
file_id?: InputMaybe<Scalars['uuid']>;
id?: InputMaybe<Scalars['uuid']>;
title?: InputMaybe<Scalars['String']>;
updatedAt?: InputMaybe<Scalars['timestamptz']>;
user_id?: InputMaybe<Scalars['uuid']>;
};
/** Streaming cursor of the table "todos" */
export type Todos_Stream_Cursor_Input = {
/** Stream column input with initial value */
initial_value: Todos_Stream_Cursor_Value_Input;
/** cursor ordering */
ordering?: InputMaybe<Cursor_Ordering>;
};
/** Initial value of the column from where the streaming should start */
export type Todos_Stream_Cursor_Value_Input = {
createdAt?: InputMaybe<Scalars['timestamp']>;
done?: InputMaybe<Scalars['Boolean']>;
file_id?: InputMaybe<Scalars['uuid']>;
id?: InputMaybe<Scalars['uuid']>;
title?: InputMaybe<Scalars['String']>;
updatedAt?: InputMaybe<Scalars['timestamptz']>;
user_id?: InputMaybe<Scalars['uuid']>;
};
/** update columns of table "todos" */
export enum Todos_Update_Column {
/** column name */
CreatedAt = 'createdAt',
/** column name */
Done = 'done',
/** column name */
FileId = 'file_id',
/** column name */
Id = 'id',
/** column name */
Title = 'title',
/** column name */
UpdatedAt = 'updatedAt',
/** column name */
UserId = 'user_id'
}
export type Todos_Updates = {
/** sets the columns of the filtered rows to the given values */
_set?: InputMaybe<Todos_Set_Input>;
/** filter the rows which have to be updated */
where: Todos_Bool_Exp;
};
/** User account information. Don't modify its structure as Hasura Auth relies on it to function properly. */
export type Users = {
__typename?: 'users';
@@ -7150,11 +6793,11 @@ export type GetGraphiteAutoEmbeddingsConfigurationsQueryVariables = Exact<{
}>;
export type GetGraphiteAutoEmbeddingsConfigurationsQuery = { __typename?: 'query_root', graphiteAutoEmbeddingsConfigurations: Array<{ __typename?: 'graphiteAutoEmbeddingsConfiguration', id: any, name: string, model: any, schemaName: string, tableName: string, columnName: string, query?: string | null, mutation?: string | null, createdAt: any, updatedAt: any }>, graphiteAutoEmbeddingsConfigurationAggregate: { __typename?: 'graphiteAutoEmbeddingsConfiguration_aggregate', aggregate?: { __typename?: 'graphiteAutoEmbeddingsConfiguration_aggregate_fields', count: number } | null } };
export type GetGraphiteAutoEmbeddingsConfigurationsQuery = { __typename?: 'query_root', graphiteAutoEmbeddingsConfigurations: Array<{ __typename?: 'graphiteAutoEmbeddingsConfiguration', id: any, name: string, model: string, schemaName: string, tableName: string, columnName: string, query?: string | null, mutation?: string | null, createdAt: any, updatedAt: any }>, graphiteAutoEmbeddingsConfigurationAggregate: { __typename?: 'graphiteAutoEmbeddingsConfiguration_aggregate', aggregate?: { __typename?: 'graphiteAutoEmbeddingsConfiguration_aggregate_fields', count: number } | null } };
export type InsertGraphiteAutoEmbeddingsConfigurationMutationVariables = Exact<{
name?: InputMaybe<Scalars['String']>;
model?: InputMaybe<Scalars['embedding_model_enum']>;
model?: InputMaybe<Scalars['String']>;
schemaName?: InputMaybe<Scalars['String']>;
tableName?: InputMaybe<Scalars['String']>;
columnName?: InputMaybe<Scalars['String']>;
@@ -7168,7 +6811,7 @@ export type InsertGraphiteAutoEmbeddingsConfigurationMutation = { __typename?: '
export type UpdateGraphiteAutoEmbeddingsConfigurationMutationVariables = Exact<{
id: Scalars['uuid'];
name?: InputMaybe<Scalars['String']>;
model?: InputMaybe<Scalars['embedding_model_enum']>;
model?: InputMaybe<Scalars['String']>;
schemaName?: InputMaybe<Scalars['String']>;
tableName?: InputMaybe<Scalars['String']>;
columnName?: InputMaybe<Scalars['String']>;
@@ -7177,7 +6820,7 @@ export type UpdateGraphiteAutoEmbeddingsConfigurationMutationVariables = Exact<{
}>;
export type UpdateGraphiteAutoEmbeddingsConfigurationMutation = { __typename?: 'mutation_root', updateGraphiteAutoEmbeddingsConfiguration?: { __typename?: 'graphiteAutoEmbeddingsConfiguration', id: any, name: string, model: any, schemaName: string, tableName: string, columnName: string, query?: string | null, mutation?: string | null } | null };
export type UpdateGraphiteAutoEmbeddingsConfigurationMutation = { __typename?: 'mutation_root', updateGraphiteAutoEmbeddingsConfiguration?: { __typename?: 'graphiteAutoEmbeddingsConfiguration', id: any, name: string, model: string, schemaName: string, tableName: string, columnName: string, query?: string | null, mutation?: string | null } | null };
export type SendDevMessageMutationVariables = Exact<{
sessionId: Scalars['String'];
@@ -7489,7 +7132,7 @@ export function refetchGetGraphiteAutoEmbeddingsConfigurationsQuery(variables: G
return { query: GetGraphiteAutoEmbeddingsConfigurationsDocument, variables: variables }
}
export const InsertGraphiteAutoEmbeddingsConfigurationDocument = gql`
mutation insertGraphiteAutoEmbeddingsConfiguration($name: String, $model: embedding_model_enum, $schemaName: String, $tableName: String, $columnName: String, $query: String, $mutation: String) {
mutation insertGraphiteAutoEmbeddingsConfiguration($name: String, $model: String, $schemaName: String, $tableName: String, $columnName: String, $query: String, $mutation: String) {
insertGraphiteAutoEmbeddingsConfiguration(
object: {name: $name, model: $model, schemaName: $schemaName, tableName: $tableName, columnName: $columnName, query: $query, mutation: $mutation}
) {
@@ -7530,7 +7173,7 @@ export type InsertGraphiteAutoEmbeddingsConfigurationMutationHookResult = Return
export type InsertGraphiteAutoEmbeddingsConfigurationMutationResult = Apollo.MutationResult<InsertGraphiteAutoEmbeddingsConfigurationMutation>;
export type InsertGraphiteAutoEmbeddingsConfigurationMutationOptions = Apollo.BaseMutationOptions<InsertGraphiteAutoEmbeddingsConfigurationMutation, InsertGraphiteAutoEmbeddingsConfigurationMutationVariables>;
export const UpdateGraphiteAutoEmbeddingsConfigurationDocument = gql`
mutation updateGraphiteAutoEmbeddingsConfiguration($id: uuid!, $name: String, $model: embedding_model_enum, $schemaName: String, $tableName: String, $columnName: String, $query: String, $mutation: String) {
mutation updateGraphiteAutoEmbeddingsConfiguration($id: uuid!, $name: String, $model: String, $schemaName: String, $tableName: String, $columnName: String, $query: String, $mutation: String) {
updateGraphiteAutoEmbeddingsConfiguration(
pk_columns: {id: $id}
_set: {name: $name, model: $model, schemaName: $schemaName, tableName: $tableName, columnName: $columnName, query: $query, mutation: $mutation}

View File

@@ -2524,6 +2524,7 @@ export type ConfigSystemConfigPostgres = {
__typename?: 'ConfigSystemConfigPostgres';
connectionString: ConfigSystemConfigPostgresConnectionString;
database: Scalars['String'];
disk?: Maybe<ConfigSystemConfigPostgresDisk>;
enabled?: Maybe<Scalars['Boolean']>;
};
@@ -2533,6 +2534,7 @@ export type ConfigSystemConfigPostgresComparisonExp = {
_or?: InputMaybe<Array<ConfigSystemConfigPostgresComparisonExp>>;
connectionString?: InputMaybe<ConfigSystemConfigPostgresConnectionStringComparisonExp>;
database?: InputMaybe<ConfigStringComparisonExp>;
disk?: InputMaybe<ConfigSystemConfigPostgresDiskComparisonExp>;
enabled?: InputMaybe<ConfigBooleanComparisonExp>;
};
@@ -2568,15 +2570,41 @@ export type ConfigSystemConfigPostgresConnectionStringUpdateInput = {
storage?: InputMaybe<Scalars['String']>;
};
export type ConfigSystemConfigPostgresDisk = {
__typename?: 'ConfigSystemConfigPostgresDisk';
iops?: Maybe<Scalars['ConfigUint32']>;
tput?: Maybe<Scalars['ConfigUint32']>;
};
export type ConfigSystemConfigPostgresDiskComparisonExp = {
_and?: InputMaybe<Array<ConfigSystemConfigPostgresDiskComparisonExp>>;
_not?: InputMaybe<ConfigSystemConfigPostgresDiskComparisonExp>;
_or?: InputMaybe<Array<ConfigSystemConfigPostgresDiskComparisonExp>>;
iops?: InputMaybe<ConfigUint32ComparisonExp>;
tput?: InputMaybe<ConfigUint32ComparisonExp>;
};
export type ConfigSystemConfigPostgresDiskInsertInput = {
iops?: InputMaybe<Scalars['ConfigUint32']>;
tput?: InputMaybe<Scalars['ConfigUint32']>;
};
export type ConfigSystemConfigPostgresDiskUpdateInput = {
iops?: InputMaybe<Scalars['ConfigUint32']>;
tput?: InputMaybe<Scalars['ConfigUint32']>;
};
export type ConfigSystemConfigPostgresInsertInput = {
connectionString: ConfigSystemConfigPostgresConnectionStringInsertInput;
database: Scalars['String'];
disk?: InputMaybe<ConfigSystemConfigPostgresDiskInsertInput>;
enabled?: InputMaybe<Scalars['Boolean']>;
};
export type ConfigSystemConfigPostgresUpdateInput = {
connectionString?: InputMaybe<ConfigSystemConfigPostgresConnectionStringUpdateInput>;
database?: InputMaybe<Scalars['String']>;
disk?: InputMaybe<ConfigSystemConfigPostgresDiskUpdateInput>;
enabled?: InputMaybe<Scalars['Boolean']>;
};
@@ -22886,7 +22914,7 @@ export type GetSmtpSettingsQueryVariables = Exact<{
}>;
export type GetSmtpSettingsQuery = { __typename?: 'query_root', config?: { __typename: 'ConfigConfig', id: 'ConfigConfig', provider?: { __typename: 'ConfigProvider', id: 'ConfigProvider', smtp?: { __typename?: 'ConfigSmtp', host: string, method: string, port: any, secure: boolean, sender: string, user: string } | null } | null } | null };
export type GetSmtpSettingsQuery = { __typename?: 'query_root', config?: { __typename: 'ConfigConfig', id: 'ConfigConfig', provider?: { __typename: 'ConfigProvider', id: 'ConfigProvider', smtp?: { __typename?: 'ConfigSmtp', host: string, method: string, port: any, secure: boolean, sender: string, user: string, password: string } | null } | null } | null };
export type UpdateConfigMutationVariables = Exact<{
appId: Scalars['uuid'];
@@ -23098,7 +23126,7 @@ export type GetRemoteAppMetricsQueryVariables = Exact<{ [key: string]: never; }>
export type GetRemoteAppMetricsQuery = { __typename?: 'query_root', filesAggregate: { __typename?: 'files_aggregate', aggregate?: { __typename?: 'files_aggregate_fields', count: number, sum?: { __typename?: 'files_sum_fields', size?: number | null } | null } | null }, usersAggregate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null } };
export type RemoteAppGetUsersFragment = { __typename?: 'users', id: any, createdAt: any, displayName: string, avatarUrl: string, email?: any | null, emailVerified: boolean, phoneNumber?: string | null, phoneNumberVerified: boolean, disabled: boolean, defaultRole: string, lastSeen?: any | null, locale: string, roles: Array<{ __typename?: 'authUserRoles', id: any, role: string }>, userProviders: Array<{ __typename?: 'authUserProviders', id: any, providerId: string }> };
export type RemoteAppGetUsersFragment = { __typename?: 'users', id: any, createdAt: any, displayName: string, avatarUrl: string, email?: any | null, emailVerified: boolean, phoneNumber?: string | null, phoneNumberVerified: boolean, disabled: boolean, defaultRole: string, lastSeen?: any | null, locale: string, metadata?: any | null, roles: Array<{ __typename?: 'authUserRoles', id: any, role: string }>, userProviders: Array<{ __typename?: 'authUserProviders', id: any, providerId: string }> };
export type RemoteAppGetUsersQueryVariables = Exact<{
where: Users_Bool_Exp;
@@ -23107,7 +23135,7 @@ export type RemoteAppGetUsersQueryVariables = Exact<{
}>;
export type RemoteAppGetUsersQuery = { __typename?: 'query_root', users: Array<{ __typename?: 'users', id: any, createdAt: any, displayName: string, avatarUrl: string, email?: any | null, emailVerified: boolean, phoneNumber?: string | null, phoneNumberVerified: boolean, disabled: boolean, defaultRole: string, lastSeen?: any | null, locale: string, roles: Array<{ __typename?: 'authUserRoles', id: any, role: string }>, userProviders: Array<{ __typename?: 'authUserProviders', id: any, providerId: string }> }>, filteredUsersAggreggate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null }, usersAggregate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null } };
export type RemoteAppGetUsersQuery = { __typename?: 'query_root', users: Array<{ __typename?: 'users', id: any, createdAt: any, displayName: string, avatarUrl: string, email?: any | null, emailVerified: boolean, phoneNumber?: string | null, phoneNumberVerified: boolean, disabled: boolean, defaultRole: string, lastSeen?: any | null, locale: string, metadata?: any | null, roles: Array<{ __typename?: 'authUserRoles', id: any, role: string }>, userProviders: Array<{ __typename?: 'authUserProviders', id: any, providerId: string }> }>, filteredUsersAggreggate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null }, usersAggregate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null } };
export type RemoteAppGetUsersCustomQueryVariables = Exact<{
where: Users_Bool_Exp;
@@ -23124,7 +23152,7 @@ export type RemoteAppGetUsersWholeQueryVariables = Exact<{
}>;
export type RemoteAppGetUsersWholeQuery = { __typename?: 'query_root', users: Array<{ __typename?: 'users', id: any, createdAt: any, displayName: string, avatarUrl: string, email?: any | null, emailVerified: boolean, phoneNumber?: string | null, phoneNumberVerified: boolean, disabled: boolean, defaultRole: string, lastSeen?: any | null, locale: string, roles: Array<{ __typename?: 'authUserRoles', id: any, role: string }>, userProviders: Array<{ __typename?: 'authUserProviders', id: any, providerId: string }> }>, usersAggregate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null } };
export type RemoteAppGetUsersWholeQuery = { __typename?: 'query_root', users: Array<{ __typename?: 'users', id: any, createdAt: any, displayName: string, avatarUrl: string, email?: any | null, emailVerified: boolean, phoneNumber?: string | null, phoneNumberVerified: boolean, disabled: boolean, defaultRole: string, lastSeen?: any | null, locale: string, metadata?: any | null, roles: Array<{ __typename?: 'authUserRoles', id: any, role: string }>, userProviders: Array<{ __typename?: 'authUserProviders', id: any, providerId: string }> }>, usersAggregate: { __typename?: 'users_aggregate', aggregate?: { __typename?: 'users_aggregate_fields', count: number } | null } };
export type TotalUsersQueryVariables = Exact<{ [key: string]: never; }>;
@@ -23623,6 +23651,7 @@ export const RemoteAppGetUsersFragmentDoc = gql`
defaultRole
lastSeen
locale
metadata
roles {
id
role
@@ -25353,6 +25382,7 @@ export const GetSmtpSettingsDocument = gql`
secure
sender
user
password
}
}
}

View File

@@ -1,5 +1,17 @@
# @nhost/docs
## 2.10.3
### Patch Changes
- a58c5cf: fix: broken link
## 2.10.2
### Patch Changes
- 9480489: fix: update docs performance info
## 2.10.1
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/docs",
"version": "2.10.1",
"version": "2.10.3",
"private": true,
"scripts": {
"start": "mintlify dev"

View File

@@ -74,19 +74,4 @@ To setup dedicated resources for your project, you can either use the Dashboard
## 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 |
By default disks are provisioned with a capacity for 3000 IOPS and 125 Mbps of throughput. If you need higher performance don't hesitate to contact us.

View File

@@ -50,7 +50,7 @@ In addition, thanks to [pgvector](https://github.com/pgvector/pgvector) you can
<CardGroup cols={4}>
<Card title="Enabling Service" icon="square-1" href="../guides/ai/enabling-service">
</Card>
<Card title="Local Development" icon="square-2" href="../guides/ai/local_development">
<Card title="Local Development" icon="square-2" href="../guides/ai/local-development">
</Card>
<Card title="Auto-Embeddings" icon="square-3" href="../guides/ai/auto-embeddings">
</Card>

View File

@@ -1,5 +1,12 @@
# @nhost-examples/cli
## 0.3.2
### Patch Changes
- Updated dependencies [304065a]
- @nhost/nhost-js@3.1.0
## 0.3.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,12 @@
# @nhost-examples/codegen-react-apollo
## 0.4.2
### Patch Changes
- @nhost/react@3.4.2
- @nhost/react-apollo@11.0.2
## 0.4.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,11 @@
# @nhost-examples/codegen-react-query
## 0.4.2
### Patch Changes
- @nhost/react@3.4.2
## 0.4.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,12 @@
# @nhost-examples/react-urql
## 0.3.2
### Patch Changes
- @nhost/react@3.4.2
- @nhost/react-urql@8.0.2
## 0.3.1
### Patch Changes

View File

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

View File

@@ -23,7 +23,7 @@ The following endpoints are now exposed:
- `http://localhost:3030`: Nhost Dashboard
- `http://localhost:1337`: Hasura Console
- `http://localhost:8025`: Mailhog SMTP testing dashboard
- `http://localhost:9090`: Traefik dashboad
- `http://localhost:9090`: Traefik dashboard
## Running the Nhost dashboard locally

View File

@@ -1,5 +1,12 @@
# @nhost-examples/multi-tenant-one-to-many
## 2.2.2
### Patch Changes
- Updated dependencies [304065a]
- @nhost/nhost-js@3.1.0
## 2.2.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,13 @@
# @nhost-examples/nextjs
## 0.3.2
### Patch Changes
- @nhost/react@3.4.2
- @nhost/react-apollo@11.0.2
- @nhost/nextjs@2.1.11
## 0.3.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,12 @@
# @nhost-examples/node-storage
## 0.2.2
### Patch Changes
- Updated dependencies [304065a]
- @nhost/nhost-js@3.1.0
## 0.2.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,12 @@
# @nhost-examples/nextjs-server-components
## 0.4.2
### Patch Changes
- Updated dependencies [304065a]
- @nhost/nhost-js@3.1.0
## 0.4.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,12 @@
# @nhost-examples/react-apollo
## 0.8.2
### Patch Changes
- @nhost/react@3.4.2
- @nhost/react-apollo@11.0.2
## 0.8.1
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/react-apollo",
"version": "0.8.1",
"version": "0.8.2",
"private": true,
"dependencies": {
"@apollo/client": "^3.9.9",

View File

@@ -1,5 +1,11 @@
# @nhost-examples/react-gqty
## 1.2.2
### Patch Changes
- @nhost/react@3.4.2
## 1.2.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,14 @@
# @nhost-examples/vue-apollo
## 0.6.2
### Patch Changes
- Updated dependencies [304065a]
- @nhost/nhost-js@3.1.0
- @nhost/apollo@7.0.0
- @nhost/vue@2.5.2
## 0.6.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,12 @@
# @nhost-examples/vue-quickstart
## 0.2.2
### Patch Changes
- @nhost/apollo@7.0.0
- @nhost/vue@2.5.2
## 0.2.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,12 @@
# @nhost/apollo
## 7.0.0
### Patch Changes
- Updated dependencies [304065a]
- @nhost/nhost-js@3.1.0
## 6.2.1
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/apollo",
"version": "6.2.1",
"version": "7.0.0",
"description": "Nhost Apollo Client library",
"license": "MIT",
"keywords": [

Some files were not shown because too many files have changed in this diff Show More