Compare commits

...

29 Commits

Author SHA1 Message Date
Hassan Ben Jobrane
b0363a4f4c Merge pull request #2110 from nhost/changeset-release/main
chore: update versions
2023-07-07 17:18:43 +01:00
github-actions[bot]
17045b2018 chore: update versions 2023-07-07 16:07:03 +00:00
Hassan Ben Jobrane
c49cc11862 Merge pull request #2108 from nhost/feat/fix-hasura-storage-file-upload
fix(hasura-storage-js): fix file upload
2023-07-07 17:05:33 +01:00
Hassan Ben Jobrane
c83fe7d776 chore(e2e): change e2e tests timeout 2023-07-07 16:48:49 +01:00
Hassan Ben Jobrane
235b4c7405 chore: wrap secret values in quotes 2023-07-07 16:08:24 +01:00
Hassan Ben Jobrane
c2c0fbd33a chore(e2e): increase timeout 2023-07-07 15:19:02 +01:00
Hassan Ben Jobrane
300e3f49e0 chore: add changeset 2023-07-07 14:21:12 +01:00
Hassan Ben Jobrane
a95a77886b fix(hasura-storage-js): fix file upload 2023-07-07 10:44:42 +01:00
Stephan van Opstal
1f3f683202 Update serverless-functions.mdx (#2105)
Please correct me if I'm wrong but I believe the endpoints in the docs
are wrong.
2023-07-07 08:36:57 +02:00
github-actions[bot]
4c67fd23c4 chore: update versions (#2101)
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@0.19.0

### Minor Changes

- 9c61c69a7: chore(dashboard):add postgres 14.6-20230705-1 to the
version selector

### Patch Changes

-   47bda15ff: feat(settings): add warning to pull config

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-06 20:15:13 +02:00
Hassan Ben Jobrane
93d8d71e34 Merge pull request #2102 from nhost/feat/pull-config-warning
feat(settings): add warning to pull config
2023-07-06 15:06:08 +01:00
Hassan Ben Jobrane
47bda15ff2 chore: add changeset 2023-07-06 14:39:27 +01:00
Hassan Ben Jobrane
4563488b5d feat(settings): show alert when there's a repo 2023-07-06 14:35:31 +01:00
Hassan Ben Jobrane
8fd35f3fea feat(settings): add warning to pull config 2023-07-06 14:26:14 +01:00
David Barroso
9c61c69a7b chore(dashboard):add postgres 14.6-20230705-1 to the version selector (#2100) 2023-07-06 15:24:06 +02:00
github-actions[bot]
030ad4621e chore: update versions (#2098)
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@0.18.0

### Minor Changes

- ee0b9b8ed: chore(dashboard):add hasura v2.28.2 and v2.29.0 to the
version selector

## @nhost/docs@0.4.0

### Minor Changes

-   c6fa8da6d: fix(docs): remove outdated reference/cli

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-06 13:34:58 +02:00
David Barroso
ee0b9b8edc chore(dashboard):add hasura v2.28.2 and v2.29.0 to the version selector (#2097) 2023-07-06 13:21:53 +02:00
David Barroso
c6fa8da6df fix(docs): remove outdated reference/cli (#2093)
Fixes nhost/cli/issues/734
2023-07-06 12:21:14 +02:00
github-actions[bot]
dd9dedc226 chore: update versions (#2086) 2023-06-30 10:12:55 +02:00
Hassan Ben Jobrane
5638a91240 Merge pull request #2080 from nhost/renovate/tsconfig-docusaurus-2.x
chore(deps): update dependency @tsconfig/docusaurus to v2
2023-06-29 18:19:00 +01:00
Hassan Ben Jobrane
cdefbdebee chore: remove unchanged packages from changeset 2023-06-29 17:09:27 +01:00
Hassan Ben Jobrane
923abd3655 chore: add changeset 2023-06-29 17:02:26 +01:00
renovate[bot]
ef28540f9a chore(deps): update dependency @tsconfig/docusaurus to v2 2023-06-29 15:10:11 +00:00
Szilárd Dóró
d54e4cdd4e fix(hasura-storage-js): allow using custom buckets for upload (#2085)
This PR is a fix for the [issue mentioned on our Discord
channel](https://discord.com/channels/552499021260914688/1123893547955933214/1123893547955933214).
It wasn't caused by the latest hasura-storage-js release.
2023-06-29 17:07:29 +02:00
David Barroso
4a00963602 feat(observability): added graph with restarts (#2084) 2023-06-29 13:57:37 +02:00
github-actions[bot]
7ea9b890c8 chore: update versions (#2083)
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@0.17.19

### Patch Changes

-   f866120a6: fix(users): use the password length from the config

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-29 11:19:53 +02:00
Szilárd Dóró
f866120a65 fix(dashboard): use dynamic validation schema for password editing (#2082)
Fixes #2081
2023-06-29 10:52:40 +02:00
github-actions[bot]
472559276c chore: update versions (#2079)
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/hasura-storage-js@2.2.0

### Minor Changes

- 2cdb13b3e: fix(upload): allow specifying `id` and `name` only when not
using `form-data`

## @nhost/apollo@5.2.13

### Patch Changes

-   @nhost/nhost-js@2.2.11

## @nhost/react-apollo@5.0.30

### Patch Changes

-   @nhost/apollo@5.2.13
-   @nhost/react@2.0.26

## @nhost/react-urql@2.0.27

### Patch Changes

-   @nhost/react@2.0.26

## @nhost/nextjs@1.13.32

### Patch Changes

-   @nhost/react@2.0.26

## @nhost/nhost-js@2.2.11

### Patch Changes

-   Updated dependencies [2cdb13b3e]
    -   @nhost/hasura-storage-js@2.2.0

## @nhost/react@2.0.26

### Patch Changes

-   @nhost/nhost-js@2.2.11

## @nhost/vue@1.13.31

### Patch Changes

-   @nhost/nhost-js@2.2.11

## @nhost/dashboard@0.17.18

### Patch Changes

-   @nhost/react-apollo@5.0.30
-   @nhost/nextjs@1.13.32

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

### Patch Changes

- 2cdb13b3e: fix(upload): allow specifying `id` and `name` only when not
using `form-data`
    -   @nhost/nhost-js@2.2.11

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-27 16:50:56 +02:00
Szilárd Dóró
2cdb13b3ef fix(hasura-storage-js): streamline file upload API (#2072)
Fixes #2071

This PR changes the API of the `hasura-storage-js` SDK slightly.

Before:
```ts
const formData = new FormData();

// first file
formData.append('file[]', '<file>');

// second file
formData.append('file[]', '<file>');

const { fileMetadata, error } = await nhost.storage.upload({
  formData,
  id: '<custom-uuid>', // ID doesn't make sense anymore when uploading multiple files
  name: '<custom-name>', // Name doesn't make sense anymore when uploading multiple files
});
```

Now:
```ts
const formData = new FormData();

// first file
formData.append('file[]', '<file>', '<custom-name>');
formData.append('metadata[]', JSON.stringify({ id: '<custom-uuid>' }))

// second file
formData.append('file[]', '<file>', '<custom-name>');
formData.append('metadata[]', JSON.stringify({ id: '<custom-uuid>' }))

const { fileMetadata, error } = await nhost.storage.upload({ formData });

// Access the metadata of upload files via fileMetadata.processedFiles
```

The `id` and `name` attributes can only be specified if you want to
upload a single file:

```ts
const file = event.target.files[0];

const { fileMetadata, error } = await nhost.storage.upload({
  file,
  id: '<custom-id>',
  name: '<custom-name>',
});

// Access the metadata of the upload file via fileMetadata
```
2023-06-27 16:20:17 +02:00
101 changed files with 1461 additions and 359 deletions

View File

@@ -34,7 +34,7 @@ Nhost consists of open source software:
- Authentication: [Hasura Auth](https://github.com/nhost/hasura-auth/)
- Storage: [Hasura Storage](https://github.com/nhost/hasura-storage)
- Serverless Functions: Node.js (JavaScript and TypeScript)
- [Nhost CLI](https://docs.nhost.io/reference/cli) for local development
- [Nhost CLI](https://docs.nhost.io/cli) for local development
## Architecture of Nhost
@@ -97,7 +97,7 @@ Nhost is frontend agnostic, which means Nhost works with all frontend frameworks
# Resources
- Start developing locally with the [Nhost CLI](https://docs.nhost.io/reference/cli)
- Start developing locally with the [Nhost CLI](https://docs.nhost.io/cli)
## Nhost Clients

View File

@@ -1,5 +1,48 @@
# @nhost/dashboard
## 0.19.1
### Patch Changes
- @nhost/react-apollo@5.0.32
- @nhost/nextjs@1.13.34
## 0.19.0
### Minor Changes
- 9c61c69a7: chore(dashboard):add postgres 14.6-20230705-1 to the version selector
### Patch Changes
- 47bda15ff: feat(settings): add warning to pull config
## 0.18.0
### Minor Changes
- ee0b9b8ed: chore(dashboard):add hasura v2.28.2 and v2.29.0 to the version selector
## 0.17.20
### Patch Changes
- @nhost/react-apollo@5.0.31
- @nhost/nextjs@1.13.33
## 0.17.19
### Patch Changes
- f866120a6: fix(users): use the password length from the config
## 0.17.18
### Patch Changes
- @nhost/react-apollo@5.0.30
- @nhost/nextjs@1.13.32
## 0.17.17
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/dashboard",
"version": "0.17.17",
"version": "0.19.1",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
@@ -101,6 +101,7 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/bcryptjs": "^2.4.2",
"@types/lodash.debounce": "^4.0.7",
"@types/node": "^16.11.7",
"@types/pluralize": "^0.0.29",

View File

@@ -3,7 +3,10 @@ import { ProjectLayout } from '@/components/layout/ProjectLayout';
import type { SettingsSidebarProps } from '@/components/layout/SettingsSidebar';
import { SettingsSidebar } from '@/components/layout/SettingsSidebar';
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
import { Alert } from '@/components/ui/v2/Alert';
import { Box } from '@/components/ui/v2/Box';
import { Text } from '@/components/ui/v2/Text';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { twMerge } from 'tailwind-merge';
export interface SettingsLayoutProps extends ProjectLayoutProps {
@@ -22,6 +25,9 @@ export default function SettingsLayout({
sidebarProps: { className: sidebarClassName, ...sidebarProps } = {},
...props
}: SettingsLayoutProps) {
const { currentProject } = useCurrentWorkspaceAndProject();
const hasGitRepo = !!currentProject?.githubRepository;
return (
<ProjectLayout
mainContainerProps={{
@@ -37,9 +43,39 @@ export default function SettingsLayout({
<Box
sx={{ backgroundColor: 'background.default' }}
className="flex w-full flex-auto flex-col overflow-x-hidden"
className="flex w-full flex-auto flex-col overflow-scroll overflow-x-hidden"
>
<RetryableErrorBoundary>{children}</RetryableErrorBoundary>
<RetryableErrorBoundary>
{hasGitRepo && (
<Alert
severity="warning"
className="grid grid-flow-row place-content-center gap-2"
>
<Text color="warning" className="text-sm ">
As you have a connected repository, make sure to synchronize
your changes with{' '}
<code className="rounded-md bg-slate-200 px-2 py-px text-slate-500">
nhost config pull
</code>{' '}
or they may be reverted with the next push.
<br />
If there are multiple projects linked to the same repository and
you only want these changes to apply to a subset of them, please
check out{' '}
<a
target="_blank"
rel="noopener noreferrer"
className="underline"
href="https://docs.nhost.io/cli/overlays"
>
docs.nhost.io/cli/overlays
</a>{' '}
for guidance.
</Text>
</Alert>
)}
{children}
</RetryableErrorBoundary>
</Box>
</ProjectLayout>
);

View File

@@ -3,12 +3,16 @@ import { Form } from '@/components/form/Form';
import { Alert } from '@/components/ui/v2/Alert';
import { Button } from '@/components/ui/v2/Button';
import { Input } from '@/components/ui/v2/Input';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
import type { DialogFormProps } from '@/types/common';
import { getToastStyleProps } from '@/utils/constants/settings';
import { getServerError } from '@/utils/getServerError';
import type { RemoteAppGetUsersQuery } from '@/utils/__generated__/graphql';
import { useUpdateRemoteAppUserMutation } from '@/utils/__generated__/graphql';
import {
useGetSignInMethodsQuery,
useUpdateRemoteAppUserMutation,
} from '@/utils/__generated__/graphql';
import { yupResolver } from '@hookform/resolvers/yup';
import bcrypt from 'bcryptjs';
import { useState } from 'react';
@@ -27,19 +31,6 @@ export interface EditUserPasswordFormProps extends DialogFormProps {
user: RemoteAppGetUsersQuery['users'][0];
}
export const validationSchema = Yup.object({
password: Yup.string()
.label('Users Password')
.min(8, 'Password must be at least 8 characters long.')
.required('This field is required.'),
cpassword: Yup.string()
.required('Confirm Password is required')
.min(8, 'Password must be at least 8 characters long.')
.oneOf([Yup.ref('password')], 'Passwords do not match'),
});
export type EditUserPasswordFormValues = Yup.InferType<typeof validationSchema>;
export default function EditUserPasswordForm({
onCancel,
user,
@@ -49,26 +40,52 @@ export default function EditUserPasswordForm({
client: remoteProjectGQLClient,
});
const { closeDialog } = useDialog();
const { currentProject } = useCurrentWorkspaceAndProject();
const { data } = useGetSignInMethodsQuery({
variables: { appId: currentProject?.id },
skip: !currentProject?.id,
});
const passwordMinLength =
data?.config?.auth?.method?.emailPassword?.passwordMinLength || 1;
const validationSchema = Yup.object({
password: Yup.string()
.label('Password')
.min(
passwordMinLength,
`Password must be at least ${passwordMinLength} characters long.`,
)
.required('This field is required.'),
cpassword: Yup.string()
.label('Password Confirmation')
.min(
passwordMinLength,
`Password must be at least ${passwordMinLength} characters long.`,
)
.oneOf([Yup.ref('password')], 'Passwords do not match')
.required('This field is required.'),
});
const [editUserPasswordFormError, setEditUserPasswordFormError] =
useState<Error | null>(null);
const form = useForm<EditUserPasswordFormValues>({
const form = useForm<Yup.InferType<typeof validationSchema>>({
defaultValues: {},
reValidateMode: 'onSubmit',
resolver: yupResolver(validationSchema),
});
const handleSubmit = async ({ password }: EditUserPasswordFormValues) => {
const handleSubmit = async ({
password,
}: Yup.InferType<typeof validationSchema>) => {
setEditUserPasswordFormError(null);
const passwordHash = await bcrypt.hash(password, 10);
const updateUserPasswordPromise = updateUser({
variables: {
id: user.id,
user: {
passwordHash,
},
user: { passwordHash },
},
client: remoteProjectGQLClient,
});

View File

@@ -31,6 +31,7 @@ export type DatabaseServiceVersionFormValues = Yup.InferType<
>;
const AVAILABLE_POSTGRES_VERSIONS = [
'14.6-20230705-1',
'14.6-20230613-1',
'14.6-20230525',
'14.6-20230406-2',

View File

@@ -31,6 +31,8 @@ export type HasuraServiceVersionFormValues = Yup.InferType<
>;
const AVAILABLE_HASURA_VERSIONS = [
'v2.29.0-ce',
'v2.28.2-ce',
'v2.27.0-ce',
'v2.25.1-ce',
'v2.25.0-ce',

View File

@@ -1,5 +1,17 @@
# @nhost/docs
## 0.4.0
### Minor Changes
- c6fa8da6d: fix(docs): remove outdated reference/cli
## 0.3.5
### Patch Changes
- 923abd365: chore(deps): update dependency @tsconfig/docusaurus to v2
## 0.3.4
### Patch Changes

View File

@@ -35,4 +35,4 @@ sudo nhost sw upgrade
- [Local Development](/cli/local-development)
- [Migrate to Nhost Config](/cli/migrate-config)
- [Multiple Projects in Parallel](/cli/multiple-projects)
- [CLI commands reference](/reference/cli)
- [CLI Documentation](/cli)

View File

@@ -1,16 +0,0 @@
---
title: 'down'
sidebar_position: 3
---
Delete all containers created by `nhost up`
```bash
nhost down
```
To delete all containers **and the local database**, append `--data` to the command.
```bash
nhost down --data
```

View File

@@ -1,22 +0,0 @@
---
title: 'Global Flags'
sidebar_position: 9
---
### `--debug`, `-d`
Turn on debug output.
```bash
nhost up --debug
nhost init -d
```
### `--log-file`, `-f`
Save output to a given file.
```bash
nhost up -d --log-file some-file.txt
nhost logs -f some-file.txt
```

View File

@@ -1,6 +0,0 @@
---
title: 'CLI'
sidebar_position: 1
---
This section is a reference for the commands available in the [Nhost CLI](/cli).

View File

@@ -1,24 +0,0 @@
---
title: 'init'
sidebar_position: 1
---
Initialize a local Nhost project.
```
nhost init
```
If you have an existing Nhost project in Nhost Cloud that you want to use as a starting point for local development and for the [Git-based workflow](/platform/git), run `nhost init --remote`.
The `nhost init --remote` command does the following:
- Creates a new local Nhost project.
- Pulls the database migrations and Hasura metadata from the Nhost Cloud project.
- Resets the remote Nhost Cloud project's database migrations.
:::warning
The `nhost init --remote` command should only be run **once**. Running it multiple times will reset the remote Nhost Cloud project's database migrations which can cause migration conflict issues in your development team.
:::

View File

@@ -1,10 +0,0 @@
---
title: 'link'
sidebar_position: 4
---
Link the local Nhost project in your working directory to a project in Nhost Cloud.
```bash
nhost link
```

View File

@@ -1,10 +0,0 @@
---
title: 'list'
sidebar_position: 7
---
List projects in Nhost Cloud.
```bash
nhost list
```

View File

@@ -1,10 +0,0 @@
---
title: 'login'
sidebar_position: 5
---
Authenticate the CLI with your Nhost user.
```bash
nhost login
```

View File

@@ -1,10 +0,0 @@
---
title: 'logout'
sidebar_position: 6
---
Remove authentication for the CLI.
```bash
nhost logout
```

View File

@@ -1,10 +0,0 @@
---
title: 'logs'
sidebar_position: 9
---
View logs of all services.
```bash
nhost logs
```

View File

@@ -1,19 +0,0 @@
---
title: 'up'
sidebar_position: 2
---
To launch the development environment for your project, use the command `nhost up`. Once the environment is running, this command will
- Apply database migrations.
- Apply Hasura metadata.
```bash
nhost up
```
If it's the first time you start the project, [seed data](/database#seed-data) will be applied.
## Stop
Use `ctrl+c` to stop the development environment.

View File

@@ -1,10 +0,0 @@
---
title: 'upgrade'
sidebar_position: 8
---
Upgrade the CLI to the latest version.
```bash
nhost upgrade
```

View File

@@ -30,7 +30,3 @@ In this section:
- [Getting started](/reference/vue)
- [Protecting routes](/reference/vue/protecting-routes)
- [Apollo GraphQL](/reference/vue/apollo)
### Nhost CLI
- [CLI overview](/reference/cli)

View File

@@ -69,12 +69,12 @@ HTTP endpoints are automatically generated based on the file structure inside `f
Here's an example of four Serverless Functions with their files and their HTTP endpoints:
| File | HTTP Endpoint |
| --------------------------- | ----------------------------------------------------------------- |
| `functions/index.js` | `https://[project-subdomain].nhost.run/v1/functions/` |
| `functions/users/index.ts` | `https://[project-subdomain].nhost.run/v1/functions/users` |
| `functions/users/active.ts` | `https://[project-subdomain].nhost.run/v1/functions/users/active` |
| `functions/my-company.js` | `https://[project-subdomain].nhost.run/v1/functions/my-company` |
| File | HTTP Endpoint |
| --------------------------- | ------------------------------------------------------------------ |
| `functions/index.js` | `https://[subdomain].functions.[region].nhost.run/v1/` |
| `functions/users/index.ts` | `https://[subdomain].functions.[region].nhost.run/v1/users` |
| `functions/users/active.ts` | `https://[subdomain].functions.[region].nhost.run/v1/users/active` |
| `functions/my-company.js` | `https://[subdomain].functions.[region].nhost.run/v1/my-company` |
You can prepend files and folders with an underscore (`_`) to prevent them from being treated as Serverless Functions and
be turned into HTTP endpoints. This is useful if you have, for example, a utils file (`functions/_utils.js`) or a utils-f

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/docs",
"version": "0.3.4",
"version": "0.4.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@@ -31,7 +31,7 @@
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.4.1",
"@tsconfig/docusaurus": "^1.0.6",
"@tsconfig/docusaurus": "^2.0.0",
"typescript": "^4.8.4"
},
"browserslist": {

View File

@@ -183,23 +183,6 @@ const sidebars = {
dirName: 'reference/docgen/vue/content'
}
]
},
{
type: 'category',
label: 'CLI',
link: { type: 'doc', id: 'reference/cli/index' },
items: [
'reference/cli/init',
'reference/cli/up',
'reference/cli/down',
'reference/cli/link',
'reference/cli/login',
'reference/cli/logout',
'reference/cli/list',
'reference/cli/upgrade',
'reference/cli/logs',
'reference/cli/global-flags'
]
}
]
}

View File

@@ -1 +1,3 @@
.secrets.nhost
.secrets
.nhost
cat.jpg

View File

@@ -1,5 +1,19 @@
# @nhost-examples/node-storage
## 0.0.4
### Patch Changes
- d54e4cdd4: fix(buckets): allow using custom buckets for upload
- @nhost/nhost-js@2.2.12
## 0.0.3
### Patch Changes
- 2cdb13b3e: fix(upload): allow specifying `id` and `name` only when not using `form-data`
- @nhost/nhost-js@2.2.11
## 0.0.2
### Patch Changes

View File

@@ -26,5 +26,4 @@ You can use the `.env.example` file as a starting point.
pnpm start
```
The example will download a file from a public URL and upload it to your Nhost
Storage bucket.
The example will run a few upload operations and then exit.

View File

@@ -0,0 +1,6 @@
actions: []
custom_types:
enums: []
input_objects: []
objects: []
scalars: []

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,14 @@
- name: default
kind: postgres
configuration:
connection_info:
database_url:
from_env: HASURA_GRAPHQL_DATABASE_URL
isolation_level: read-committed
pool_settings:
connection_lifetime: 600
idle_timeout: 180
max_connections: 50
retries: 1
use_prepared_statements: true
tables: "!include default/tables/tables.yaml"

View File

@@ -0,0 +1,23 @@
table:
name: provider_requests
schema: auth
configuration:
column_config:
id:
custom_name: id
options:
custom_name: options
custom_column_names:
id: id
options: options
custom_name: authProviderRequests
custom_root_fields:
delete: deleteAuthProviderRequests
delete_by_pk: deleteAuthProviderRequest
insert: insertAuthProviderRequests
insert_one: insertAuthProviderRequest
select: authProviderRequests
select_aggregate: authProviderRequestsAggregate
select_by_pk: authProviderRequest
update: updateAuthProviderRequests
update_by_pk: updateAuthProviderRequest

View File

@@ -0,0 +1,28 @@
table:
name: providers
schema: auth
configuration:
column_config:
id:
custom_name: id
custom_column_names:
id: id
custom_name: authProviders
custom_root_fields:
delete: deleteAuthProviders
delete_by_pk: deleteAuthProvider
insert: insertAuthProviders
insert_one: insertAuthProvider
select: authProviders
select_aggregate: authProvidersAggregate
select_by_pk: authProvider
update: updateAuthProviders
update_by_pk: updateAuthProvider
array_relationships:
- name: userProviders
using:
foreign_key_constraint_on:
column: provider_id
table:
name: user_providers
schema: auth

View File

@@ -0,0 +1,26 @@
table:
name: refresh_token_types
schema: auth
is_enum: true
configuration:
column_config: {}
custom_column_names: {}
custom_name: authRefreshTokenTypes
custom_root_fields:
delete: deleteAuthRefreshTokenTypes
delete_by_pk: deleteAuthRefreshTokenType
insert: insertAuthRefreshTokenTypes
insert_one: insertAuthRefreshTokenType
select: authRefreshTokenTypes
select_aggregate: authRefreshTokenTypesAggregate
select_by_pk: authRefreshTokenType
update: updateAuthRefreshTokenTypes
update_by_pk: updateAuthRefreshTokenType
array_relationships:
- name: refreshTokens
using:
foreign_key_constraint_on:
column: type
table:
name: refresh_tokens
schema: auth

View File

@@ -0,0 +1,55 @@
table:
name: refresh_tokens
schema: auth
configuration:
column_config:
created_at:
custom_name: createdAt
expires_at:
custom_name: expiresAt
refresh_token_hash:
custom_name: refreshTokenHash
user_id:
custom_name: userId
custom_column_names:
created_at: createdAt
expires_at: expiresAt
refresh_token_hash: refreshTokenHash
user_id: userId
custom_name: authRefreshTokens
custom_root_fields:
delete: deleteAuthRefreshTokens
delete_by_pk: deleteAuthRefreshToken
insert: insertAuthRefreshTokens
insert_one: insertAuthRefreshToken
select: authRefreshTokens
select_aggregate: authRefreshTokensAggregate
select_by_pk: authRefreshToken
update: updateAuthRefreshTokens
update_by_pk: updateAuthRefreshToken
object_relationships:
- name: user
using:
foreign_key_constraint_on: user_id
select_permissions:
- role: user
permission:
columns:
- id
- created_at
- expires_at
- metadata
- type
- user_id
filter:
user_id:
_eq: X-Hasura-User-Id
delete_permissions:
- role: user
permission:
filter:
_and:
- user_id:
_eq: X-Hasura-User-Id
- type:
_eq: pat

View File

@@ -0,0 +1,35 @@
table:
name: roles
schema: auth
configuration:
column_config:
role:
custom_name: role
custom_column_names:
role: role
custom_name: authRoles
custom_root_fields:
delete: deleteAuthRoles
delete_by_pk: deleteAuthRole
insert: insertAuthRoles
insert_one: insertAuthRole
select: authRoles
select_aggregate: authRolesAggregate
select_by_pk: authRole
update: updateAuthRoles
update_by_pk: updateAuthRole
array_relationships:
- name: userRoles
using:
foreign_key_constraint_on:
column: role
table:
name: user_roles
schema: auth
- name: usersByDefaultRole
using:
foreign_key_constraint_on:
column: default_role
table:
name: users
schema: auth

View File

@@ -0,0 +1,48 @@
table:
name: user_providers
schema: auth
configuration:
column_config:
access_token:
custom_name: accessToken
created_at:
custom_name: createdAt
id:
custom_name: id
provider_id:
custom_name: providerId
provider_user_id:
custom_name: providerUserId
refresh_token:
custom_name: refreshToken
updated_at:
custom_name: updatedAt
user_id:
custom_name: userId
custom_column_names:
access_token: accessToken
created_at: createdAt
id: id
provider_id: providerId
provider_user_id: providerUserId
refresh_token: refreshToken
updated_at: updatedAt
user_id: userId
custom_name: authUserProviders
custom_root_fields:
delete: deleteAuthUserProviders
delete_by_pk: deleteAuthUserProvider
insert: insertAuthUserProviders
insert_one: insertAuthUserProvider
select: authUserProviders
select_aggregate: authUserProvidersAggregate
select_by_pk: authUserProvider
update: updateAuthUserProviders
update_by_pk: updateAuthUserProvider
object_relationships:
- name: provider
using:
foreign_key_constraint_on: provider_id
- name: user
using:
foreign_key_constraint_on: user_id

View File

@@ -0,0 +1,36 @@
table:
name: user_roles
schema: auth
configuration:
column_config:
created_at:
custom_name: createdAt
id:
custom_name: id
role:
custom_name: role
user_id:
custom_name: userId
custom_column_names:
created_at: createdAt
id: id
role: role
user_id: userId
custom_name: authUserRoles
custom_root_fields:
delete: deleteAuthUserRoles
delete_by_pk: deleteAuthUserRole
insert: insertAuthUserRoles
insert_one: insertAuthUserRole
select: authUserRoles
select_aggregate: authUserRolesAggregate
select_by_pk: authUserRole
update: updateAuthUserRoles
update_by_pk: updateAuthUserRole
object_relationships:
- name: roleByRole
using:
foreign_key_constraint_on: role
- name: user
using:
foreign_key_constraint_on: user_id

View File

@@ -0,0 +1,33 @@
table:
name: user_security_keys
schema: auth
configuration:
column_config:
credential_id:
custom_name: credentialId
credential_public_key:
custom_name: credentialPublicKey
id:
custom_name: id
user_id:
custom_name: userId
custom_column_names:
credential_id: credentialId
credential_public_key: credentialPublicKey
id: id
user_id: userId
custom_name: authUserSecurityKeys
custom_root_fields:
delete: deleteAuthUserSecurityKeys
delete_by_pk: deleteAuthUserSecurityKey
insert: insertAuthUserSecurityKeys
insert_one: insertAuthUserSecurityKey
select: authUserSecurityKeys
select_aggregate: authUserSecurityKeysAggregate
select_by_pk: authUserSecurityKey
update: updateAuthUserSecurityKeys
update_by_pk: updateAuthUserSecurityKey
object_relationships:
- name: user
using:
foreign_key_constraint_on: user_id

View File

@@ -0,0 +1,122 @@
table:
name: users
schema: auth
configuration:
column_config:
active_mfa_type:
custom_name: activeMfaType
avatar_url:
custom_name: avatarUrl
created_at:
custom_name: createdAt
default_role:
custom_name: defaultRole
disabled:
custom_name: disabled
display_name:
custom_name: displayName
email:
custom_name: email
email_verified:
custom_name: emailVerified
id:
custom_name: id
is_anonymous:
custom_name: isAnonymous
last_seen:
custom_name: lastSeen
locale:
custom_name: locale
new_email:
custom_name: newEmail
otp_hash:
custom_name: otpHash
otp_hash_expires_at:
custom_name: otpHashExpiresAt
otp_method_last_used:
custom_name: otpMethodLastUsed
password_hash:
custom_name: passwordHash
phone_number:
custom_name: phoneNumber
phone_number_verified:
custom_name: phoneNumberVerified
ticket:
custom_name: ticket
ticket_expires_at:
custom_name: ticketExpiresAt
totp_secret:
custom_name: totpSecret
updated_at:
custom_name: updatedAt
webauthn_current_challenge:
custom_name: currentChallenge
custom_column_names:
active_mfa_type: activeMfaType
avatar_url: avatarUrl
created_at: createdAt
default_role: defaultRole
disabled: disabled
display_name: displayName
email: email
email_verified: emailVerified
id: id
is_anonymous: isAnonymous
last_seen: lastSeen
locale: locale
new_email: newEmail
otp_hash: otpHash
otp_hash_expires_at: otpHashExpiresAt
otp_method_last_used: otpMethodLastUsed
password_hash: passwordHash
phone_number: phoneNumber
phone_number_verified: phoneNumberVerified
ticket: ticket
ticket_expires_at: ticketExpiresAt
totp_secret: totpSecret
updated_at: updatedAt
webauthn_current_challenge: currentChallenge
custom_name: users
custom_root_fields:
delete: deleteUsers
delete_by_pk: deleteUser
insert: insertUsers
insert_one: insertUser
select: users
select_aggregate: usersAggregate
select_by_pk: user
update: updateUsers
update_by_pk: updateUser
object_relationships:
- name: defaultRoleByRole
using:
foreign_key_constraint_on: default_role
array_relationships:
- name: refreshTokens
using:
foreign_key_constraint_on:
column: user_id
table:
name: refresh_tokens
schema: auth
- name: roles
using:
foreign_key_constraint_on:
column: user_id
table:
name: user_roles
schema: auth
- name: securityKeys
using:
foreign_key_constraint_on:
column: user_id
table:
name: user_security_keys
schema: auth
- name: userProviders
using:
foreign_key_constraint_on:
column: user_id
table:
name: user_providers
schema: auth

View File

@@ -0,0 +1,49 @@
table:
name: buckets
schema: storage
configuration:
column_config:
cache_control:
custom_name: cacheControl
created_at:
custom_name: createdAt
download_expiration:
custom_name: downloadExpiration
id:
custom_name: id
max_upload_file_size:
custom_name: maxUploadFileSize
min_upload_file_size:
custom_name: minUploadFileSize
presigned_urls_enabled:
custom_name: presignedUrlsEnabled
updated_at:
custom_name: updatedAt
custom_column_names:
cache_control: cacheControl
created_at: createdAt
download_expiration: downloadExpiration
id: id
max_upload_file_size: maxUploadFileSize
min_upload_file_size: minUploadFileSize
presigned_urls_enabled: presignedUrlsEnabled
updated_at: updatedAt
custom_name: buckets
custom_root_fields:
delete: deleteBuckets
delete_by_pk: deleteBucket
insert: insertBuckets
insert_one: insertBucket
select: buckets
select_aggregate: bucketsAggregate
select_by_pk: bucket
update: updateBuckets
update_by_pk: updateBucket
array_relationships:
- name: files
using:
foreign_key_constraint_on:
column: bucket_id
table:
name: files
schema: storage

View File

@@ -0,0 +1,51 @@
table:
name: files
schema: storage
configuration:
column_config:
bucket_id:
custom_name: bucketId
created_at:
custom_name: createdAt
etag:
custom_name: etag
id:
custom_name: id
is_uploaded:
custom_name: isUploaded
mime_type:
custom_name: mimeType
name:
custom_name: name
size:
custom_name: size
updated_at:
custom_name: updatedAt
uploaded_by_user_id:
custom_name: uploadedByUserId
custom_column_names:
bucket_id: bucketId
created_at: createdAt
etag: etag
id: id
is_uploaded: isUploaded
mime_type: mimeType
name: name
size: size
updated_at: updatedAt
uploaded_by_user_id: uploadedByUserId
custom_name: files
custom_root_fields:
delete: deleteFiles
delete_by_pk: deleteFile
insert: insertFiles
insert_one: insertFile
select: files
select_aggregate: filesAggregate
select_by_pk: file
update: updateFiles
update_by_pk: updateFile
object_relationships:
- name: bucket
using:
foreign_key_constraint_on: bucket_id

View File

@@ -0,0 +1,11 @@
- "!include auth_provider_requests.yaml"
- "!include auth_providers.yaml"
- "!include auth_refresh_token_types.yaml"
- "!include auth_refresh_tokens.yaml"
- "!include auth_roles.yaml"
- "!include auth_user_providers.yaml"
- "!include auth_user_roles.yaml"
- "!include auth_user_security_keys.yaml"
- "!include auth_users.yaml"
- "!include storage_buckets.yaml"
- "!include storage_files.yaml"

View File

@@ -0,0 +1 @@
disabled_for_roles: []

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1 @@
[]

View File

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

View File

@@ -0,0 +1 @@
DELETE FROM "storage"."buckets" WHERE "id" = 'custom';

View File

@@ -0,0 +1 @@
INSERT INTO "storage"."buckets"("presigned_urls_enabled", "download_expiration", "max_upload_file_size", "min_upload_file_size", "cache_control", "id", "created_at", "updated_at") VALUES (true, 30, 30000000, 1, E'max-age=3600', E'custom', E'2023-06-29T14:30:13.859559+00:00', E'2023-06-29T14:30:13.859559+00:00');

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/node-storage",
"version": "0.0.2",
"version": "0.0.4",
"private": true,
"description": "This is an example of how to use the Storage with Node.js",
"main": "src/index.mjs",
@@ -14,9 +14,12 @@
"@nhost/nhost-js": "*",
"dotenv": "^16.1.3",
"form-data": "^4.0.0",
"node-fetch": "^3.3.0"
"fs-extra": "^11.1.1",
"node-fetch": "^3.3.0",
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/node": "^18.15.11"
"@types/node": "^18.15.11",
"@types/uuid": "^9.0.1"
}
}

View File

@@ -10,14 +10,23 @@ dependencies:
form-data:
specifier: ^4.0.0
version: 4.0.0
fs-extra:
specifier: ^11.1.1
version: 11.1.1
node-fetch:
specifier: ^3.3.0
version: 3.3.0
uuid:
specifier: ^9.0.0
version: 9.0.0
devDependencies:
'@types/node':
specifier: ^18.15.11
version: 18.15.11
'@types/uuid':
specifier: ^9.0.1
version: 9.0.1
packages:
@@ -32,6 +41,10 @@ packages:
resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
dev: true
/@types/uuid@9.0.1:
resolution: {integrity: sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==}
dev: true
/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: false
@@ -92,6 +105,27 @@ packages:
fetch-blob: 3.2.0
dev: false
/fs-extra@11.1.1:
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
engines: {node: '>=14.14'}
dependencies:
graceful-fs: 4.2.11
jsonfile: 6.1.0
universalify: 2.0.0
dev: false
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
dev: false
/jsonfile@6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
dependencies:
universalify: 2.0.0
optionalDependencies:
graceful-fs: 4.2.11
dev: false
/jwt-decode@3.1.2:
resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==}
dev: false
@@ -142,6 +176,16 @@ packages:
engines: {node: '>=4'}
dev: false
/universalify@2.0.0:
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
engines: {node: '>= 10.0.0'}
dev: false
/uuid@9.0.0:
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
hasBin: true
dev: false
/web-streams-polyfill@3.2.1:
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
engines: {node: '>= 8'}

View File

@@ -1,4 +1,7 @@
import { NhostClient } from '@nhost/nhost-js'
import dotenv from 'dotenv'
dotenv.config()
/**
* Create a new Nhost client.

View File

@@ -1,62 +1,17 @@
import dotenv from 'dotenv'
import FormData from 'form-data'
import fetch from 'node-fetch'
import { createClient } from './client.mjs'
import { uploadFile } from './uploadFile.mjs'
import { uploadFormData } from './uploadFormData.mjs'
import { uploadToBucket } from './uploadToBucket.mjs'
dotenv.config()
async function uploadFiles() {
await uploadFormData()
const client = createClient()
console.info('-----')
async function uploadImage() {
try {
// Download image from remote URL
const response = await fetch(
'https://hips.hearstapps.com/hmg-prod/images/cute-cat-photos-1593441022.jpg?crop=1.00xw:0.753xh;0,0.153xh&resize=1200:*'
)
await uploadFile()
if (!response.ok) {
console.error('Image not found!')
console.info('-----')
return
}
const arrayBuffer = await response.arrayBuffer()
const formData = new FormData()
formData.append('file[]', Buffer.from(arrayBuffer), 'cat.jpg')
// Upload file to Nhost Storage
const { error: uploadError, fileMetadata } = await client.storage.upload({
formData,
headers: { ...formData.getHeaders() }
})
if (uploadError) {
console.error(uploadError)
return
}
console.info(`File has been uploaded successfully!`)
const uploadedFile =
'processedFiles' in fileMetadata ? fileMetadata.processedFiles[0] : fileMetadata
console.info(`ID: ${uploadedFile?.id}`)
// Generate a presigned URL for the uploaded file
const { error: presignError, presignedUrl: blurredImage } =
await client.storage.getPresignedUrl({ fileId: uploadedFile.id })
if (presignError) {
console.error(presignError)
return
}
console.info(`Presigned URL: ${blurredImage.url}`)
} catch (error) {
console.error(error.message)
}
await uploadToBucket()
}
uploadImage()
uploadFiles()

View File

@@ -0,0 +1,70 @@
import fs from 'fs'
import fetch from 'node-fetch'
import { v4 as uuidv4 } from 'uuid'
import { createClient } from './client.mjs'
const client = createClient()
export async function uploadFile() {
console.info('Uploading a Single File Directly...')
try {
// Download image from remote URL
const response = await fetch(
'https://hips.hearstapps.com/hmg-prod/images/cute-cat-photos-1593441022.jpg?crop=1.00xw:0.753xh;0,0.153xh&resize=1200:*'
)
if (!response.ok) {
console.error(`[file]`, 'Image not found!')
return
}
const arrayBuffer = await response.arrayBuffer()
const fileBuffer = Buffer.from(arrayBuffer)
const fileName = 'cat.jpg'
fs.writeFile(fileName, fileBuffer, async (err) => {
if (err) {
console.error(`[file]`, err)
return
}
const file = fs.createReadStream(fileName)
const customFileId = uuidv4()
const { error: uploadError, fileMetadata } = await client.storage.upload({
file,
id: customFileId
})
if (uploadError) {
console.error(`[file]`, uploadError)
return
}
console.info(`[file]`, `File has been uploaded successfully!`)
console.info(`[file]`, `ID: ${fileMetadata?.id}`)
console.info(`[file]`, `Matches custom ID: ${fileMetadata?.id === customFileId}`)
// Generate a presigned URL for the uploaded file
const { error: presignError, presignedUrl: image } = await client.storage.getPresignedUrl({
fileId: fileMetadata.id
})
if (presignError) {
console.error(`[file]`, presignError)
return
}
console.info(`[file]`, `Presigned URL: ${image.url}`)
})
// Upload file to Nhost Storage
} catch (error) {
console.error(`[file]`, error.message)
}
}

View File

@@ -0,0 +1,90 @@
import FormData from 'form-data'
import fetch from 'node-fetch'
import { v4 as uuidv4 } from 'uuid'
import { createClient } from './client.mjs'
const client = createClient()
export async function uploadFormData() {
console.info('Uploading 2 Files via Form Data...')
try {
// Download image from remote URL
const response = await fetch(
'https://hips.hearstapps.com/hmg-prod/images/cute-cat-photos-1593441022.jpg?crop=1.00xw:0.753xh;0,0.153xh&resize=1200:*'
)
if (!response.ok) {
console.error(`[form-data]`, 'Image not found!')
return
}
const arrayBuffer = await response.arrayBuffer()
const customValues = [
{
id: uuidv4(),
name: 'cat1.jpg'
},
{
id: uuidv4(),
name: 'cat2.jpg'
}
]
const formData = new FormData()
formData.append('file[]', Buffer.from(arrayBuffer), customValues[0].name)
formData.append('metadata[]', JSON.stringify({ id: customValues[0].id }))
formData.append('file[]', Buffer.from(arrayBuffer), customValues[1].name)
formData.append('metadata[]', JSON.stringify({ id: customValues[1].id }))
// Upload files to Nhost Storage
const { error: uploadError, fileMetadata } = await client.storage.upload({
formData,
headers: { ...formData.getHeaders() }
})
if (uploadError) {
console.error(`[form-data]`, uploadError)
return
}
if (fileMetadata.processedFiles.length !== 2) {
console.error(
`[form-data]`,
`Expected 2 files to be uploaded, but got ${fileMetadata.processedFiles.length}`
)
return
}
await Promise.all(
fileMetadata?.processedFiles.map(async (processedFile, index) => {
console.info(`[form-data]`, `File has been uploaded successfully!`)
console.info(`[form-data]`, `ID: ${processedFile.id}`)
console.info(
`[form-data]`,
`Matches custom ID: ${customValues.some(({ id }) => processedFile.id === id)}`
)
// Generate a presigned URL for the uploaded file
const { error: presignError, presignedUrl: image } = await client.storage.getPresignedUrl({
fileId: processedFile.id
})
if (presignError) {
console.error(`[form-data]`, presignError)
return
}
console.info(`[form-data]`, `Presigned URL: ${image.url}`)
})
)
} catch (error) {
console.error(`[form-data]`, error.message)
}
}

View File

@@ -0,0 +1,68 @@
import fs from 'fs'
import fetch from 'node-fetch'
import { createClient } from './client.mjs'
const client = createClient()
export async function uploadToBucket() {
console.info('Uploading a Single File to a custom bucket...')
try {
// Download image from remote URL
const response = await fetch(
'https://hips.hearstapps.com/hmg-prod/images/cute-cat-photos-1593441022.jpg?crop=1.00xw:0.753xh;0,0.153xh&resize=1200:*'
)
if (!response.ok) {
console.error(`[file-to-bucket]`, 'Image not found!')
return
}
const arrayBuffer = await response.arrayBuffer()
const fileBuffer = Buffer.from(arrayBuffer)
const fileName = 'cat.jpg'
fs.writeFile(fileName, fileBuffer, async (err) => {
if (err) {
console.error(`[file-to-bucket]`, err)
return
}
const file = fs.createReadStream(fileName)
const { error: uploadError, fileMetadata } = await client.storage.upload({
file,
bucketId: 'custom'
})
if (uploadError) {
console.error(`[file-to-bucket]`, uploadError)
return
}
console.info(`[file-to-bucket]`, `File has been uploaded successfully!`)
console.info(`[file-to-bucket]`, `ID: ${fileMetadata?.id}`)
console.log(fileMetadata.bucketId)
// Generate a presigned URL for the uploaded file
const { error: presignError, presignedUrl: image } = await client.storage.getPresignedUrl({
fileId: fileMetadata.id
})
if (presignError) {
console.error(`[file-to-bucket]`, presignError)
return
}
console.info(`[file-to-bucket]`, `Presigned URL: ${image.url}`)
})
// Upload file to Nhost Storage
} catch (error) {
console.error(`[file-to-bucket]`, error.message)
}
}

View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
"allowJs": true,
"skipLibCheck": true,
"noEmit": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"isolatedModules": true,
"strictNullChecks": false
}
}

View File

@@ -1,4 +1,4 @@
HASURA_GRAPHQL_ADMIN_SECRET=nhost-admin-secret
HASURA_GRAPHQL_JWT_SECRET=oqpdwyffgxncqamwlyebkaifyazvqgso
NHOST_WEBHOOK_SECRET=nhost-webhook-secret
GRAFANA_ADMIN_PASSWORD=FIXME
HASURA_GRAPHQL_ADMIN_SECRET='nhost-admin-secret'
HASURA_GRAPHQL_JWT_SECRET='oqpdwyffgxncqamwlyebkaifyazvqgso'
NHOST_WEBHOOK_SECRET='nhost-webhook-secret'
GRAFANA_ADMIN_PASSWORD='FIXME'

View File

@@ -125,7 +125,7 @@
buildInputs = with pkgs; [
nhost
nodejs_18
# nodePackages.pnpm
nodePackages.pnpm
] ++ buildInputs ++ nativeBuildInputs;
};
};

View File

@@ -1,5 +1,23 @@
# @nhost/apollo
## 5.2.15
### Patch Changes
- @nhost/nhost-js@2.2.13
## 5.2.14
### Patch Changes
- @nhost/nhost-js@2.2.12
## 5.2.13
### Patch Changes
- @nhost/nhost-js@2.2.11
## 5.2.12
### Patch Changes

View File

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

View File

@@ -1,5 +1,26 @@
# @nhost/react-apollo
## 5.0.32
### Patch Changes
- @nhost/apollo@5.2.15
- @nhost/react@2.0.28
## 5.0.31
### Patch Changes
- @nhost/apollo@5.2.14
- @nhost/react@2.0.27
## 5.0.30
### Patch Changes
- @nhost/apollo@5.2.13
- @nhost/react@2.0.26
## 5.0.29
### Patch Changes

View File

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

View File

@@ -1,5 +1,23 @@
# @nhost/react-urql
## 2.0.29
### Patch Changes
- @nhost/react@2.0.28
## 2.0.28
### Patch Changes
- @nhost/react@2.0.27
## 2.0.27
### Patch Changes
- @nhost/react@2.0.26
## 2.0.26
### Patch Changes

View File

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

View File

@@ -629,6 +629,103 @@
"title": "Network Traffic",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "This graph shows when a service was restarted. There are two main reasons why a service may be restarted:\n\n- OOMKilled - This means the service tried to use more memory than it has available and had to be restarted. For more information on resources you can check the [documentation](https://docs.nhost.io/platform/compute).\n- Error - This can show for mainly two reasons; when new configuration needs to be applied the service is terminated and due to limitations this shows as \"Error\" but it is, in fact, part of normal operations. This can also show if your service is misconfigured and/or can't start correctly for some reason. If this error doesn't show constantly it is safe to ignore this error.",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 2,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 29
},
"id": 37,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "sum by(container, reason) (increase(pod_terminated_total[$__rate_interval]))",
"hide": false,
"interval": "2m",
"legendFormat": "{{container}}-{{reason}}",
"range": true,
"refId": "A"
}
],
"title": "Service Restarts",
"type": "timeseries"
},
{
"collapsed": false,
"gridPos": {

View File

@@ -1,4 +1,4 @@
HASURA_GRAPHQL_ADMIN_SECRET=nhost-admin-secret
HASURA_GRAPHQL_JWT_SECRET=oqpdwyffgxncqamwlyebkaifyazvqgso
NHOST_WEBHOOK_SECRET=nhost-webhook-secret
GRAFANA_ADMIN_PASSWORD=FIXME
HASURA_GRAPHQL_ADMIN_SECRET='nhost-admin-secret'
HASURA_GRAPHQL_JWT_SECRET='oqpdwyffgxncqamwlyebkaifyazvqgso'
NHOST_WEBHOOK_SECRET='nhost-webhook-secret'
GRAFANA_ADMIN_PASSWORD='FIXME'

View File

@@ -1,4 +1,4 @@
HASURA_GRAPHQL_ADMIN_SECRET=nhost-admin-secret
HASURA_GRAPHQL_JWT_SECRET=oqpdwyffgxncqamwlyebkaifyazvqgso
NHOST_WEBHOOK_SECRET=nhost-webhook-secret
GRAFANA_ADMIN_PASSWORD=FIXME
HASURA_GRAPHQL_ADMIN_SECRET='nhost-admin-secret'
HASURA_GRAPHQL_JWT_SECRET='oqpdwyffgxncqamwlyebkaifyazvqgso'
NHOST_WEBHOOK_SECRET='nhost-webhook-secret'
GRAFANA_ADMIN_PASSWORD='FIXME'

View File

@@ -1,5 +1,23 @@
# @nhost/hasura-storage-js
## 2.2.2
### Patch Changes
- 300e3f49e: fix(hasura-storage-js): fix file upload formData field
## 2.2.1
### Patch Changes
- d54e4cdd4: fix(buckets): allow using custom buckets for upload
## 2.2.0
### Minor Changes
- 2cdb13b3e: fix(upload): allow specifying `id` and `name` only when not using `form-data`
## 2.1.6
### Patch Changes

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,26 @@
table:
name: refresh_token_types
schema: auth
is_enum: true
configuration:
column_config: {}
custom_column_names: {}
custom_name: authRefreshTokenTypes
custom_root_fields:
delete: deleteAuthRefreshTokenTypes
delete_by_pk: deleteAuthRefreshTokenType
insert: insertAuthRefreshTokenTypes
insert_one: insertAuthRefreshTokenType
select: authRefreshTokenTypes
select_aggregate: authRefreshTokenTypesAggregate
select_by_pk: authRefreshTokenType
update: updateAuthRefreshTokenTypes
update_by_pk: updateAuthRefreshTokenType
array_relationships:
- name: refreshTokens
using:
foreign_key_constraint_on:
column: type
table:
name: refresh_tokens
schema: auth

View File

@@ -7,14 +7,14 @@ configuration:
custom_name: createdAt
expires_at:
custom_name: expiresAt
refresh_token:
custom_name: refreshToken
refresh_token_hash:
custom_name: refreshTokenHash
user_id:
custom_name: userId
custom_column_names:
created_at: createdAt
expires_at: expiresAt
refresh_token: refreshToken
refresh_token_hash: refreshTokenHash
user_id: userId
custom_name: authRefreshTokens
custom_root_fields:
@@ -31,3 +31,25 @@ object_relationships:
- name: user
using:
foreign_key_constraint_on: user_id
select_permissions:
- role: user
permission:
columns:
- id
- created_at
- expires_at
- metadata
- type
- user_id
filter:
user_id:
_eq: X-Hasura-User-Id
delete_permissions:
- role: user
permission:
filter:
_and:
- user_id:
_eq: X-Hasura-User-Id
- type:
_eq: pat

View File

@@ -1,5 +1,6 @@
- "!include auth_provider_requests.yaml"
- "!include auth_providers.yaml"
- "!include auth_refresh_token_types.yaml"
- "!include auth_refresh_tokens.yaml"
- "!include auth_roles.yaml"
- "!include auth_user_providers.yaml"

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/hasura-storage-js",
"version": "2.1.6",
"version": "2.2.2",
"description": "Hasura-storage client",
"license": "MIT",
"keywords": [

View File

@@ -1,11 +1,14 @@
import fetchPonyfill from 'fetch-ponyfill'
import FormData from 'form-data'
import {
ApiDeleteParams,
ApiDeleteResponse,
ApiGetPresignedUrlParams,
ApiGetPresignedUrlResponse,
StorageUploadFileParams,
StorageUploadFileResponse,
StorageUploadFormDataParams,
StorageUploadResponse
StorageUploadFormDataResponse
} from './utils/types'
import { fetchUpload } from './utils/upload'
@@ -24,21 +27,65 @@ export class HasuraStorageApi {
this.url = url
}
async upload({
async uploadFormData({
formData,
headers,
bucketId
}: StorageUploadFormDataParams): Promise<StorageUploadFormDataResponse> {
const { error, fileMetadata } = await fetchUpload(this.url, formData, {
accessToken: this.accessToken,
adminSecret: this.adminSecret,
bucketId,
headers
})
if (error) {
return { fileMetadata: null, error }
}
if (fileMetadata && !('processedFiles' in fileMetadata)) {
return {
fileMetadata: {
processedFiles: [fileMetadata]
},
error: null
}
}
return { fileMetadata, error: null }
}
async uploadFile({
file,
bucketId,
id,
name
}: StorageUploadFormDataParams): Promise<StorageUploadResponse> {
return fetchUpload(this.url, formData, {
}: StorageUploadFileParams): Promise<StorageUploadFileResponse> {
const formData = new FormData()
formData.append('file[]', file)
formData.append('metadata[]', JSON.stringify({ id, name }))
const { error, fileMetadata } = await fetchUpload(this.url, formData, {
accessToken: this.accessToken,
adminSecret: this.adminSecret,
bucketId,
fileId: id,
name,
headers
name
})
if (error) {
return { fileMetadata: null, error }
}
if (fileMetadata && 'processedFiles' in fileMetadata) {
return {
fileMetadata: fileMetadata.processedFiles[0],
error: null
}
}
return { fileMetadata, error: null }
}
async getPresignedUrl(params: ApiGetPresignedUrlParams): Promise<ApiGetPresignedUrlResponse> {

View File

@@ -1,4 +1,3 @@
import FormData from 'form-data'
import { HasuraStorageApi } from './hasura-storage-api'
import {
appendImageTransformationParameters,
@@ -8,7 +7,9 @@ import {
StorageGetPresignedUrlResponse,
StorageGetUrlParams,
StorageUploadFileParams,
StorageUploadFileResponse,
StorageUploadFormDataParams,
StorageUploadFormDataResponse,
StorageUploadParams,
StorageUploadResponse
} from './utils'
@@ -73,21 +74,14 @@ export class HasuraStorageClient {
* @docs https://docs.nhost.io/reference/javascript/storage/upload
*/
async upload(params: StorageUploadFileParams): Promise<StorageUploadResponse>
async upload(params: StorageUploadFormDataParams): Promise<StorageUploadResponse>
async upload(params: StorageUploadFileParams): Promise<StorageUploadFileResponse>
async upload(params: StorageUploadFormDataParams): Promise<StorageUploadFormDataResponse>
async upload(params: StorageUploadParams): Promise<StorageUploadResponse> {
if ('file' in params) {
const formData = new FormData()
formData.append('file[]', params.file)
return this.api.upload({
...params,
formData
})
return this.api.uploadFile(params)
}
return this.api.upload(params)
return this.api.uploadFormData(params)
}
/**

View File

@@ -118,7 +118,7 @@ export const createFileUploadMachine = () =>
uploadFile: (context, event) => (callback) => {
const file = (event.file || context.file)!
const data = new FormData()
data.append('file', file)
data.append('file[]', file)
let currentLoaded = 0

View File

@@ -36,8 +36,6 @@ export interface StorageUploadFileParams {
// works in browser and server
export interface StorageUploadFormDataParams {
formData: FormData
id?: string
name?: string
bucketId?: string
headers?: Record<string, string>
}
@@ -45,10 +43,16 @@ export interface StorageUploadFormDataParams {
// works in browser and server
export type StorageUploadParams = StorageUploadFileParams | StorageUploadFormDataParams
export type StorageUploadResponse =
| { fileMetadata: FileResponse | { processedFiles: FileResponse[] }; error: null }
export type StorageUploadFileResponse =
| { fileMetadata: FileResponse; error: null }
| { fileMetadata: null; error: StorageErrorPayload }
export type StorageUploadFormDataResponse =
| { fileMetadata: { processedFiles: FileResponse[] }; error: null }
| { fileMetadata: null; error: StorageErrorPayload }
export type StorageUploadResponse = StorageUploadFileResponse | StorageUploadFormDataResponse
export interface StorageImageTransformationParams {
/** Image width, in pixels */
width?: number

View File

@@ -38,15 +38,8 @@ export const fetchUpload = async (
const headers: HeadersInit = {
...initialHeaders
}
if (fileId) {
headers['x-nhost-file-id'] = fileId
}
if (bucketId) {
headers['x-nhost-bucket-id'] = bucketId
}
if (name) {
headers['x-nhost-file-name'] = toIso88591(name)
data.append('bucket-id', bucketId)
}
if (adminSecret) {
headers['x-hasura-admin-secret'] = adminSecret

View File

@@ -1,17 +1,15 @@
import fs from 'fs'
import { describe, expect, it } from 'vitest'
import { v4 as uuidv4 } from 'uuid'
import { describe, expect, it } from 'vitest'
import { storage } from './utils/helpers'
import FormData from 'form-data'
describe('test delete file', () => {
it('should be able to get delete file', async () => {
const fd = new FormData()
fd.append('file', fs.createReadStream('./tests/assets/sample.pdf'))
const file = fs.createReadStream('./tests/assets/sample.pdf')
const { fileMetadata } = await storage.upload({
formData: fd
file: file as unknown as File
})
const { error } = await storage.delete({

View File

@@ -38,11 +38,10 @@ describe('test upload', () => {
it('should upload a file with specific id', async () => {
const RANDOM_UUID = uuidv4()
const fd = new FormData()
fd.append('file', fs.createReadStream('./tests/assets/sample.pdf'))
const file = fs.createReadStream('./tests/assets/sample.pdf')
const { fileMetadata, error } = await storage.upload({
formData: fd,
file: file as unknown as File,
id: RANDOM_UUID
})
@@ -50,8 +49,7 @@ describe('test upload', () => {
throw new Error('fileMetadata is missing')
}
const { id: fileId } =
'processedFiles' in fileMetadata ? fileMetadata.processedFiles[0] : fileMetadata
const { id: fileId } = fileMetadata
expect(error).toBeNull()
expect(fileId).toBe(RANDOM_UUID)
@@ -60,11 +58,10 @@ describe('test upload', () => {
it('should upload a file with specific name', async () => {
const FILE_NAME = 'special-name.pdf'
const fd = new FormData()
fd.append('file', fs.createReadStream('./tests/assets/sample.pdf'))
const file = fs.createReadStream('./tests/assets/sample.pdf')
const { fileMetadata, error } = await storage.upload({
formData: fd,
file: file as unknown as File,
name: FILE_NAME
})
@@ -72,19 +69,17 @@ describe('test upload', () => {
throw new Error('fileMetadata is missing')
}
const { name: fileName } =
'processedFiles' in fileMetadata ? fileMetadata.processedFiles[0] : fileMetadata
const { name: fileName } = fileMetadata
expect(error).toBeNull()
expect(fileName).toBe(FILE_NAME)
})
it('should upload a file with a non-ISO 8859-1 name', async () => {
const fd = new FormData()
fd.append('file', fs.createReadStream('./tests/assets/sample.pdf'))
const file = fs.createReadStream('./tests/assets/sample.pdf')
const { fileMetadata, error } = await storage.upload({
formData: fd,
file: file as unknown as File,
name: '你 好'
})
@@ -92,8 +87,7 @@ describe('test upload', () => {
throw new Error('fileMetadata is missing')
}
const { name: fileName } =
'processedFiles' in fileMetadata ? fileMetadata.processedFiles[0] : fileMetadata
const { name: fileName } = fileMetadata
expect(error).toBeNull()
expect(fileName).toMatchInlineSnapshot('"%E4%BD%A0%20%E5%A5%BD"')
@@ -103,11 +97,10 @@ describe('test upload', () => {
const RANDOM_UUID = uuidv4()
const FILE_NAME = 'special-name.pdf'
const fd = new FormData()
fd.append('file', fs.createReadStream('./tests/assets/sample.pdf'))
const file = fs.createReadStream('./tests/assets/sample.pdf')
const { fileMetadata, error } = await storage.upload({
formData: fd,
file: file as unknown as File,
id: RANDOM_UUID,
name: FILE_NAME
})
@@ -116,8 +109,7 @@ describe('test upload', () => {
throw new Error('fileMetadata is missing')
}
const { id: fileId, name: fileName } =
'processedFiles' in fileMetadata ? fileMetadata.processedFiles[0] : fileMetadata
const { id: fileId, name: fileName } = fileMetadata
expect(error).toBeNull()
expect(fileId).toBe(RANDOM_UUID)

View File

@@ -1,5 +1,23 @@
# @nhost/nextjs
## 1.13.34
### Patch Changes
- @nhost/react@2.0.28
## 1.13.33
### Patch Changes
- @nhost/react@2.0.27
## 1.13.32
### Patch Changes
- @nhost/react@2.0.26
## 1.13.31
### Patch Changes

View File

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

View File

@@ -1,4 +1,4 @@
HASURA_GRAPHQL_ADMIN_SECRET=nhost-admin-secret
HASURA_GRAPHQL_JWT_SECRET=oqpdwyffgxncqamwlyebkaifyazvqgso
NHOST_WEBHOOK_SECRET=nhost-webhook-secret
GRAFANA_ADMIN_PASSWORD=FIXME
HASURA_GRAPHQL_ADMIN_SECRET='nhost-admin-secret'
HASURA_GRAPHQL_JWT_SECRET='oqpdwyffgxncqamwlyebkaifyazvqgso'
NHOST_WEBHOOK_SECRET='nhost-webhook-secret'
GRAFANA_ADMIN_PASSWORD='FIXME'

View File

@@ -1,5 +1,26 @@
# @nhost/nhost-js
## 2.2.13
### Patch Changes
- Updated dependencies [300e3f49e]
- @nhost/hasura-storage-js@2.2.2
## 2.2.12
### Patch Changes
- Updated dependencies [d54e4cdd4]
- @nhost/hasura-storage-js@2.2.1
## 2.2.11
### Patch Changes
- Updated dependencies [2cdb13b3e]
- @nhost/hasura-storage-js@2.2.0
## 2.2.10
### Patch Changes

View File

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

View File

@@ -1,5 +1,23 @@
# @nhost/react
## 2.0.28
### Patch Changes
- @nhost/nhost-js@2.2.13
## 2.0.27
### Patch Changes
- @nhost/nhost-js@2.2.12
## 2.0.26
### Patch Changes
- @nhost/nhost-js@2.2.11
## 2.0.25
### Patch Changes

View File

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

View File

@@ -1,5 +1,23 @@
# @nhost/vue
## 1.13.33
### Patch Changes
- @nhost/nhost-js@2.2.13
## 1.13.32
### Patch Changes
- @nhost/nhost-js@2.2.12
## 1.13.31
### Patch Changes
- @nhost/nhost-js@2.2.11
## 1.13.30
### Patch Changes

View File

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

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