Compare commits

...

598 Commits

Author SHA1 Message Date
Hassan Ben Jobrane
90e8843314 Merge pull request #2421 from nhost/changeset-release/main
chore: update versions
2023-12-22 11:51:01 +01:00
github-actions[bot]
aa5b360932 chore: update versions 2023-12-22 10:30:28 +00:00
Hassan Ben Jobrane
daa4b8b2ad Merge pull request #2400 from nhost/changeset-release/main
chore: update versions
2023-12-22 11:28:17 +01:00
Seth Deegan
a1c5c97a59 chore (examples/docker-compose): update README.md to explain why hasura-console is needed (#2395) 2023-12-11 20:14:59 +01:00
Alex Nguyen
b338793d6d Update hasura-auth-client.ts (#2408) 2023-12-11 13:44:00 +01:00
Hassan Ben Jobrane
b1fb4b2400 chore: run pnpm install 2023-12-07 19:49:14 +01:00
github-actions[bot]
f75e023672 chore: update versions 2023-12-05 15:18:53 +00:00
Hassan Ben Jobrane
8e78c1ff00 Merge pull request #2406 from nhost/fix/ci/revert
chore(ci): revert ci changes to use `pull_request`
2023-12-05 16:16:39 +01:00
Hassan Ben Jobrane
9cbb0b2986 chore(ci): revert ci changes to use pull_request 2023-12-05 14:09:53 +01:00
Hassan Ben Jobrane
363a3b92e5 Merge pull request #2405 from nhost/fix/ci/checkout-ref
fix(ci): add ref to all checkout steps
2023-12-05 12:58:47 +01:00
Hassan Ben Jobrane
6a078fc972 fix(ci): add ref to all checkout steps 2023-12-05 12:51:47 +01:00
Hassan Ben Jobrane
1091e9674a Merge pull request #2404 from nhost/fix/ci-checkout-step
chore(ci): add ref to checkout step
2023-12-05 12:26:57 +01:00
Hassan Ben Jobrane
9738108d58 chore(ci): add ref to checkout step 2023-12-05 12:13:53 +01:00
Hassan Ben Jobrane
65951e1d1d Merge pull request #2403 from nhost/ci_target
chore(ci): change to pull_request_target to run workflows "locally"
2023-12-05 11:55:30 +01:00
David Barroso
b4af994a58 chore(ci): change pull_request to pull_request_target to run workflows locally 2023-12-05 11:39:58 +01:00
Hassan Ben Jobrane
c6347e10bc Merge pull request #2402 from nhost/fix/ci/pin-install-nhost-dep
fix(ci): pin `@nhost/nhost-js` dep version in sveltekit quickstart
2023-12-04 17:30:10 +01:00
Hassan Ben Jobrane
278a641bc1 fix(ci): pin @nhost/nhost-js dep version in sveltekit quickstart 2023-12-04 16:18:02 +01:00
Hassan Ben Jobrane
3320ddd8c8 Merge pull request #2393 from nhost/chore/sdk/remove-backendUrl
chore: remove support for using `backendUrl`
2023-12-04 15:05:52 +01:00
Hassan Ben Jobrane
bc9eff6e41 chore: update the changeset to reflect a major version increment 2023-12-04 14:38:56 +01:00
Hassan Ben Jobrane
258c608882 Revert "chore: hardcode staging auth URL for testing"
This reverts commit d8c0bb5ea4e073a7131df3726728845b2bc5e1a1.
2023-12-04 14:38:56 +01:00
Hassan Ben Jobrane
ae84f269d4 chore: hardcode staging auth URL for testing 2023-12-04 14:38:56 +01:00
Hassan Ben Jobrane
0327250b19 Revert "chore: test different subdomain"
This reverts commit 9dfd9399a0a0b1ec931e02304dbe62183b2cb500.
2023-12-04 14:38:56 +01:00
Hassan Ben Jobrane
7f56eabd24 chore: test different subdomain 2023-12-04 14:38:56 +01:00
Hassan Ben Jobrane
be110df83a fix: refactor urlFromSubdomain and fix unit tests 2023-12-04 14:38:56 +01:00
Hassan Ben Jobrane
361e648daf chore: add changeset 2023-12-04 14:38:56 +01:00
Hassan Ben Jobrane
8a72e20e3d chore: refactor generateAppServiceUrl function and remove unused code 2023-12-04 14:38:56 +01:00
Hassan Ben Jobrane
125ec390ca chore: add storage service URL to Nhost client
configuration
2023-12-04 14:38:56 +01:00
Hassan Ben Jobrane
7cc788a373 refactor: remove backendUrl from Nhost client initialization 2023-12-04 14:38:56 +01:00
David Barroso
2a04bc9e5d chore(docs): added functions to custom domains documentation (#2399) 2023-12-04 11:11:08 +01:00
Hassan Ben Jobrane
f7c2148ace Merge pull request #2392 from nhost/feat/dashboard/functions-custom-domains
feat(dashboard): add serverless functions custom domain settings
2023-11-30 14:43:18 +01:00
Hassan Ben Jobrane
78d35eed09 feat(dashboard): add serverless functions custom domain settings 2023-11-30 12:11:22 +01:00
Hassan Ben Jobrane
c5ff53c622 Merge pull request #2389 from nhost/changeset-release/main
chore: update versions
2023-11-29 12:51:40 +01:00
github-actions[bot]
d21714d169 chore: update versions 2023-11-29 10:58:39 +00:00
Hassan Ben Jobrane
0d16ad41b8 Merge pull request #2384 from nhost/fix/quickstarts-auth
fix: update auth version and webauthn origins
2023-11-29 11:56:40 +01:00
Hassan Ben Jobrane
82c328eeda Merge pull request #2388 from nhost/fix/dashboard/secrets
fix: make sure secrets are not resolved
2023-11-29 11:26:23 +01:00
Hassan Ben Jobrane
d991cd8c7e chore: run pnpm install 2023-11-29 11:25:09 +01:00
Hassan Ben Jobrane
e469628ebe chore: add changeset 2023-11-29 11:22:07 +01:00
Hassan Ben Jobrane
856bc0a4bb chore: use workspace nhost-js 2023-11-28 17:36:45 +01:00
Hassan Ben Jobrane
9b1fb1ce28 chore: update dependencies in package.json and fix
NHOST_SESSION_KEY constant
2023-11-28 17:34:19 +01:00
Hassan Ben Jobrane
a4d16f1835 fix: update NHOST_SESSION_KEY value for webauthn 2023-11-28 15:43:27 +01:00
Hassan Ben Jobrane
3db8644075 chore: update pnpm-workspace.yaml 2023-11-28 15:03:02 +01:00
Hassan Ben Jobrane
7f667f6acb chore: update auth version to 0.24.0 2023-11-28 11:48:32 +01:00
Hassan Ben Jobrane
685dc6c1e4 chore: update auth version and webauthn id & origins 2023-11-28 11:46:53 +01:00
Hassan Ben Jobrane
6f7f2b0a65 chore: update changeset 2023-11-27 14:49:32 +01:00
Hassan Ben Jobrane
6d0167b33f fix: update config resolve to true in project.gql 2023-11-27 14:41:48 +01:00
David Barroso
3ffb60f0ae fix(docs): typo in Run deploy example script (#2345) 2023-11-27 13:49:32 +01:00
Hassan Ben Jobrane
97ced73a3c chore: add changeset 2023-11-27 13:17:24 +01:00
Hassan Ben Jobrane
39c86cea25 fix: make sure secrets are not resolved 2023-11-27 13:15:52 +01:00
Hassan Ben Jobrane
d2d590db7e Merge pull request #2369 from nhost/changeset-release/main
chore: update versions
2023-11-24 16:37:42 +01:00
github-actions[bot]
3bdbefc015 chore: update versions 2023-11-24 13:05:27 +00:00
Hassan Ben Jobrane
79081b43c2 Merge pull request #2376 from nhost/feat/database/sql-editor
feat(dashboard): add sql editor
2023-11-24 14:03:15 +01:00
Hassan Ben Jobrane
a4b541f100 fix(quickstarts): update webauthn origins 2023-11-24 10:45:03 +01:00
Hassan Ben Jobrane
4523020c33 fix: update auth version and webauthn origins 2023-11-24 10:31:41 +01:00
Hassan Ben Jobrane
2e2248fd44 chore: add changeset 2023-11-24 10:07:21 +01:00
Hassan Ben Jobrane
63358eb80b chore: add comments 2023-11-24 09:59:55 +01:00
Hassan Ben Jobrane
ded674fab6 fix: add min height to codemirror 2023-11-24 09:55:16 +01:00
Hassan Ben Jobrane
85f2f28902 refactor(dashboard): move run-sql logic to a custom hook 2023-11-24 09:55:16 +01:00
Hassan Ben Jobrane
b8e9ad831e refactor(dashboard): add proper error handling 2023-11-24 09:55:16 +01:00
Hassan Ben Jobrane
4e0c5dd1d3 refactor(dashboard): improve SQL parsing in SQLEditor 2023-11-24 09:55:16 +01:00
Hassan Ben Jobrane
b874109c6d fix: rely on error returned from api call to update metadata 2023-11-24 09:55:16 +01:00
Hassan Ben Jobrane
21b926cc07 feat(dashboard): add create migration option to the sql editor 2023-11-24 09:55:16 +01:00
Hassan Ben Jobrane
c35cd47d97 feat: implement track tables in the sql editor 2023-11-24 09:55:16 +01:00
Hassan Ben Jobrane
8dcd801c7c feat(dashboard): add support for resizing the sql query results container 2023-11-24 09:55:16 +01:00
Hassan Ben Jobrane
e3199be749 feat(dashboard): add sql editor tab and basic func 2023-11-24 09:55:16 +01:00
Hassan Ben Jobrane
284b31e036 Merge pull request #2383 from nhost/chore/dashboard/update-node
chore: update node to v18
2023-11-24 09:18:42 +01:00
Hassan Ben Jobrane
e7593c7de8 chore: update node to v18 in Dockerfile 2023-11-23 16:15:07 +01:00
Hassan Ben Jobrane
e6d862ac1b Merge pull request #2342 from nhost/fix/quickstarts-workspace-deps
fix: ensure `pnpm clean` and `pnpm install` work correctly for the quickstarts
2023-11-15 21:30:21 +01:00
Hassan Ben Jobrane
f73672372f chore: update baseURL in playwright.config.js 2023-11-15 21:08:03 +01:00
Hassan Ben Jobrane
7f12b98d94 chore: fix linter issue 2023-11-15 21:01:07 +01:00
Hassan Ben Jobrane
d79b66314d chore: fix linter issues 2023-11-15 20:49:11 +01:00
Hassan Ben Jobrane
2a58266592 chore: add allowedUrls to auth.redirections and set redirect option for Google sign-in 2023-11-15 20:24:53 +01:00
Hassan Ben Jobrane
44c2c5467d fix: replace @apollo/client with graphql-tag 2023-11-15 20:21:54 +01:00
Hassan Ben Jobrane
142752cb79 Revert "fix: update Apollo client import"
This reverts commit 11a46a0db1.
2023-11-15 20:13:48 +01:00
Hassan Ben Jobrane
b05236a23c chore: run pnpm install 2023-11-15 19:53:11 +01:00
Hassan Ben Jobrane
11a46a0db1 fix: update Apollo client import 2023-11-15 19:53:11 +01:00
Hassan Ben Jobrane
cedff501d6 chore: update auth version to 0.22.1 2023-11-15 19:53:11 +01:00
Hassan Ben Jobrane
7c426dafb2 fix: rectify clean scripts 2023-11-15 19:53:11 +01:00
Hassan Ben Jobrane
57e7f794f5 fix: make sure pnpm clean and pnpm install work correctly for the quickstarts 2023-11-15 19:53:11 +01:00
Hassan Ben Jobrane
d4b6cb0acf Merge pull request #2370 from nhost/chore/quickstarts/update-metadata
chore(quickstarts): add virus table metadata
2023-11-15 19:50:17 +01:00
Nuno Pato
5d0cf8814b Merge pull request #2372 from nhost/chore/dashboard-update-storage-capacity-alert
Chore/dashboard update storage capacity alert
2023-11-13 16:30:17 -01:00
Nuno Pato
96cf17bbeb Apply suggestions from code review
fix typos

Co-authored-by: Hassan Ben Jobrane <hsanbenjobrane@gmail.com>
2023-11-13 16:26:16 -01:00
Nuno Pato
ed1a8d458e add changeset 2023-11-13 16:12:20 -01:00
Nuno Pato
8077495c18 Change dashboard alert for volume capacity 2023-11-13 16:09:33 -01:00
Hassan Ben Jobrane
b617ec7186 chore(quickstarts): add virus table metadata 2023-11-13 17:02:43 +01:00
Hassan Ben Jobrane
bb2da11dd4 Merge pull request #2367 from nhost/fix/docs/signin-linkedin-guide
fix(docs): add instructions for enabling Sign In with LinkedIn using OpenID Connect
2023-11-13 16:18:03 +01:00
Hassan Ben Jobrane
94fa824e7d Merge pull request #2366 from nhost/feat/react-apollo/sign-in-with-linked-in
feat(examples): add sign-in with Linked to react-apollo
2023-11-13 15:56:30 +01:00
Hassan Ben Jobrane
32d1ee124f chore(react-apollo): update auth version to 0.22.1 2023-11-13 15:29:28 +01:00
Hassan Ben Jobrane
138bf9eb5a chore: add changeset 2023-11-11 20:26:50 +01:00
Hassan Ben Jobrane
d8d9310e0b fix: add instructions for enabling Sign In with LinkedIn using OpenID Connect 2023-11-11 20:26:05 +01:00
Hassan Ben Jobrane
67b2c044b8 chore: add changeset 2023-11-11 16:14:33 +01:00
Hassan Ben Jobrane
0b7790ca83 feat(examples): add sign-in with Linked to react-apollo 2023-11-11 16:11:56 +01:00
Hassan Ben Jobrane
55267c680e Merge pull request #2358 from nhost/changeset-release/main
chore: update versions
2023-11-10 16:42:15 +01:00
github-actions[bot]
4d856f557f chore: update versions 2023-11-10 15:22:59 +00:00
Hassan Ben Jobrane
64c579cf8c Merge pull request #2365 from nhost/feat/delete-account
feat: delete account
2023-11-10 16:21:04 +01:00
Hassan Ben Jobrane
eae65c715b fix: disable delete account when user has projects 2023-11-10 15:26:38 +01:00
Hassan Ben Jobrane
9e69f9f235 Merge pull request #2362 from spakanati/feat/export-url-helpers
feat: export urlFromSubdomain helper
2023-11-10 14:30:08 +01:00
Hassan Ben Jobrane
8b127fbb62 chore: add changeset 2023-11-10 14:13:27 +01:00
Hassan Ben Jobrane
86ba2081ec chore: fix docusaurus front matter issue 2023-11-10 14:13:20 +01:00
Hassan Ben Jobrane
7c2c31082a chore: add changeset 2023-11-10 11:50:54 +01:00
Hassan Ben Jobrane
60f705b033 feat: add user account deletion functionality 2023-11-10 11:49:22 +01:00
Sheena Pakanati
ea34635eb2 feat: export urlFromSubdomain helper 2023-11-08 11:30:10 -05:00
Hassan Ben Jobrane
2004687044 Merge pull request #2360 from nhost/fix/examples/react-apollo
fix(react-apollo): update Apple OAuth secrets in nhost.toml
2023-11-07 11:24:51 +01:00
Hassan Ben Jobrane
bd025d43ca fix: update Apple OAuth secrets in nhost.toml 2023-11-07 10:54:16 +01:00
Hassan Ben Jobrane
87a05f7374 Merge pull request #2353 from nhost/feat/react-appollo/signin-with-apple
feat(react-apollo): add SignIn with Apple
2023-11-07 08:53:19 +01:00
Hassan Ben Jobrane
798f147db7 chore: remove console.log statement 2023-11-06 20:13:05 +01:00
Hassan Ben Jobrane
62b7fd2376 chore: update auth version to 0.21.4 2023-11-06 20:11:33 +01:00
David Barroso
1ee021b4a3 chore(docs): remove custom domains from roadmap (#2352) 2023-11-04 12:40:18 +01:00
Hassan Ben Jobrane
6e61dce297 chore: add changeset 2023-11-03 17:28:01 +01:00
Hassan Ben Jobrane
bd744e52dc feat(examples): add SignIn with Apple to the react-apollo example 2023-11-03 17:26:23 +01:00
Nestor Manrique
85723d740b Merge pull request #2343 from nhost/nestor/fix/ingress-tenant-dashboard
fix (observability): ingress tenant dashboard
2023-10-26 21:56:18 +02:00
Hassan Ben Jobrane
36e79e7b32 Merge pull request #2344 from nhost/chore/quickstarts/upgrade-storage
chore: bump quickstarts storage to `0.4.0`
2023-10-26 11:39:58 +01:00
Hassan Ben Jobrane
f61264b319 chore: bump quickstarts storage to 0.4.0 2023-10-26 11:22:41 +01:00
Nestor Manrique
e84d9d2576 Fix legends 2023-10-26 11:28:33 +02:00
Hassan Ben Jobrane
ea69d4f0f1 Merge pull request #2341 from nhost/changeset-release/main
chore: update versions
2023-10-25 14:53:21 +01:00
github-actions[bot]
212d58bee5 chore: update versions 2023-10-25 13:22:09 +00:00
Hassan Ben Jobrane
c3d6b7beec Merge pull request #2333 from nhost/feat/vue-sdk/upload-multiple-files
feat(vue-sdk): add support for uploading multiple files
2023-10-25 14:18:36 +01:00
Hassan Ben Jobrane
5d5d8ef4f3 chore: use @nhost/nhost-js from workspace 2023-10-25 13:31:12 +01:00
Hassan Ben Jobrane
deb61fe97c chore: add @nhost/nhost-js to vue-apollo example 2023-10-25 13:21:36 +01:00
Nestor Manrique
04d36154b0 Merge pull request #2334 from nhost/nestor/feat/add-ingress-dashboard
feat(observability): Add ingress metrics dashboard for tenants
2023-10-25 14:21:01 +02:00
Hassan Ben Jobrane
203cfb10b9 chore: fix JSDoc 2023-10-25 12:43:54 +01:00
Hassan Ben Jobrane
9690f871fa chore: fix JSDoc 2023-10-25 11:44:45 +01:00
Hassan Ben Jobrane
74a6b93971 Merge pull request #2335 from nhost/chore/examples/upgrade-to-node18
chore: update toml files to use node 18
2023-10-25 10:36:37 +01:00
Nestor Manrique
dd4c0d2430 wip 2023-10-25 03:30:52 +02:00
Hassan Ben Jobrane
83f2ca5cde chore: update toml files to use node 18 2023-10-24 16:39:09 +01:00
Hassan Ben Jobrane
0c49e757c8 chore: add changeset 2023-10-24 16:25:07 +01:00
Hassan Ben Jobrane
e90a9d7696 feat: add storage page to vue-apollo example 2023-10-24 16:20:02 +01:00
Hassan Ben Jobrane
00a06466f5 fix: return refs from useFileUpload 2023-10-24 16:20:02 +01:00
Hassan Ben Jobrane
8ca9f76cb2 wip: add support for uploading multiple files 2023-10-24 16:20:02 +01:00
Hassan Ben Jobrane
78113dd62a wip: feat: vue-sdk: introduce new composable to upload multiple files 2023-10-24 16:20:02 +01:00
Nestor Manrique
adb0ee82c6 wip 2023-10-24 14:29:25 +02:00
Nestor Manrique
a41bb6cae6 wip 2023-10-24 14:05:42 +02:00
Hassan Ben Jobrane
1c59c363ee Merge pull request #2328 from nhost/changeset-release/main
chore: update versions
2023-10-24 11:20:22 +01:00
github-actions[bot]
1d99f26fec chore: update versions 2023-10-24 09:59:28 +00:00
Hassan Ben Jobrane
49edb0e627 Merge pull request #2332 from ttiras/patch-1
Update Docs for useChangeEmail.ts Example
2023-10-24 10:56:55 +01:00
Hassan Ben Jobrane
f011e71ae1 chore: fix typo 2023-10-23 19:49:05 +01:00
Hassan Ben Jobrane
00c363f808 chore: add changeset 2023-10-23 18:10:04 +01:00
Hassan Ben Jobrane
0b2f749ae9 fix: docs: vuejs: update changeEmail docs reference 2023-10-23 18:05:24 +01:00
Hassan Ben Jobrane
cf62a1e6e3 Merge pull request #2331 from nhost/fix/custom-domains/reset-domain
fix(dashboard): allow resetting custom domains
2023-10-20 17:58:06 +01:00
Hassan Ben Jobrane
8df84d782f chore: add changeset 2023-10-20 16:01:47 +01:00
Hassan Ben Jobrane
f0deffafe1 fix(dashboard): allow resetting custom domains 2023-10-20 15:59:54 +01:00
Hassan Ben Jobrane
a291da661d Merge pull request #2321 from MainaMary/bug/update-use-change-password-interface
fix: update useChangePassword hook interface
2023-10-20 11:42:41 +01:00
Mary
66c3193bc9 chore: add changeset 2023-10-20 13:03:00 +03:00
Hassan Ben Jobrane
ac7be49cef Merge pull request #2327 from nhost/chore/run/tweaks
chore(dashboard): fixes and tweaks to services form and dialog
2023-10-19 11:53:32 +01:00
Hassan Ben Jobrane
fa79b77093 chore: add changeset 2023-10-19 11:22:18 +01:00
Hassan Ben Jobrane
5823947933 chore: add missing key to service details dialog 2023-10-19 11:21:11 +01:00
Hassan Ben Jobrane
333837fb57 chore: fix update button icon on service form 2023-10-19 11:10:01 +01:00
Hassan Ben Jobrane
7fae68f6cf Merge pull request #2324 from nhost/changeset-release/main
chore: update versions
2023-10-18 17:09:56 +01:00
github-actions[bot]
f2751f4bac chore: update versions 2023-10-18 16:00:42 +00:00
Hassan Ben Jobrane
089acbbe70 Merge pull request #2320 from nhost/feat/custom-domains
feat(dashboard): custom domains
2023-10-18 16:58:03 +01:00
Nuno Pato
6e08a82f49 Merge pull request #2325 from nhost/docs/custom-domains
chore: docs: add custom domains
2023-10-18 15:49:47 +00:00
Hassan Ben Jobrane
6899ef3b39 chore: run pnpm codegen 2023-10-18 16:20:17 +01:00
Hassan Ben Jobrane
cad3686364 fix: tweak text when dark mode is on 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
8f2c002715 fix: fix custom domains page on small screens 2023-10-18 16:18:45 +01:00
Nuno Pato
b70d61198f fix link to docs 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
d29af2ce6f fix: make sure settings container title supports both a ReactNode and a string 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
cdc992b888 chore: update custom domains header message
Co-authored-by: Nuno Pato <nunopato@gmail.com>
2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
205a20de87 fix: use correct database host 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
b092b8fe08 chore: tweak database domain description and docs link 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
2d40cbf624 fix: tweak verification box 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
7b591e8c4c fix dns verification values 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
72b425a5bc fix: remove duplicated nhost.run suffix 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
971ff92ab4 chore: remove comment 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
b7f801874d chore: add changeset 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
ff69f30e47 chore: move docs link to the top section 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
cc1932492d fix: only show services that have ports 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
f45037e79f fix: always show CNAME verification panel 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
48658e2925 feat: added run services port domains 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
b90bb6b924 feat: add database domain form 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
de61f45bd5 fix: auth and hasura domain forms 2023-10-18 16:18:45 +01:00
Hassan Ben Jobrane
fd11e5ca2c feat: add Hasura Domain 2023-10-18 16:18:44 +01:00
Hassan Ben Jobrane
7839c786ef chore: run pnpm codegen 2023-10-18 16:18:44 +01:00
Hassan Ben Jobrane
a2bcd6a4b6 feat: add auth domain form 2023-10-18 16:18:44 +01:00
Hassan Ben Jobrane
2cd5b26e0e chore: run pnpm codegen 2023-10-18 16:18:44 +01:00
Hassan Ben Jobrane
559611af70 feat: add upgrade banner to access custom domains 2023-10-18 16:16:37 +01:00
Hassan Ben Jobrane
ffb45f5a49 Merge pull request #2326 from nhost/feat/database/storage-capacity
feat(dashboard): add database storage capacity setting
2023-10-18 15:53:18 +01:00
Hassan Ben Jobrane
451e80ac12 chore: add a warning message that db storage can't be downgraded 2023-10-18 15:19:18 +01:00
Hassan Ben Jobrane
c9f8e523f2 chore: fix upgrade message text 2023-10-18 13:43:37 +01:00
Hassan Ben Jobrane
331ba03768 chore: add changeset 2023-10-18 13:29:42 +01:00
Hassan Ben Jobrane
611b26bc7d chore: fix mocks 2023-10-18 13:29:33 +01:00
Nuno Pato
a446c3efca use custom-domain.com 2023-10-18 12:24:11 +00:00
Hassan Ben Jobrane
24424ae4dc feat: add postgres storage capacity setting 2023-10-18 13:19:43 +01:00
Hassan Ben Jobrane
2a5b705c26 chore: run pnpm codegen & remove deprecated insertFeedback 2023-10-18 13:18:56 +01:00
Nuno Pato
7f3a32d386 use tabs 2023-10-18 11:51:14 +00:00
Nuno Pato
11fa442aa8 Merge branch 'main' into docs/custom-domains 2023-10-18 11:45:19 +00:00
Nuno Pato
5764f46d99 add changeset 2023-10-18 11:36:26 +00:00
Nuno Pato
78d501801b docs: custom domains 2023-10-18 11:34:38 +00:00
David Barroso
cc8cc8d45d chore(docs): database: added extension http (#2323) 2023-10-18 11:42:50 +02:00
Mary
61fc83996b fix: update useChangePassword hook interface 2023-10-17 13:29:49 +03:00
ttiras
9ddb37e9bb Update useChangeEmail.ts
the wrong example has been modified from;
 
 await changeEmail({
    email: 'new@example.com'
  })

to;

 await changeEmail('new@example.com')
2023-10-14 13:53:07 +03:00
Hassan Ben Jobrane
262828f9a1 Merge pull request #2318 from nhost/changeset-release/main
chore: update versions
2023-10-12 16:36:06 +01:00
github-actions[bot]
12f9726ad7 chore: update versions 2023-10-12 13:40:33 +00:00
Hassan Ben Jobrane
845937b552 Merge pull request #2317 from nhost/fix/apollo-integration
fix: integrations: apollo: correct accessToken nullability test
2023-10-12 14:37:49 +01:00
Hassan Ben Jobrane
f777a3380a chore: add changeset 2023-10-12 10:26:00 +01:00
Hassan Ben Jobrane
5081372cab fix: integrations: apollo: correct accessToken nullability test 2023-10-12 10:23:09 +01:00
Hassan Ben Jobrane
82212345c8 Merge pull request #2311 from nhost/changeset-release/main
chore: update versions
2023-10-11 13:54:25 +01:00
Hassan Ben Jobrane
32d3f167c5 Merge pull request #2313 from nhost/fix/quickstarts
fix: quickstarts: toml node version and tsconfig moduleResolution
2023-10-11 12:37:38 +01:00
github-actions[bot]
3d5f1ea922 chore: update versions 2023-10-11 10:24:24 +00:00
Hassan Ben Jobrane
97841ee5e8 Merge pull request #2312 from nhost/fix/dashboard/run-tab
fix: dashboard: disable run tab when developing locally
2023-10-11 11:21:38 +01:00
Hassan Ben Jobrane
4f3a615ebe fix: quickstarts: fix backend functions node version 2023-10-11 10:14:09 +01:00
Hassan Ben Jobrane
8e8197691c fix: quickstarts: change module resolution to node 2023-10-11 10:13:40 +01:00
Hassan Ben Jobrane
e10389ecf6 chore: add changeset 2023-10-11 10:08:11 +01:00
Hassan Ben Jobrane
cbdf6affec fix(dashboard): disable run services tab in local dev mode 2023-10-11 10:07:02 +01:00
Hassan Ben Jobrane
d19406e694 Merge pull request #2309 from nhost/fix/integration/apollo
fix: integrations: apollo: set accessToken to null after TOKEN_CHANGED event on sign-out
2023-10-10 16:24:39 +01:00
Hassan Ben Jobrane
cffc5dc65b Merge pull request #2310 from nhost/chore/ci/stop-dashboard-releases
chore: ci: stop @nhost/dashboard github releases
2023-10-10 16:24:01 +01:00
Hassan Ben Jobrane
2b5cb58553 chore: ci: stop @nhost/dashboard github releases 2023-10-10 16:03:37 +01:00
Hassan Ben Jobrane
7459a9413e chore: add changeset 2023-10-10 15:43:28 +01:00
Hassan Ben Jobrane
56871cc9f7 fix: integrations: apollo: set accessToken to null after TOKEN_CHANGED event on signout 2023-10-10 13:38:04 +01:00
Hassan Ben Jobrane
8f4d66e52d Merge pull request #2308 from nhost/changeset-release/main
chore: update versions
2023-10-10 13:31:53 +01:00
github-actions[bot]
315a820073 chore: update versions 2023-10-10 12:04:28 +00:00
Hassan Ben Jobrane
ca57ad2cbd Merge pull request #2301 from nhost/feat/quickstarts/sveltekit
feat(quickstarts): sveltekit <--> nhost
2023-10-10 13:00:14 +01:00
Hassan Ben Jobrane
40259344eb Merge pull request #2306 from nhost/chore/providers-updated-notice
chore(dashboard): show oauth providers update notice
2023-10-10 12:45:12 +01:00
Hassan Ben Jobrane
4749f60a08 chore: add changeset 2023-10-09 20:12:04 +01:00
Hassan Ben Jobrane
ac1888514d chore: update readme for sveltekit quickstart 2023-10-09 19:52:44 +01:00
Hassan Ben Jobrane
49b4af439b feat: signup/signin via webauthn 2023-10-09 19:23:50 +01:00
Hassan Ben Jobrane
61e03d6c70 chore: make sure deprication notice is under a project 2023-10-09 16:48:01 +01:00
Hassan Ben Jobrane
bec0fce497 chore: add deprication banner 2023-10-09 15:00:58 +01:00
Hassan Ben Jobrane
c01568a7dd chore: add changeset 2023-10-09 14:28:40 +01:00
Hassan Ben Jobrane
e934216a82 chore: bring back providers update alert 2023-10-09 14:26:41 +01:00
Hassan Ben Jobrane
701d6b8c84 feat: sveltekit: add delete pat 2023-10-07 19:47:05 +01:00
Hassan Ben Jobrane
e158e2440a wip: sveltekit: add echo and pat pages 2023-10-07 19:22:29 +01:00
Hassan Ben Jobrane
fbaa657001 wip: sveltekit: refresh access token + create/delete todos 2023-10-05 17:08:55 +01:00
Hassan Ben Jobrane
559db6d0ec wip: sveltekit: auth + todos(create) 2023-10-05 12:48:59 +01:00
Hassan Ben Jobrane
4c844930f1 wip: update: sveltekit 2023-10-04 16:58:59 +01:00
Hassan Ben Jobrane
3ef503ff81 Merge pull request #2298 from nhost/changeset-release/main
chore: update versions
2023-10-04 16:47:51 +02:00
github-actions[bot]
bfcfd236ea chore: update versions 2023-10-04 14:13:38 +00:00
Hassan Ben Jobrane
bfa7033506 Merge pull request #2296 from nhost/feat/query-announcements
feat: dashboard: query announcements
2023-10-04 16:09:48 +02:00
Hassan Ben Jobrane
78c29fcf0e feat: filter expired announcements 2023-10-04 14:40:22 +01:00
Hassan Ben Jobrane
f1b934ed22 chore: remove old announcement provider 2023-10-04 10:52:20 +01:00
Hassan Ben Jobrane
914369c53f feat: add announcements list component 2023-10-03 20:18:41 +01:00
Hassan Ben Jobrane
af379b967e chore: clean up commented code 2023-10-03 17:43:24 +01:00
Hassan Ben Jobrane
c3efb7ec84 chore: add changeset 2023-10-03 17:41:35 +01:00
Hassan Ben Jobrane
27cbd48c8c feat(dashboard): query latest announcement from platform 2023-10-03 17:40:50 +01:00
Hassan Ben Jobrane
236996a903 Merge pull request #2293 from nhost/changeset-release/main
chore: update versions
2023-10-02 13:01:48 +02:00
github-actions[bot]
5d0936bb93 chore: update versions 2023-10-02 10:38:35 +00:00
Hassan Ben Jobrane
733c212f2d Merge pull request #2291 from nhost/chore/announcement/node18
chore: node18 announcement
2023-10-02 12:35:53 +02:00
Hassan Ben Jobrane
8b47549189 Merge pull request #2286 from nhost/chore/ci/disable-github-releases
chore(ci): set createGithubReleases to false
2023-10-02 12:26:10 +02:00
Hassan Ben Jobrane
3c9c1025ce Merge pull request #2287 from nhost/fix/vue-sdk/nested-unref
fix(vue-sdk): correctly unref arrays
2023-10-02 12:25:14 +02:00
Hassan Ben Jobrane
3e46d3873c chore: add changeset 2023-10-02 10:50:46 +01:00
Hassan Ben Jobrane
4cf8820d72 chore: open announcement link in a new tab 2023-10-02 10:39:15 +01:00
Hassan Ben Jobrane
02a11184fb chore: change announcement link 2023-10-02 10:38:04 +01:00
Hassan Ben Jobrane
7214d47cc7 chore: add changeset 2023-09-30 17:54:42 +01:00
Hassan Ben Jobrane
238b77baad fix(vue): correctly unref arrays 2023-09-30 17:53:05 +01:00
Hassan Ben Jobrane
81b8e538b4 chore(ci): set createGithubReleases to false 2023-09-29 17:12:21 +01:00
Hassan Ben Jobrane
563a37e58d Merge pull request #2285 from nhost/changeset-release/main
chore: update versions
2023-09-29 18:05:40 +02:00
github-actions[bot]
bff23720ee chore: update versions 2023-09-29 15:43:43 +00:00
Hassan Ben Jobrane
02cbaeffd2 Merge pull request #2225 from nhost/feat/examples/nextjs-server-components
feat: quickstarts: draft for using server components
2023-09-29 17:40:52 +02:00
Hassan Ben Jobrane
9eb814c79a chore: update readme 2023-09-29 15:57:53 +01:00
Hassan Ben Jobrane
ebc5913bb3 chore: naming consistency 2023-09-29 15:57:53 +01:00
Hassan Ben Jobrane
4fe4a16964 chore: add changeset 2023-09-29 15:57:53 +01:00
Hassan Ben Jobrane
92c475b7a7 chore: add missing refreshToken 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
679b34b031 chore: cleanup 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
d3186aefbd refactor: extract session middleware into helper function 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
fdecac9d69 refactor: add high order component for protected pages 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
5077283028 chore: merge oauth handling in middleware 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
f5f662aad1 chore: refactor server actions 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
735b779af7 chore: clean up database setup 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
4418d6abcf chore: cleanup 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
049e315c30 fix: set correct path on cookie on oauth signin 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
764597538b fix: make sure that hasura-storage-js works on EdgeRuntime 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
c8aea785cc fix: tweak todo item layout 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
e0e44b2ff4 fix: set same path for session cookie 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
12280f7c87 feat: pat list pagination 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
732a4f40ca wip 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
d67fd599e4 feat: todos CRUD 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
a41231927a feat: add signin with pat 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
42ec665950 fix: return refreshToken in getAuthenticationResult 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
7225712a30 chore: update hasura auth 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
6593fdd9bb fix: make sure refreshToken is returned after signin/signup 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
40039fece5 Revert "refactor: make sure to return refresh token"
This reverts commit b31b358ca1898bb4173954b8b33059d92cc8c126.
2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
e5fcfb3cd5 refactor: make sure to return refresh token 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
218ec314fb feat(quickstarts): refactor and organize signup/signin 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
9367e91d45 feat: examples: add other sign in methods
Add sign in with google and webauthn
2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
06c640be2c chore: delete unnecessary files 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
ae45be9816 feat(quickstarts): draft for using server components 2023-09-29 15:57:52 +01:00
Hassan Ben Jobrane
ec4be590d8 Merge pull request #2284 from nhost/chore/ci/fix-release
chore: ci: fix release worflow
2023-09-29 16:56:12 +02:00
Hassan Ben Jobrane
5c51653aa0 chore: fix release worflow 2023-09-29 13:29:44 +01:00
Hassan Ben Jobrane
7348c15ad1 Merge pull request #2281 from nhost/changeset-release/main
chore: update versions
2023-09-29 13:48:59 +02:00
Hassan Ben Jobrane
44831e32a7 Merge pull request #2282 from nhost/chore/ci/fix-release-workflow
chore(ci): fix release workflow
2023-09-29 13:47:21 +02:00
Hassan Ben Jobrane
ee0f837762 chore(ci): fix release workflow 2023-09-29 12:32:05 +01:00
github-actions[bot]
e040979e91 chore: update versions 2023-09-29 10:59:27 +00:00
Hassan Ben Jobrane
68100d63b9 Merge pull request #2267 from nhost/changeset-release/main
chore: update versions
2023-09-29 12:56:45 +02:00
github-actions[bot]
9b800046d7 chore: update versions 2023-09-29 10:37:52 +00:00
Hassan Ben Jobrane
807d8574b6 Merge pull request #2280 from nhost/feat/dashboard/multiline-variables-input
feat(dashboard): make env value input multiline
2023-09-29 12:34:59 +02:00
Hassan Ben Jobrane
77028e4eef Merge pull request #2265 from nhost/chore/ci/disable-changeset-github-releases
chore(ci): disable automatic GitHub releases
2023-09-29 12:34:36 +02:00
Hassan Ben Jobrane
e0d32aab33 Merge pull request #2235 from nhost/chore/ci/release/recorder-steps
chore(ci): run publish to vercel before docker build
2023-09-29 12:34:16 +02:00
Hassan Ben Jobrane
75c4c8ae36 chore: add changeset 2023-09-29 11:14:53 +01:00
Hassan Ben Jobrane
1d90639e46 feat: make env value input multiline 2023-09-29 11:12:35 +01:00
David Barroso
765b398b21 chore(docs): added jit settings documentation (#2274)
Solves #2273
2023-09-29 10:44:32 +02:00
David Barroso
30aae1557c chore(docs): minor fix to performance documentation (#2272) 2023-09-29 10:44:02 +02:00
David Barroso
a3efc1d131 chore (docs): added storage/antivirus documentation (#2268)
Rearranged the storage section a bit and added:

https://docs-git-docs-storage-nhost.vercel.app/storage/av
2023-09-28 07:27:57 +02:00
David Barroso
612d754965 chore: docs: added more docs (#2264)
https://docs-git-docs-postgres-nhost.vercel.app/database/settings
https://docs-git-docs-postgres-nhost.vercel.app/database/extensions
https://docs-git-docs-postgres-nhost.vercel.app/graphql/settings
https://docs-git-docs-postgres-nhost.vercel.app/database/performance

---------

Co-authored-by: Hassan Ben Jobrane <hsanbenjobrane@gmail.com>
2023-09-28 07:27:15 +02:00
Hassan Ben Jobrane
b2e5f30379 Merge pull request #2269 from nhost/fix/docs/mermaid-theme
fix(docs): mermaid diagrams dark mode
2023-09-27 16:28:53 +01:00
Hassan Ben Jobrane
3b3e83a218 chore: add changeset 2023-09-27 15:42:48 +01:00
Hassan Ben Jobrane
0d5231f1a1 fix(docs): correct rendering of mermaid diagrams in dark mode 2023-09-27 15:41:59 +01:00
Hassan Ben Jobrane
1a8332a3ca Merge pull request #2266 from nhost/fix/settings/compute-resources
fix: make sure dedicated resources pricing follows total resources
2023-09-27 10:06:48 +01:00
Hassan Ben Jobrane
7418105de2 chore: remove commented code 2023-09-26 19:29:48 +01:00
Hassan Ben Jobrane
425d485f85 chore: add changeset 2023-09-26 17:09:08 +01:00
Hassan Ben Jobrane
d8d25b3ea0 fix: make sure dedicated resources pricing follows total resources on the top section 2023-09-26 17:06:51 +01:00
Hassan Ben Jobrane
320513f6f5 chore(ci): disable automatic GitHub releases 2023-09-26 16:34:18 +01:00
Hassan Ben Jobrane
b37053376d Merge pull request #2261 from nhost/changeset-release/main
chore: update versions
2023-09-22 12:00:44 +01:00
github-actions[bot]
c21ba4aebd chore: update versions 2023-09-22 10:46:59 +00:00
Hassan Ben Jobrane
58948c50d4 Merge pull request #2260 from nhost/fix/graphql/remove-unused-fields
fix: dashboard: remove unused fields
2023-09-22 11:44:23 +01:00
Hassan Ben Jobrane
ae324f67fa chore: add changeset 2023-09-22 11:33:51 +01:00
Hassan Ben Jobrane
acabf2b168 fix(dashboard): remove unused fields 2023-09-22 11:31:57 +01:00
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
Hassan Ben Jobrane
e622ca0d83 chore(ci): run publish to vercel before docker build 2023-09-12 15:09:34 +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
d9fd1a54a5 Merge pull request #2192 from nhost/changeset-release/main
chore: update versions
2023-08-23 11:19:48 +01:00
github-actions[bot]
a19b85c8ac chore: update versions 2023-08-23 09:45:31 +00:00
Hassan Ben Jobrane
4e1aaca0ee Merge pull request #2194 from nhost/feat/toggle-av
feat: toggle av
2023-08-23 10:42:20 +01:00
Hassan Ben Jobrane
34ef37cdce Merge pull request #2190 from dddenis/fix/storage-upload-status-error
fix(hasura-storage-js): fix upload response status code check
2023-08-23 10:40:29 +01:00
Hassan Ben Jobrane
5d6b655cb1 fix: make sure AV turns off correctly 2023-08-23 01:40:01 +01:00
Hassan Ben Jobrane
074a0fa111 chore: add changeset 2023-08-22 18:32:34 +01:00
Hassan Ben Jobrane
403d839fca chore: cleanup 2023-08-22 18:30:26 +01:00
Hassan Ben Jobrane
4e3098240b feat(settings): add toggle av settings 2023-08-22 18:28:27 +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
07fda9bbb3 Merge pull request #2193 from nhost/fix/distinguish-not-uploaded-files
fix: grey out not uploaded files
2023-08-22 13:11:52 +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
Hassan Ben Jobrane
4a7ede11e9 chore: add changeset 2023-08-22 11:33:42 +01:00
Hassan Ben Jobrane
482ae4c4f1 fix: grey not uploaded files 2023-08-22 11:25:23 +01:00
Denis Goncharenko
08fe4cd65f fix(hasura-storage-js): update upload response error details 2023-08-22 11:34:41 +02:00
Hassan Ben Jobrane
5781721bca Merge pull request #2188 from nhost/feat/one-click-run-service
feat: add support for template run services
2023-08-22 10:18:54 +01:00
Denis Goncharenko
39de0063bf chore: add changeset 2023-08-21 20:49:56 +02:00
Hassan Ben Jobrane
202b647234 chore: add changeset 2023-08-21 15:45:43 +01:00
Hassan Ben Jobrane
51c163a268 fix: copy complete link to config 2023-08-21 15:43:46 +01:00
Hassan Ben Jobrane
6e802c9938 fix: handle the case where the config is not set in the URL 2023-08-21 13:38:38 +01:00
Hassan Ben Jobrane
9a46104e37 feat: replace project selector with a searchable list 2023-08-21 13:26:27 +01:00
Hassan Ben Jobrane
655b317c39 fix: keep image field when copying config to clipboard 2023-08-21 13:25:57 +01:00
Hassan Ben Jobrane
d3ad7c9d4a fix: handle error when navigating back when service form is still open 2023-08-21 13:25:01 +01:00
David Barroso
09fc852c3a asd 2023-08-21 13:13:41 +02:00
Hassan Ben Jobrane
ece08d3efd feat: add ability to copy current service config from the editor 2023-08-21 10:47:54 +01:00
Hassan Ben Jobrane
3493442c2d fix: fix command import when value is null 2023-08-21 10:47:26 +01:00
Denis Goncharenko
632a79b9e4 fix(hasura-storage-js): fix upload response status code check 2023-08-20 14:15:24 +02:00
Hassan Ben Jobrane
4a4d85757a fix: use serviceId to determine whether to create or update service 2023-08-19 19:33:45 +01:00
Hassan Ben Jobrane
88a01004b7 feat: parse base64 encoded config from query param 2023-08-19 18:50:46 +01:00
David Barroso
73230eb35a chore: fix and deploy react example (#2183) 2023-08-19 07:57:42 +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
Hassan Ben Jobrane
5cf9dd9bc2 Merge pull request #2173 from nhost/changeset-release/main
chore: update versions
2023-08-14 11:34:45 +01:00
github-actions[bot]
27e74c10d7 chore: update versions 2023-08-10 11:05:13 +00:00
Hassan Ben Jobrane
bd807a5ee1 Merge pull request #2171 from nhost/feat/run-pricing
feat: add pricing info and confirmation dialog
2023-08-10 12:01:04 +01:00
Hassan Ben Jobrane
4093e03a13 Merge pull request #2170 from nhost/feat/multiline-env-input
fix: nhost run enhancements
2023-08-10 12:00:53 +01:00
Hassan Ben Jobrane
29076d0304 Merge pull request #2169 from nhost/fix/null-values-services-form
fix(services): handle null values when editing a service
2023-08-10 12:00:43 +01:00
Hassan Ben Jobrane
ab83fa6b5e fix: make sure vCPUs are shown correctly 2023-08-09 16:20:45 +01:00
Hassan Ben Jobrane
b20761e976 chore: add changeset 2023-08-09 14:55:31 +01:00
Hassan Ben Jobrane
a445e5b786 feat: add pricing info and confirmation dialog 2023-08-09 14:53:05 +01:00
Hassan Ben Jobrane
90df6d81d8 chore: add changeset 2023-08-08 18:04:16 +01:00
Hassan Ben Jobrane
aa85084675 chore: add changeset 2023-08-08 18:02:35 +01:00
Hassan Ben Jobrane
07ad470c0c fix: query service logs correctly 2023-08-08 18:00:25 +01:00
Hassan Ben Jobrane
fa6b58a9c5 feat: enable multiline support for environment value input 2023-08-08 17:50:07 +01:00
Hassan Ben Jobrane
acf55376ba fix(services): handle null values when editing a service 2023-08-08 15:42:09 +01:00
Hassan Ben Jobrane
b0a9798b04 Merge pull request #2168 from nhost/changeset-release/main
chore: update versions
2023-08-08 11:17:25 +01:00
github-actions[bot]
3952e87f01 chore: update versions 2023-08-07 17:53:16 +00:00
Hassan Ben Jobrane
b95ccf873d Merge pull request #2167 from nhost/fix/announcement-banner-text
fix: make announcement text white in dark mode
2023-08-07 18:50:41 +01:00
Hassan Ben Jobrane
8d7f84b8da chore: add changeset 2023-08-07 18:33:17 +01:00
Hassan Ben Jobrane
bd1b69bd75 fix: make announcement text white in dark mode 2023-08-07 12:17:18 +01:00
Hassan Ben Jobrane
84d5436634 Merge pull request #2165 from nhost/changeset-release/main
chore: update versions
2023-08-07 12:09:08 +01:00
github-actions[bot]
2325766c1d chore: update versions 2023-08-04 16:06:40 +00:00
Hassan Ben Jobrane
2c355eaae4 Merge pull request #2162 from nhost/fix/dedicated-resources-modal
fix: show zero values when dedicated resources is disabled
2023-08-04 17:03:08 +01:00
Hassan Ben Jobrane
9e26ed767e Merge pull request #2161 from nhost/fix/announcement-banner
fix: make announcement close properly
2023-08-04 17:02:58 +01:00
Hassan Ben Jobrane
abdb6c56f4 chore: increase e2e CI timeout 2023-08-03 17:18:03 +01:00
Hassan Ben Jobrane
3b75bfce27 chore: add changeset 2023-08-03 17:03:01 +01:00
Hassan Ben Jobrane
f498190758 chore: add changeset 2023-08-03 17:00:10 +01:00
Hassan Ben Jobrane
b4158fa513 fix: show zero values when dedicated resources is disabled 2023-08-03 16:54:12 +01:00
Hassan Ben Jobrane
3d1a177632 fix: make announcement close properly 2023-08-03 15:17:06 +01:00
Hassan Ben Jobrane
0675a213b5 Merge pull request #2155 from nhost/changeset-release/main
chore: update versions
2023-08-01 14:28:53 +01:00
github-actions[bot]
a8ff383490 chore: update versions 2023-08-01 13:09:10 +00:00
David Barroso
960d815f68 chore(docs): added Nhost Run documentation (#2125)
Co-authored-by: Nuno Pato <nunopato@gmail.com>
2023-08-01 15:05:56 +02:00
Hassan Ben Jobrane
edf2b4e93f Merge pull request #2150 from nhost/feat/tweak-copy-service-form
feat(services): consistent naming for compute
2023-08-01 14:04:22 +01:00
Hassan Ben Jobrane
fe240542a4 Merge pull request #2152 from nhost/fix/services-form
fix(services): fix when config is null
2023-08-01 14:04:09 +01:00
Hassan Ben Jobrane
c7752c0657 Merge pull request #2154 from nhost/feat/run-announcement
feat(run): add annoucement for nhost run
2023-08-01 14:03:55 +01:00
Hassan Ben Jobrane
d1e2b1c75a chore: tweak announcement copy 2023-08-01 14:03:25 +01:00
Hassan Ben Jobrane
bcdab66bf8 chore: add changeset 2023-08-01 13:26:43 +01:00
Hassan Ben Jobrane
7636f40030 chore: remove unused 2023-08-01 13:25:28 +01:00
Hassan Ben Jobrane
e643bd3620 chore: add changeset 2023-08-01 13:23:13 +01:00
Hassan Ben Jobrane
311c7756d7 chore: add changeset 2023-08-01 13:21:17 +01:00
David Barroso
f967a2e596 chore(docs): added note about storage not being able to be downsized (#2153) 2023-08-01 14:16:16 +02:00
Hassan Ben Jobrane
4c4b253a71 chore: fix lockfile 2023-08-01 13:07:16 +01:00
Hassan Ben Jobrane
0f5f8c0d90 chore: revert lockfile 2023-08-01 13:00:37 +01:00
Hassan Ben Jobrane
37a7fc05d5 feat(run): add annoucement for nhost run 2023-08-01 12:37:23 +01:00
Hassan Ben Jobrane
bf93d87b36 fix(services): fix when config is null 2023-07-31 18:46:39 +01:00
Hassan Ben Jobrane
efb3dc7294 feat(services): consistent naming for compute 2023-07-27 17:24:42 +01:00
Hassan Ben Jobrane
42bd7807b2 Merge pull request #2148 from nhost/chore/increase-ci-timeout
chore: increase timeout for Publish to Docker Hub
2023-07-25 16:08:34 +01:00
Hassan Ben Jobrane
eea59bd202 chore: increase timeout for Publish to Docker Hub 2023-07-25 15:36:15 +01:00
Hassan Ben Jobrane
7248eb733f Merge pull request #2146 from nhost/changeset-release/main
chore: update versions
2023-07-25 13:12:50 +01:00
github-actions[bot]
fceb6a4a89 chore: update versions 2023-07-25 11:52:49 +00:00
Hassan Ben Jobrane
b10eca09a8 Merge pull request #2126 from nhost/renovate/turbo-monorepo
chore(deps): update dependency turbo to v1.10.11
2023-07-25 12:49:57 +01:00
Hassan Ben Jobrane
4799b65e96 Merge pull request #2144 from nhost/feat/add-run-services-to-logs
feat: add run/services to the logs
2023-07-25 12:24:13 +01:00
Hassan Ben Jobrane
067eb9d6a9 Merge pull request #2143 from nhost/feat/change-grafana-url
feat: change grafana url to point to dashboards
2023-07-25 12:24:03 +01:00
Hassan Ben Jobrane
219d5ecdcf Merge pull request #2140 from nhost/fix/create-service-form
fix: tweaking the create service form
2023-07-25 12:23:47 +01:00
Hassan Ben Jobrane
9073182d51 chore: add changeset 2023-07-25 12:20:15 +01:00
Hassan Ben Jobrane
bdb5783e79 chore: sync turbo version in Dockerfile 2023-07-25 12:19:09 +01:00
Hassan Ben Jobrane
ece717d6e0 chore: add changeset 2023-07-25 12:01:53 +01:00
Hassan Ben Jobrane
b135ef695c chore: add changeset 2023-07-25 11:38:00 +01:00
Hassan Ben Jobrane
82b3353110 chore: add changeset 2023-07-25 11:34:20 +01:00
Hassan Ben Jobrane
3f165a85e3 fix: grafana urls in generateAppServiceUrl.test 2023-07-25 11:14:58 +01:00
renovate[bot]
aa4018909f chore(deps): update dependency turbo to v1.10.11 2023-07-24 23:03:50 +00:00
Hassan Ben Jobrane
98397e3ccd feat: add run/services to the logs 2023-07-24 18:00:22 +01:00
Hassan Ben Jobrane
911e7112c9 feat: change grafana url to point to dashboards 2023-07-24 17:32:46 +01:00
Hassan Ben Jobrane
e62402ecfc feat(services): add toolip content 2023-07-24 10:24:24 +01:00
Hassan Ben Jobrane
9190dd726d Merge pull request #2138 from nhost/changeset-release/main
chore: update versions
2023-07-23 21:31:33 +01:00
github-actions[bot]
ae093283d0 chore: update versions 2023-07-23 20:13:51 +00:00
Hassan Ben Jobrane
875327fbea Merge pull request #2136 from tawsbob/bug/users-page-limit
fix: back to previous limit
2023-07-23 21:11:11 +01:00
Hassan Ben Jobrane
3d5c34f4ce chore: add changeset 2023-07-23 20:53:14 +01:00
Dellean Santos
58c2a20532 fix: back to previous limit 2023-07-23 16:17:10 -03:00
Hassan Ben Jobrane
6c90cb5024 fix: make command optional and set min replicas 0 2023-07-22 19:58:34 +01:00
Hassan Ben Jobrane
7e37570587 Merge pull request #2133 from nhost/chore/ci-trigger-release-manually
chore(ci): add support for triggering release manually
2023-07-21 19:27:04 +01:00
Hassan Ben Jobrane
87d225a840 chore(ci): trigger release manually 2023-07-21 19:08:34 +01:00
Hassan Ben Jobrane
7b0de27c80 Merge pull request #2132 from nhost/changeset-release/main
chore: update versions
2023-07-21 17:43:45 +01:00
github-actions[bot]
564fc76195 chore: update versions 2023-07-21 16:24:17 +00:00
Hassan Ben Jobrane
2ed4f40c12 Merge pull request #2120 from nhost/feat/services
feat: services
2023-07-21 17:21:44 +01:00
Hassan Ben Jobrane
d67a023e21 feat(services): block services for free apps 2023-07-21 16:51:58 +01:00
Hassan Ben Jobrane
c99d117d1c chore: add changeset 2023-07-21 15:40:08 +01:00
Hassan Ben Jobrane
a497a6ba0a feat(services): mod port url if values are empty 2023-07-21 12:57:18 +01:00
Hassan Ben Jobrane
160cd08cc7 feat(services): fix e2e nav test 2023-07-21 10:35:51 +01:00
Hassan Ben Jobrane
120151c40c feat(services): run pnpm install 2023-07-20 19:14:47 +01:00
Hassan Ben Jobrane
9dc16f29b3 feat(services): add services pagination 2023-07-20 19:10:10 +01:00
Hassan Ben Jobrane
964fc5644a feat(services): put services behind a feature flag 2023-07-20 19:09:48 +01:00
Hassan Ben Jobrane
2f907fc68f feat(services): tweak service form on mobile 2023-07-20 12:41:35 +01:00
Hassan Ben Jobrane
fe6cadc2cd feat(services): tweak services list 2023-07-20 11:40:45 +01:00
Hassan Ben Jobrane
338c8e5a80 feat(services): fix command on initialData 2023-07-19 17:55:47 +01:00
Hassan Ben Jobrane
e6f3a1a39d feat(services): tweak compute section 2023-07-19 16:20:52 +01:00
Hassan Ben Jobrane
a168faeb69 feat(services): tweak service form 2023-07-19 14:00:25 +01:00
Hassan Ben Jobrane
b1628c59b5 feat(services): refactor command field 2023-07-19 13:36:37 +01:00
Hassan Ben Jobrane
32a2f5db9a feat(services): fix replicas form section 2023-07-19 11:57:10 +01:00
Hassan Ben Jobrane
818a48f74d feat(services): refactor edit service 2023-07-19 10:58:56 +01:00
Hassan Ben Jobrane
bed377d05f feat(services): add service details page 2023-07-18 16:19:48 +01:00
Hassan Ben Jobrane
709a616cfa feat(services): fix e2e nav item count 2023-07-17 19:59:24 +01:00
Hassan Ben Jobrane
860e2d877c feat(services): fix linter errors 2023-07-17 19:40:21 +01:00
Hassan Ben Jobrane
5c6b2f88b9 fix: export component properly 2023-07-17 18:43:41 +01:00
Hassan Ben Jobrane
f151a0e872 feat(services): tweaks + show URL for ports/image 2023-07-17 17:01:28 +01:00
Hassan Ben Jobrane
4a84bbb410 feat(service): tweaks + add service details page 2023-07-17 16:10:18 +01:00
Hassan Ben Jobrane
fa3a50e323 feat(service): add GiB to storage capacity field 2023-07-16 23:48:48 +01:00
Hassan Ben Jobrane
398152358c feat(service): tweak compute form section 2023-07-16 23:43:44 +01:00
Hassan Ben Jobrane
34ae9046f3 feat(services): add fetch and delete services 2023-07-16 23:32:24 +01:00
Hassan Ben Jobrane
a478689587 feat(services): fix compute section 2023-07-14 19:54:24 +01:00
Hassan Ben Jobrane
9dbc0607dc feat(services): hook up create service to the api 2023-07-14 19:43:08 +01:00
Hassan Ben Jobrane
7455efdd53 feat(services): tweak create service form 2023-07-14 15:20:14 +01:00
Hassan Ben Jobrane
d0aff6141f feat(services): add info tooltip to each section 2023-07-14 13:59:41 +01:00
Hassan Ben Jobrane
aed0c4f82a feat(services): add create service form 2023-07-14 12:20:34 +01:00
Hassan Ben Jobrane
74d4276c1a feat(services): add new page for services 2023-07-13 11:55:20 +01:00
Hassan Ben Jobrane
1e98130aa1 Merge pull request #2113 from nhost/changeset-release/main
chore: update versions
2023-07-12 20:34:35 +01:00
github-actions[bot]
52e9b510da chore: update versions 2023-07-12 19:22:34 +00:00
Hassan Ben Jobrane
ece197eb6b Merge pull request #2116 from nhost/renovate/prettier-plugin-tailwindcss-0.x
chore(deps): update dependency prettier-plugin-tailwindcss to ^0.4.0
2023-07-12 20:20:50 +01:00
Hassan Ben Jobrane
d14e112bff chore: add changeset 2023-07-12 17:29:12 +01:00
renovate[bot]
83884f04a5 chore(deps): update dependency prettier-plugin-tailwindcss to ^0.4.0 2023-07-12 16:10:24 +00:00
Hassan Ben Jobrane
977de21e86 Merge pull request #2117 from nhost/chore/add-hasura-auth-version
chore: add hasura-auth version 0.20.2
2023-07-12 17:07:27 +01:00
Hassan Ben Jobrane
462a60a8f8 chore: fix hasura-auth version 2023-07-12 16:45:01 +01:00
Hassan Ben Jobrane
9aa4371ef4 chore: add changeset 2023-07-12 16:45:01 +01:00
Hassan Ben Jobrane
f0feddd83f chore: add hasura-auth version 0.20.2 2023-07-12 16:45:01 +01:00
Hassan Ben Jobrane
0748cab125 Merge pull request #2087 from nhost/renovate/vite-plugin-dts-3.x
chore(deps): update dependency vite-plugin-dts to v3
2023-07-12 16:41:59 +01:00
Hassan Ben Jobrane
27885491ee chore: fix test project subdomain 2023-07-12 13:55:14 +01:00
Hassan Ben Jobrane
a36bdbf907 chore: uncomment setting preview URL 2023-07-12 13:53:42 +01:00
Hassan Ben Jobrane
d3e8bb94ae chore: add changeset 2023-07-11 16:39:26 +01:00
Hassan Ben Jobrane
645595ee43 Revert "chore: increase playwright timeout"
This reverts commit 72d1e94cb3.
2023-07-11 16:36:46 +01:00
Hassan Ben Jobrane
4d82bc5609 Revert "chore: playwright: increase number of workers"
This reverts commit b4c10f9f8a.
2023-07-11 16:17:30 +01:00
Hassan Ben Jobrane
fdf1e555d8 chore: ci: comment Fetch Dashboard Preview URL 2023-07-11 16:13:50 +01:00
Hassan Ben Jobrane
90c694cbba chore: ci: Comment step Set Dashboard Preview URL 2023-07-11 15:51:03 +01:00
Hassan Ben Jobrane
3262fa7b37 chore: teardown: run playwright in slowMo 2023-07-11 15:09:25 +01:00
Hassan Ben Jobrane
ab43fe567f chore: fix inserting sql in hasura page 2023-07-11 14:47:21 +01:00
Hassan Ben Jobrane
b4c10f9f8a chore: playwright: increase number of workers 2023-07-11 14:26:22 +01:00
Hassan Ben Jobrane
f4c6e7cfab chore: bring back raw_sql fill 2023-07-11 13:58:41 +01:00
Hassan Ben Jobrane
72d1e94cb3 chore: increase playwright timeout 2023-07-11 13:36:44 +01:00
Hassan Ben Jobrane
82d221a48d Revert "chore: increase CI e2e timeout"
This reverts commit 3fe46771b9.
2023-07-11 11:57:36 +01:00
Hassan Ben Jobrane
3fe46771b9 chore: increase CI e2e timeout 2023-07-11 11:06:09 +01:00
Hassan Ben Jobrane
a1c487aa21 chore: fix lock file 2023-07-11 01:55:39 +01:00
Hassan Ben Jobrane
cf455608e2 chore: fix e2e tests 2023-07-11 01:40:17 +01:00
Hassan Ben Jobrane
5dac12dd41 chore: use node v18 2023-07-10 18:15:22 +01:00
Hassan Ben Jobrane
2389b46e0d chore: update node to v18 2023-07-10 16:40:41 +01:00
renovate[bot]
6fe2d22d0e chore(deps): update dependency vite-plugin-dts to v3 2023-07-10 15:23:12 +00:00
Hassan Ben Jobrane
0b439149e4 Merge pull request #2106 from nhost/renovate/pluralize-0.x
chore(deps): update dependency @types/pluralize to ^0.0.30
2023-07-10 16:19:28 +01:00
Hassan Ben Jobrane
a9d7da8af7 chore: add changeset 2023-07-10 16:09:14 +01:00
renovate[bot]
3ecc21a45e chore(deps): update dependency @types/pluralize to ^0.0.30 2023-07-10 14:30:41 +00:00
Hassan Ben Jobrane
aa19e85cdc Merge pull request #2088 from nhost/renovate/turbo-monorepo
chore(deps): update dependency turbo to v1.10.7
2023-07-10 15:28:49 +01:00
Hassan Ben Jobrane
26c650227d Merge pull request #2111 from nhost/fix/tweak-config-warning
fix: tweak warning in dark mode
2023-07-10 15:16:25 +01:00
Hassan Ben Jobrane
face99ccde chore: add changeset 2023-07-10 15:07:25 +01:00
Hassan Ben Jobrane
49bcc525ad chore: bump turbo version in Dockerfile 2023-07-10 15:07:25 +01:00
renovate[bot]
533563c893 chore(deps): update dependency turbo to v1.10.7 2023-07-10 15:07:25 +01:00
Hassan Ben Jobrane
cfe527307e chore: add changeset 2023-07-10 15:05:22 +01:00
Hassan Ben Jobrane
1e36c6706d Revert "chore: use node 18 for GH actions"
This reverts commit 6e40b114fc.
2023-07-10 15:01:46 +01:00
Hassan Ben Jobrane
6e40b114fc chore: use node 18 for GH actions 2023-07-10 13:58:11 +01:00
Hassan Ben Jobrane
77acf1385d Revert "chore: increase ci timeout"
This reverts commit cec7edd2d5.
2023-07-10 12:23:22 +01:00
Hassan Ben Jobrane
cec7edd2d5 chore: increase ci timeout 2023-07-10 10:50:55 +01:00
Hassan Ben Jobrane
9dbbdb3121 fix: show only when a repo is connected 2023-07-07 19:24:09 +01:00
Hassan Ben Jobrane
79d2602648 fix: tweak warning in dark mode 2023-07-07 18:52:24 +01:00
546 changed files with 23871 additions and 2745 deletions

View File

@@ -5,6 +5,5 @@
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
"updateInternalDependencies": "patch"
}

View File

@@ -26,10 +26,10 @@ runs:
path: ${{ steps.pnpm-cache-dir.outputs.dir }}
key: ${{ runner.os }}-node-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: ${{ runner.os }}-node-
- name: Use Node.js v16
- name: Use Node.js v18
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- shell: bash
name: Install packages
run: pnpm install --frozen-lockfile

View File

@@ -10,6 +10,7 @@ on:
- '**.md'
- '!.changeset/**'
- 'LICENSE'
workflow_dispatch:
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
@@ -41,6 +42,7 @@ jobs:
commit: 'chore: update versions'
title: 'chore: update versions'
publish: pnpm run release
createGithubReleases: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -61,12 +63,39 @@ jobs:
uses: ./.github/workflows/dashboard.yaml
secrets: inherit
publish-vercel:
name: Publish to Vercel
runs-on: ubuntu-latest
needs:
- test
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Node and dependencies
uses: ./.github/actions/install-dependencies
with:
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
TURBO_TEAM: ${{ env.TURBO_TEAM }}
- name: Setup Vercel CLI
run: pnpm add -g vercel
- name: Trigger a Vercel deployment
env:
VERCEL_ORG_ID: ${{ secrets.DASHBOARD_VERCEL_TEAM_ID }}
VERCEL_PROJECT_ID: ${{ secrets.DASHBOARD_VERCEL_PROJECT_ID }}
run: |
vercel pull --environment=production --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
vercel build --prod --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
vercel deploy --prebuilt --prod --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
publish-docker:
name: Publish to Docker Hub
runs-on: ubuntu-latest
needs:
- test
- version
- publish-vercel
steps:
- name: Checkout repository
uses: actions/checkout@v3
@@ -99,7 +128,7 @@ jobs:
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push to Docker Hub
uses: docker/build-push-action@v4
timeout-minutes: 60
timeout-minutes: 90
with:
context: .
file: ./dashboard/Dockerfile
@@ -112,42 +141,6 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
push: true
- name: Create GitHub Release
uses: taiki-e/create-gh-release-action@v1
with:
changelog: dashboard/CHANGELOG.md
token: ${{ secrets.GITHUB_TOKEN }}
prefix: ${{ env.DASHBOARD_PACKAGE }}@
ref: refs/tags/${{ env.DASHBOARD_PACKAGE }}@${{ needs.version.outputs.dashboardVersion }}
- name: Remove tag on failure
if: failure()
run: git push --delete origin ${{ env.DASHBOARD_PACKAGE }}@${{ needs.version.outputs.dashboardVersion }}
publish-vercel:
name: Publish to Vercel
runs-on: ubuntu-latest
needs:
- publish-docker
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Node and dependencies
uses: ./.github/actions/install-dependencies
with:
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
TURBO_TEAM: ${{ env.TURBO_TEAM }}
- name: Setup Vercel CLI
run: pnpm add -g vercel
- name: Trigger a Vercel deployment
env:
VERCEL_ORG_ID: ${{ secrets.DASHBOARD_VERCEL_TEAM_ID }}
VERCEL_PROJECT_ID: ${{ secrets.DASHBOARD_VERCEL_PROJECT_ID }}
run: |
vercel pull --environment=production --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
vercel build --prod --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
vercel deploy --prebuilt --prod --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
bump-cli:
name: Bump Dashboard version in the Nhost CLI

View File

@@ -146,7 +146,7 @@ jobs:
run: echo "NHOST_TEST_DASHBOARD_URL=https://${{ steps.fetch-dashboard-preview-url.outputs.preview_url }}" >> $GITHUB_ENV
# * Run the `ci` script of the current package of the matrix. Dependencies build is cached by Turborepo
- name: Run e2e tests
timeout-minutes: 7
timeout-minutes: 15
run: pnpm --filter="${{ matrix.package.name }}" run e2e
- id: file-name
if: ${{ failure() }}

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

@@ -2,5 +2,6 @@
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"eslint.workingDirectories": ["./dashboard"]
"eslint.workingDirectories": ["./dashboard"],
"typescript.tsdk": "node_modules/typescript/lib"
}

View File

@@ -7,7 +7,8 @@ import baseLibConfig from './vite.lib.config'
export default defineConfig({
...baseLibConfig,
optimizeDeps: {
include: ['react/jsx-runtime']
include: ['react/jsx-runtime'],
exclude: ['react-hook-form']
},
plugins: [react({ jsxRuntime: 'classic' }), ...baseLibConfig.plugins]
})

View File

@@ -1,5 +1,251 @@
# @nhost/dashboard
## 1.0.1
### Patch Changes
- @nhost/react-apollo@7.0.1
- @nhost/nextjs@2.0.1
## 1.0.0
### Major Changes
- bc9eff6e4: chore: remove support for using backendUrl when instantiating the Nhost client
### Patch Changes
- Updated dependencies [bc9eff6e4]
- @nhost/nextjs@2.0.0
- @nhost/react-apollo@7.0.0
## 0.21.1
### Patch Changes
- 97ced73a3: fix(dashboard): prevent dashboard from resolving secrets
## 0.21.0
### Minor Changes
- ed1a8d458: Update alert message on increasing PostgreSQL's volume capacity
- 2e2248fd4: feat(dashboard): add SQL editor
## 0.20.28
### Patch Changes
- 7c2c31082: feat: add support for users to delete their account
- @nhost/react-apollo@6.0.1
- @nhost/nextjs@1.13.40
## 0.20.27
### Patch Changes
- fa79b7709: chore(dashboard): tweaks and fixes to the service form and dialog
- 8df84d782: fix(dashboard): allow resetting custom domains
- @nhost/react-apollo@6.0.0
- @nhost/nextjs@1.13.39
## 0.20.26
### Patch Changes
- 331ba0376: feat(dashboard): add postgres storage capacity modifier in the settings
- b7f801874: feat(dashboard): add new settings page for custom domains
## 0.20.25
### Patch Changes
- @nhost/react-apollo@5.0.38
## 0.20.24
### Patch Changes
- e10389ecf: fix(dashboard): disable run tab when developing locally
- @nhost/react-apollo@5.0.37
## 0.20.23
### Patch Changes
- c01568a7d: chore(dashboard): show alert to update oauth providers
## 0.20.22
### Patch Changes
- c3efb7ec8: feat(dashboard): query latest announcement from platform
## 0.20.21
### Patch Changes
- 3e46d3873: chore: update link to node18 announcement
## 0.20.20
### Patch Changes
- @nhost/react-apollo@5.0.36
- @nhost/nextjs@1.13.38
## 0.20.19
### Patch Changes
- 75c4c8ae3: feat(dashboard): make env value input multiline
## 0.20.18
### Patch Changes
- 425d485f8: fix(dashboard): make sure dedicated resources pricing follows total resources
## 0.20.17
### Patch Changes
- ae324f67f: fix(dashboard): remove unused graphql fields
## 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
- 4a7ede11e: fix: distinguish files that were not uploaded
- 202b64723: feat(nhost-run): add support for one-click-install run services
- 074a0fa11: feat(dashboard): add settings toggle to enable/disable antivirus
- @nhost/react-apollo@5.0.33
- @nhost/nextjs@1.13.35
## 0.20.6
### Patch Changes
- b20761e97: feat(services): add pricing info and confirmation dialog
- 90df6d81d: fix(services): handle null values when editing a service
- aa8508467: fix: query service logs correctly
feat: enable multiline support for environment value input
## 0.20.5
### Patch Changes
- 8d7f84b8d: fix: make announcement adapt to theme
## 0.20.4
### Patch Changes
- 3b75bfce2: fix: make announcement close properly
- f49819075: fix: show correct values when dedicated resources are disabled
## 0.20.3
### Patch Changes
- e643bd362: fix(services): fix errors when config is null
- bcdab66bf: feat: add annoucement for nhost run
- f967a2e59: added note about storage not being able to be downsized
- 311c7756d: chore(services): consistent naming for compute
## 0.20.2
### Patch Changes
- 9073182d5: chore(dashboard): bump `turbo` to 1.10.11
- ece717d6e: feat(logs): show services in the logs page
- 82b335311: feat(metrics): change grafana link to point to the dashboards
- b135ef695: fix(services): set command as optional and set min replicas to 0
## 0.20.1
### Patch Changes
- 3d5c34f4c: fix(auth): fix users pagination limit
## 0.20.0
### Minor Changes
- c99d117d1: feat(services): add support for custom services
## 0.19.2
### Patch Changes
- face99ccd: chore(deps): bump turbo version
- cfe527307: style: tweak pull config warning in dark mode
- a9d7da8af: chore(deps): update dependency @types/pluralize to ^0.0.30
- 9aa4371ef: chore: add hasura-auth version 0.21.2
- d14e112bf: chore(deps): update dependency prettier-plugin-tailwindcss to ^0.4.0
- d3e8bb94a: chore(deps): update dependency vite-plugin-dts to v3
## 0.19.1
### Patch Changes

View File

@@ -1,13 +1,13 @@
FROM node:16-alpine AS pruner
FROM node:18-alpine AS pruner
RUN apk add --no-cache libc6-compat
RUN apk update
WORKDIR /app
RUN yarn global add turbo@1.10.6
RUN yarn global add turbo@1.10.11
COPY . .
RUN turbo prune --scope="@nhost/dashboard" --docker
FROM node:16-alpine AS builder
FROM node:18-alpine AS builder
ARG TURBO_TOKEN
ARG TURBO_TEAM
@@ -40,7 +40,7 @@ COPY turbo.json turbo.json
COPY config/ config/
RUN pnpm build:dashboard
FROM node:16-alpine AS runner
FROM node:18-alpine AS runner
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs

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

@@ -9,7 +9,7 @@ import { openProject } from '@/e2e/utils';
import { chromium } from '@playwright/test';
async function globalTeardown() {
const browser = await chromium.launch();
const browser = await chromium.launch({ slowMo: 1000 });
const context = await browser.newContext({
baseURL: TEST_DASHBOARD_URL,
@@ -46,18 +46,23 @@ async function globalTeardown() {
await hasuraPage.locator('a', { hasText: /data/i }).click();
await hasuraPage.getByRole('link', { name: /sql/i }).click();
await hasuraPage.locator('#raw_sql > textarea').fill(`
DO $$ DECLARE
tablename text;
BEGIN
FOR tablename IN
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public'
LOOP
EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(tablename) || ' CASCADE';
END LOOP;
END $$;
`);
// Set the value of the Ace code editor using JavaScript evaluation in the browser context
await hasuraPage.evaluate(() => {
const editor = ace.edit('raw_sql');
editor.setValue(`
DO $$ DECLARE
tablename text;
BEGIN
FOR tablename IN
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public'
LOOP
EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(tablename) || ' CASCADE';
END LOOP;
END $$;
`);
});
await hasuraPage.getByRole('button', { name: /run!/i }).click();
await hasuraPage.getByText(/sql executed!/i).waitFor();

View File

@@ -0,0 +1,5 @@
query InitQuery {
root {
enableServices
}
}

5
dashboard/hypertune.json Normal file
View File

@@ -0,0 +1,5 @@
{
"projectId": 2596,
"token": "U2FsdGVkX19+V8BJnVR0xLEC+42OW5qZl/A0i6beAaRmJoIhFh5Yf6eIKBzLbV9h",
"outputDirectoryPath": "src/hypertune"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/dashboard",
"version": "0.19.1",
"version": "1.0.1",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
@@ -19,7 +19,7 @@
},
"dependencies": {
"@apollo/client": "^3.7.10",
"@codemirror/language": "^6.3.0",
"@codemirror/lang-sql": "^6.5.4",
"@emotion/cache": "^11.10.5",
"@emotion/react": "^11.10.5",
"@emotion/server": "^11.4.0",
@@ -44,6 +44,8 @@
"@tanstack/react-query": "^4.16.1",
"@tanstack/react-table": "^8.5.30",
"@tanstack/react-virtual": "^3.0.0-beta.23",
"@uiw/codemirror-theme-github": "^4.21.20",
"@uiw/react-codemirror": "^4.21.20",
"analytics-node": "^6.2.0",
"bcryptjs": "^2.4.3",
"clsx": "^1.2.1",
@@ -54,6 +56,7 @@
"graphql-request": "^6.0.0",
"graphql-tag": "^2.12.6",
"graphql-ws": "^5.11.2",
"hypertune": "^1.4.4",
"just-kebab-case": "^4.1.1",
"lodash.debounce": "^4.0.8",
"next": "^12.3.1",
@@ -65,12 +68,15 @@
"react-error-boundary": "^4.0.0",
"react-hook-form": "^7.42.1",
"react-hot-toast": "^2.4.0",
"react-intersection-observer": "^9.5.2",
"react-is": "18.2.0",
"react-loading-skeleton": "^2.2.0",
"react-merge-refs": "^1.1.0",
"react-resizable-layout": "^0.7.2",
"react-syntax-highlighter": "^15.4.5",
"react-table": "^7.8.0",
"sharp": "^0.32.0",
"shell-quote": "^1.8.1",
"slugify": "^1.6.5",
"stripe": "^10.17.0",
"tailwind-merge": "^1.8.0",
@@ -101,13 +107,16 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@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.29",
"@types/pluralize": "^0.0.30",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@types/react-table": "^7.7.12",
"@types/shell-quote": "^1.7.1",
"@types/testing-library__jest-dom": "^5.14.5",
"@types/validator": "^13.7.10",
"@typescript-eslint/eslint-plugin": "^5.43.0",
@@ -137,7 +146,7 @@
"postcss": "^8.4.19",
"prettier": "^2.7.1",
"prettier-plugin-organize-imports": "^3.2.0",
"prettier-plugin-tailwindcss": "^0.3.0",
"prettier-plugin-tailwindcss": "^0.4.0",
"react-date-fns-hooks": "^0.9.4",
"require-from-string": "^2.0.2",
"snake-case": "^3.0.4",

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

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

@@ -0,0 +1,28 @@
import { Alert } from '@/components/ui/v2/Alert';
import { Text } from '@/components/ui/v2/Text';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
export default function DepricationNotice() {
const { currentProject } = useCurrentWorkspaceAndProject();
return (
!currentProject?.providersUpdated && (
<Alert severity="warning" className="grid place-content-center">
<Text color="warning" className="max-w-3xl text-sm">
On December 1st the old backend domain will cease to work. You need to
make sure your client is instantiated using the subdomain and region
and update your oauth2 settings. You can find more information{' '}
<a
target="_blank"
rel="noopener noreferrer"
className="underline"
href="https://github.com/nhost/nhost/discussions/2303"
>
here
</a>
.
</Text>
</Alert>
)
);
}

View File

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

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

@@ -15,6 +15,7 @@ export type PaginationProps = DetailedHTMLProps<
* Total number of pages.
*/
totalNrOfPages: number;
/**
* Number of total elements per page.
*/
@@ -23,6 +24,10 @@ export type PaginationProps = DetailedHTMLProps<
* Total number of elements.
*/
totalNrOfElements: number;
/**
* Label of the elements displayed ex: pages, users...
*/
itemsLabel: string;
/**
* Current page number.
*/
@@ -64,6 +69,7 @@ export default function Pagination({
elementsPerPage,
onPageChange,
totalNrOfElements,
itemsLabel,
...props
}: PaginationProps) {
return (
@@ -132,7 +138,7 @@ export default function Pagination({
{totalNrOfElements < currentPageNumber * elementsPerPage
? totalNrOfElements
: currentPageNumber * elementsPerPage}{' '}
of {totalNrOfElements} users
of {totalNrOfElements} {itemsLabel}
</Text>
</div>
</div>

View File

@@ -0,0 +1,100 @@
import { useDialog } from '@/components/common/DialogProvider';
import { NhostIcon } from '@/components/presentational/NhostIcon';
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { ArrowSquareOutIcon } from '@/components/ui/v2/icons/ArrowSquareOutIcon';
import { Link } from '@/components/ui/v2/Link';
import { Text } from '@/components/ui/v2/Text';
import { ChangePlanModal } from '@/features/projects/common/components/ChangePlanModal';
import { useIsCurrentUserOwner } from '@/features/projects/common/hooks/useIsCurrentUserOwner';
import Image from 'next/image';
interface UpgradeToProBannerProps {
title: string;
description: string;
}
export default function UpgradeToProBanner({
title,
description,
}: UpgradeToProBannerProps) {
const { openDialog, openAlertDialog } = useDialog();
const isOwner = useIsCurrentUserOwner();
return (
<Box
sx={{ backgroundColor: 'primary.light' }}
className="flex flex-col p-4 space-y-4 rounded-md lg:flex-row lg:items-center lg:space-y-0"
>
<div className="flex flex-col justify-between space-y-4">
<div className="space-y-2">
<div className="flex flex-col space-y-2 xs:flex-row xs:space-y-0 xs:space-x-2">
<Text>Available with</Text>
<div className="flex flex-row space-x-2">
<NhostIcon />
<Text sx={{ color: 'primary.main' }} className="font-semibold">
Nhost Pro
</Text>
</div>
</div>
<Text variant="h3">{title}</Text>
<Text>{description}</Text>
</div>
<div className="flex flex-col space-y-2 lg:flex-row lg:items-center lg:space-y-0 lg:space-x-2">
<Button
className="rounded-md"
onClick={() => {
if (isOwner) {
openDialog({
component: <ChangePlanModal />,
props: {
PaperProps: { className: 'p-0 max-w-xl w-full' },
},
});
} else {
openAlertDialog({
title: "You can't upgrade this project",
payload: (
<Text variant="subtitle1" component="span">
Ask an owner of this workspace to upgrade the project.
</Text>
),
props: {
secondaryButtonText: 'I understand',
hidePrimaryAction: true,
},
});
}
}}
>
Upgrade to Pro
</Button>
<Link
href="https://nhost.io/pricing"
target="_blank"
rel="noopener noreferrer"
underline="hover"
className="font-medium text-center"
sx={{
color: 'text.secondary',
}}
>
See all features
<ArrowSquareOutIcon className="w-4 h-4 ml-1" />
</Link>
</div>
</div>
<div className="max-w-xs mx-auto">
<Image
src="/illustration-unbox.png"
width={400}
height={260}
objectFit="contain"
/>
</div>
</Box>
);
}

View File

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

View File

@@ -178,6 +178,22 @@ export default function DataGridBody<T extends object>({
}
}
const getBackgroundCellColor = (
row: Row<T>,
column: DataBrowserGridColumn<T>,
) => {
// Grey out files not uploaded
if (!row.values.isUploaded) {
return 'grey.200';
}
if (column.isDisabled) {
return 'grey.100';
}
return 'background.paper';
};
return (
<div {...getTableBodyProps()} ref={bodyRef} {...props}>
{rows.length === 0 && !loading && (
@@ -260,9 +276,7 @@ export default function DataGridBody<T extends object>({
})}
cell={cell}
sx={{
backgroundColor: column.isDisabled
? 'grey.100'
: 'background.paper',
backgroundColor: getBackgroundCellColor(row, column),
color: isCellDisabled ? 'text.secondary' : 'text.primary',
}}
className={twMerge(

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';
@@ -54,7 +54,7 @@ export default function Header({ className, ...props }: HeaderProps) {
sx={{ backgroundColor: 'background.paper' }}
{...props}
>
<div className="grid grid-flow-col items-center gap-3">
<div className="grid grid-flow-col items-center gap-3 ">
<NavLink href="/" className="w-12">
<Logo className="mx-auto cursor-pointer" />
</NavLink>
@@ -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

@@ -114,7 +114,7 @@ export default function SettingsContainer({
<Box
{...root}
className={twMerge(
'grid grid-flow-row gap-4 rounded-lg border-1 py-4',
'grid grid-flow-row gap-4 overflow-hidden rounded-lg border-1 py-4',
root?.className || rootClassName,
)}
>
@@ -128,7 +128,11 @@ export default function SettingsContainer({
icon}
<div className="grid grid-flow-row gap-1">
<Text className="text-lg font-semibold">{title}</Text>
{typeof title === 'string' ? (
<Text className="text-lg font-semibold">{title}</Text>
) : (
title
)}
{description && <Text color="secondary">{description}</Text>}
</div>

View File

@@ -1,3 +1,4 @@
import DepricationNotice from '@/components/common/DepricationNotice/DepricationNotice';
import type { ProjectLayoutProps } from '@/components/layout/ProjectLayout';
import { ProjectLayout } from '@/components/layout/ProjectLayout';
import type { SettingsSidebarProps } from '@/components/layout/SettingsSidebar';
@@ -7,6 +8,7 @@ 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 { useTheme } from '@mui/material';
import { twMerge } from 'tailwind-merge';
export interface SettingsLayoutProps extends ProjectLayoutProps {
@@ -25,6 +27,7 @@ export default function SettingsLayout({
sidebarProps: { className: sidebarClassName, ...sidebarProps } = {},
...props
}: SettingsLayoutProps) {
const theme = useTheme();
const { currentProject } = useCurrentWorkspaceAndProject();
const hasGitRepo = !!currentProject?.githubRepository;
@@ -46,34 +49,44 @@ export default function SettingsLayout({
className="flex w-full flex-auto flex-col overflow-scroll overflow-x-hidden"
>
<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>
)}
<div className="flex flex-col space-y-2">
<DepricationNotice />
{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={twMerge(
'rounded-md px-2 py-px',
theme.palette.mode === 'dark'
? 'bg-brown text-copper'
: 'bg-slate-200 text-slate-700',
)}
>
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>
)}
</div>
{children}
</RetryableErrorBoundary>
</Box>

View File

@@ -200,6 +200,14 @@ export default function SettingsSidebar({
>
Secrets
</SettingsNavLink>
<SettingsNavLink
href="/custom-domains"
exact={false}
onClick={handleSelect}
>
Custom Domains
</SettingsNavLink>
</List>
</nav>
</Box>

View File

@@ -0,0 +1,35 @@
import type { ForwardedRef, SVGProps } from 'react';
import { forwardRef } from 'react';
function NhostIcon(
props: SVGProps<SVGSVGElement>,
ref: ForwardedRef<SVGSVGElement>,
) {
return (
<svg
ref={ref}
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-label="Logo of Nhost"
{...props}
>
<g clipPath="url(#clip0_9802_20458)">
<rect width="24" height="24" fill="#0052CD" />
<path
d="M17.4656 7.39804L12.4705 4.51369C12.0223 4.25553 11.466 4.25553 11.0169 4.51369C10.5688 4.77276 10.2906 5.25455 10.2906 5.77179V6.14813L9.96517 5.95996C9.51702 5.70179 8.96069 5.70179 8.51163 5.95996C8.06348 6.21903 7.78531 6.70082 7.78531 7.21896V7.5953L7.45988 7.40713C7.01173 7.14897 6.4554 7.14897 6.00634 7.40713C5.55819 7.66621 5.28003 8.14799 5.28003 8.66614V17.7037C5.28003 17.9637 5.43093 18.2055 5.66546 18.3182C5.89908 18.4318 6.1827 18.4009 6.38632 18.24L8.86342 16.2865L12.6832 18.4918C12.7886 18.5527 12.9068 18.5827 13.025 18.5827C13.1431 18.5827 13.2613 18.5518 13.3668 18.4918C13.5777 18.37 13.7086 18.1437 13.7086 17.9001V12.4613C13.7086 11.5687 13.2286 10.7378 12.4559 10.2915L11.2033 9.56789V5.7727C11.2033 5.57998 11.3069 5.4 11.4742 5.30364C11.6414 5.20728 11.8487 5.20728 12.0159 5.30364L17.0111 8.18708C17.5028 8.4707 17.8083 9.00066 17.8083 9.56789V16.3402C17.8083 16.5329 17.7046 16.7129 17.5374 16.8092L16.2138 17.5737V11.0142C16.2138 10.1215 15.7339 9.29064 14.9612 8.84431L11.8859 7.06897V8.12072L14.5058 9.63334C14.9976 9.91696 15.303 10.446 15.303 11.0142V17.9673C15.303 18.21 15.4339 18.4373 15.6448 18.5591C15.7502 18.62 15.8684 18.65 15.9866 18.65C16.1048 18.65 16.2229 18.6191 16.3284 18.5591L17.9937 17.5974C18.4419 17.3383 18.72 16.8565 18.72 16.3383V9.56608C18.7182 8.67614 18.2382 7.84438 17.4656 7.39804ZM11.9987 11.0805C12.4905 11.3641 12.7959 11.8932 12.7959 12.4613V17.5064L9.63246 15.6802L10.6478 14.8803C10.9996 14.603 11.2014 14.1876 11.2014 13.7394V10.6215L11.9987 11.0805ZM10.2906 10.0942V13.7376C10.2906 13.9049 10.2152 14.0603 10.0842 14.163L6.19088 17.2328V8.66523C6.19088 8.47251 6.29451 8.29253 6.46177 8.19617C6.62903 8.09981 6.83629 8.09981 7.00355 8.19617L7.78531 8.64705V15.1057L8.69616 14.3876V7.21896C8.69616 7.02625 8.79979 6.84626 8.96705 6.7499C9.13431 6.65355 9.34157 6.65355 9.50883 6.7499L10.2906 7.20078V9.04157L9.37975 8.51524V9.56789L10.2906 10.0942Z"
fill="white"
/>
</g>
<defs>
<clipPath id="clip0_9802_20458">
<rect width="24" height="24" rx="4" fill="white" />
</clipPath>
</defs>
</svg>
);
}
export default forwardRef(NhostIcon);

View File

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

View File

@@ -0,0 +1,100 @@
import { useDialog } from '@/components/common/DialogProvider';
import { Alert } from '@/components/ui/v2/Alert';
import { Button } from '@/components/ui/v2/Button';
import { ArrowSquareOutIcon } from '@/components/ui/v2/icons/ArrowSquareOutIcon';
import { Link } from '@/components/ui/v2/Link';
import { Text } from '@/components/ui/v2/Text';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { getToastStyleProps } from '@/utils/constants/settings';
import { useConfirmProvidersUpdatedMutation } from '@/utils/__generated__/graphql';
import { useTheme } from '@mui/material';
import { useState } from 'react';
import toast from 'react-hot-toast';
export default function ProvidersUpdatedAlert() {
const theme = useTheme();
const { openAlertDialog } = useDialog();
const [confirmed, setConfirmed] = useState(true);
const { currentProject } = useCurrentWorkspaceAndProject();
const [confirmProvidersUpdated] = useConfirmProvidersUpdatedMutation({
variables: { id: currentProject?.id },
});
async function handleSubmitConfirmation() {
const confirmProvidersUpdatedPromise = confirmProvidersUpdated();
await toast.promise(
confirmProvidersUpdatedPromise,
{
loading: 'Confirming...',
success: 'Your settings have been updated successfully.',
error: 'An error occurred while trying to confirm the message.',
},
getToastStyleProps(),
);
setConfirmed(false);
}
function handleOpenConfirmationDialog() {
openAlertDialog({
title: 'Confirm all providers updated?',
payload: (
<Text variant="subtitle1" component="span">
Please make sure to update all providers before continuing. Your
sign-in flows might break if you don&apos;t.
</Text>
),
props: {
onPrimaryAction: handleSubmitConfirmation,
},
});
}
if (!confirmed) {
return null;
}
return (
<Alert
severity="warning"
className="grid items-center grid-flow-row gap-2 p-4 place-items-center lg:grid-flow-col lg:place-content-between"
>
<div className="grid grid-flow-row gap-1 text-left">
<Text className="font-semibold">
Please update the Redirect URL for all providers being used
</Text>
<Text className="text-sm+">
We are deprecating your project&apos;s old DNS name in favor of
individual DNS names for each service. Please make sure to update your
providers to use the new auth specific URL under <b>Redirect URL</b>{' '}
before the 1st of February 2023.{' '}
<Link
href="https://github.com/nhost/nhost/discussions/1319"
target="_blank"
rel="noopener noreferrer"
underline="hover"
className="font-medium"
>
Read the discussion here.
<ArrowSquareOutIcon className="w-4 h-4 ml-1" />
</Link>
</Text>
</div>
<Button
variant="borderless"
className={
theme.palette.mode === 'dark'
? 'text-white hover:bg-brown'
: 'text-black hover:bg-orange-300'
}
onClick={handleOpenConfirmationDialog}
>
I have updated all Redirect URLs
</Button>
</Alert>
);
}

View File

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

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

@@ -0,0 +1,44 @@
import type { IconProps } from '@/components/ui/v2/icons';
function ArrowsClockwise(props: IconProps) {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
aria-label="Update"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M11.0103 6.23227H14.0103V3.23227"
stroke="currentColor"
strokeWidth="1.5"
strokeLinejoin="round"
/>
<path
d="M4.11084 4.11091C4.62156 3.60019 5.22788 3.19506 5.89517 2.91866C6.56246 2.64226 7.27766 2.5 7.99993 2.5C8.7222 2.5 9.4374 2.64226 10.1047 2.91866C10.772 3.19506 11.3783 3.60019 11.889 4.11091L14.0103 6.23223"
stroke="currentColor"
strokeWidth="1.5"
strokeLinejoin="round"
/>
<path
d="M4.98975 9.76773H1.98975V12.7677"
stroke="currentColor"
strokeWidth="1.5"
strokeLinejoin="round"
/>
<path
d="M11.8892 11.8891C11.3785 12.3998 10.7722 12.8049 10.1049 13.0813C9.43762 13.3577 8.72242 13.5 8.00015 13.5C7.27788 13.5 6.56269 13.3577 5.89539 13.0813C5.2281 12.8049 4.62179 12.3998 4.11107 11.8891L1.98975 9.76776"
stroke="currentColor"
strokeWidth="1.5"
strokeLinejoin="round"
/>
</svg>
);
}
ArrowsClockwise.displayName = 'NhostArrowsClockwise';
export default ArrowsClockwise;

View File

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

View File

@@ -0,0 +1,40 @@
import type { IconProps } from '@/components/ui/v2/icons';
function CubeIcon(props: IconProps) {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M14 11.0826V4.91742C14 4.8287 13.9764 4.74158 13.9316 4.665C13.8868 4.58841 13.8225 4.52513 13.7451 4.48163L8.24513 1.38788C8.17029 1.34578 8.08587 1.32367 8 1.32367C7.91413 1.32367 7.82971 1.34578 7.75487 1.38788L2.25487 4.48163C2.17754 4.52513 2.11318 4.58841 2.0684 4.665C2.02361 4.74158 2 4.8287 2 4.91742V11.0826C2 11.1713 2.02361 11.2584 2.0684 11.335C2.11318 11.4116 2.17754 11.4749 2.25487 11.5184L7.75487 14.6121C7.82971 14.6542 7.91413 14.6763 8 14.6763C8.08587 14.6763 8.17029 14.6542 8.24513 14.6121L13.7451 11.5184C13.8225 11.4749 13.8868 11.4116 13.9316 11.335C13.9764 11.2584 14 11.1713 14 11.0826Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M13.9311 4.66414L8.0594 8.00001L2.06934 4.66357"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8.05916 8L8.00049 14.6763"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
CubeIcon.displayName = 'NhostCubeIcon';
export default CubeIcon;

View File

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

View File

@@ -0,0 +1,27 @@
import type { IconProps } from '@/components/ui/v2/icons';
import { SvgIcon } from '@/components/ui/v2/icons/SvgIcon';
function ServicesIcon(props: IconProps) {
return (
<SvgIcon
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-label="Services"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.89295 4.15125H9.21701C9.28097 4.15125 9.33291 4.09959 9.33326 4.03565V2.8556C9.33291 2.79163 9.28097 2.73999 9.21701 2.73999H7.89295C7.82909 2.73999 7.77734 2.79174 7.77734 2.8556V4.03562C7.77734 4.09948 7.82911 4.15125 7.89295 4.15125ZM5.53406 5.84862H4.21001C4.14594 5.84826 4.09411 5.79643 4.09375 5.73236V4.55298C4.09411 4.48902 4.14606 4.43738 4.21001 4.43738H5.53406C5.5979 4.43738 5.64967 4.48912 5.64967 4.55298V5.73236C5.64967 5.79631 5.59801 5.84826 5.53406 5.84862ZM14.6307 6.48419C15.4316 6.48419 15.8114 6.77094 15.8521 6.80325L16 6.92016L15.9386 7.09971C15.8408 7.34738 15.69 7.57067 15.4968 7.75398C15.2062 8.04139 14.6791 8.38436 13.8221 8.38436H13.6839C13.337 9.26145 12.8707 10.2484 12.0879 11.1345C11.6196 11.6644 11.0689 12.1152 10.457 12.4696C9.71438 12.8901 8.90665 13.1835 8.06725 13.3376C7.4634 13.45 6.85036 13.5056 6.23616 13.5036C4.87658 13.5036 3.67717 13.2453 2.93893 12.7932C2.28012 12.3908 1.77374 11.7333 1.43337 10.8407C1.13576 10.0277 0.989105 9.1673 1.00063 8.30169C1.00204 8.04363 1.21146 7.83507 1.46954 7.83472H11.3503C11.471 7.8302 12.0678 7.77917 12.4399 7.57185C12.1318 7.08484 12.0446 6.51519 12.188 5.9087C12.2639 5.59123 12.3932 5.28898 12.5703 5.01479L12.7118 4.81068L12.9268 4.93471L12.9269 4.93473C12.9668 4.9583 13.8447 5.47632 13.9996 6.53843C14.2082 6.50325 14.4192 6.48511 14.6307 6.48419ZM3.7092 7.54529H2.38514C2.32128 7.54529 2.26953 7.49353 2.26953 7.42967V6.25029V6.24964C2.26953 6.1858 2.32128 6.13403 2.38514 6.13403H3.7092H3.70985C3.77369 6.13439 3.82516 6.18643 3.8248 6.25029V7.42969C3.8248 7.49353 3.77306 7.54529 3.7092 7.54529ZM4.21003 7.54529H5.53409C5.59794 7.54529 5.64969 7.49353 5.64969 7.42969V6.25029C5.65005 6.18643 5.59858 6.13439 5.53472 6.13403H5.53407H4.21001C4.14579 6.13403 4.09375 6.18607 4.09375 6.25029V7.42967C4.09413 7.49363 4.14606 7.54529 4.21003 7.54529ZM7.38597 7.54529H6.06191C5.99808 7.54529 5.94631 7.49353 5.94629 7.42967V6.25029V6.24964C5.94629 6.1858 5.99803 6.13403 6.06189 6.13403H7.38595H7.3866C7.45046 6.13439 7.50193 6.18643 7.50157 6.25029V7.42969C7.50157 7.49353 7.44983 7.54529 7.38597 7.54529ZM7.89295 7.54529H9.21701C9.28097 7.54529 9.33291 7.49365 9.33326 7.42969V6.25029C9.33326 6.18607 9.28122 6.13403 9.21701 6.13403H7.89295C7.82909 6.13403 7.77734 6.1858 7.77734 6.24964V6.25029V7.42967C7.77734 7.49353 7.82911 7.54529 7.89295 7.54529ZM6.06189 5.84862H7.38595C7.4499 5.84826 7.50156 5.79631 7.50156 5.73236V4.55298C7.50156 4.48912 7.44979 4.43738 7.38595 4.43738H6.06189C5.99804 4.43738 5.94629 4.48915 5.94629 4.55298V5.73236C5.94629 5.79631 5.99795 5.84826 6.06189 5.84862ZM9.21701 5.84862H7.89295C7.82901 5.84826 7.77734 5.79631 7.77734 5.73236V4.55298C7.77734 4.48915 7.82909 4.43738 7.89295 4.43738H9.21701C9.28097 4.43738 9.33291 4.48902 9.33326 4.55298V5.73236C9.33291 5.79643 9.28108 5.84826 9.21701 5.84862ZM11.0637 7.54529H9.73963C9.67579 7.54529 9.62402 7.49353 9.62402 7.42967V6.25029V6.24964C9.62402 6.1858 9.67579 6.13403 9.73963 6.13403H11.0637C11.1279 6.13403 11.1799 6.18607 11.1799 6.25029V7.42969C11.1796 7.49365 11.1277 7.54529 11.0637 7.54529Z"
fill="currentColor"
/>
</SvgIcon>
);
}
ServicesIcon.displayName = 'NhostServicesIcon';
export default ServicesIcon;

View File

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

View File

@@ -0,0 +1,26 @@
import type { IconProps } from '@/components/ui/v2/icons';
import { SvgIcon } from '@/components/ui/v2/icons/SvgIcon';
function TerminalIcon(props: IconProps) {
return (
<SvgIcon
width="16"
height="16"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
aria-label="Trash"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.49851 3.43968L2.93795 2.94141L1.94141 4.06252L2.50196 4.56079L6.37134 8.00024L2.50196 11.4397L1.94141 11.938L2.93795 13.0591L3.49851 12.5608L7.99851 8.56079C8.15863 8.41847 8.25024 8.21446 8.25024 8.00024C8.25024 7.78601 8.15863 7.582 7.99851 7.43968L3.49851 3.43968ZM7.99987 11.2502H7.24987V12.7502H7.99987H13.9999H14.7499V11.2502H13.9999H7.99987Z"
fill="currentColor"
/>
</SvgIcon>
);
}
TerminalIcon.displayName = 'NhostTerminalIcon';
export default TerminalIcon;

View File

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

View File

@@ -0,0 +1,161 @@
import { useDialog } from '@/components/common/DialogProvider';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
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 { getToastStyleProps } from '@/utils/constants/settings';
import {
useDeleteUserAccountMutation,
useGetAllWorkspacesAndProjectsQuery,
} from '@/utils/__generated__/graphql';
import { type ApolloError } from '@apollo/client';
import { useSignOut, useUserData } from '@nhost/nextjs';
import { useRouter } from 'next/router';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { twMerge } from 'tailwind-merge';
function ConfirmDeleteAccountModal({
close,
onDelete,
}: {
onDelete?: () => Promise<any>;
close: () => void;
}) {
const [remove, setRemove] = useState(false);
const [loadingRemove, setLoadingRemove] = useState(false);
const user = useUserData();
const { data, loading } = useGetAllWorkspacesAndProjectsQuery({
skip: !user,
});
const userHasProjects =
!loading && data?.workspaces.some((workspace) => workspace.projects.length);
const userData = useUserData();
const [deleteUserAccount] = useDeleteUserAccountMutation({
variables: { id: userData?.id },
});
const onClickConfirm = async () => {
setLoadingRemove(true);
await toast.promise(
deleteUserAccount(),
{
loading: 'Deleting your account...',
success: `The account 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 your account. Please try again.'
);
},
},
getToastStyleProps(),
);
onDelete?.();
close();
};
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 Account?
</Text>
{userHasProjects && (
<Text
variant="subtitle2"
className="font-bold"
sx={{ color: (theme) => `${theme.palette.error.main} !important` }}
>
You still have active projects. Please delete your projects before
proceeding with the account deletion.
</Text>
)}
<Box className="my-4">
<Checkbox
id="accept-1"
label={`I'm sure I want to delete my account`}
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={onClickConfirm}
disabled={userHasProjects}
loading={loadingRemove}
>
Delete
</Button>
<Button variant="outlined" color="secondary" onClick={close}>
Cancel
</Button>
</div>
</div>
</Box>
);
}
export default function DeleteAccount() {
const router = useRouter();
const { signOut } = useSignOut();
const { openDialog, closeDialog } = useDialog();
const onDelete = async () => {
await signOut();
await router.push('/signin');
};
const confirmDeleteAccount = async () => {
openDialog({
component: (
<ConfirmDeleteAccountModal close={closeDialog} onDelete={onDelete} />
),
});
};
return (
<SettingsContainer
title="Delete Account"
description="Please proceed with caution as the removal of your Personal Account and its contents from the Nhost platform is irreversible. This action will permanently delete your account and all associated data."
className="px-0"
slotProps={{
submitButton: { className: 'hidden' },
footer: { className: 'hidden' },
}}
>
<Box className="grid grid-flow-row border-t-1">
<Button
color="error"
className="mx-4 mt-4 justify-self-end"
onClick={confirmDeleteAccount}
>
Delete Personal Account
</Button>
</Box>
</SettingsContainer>
);
}

View File

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

View File

@@ -0,0 +1,5 @@
mutation deleteUserAccount($id: uuid!) {
deleteUser(id: $id) {
__typename
}
}

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,14 +30,6 @@ export type AuthServiceVersionFormValues = Yup.InferType<
typeof validationSchema
>;
const AVAILABLE_AUTH_VERSIONS = [
'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();
@@ -48,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

@@ -1,5 +1,5 @@
query GetAuthenticationSettings($appId: uuid!) {
config(appID: $appId, resolve: true) {
config(appID: $appId, resolve: false) {
id: __typename
__typename
auth {
@@ -24,6 +24,13 @@ query GetAuthenticationSettings($appId: uuid!) {
expiresIn
}
}
resources {
networking {
ingresses {
fqdn
}
}
}
user {
email {
allowed
@@ -38,6 +45,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

@@ -17,6 +17,7 @@ import { DotsHorizontalIcon } from '@/components/ui/v2/icons/DotsHorizontalIcon'
import { LockIcon } from '@/components/ui/v2/icons/LockIcon';
import { PencilIcon } from '@/components/ui/v2/icons/PencilIcon';
import { PlusIcon } from '@/components/ui/v2/icons/PlusIcon';
import { TerminalIcon } from '@/components/ui/v2/icons/TerminalIcon';
import { TrashIcon } from '@/components/ui/v2/icons/TrashIcon';
import { UsersIcon } from '@/components/ui/v2/icons/UsersIcon';
import { Link } from '@/components/ui/v2/Link';
@@ -86,7 +87,9 @@ function DataBrowserSidebarContent({
const isGitHubConnected = !!currentProject?.githubRepository;
const router = useRouter();
const {
asPath,
query: { workspaceSlug, appSlug, dataSourceSlug, schemaSlug, tableSlug },
} = router;
@@ -108,6 +111,8 @@ function DataBrowserSidebarContent({
*/
const [sidebarMenuTable, setSidebarMenuTable] = useState<string>();
const sqlEditorHref = `/${workspaceSlug}/${appSlug}/database/browser/default/editor`;
useEffect(() => {
if (selectedSchema) {
return;
@@ -258,194 +263,135 @@ function DataBrowserSidebarContent({
}
return (
<div className="grid gap-1">
{schemas && schemas.length > 0 && (
<Select
renderValue={(option) => (
<span className="grid grid-flow-col items-center gap-1">
{option?.label}
</span>
)}
slotProps={{
listbox: { className: 'max-w-[220px] min-w-[initial] w-full' },
popper: { className: 'max-w-[220px] min-w-[initial] w-full' },
}}
value={selectedSchema}
onChange={(_event, value) => setSelectedSchema(value as string)}
>
{schemas.map((schema) => (
<Option
className="grid grid-flow-col items-center gap-1"
value={schema.schema_name}
key={schema.schema_name}
>
<Text className="text-sm">
<Text component="span" color="disabled">
schema.
</Text>
<Text component="span" className="font-medium">
{schema.schema_name}
</Text>
</Text>
{(isSchemaLocked(schema.schema_name) || isGitHubConnected) && (
<LockIcon
className="h-3 w-3"
sx={{ color: 'text.secondary' }}
/>
)}
</Option>
))}
</Select>
)}
{isGitHubConnected && (
<Box
className="mt-1.5 grid grid-flow-row justify-items-start gap-2 rounded-md p-2"
sx={{ backgroundColor: 'grey.200' }}
>
<Text>
Your project is connected to GitHub. Please use the CLI to make
schema changes.
</Text>
<Link
href="https://docs.nhost.io/platform/github-integration"
target="_blank"
rel="noopener noreferrer"
underline="hover"
className="grid grid-flow-col items-center justify-start gap-1"
<Box className="flex h-full flex-col justify-between">
<Box className="flex flex-col px-2">
{schemas && schemas.length > 0 && (
<Select
renderValue={(option) => (
<span className="grid grid-flow-col items-center gap-1">
{option?.label}
</span>
)}
slotProps={{
listbox: { className: 'max-w-[220px] min-w-[initial] w-full' },
popper: { className: 'max-w-[220px] min-w-[initial] w-full' },
}}
value={selectedSchema}
onChange={(_event, value) => setSelectedSchema(value as string)}
>
Learn More <ArrowRightIcon />
</Link>
</Box>
)}
{!isSelectedSchemaLocked && (
<Button
variant="borderless"
endIcon={<PlusIcon />}
className="mt-1 w-full justify-between px-2"
onClick={() => {
openDrawer({
title: 'Create a New Table',
component: (
<CreateTableForm onSubmit={refetch} schema={selectedSchema} />
),
});
onSidebarItemClick();
}}
disabled={isGitHubConnected}
>
New Table
</Button>
)}
{schemas && schemas.length > 0 && tablesInSelectedSchema.length === 0 && (
<Text className="py-1.5 px-2 text-xs" color="disabled">
No tables found.
</Text>
)}
<nav aria-label="Database navigation">
{tablesInSelectedSchema.length > 0 && (
<List className="grid gap-1 pb-6">
{tablesInSelectedSchema.map((table) => {
const tablePath = `${table.table_schema}.${table.table_name}`;
const isSelected = `${schemaSlug}.${tableSlug}` === tablePath;
const isSidebarMenuOpen = sidebarMenuTable === tablePath;
return (
<ListItem.Root
className="group"
key={tablePath}
secondaryAction={
<Dropdown.Root
id="table-management-menu"
onOpen={() => setSidebarMenuTable(tablePath)}
onClose={() => setSidebarMenuTable(undefined)}
>
<Dropdown.Trigger
asChild
hideChevron
disabled={tablePath === removableTable}
{schemas.map((schema) => (
<Option
className="grid grid-flow-col items-center gap-1"
value={schema.schema_name}
key={schema.schema_name}
>
<Text className="text-sm">
<Text component="span" color="disabled">
schema.
</Text>
<Text component="span" className="font-medium">
{schema.schema_name}
</Text>
</Text>
{(isSchemaLocked(schema.schema_name) || isGitHubConnected) && (
<LockIcon
className="h-3 w-3"
sx={{ color: 'text.secondary' }}
/>
)}
</Option>
))}
</Select>
)}
{isGitHubConnected && (
<Box
className="mt-1.5 grid grid-flow-row justify-items-start gap-2 rounded-md p-2"
sx={{ backgroundColor: 'grey.200' }}
>
<Text>
Your project is connected to GitHub. Please use the CLI to make
schema changes.
</Text>
<Link
href="https://docs.nhost.io/platform/github-integration"
target="_blank"
rel="noopener noreferrer"
underline="hover"
className="grid grid-flow-col items-center justify-start gap-1"
>
Learn More <ArrowRightIcon />
</Link>
</Box>
)}
{!isSelectedSchemaLocked && (
<Button
variant="borderless"
endIcon={<PlusIcon />}
className="mt-1 w-full justify-between px-2"
onClick={() => {
openDrawer({
title: 'Create a New Table',
component: (
<CreateTableForm onSubmit={refetch} schema={selectedSchema} />
),
});
onSidebarItemClick();
}}
disabled={isGitHubConnected}
>
New Table
</Button>
)}
{schemas && schemas.length > 0 && tablesInSelectedSchema.length === 0 && (
<Text className="py-1.5 px-2 text-xs" color="disabled">
No tables found.
</Text>
)}
<nav aria-label="Database navigation">
{tablesInSelectedSchema.length > 0 && (
<List className="grid gap-1 pb-6">
{tablesInSelectedSchema.map((table) => {
const tablePath = `${table.table_schema}.${table.table_name}`;
const isSelected = `${schemaSlug}.${tableSlug}` === tablePath;
const isSidebarMenuOpen = sidebarMenuTable === tablePath;
return (
<ListItem.Root
className="group"
key={tablePath}
secondaryAction={
<Dropdown.Root
id="table-management-menu"
onOpen={() => setSidebarMenuTable(tablePath)}
onClose={() => setSidebarMenuTable(undefined)}
>
<IconButton
variant="borderless"
color={isSelected ? 'primary' : 'secondary'}
className={twMerge(
!isSelected &&
'opacity-0 group-focus-within:opacity-100 group-hover:opacity-100 group-active:opacity-100',
)}
<Dropdown.Trigger
asChild
hideChevron
disabled={tablePath === removableTable}
>
<DotsHorizontalIcon />
</IconButton>
</Dropdown.Trigger>
<Dropdown.Content menu PaperProps={{ className: 'w-52' }}>
{isGitHubConnected ? (
<Dropdown.Item
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
onClick={() =>
handleEditPermissionClick(
table.table_schema,
table.table_name,
true,
)
}
<IconButton
variant="borderless"
color={isSelected ? 'primary' : 'secondary'}
className={twMerge(
!isSelected &&
'opacity-0 group-focus-within:opacity-100 group-hover:opacity-100 group-active:opacity-100',
)}
>
<UsersIcon
className="h-4 w-4"
sx={{ color: 'text.secondary' }}
/>
<span>View Permissions</span>
</Dropdown.Item>
) : (
[
!isSelectedSchemaLocked && (
<Dropdown.Item
key="edit-table"
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
onClick={() =>
openDrawer({
title: 'Edit Table',
component: (
<EditTableForm
onSubmit={async () => {
await queryClient.refetchQueries([
`${dataSourceSlug}.${table.table_schema}.${table.table_name}`,
]);
await refetch();
}}
schema={table.table_schema}
table={table}
/>
),
})
}
>
<PencilIcon
className="h-4 w-4"
sx={{ color: 'text.secondary' }}
/>
<span>Edit Table</span>
</Dropdown.Item>
),
!isSelectedSchemaLocked && (
<Divider
key="edit-table-separator"
component="li"
/>
),
<DotsHorizontalIcon />
</IconButton>
</Dropdown.Trigger>
<Dropdown.Content
menu
PaperProps={{ className: 'w-52' }}
>
{isGitHubConnected ? (
<Dropdown.Item
key="edit-permissions"
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
onClick={() =>
handleEditPermissionClick(
table.table_schema,
table.table_name,
true,
)
}
>
@@ -453,68 +399,135 @@ function DataBrowserSidebarContent({
className="h-4 w-4"
sx={{ color: 'text.secondary' }}
/>
<span>Edit Permissions</span>
</Dropdown.Item>,
!isSelectedSchemaLocked && (
<Divider
key="edit-permissions-separator"
component="li"
/>
),
!isSelectedSchemaLocked && (
<span>View Permissions</span>
</Dropdown.Item>
) : (
[
!isSelectedSchemaLocked && (
<Dropdown.Item
key="edit-table"
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
onClick={() =>
openDrawer({
title: 'Edit Table',
component: (
<EditTableForm
onSubmit={async () => {
await queryClient.refetchQueries([
`${dataSourceSlug}.${table.table_schema}.${table.table_name}`,
]);
await refetch();
}}
schema={table.table_schema}
table={table}
/>
),
})
}
>
<PencilIcon
className="h-4 w-4"
sx={{ color: 'text.secondary' }}
/>
<span>Edit Table</span>
</Dropdown.Item>
),
!isSelectedSchemaLocked && (
<Divider
key="edit-table-separator"
component="li"
/>
),
<Dropdown.Item
key="delete-table"
key="edit-permissions"
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
sx={{ color: 'error.main' }}
onClick={() =>
handleDeleteTableClick(
handleEditPermissionClick(
table.table_schema,
table.table_name,
)
}
>
<TrashIcon
<UsersIcon
className="h-4 w-4"
sx={{ color: 'error.main' }}
sx={{ color: 'text.secondary' }}
/>
<span>Delete Table</span>
</Dropdown.Item>
),
]
)}
</Dropdown.Content>
</Dropdown.Root>
}
>
<ListItem.Button
dense
selected={isSelected}
disabled={tablePath === removableTable}
className="group-focus-within:pr-9 group-hover:pr-9 group-active:pr-9"
sx={{
paddingRight:
(isSelected || isSidebarMenuOpen) &&
'2.25rem !important',
}}
component={NavLink}
href={`/${workspaceSlug}/${appSlug}/database/browser/default/${table.table_schema}/${table.table_name}`}
onClick={() => {
if (onSidebarItemClick) {
onSidebarItemClick(`default.${tablePath}`);
}
}}
<span>Edit Permissions</span>
</Dropdown.Item>,
!isSelectedSchemaLocked && (
<Divider
key="edit-permissions-separator"
component="li"
/>
),
!isSelectedSchemaLocked && (
<Dropdown.Item
key="delete-table"
className="grid grid-flow-col items-center gap-2 p-2 text-sm+ font-medium"
sx={{ color: 'error.main' }}
onClick={() =>
handleDeleteTableClick(
table.table_schema,
table.table_name,
)
}
>
<TrashIcon
className="h-4 w-4"
sx={{ color: 'error.main' }}
/>
<span>Delete Table</span>
</Dropdown.Item>
),
]
)}
</Dropdown.Content>
</Dropdown.Root>
}
>
<ListItem.Text>{table.table_name}</ListItem.Text>
</ListItem.Button>
</ListItem.Root>
);
})}
</List>
)}
</nav>
</div>
<ListItem.Button
dense
selected={isSelected}
disabled={tablePath === removableTable}
className="group-focus-within:pr-9 group-hover:pr-9 group-active:pr-9"
sx={{
paddingRight:
(isSelected || isSidebarMenuOpen) &&
'2.25rem !important',
}}
component={NavLink}
href={`/${workspaceSlug}/${appSlug}/database/browser/default/${table.table_schema}/${table.table_name}`}
onClick={() => {
if (onSidebarItemClick) {
onSidebarItemClick(`default.${tablePath}`);
}
}}
>
<ListItem.Text>{table.table_name}</ListItem.Text>
</ListItem.Button>
</ListItem.Root>
);
})}
</List>
)}
</nav>
</Box>
<Box className="border-t">
<ListItem.Button
dense
selected={asPath === sqlEditorHref}
className="flex border group-focus-within:pr-9 group-hover:pr-9 group-active:pr-9"
component={NavLink}
href={sqlEditorHref}
>
<div className="flex w-full flex-row items-center justify-center space-x-4">
<TerminalIcon />
<span className="flex">SQL Editor</span>
</div>
</ListItem.Button>
</Box>
</Box>
);
}
@@ -580,7 +593,7 @@ export default function DataBrowserSidebar({
<Box
component="aside"
className={twMerge(
'absolute top-0 z-[35] h-full w-full overflow-auto border-r-1 px-2 pt-2 pb-17 motion-safe:transition-transform sm:relative sm:z-0 sm:h-full sm:py-2.5 sm:transition-none',
'absolute top-0 z-[35] h-full w-full overflow-auto border-r-1 pt-2 pb-17 motion-safe:transition-transform sm:relative sm:z-0 sm:h-full sm:pt-2.5 sm:pb-0 sm:transition-none',
expanded ? 'translate-x-0' : '-translate-x-full sm:translate-x-0',
className,
)}

View File

@@ -0,0 +1,263 @@
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Alert } from '@/components/ui/v2/Alert';
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { InfoIcon } from '@/components/ui/v2/icons/InfoIcon';
import { PlayIcon } from '@/components/ui/v2/icons/PlayIcon';
import { Input } from '@/components/ui/v2/Input';
import { Switch } from '@/components/ui/v2/Switch';
import { Table } from '@/components/ui/v2/Table';
import { TableBody } from '@/components/ui/v2/TableBody';
import { TableCell } from '@/components/ui/v2/TableCell';
import { TableHead } from '@/components/ui/v2/TableHead';
import { TableRow } from '@/components/ui/v2/TableRow';
import { Text } from '@/components/ui/v2/Text';
import { Tooltip } from '@/components/ui/v2/Tooltip';
import { useRunSQL } from '@/features/database/dataGrid/hooks/useRunSQL';
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
import { PostgreSQL, sql } from '@codemirror/lang-sql';
import { useTheme } from '@mui/material';
import { githubDark, githubLight } from '@uiw/codemirror-theme-github';
import CodeMirror from '@uiw/react-codemirror';
import { useCallback, useState } from 'react';
import { useResizable } from 'react-resizable-layout';
export default function SQLEditor() {
const theme = useTheme();
const isPlatform = useIsPlatform();
const [sqlCode, setSQLCode] = useState('');
const [track, setTrack] = useState(false);
const [cascade, setCascade] = useState(false);
const [readOnly, setReadOnly] = useState(false);
const [isMigration, setIsMigration] = useState(false);
const [migrationName, setMigrationName] = useState('');
const onChange = useCallback((value: string) => setSQLCode(value), []);
const { runSQL, loading, errorMessage, commandOk, rows, columns } = useRunSQL(
sqlCode,
track,
cascade,
readOnly,
isMigration,
migrationName,
);
const { position, separatorProps } = useResizable({
axis: 'y',
initial: 400,
min: 50,
reverse: true,
});
return (
<Box className="flex flex-1 flex-col justify-center overflow-hidden">
<Box className="flex flex-col space-y-2 border-b p-4">
<Text className="font-semibold">Raw SQL</Text>
<Box className="flex flex-col justify-between space-y-2 lg:flex-row lg:space-y-0 lg:space-x-4">
<Box className="flex w-full flex-col space-y-2 lg:flex-row lg:space-x-4 lg:space-y-0 xl:h-10">
<Box className="flex items-center space-x-2">
<Switch
label={
<Text variant="subtitle1" component="span">
Track this
</Text>
}
checked={track}
onChange={(event) => setTrack(event.currentTarget.checked)}
/>
<Tooltip title="If you are creating tables, views or functions, checking this will also expose them over the GraphQL API as top level fields. Functions only intended to be used as computed fields should not be tracked.">
<InfoIcon
aria-label="Info"
className="h-4 w-4"
color="primary"
/>
</Tooltip>
</Box>
<Box className="flex items-center space-x-2">
<Switch
label={
<Text variant="subtitle1" component="span">
Cascade metadata
</Text>
}
checked={cascade}
onChange={(e) => setCascade(e.target.checked)}
/>
<Tooltip title="Cascade actions on all dependent metadata references, like relationships and permissions">
<InfoIcon
aria-label="Info"
className="h-4 w-4"
color="primary"
/>
</Tooltip>
</Box>
<Box className="flex items-center space-x-2">
<Switch
label={
<Text variant="subtitle1" component="span">
Read only
</Text>
}
checked={readOnly}
onChange={(e) => setReadOnly(e.target.checked)}
/>
<Tooltip title="When set to true, the request will be run in READ ONLY transaction access mode which means only select queries will be successful. This flag ensures that the GraphQL schema is not modified and is hence highly performant.">
<InfoIcon
aria-label="Info"
className="h-4 w-4"
color="primary"
/>
</Tooltip>
</Box>
{!isPlatform && (
<Box className="flex flex-col space-x-0 space-y-2 xl:flex-row xl:space-x-4 xl:space-y-0">
<Box className="flex items-center space-x-2">
<Switch
label={
<Text variant="subtitle1" component="span">
This is a migration
</Text>
}
checked={isMigration}
onChange={(e) => setIsMigration(e.target.checked)}
/>
<Tooltip title="Create a migration file with the SQL statement">
<InfoIcon
aria-label="Info"
className="h-4 w-4"
color="primary"
/>
</Tooltip>
</Box>
{isMigration && (
<Input
name="isMigration"
id="isMigration"
placeholder="migration_name"
className="h-auto w-auto max-w-md"
fullWidth
hideEmptyHelperText
onChange={(e) => setMigrationName(e.target.value)}
/>
)}
</Box>
)}
</Box>
<Button
disabled={loading || !sqlCode.trim()}
variant="contained"
className="self-start"
startIcon={<PlayIcon />}
onClick={runSQL}
>
Run
</Button>
</Box>
</Box>
<CodeMirror
value={sqlCode}
height="100%"
className="min-h-[100px] flex-1 overflow-y-auto"
theme={theme.palette.mode === 'light' ? githubLight : githubDark}
extensions={[sql({ dialect: PostgreSQL })]}
onChange={onChange}
/>
<Box
className="h-2 border-t hover:cursor-row-resize"
sx={{
background: theme.palette.background.default,
}}
{...separatorProps}
/>
<Box
className="flex items-start overflow-auto p-4"
style={{ height: position }}
>
{loading && (
<ActivityIndicator
className="mx-auto self-center"
circularProgressProps={{
className: 'w-5 h-5',
}}
/>
)}
{errorMessage && (
<Alert
severity="error"
className="mx-auto grid grid-flow-row place-content-center gap-2 self-center"
>
<code>{errorMessage}</code>
</Alert>
)}
{!loading && !errorMessage && commandOk && (
<Alert
severity="success"
className="mx-auto grid grid-flow-row place-content-center gap-2 self-center"
>
<code>Success, no rows returned</code>
</Alert>
)}
{!loading && !errorMessage && (
<Table
style={{
tableLayout: 'auto',
}}
className="w-auto"
>
<TableHead
sx={{
background: theme.palette.background.default,
}}
>
<TableRow>
{columns.map((header) => (
<TableCell
key={header}
scope="col"
className="whitespace-nowrap border px-6 py-3 font-bold"
>
{header}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.map((row, rowIndex) => (
<TableRow
// eslint-disable-next-line react/no-array-index-key
key={String(rowIndex)}
// className="px-6 py-4 border whitespace-nowrap"
>
{row.map((value, valueIndex) => (
<TableCell
// eslint-disable-next-line react/no-array-index-key
key={`${value}-${valueIndex}`}
className="whitespace-nowrap border px-6 py-4"
>
{value}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
)}
</Box>
</Box>
);
}

View File

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

View File

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

View File

@@ -0,0 +1,283 @@
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { generateAppServiceUrl } from '@/features/projects/common/utils/generateAppServiceUrl';
import { getToastStyleProps } from '@/utils/constants/settings';
import { getHasuraAdminSecret } from '@/utils/env';
import { parseIdentifiersFromSQL } from '@/utils/sql';
import toast from 'react-hot-toast';
import { useState } from 'react';
export default function useRunSQL(
sqlCode: string,
track: boolean,
cascade: boolean,
readOnly: boolean,
isMigration: boolean,
migrationName: string,
) {
const { currentProject } = useCurrentWorkspaceAndProject();
const [loading, setLoading] = useState(false);
const [commandOk, setCommandOk] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const [columns, setColumns] = useState<string[]>([]);
const [rows, setRows] = useState<string[][]>([[]]);
const appUrl = generateAppServiceUrl(
currentProject?.subdomain,
currentProject?.region,
'hasura',
);
const adminSecret =
process.env.NEXT_PUBLIC_ENV === 'dev'
? getHasuraAdminSecret()
: currentProject?.config?.hasura.adminSecret;
const toastStyle = getToastStyleProps();
const createMigration = async (
inputSQL: string,
migration: string,
isCascade: boolean,
) => {
try {
const migrationApiResponse = await fetch(`${appUrl}/apis/migrate`, {
method: 'POST',
headers: { 'x-hasura-admin-secret': adminSecret },
body: JSON.stringify({
name: migration,
datasource: 'default',
up: [
{
type: 'run_sql',
args: {
source: 'default',
sql: inputSQL,
cascade: isCascade,
read_only: false,
},
},
],
down: [
{
type: 'run_sql',
args: {
source: 'default',
sql: '-- Could not auto-generate a down migration.',
cascade: isCascade,
read_only: false,
},
},
],
}),
});
if (!migrationApiResponse.ok) {
throw new Error('Migration API call failed');
}
return {
error: null,
};
} catch (createMigrationError) {
toast.error('An error happened when calling the migration API', {
style: toastStyle.style,
...toastStyle.error,
});
return {
error: createMigrationError,
};
}
};
const sendSQLToHasura = async (
inputSQL: string,
isCascade: boolean,
isReadOnly: boolean,
) => {
try {
if (!inputSQL) {
return {
result_type: 'error',
columns: [],
rows: [],
queryApiError: 'No SQL provided',
};
}
const response = await fetch(`${appUrl}/v2/query`, {
method: 'POST',
headers: { 'x-hasura-admin-secret': adminSecret },
body: JSON.stringify({
type: 'run_sql',
args: {
source: 'default',
sql: inputSQL,
cascade: isCascade,
read_only: isReadOnly,
},
}),
});
if (!response.ok) {
const errorResponse = await response.json();
const queryApiError =
errorResponse?.internal?.error?.message || 'Unknown error';
return {
result_type: 'error',
columns: [],
rows: [],
error: queryApiError,
};
}
const responseBody = await response.json();
if (responseBody?.result_type === 'TuplesOk') {
return {
result_type: 'TuplesOk',
columns: responseBody.result[0],
rows: responseBody.result.slice(1),
error: '',
};
}
if (responseBody?.result_type === 'CommandOk') {
return {
result_type: 'CommandOk',
columns: [],
rows: [],
error: '',
};
}
// If the result_type is neither TuplesOk nor CommandOk
return {
result_type: 'error',
columns: [],
rows: [],
error: 'Unknown response type',
};
} catch (error) {
return {
result_type: 'error',
columns: [],
rows: [],
error: error.message || 'Unknown error',
};
}
};
const updateMetadata = async (inputSQL: string) => {
const entities = parseIdentifiersFromSQL(inputSQL);
const tablesOrViewEntities = entities.filter(
(entity) => entity.type !== 'function',
);
const functionEntities = entities.filter(
(entity) => entity.type === 'function',
);
const trackTablesOrViews = tablesOrViewEntities.map(({ name, schema }) => ({
type: 'pg_track_table',
args: {
source: 'default',
table: {
name,
schema,
},
},
}));
const trackFunctions = functionEntities.map(({ name, schema }) => ({
type: 'pg_track_function',
args: {
source: 'default',
function: {
name,
schema,
configuration: {},
},
},
}));
const metaDataPayload = {
source: 'default',
type: 'bulk',
args: [...trackTablesOrViews, ...trackFunctions],
};
try {
if (entities.length > 0) {
const metadataApiResponse = await fetch(`${appUrl}/v1/metadata`, {
method: 'POST',
headers: { 'x-hasura-admin-secret': adminSecret },
body: JSON.stringify(metaDataPayload),
});
if (!metadataApiResponse.ok) {
throw new Error('Metadata API call failed');
}
}
} catch (error) {
toast.error('An error happened when calling the metadata API', {
style: toastStyle.style,
...toastStyle.error,
});
}
};
const runSQL = async () => {
setLoading(true);
setCommandOk(false);
setErrorMessage('');
if (isMigration) {
const { error: createMigrationError } = await createMigration(
sqlCode,
migrationName,
cascade,
);
setCommandOk(!createMigrationError);
if (createMigrationError) {
setErrorMessage('An unknown error occurred');
}
// if running the migration fails then we don't update the metadata
if (track && !createMigrationError) {
await updateMetadata(sqlCode);
}
} else {
const {
result_type,
error: $error,
columns: $columns,
rows: $rows,
} = await sendSQLToHasura(sqlCode, cascade, readOnly);
setCommandOk(result_type === 'CommandOk');
setColumns($columns);
setRows($rows);
setErrorMessage($error);
// if running the sql fails then we don't update the metadata
if (track && !$error) {
await updateMetadata(sqlCode);
}
}
setLoading(false);
};
return {
runSQL,
loading,
errorMessage,
commandOk,
rows,
columns,
};
}

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

@@ -0,0 +1,157 @@
import { useUI } from '@/components/common/UIProvider';
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Alert } from '@/components/ui/v2/Alert';
import { Box } from '@/components/ui/v2/Box';
import { Input } from '@/components/ui/v2/Input';
import { UpgradeNotification } from '@/features/projects/common/components/UpgradeNotification';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import {
useGetPostgresSettingsQuery,
useUpdateConfigMutation,
} from '@/generated/graphql';
import { getToastStyleProps } from '@/utils/constants/settings';
import { getServerError } from '@/utils/getServerError';
import { yupResolver } from '@hookform/resolvers/yup';
import { useEffect } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import * as Yup from 'yup';
const validationSchema = Yup.object({
capacity: Yup.number().required(),
});
export type AuthDomainFormValues = Yup.InferType<typeof validationSchema>;
export default function AuthDomain() {
const { maintenanceActive } = useUI();
const { currentProject } = useCurrentWorkspaceAndProject();
const {
data,
loading,
error,
refetch: refetchPostgresSettings,
} = useGetPostgresSettingsQuery({
variables: { appId: currentProject?.id },
fetchPolicy: 'cache-only',
});
const capacity =
data?.config?.postgres?.resources?.storage?.capacity ??
currentProject.plan.featureMaxDbSize;
const [updateConfig] = useUpdateConfigMutation();
const form = useForm<{ capacity: number }>({
reValidateMode: 'onSubmit',
defaultValues: { capacity },
resolver: yupResolver(validationSchema),
});
const { formState, register, reset } = form;
const isDirty = Object.keys(formState.dirtyFields).length > 0;
useEffect(() => {
if (data && !loading) {
reset({ capacity });
}
}, [loading, data, reset, capacity]);
if (loading) {
return (
<ActivityIndicator
delay={1000}
label="Loading Auth Domain Settings..."
className="justify-center"
/>
);
}
if (error) {
throw error;
}
async function handleSubmit(formValues: AuthDomainFormValues) {
try {
await toast.promise(
updateConfig({
variables: {
appId: currentProject.id,
config: {
postgres: {
resources: {
storage: {
capacity: formValues.capacity,
},
},
},
},
},
}),
{
loading: `Database storage capacity is being updated...`,
success: `Database storage capacity has been updated successfully.`,
error: getServerError(
`An error occurred while trying to update the database storage capacity.`,
),
},
getToastStyleProps(),
);
form.reset(formValues);
await refetchPostgresSettings();
} catch {
// Note: The toast will handle the error.
}
}
return (
<FormProvider {...form}>
<Form onSubmit={handleSubmit}>
<SettingsContainer
title="Storage capacity"
description="Specify the storage capacity for your PostgreSQL database."
slotProps={{
submitButton: {
disabled: !isDirty || maintenanceActive,
loading: formState.isSubmitting,
},
}}
className="flex flex-col"
>
{currentProject.plan.isFree && (
<UpgradeNotification message="Unlock by upgrading your project to the Pro plan." />
)}
<Box className="grid grid-flow-row lg:grid-cols-5">
<Input
{...register('capacity')}
id="capacity"
name="capacity"
type="number"
fullWidth
disabled={currentProject.plan.isFree}
className="lg:col-span-2"
error={Boolean(formState.errors.capacity?.message)}
helperText={formState.errors.capacity?.message}
slotProps={{
inputRoot: {
min: capacity,
},
}}
/>
</Box>
{!currentProject.plan.isFree && (
<Alert severity="info" className="col-span-6 text-left">
Note that volumes can only be increased (not decreased). Also, due
to an AWS limitation, the same volume can only be increased once
every 6 hours.
</Alert>
)}
</SettingsContainer>
</Form>
</FormProvider>
);
}

View File

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

View File

@@ -4,11 +4,16 @@ query GetPostgresSettings($appId: uuid!) {
database
}
}
config(appID: $appId, resolve: true) {
config(appID: $appId, resolve: false) {
id: __typename
__typename
postgres {
version
resources {
storage {
capacity
}
}
}
}
}

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,5 +1,5 @@
query GetHasuraSettings($appId: uuid!) {
config(appID: $appId, resolve: true) {
config(appID: $appId, resolve: false) {
id: __typename
__typename
hasura {
@@ -18,6 +18,13 @@ query GetHasuraSettings($appId: uuid!) {
events {
httpPoolSize
}
resources {
networking {
ingresses {
fqdn
}
}
}
}
}
}

View File

@@ -0,0 +1,47 @@
import { List } from '@/components/ui/v2/List';
import { ListItem } from '@/components/ui/v2/ListItem';
import { Text } from '@/components/ui/v2/Text';
import { useGetAnnouncementsQuery } from '@/utils/__generated__/graphql';
import formatDistance from 'date-fns/formatDistance';
export default function Announcements() {
const { data, loading, error } = useGetAnnouncementsQuery();
const announcements = data?.announcements || [];
if (loading || error) {
return null;
}
return (
<section>
<Text color="secondary" className="mb-2">
Latest announcements
</Text>
<List className="relative space-y-4 border-l border-gray-200 dark:border-gray-700">
{announcements.map((item) => (
<ListItem.Root key={item.id} className="ml-4">
<div className="flex flex-col">
<time className="mb-1 text-sm font-normal leading-none text-gray-400 dark:text-gray-500">
{formatDistance(new Date(item.createdAt), new Date(), {
addSuffix: true,
})}
</time>
<a href={item.href} target="_blank" rel="noopener noreferrer">
<ListItem.Button
dense
aria-label={`View ${item.content}`}
className="!p-1"
>
<p className="text-sm">{item.content}</p>
</ListItem.Button>
</a>
</div>
<div className="absolute top-[0.15rem] -ml-[1.4rem] h-3 w-3 rounded-full border border-white bg-gray-200 dark:border-gray-900 dark:bg-gray-700" />
</ListItem.Root>
))}
</List>
</section>
);
}

View File

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

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

@@ -8,6 +8,7 @@ import { PlusCircleIcon } from '@/components/ui/v2/icons/PlusCircleIcon';
import { List } from '@/components/ui/v2/List';
import { ListItem } from '@/components/ui/v2/ListItem';
import { Text } from '@/components/ui/v2/Text';
import { Announcements } from '@/features/projects/common/components/Announcements';
import { EditWorkspaceNameForm } from '@/features/projects/workspaces/components/EditWorkspaceNameForm';
import type { Workspace } from '@/types/application';
import Image from 'next/image';
@@ -38,6 +39,8 @@ export default function WorkspaceSidebar({
)}
{...props}
>
<Announcements />
<section className="grid grid-flow-row gap-2">
<Text color="secondary">My Workspaces</Text>

View File

@@ -111,7 +111,6 @@ export default function useCurrentWorkspaceAndProject(): UseCurrentWorkspaceAndP
awsName: null,
domain: null,
},
isProvisioned: true,
createdAt: new Date().toISOString(),
desiredState: ApplicationStatus.Live,
featureFlags: [],

View File

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

View File

@@ -0,0 +1,12 @@
import { useEffect, useState } from 'react';
export default function useHostName() {
const [hostName, setHostName] = useState('');
useEffect(() => {
const { port, hostname, protocol } = window.location;
setHostName(`${protocol}//${hostname}:${port}`);
}, []);
return hostName;
}

View File

@@ -8,6 +8,7 @@ import { GraphQLIcon } from '@/components/ui/v2/icons/GraphQLIcon';
import { HasuraIcon } from '@/components/ui/v2/icons/HasuraIcon';
import { HomeIcon } from '@/components/ui/v2/icons/HomeIcon';
import { RocketIcon } from '@/components/ui/v2/icons/RocketIcon';
import { ServicesIcon } from '@/components/ui/v2/icons/ServicesIcon';
import { StorageIcon } from '@/components/ui/v2/icons/StorageIcon';
import type { SvgIconProps } from '@/components/ui/v2/icons/SvgIcon';
import { UserIcon } from '@/components/ui/v2/icons/UserIcon';
@@ -136,6 +137,13 @@ export default function useProjectRoutes() {
label: 'Storage',
icon: <StorageIcon />,
},
{
relativePath: '/services',
exact: false,
label: 'Run',
icon: <ServicesIcon />,
disabled: !isPlatform,
},
...nhostRoutes,
];

View File

@@ -1,7 +1,6 @@
import type { ProjectFragment } from '@/utils/__generated__/graphql';
import { test, vi } from 'vitest';
import generateAppServiceUrl, {
defaultLocalBackendSlugs,
defaultRemoteBackendSlugs,
} from './generateAppServiceUrl';
@@ -69,7 +68,7 @@ test('should generate a per service subdomain in remote mode', () => {
);
expect(generateAppServiceUrl('test', region, 'grafana')).toBe(
'https://test.grafana.eu-west-1.nhost.run',
'https://test.grafana.eu-west-1.nhost.run/dashboards',
);
});
@@ -102,7 +101,7 @@ test('should generate staging subdomains in staging environment', () => {
);
expect(generateAppServiceUrl('test', stagingRegion, 'grafana')).toBe(
'https://test.grafana.eu-west-1.staging.nhost.run',
'https://test.grafana.eu-west-1.staging.nhost.run/dashboards',
);
});
@@ -120,7 +119,7 @@ test('should generate no slug for Hasura and Grafana neither in local mode nor i
'https://test.hasura.eu-west-1.staging.nhost.run',
);
expect(generateAppServiceUrl('test', stagingRegion, 'grafana')).toBe(
'https://test.grafana.eu-west-1.staging.nhost.run',
'https://test.grafana.eu-west-1.staging.nhost.run/dashboards',
);
process.env.NEXT_PUBLIC_ENV = 'production';
@@ -129,7 +128,7 @@ test('should generate no slug for Hasura and Grafana neither in local mode nor i
'https://test.hasura.eu-west-1.nhost.run',
);
expect(generateAppServiceUrl('test', region, 'grafana')).toBe(
'https://test.grafana.eu-west-1.nhost.run',
'https://test.grafana.eu-west-1.nhost.run/dashboards',
);
});
@@ -138,7 +137,7 @@ test('should be able to override the default remote backend slugs', () => {
process.env.NEXT_PUBLIC_ENV = 'production';
expect(
generateAppServiceUrl('test', region, 'hasura', defaultLocalBackendSlugs, {
generateAppServiceUrl('test', region, 'hasura', {
...defaultRemoteBackendSlugs,
hasura: '/lorem-ipsum',
}),
@@ -187,24 +186,3 @@ test('should construct service URLs based on environment variables', () => {
'https://localdev4.nhost.run/v1/functions',
);
});
test('should generate a basic subdomain with a custom port if provided', () => {
process.env.NEXT_PUBLIC_NHOST_BACKEND_URL = `http://localhost:1338`;
process.env.NEXT_PUBLIC_NHOST_PLATFORM = 'true';
expect(generateAppServiceUrl('test', region, 'auth')).toBe(
`http://localhost:1338/v1/auth`,
);
expect(generateAppServiceUrl('test', region, 'storage')).toBe(
`http://localhost:1338/v1/files`,
);
expect(generateAppServiceUrl('test', region, 'graphql')).toBe(
`http://localhost:1338/v1/graphql`,
);
expect(generateAppServiceUrl('test', region, 'functions')).toBe(
`http://localhost:1338/v1/functions`,
);
});

View File

@@ -62,7 +62,6 @@ export default function generateAppServiceUrl(
subdomain: string,
region: ProjectFragment['region'],
service: NhostService,
localBackendSlugs = defaultLocalBackendSlugs,
remoteBackendSlugs = defaultRemoteBackendSlugs,
) {
const IS_PLATFORM = isPlatform();
@@ -87,12 +86,6 @@ export default function generateAppServiceUrl(
return serviceUrls[service];
}
// This is only used when running the dashboard locally against its own
// backend.
if (process.env.NEXT_PUBLIC_ENV === 'dev') {
return `${process.env.NEXT_PUBLIC_NHOST_BACKEND_URL}${localBackendSlugs[service]}`;
}
const constructedDomain = [
subdomain,
service,
@@ -102,5 +95,11 @@ export default function generateAppServiceUrl(
.filter(Boolean)
.join('.');
return `https://${constructedDomain}${remoteBackendSlugs[service]}`;
let url = `https://${constructedDomain}${remoteBackendSlugs[service]}`;
if (service === 'grafana') {
url = `${url}/dashboards`;
}
return url;
}

View File

@@ -0,0 +1,153 @@
import { useUI } from '@/components/common/UIProvider';
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Input } from '@/components/ui/v2/Input';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { VerifyDomain } from '@/features/projects/custom-domains/settings/components/VerifyDomain';
import {
useGetAuthenticationSettingsQuery,
useUpdateConfigMutation,
type ConfigIngressUpdateInput,
} from '@/generated/graphql';
import { getToastStyleProps } from '@/utils/constants/settings';
import { getServerError } from '@/utils/getServerError';
import { yupResolver } from '@hookform/resolvers/yup';
import { useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import * as Yup from 'yup';
const validationSchema = Yup.object({
auth_fqdn: Yup.string(),
});
export type AuthDomainFormValues = Yup.InferType<typeof validationSchema>;
export default function AuthDomain() {
const { maintenanceActive } = useUI();
const [isVerified, setIsVerified] = useState(false);
const { currentProject, refetch: refetchWorkspaceAndProject } =
useCurrentWorkspaceAndProject();
const [updateConfig] = useUpdateConfigMutation();
const form = useForm<{ auth_fqdn: string }>({
reValidateMode: 'onSubmit',
defaultValues: { auth_fqdn: null },
resolver: yupResolver(validationSchema),
});
const { data, loading, error } = useGetAuthenticationSettingsQuery({
variables: {
appId: currentProject.id,
},
});
const { networking } = data?.config?.auth?.resources || {};
const initialValue = networking?.ingresses?.[0]?.fqdn?.[0];
useEffect(() => {
if (!loading && data) {
form.reset({ auth_fqdn: initialValue });
}
}, [data, loading, form, initialValue]);
if (loading) {
return (
<ActivityIndicator
delay={1000}
label="Loading Auth Domain Settings..."
className="justify-center"
/>
);
}
if (error) {
throw error;
}
const { formState, register, watch } = form;
const isDirty = Object.keys(formState.dirtyFields).length > 0;
const auth_fqdn = watch('auth_fqdn');
async function handleSubmit(formValues: AuthDomainFormValues) {
const ingresses: ConfigIngressUpdateInput[] =
formValues.auth_fqdn.length > 0 ? [{ fqdn: [formValues.auth_fqdn] }] : [];
const updateConfigPromise = updateConfig({
variables: {
appId: currentProject.id,
config: {
auth: {
resources: {
networking: {
ingresses,
},
},
},
},
},
});
try {
await toast.promise(
updateConfigPromise,
{
loading: `Auth domain is being updated...`,
success: `Auth domain has been updated successfully.`,
error: getServerError(
`An error occurred while trying to update the auth domain.`,
),
},
getToastStyleProps(),
);
form.reset(formValues);
await refetchWorkspaceAndProject();
} catch {
// Note: The toast will handle the error.
}
}
return (
<FormProvider {...form}>
<Form onSubmit={handleSubmit}>
<SettingsContainer
title="Auth Domain"
description="Enter below your custom domain for the authentication service."
slotProps={{
submitButton: {
disabled:
!isDirty || maintenanceActive || (!isVerified && !initialValue),
loading: formState.isSubmitting,
},
}}
className="grid grid-flow-row px-4 gap-y-4 gap-x-4 lg:grid-cols-5"
>
<Input
{...register('auth_fqdn')}
id="auth_fqdn"
name="auth_fqdn"
type="string"
fullWidth
className="col-span-5 lg:col-span-2"
placeholder="auth.mydomain.dev"
error={Boolean(formState.errors.auth_fqdn?.message)}
helperText={formState.errors.auth_fqdn?.message}
slotProps={{ inputRoot: { min: 1, max: 100 } }}
/>
<div className="col-span-5 row-start-2">
<VerifyDomain
recordType="CNAME"
hostname={auth_fqdn}
value={`lb.${currentProject.region.awsName}.${currentProject.region.domain}.`}
onHostNameVerified={() => setIsVerified(true)}
/>
</div>
</SettingsContainer>
</Form>
</FormProvider>
);
}

View File

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

View File

@@ -0,0 +1,61 @@
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { Input } from '@/components/ui/v2/Input';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { generateAppServiceUrl } from '@/features/projects/common/utils/generateAppServiceUrl';
import { VerifyDomain } from '@/features/projects/custom-domains/settings/components/VerifyDomain';
import { useState } from 'react';
import * as Yup from 'yup';
const validationSchema = Yup.object({
database_fqdn: Yup.string().required(),
});
export type DatabaseDomainFormValues = Yup.InferType<typeof validationSchema>;
export default function DatabaseDomain() {
const { currentProject } = useCurrentWorkspaceAndProject();
const [dbFQDN, setDbFQDN] = useState('');
const postgresHost = generateAppServiceUrl(
currentProject.subdomain,
currentProject.region,
'db',
).replace('https://', '');
return (
<SettingsContainer
title="Database Domain"
description="Enter below your custom domain for the PostgreSQL database to verify. Once verified there is no need to save this value as no configuration on our end is required."
slotProps={{
submitButton: {
hidden: true,
},
footer: {
className: 'hidden',
},
}}
className="grid grid-flow-row px-4 gap-y-4 gap-x-4 lg:grid-cols-5"
>
<Input
id="database_fqdn"
name="database_fqdn"
type="string"
fullWidth
className="col-span-5 lg:col-span-2"
placeholder="db.mydomain.dev"
onChange={(e) => {
setDbFQDN(e.target.value);
}}
slotProps={{ inputRoot: { min: 1, max: 100 } }}
/>
<div className="col-span-5 row-start-2">
<VerifyDomain
recordType="CNAME"
hostname={dbFQDN}
value={`${postgresHost}.`}
/>
</div>
</SettingsContainer>
);
}

View File

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

View File

@@ -0,0 +1,155 @@
import { useUI } from '@/components/common/UIProvider';
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Input } from '@/components/ui/v2/Input';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { VerifyDomain } from '@/features/projects/custom-domains/settings/components/VerifyDomain';
import {
useGetHasuraSettingsQuery,
useUpdateConfigMutation,
type ConfigIngressUpdateInput,
} from '@/generated/graphql';
import { getToastStyleProps } from '@/utils/constants/settings';
import { getServerError } from '@/utils/getServerError';
import { yupResolver } from '@hookform/resolvers/yup';
import { useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import * as Yup from 'yup';
const validationSchema = Yup.object({
hasura_fqdn: Yup.string(),
});
export type HasuraDomainFormValues = Yup.InferType<typeof validationSchema>;
export default function HasuraDomain() {
const { maintenanceActive } = useUI();
const [isVerified, setIsVerified] = useState(false);
const { currentProject, refetch: refetchWorkspaceAndProject } =
useCurrentWorkspaceAndProject();
const [updateConfig] = useUpdateConfigMutation();
const form = useForm<{ hasura_fqdn: string }>({
reValidateMode: 'onSubmit',
defaultValues: { hasura_fqdn: null },
resolver: yupResolver(validationSchema),
});
const { data, loading, error } = useGetHasuraSettingsQuery({
variables: {
appId: currentProject.id,
},
});
const { networking } = data?.config?.hasura?.resources || {};
const initialValue = networking?.ingresses?.[0]?.fqdn?.[0];
useEffect(() => {
if (!loading && data) {
form.reset({ hasura_fqdn: initialValue });
}
}, [data, loading, form, initialValue]);
if (loading) {
return (
<ActivityIndicator
delay={0}
label="Loading Hasura Domain..."
className="justify-center"
/>
);
}
if (error) {
throw error;
}
const { formState, register, watch } = form;
const isDirty = Object.keys(formState.dirtyFields).length > 0;
const hasura_fqdn = watch('hasura_fqdn');
async function handleSubmit(formValues: HasuraDomainFormValues) {
const ingresses: ConfigIngressUpdateInput[] =
formValues.hasura_fqdn.length > 0
? [{ fqdn: [formValues.hasura_fqdn] }]
: [];
const updateConfigPromise = updateConfig({
variables: {
appId: currentProject.id,
config: {
hasura: {
resources: {
networking: {
ingresses,
},
},
},
},
},
});
try {
await toast.promise(
updateConfigPromise,
{
loading: `Hasura domain is being updated...`,
success: `Hasura domain has been updated successfully.`,
error: getServerError(
`An error occurred while trying to update the Hasura domain.`,
),
},
getToastStyleProps(),
);
form.reset(formValues);
await refetchWorkspaceAndProject();
} catch {
// Note: The toast will handle the error.
}
}
return (
<FormProvider {...form}>
<Form onSubmit={handleSubmit}>
<SettingsContainer
title="Hasura Domain"
description="Enter below your custom domain for the Hasura/GraphQL service."
slotProps={{
submitButton: {
disabled:
!isDirty || maintenanceActive || (!isVerified && !initialValue),
loading: formState.isSubmitting,
},
}}
className="grid grid-flow-row px-4 gap-y-4 gap-x-4 lg:grid-cols-5"
>
<Input
{...register('hasura_fqdn')}
id="hasura_fqdn"
name="hasura_fqdn"
type="string"
fullWidth
className="col-span-5 lg:col-span-2"
placeholder="auth.mydomain.dev"
error={Boolean(formState.errors.hasura_fqdn?.message)}
helperText={formState.errors.hasura_fqdn?.message}
slotProps={{ inputRoot: { min: 1, max: 100 } }}
/>
<div className="col-span-5 row-start-2">
<VerifyDomain
recordType="CNAME"
hostname={hasura_fqdn}
value={`lb.${currentProject.region.awsName}.${currentProject.region.domain}.`}
onHostNameVerified={() => setIsVerified(true)}
/>
</div>
</SettingsContainer>
</Form>
</FormProvider>
);
}

View File

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

View File

@@ -0,0 +1,88 @@
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { ArrowSquareOutIcon } from '@/components/ui/v2/icons/ArrowSquareOutIcon';
import { Link } from '@/components/ui/v2/Link';
import { Text } from '@/components/ui/v2/Text';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { RunServicePortDomain } from '@/features/projects/custom-domains/settings/components/RunServicePortDomain';
import { useGetRunServicesQuery } from '@/utils/__generated__/graphql';
import { useMemo } from 'react';
export default function RunServiceDomains() {
const { currentProject, currentWorkspace } = useCurrentWorkspaceAndProject();
const {
data,
loading,
// refetch: refetchServices, // TODO refetch after update
} = useGetRunServicesQuery({
variables: {
appID: currentProject.id,
resolve: false,
limit: 1000, // TODO consider pagination
offset: 0,
},
});
const services = useMemo(
() => data?.app?.runServices.map((service) => service) ?? [],
[data],
);
if (loading) {
return (
<ActivityIndicator
delay={1000}
label="Loading Run Services Domains..."
className="justify-center"
/>
);
}
return (
<div className="flex flex-col gap-6">
{services
.filter((service) => service.config?.ports?.length > 0)
.map((service) => (
<SettingsContainer
key={service.id}
title={
<div className="flex flex-row items-center">
<Text className="text-lg font-semibold">
{service.config?.name ?? 'unset'}
</Text>
<Link
href={`/${currentWorkspace.slug}/${currentProject.slug}/services`}
target="_blank"
rel="noopener noreferrer"
underline="hover"
className="font-medium"
>
<ArrowSquareOutIcon className="mb-1 ml-1 h-4 w-4" />
</Link>
</div>
}
description="Enter below your custom domain for the published ports."
docsTitle={service.config?.name ?? 'unset'}
slotProps={{
submitButton: {
hidden: true,
},
footer: {
className: 'hidden',
},
}}
className="grid gap-y-4 gap-x-4 px-4"
>
{service.config?.ports?.map((port) => (
<RunServicePortDomain
key={String(port.port)}
service={service}
port={port.port}
/>
))}
</SettingsContainer>
))}
</div>
);
}

View File

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

View File

@@ -0,0 +1,159 @@
import { useUI } from '@/components/common/UIProvider';
import { Form } from '@/components/form/Form';
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 { VerifyDomain } from '@/features/projects/custom-domains/settings/components/VerifyDomain';
import { useUpdateRunServiceConfigMutation } from '@/generated/graphql';
import { getToastStyleProps } from '@/utils/constants/settings';
import { getServerError } from '@/utils/getServerError';
import { yupResolver } from '@hookform/resolvers/yup';
import { type RunService } from 'pages/[workspaceSlug]/[appSlug]/services';
import { useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import * as Yup from 'yup';
interface RunServicePortProps {
service: RunService;
port: number;
}
const validationSchema = Yup.object({
runServicePortFQDN: Yup.string(),
});
export type RunServicePortFormValues = Yup.InferType<typeof validationSchema>;
export default function RunServicePortDomain({
service,
port,
}: RunServicePortProps) {
const { maintenanceActive } = useUI();
const [loading, setLoading] = useState(false);
const [isVerified, setIsVerified] = useState(false);
const { currentProject } = useCurrentWorkspaceAndProject();
const [updateRunServiceConfig] = useUpdateRunServiceConfigMutation();
const runServicePort = service.config.ports.find((p) => p.port === port);
const initialValue = runServicePort?.ingresses?.[0]?.fqdn?.[0];
const form = useForm<{ runServicePortFQDN: string }>({
reValidateMode: 'onSubmit',
defaultValues: {
runServicePortFQDN: initialValue,
},
resolver: yupResolver(validationSchema),
});
const { formState, register, watch } = form;
const isDirty = Object.keys(formState.dirtyFields).length > 0;
const runServicePortFQDN = watch('runServicePortFQDN');
async function handleSubmit(formValues: RunServicePortFormValues) {
try {
setLoading(true);
await toast.promise(
updateRunServiceConfig({
variables: {
appID: currentProject.id,
serviceID: service.id,
config: {
ports: service?.config?.ports?.map((p) => {
// exclude the `__typename` because the mutation will fail otherwise
const { __typename, ...rest } = p;
if (rest.port === port) {
return {
...rest,
ingresses:
formValues.runServicePortFQDN.length > 0
? [{ fqdn: [formValues.runServicePortFQDN] }]
: [],
};
}
return {
...rest,
// exclude the `__typename` because the mutation will fail otherwise
ingresses: rest.ingresses.map((item) => ({
fqdn: item.fqdn,
})),
};
}),
},
},
}),
{
loading: `Port ${port} is being updated...`,
success: `Port ${port} has been updated successfully.`,
error: getServerError(
`An error occurred while trying to update Port ${port}.`,
),
},
getToastStyleProps(),
);
form.reset(formValues);
// TODO refetch the service config
// await refetchWorkspaceAndProject();
} catch {
// Note: The toast will handle the error.
} finally {
setLoading(false);
}
}
return (
<FormProvider {...form}>
<Form onSubmit={handleSubmit}>
<div className="space-y-2">
<Text className="text-sm font-semibold">{`${runServicePort.type} <--> ${runServicePort.port}`}</Text>
<div className="flex flex-row space-x-4">
<Input
{...register('runServicePortFQDN')}
id="runServicePortFQDN"
name="runServicePortFQDN"
type="string"
fullWidth
className=""
placeholder={`${service.config?.name ?? 'unset'}-${
runServicePort.port
}.mydomain.dev`}
error={Boolean(formState.errors.runServicePortFQDN?.message)}
helperText={formState.errors.runServicePortFQDN?.message}
slotProps={{
inputRoot: { min: 1, max: 100 },
}}
/>
<Button
variant="outlined"
type="submit"
disabled={
loading ||
!isDirty ||
maintenanceActive ||
(!isVerified && !initialValue)
}
>
Save
</Button>
</div>
</div>
<div className="col-span-5 row-start-2 mt-4">
<VerifyDomain
recordType="CNAME"
hostname={runServicePortFQDN}
value={`lb.${currentProject.region.awsName}.${currentProject.region.domain}.`}
onHostNameVerified={() => setIsVerified(true)}
/>
</div>
</Form>
</FormProvider>
);
}

View File

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

View File

@@ -0,0 +1,157 @@
import { useUI } from '@/components/common/UIProvider';
import { Form } from '@/components/form/Form';
import { SettingsContainer } from '@/components/layout/SettingsContainer';
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
import { Input } from '@/components/ui/v2/Input';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { VerifyDomain } from '@/features/projects/custom-domains/settings/components/VerifyDomain';
import {
useGetServerlessFunctionsSettingsQuery,
useUpdateConfigMutation,
type ConfigIngressUpdateInput,
} from '@/generated/graphql';
import { getToastStyleProps } from '@/utils/constants/settings';
import { getServerError } from '@/utils/getServerError';
import { yupResolver } from '@hookform/resolvers/yup';
import { useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import * as Yup from 'yup';
const validationSchema = Yup.object({
functions_fqdn: Yup.string(),
});
export type ServerlessFunctionsDomainFormValues = Yup.InferType<
typeof validationSchema
>;
export default function ServerlessFunctionsDomain() {
const { maintenanceActive } = useUI();
const [isVerified, setIsVerified] = useState(false);
const { currentProject, refetch: refetchWorkspaceAndProject } =
useCurrentWorkspaceAndProject();
const [updateConfig] = useUpdateConfigMutation();
const form = useForm<{ functions_fqdn: string }>({
reValidateMode: 'onSubmit',
defaultValues: { functions_fqdn: null },
resolver: yupResolver(validationSchema),
});
const { data, loading, error } = useGetServerlessFunctionsSettingsQuery({
variables: {
appId: currentProject.id,
},
});
const { networking } = data?.config?.functions?.resources || {};
const initialValue = networking?.ingresses?.[0]?.fqdn?.[0];
useEffect(() => {
if (!loading && data) {
form.reset({ functions_fqdn: initialValue });
}
}, [data, loading, form, initialValue]);
if (loading) {
return (
<ActivityIndicator
delay={1000}
label="Loading Serverless Functions Domain Settings..."
className="justify-center"
/>
);
}
if (error) {
throw error;
}
const { formState, register, watch } = form;
const isDirty = Object.keys(formState.dirtyFields).length > 0;
const functions_fqdn = watch('functions_fqdn');
async function handleSubmit(formValues: ServerlessFunctionsDomainFormValues) {
const ingresses: ConfigIngressUpdateInput[] =
formValues.functions_fqdn.length > 0
? [{ fqdn: [formValues.functions_fqdn] }]
: [];
const updateConfigPromise = updateConfig({
variables: {
appId: currentProject.id,
config: {
functions: {
resources: {
networking: {
ingresses,
},
},
},
},
},
});
try {
await toast.promise(
updateConfigPromise,
{
loading: `Serverless Functions domain is being updated...`,
success: `Serverless Functions domain has been updated successfully.`,
error: getServerError(
`An error occurred while trying to update the Serverless Functions domain.`,
),
},
getToastStyleProps(),
);
form.reset(formValues);
await refetchWorkspaceAndProject();
} catch {
// Note: The toast will handle the error.
}
}
return (
<FormProvider {...form}>
<Form onSubmit={handleSubmit}>
<SettingsContainer
title="Serverless Functions Domain"
description="Enter below your custom domain for Serverless Functions."
slotProps={{
submitButton: {
disabled:
!isDirty || maintenanceActive || (!isVerified && !initialValue),
loading: formState.isSubmitting,
},
}}
className="grid grid-flow-row px-4 gap-y-4 gap-x-4 lg:grid-cols-5"
>
<Input
{...register('functions_fqdn')}
id="functions_fqdn"
name="functions_fqdn"
type="string"
fullWidth
className="col-span-5 lg:col-span-2"
placeholder="functions.mydomain.dev"
error={Boolean(formState.errors.functions_fqdn?.message)}
helperText={formState.errors.functions_fqdn?.message}
slotProps={{ inputRoot: { min: 1, max: 100 } }}
/>
<div className="col-span-5 row-start-2">
<VerifyDomain
recordType="CNAME"
hostname={functions_fqdn}
value={`lb.${currentProject.region.awsName}.${currentProject.region.domain}.`}
onHostNameVerified={() => setIsVerified(true)}
/>
</div>
</SettingsContainer>
</Form>
</FormProvider>
);
}

View File

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

View File

@@ -0,0 +1,155 @@
import { Box } from '@/components/ui/v2/Box';
import { Button } from '@/components/ui/v2/Button';
import { IconButton } from '@/components/ui/v2/IconButton';
import { CopyIcon } from '@/components/ui/v2/icons/CopyIcon';
import { Text } from '@/components/ui/v2/Text';
import { getToastStyleProps } from '@/utils/constants/settings';
import { copy } from '@/utils/copy';
import { useDnsLookupCnameLazyQuery } from '@/utils/__generated__/graphql';
import { ApolloError } from '@apollo/client';
import { useState } from 'react';
import { toast } from 'react-hot-toast';
interface VerifyDomainProps {
recordType: string;
hostname: string;
value: string;
onHostNameVerified?: () => void;
}
export default function VerifyDomain({
recordType,
hostname,
value,
onHostNameVerified,
}: VerifyDomainProps) {
const [verificationFailed, setVerificationFailed] = useState(false);
const [verificationSucceeded, setVerificationSucceeded] = useState(false);
const [loading, setLoading] = useState(false);
const [fireLookupCNAME] = useDnsLookupCnameLazyQuery();
const handleVerifyDomain = async () => {
setLoading(true);
try {
await toast.promise(
fireLookupCNAME({
variables: {
hostname,
},
}).then(({ data: { dnsLookupCNAME } }) => {
if (dnsLookupCNAME !== value) {
throw new Error(`Could not verify ${hostname}`);
}
}),
{
loading: `Verifying ${hostname} ...`,
success: () => {
setVerificationFailed(false);
setVerificationSucceeded(true);
setLoading(false);
onHostNameVerified?.();
return `${hostname} has been verified.`;
},
error: (arg: Error | ApolloError) => {
setVerificationFailed(true);
setVerificationSucceeded(false);
setLoading(false);
if (arg instanceof 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 trying to verify ${hostname}. Please try again.`
);
}
return arg.message;
},
},
getToastStyleProps(),
);
} catch (error) {
// Note: The toast will handle the error.
}
};
return (
<Box
sx={[
{ backgroundColor: 'primary.light' },
verificationFailed && {
backgroundColor: 'error.light',
color: 'error.main',
},
verificationSucceeded && {
backgroundColor: 'success.light',
color: 'success.dark',
},
]}
className="flex flex-col p-4 space-y-4 rounded-md"
>
<div className="flex flex-row items-center justify-between">
{!verificationFailed && !verificationSucceeded && (
<Text>
Add the record below in your DNS provider to verify {hostname}
</Text>
)}
{verificationSucceeded && (
<Text>
<span className="font-semibold">{hostname}</span> was verified
successfully. Hit save to apply.
</Text>
)}
{verificationFailed && (
<Text>
An error occurred while trying to verify{' '}
<span className="font-semibold">{hostname}</span>. Make sure you
correctly added the <span className="font-semibold">CNAME</span> and
try again.
</Text>
)}
</div>
<div className="relative flex flex-col text-slate-500">
<div className="flex space-x-2">
<Text>Record type: </Text>
<Text className="font-bold">{recordType}</Text>
</div>
<div className="flex space-x-2">
<Text>Host:</Text>
<Text className="font-bold">{hostname}</Text>
</div>
<div className="flex flex-row space-x-2">
<Text>Value:</Text>
<Text className="font-bold">{value}</Text>
<IconButton
aria-label="Copy Personal Access Token"
variant="borderless"
color="secondary"
onClick={() => copy(value, 'CNAME Value')}
>
<CopyIcon className="w-4 h-4" />
</IconButton>
</div>
<Button
disabled={loading || !hostname}
onClick={handleVerifyDomain}
className="mt-4 sm:absolute sm:bottom-0 sm:right-0 sm:mt-0"
>
Verify
</Button>
</div>
</Box>
);
}

View File

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

View File

@@ -128,6 +128,8 @@ export default function BaseEnvironmentVariableForm({
error={!!errors.value}
helperText={errors?.value?.message}
fullWidth
multiline
rows={5}
autoComplete="off"
autoFocus={mode === 'edit'}
/>

View File

@@ -16,7 +16,6 @@ import { useAppClient } from '@/features/projects/common/hooks/useAppClient';
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
import {
defaultLocalBackendSlugs,
defaultRemoteBackendSlugs,
generateAppServiceUrl,
} from '@/features/projects/common/utils/generateAppServiceUrl';
@@ -110,7 +109,6 @@ export default function SystemEnvironmentVariableSettings() {
currentProject?.subdomain,
currentProject?.region,
'hasura',
defaultLocalBackendSlugs,
{ ...defaultRemoteBackendSlugs, hasura: '/console' },
),
},

View File

@@ -10,6 +10,7 @@ import type { LogsCustomInterval } from '@/features/projects/logs/utils/constant
import { LOGS_AVAILABLE_INTERVALS } from '@/features/projects/logs/utils/constants/intervals';
import type { AvailableLogsService } from '@/features/projects/logs/utils/constants/services';
import { LOGS_AVAILABLE_SERVICES } from '@/features/projects/logs/utils/constants/services';
import { useGetRunServicesQuery } from '@/utils/__generated__/graphql';
import { subMinutes } from 'date-fns';
import { useEffect, useState } from 'react';
import { twMerge } from 'tailwind-merge';
@@ -132,6 +133,37 @@ export default function LogsHeader({
const { currentProject } = useCurrentWorkspaceAndProject();
const applicationCreationDate = new Date(currentProject.createdAt);
const [runServices, setRunServices] = useState<
{
label: string;
value: string;
}[]
>([]);
const { data, loading } = useGetRunServicesQuery({
variables: {
appID: currentProject.id,
resolve: false,
limit: 1000,
offset: 0,
},
});
useEffect(() => {
if (!loading) {
const services = data.app?.runServices ?? [];
setRunServices(
services
.filter((s) => !!s.config?.name)
.map((s) => ({
label: s.config.name,
value: `run-${s.config.name}`,
})),
);
}
}, [loading, data]);
/**
* Will subtract the `customInterval` time in minutes from the current date.
*/
@@ -181,15 +213,17 @@ export default function LogsHeader({
root: { className: 'min-h-[initial] h-9 leading-[initial]' },
}}
>
{LOGS_AVAILABLE_SERVICES.map(({ value, label }) => (
<Option
key={value}
value={value}
className="text-sm+ font-medium"
>
{label}
</Option>
))}
{[...LOGS_AVAILABLE_SERVICES, ...runServices].map(
({ value, label }) => (
<Option
key={value}
value={value}
className="text-sm+ font-medium"
>
{label}
</Option>
),
)}
</Select>
</Box>
</Box>

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

@@ -68,11 +68,15 @@ export default function ResourcesConfirmationDialog({
const totalBillableVCPU = formValues.enabled ? billableResources.vcpu : 0;
const totalBillableMemory = formValues.enabled ? billableResources.memory : 0;
const updatedPrice =
Math.max(
priceForTotalAvailableVCPU,
(billableResources.vcpu / RESOURCE_VCPU_MULTIPLIER) * RESOURCE_VCPU_PRICE,
) + proPlan.price;
const { enabled } = formValues;
const updatedPrice = enabled
? Math.max(
priceForTotalAvailableVCPU,
(billableResources.vcpu / RESOURCE_VCPU_MULTIPLIER) *
RESOURCE_VCPU_PRICE,
) + proPlan.price
: proPlan.price;
if (!loading && !proPlan) {
return (
@@ -86,18 +90,30 @@ export default function ResourcesConfirmationDialog({
throw error;
}
const databaseVCPU = enabled ? formValues.database.vcpu : 0;
const databaseMemory = enabled ? formValues.database.memory : 0;
const hasuraVCPU = enabled ? formValues.hasura.vcpu : 0;
const hasuraMemory = enabled ? formValues.hasura.memory : 0;
const authVCPU = enabled ? formValues.auth.vcpu : 0;
const authMemory = enabled ? formValues.auth.memory : 0;
const storageVCPU = enabled ? formValues.storage.vcpu : 0;
const storageMemory = enabled ? formValues.storage.memory : 0;
const databaseResources = `${prettifyVCPU(
formValues.database.vcpu,
)} vCPU + ${prettifyMemory(formValues.database.memory)}`;
const hasuraResources = `${prettifyVCPU(
formValues.hasura.vcpu,
)} vCPU + ${prettifyMemory(formValues.hasura.memory)}`;
const authResources = `${prettifyVCPU(
formValues.auth.vcpu,
)} vCPU + ${prettifyMemory(formValues.auth.memory)}`;
databaseVCPU,
)} vCPU + ${prettifyMemory(databaseMemory)}`;
const hasuraResources = `${prettifyVCPU(hasuraVCPU)} vCPU + ${prettifyMemory(
hasuraMemory,
)}`;
const authResources = `${prettifyVCPU(authVCPU)} vCPU + ${prettifyMemory(
authMemory,
)}`;
const storageResources = `${prettifyVCPU(
formValues.storage.vcpu,
)} vCPU + ${prettifyMemory(formValues.storage.memory)}`;
storageVCPU,
)} vCPU + ${prettifyMemory(storageMemory)}`;
return (
<div className="grid grid-flow-row gap-6 px-6 pb-6">

View File

@@ -4,7 +4,6 @@ import { ArrowRightIcon } from '@/components/ui/v2/icons/ArrowRightIcon';
import { Slider, sliderClasses } from '@/components/ui/v2/Slider';
import { Text } from '@/components/ui/v2/Text';
import { useProPlan } from '@/features/projects/common/hooks/useProPlan';
import { calculateBillableResources } from '@/features/projects/resources/settings/utils/calculateBillableResources';
import { getAllocatedResources } from '@/features/projects/resources/settings/utils/getAllocatedResources';
import { prettifyMemory } from '@/features/projects/resources/settings/utils/prettifyMemory';
import { prettifyVCPU } from '@/features/projects/resources/settings/utils/prettifyVCPU';
@@ -63,34 +62,7 @@ export default function TotalResourcesFormFragment({
(formValues.totalAvailableVCPU / RESOURCE_VCPU_MULTIPLIER) *
RESOURCE_VCPU_PRICE;
const billableResources = calculateBillableResources(
{
replicas: formValues.database?.replicas,
vcpu: formValues.database?.vcpu,
memory: formValues.database?.memory,
},
{
replicas: formValues.hasura?.replicas,
vcpu: formValues.hasura?.vcpu,
memory: formValues.hasura?.memory,
},
{
replicas: formValues.auth?.replicas,
vcpu: formValues.auth?.vcpu,
memory: formValues.auth?.memory,
},
{
replicas: formValues.storage?.replicas,
vcpu: formValues.storage?.vcpu,
memory: formValues.storage?.memory,
},
);
const updatedPrice =
Math.max(
priceForTotalAvailableVCPU,
(billableResources.vcpu / RESOURCE_VCPU_MULTIPLIER) * RESOURCE_VCPU_PRICE,
) + proPlan.price;
const updatedPrice = priceForTotalAvailableVCPU + proPlan.price;
const { vcpu: allocatedVCPU, memory: allocatedMemory } =
getAllocatedResources(formValues);

View File

@@ -38,7 +38,7 @@ fragment ServiceResources on ConfigConfig {
}
query GetResources($appId: uuid!) {
config(appID: $appId, resolve: true) {
config(appID: $appId, resolve: false) {
...ServiceResources
}
}

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', () => {

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