Compare commits

..

120 Commits

Author SHA1 Message Date
Hassan Ben Jobrane
73cb65b9be Merge pull request #2255 from nhost/changeset-release/main
chore: update versions
2023-09-21 14:48:34 +01:00
github-actions[bot]
5e7c8395c2 chore: update versions 2023-09-21 11:33:11 +00:00
David Barroso
c2837209e6 chore: added codeql to CI (#2252) 2023-09-21 13:30:29 +02:00
Hassan Ben Jobrane
638710ea29 Merge pull request #2257 from nhost/fix/run/service-details
fix: dashboard: run: show correct private registry
2023-09-21 12:29:42 +01:00
Hassan Ben Jobrane
a79fddbafb fix(dashboard/run): remove image prop 2023-09-21 11:23:39 +01:00
Hassan Ben Jobrane
ab6a8f2add Merge pull request #2256 from nhost/feat/dashboard/query-software-versions
feat: dashboard: query software versions from platform
2023-09-21 11:12:52 +01:00
Hassan Ben Jobrane
69a5661bcf fix(dashboard/run): remove unused image prop 2023-09-21 10:37:18 +01:00
Hassan Ben Jobrane
0886118f9d Merge pull request #2254 from nhost/chore/run/remove-feature-flag
chore: remove run feature flag
2023-09-21 10:31:09 +01:00
Hassan Ben Jobrane
34fc08ca7c chore: add changeset 2023-09-21 10:18:05 +01:00
Hassan Ben Jobrane
153de22713 fix(dashboard): show correct private registry when showing service details 2023-09-21 10:16:52 +01:00
Hassan Ben Jobrane
bf4a1f6c2a chore: add changeset 2023-09-20 15:26:22 +01:00
Hassan Ben Jobrane
2a67d0f872 feat(dashboard): fetch Storage versions from platform 2023-09-20 15:23:17 +01:00
Hassan Ben Jobrane
b156c7b72e feat(dashboard): fetch Auth versions from platform 2023-09-20 15:22:55 +01:00
Hassan Ben Jobrane
b484b04ae2 feat(dashboard): fetch Hasura versions from platform 2023-09-20 15:18:40 +01:00
Hassan Ben Jobrane
2e55c7f46a feat(dashboard): fetch postgres versions from platform 2023-09-20 14:06:19 +01:00
Hassan Ben Jobrane
2d983e6ab1 feat(dashboard): codegen graphql query for getting software versions 2023-09-20 14:05:41 +01:00
Hassan Ben Jobrane
df5b4302c3 chore: add changeset 2023-09-20 12:12:42 +01:00
Hassan Ben Jobrane
828aed2df9 chore: remove run feature flag 2023-09-20 12:12:42 +01:00
Hassan Ben Jobrane
310df10892 Merge pull request #2251 from nhost/chore/replace-feedback
chore(dashboard): replace feedback form with contact us
2023-09-20 12:08:52 +01:00
Hassan Ben Jobrane
555fba4400 chore: re-arrange text content 2023-09-20 11:43:27 +01:00
Hassan Ben Jobrane
885d10620a chore: add changeset 2023-09-20 11:24:20 +01:00
Hassan Ben Jobrane
a8370f5aaa chore: fix nav test 2023-09-20 11:03:43 +01:00
Hassan Ben Jobrane
bd07905846 chore(dashboard): replace feedback form with contact us 2023-09-19 19:25:53 +01:00
Nuno Pato
47a2164549 Merge pull request #2250 from nhost/changeset-release/main
chore: update versions
2023-09-19 16:57:24 +00:00
github-actions[bot]
a96c79de00 chore: update versions 2023-09-19 16:42:43 +00:00
Nuno Pato
596d0666fc Merge pull request #2249 from nhost/chore/docs-run-public-beta
chore: docs: Run is now in public beta
2023-09-19 16:40:04 +00:00
Nuno Pato
9aaa407d29 Run is now in public beta 2023-09-19 16:01:57 +00:00
Hassan Ben Jobrane
1767b2f105 Merge pull request #2248 from nhost/changeset-release/main
chore: update versions
2023-09-18 20:17:34 +01:00
github-actions[bot]
c99c5c4191 chore: update versions 2023-09-18 19:03:08 +00:00
Hassan Ben Jobrane
d845da2503 Merge pull request #2246 from nhost/fix/one-click-install
fix: run: center the loading indicator
2023-09-18 20:00:37 +01:00
Hassan Ben Jobrane
9f1ba1686c Merge pull request #2247 from nhost/feat/run/delete-service-confirmation-dialog
feat: run: delete service confirmation dialog
2023-09-18 19:57:21 +01:00
Hassan Ben Jobrane
48b09a58ff fix: typo
Co-authored-by: Nuno Pato <nunopato@gmail.com>
2023-09-18 18:54:37 +01:00
Hassan Ben Jobrane
2169908883 chore: add changeset 2023-09-18 18:48:06 +01:00
Hassan Ben Jobrane
ed16c8b5de chore: add changeset 2023-09-18 18:45:11 +01:00
Hassan Ben Jobrane
c618503376 feat(run): add a confirmation dialog when deleting a run service 2023-09-18 18:45:03 +01:00
Hassan Ben Jobrane
f306c3940c fix: center the loading indicator 2023-09-18 16:53:15 +01:00
Hassan Ben Jobrane
ef125216bb Merge pull request #2242 from nhost/changeset-release/main
chore: update versions
2023-09-15 17:33:37 +01:00
github-actions[bot]
fb43fefb5c chore: update versions 2023-09-15 16:22:32 +00:00
Hassan Ben Jobrane
73744c90f0 Merge pull request #2241 from nhost/feat/node18-announcement
feat: add node18 announcement banner
2023-09-15 17:19:27 +01:00
Hassan Ben Jobrane
9fbea9787e chore: add changeset 2023-09-15 16:47:21 +01:00
Hassan Ben Jobrane
e5f54bc197 feat: add node18 announcement banner 2023-09-15 16:43:59 +01:00
Hassan Ben Jobrane
10a6ae4853 Merge pull request #2233 from nhost/changeset-release/main
chore: update versions
2023-09-13 17:11:54 +01:00
github-actions[bot]
d6ca1c7cfd chore: update versions 2023-09-13 13:59:01 +00:00
Hassan Ben Jobrane
bb85a95eda Merge pull request #2236 from nhost/fix/run/subdomain-optional
fix: run: handle subdomain nullability
2023-09-13 14:56:22 +01:00
Hassan Ben Jobrane
e84acf4692 chore: add changeset 2023-09-13 13:06:05 +01:00
Hassan Ben Jobrane
2f20a70a28 fix(run): subdomain is not set when creating a new service 2023-09-13 13:00:18 +01:00
David Barroso
819e1e97dc chore (docs): update fqdn format for nhost run (#2232) 2023-09-12 14:54:39 +02:00
Hassan Ben Jobrane
7c1cca0a43 Merge pull request #2231 from nhost/changeset-release/main
chore: update versions
2023-09-12 13:09:07 +01:00
github-actions[bot]
0f51f4e868 chore: update versions 2023-09-12 12:05:55 +00:00
Hassan Ben Jobrane
97a6fcead9 Merge pull request #2230 from nhost/feat/run/copy-urls-dialog
feat(run): add dialog to copy service urls
2023-09-12 13:03:17 +01:00
Hassan Ben Jobrane
b7c799d62c chore: add changeset 2023-09-12 12:04:41 +01:00
Hassan Ben Jobrane
18b14b27fd refactor: pass service data directly to the details dialog 2023-09-12 12:01:20 +01:00
Hassan Ben Jobrane
67a867c93a feat: add dialog to copy service urls 2023-09-11 19:52:51 +01:00
David Barroso
0a1fb12467 feat: observability: add egress/requests metrics to general dashboard (#2227) 2023-09-06 18:03:25 +02:00
Hassan Ben Jobrane
78467ee348 Merge pull request #2219 from nhost/changeset-release/main
chore: update versions
2023-09-04 11:58:34 +01:00
github-actions[bot]
c24eef0db9 chore: update versions 2023-09-04 10:24:35 +00:00
Hassan Ben Jobrane
2159b8171e Merge pull request #2218 from nhost/fix/format-functions-execution
fix: dashboard: usage stats
2023-09-04 11:21:25 +01:00
Hassan Ben Jobrane
8903e6abd9 chore: add changeset 2023-09-02 15:18:54 +01:00
Hassan Ben Jobrane
7290260990 fix: show correct egress volume limit 2023-09-02 15:15:43 +01:00
Hassan Ben Jobrane
06529a1ea4 fix: round up functions duration 2023-09-02 15:14:29 +01:00
Hassan Ben Jobrane
607d89e2aa Merge pull request #2215 from nhost/changeset-release/main
chore: update versions
2023-09-01 19:13:41 +01:00
github-actions[bot]
0cca72311c chore: update versions 2023-09-01 15:26:44 +00:00
Hassan Ben Jobrane
a6525b6467 Merge pull request #2214 from nhost/feat/update-usage-metrics
feat(dashboard): update usage metrics
2023-09-01 16:24:06 +01:00
Hassan Ben Jobrane
387be37b6e chore: remove redundant egress card 2023-09-01 15:30:01 +01:00
Hassan Ben Jobrane
c8fd8bbcc7 fix: update storage upper limit for pro plan 2023-09-01 13:01:27 +01:00
Hassan Ben Jobrane
bfb34bad00 fix: use correct value for functions duration 2023-09-01 12:28:19 +01:00
Hassan Ben Jobrane
666a75a233 chore: add changeset 2023-09-01 12:26:41 +01:00
Hassan Ben Jobrane
3b050217df feat(dashboard): tweak usage metrics 2023-09-01 12:25:15 +01:00
Hassan Ben Jobrane
0ed4481615 feat(dashboard): update usage metrics 2023-09-01 11:14:49 +01:00
Hassan Ben Jobrane
ac3f12c878 Merge pull request #2211 from nhost/changeset-release/main
chore: update versions
2023-08-31 12:29:34 +01:00
github-actions[bot]
65cabb089f chore: update versions 2023-08-31 11:01:17 +00:00
Hassan Ben Jobrane
2905beb0a1 Merge pull request #2212 from nhost/fix/hasura-storage-js-edge-runtime
fix(hasura-storage-js): swap fetch when running on edge runtime
2023-08-31 11:58:43 +01:00
Hassan Ben Jobrane
83fee54460 chore: add changeset 2023-08-31 11:11:44 +01:00
Hassan Ben Jobrane
82898b6dae fix(hasura-storage-js): swap fetch when running on edge runtime 2023-08-31 11:09:37 +01:00
Hassan Ben Jobrane
500f76a38d Merge pull request #2208 from nhost/fix/user-auth-locales
fix: remove hardcoded locales
2023-08-30 10:31:43 +01:00
Hassan Ben Jobrane
5e1e80aa8b chore: add changeset 2023-08-29 20:05:29 +01:00
Hassan Ben Jobrane
6d0a126907 fix: remove hardcoded locales 2023-08-29 13:32:12 +01:00
Hassan Ben Jobrane
1b7dcf2121 Merge pull request #2207 from nhost/changeset-release/main
chore: update versions
2023-08-28 16:40:51 +01:00
github-actions[bot]
2b9205b6cf chore: update versions 2023-08-28 15:16:01 +00:00
Hassan Ben Jobrane
bdc4d4a88c Merge pull request #2206 from nhost/fix/stripe-graphql-js
fix(stripe-graphql-js): fix stripe GraphQL extension export issue in serverless functions
2023-08-28 16:12:13 +01:00
Hassan Ben Jobrane
45759c4d4c chore: add changeset 2023-08-28 15:49:17 +01:00
Hassan Ben Jobrane
5f9886577a fix: import 2023-08-28 15:47:49 +01:00
Hassan Ben Jobrane
fa65496327 fix(stripe-extension): return yoga instance instead of node http server 2023-08-28 15:24:56 +01:00
Hassan Ben Jobrane
03777680c1 chore: add STRIPE_SECRET_KEY 2023-08-26 16:51:31 +01:00
Hassan Ben Jobrane
72c81207ff Merge pull request #2201 from nhost/chore/add-missing-changeset
chore: add missing changeset
2023-08-24 16:47:41 +01:00
Hassan Ben Jobrane
5ca2a394e8 chore: sync version in package.json 2023-08-24 16:30:18 +01:00
Hassan Ben Jobrane
e63b8da58a chore: add missing changeset 2023-08-24 16:27:38 +01:00
Hassan Ben Jobrane
bf8543cd34 Merge pull request #2195 from nhost/changeset-release/main
chore: update versions
2023-08-24 13:57:34 +01:00
github-actions[bot]
8a557bbd02 chore: update versions 2023-08-24 12:21:34 +00:00
Hassan Ben Jobrane
327e30b859 Merge pull request #2200 from nhost/chore/ignore-version-update-sveltekit-example
chore: sveltekit-example: changeset ignore dep version update
2023-08-24 13:18:25 +01:00
Hassan Ben Jobrane
bbfaf9732b chore: sveltekit-example: ignore changeset dep version update 2023-08-24 12:44:16 +01:00
Hassan Ben Jobrane
c064a53256 Merge pull request #2199 from nhost/chore/fix-dep-version
chore: fix dep version for sveltekit example
2023-08-24 12:03:57 +01:00
Hassan Ben Jobrane
ebda86f1f0 chore: sync lockfile 2023-08-24 11:53:41 +01:00
Hassan Ben Jobrane
8948be9d3d chore: fix dep version for sveltekit example 2023-08-24 11:50:47 +01:00
Hassan Ben Jobrane
54e9b141f1 Merge pull request #2191 from nhost/dbarroso/react-example
chore: react-apollo-example: add profile to allowedUrls
2023-08-24 10:56:45 +01:00
Hassan Ben Jobrane
dba71483df chore: add changeset 2023-08-24 10:41:58 +01:00
Hassan Ben Jobrane
77ef68232a Merge pull request #2197 from nhost/fix/webauthn-error-handling
fix(hasura-auth-js): make sure CodifiedError works on non v8 browsers
2023-08-24 10:26:46 +01:00
Hassan Ben Jobrane
8fbc7f9f95 Merge pull request #2198 from nhost/chore/remove-facebook-login
chore(react-apollo-example): remove facebook login
2023-08-24 10:26:31 +01:00
Hassan Ben Jobrane
ca9f0f6ae9 chore: show error toast when adding a security key fails 2023-08-23 23:48:45 +01:00
Hassan Ben Jobrane
e819903f1b chore: add changeset 2023-08-23 17:00:30 +01:00
Hassan Ben Jobrane
f780b17581 chore: remove facebook login from react apollo example 2023-08-23 16:59:44 +01:00
Hassan Ben Jobrane
032c0bd217 chore: add changeset 2023-08-23 16:51:14 +01:00
Hassan Ben Jobrane
5d278709cb fix(hasura-auth-js): make sure CodifiedError works on non v8 browsers 2023-08-23 16:25:57 +01:00
Hassan Ben Jobrane
3a012e089a Merge pull request #2182 from nhost/feat/add-sveltekit-example
feat: add sveltekit example
2023-08-23 12:14:38 +01:00
Hassan Ben Jobrane
7aed620e12 chore: fix tests 2023-08-23 11:39:29 +01:00
Hassan Ben Jobrane
dd0a5cf3c1 chore: fix lock file 2023-08-22 16:55:28 +01:00
Hassan Ben Jobrane
5187fd3a4b chore: dashboard tests 2023-08-22 16:49:26 +01:00
Hassan Ben Jobrane
d8dfd6bf80 Revert "chore: add missing dep for vitest"
This reverts commit 6ea6ad61db.
2023-08-22 16:16:20 +01:00
Hassan Ben Jobrane
6ea6ad61db chore: add missing dep for vitest 2023-08-22 16:04:24 +01:00
Hassan Ben Jobrane
fd0b904ed4 chore: fix dashboard e2e tests 2023-08-22 15:41:36 +01:00
Hassan Ben Jobrane
8989e314a6 fix: ignore conflict with linting and sveltekit build 2023-08-22 14:40:18 +01:00
Hassan Ben Jobrane
5b5a1219c5 fix: make sure linting runs correctly 2023-08-22 14:31:45 +01:00
Hassan Ben Jobrane
2fa828fef1 chore: cleanup .gitignore file 2023-08-22 13:10:59 +01:00
Hassan Ben Jobrane
d5ec69ac37 chore(examples-sveltekit): add a basic test 2023-08-22 13:07:06 +01:00
David Barroso
09fc852c3a asd 2023-08-21 13:13:41 +02:00
Hassan Ben Jobrane
27e1c90624 fix: change env to dynamic 2023-08-18 18:26:09 +01:00
Hassan Ben Jobrane
1cc53d550a chore: add changeset 2023-08-18 18:04:12 +01:00
Hassan Ben Jobrane
22d3f71e02 fix: make sure to include lib folder in sveltekit example 2023-08-18 17:58:29 +01:00
Hassan Ben Jobrane
010b816866 chore: fix README 2023-08-18 17:36:10 +01:00
Hassan Ben Jobrane
4a6e62e673 feat: add sveltekit example 2023-08-18 17:16:13 +01:00
111 changed files with 5332 additions and 663 deletions

View File

@@ -6,5 +6,5 @@
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
"ignore": ["@nhost-examples/sveltekit"]
}

56
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: "CodeQL"
on:
push: {}
pull_request: {}
schedule:
- cron: '20 23 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

3
.gitignore vendored
View File

@@ -19,10 +19,8 @@ logs/
coverage/
dist/
umd/
lib/
node_modules/
tmp/
.docz/
.pnpm-store
.turbo
.env
@@ -32,7 +30,6 @@ out/
# Custom
*.min.js
*.map
todo.md
# Config files that are not part of the repository root anymore. Should be removed in the future.
/.eslintignore

View File

@@ -1,5 +1,66 @@
# @nhost/dashboard
## 0.20.16
### Patch Changes
- df5b4302c: chore(dashboard): remove run feature flag
- bf4a1f6c2: feat(dashboard): fetch auth, postgres, hasura and storage versions from dashboard
- 34fc08ca7: fix(dashboard/run): show correct private registry in service details
- 885d10620: chore(dashboard): change feedback to contact us
## 0.20.15
### Patch Changes
- ed16c8b5d: feat(run): add a confirmation dialog when deleting a run service
- 216990888: fix(run): center loading indicator when selecting a project
## 0.20.14
### Patch Changes
- 9fbea9787: feat: add node18 announcement
## 0.20.13
### Patch Changes
- e84acf469: fix(run): handle subdomain undefined error when creating a new service
## 0.20.12
### Patch Changes
- b7c799d62: feat(run): add dialog to copy registry and URLs
## 0.20.11
### Patch Changes
- 8903e6abd: fix(dashboard): show correct egress limit in usage stats
## 0.20.10
### Patch Changes
- 666a75a23: feat(dashboard): add functions execution time and egress volume to usage stats
## 0.20.9
### Patch Changes
- 5e1e80aa8: fix(dashboard): show correct locales in user details
- @nhost/react-apollo@5.0.35
- @nhost/nextjs@1.13.37
## 0.20.8
### Patch Changes
- @nhost/react-apollo@5.0.34
- @nhost/nextjs@1.13.36
## 0.20.7
### Patch Changes

View File

@@ -30,7 +30,7 @@ test('should show a sidebar with menu items', async () => {
const navLocator = page.getByRole('navigation', { name: /main navigation/i });
await expect(navLocator).toBeVisible();
await expect(navLocator.getByRole('list').getByRole('listitem')).toHaveCount(
11,
12,
);
await expect(
navLocator.getByRole('link', { name: /overview/i }),

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/dashboard",
"version": "0.20.7",
"version": "0.20.16",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
@@ -106,6 +106,7 @@
"@testing-library/user-event": "^14.4.3",
"@types/ace": "^0.0.48",
"@types/bcryptjs": "^2.4.2",
"@types/jest": "^29.5.3",
"@types/lodash.debounce": "^4.0.7",
"@types/node": "^16.11.7",
"@types/pluralize": "^0.0.30",

View File

@@ -37,10 +37,10 @@ export interface AnnouncementContextProps {
// Note: You can define the active announcement here.
const announcement: AnnouncementType = {
id: 'nhost-run',
href: 'https://discord.com/invite/9V7Qb2U',
id: 'node-18',
href: 'https://github.com/nhost/nhost/discussions/2239',
content:
'Now you can bring custom and third-party OSS services to run alongside your Nhost projects',
"Starting October 1st, we're upgrading to Node.js 18 for improved performance, security, and stability. Learn more.",
};
export const AnnouncementContext = createContext<AnnouncementContextProps>({});

View File

@@ -0,0 +1,70 @@
import { Link } from '@/components/ui/v2/Link';
import { Text } from '@/components/ui/v2/Text';
import type { DetailedHTMLProps, HTMLProps } from 'react';
import { twMerge } from 'tailwind-merge';
export interface ContactUsProps
extends DetailedHTMLProps<HTMLProps<HTMLDivElement>, HTMLDivElement> {}
export default function FeedbackForm({ className, ...props }: ContactUsProps) {
return (
<div
className={twMerge(
'grid max-w-md grid-flow-row gap-2 py-4 px-5',
className,
)}
{...props}
>
<Text variant="h3" component="h2">
Contact us
</Text>
<Text>
To report issues with Nhost, please open a GitHub issue in the{' '}
<Link
href="https://github.com/nhost/nhost/issues/new"
target="_blank"
rel="noopener noreferrer"
underline="hover"
>
nhost/nhost
</Link>{' '}
repository.
</Text>
<Text>
For issues related to the CLI, please visit the{' '}
<Link
href="https://github.com/nhost/cli/issues/new"
target="_blank"
rel="noopener noreferrer"
underline="hover"
>
nhost/cli
</Link>{' '}
repository.
</Text>
<Text>
If you need assistance or have any questions, feel free to join us on{' '}
<Link
href="https://discord.com/invite/9V7Qb2U"
target="_blank"
rel="noopener noreferrer"
underline="hover"
>
Discord
</Link>
. Alternatively, if you prefer, you can also open a{' '}
<Link
href="https://github.com/nhost/nhost/discussions/new/choose"
target="_blank"
rel="noopener noreferrer"
underline="hover"
>
GitHub discussion
</Link>
.
</Text>
<Text>We&apos;re here to help, so don&apos;t hesitate to reach out!</Text>
</div>
);
}

View File

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

View File

@@ -1,148 +0,0 @@
import { Avatar } from '@/components/ui/v2/Avatar';
import { Button } from '@/components/ui/v2/Button';
import { Input } from '@/components/ui/v2/Input';
import { Text } from '@/components/ui/v2/Text';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useInsertFeedbackOneMutation } from '@/utils/__generated__/graphql';
import { useUserData } from '@nhost/nextjs';
import Image from 'next/image';
import type { DetailedHTMLProps, HTMLProps } from 'react';
import { useState } from 'react';
import { twMerge } from 'tailwind-merge';
export interface FeedbackFormProps
extends DetailedHTMLProps<HTMLProps<HTMLDivElement>, HTMLDivElement> {}
// TODO: Use `react-hook-form` here instead of the custom form implementation
export default function FeedbackForm({
className,
...props
}: FeedbackFormProps) {
const { currentProject } = useCurrentWorkspaceAndProject();
const [insertFeedback, { loading }] = useInsertFeedbackOneMutation();
const user = useUserData();
const [feedback, setFeedback] = useState('');
const [feedbackSent, setFeedbackSent] = useState(false);
function handleClose() {
setTimeout(() => {
setFeedbackSent(false);
}, 500);
}
async function handleSubmit(e: React.SyntheticEvent<HTMLFormElement>) {
e.preventDefault();
const feedbackWithProjectInfo = [
currentProject && `Project ID: ${currentProject.id}`,
typeof window !== 'undefined' && `URL: ${window.location.href}`,
feedback,
]
.filter(Boolean)
.join('\n\n');
try {
await insertFeedback({
variables: {
feedback: {
feedback: feedbackWithProjectInfo,
},
},
});
setFeedbackSent(true);
setFeedback('');
} catch (error) {
// TODO: Display error to user and use a logging solution
}
}
if (feedbackSent) {
return (
<div
className={twMerge(
'grid max-w-md grid-flow-row justify-center gap-4 py-4 px-5 text-center',
className,
)}
{...props}
>
<Image
src="/assets/FeedbackReceived.svg"
alt="Light bulb with a checkmark"
width={72}
height={72}
/>
<div className="grid grid-flow-row gap-2">
<Text variant="h3" component="h2" className="text-center">
Feedback Received
</Text>
<Text>
Thanks for sending us your thoughts! Feel free to send more feedback
as you explore the beta, and stay tuned for updates.
</Text>
</div>
<Button
variant="outlined"
color="secondary"
className="mt-2 text-sm+ font-normal"
onClick={handleClose}
>
Go Back
</Button>
</div>
);
}
return (
<div
className={twMerge(
'grid max-w-md grid-flow-row gap-2 py-4 px-5',
className,
)}
{...props}
>
<Text variant="h3" component="h2">
Leave Feedback
</Text>
<Text>
Nhost is still in beta and not everything is in place yet, but we&apos;d
love to know what you think of it so far.
</Text>
<form onSubmit={handleSubmit} className="grid grid-flow-row gap-2">
<div className="grid grid-flow-col place-content-between gap-2">
<Text className="font-medium">
What do you think we should improve?
</Text>
<Avatar
className="h-6 w-6 rounded-full"
alt={user?.displayName}
src={user?.avatarUrl}
>
{user?.displayName}
</Avatar>
</div>
<Input
multiline
value={feedback}
onChange={(event) => setFeedback(event.target.value)}
placeholder="Your feedback"
rows={6}
required
fullWidth
hideEmptyHelperText
/>
<Button type="submit" disabled={!feedback} loading={loading}>
Send Feedback
</Button>
</form>
</div>
);
}

View File

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

View File

@@ -1,4 +1,4 @@
import { FeedbackForm } from '@/components/common/FeedbackForm';
import { ContactUs } from '@/components/common/ContactUs';
import { NavLink } from '@/components/common/NavLink';
import { AccountMenu } from '@/components/layout/AccountMenu';
import { Breadcrumbs } from '@/components/layout/Breadcrumbs';
@@ -75,14 +75,14 @@ export default function Header({ className, ...props }: HeaderProps) {
hideChevron
className="rounded-md px-2.5 py-1.5 text-sm motion-safe:transition-colors"
>
Feedback
Contact us
</Dropdown.Trigger>
<Dropdown.Content
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
>
<FeedbackForm className="max-w-md" />
<ContactUs className="max-w-md" />
</Dropdown.Content>
</Dropdown.Root>
)}

View File

@@ -1,4 +1,4 @@
import { FeedbackForm } from '@/components/common/FeedbackForm';
import { ContactUs } from '@/components/common/ContactUs';
import { NavLink } from '@/components/common/NavLink';
import { ThemeSwitcher } from '@/components/common/ThemeSwitcher';
import { Nav } from '@/components/presentational/Nav';
@@ -171,7 +171,7 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
className="w-full"
role={undefined}
>
<ListItem.Text>Feedback</ListItem.Text>
<ListItem.Text>Contact us</ListItem.Text>
</ListItem.Button>
</ListItem.Root>
</Dropdown.Trigger>
@@ -180,7 +180,7 @@ export default function MobileNav({ className, ...props }: MobileNavProps) {
transformOrigin={{ vertical: 'top', horizontal: 'center' }}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
>
<FeedbackForm className="max-w-md" />
<ContactUs className="max-w-md" />
</Dropdown.Content>
</Dropdown.Root>
)}

View File

@@ -7,14 +7,15 @@ import MaterialLinearProgress, {
export interface LinearProgressProps extends MaterialLinearProgressProps {}
const LinearProgress = styled(MaterialLinearProgress)(({ theme }) => ({
const LinearProgress = styled(MaterialLinearProgress)(({ theme, value }) => ({
height: 12,
borderRadius: 1,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[300],
},
[`& .${linearProgressClasses.bar}`]: {
backgroundColor: theme.palette.primary.main,
backgroundColor:
value >= 100 ? theme.palette.error.dark : theme.palette.primary.main,
},
}));

View File

@@ -7,7 +7,9 @@ import { filterOptions } from '@/components/ui/v2/Autocomplete';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import {
GetAuthenticationSettingsDocument,
Software_Type_Enum,
useGetAuthenticationSettingsQuery,
useGetSoftwareVersionsQuery,
useUpdateConfigMutation,
} from '@/generated/graphql';
import { getToastStyleProps } from '@/utils/constants/settings';
@@ -28,15 +30,6 @@ export type AuthServiceVersionFormValues = Yup.InferType<
typeof validationSchema
>;
const AVAILABLE_AUTH_VERSIONS = [
'0.21.2',
'0.20.1',
'0.20.0',
'0.19.3',
'0.19.2',
'0.19.1',
];
export default function AuthServiceVersionSettings() {
const { maintenanceActive } = useUI();
const { currentProject } = useCurrentWorkspaceAndProject();
@@ -49,9 +42,16 @@ export default function AuthServiceVersionSettings() {
fetchPolicy: 'cache-only',
});
const { data: authVersionsData } = useGetSoftwareVersionsQuery({
variables: {
software: Software_Type_Enum.Auth,
},
});
const { version } = data?.config?.auth || {};
const versions = authVersionsData?.softwareVersions || [];
const availableVersions = Array.from(
new Set(AVAILABLE_AUTH_VERSIONS).add(version),
new Set(versions.map((el) => el.version)).add(version),
)
.sort()
.reverse()

View File

@@ -38,6 +38,10 @@ query GetAuthenticationSettings($appId: uuid!) {
default
rating
}
locale {
allowed
default
}
}
version
}

View File

@@ -24,6 +24,7 @@ import { copy } from '@/utils/copy';
import { getServerError } from '@/utils/getServerError';
import {
RemoteAppGetUsersDocument,
useGetProjectLocalesQuery,
useGetRolesPermissionsQuery,
useUpdateRemoteAppUserMutation,
} from '@/utils/__generated__/graphql';
@@ -146,6 +147,14 @@ export default function EditUserForm({
dataRoles?.config?.auth?.user?.roles?.allowed,
);
const { data } = useGetProjectLocalesQuery({
variables: {
appId: currentProject?.id,
},
});
const allowedLocales = data?.config?.auth?.user?.locale?.allowed || [];
/**
* This will change the `disabled` field in the user to its opposite.
* If the user is disabled, it will be enabled and vice versa.
@@ -374,12 +383,11 @@ export default function EditUserForm({
error={!!errors.locale}
helperText={errors?.locale?.message}
>
<Option key="en" value="en">
en
</Option>
<Option key="fr" value="fr">
fr
</Option>
{allowedLocales.map((locale) => (
<Option key={locale} value={locale}>
{locale}
</Option>
))}
</ControlledSelect>
</Box>
<Box

View File

@@ -2,8 +2,9 @@ import permissionVariablesQuery from '@/tests/msw/mocks/graphql/permissionVariab
import hasuraMetadataQuery from '@/tests/msw/mocks/rest/hasuraMetadataQuery';
import tableQuery from '@/tests/msw/mocks/rest/tableQuery';
import { render, screen } from '@/tests/testUtils';
import '@testing-library/jest-dom';
import { setupServer } from 'msw/node';
import { test, vi } from 'vitest';
import { afterAll, afterEach, beforeAll, test, vi } from 'vitest';
import ColumnAutocomplete from './ColumnAutocomplete';
const server = setupServer(

View File

@@ -7,7 +7,9 @@ import { filterOptions } from '@/components/ui/v2/Autocomplete';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import {
GetPostgresSettingsDocument,
Software_Type_Enum,
useGetPostgresSettingsQuery,
useGetSoftwareVersionsQuery,
useUpdateConfigMutation,
} from '@/generated/graphql';
import { getToastStyleProps } from '@/utils/constants/settings';
@@ -30,15 +32,6 @@ export type DatabaseServiceVersionFormValues = Yup.InferType<
typeof validationSchema
>;
const AVAILABLE_POSTGRES_VERSIONS = [
'14.6-20230705-1',
'14.6-20230613-1',
'14.6-20230525',
'14.6-20230406-2',
'14.6-20230406-1',
'14.6-20230404',
];
export default function DatabaseServiceVersionSettings() {
const { maintenanceActive } = useUI();
const { currentProject } = useCurrentWorkspaceAndProject();
@@ -51,9 +44,16 @@ export default function DatabaseServiceVersionSettings() {
fetchPolicy: 'cache-only',
});
const { data: databaseVersionsData } = useGetSoftwareVersionsQuery({
variables: {
software: Software_Type_Enum.PostgreSql,
},
});
const { version } = data?.config?.postgres || {};
const databaseVersions = databaseVersionsData?.softwareVersions || [];
const availableVersions = Array.from(
new Set(AVAILABLE_POSTGRES_VERSIONS).add(version),
new Set(databaseVersions.map((el) => el.version)).add(version),
)
.sort()
.reverse()

View File

@@ -7,7 +7,9 @@ import { filterOptions } from '@/components/ui/v2/Autocomplete';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import {
GetHasuraSettingsDocument,
Software_Type_Enum,
useGetHasuraSettingsQuery,
useGetSoftwareVersionsQuery,
useUpdateConfigMutation,
} from '@/generated/graphql';
import { getToastStyleProps } from '@/utils/constants/settings';
@@ -30,16 +32,6 @@ export type HasuraServiceVersionFormValues = Yup.InferType<
typeof validationSchema
>;
const AVAILABLE_HASURA_VERSIONS = [
'v2.29.0-ce',
'v2.28.2-ce',
'v2.27.0-ce',
'v2.25.1-ce',
'v2.25.0-ce',
'v2.24.1-ce',
'v2.15.2',
];
export default function HasuraServiceVersionSettings() {
const { maintenanceActive } = useUI();
const { currentProject, refetch: refetchWorkspaceAndProject } =
@@ -53,9 +45,16 @@ export default function HasuraServiceVersionSettings() {
fetchPolicy: 'cache-only',
});
const { data: hasuraVersionsData } = useGetSoftwareVersionsQuery({
variables: {
software: Software_Type_Enum.Hasura,
},
});
const { version } = data?.config?.hasura || {};
const versions = hasuraVersionsData?.softwareVersions || [];
const availableVersions = Array.from(
new Set(AVAILABLE_HASURA_VERSIONS).add(version),
new Set(versions.map((el) => el.version)).add(version),
)
.sort()
.reverse()

View File

@@ -1,4 +1,4 @@
import { FeedbackForm } from '@/components/common/FeedbackForm';
import { ContactUs } from '@/components/common/ContactUs';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Button } from '@/components/ui/v2/Button';
import { Dropdown } from '@/components/ui/v2/Dropdown';
@@ -99,7 +99,7 @@ export default function AppLoader({
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
transformOrigin={{ vertical: 'top', horizontal: 'center' }}
>
<FeedbackForm />
<ContactUs />
</Dropdown.Content>
</Dropdown.Root>
)}

View File

@@ -1,4 +1,4 @@
import { FeedbackForm } from '@/components/common/FeedbackForm';
import { ContactUs } from '@/components/common/ContactUs';
import { Container } from '@/components/layout/Container';
import { Modal } from '@/components/ui/v1/Modal';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
@@ -250,7 +250,7 @@ export default function ApplicationErrored() {
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
transformOrigin={{ vertical: 'top', horizontal: 'center' }}
>
<FeedbackForm />
<ContactUs />
</Dropdown.Content>
</Dropdown.Root>

View File

@@ -1,4 +1,4 @@
import { FeedbackForm } from '@/components/common/FeedbackForm';
import { ContactUs } from '@/components/common/ContactUs';
import { Container } from '@/components/layout/Container';
import { Modal } from '@/components/ui/v1/Modal';
import { Button } from '@/components/ui/v2/Button';
@@ -65,7 +65,7 @@ export default function ApplicationUnknown() {
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
transformOrigin={{ vertical: 'top', horizontal: 'center' }}
>
<FeedbackForm />
<ContactUs />
</Dropdown.Content>
</Dropdown.Root>

View File

@@ -0,0 +1,116 @@
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { Checkbox } from '@/components/ui/v2/Checkbox';
import { Text } from '@/components/ui/v2/Text';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { getToastStyleProps } from '@/utils/constants/settings';
import {
useDeleteRunServiceConfigMutation,
useDeleteRunServiceMutation,
} from '@/utils/__generated__/graphql';
import type { ApolloError } from '@apollo/client';
import { type RunService } from 'pages/[workspaceSlug]/[appSlug]/services';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { twMerge } from 'tailwind-merge';
export interface DeleteServiceModalProps {
service: RunService;
onDelete?: () => Promise<any>;
close: () => void;
}
export default function DeleteServiceModal({
service,
onDelete,
close,
}: DeleteServiceModalProps) {
const [remove, setRemove] = useState(false);
const [loadingRemove, setLoadingRemove] = useState(false);
const { currentProject } = useCurrentWorkspaceAndProject();
const [deleteRunService] = useDeleteRunServiceMutation();
const [deleteRunServiceConfig] = useDeleteRunServiceConfigMutation();
const deleteServiceAndConfig = async () => {
await deleteRunService({ variables: { serviceID: service.id } });
await deleteRunServiceConfig({
variables: { appID: currentProject.id, serviceID: service.id },
});
await onDelete?.();
close();
};
async function handleClick() {
setLoadingRemove(true);
await toast.promise(
deleteServiceAndConfig(),
{
loading: 'Deleting the service...',
success: `The service has been deleted successfully.`,
error: (arg: ApolloError) => {
// we need to get the internal error message from the GraphQL error
const { internal } = arg.graphQLErrors[0]?.extensions || {};
const { message } = (internal as Record<string, any>)?.error || {};
// we use the default Apollo error message if we can't find the
// internal error message
return (
message ||
arg.message ||
'An error occurred while deleting the service. Please try again.'
);
},
},
getToastStyleProps(),
);
}
return (
<Box className={twMerge('w-full rounded-lg p-6 text-left')}>
<div className="grid grid-flow-row gap-1">
<Text variant="h3" component="h2">
Delete Service {service?.config?.name}
</Text>
<Text variant="subtitle2">
Are you sure you want to delete this service?
</Text>
<Text
variant="subtitle2"
className="font-bold"
sx={{ color: (theme) => `${theme.palette.error.main} !important` }}
>
This cannot be undone.
</Text>
<Box className="my-4">
<Checkbox
id="accept-1"
label={`I'm sure I want to delete ${service?.config?.name}`}
className="py-2"
checked={remove}
onChange={(_event, checked) => setRemove(checked)}
aria-label="Confirm Delete Project #1"
/>
</Box>
<div className="grid grid-flow-row gap-2">
<Button
color="error"
onClick={handleClick}
disabled={!remove}
loading={loadingRemove}
>
Delete Service
</Button>
<Button variant="outlined" color="secondary" onClick={close}>
Cancel
</Button>
</div>
</div>
</Box>
);
}

View File

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

View File

@@ -14,7 +14,6 @@ import type { SvgIconProps } from '@/components/ui/v2/icons/SvgIcon';
import { UserIcon } from '@/components/ui/v2/icons/UserIcon';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
import { useHypertune } from '@/hooks/useHypertune';
import type { ReactElement } from 'react';
export interface ProjectRoute {
@@ -58,26 +57,8 @@ export interface ProjectRoute {
export default function useProjectRoutes() {
const isPlatform = useIsPlatform();
const { maintenanceActive } = useUI();
const {
currentWorkspace,
currentProject,
loading: currentProjectLoading,
} = useCurrentWorkspaceAndProject();
const hypertune = useHypertune();
const enableServices =
currentWorkspace &&
hypertune
.root({
context: {
workSpace: {
id: currentWorkspace.id,
},
},
})
.enableServices({})
.get(false);
const { currentProject, loading: currentProjectLoading } =
useCurrentWorkspaceAndProject();
const nhostRoutes: ProjectRoute[] = [
{
@@ -118,7 +99,7 @@ export default function useProjectRoutes() {
},
];
let allRoutes: ProjectRoute[] = [
const allRoutes: ProjectRoute[] = [
{
relativePath: '/',
exact: true,
@@ -156,18 +137,14 @@ export default function useProjectRoutes() {
label: 'Storage',
icon: <StorageIcon />,
},
];
if (enableServices) {
allRoutes.push({
{
relativePath: '/services',
exact: false,
label: 'Run',
icon: <ServicesIcon />,
});
}
allRoutes = [...allRoutes, ...nhostRoutes];
},
...nhostRoutes,
];
return {
nhostRoutes,

View File

@@ -41,11 +41,6 @@ export default function OverviewMetrics() {
numberOfDecimals: 0,
}),
},
{
label: 'Egress Volume',
tooltip: 'Amount of data your services have sent to users',
value: prettifySize(data?.egressVolume?.value || 0),
},
{
label: 'Logs',
tooltip: 'Amount of logs stored',

View File

@@ -96,7 +96,7 @@ export function OverviewUsageMetrics() {
remoteAppMetricsData?.filesAggregate?.aggregate?.sum?.size || 0;
const totalStorage = currentProject?.plan?.isFree
? 1 * 1000 ** 3 // 1 GB
: 10 * 1000 ** 3; // 10 GB
: 50 * 1000 ** 3; // 10 GB
// metrics for users
const usedUsers = remoteAppMetricsData?.usersAggregate?.aggregate?.count || 0;
@@ -105,6 +105,16 @@ export function OverviewUsageMetrics() {
// metrics for functions
const usedFunctions = functionsInfoData?.app.metadataFunctions.length || 0;
const totalFunctions = currentProject?.plan?.isFree ? 10 : 50;
const usedFunctionsDuration = projectMetrics?.functionsDuration.value || 0;
const totalFunctionsDuration = currentProject?.plan?.isFree
? 3600 // 1 hour
: 3600 * 10; // 10 hours
// metrics for egress
const usedEgressVolume = projectMetrics?.egressVolume.value || 0;
const totalEgressVolume = currentProject?.plan?.isFree
? 5 * 1000 ** 3 // 5 GB
: 50 * 1000 ** 3; // 50 GB
if (metricsLoading) {
return (
@@ -112,7 +122,9 @@ export function OverviewUsageMetrics() {
<UsageProgress label="Database" percentage={0} />
<UsageProgress label="Storage" percentage={0} />
<UsageProgress label="Users" percentage={0} />
<UsageProgress label="Functions" percentage={0} />
<UsageProgress label="Number of Functions" percentage={0} />
<UsageProgress label="Functions Execution Time" percentage={0} />
<UsageProgress label="Egress Volume" percentage={0} />
</div>
);
}
@@ -139,6 +151,18 @@ export function OverviewUsageMetrics() {
used={usedFunctions}
percentage={100}
/>
<UsageProgress
label="Functions"
used={usedFunctionsDuration}
percentage={100}
/>
<UsageProgress
label="Egress"
used={usedEgressVolume}
percentage={100}
/>
</div>
);
}
@@ -167,11 +191,25 @@ export function OverviewUsageMetrics() {
/>
<UsageProgress
label="Functions"
label="Number of Functions"
used={usedFunctions}
total={totalFunctions}
percentage={(usedFunctions / totalFunctions) * 100}
/>
<UsageProgress
label="Functions Execution Time"
used={Math.trunc(usedFunctionsDuration)}
total={`${totalFunctionsDuration} seconds`}
percentage={(usedFunctionsDuration / totalFunctionsDuration) * 100}
/>
<UsageProgress
label="Egress Volume"
used={prettifySize(usedEgressVolume)}
total={prettifySize(totalEgressVolume)}
percentage={(usedEgressVolume / totalEgressVolume) * 100}
/>
</div>
);
}

View File

@@ -1,4 +1,5 @@
import type { PermissionVariable } from '@/types/application';
import { expect, test } from 'vitest';
import getAllPermissionVariables from './getAllPermissionVariables';
test('should convert permission variable object to array', () => {

View File

@@ -1,4 +1,4 @@
import { test } from 'vitest';
import { expect, test } from 'vitest';
import getAllocatedResources from './getAllocatedResources';
test('should return the total number of allocated resources', () => {

View File

@@ -43,6 +43,7 @@ import { toast } from 'react-hot-toast';
import { parse } from 'shell-quote';
import * as Yup from 'yup';
import { ServiceConfirmationDialog } from './components/ServiceConfirmationDialog';
import { ServiceDetailsDialog } from './components/ServiceDetailsDialog';
export enum PortTypes {
HTTP = 'http',
@@ -94,7 +95,7 @@ export interface ServiceFormProps extends DialogFormProps {
/**
* if there is initialData then it's an update operation
*/
initialData?: ServiceFormValues;
initialData?: ServiceFormValues & { subdomain?: string }; // subdomain is only set on the backend
/**
* Function to be called when the operation is cancelled.
@@ -119,6 +120,10 @@ export default function ServiceForm({
const { currentProject } = useCurrentWorkspaceAndProject();
const [insertRunServiceConfig] = useInsertRunServiceConfigMutation();
const [replaceRunServiceConfig] = useReplaceRunServiceConfigMutation();
const [detailsServiceId, setDetailsServiceId] = useState('');
const [detailsServiceSubdomain, setDetailsServiceSubdomain] = useState(
initialData?.subdomain,
);
const [createServiceFormError, setCreateServiceFormError] =
useState<Error | null>(null);
@@ -196,11 +201,13 @@ export default function ServiceForm({
config,
},
});
setDetailsServiceId(serviceID);
} else {
// Insert service config
const {
data: {
insertRunService: { id: newServiceID },
insertRunService: { id: newServiceID, subdomain },
},
} = await insertRunService({
variables: {
@@ -227,6 +234,9 @@ export default function ServiceForm({
},
},
});
setDetailsServiceId(newServiceID);
setDetailsServiceSubdomain(subdomain);
}
};
@@ -254,8 +264,6 @@ export default function ServiceForm({
getToastStyleProps(),
);
// await refetchWorkspaceAndProject();
// refestch the services
onSubmit?.();
} catch {
// Note: The toast will handle the error.
@@ -277,6 +285,28 @@ export default function ServiceForm({
});
};
useEffect(() => {
(async () => {
if (detailsServiceId) {
openDialog({
title: 'Service Details',
component: (
<ServiceDetailsDialog
serviceID={detailsServiceId}
subdomain={detailsServiceSubdomain}
ports={formValues.ports}
/>
),
props: {
PaperProps: {
className: 'max-w-2xl',
},
},
});
}
})();
}, [detailsServiceId, detailsServiceSubdomain, formValues, openDialog]);
const pricingExplanation = () => {
const vCPUs = `${formValues.compute.cpu / RESOURCE_VCPU_MULTIPLIER} vCPUs`;
const mem = `${formValues.compute.memory} MiB Mem`;

View File

@@ -32,20 +32,20 @@ export default function PortsFormSection() {
name: 'ports',
});
const formValues = useWatch<ServiceFormValues>();
const formValues = useWatch<ServiceFormValues & { subdomain: string }>();
const onChangePortType = (value: string | undefined, index: number) =>
setValue(`ports.${index}.type`, value as PortTypes);
const showURL = (index: number) =>
formValues.subdomain &&
formValues.ports[index]?.type === PortTypes.HTTP &&
formValues.ports[index]?.publish;
const getPortURL = (_port: string | number, _name: string) => {
const getPortURL = (_port: string | number, subdomain: string) => {
const port = Number(_port) > 0 ? Number(_port) : '[port]';
const name = _name && _name.length > 0 ? _name : '[name]';
return `https://${currentProject?.subdomain}-${name}-${port}.svc.${currentProject?.region.awsName}.${currentProject?.region.domain}`;
return `https://${subdomain}-${port}.svc.${currentProject?.region.awsName}.${currentProject?.region.domain}`;
};
return (
@@ -144,7 +144,7 @@ export default function PortsFormSection() {
title="URL"
value={getPortURL(
formValues.ports[index]?.port,
formValues.name,
formValues.subdomain,
)}
/>
)}

View File

@@ -0,0 +1,75 @@
import { useDialog } from '@/components/common/DialogProvider';
import { Button } from '@/components/ui/v2/Button';
import { Text } from '@/components/ui/v2/Text';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { InfoCard } from '@/features/projects/overview/components/InfoCard';
import type { ConfigRunServicePort } from '@/utils/__generated__/graphql';
export interface ServiceDetailsDialogProps {
/**
* The id of the service
*/
serviceID: string;
/**
* The subdomain of the service
*/
subdomain: string;
/**
* The service ports
* We use partial here because `port` is set as required in ConfigRunServicePort
*/
ports: Partial<ConfigRunServicePort>[];
}
export default function ServiceDetailsDialog({
serviceID,
subdomain,
ports,
}: ServiceDetailsDialogProps) {
const { currentProject } = useCurrentWorkspaceAndProject();
const { closeDialog } = useDialog();
const getPortURL = (_port: string | number) => {
const port = Number(_port) > 0 ? Number(_port) : '[port]';
return `https://${subdomain}-${port}.svc.${currentProject?.region.awsName}.${currentProject?.region.domain}`;
};
return (
<div className="flex flex-col gap-4 px-6 pb-6">
<div className="flex flex-col gap-2">
<Text color="secondary">Private registry</Text>
<InfoCard
title=""
value={`registry.${currentProject.region.awsName}.${currentProject.region.domain}/${serviceID}`}
/>
</div>
{ports?.length > 0 && (
<div className="flex flex-col gap-2">
<Text color="secondary">Ports</Text>
{ports
.filter((port) => port.publish)
.map((port) => (
<InfoCard
title={`${port.type}:${port.port}`}
value={getPortURL(port.port)}
/>
))}
</div>
)}
<Button
className="w-full"
color="primary"
onClick={() => closeDialog()}
autoFocus
>
OK
</Button>
</div>
);
}

View File

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

View File

@@ -10,21 +10,14 @@ import { TrashIcon } from '@/components/ui/v2/icons/TrashIcon';
import { UserIcon } from '@/components/ui/v2/icons/UserIcon';
import { Text } from '@/components/ui/v2/Text';
import { Tooltip } from '@/components/ui/v2/Tooltip';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { DeleteServiceModal } from '@/features/projects/common/components/DeleteServiceModal';
import {
ServiceForm,
type PortTypes,
} from '@/features/services/components/ServiceForm';
import { getToastStyleProps } from '@/utils/constants/settings';
import { copy } from '@/utils/copy';
import {
useDeleteRunServiceConfigMutation,
useDeleteRunServiceMutation,
} from '@/utils/__generated__/graphql';
import type { ApolloError } from '@apollo/client';
import { formatDistanceToNow } from 'date-fns';
import type { RunService } from 'pages/[workspaceSlug]/[appSlug]/services';
import { toast } from 'react-hot-toast';
interface ServicesListProps {
/**
@@ -51,16 +44,7 @@ export default function ServicesList({
onCreateOrUpdate,
onDelete,
}: ServicesListProps) {
const { openDrawer } = useDialog();
const [deleteRunService] = useDeleteRunServiceMutation();
const { currentProject } = useCurrentWorkspaceAndProject();
const [deleteRunServiceConfig] = useDeleteRunServiceConfigMutation();
const deleteServiceAndConfig = async (appID: string, serviceID: string) => {
await deleteRunService({ variables: { serviceID } });
await deleteRunServiceConfig({ variables: { appID, serviceID } });
await onDelete?.();
};
const { openDrawer, openDialog, closeDialog } = useDialog();
const viewService = async (service: RunService) => {
openDrawer({
@@ -76,6 +60,7 @@ export default function ServicesList({
initialData={{
...service.config,
image: service.config?.image?.image,
subdomain: service.subdomain,
command: service.config?.command?.join(' '),
ports: service.config?.ports?.map((item) => ({
port: item.port,
@@ -95,28 +80,16 @@ export default function ServicesList({
});
};
const deleteService = async (serviceID: string) => {
await toast.promise(
deleteServiceAndConfig(currentProject.id, serviceID),
{
loading: 'Deleteing the service...',
success: `The service has been deleted successfully.`,
error: (arg: ApolloError) => {
// we need to get the internal error message from the GraphQL error
const { internal } = arg.graphQLErrors[0]?.extensions || {};
const { message } = (internal as Record<string, any>)?.error || {};
// we use the default Apollo error message if we can't find the
// internal error message
return (
message ||
arg.message ||
'An error occurred while deleting the service. Please try again.'
);
},
},
getToastStyleProps(),
);
const deleteService = async (service: RunService) => {
openDialog({
component: (
<DeleteServiceModal
service={service}
close={closeDialog}
onDelete={onDelete}
/>
),
});
};
return (
@@ -203,7 +176,7 @@ export default function ServicesList({
<Dropdown.Item
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
sx={{ color: 'error.main' }}
onClick={() => deleteService(service.id)}
onClick={() => deleteService(service)}
>
<TrashIcon className="h-4 w-4" />
<Text className="font-medium" color="error">

View File

@@ -7,6 +7,8 @@ import { filterOptions } from '@/components/ui/v2/Autocomplete';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import {
GetStorageSettingsDocument,
Software_Type_Enum,
useGetSoftwareVersionsQuery,
useGetStorageSettingsQuery,
useUpdateConfigMutation,
} from '@/generated/graphql';
@@ -30,8 +32,6 @@ export type StorageServiceVersionFormValues = Yup.InferType<
typeof validationSchema
>;
const AVAILABLE_STORAGE_VERSIONS = ['0.3.5', '0.3.4', '0.3.3', '0.3.2'];
export default function StorageServiceVersionSettings() {
const { maintenanceActive } = useUI();
const { currentProject } = useCurrentWorkspaceAndProject();
@@ -44,9 +44,16 @@ export default function StorageServiceVersionSettings() {
fetchPolicy: 'cache-only',
});
const { data: storageVersionsData } = useGetSoftwareVersionsQuery({
variables: {
software: Software_Type_Enum.Storage,
},
});
const { version } = data?.config?.storage || {};
const versions = storageVersionsData?.softwareVersions || [];
const availableVersions = Array.from(
new Set(AVAILABLE_STORAGE_VERSIONS).add(version),
new Set(versions.map((el) => el.version)).add(version),
)
.sort()
.reverse()

View File

@@ -0,0 +1,12 @@
query getProjectLocales($appId: uuid!) {
config(appID: $appId, resolve: true) {
auth {
user {
locale {
allowed
default
}
}
}
}
}

View File

@@ -17,6 +17,9 @@ query GetProjectMetrics(
) {
value
}
functionsDuration: getFunctionsDuration(appID: $appId, from: $from, to: $to) {
value
}
postgresVolumeCapacity: getPostgresVolumeCapacity(appID: $appId) {
value
}

View File

@@ -0,0 +1,9 @@
query getSoftwareVersions($software: software_type_enum!) {
softwareVersions(
where: { software: { _eq: $software } }
order_by: { version: desc }
) {
version
software
}
}

View File

@@ -1,6 +1,7 @@
query getRunService($id: uuid!, $resolve: Boolean!) {
runService(id: $id) {
id
subdomain
config(resolve: $resolve) {
name
image {

View File

@@ -9,6 +9,7 @@ query getRunServices(
id
createdAt
updatedAt
subdomain
config(resolve: $resolve) {
name
image {

View File

@@ -1,6 +1,6 @@
mutation insertRunService($object: run_service_insert_input!) {
insertRunService(object: $object) {
id
appID
subdomain
}
}

View File

@@ -133,10 +133,12 @@ export default function SelectWorkspaceAndProject() {
if (loading) {
return (
<ActivityIndicator
delay={500}
label="Loading workspaces and projects..."
/>
<div className="flex w-full justify-center">
<ActivityIndicator
delay={500}
label="Loading workspaces and projects..."
/>
</div>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,17 @@
# @nhost/docs
## 0.6.0
### Minor Changes
- 9aaa407d2: Fix messaging around Run's private beta
## 0.5.1
### Patch Changes
- 819e1e97d: update fqdn format for nhost run
## 0.5.0
### Minor Changes

View File

@@ -112,8 +112,8 @@ Currently, only services of type `http` can be exposed to the internet.
2. Once the service of type `http` is published, you can connect to it using a URL with the following format:
`https://<subdomain>-<svc_name>-<port>.svc.<region>.nhost.run`
`https://<run_service_subdomain>-<port>.svc.<region>.nhost.run`
For example:
`https://zlbmqjfczuwqvsquujno-mysvc-3000.svc.eu-central-1.nhost.run`
`https://zlbmqjfczuwqvsquujno-3000.svc.eu-central-1.nhost.run`

View File

@@ -12,7 +12,7 @@ Nhost Run enables you to seamlessly incorporate your custom software within your
![Nhost Architecture Diagram](/img/run/overview.png)
:::info
Currently Nhost Run is in private beta. If you are interested in using this service feel free to reach out to us via [email](mailto:support@nhost.io), [GitHub](https://github.com/nhost/nhost/issues), or [Discord](https://discord.com/invite/9V7Qb2U)
Currently Nhost Run is in public beta. If you find any bugs or if you have any feedback and suggestions, please reach out to us via [email](mailto:support@nhost.io), [GitHub](https://github.com/nhost/nhost/issues), or [Discord](https://discord.com/invite/9V7Qb2U)
:::
## Use Cases

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/docs",
"version": "0.5.0",
"version": "0.6.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",

View File

@@ -1,5 +1,14 @@
# @nhost-examples/react-apollo
## 0.1.15
### Patch Changes
- dba71483d: chore: react-apollo-example: add profile to allowedUrls
- e819903f1: chore: remove facebook login
- @nhost/react@2.0.30
- @nhost/react-apollo@5.0.34
## 0.1.14
### Patch Changes

View File

@@ -32,6 +32,7 @@ version = '0.21.2'
[auth.redirections]
clientUrl = 'https://react-apollo.example.nhost.io/'
allowedUrls = ['https://react-apollo.example.nhost.io/profile']
[auth.signUp]
enabled = true

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/react-apollo",
"version": "0.1.14",
"version": "0.1.15",
"private": true,
"dependencies": {
"@apollo/client": "^3.7.14",

View File

@@ -1,11 +1,11 @@
import { FaFacebook, FaGithub, FaGoogle } from 'react-icons/fa/index.js'
import { FaGithub, FaGoogle } from 'react-icons/fa/index.js'
import { useProviderLink } from '@nhost/react'
import AuthLink from './AuthLink'
export default function OauthLinks() {
const { github, google, facebook } = useProviderLink({ redirectTo: window.location.origin })
const { github, google } = useProviderLink({ redirectTo: window.location.origin })
return (
<>
<AuthLink leftIcon={<FaGithub />} link={github} color="#333">
@@ -14,9 +14,6 @@ export default function OauthLinks() {
<AuthLink leftIcon={<FaGoogle />} link={google} color="#de5246">
Continue with Google
</AuthLink>
<AuthLink leftIcon={<FaFacebook />} link={facebook} color="#3b5998">
Continue with Facebook
</AuthLink>
</>
)
}

View File

@@ -5,6 +5,7 @@ import { RemoveSecurityKeyMutation, SecurityKeysQuery } from 'src/generated'
import { gql, useMutation } from '@apollo/client'
import { ActionIcon, Button, Card, SimpleGrid, Table, TextInput, Title } from '@mantine/core'
import { useInputState } from '@mantine/hooks'
import { showNotification } from '@mantine/notifications'
import { useAddSecurityKey, useUserId } from '@nhost/react'
import { useAuthQuery } from '@nhost/react-apollo'
@@ -42,9 +43,14 @@ export const SecurityKeys: React.FC = () => {
const addKey = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const { key, error } = await add(nickname)
if (error) {
const { key, isError, error } = await add(nickname)
if (isError) {
console.log(error)
showNotification({
color: 'red',
title: 'Error',
message: error?.message || null
})
} else {
setNickname('')
}

View File

@@ -1,5 +1,13 @@
# @nhost-examples/serverless-functions
## 0.0.9
### Patch Changes
- 45759c4d4: fix(stripe-graphql-js): fix stripe GraphQL extension export issue in serverless functions
- Updated dependencies [45759c4d4]
- @nhost/stripe-graphql-js@1.0.5
## 0.0.8
### Patch Changes

View File

@@ -17,6 +17,4 @@ https://github.com/nhost/nhost/tree/main/integrations/stripe-graphql-js
import { createStripeGraphQLServer } from '@nhost/stripe-graphql-js'
const server = createStripeGraphQLServer()
export default server
export default createStripeGraphQLServer()

View File

@@ -1,4 +1,7 @@
[global]
[[global.environment]]
name='STRIPE_SECRET_KEY'
value='{{ secrets.STRIPE_SECRET_KEY }}'
[hasura]
version = 'v2.25.1-ce'

View File

@@ -1,13 +1,13 @@
{
"name": "@nhost-examples/serverless-functions",
"private": true,
"version": "0.0.8",
"version": "0.0.9",
"devDependencies": {
"@types/express": "^4.17.13"
},
"dependencies": {
"@graphql-yoga/node": "^2.13.13",
"@nhost/stripe-graphql-js": "^1.0.2",
"@nhost/stripe-graphql-js": "^1.0.5",
"@pothos/core": "^3.21.0",
"cross-fetch": "^3.1.5",
"graphql": "15.7.2",

13
examples/sveltekit/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
/test-results/
/playwright-report/
/playwright/.cache/

View File

@@ -0,0 +1 @@
hoist-pattern[]=!@nhost/nhost-js

View File

@@ -0,0 +1,5 @@
---
'@nhost-examples/sveltekit': minor
---
feat: add nhost with sveltekit example project

View File

@@ -0,0 +1,44 @@
# Nhost with SvelteKit Example
## Get Started
1. Clone the repository
```sh
git clone https://github.com/nhost/nhost
cd nhost
```
2. Install and build dependencies
```sh
pnpm install
pnpm build
```
3. Go to the SvelteKit example folder
```sh
cd examples/sveltekit
```
4. Create a `.env` file and set the subdomain and region of your Nhost project. When running locally with the CLI, set the subdomain to `local`.
```sh
PUBLIC_NHOST_SUBDOMAIN=
PUBLIC_NHOST_REGION=
```
5. Terminal 1: Start Nhost
> Make sure you have the [Nhost CLI installed](https://docs.nhost.io/platform/cli).
```sh
nhost up
```
6. Terminal 2: Start the SvelteKit dev server
```sh
pnpm dev
```

View File

@@ -0,0 +1,17 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

View File

@@ -0,0 +1,45 @@
{
"name": "@nhost-examples/sveltekit",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
"install-browsers": "pnpm dlx playwright@1.31.0 install --with-deps",
"add-nhost-js": "pnpm add @nhost/nhost-js --ignore-workspace",
"test": "pnpm install-browsers && pnpm add-nhost-js && pnpm dlx playwright@1.31.0 test",
"lint": "eslint .",
"postinstall": "pnpm add-nhost-js"
},
"devDependencies": {
"@nhost/nhost-js": "2.2.13",
"@playwright/test": "^1.31.0",
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.5.0",
"autoprefixer": "^10.4.14",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.26.0",
"postcss": "^8.4.23",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.8.1",
"svelte": "^3.54.0",
"svelte-check": "^3.0.1",
"tailwindcss": "^3.3.2",
"typescript": "^5.0.0",
"vite": "^4.3.0",
"vitest": "^0.25.3"
},
"type": "module",
"dependencies": {
"@apollo/client": "^3.8.1",
"graphql": "^16.7.1",
"graphql-tag": "^2.12.6",
"js-cookie": "^3.0.5",
"playwright": "^1.37.1",
"uuid": "^9.0.0"
}
}

View File

@@ -0,0 +1,57 @@
// @ts-check
import { defineConfig, devices } from '@playwright/test'
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();
/**
* @see https://playwright.dev/docs/test-configuration
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'http://localhost:5173',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry'
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] }
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] }
}
],
webServer: {
command: 'pnpm dev',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI
}
})

179
examples/sveltekit/pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,179 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
graphql:
specifier: ^16.7.1
version: 16.8.0
devDependencies:
'@nhost/nhost-js':
specifier: 2.2.13
version: 2.2.13(graphql@16.8.0)
packages:
/@graphql-typed-document-node/core@3.2.0(graphql@16.8.0):
resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==}
peerDependencies:
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
dependencies:
graphql: 16.8.0
dev: true
/@nhost/graphql-js@0.1.4(graphql@16.8.0):
resolution: {integrity: sha512-IPHuGOf4iQrFsxG7Rh5jCCZzPCN9JkvldFww4Fz1lCVi9ZQNEaGaawIP5gBuBHeYIuALeaK1wVYKPc7vJ/euCA==}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0
dependencies:
'@graphql-typed-document-node/core': 3.2.0(graphql@16.8.0)
graphql: 16.8.0
isomorphic-unfetch: 3.1.0
transitivePeerDependencies:
- encoding
dev: true
/@nhost/hasura-auth-js@2.1.7:
resolution: {integrity: sha512-dbi8zrmuE3xSlA7WMNyrZzVPLKYSBUlKtUjob+axhts0+4TsqsB7NvdLXrhM0fYjRY5SFUtN2JLs+EWDGKAoLQ==}
dependencies:
'@simplewebauthn/browser': 6.2.2
fetch-ponyfill: 7.1.0
js-cookie: 3.0.5
jwt-decode: 3.1.2
xstate: 4.38.2
transitivePeerDependencies:
- encoding
dev: true
/@nhost/hasura-storage-js@2.2.2:
resolution: {integrity: sha512-GMeB1m6YKZMZDSO6UtmgXYWxsFUNlphIZH9JeiDX+6UTmbd62UlDmhBcmx2OGy/tvv57D+HbohpV6AY9S6QL4w==}
dependencies:
fetch-ponyfill: 7.1.0
form-data: 4.0.0
xstate: 4.38.2
transitivePeerDependencies:
- encoding
dev: true
/@nhost/nhost-js@2.2.13(graphql@16.8.0):
resolution: {integrity: sha512-HU9eOpkVBGGMHsRThGyl654Y9W8bksSEnyEvDojUg76+3ngOn2ebrasXR/94YoR206soEzq3v4b0OKmn/1jlnQ==}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0
dependencies:
'@nhost/graphql-js': 0.1.4(graphql@16.8.0)
'@nhost/hasura-auth-js': 2.1.7
'@nhost/hasura-storage-js': 2.2.2
graphql: 16.8.0
isomorphic-unfetch: 3.1.0
transitivePeerDependencies:
- encoding
dev: true
/@simplewebauthn/browser@6.2.2:
resolution: {integrity: sha512-VUtne7+s6BmW4usnbitjZEI1VNT/PNh6bYg+AI4OMdfpo5z+yAq+6iVAWBJlIUGVk5InetEQvTUp6OefBam8qg==}
dev: true
/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: true
/combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: true
/delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: true
/fetch-ponyfill@7.1.0:
resolution: {integrity: sha512-FhbbL55dj/qdVO3YNK7ZEkshvj3eQ7EuIGV2I6ic/2YiocvyWv+7jg2s4AyS0wdRU75s3tA8ZxI/xPigb0v5Aw==}
dependencies:
node-fetch: 2.6.12
transitivePeerDependencies:
- encoding
dev: true
/form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: true
/graphql@16.8.0:
resolution: {integrity: sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg==}
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
/isomorphic-unfetch@3.1.0:
resolution: {integrity: sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==}
dependencies:
node-fetch: 2.6.12
unfetch: 4.2.0
transitivePeerDependencies:
- encoding
dev: true
/js-cookie@3.0.5:
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
engines: {node: '>=14'}
dev: true
/jwt-decode@3.1.2:
resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==}
dev: true
/mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
dev: true
/mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.52.0
dev: true
/node-fetch@2.6.12:
resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
dependencies:
whatwg-url: 5.0.0
dev: true
/tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
dev: true
/unfetch@4.2.0:
resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==}
dev: true
/webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
dev: true
/whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
dev: true
/xstate@4.38.2:
resolution: {integrity: sha512-Fba/DwEPDLneHT3tbJ9F3zafbQXszOlyCJyQqqdzmtlY/cwE2th462KK48yaANf98jHlP6lJvxfNtN0LFKXPQg==}
dev: true

View File

@@ -0,0 +1,7 @@
// eslint-disable-next-line import/no-anonymous-default-export
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
}

13
examples/sveltekit/src/app.d.ts vendored Normal file
View File

@@ -0,0 +1,13 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
interface Locals {}
interface PageData {}
// interface Platform {}
}
}
export { };

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@@ -0,0 +1,15 @@
<script>
/** @type {("button" | "submit" | "reset" | null | undefined)} */
export let type = 'button';
export let disabled = false;
</script>
<button
{disabled}
{type}
class="{disabled == true
? 'bg-indigo-200 hover:bg-grey-700'
: 'bg-indigo-600 hover:bg-indigo-700 focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500'} inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md shadow-sm text-white focus:outline-none"
>
<slot />
</button>

View File

@@ -0,0 +1,40 @@
<script>
export let value = '';
/** @type {string} */
export let id;
/** @type {string} */
export let label;
export let type = 'text';
/** @type {string} */
export let name;
export let required = false;
export let inputRef = null;
/** @param {HTMLInputElement} node */
function setType(node) {
node.type = type;
}
</script>
<div class={$$props.class}>
<label for={id} class="block text-sm font-medium text-gray-700">
{label}
</label>
<div class="mt-1">
<input
use:setType
{name}
{id}
{required}
bind:value
bind:this={inputRef}
class="block w-full p-3 border rounded-md border-slate-300 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
/>
</div>
</div>

View File

@@ -0,0 +1,66 @@
<script>
import { createEventDispatcher } from 'svelte';
/** @type {import('@nhost/nhost-js').User | undefined} */
export let user;
$: navigation = [
{
href: '/',
name: 'Home'
},
{
href: '/protected',
name: `${user ? '🔓' : '🔒'} Protected`
}
];
const dispatch = createEventDispatcher();
const handleSignOut = () => {
user = undefined;
dispatch('signout', {
signout: true
});
};
</script>
<header class="bg-indigo-600">
<nav class="container mx-auto">
<div class="flex items-center justify-between w-full py-4">
<div class="flex items-center">
<div class="ml-10 space-x-8">
{#each navigation as link}
<a href={link.href} class="text-lg font-medium text-white hover:text-indigo-50">
{link.name}
</a>
{/each}
</div>
</div>
<div class="ml-10 space-x-4">
{#if user}
<button
on:click={handleSignOut}
class="inline-block px-4 py-2 text-base font-medium text-white bg-indigo-500 border border-transparent rounded-md hover:bg-opacity-75"
>
Sign out
</button>
{:else}
<a
href="/sign-in"
class="inline-block px-4 py-2 text-base font-medium text-white bg-indigo-500 border border-transparent rounded-md hover:bg-opacity-75"
>
Sign in
</a>
<a
href="/sign-up"
class="inline-block px-4 py-2 text-base font-medium text-indigo-600 bg-white border border-transparent rounded-md hover:bg-indigo-50"
>
Sign up
</a>
{/if}
</div>
</div>
</nav>
</header>

View File

@@ -0,0 +1,62 @@
import { invalidate } from '$app/navigation';
import { env } from '$env/dynamic/public';
import { NhostClient } from '@nhost/nhost-js';
import Cookies from 'js-cookie';
export const NHOST_SESSION_KEY = 'nhostSession';
const isBrowser = typeof window !== 'undefined';
/** @type {import('@nhost/nhost-js').NhostClient | null} */
let nhost;
/** @param {import('@nhost/nhost-js').NhostSession | null} session */
export const setNhostSessionInCookie = (session) => {
if (!session) {
Cookies.remove(NHOST_SESSION_KEY);
return;
}
const expires = new Date();
// * Expire the cookie 60 seconds before the token expires
expires.setSeconds(expires.getSeconds() + session.accessTokenExpiresIn - 60);
Cookies.set(NHOST_SESSION_KEY, JSON.stringify(session), {
sameSite: 'strict',
expires
});
};
/** @param {import('@sveltejs/kit').Cookies} cookies */
export const getNhostSessionFromCookie = (cookies) => {
const nhostSessionCookie = cookies.get(NHOST_SESSION_KEY);
return nhostSessionCookie ? JSON.parse(nhostSessionCookie) : null;
};
/** @param {import('@nhost/nhost-js').NhostSession} session */
export const getNhostLoadClient = async (session) => {
if (isBrowser && nhost) {
return nhost;
}
nhost = new NhostClient({
subdomain: env.PUBLIC_NHOST_SUBDOMAIN || 'local',
region: env.PUBLIC_NHOST_REGION,
start: false
});
if (isBrowser) {
nhost.auth.onAuthStateChanged((_, session) => {
setNhostSessionInCookie(session);
invalidate('nhost:auth');
});
nhost.auth.onTokenChanged((session) => {
setNhostSessionInCookie(session);
});
}
nhost.auth.client.start({ initialSession: session });
return nhost;
};

View File

@@ -0,0 +1,22 @@
import { getNhostLoadClient } from '$lib/nhost-auth-sveltekit.js';
import { redirect } from '@sveltejs/kit';
const unProtectedRoutes = ['/', '/sign-in', '/sign-up'];
export const load = async ({ data, depends, route }) => {
depends('nhost:auth');
const nhost = await getNhostLoadClient(data.nhostSession);
const session = nhost.auth.getSession();
if (!unProtectedRoutes.includes(route.id ?? '')) {
if (!session) {
throw redirect(303, '/');
}
}
return {
nhost: nhost,
session: session
};
};

View File

@@ -0,0 +1,8 @@
import { getNhostSessionFromCookie } from '$lib/nhost-auth-sveltekit';
/** @type {import('./$types').LayoutServerLoad} */
export async function load({ cookies }) {
return {
nhostSession: getNhostSessionFromCookie(cookies)
};
}

View File

@@ -0,0 +1,24 @@
<script>
import { goto } from '$app/navigation';
import Navigation from '$lib/components/Navigation.svelte';
import './styles.css';
export let data;
let { nhost } = data;
/**
* @param {{ detail: { signout: any; }; }} event
*/
async function handleSignOut(event) {
await nhost.auth.signOut();
await goto(`/`);
}
</script>
<div class="app">
<Navigation user={data.session?.user} on:signout={handleSignOut} />
<div class="container p-4 mx-auto mt-8 antialiased">
<slot />
</div>
</div>

View File

@@ -0,0 +1,6 @@
<svelte:head>
<title>Login</title>
<meta name="description" content="About this app" />
</svelte:head>
<h1 class="text-2xl text-center">Hi, login/register to get started</h1>

View File

@@ -0,0 +1,44 @@
<script>
export let data;
let { session, nhost } = data;
import { gql } from 'graphql-tag';
const getFiles = async () => {
const response = await nhost.graphql.request(gql`
{
files {
id
name
}
}
`);
return response.data.files;
};
</script>
<svelte:head>
<title>Protected Page</title>
<meta name="description" content="About this app" />
</svelte:head>
<h1 class="text-2xl font-semibold text-center">
Hi! You are registered with email: {session?.user.email}.
</h1>
<h2>Files</h2>
{#await getFiles()}
<p>Loading...</p>
{:then files}
<p>Showing {files.length} files</p>
<ul>
{#each files as file}
<li>
{file.name}
</li>
{/each}
</ul>
{:catch error}
<p>{error.message}</p>
{/await}

View File

@@ -0,0 +1,56 @@
<script>
import Input from '$lib/components/Input.svelte';
import Button from '$lib/components/Button.svelte';
import { goto, invalidate } from '$app/navigation';
export let data;
let { nhost } = data;
/** @type {string}*/
let email;
/** @type {string}*/
let password;
/** @type {import('@nhost/nhost-js').AuthErrorPayload | null} */
let error;
const handleSignIn = async () => {
const { error: signInError } = await nhost.auth.signIn({
email,
password
});
error = signInError;
if (!error) {
await invalidate('nhost:auth');
await goto('/protected');
}
};
</script>
<svelte:head>
<title>Sign In</title>
</svelte:head>
<h1 class="text-2xl font-semibold text-center">Sign In</h1>
{#if error}
<p class="mt-3 font-semibold text-center text-red-500">{error.message}</p>
{/if}
<form class="space-y-5" on:submit={handleSignIn}>
<Input label="Email" id="email" name="email" type="email" bind:value={email} required />
<Input
label="Password"
id="password"
name="password"
type="password"
bind:value={password}
required
/>
<Button type="submit">Sign In</Button>
</form>

View File

@@ -0,0 +1,82 @@
<script>
import Input from '$lib/components/Input.svelte';
import Button from '$lib/components/Button.svelte';
import { goto } from '$app/navigation';
export let data;
let { nhost } = data;
/** @type {string}*/
let firstName;
/** @type {string}*/
let lastName;
/** @type {string}*/
let email;
/** @type {string}*/
let password;
/** @type {import('@nhost/nhost-js').AuthErrorPayload | null} */
let error;
const handleSignUp = async () => {
const { error: signUpError } = await nhost.auth.signUp({
email,
password,
options: {
displayName: `${firstName} ${lastName}`
}
});
error = signUpError;
if (!error) {
await goto('/sign-in');
}
};
</script>
<svelte:head>
<title>Sign Up</title>
</svelte:head>
<h1 class="text-2xl font-semibold text-center">Sign Up</h1>
{#if error}
<p class="mt-3 font-semibold text-center text-red-500">{error.message}</p>
{/if}
<form class="space-y-5" on:submit={handleSignUp}>
<Input
label="First Name"
id="firstName"
name="firstName"
type="text"
bind:value={firstName}
required
/>
<Input
label="Last Name"
id="lastName"
name="lastName"
type="text"
bind:value={lastName}
required
/>
<Input label="Email" id="email" name="email" type="email" bind:value={email} required />
<Input
label="Password"
id="password"
name="password"
type="password"
bind:value={password}
required
/>
<Button type="submit">Sign Up</Button>
</form>

View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,13 @@
import adapter from '@sveltejs/adapter-auto';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
};
export default config;

View File

@@ -0,0 +1,9 @@
/** @type {import('tailwindcss').Config} */
// eslint-disable-next-line import/no-anonymous-default-export
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {}
},
plugins: []
}

View File

@@ -0,0 +1,6 @@
import { expect, test } from '@playwright/test'
test('has title', async ({ page }) => {
await page.goto('/')
await expect(page).toHaveTitle(/Login/)
})

View File

@@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [sveltekit()]
})

View File

@@ -1,5 +1,17 @@
# @nhost/apollo
## 5.2.18
### Patch Changes
- @nhost/nhost-js@2.2.16
## 5.2.17
### Patch Changes
- @nhost/nhost-js@2.2.15
## 5.2.16
### Patch Changes

View File

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

View File

@@ -1,5 +1,19 @@
# @nhost/react-apollo
## 5.0.35
### Patch Changes
- @nhost/apollo@5.2.18
- @nhost/react@2.0.31
## 5.0.34
### Patch Changes
- @nhost/apollo@5.2.17
- @nhost/react@2.0.30
## 5.0.33
### Patch Changes

View File

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

View File

@@ -1,5 +1,17 @@
# @nhost/react-urql
## 2.0.32
### Patch Changes
- @nhost/react@2.0.31
## 2.0.31
### Patch Changes
- @nhost/react@2.0.30
## 2.0.30
### Patch Changes

View File

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

View File

@@ -1,5 +1,11 @@
# @nhost/stripe-graphql-js
## 1.0.5
### Patch Changes
- 45759c4d4: fix(stripe-graphql-js): fix stripe GraphQL extension export issue in serverless functions
## 1.0.4
### Patch Changes

View File

@@ -1,3 +1,4 @@
import { createServer } from 'http'
import { Context, createStripeGraphQLServer } from '../src/index'
const isAllowed = (stripeCustomerId: string, context: Context) => {
@@ -10,11 +11,13 @@ const isAllowed = (stripeCustomerId: string, context: Context) => {
return false
}
const server = createStripeGraphQLServer({
const yoga = createStripeGraphQLServer({
isAllowed,
graphiql: true
})
const server = createServer(yoga)
server.listen(4000, () => {
console.info('Stripe GraphQL API server is running on http://localhost:4000')
})

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/stripe-graphql-js",
"version": "1.0.4",
"version": "1.0.5",
"description": "Stripe GraphQL API",
"license": "MIT",
"keywords": [

View File

@@ -1,4 +1,3 @@
import { createServer } from 'node:http'
import { createYoga, YogaInitialContext } from 'graphql-yoga'
import { schema } from './schema'
@@ -54,7 +53,7 @@ const createStripeGraphQLServer = (params?: CreateServerProps) => {
graphqlEndpoint: '*'
})
return createServer(yoga)
return yoga
}
export { createStripeGraphQLServer, schema }

View File

@@ -726,13 +726,226 @@
"title": "Service Restarts",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"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"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "bytes"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 37
},
"id": 39,
"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(ingress) (irate(nginx_ingress_controller_response_size_sum[$__rate_interval]))",
"interval": "2m",
"legendFormat": "__auto",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "sum(irate(fastly_prom_exporter_bytes_sent[$__rate_interval]))",
"hide": false,
"interval": "2m",
"legendFormat": "storage-cdn",
"range": true,
"refId": "B"
}
],
"title": "Egress Traffic",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"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"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 37
},
"id": 41,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "builder",
"expr": "sum by(ingress) (irate(nginx_ingress_controller_requests[$__interval]))",
"interval": "2m",
"legendFormat": "__auto",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "builder",
"expr": "sum(irate(fastly_prom_exporter_requests_total[$__interval]))",
"hide": false,
"interval": "2m",
"legendFormat": "storage-cdn",
"range": true,
"refId": "B"
}
],
"title": "Requests Rate",
"type": "timeseries"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 37
"y": 45
},
"id": 10,
"panels": [],
@@ -801,7 +1014,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 38
"y": 46
},
"id": 12,
"options": {
@@ -908,7 +1121,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 38
"y": 46
},
"id": 14,
"options": {
@@ -1015,7 +1228,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 46
"y": 54
},
"id": 16,
"options": {
@@ -1120,7 +1333,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 46
"y": 54
},
"id": 19,
"options": {
@@ -1171,7 +1384,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 54
"y": 62
},
"id": 21,
"panels": [],
@@ -1240,7 +1453,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 55
"y": 63
},
"id": 22,
"options": {
@@ -1347,7 +1560,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 55
"y": 63
},
"id": 23,
"options": {
@@ -1397,7 +1610,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 63
"y": 71
},
"id": 25,
"panels": [],
@@ -1466,7 +1679,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 64
"y": 72
},
"id": 26,
"options": {
@@ -1573,7 +1786,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 64
"y": 72
},
"id": 27,
"options": {
@@ -1623,7 +1836,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 72
"y": 80
},
"id": 29,
"panels": [],
@@ -1692,7 +1905,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 73
"y": 81
},
"id": 30,
"options": {
@@ -1799,7 +2012,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 73
"y": 81
},
"id": 31,
"options": {

View File

@@ -1,5 +1,11 @@
# @nhost/hasura-auth-js
## 2.1.8
### Patch Changes
- 032c0bd21: fix: make sure errors are correctly thrown on non v8 browsers
## 2.1.7
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/hasura-auth-js",
"version": "2.1.7",
"version": "2.1.8",
"description": "Hasura-auth client",
"license": "MIT",
"keywords": [

View File

@@ -15,7 +15,9 @@ export class CodifiedError extends Error {
error: AuthErrorPayload
constructor(original: Error | AuthErrorPayload) {
super(original.message)
Error.captureStackTrace(this, this.constructor)
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor)
if (original instanceof Error) {
this.name = original.name
this.error = {

View File

@@ -1,5 +1,11 @@
# @nhost/hasura-storage-js
## 2.2.4
### Patch Changes
- 83fee5446: fix(hasura-storage-js): swap fetch when running on edge runtime
## 2.2.3
### Patch Changes

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