Compare commits
10 Commits
storage@0.
...
feat/datab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4685acc977 | ||
|
|
bb9aaf2903 | ||
|
|
8e82edd0c6 | ||
|
|
b0256da33f | ||
|
|
351daa5fbe | ||
|
|
4e9de6a764 | ||
|
|
9dab347348 | ||
|
|
6c044458ca | ||
|
|
5b98f4ece2 | ||
|
|
dc7d3fb4cc |
@@ -49,4 +49,4 @@ This repository is a monorepo that contains multiple packages and applications.
|
||||
- `tools/codegen` - Internal code generation tool to build the SDK
|
||||
- `tools/mintlify-openapi` - Internal tool to generate reference documentation for Mintlify from an OpenAPI spec.
|
||||
|
||||
For details about those projects and how to contribure, please refer to their respective `README.md` and `CONTRIBUTING.md` files.
|
||||
For details about those projects and how to contribute, please refer to their respective `README.md` and `CONTRIBUTING.md` files.
|
||||
|
||||
@@ -107,6 +107,7 @@ Nhost is frontend agnostic, which means Nhost works with all frontend frameworks
|
||||
# Resources
|
||||
|
||||
- Start developing locally with the [Nhost CLI](https://docs.nhost.io/platform/cli/local-development)
|
||||
|
||||
## Nhost Clients
|
||||
|
||||
- [JavaScript/TypeScript](https://docs.nhost.io/reference/javascript/nhost-js/main)
|
||||
@@ -137,7 +138,7 @@ Here are some ways of contributing to making Nhost better:
|
||||
|
||||
- **[Try out Nhost](https://docs.nhost.io)**, and think of ways to make the service better. Let us know here on GitHub.
|
||||
- Join our [Discord](https://discord.com/invite/9V7Qb2U) and connect with other members to share and learn from.
|
||||
- Send a pull request to any of our [open source repositories](https://github.com/nhost) on Github. Check our [contribution guide](https://github.com/nhost/nhost/blob/main/CONTRIBUTING.md) and our [developers guide](https://github.com/nhost/nhost/blob/main/DEVELOPERS.md) for more details about how to contribute. We're looking forward to your contribution!
|
||||
- Send a pull request to any of our [open source repositories](https://github.com/nhost) on Github. Check out our [contribution guide](https://github.com/nhost/nhost/blob/main/CONTRIBUTING.md) for more details about how to contribute. We're looking forward to your contribution!
|
||||
|
||||
### Contributors
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"$schema": "https://github.com/IBM/audit-ci/raw/main/docs/schema.json",
|
||||
"moderate": true,
|
||||
"allowlist": [
|
||||
"GHSA-9965-vmph-33xx", // https://github.com/advisories/GHSA-9965-vmph-33xx Update package once have a fix
|
||||
"GHSA-7mvr-c777-76hp" // https://github.com/advisories/GHSA-7mvr-c777-76hp Update package once Nix side is also updated
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
## [cli@1.34.7] - 2025-11-13
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(cli)* Bump nhost/dashboard to 2.42.0 (#3693)
|
||||
|
||||
## [cli@1.34.6] - 2025-11-13
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
@@ -56,7 +56,7 @@ func CommandCloud() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagDashboardVersion,
|
||||
Usage: "Dashboard version to use",
|
||||
Value: "nhost/dashboard:2.41.0",
|
||||
Value: "nhost/dashboard:2.42.0",
|
||||
Sources: cli.EnvVars("NHOST_DASHBOARD_VERSION"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
|
||||
@@ -111,7 +111,7 @@ func CommandUp() *cli.Command { //nolint:funlen
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagDashboardVersion,
|
||||
Usage: "Dashboard version to use",
|
||||
Value: "nhost/dashboard:2.41.0",
|
||||
Value: "nhost/dashboard:2.42.0",
|
||||
Sources: cli.EnvVars("NHOST_DASHBOARD_VERSION"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
"timezones-list": "^3.1.0",
|
||||
"utility-types": "^3.11.0",
|
||||
"uuid": "^9.0.1",
|
||||
"validator": "^13.11.0",
|
||||
"validator": "^13.15.20",
|
||||
"yup": "^1.4.0",
|
||||
"yup-password": "^0.2.2",
|
||||
"zod": "^3.23.8"
|
||||
@@ -232,7 +232,9 @@
|
||||
}
|
||||
},
|
||||
"overrides": {
|
||||
"esbuild@<=0.24.2": ">=0.25.0"
|
||||
"esbuild@<=0.24.2": ">=0.25.0",
|
||||
"js-yaml@<=4.1.0": ">=4.1.1",
|
||||
"glob@>=10.3.7 <=11.0.3": ">=11.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
128
dashboard/pnpm-lock.yaml
generated
128
dashboard/pnpm-lock.yaml
generated
@@ -6,6 +6,8 @@ settings:
|
||||
|
||||
overrides:
|
||||
esbuild@<=0.24.2: '>=0.25.0'
|
||||
js-yaml@<=4.1.0: '>=4.1.1'
|
||||
glob@>=10.3.7 <=11.0.3: '>=11.1.0'
|
||||
|
||||
packageExtensionsChecksum: sha256-gRFeykwiwMfEE6etcYx6N48XwVeKzxbqNveL7KTQgSQ=
|
||||
|
||||
@@ -320,8 +322,8 @@ importers:
|
||||
specifier: ^9.0.1
|
||||
version: 9.0.1
|
||||
validator:
|
||||
specifier: ^13.11.0
|
||||
version: 13.12.0
|
||||
specifier: ^13.15.20
|
||||
version: 13.15.23
|
||||
yup:
|
||||
specifier: ^1.4.0
|
||||
version: 1.5.0
|
||||
@@ -2245,6 +2247,14 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@isaacs/balanced-match@4.0.1':
|
||||
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
'@isaacs/brace-expansion@5.0.0':
|
||||
resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2618,10 +2628,6 @@ packages:
|
||||
'@orval/zod@7.11.2':
|
||||
resolution: {integrity: sha512-4MzTg5Wms8/LlM3CbYu80dvCbP88bVlQjnYsBdFXuEv0K2GYkBCAhVOrmXCVrPXE89neV6ABkvWQeuKZQpkdxQ==}
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@playwright/test@1.54.1':
|
||||
resolution: {integrity: sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -5649,8 +5655,8 @@ packages:
|
||||
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
foreground-child@3.1.1:
|
||||
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
|
||||
foreground-child@3.3.1:
|
||||
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
form-data@4.0.4:
|
||||
@@ -5779,8 +5785,9 @@ packages:
|
||||
glob-to-regexp@0.4.1:
|
||||
resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
|
||||
|
||||
glob@10.4.5:
|
||||
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
|
||||
glob@12.0.0:
|
||||
resolution: {integrity: sha512-5Qcll1z7IKgHr5g485ePDdHcNQY0k2dtv/bjYy0iuyGxQw2qSOiiXUXJ+AYQpg3HNoUMHqAruX478Jeev7UULw==}
|
||||
engines: {node: 20 || >=22}
|
||||
hasBin: true
|
||||
|
||||
glob@7.1.7:
|
||||
@@ -6288,9 +6295,9 @@ packages:
|
||||
iterator.prototype@1.1.2:
|
||||
resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
|
||||
|
||||
jackspeak@3.2.3:
|
||||
resolution: {integrity: sha512-htOzIMPbpLid/Gq9/zaz9SfExABxqRe1sSCdxntlO/aMD6u0issZQiY25n2GKQUtJ02j7z5sfptlAOMpWWOmvw==}
|
||||
engines: {node: '>=14'}
|
||||
jackspeak@4.1.1:
|
||||
resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
jest-diff@29.7.0:
|
||||
resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==}
|
||||
@@ -6337,8 +6344,8 @@ packages:
|
||||
js-tokens@9.0.1:
|
||||
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsdom@22.1.0:
|
||||
@@ -6555,9 +6562,9 @@ packages:
|
||||
lowlight@3.1.0:
|
||||
resolution: {integrity: sha512-CEbNVoSikAxwDMDPjXlqlFYiZLkDJHwyGu/MfOsJnF3d7f3tds5J3z8s/l9TMXhzfsJCCJEAsD78842mwmg0PQ==}
|
||||
|
||||
lru-cache@10.2.2:
|
||||
resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==}
|
||||
engines: {node: 14 || >=16.14}
|
||||
lru-cache@11.2.2:
|
||||
resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
@@ -6798,6 +6805,10 @@ packages:
|
||||
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
|
||||
hasBin: true
|
||||
|
||||
minimatch@10.1.1:
|
||||
resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
minimatch@3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||
|
||||
@@ -6809,10 +6820,6 @@ packages:
|
||||
resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
minimatch@9.0.4:
|
||||
resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
minimatch@9.0.5:
|
||||
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
@@ -7181,9 +7188,9 @@ packages:
|
||||
resolution: {integrity: sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
path-scurry@1.11.1:
|
||||
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
||||
engines: {node: '>=16 || 14 >=14.18'}
|
||||
path-scurry@2.0.1:
|
||||
resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
path-to-regexp@6.3.0:
|
||||
resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==}
|
||||
@@ -8594,8 +8601,8 @@ packages:
|
||||
v8-compile-cache-lib@3.0.1:
|
||||
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
||||
|
||||
validator@13.12.0:
|
||||
resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==}
|
||||
validator@13.15.23:
|
||||
resolution: {integrity: sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
vfile-message@4.0.2:
|
||||
@@ -8936,7 +8943,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jsdevtools/ono': 7.1.3
|
||||
'@types/json-schema': 7.0.15
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
|
||||
'@apidevtools/openapi-schemas@2.1.0': {}
|
||||
|
||||
@@ -10335,7 +10342,7 @@ snapshots:
|
||||
globals: 13.24.0
|
||||
ignore: 5.3.2
|
||||
import-fresh: 3.3.0
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
minimatch: 3.1.2
|
||||
strip-json-comments: 3.1.1
|
||||
transitivePeerDependencies:
|
||||
@@ -11019,7 +11026,7 @@ snapshots:
|
||||
loglevel: 1.9.2
|
||||
loglevel-plugin-prefix: 0.8.4
|
||||
minimatch: 6.2.0
|
||||
validator: 13.12.0
|
||||
validator: 13.15.23
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
@@ -11152,11 +11159,17 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 20.14.8
|
||||
|
||||
'@isaacs/balanced-match@4.0.1': {}
|
||||
|
||||
'@isaacs/brace-expansion@5.0.0':
|
||||
dependencies:
|
||||
'@isaacs/balanced-match': 4.0.1
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
dependencies:
|
||||
string-width: 5.1.2
|
||||
string-width-cjs: string-width@4.2.3
|
||||
strip-ansi: 7.1.0
|
||||
strip-ansi: 7.1.2
|
||||
strip-ansi-cjs: strip-ansi@6.0.1
|
||||
wrap-ansi: 8.1.0
|
||||
wrap-ansi-cjs: wrap-ansi@7.0.0
|
||||
@@ -11604,9 +11617,6 @@ snapshots:
|
||||
- openapi-types
|
||||
- supports-color
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
optional: true
|
||||
|
||||
'@playwright/test@1.54.1':
|
||||
dependencies:
|
||||
playwright: 1.54.1
|
||||
@@ -14198,7 +14208,7 @@ snapshots:
|
||||
cosmiconfig@8.3.6(typescript@5.8.3):
|
||||
dependencies:
|
||||
import-fresh: 3.3.1
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
parse-json: 5.2.0
|
||||
path-type: 4.0.0
|
||||
optionalDependencies:
|
||||
@@ -14208,7 +14218,7 @@ snapshots:
|
||||
dependencies:
|
||||
env-paths: 2.2.1
|
||||
import-fresh: 3.3.1
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
parse-json: 5.2.0
|
||||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
@@ -15062,7 +15072,7 @@ snapshots:
|
||||
imurmurhash: 0.1.4
|
||||
is-glob: 4.0.3
|
||||
is-path-inside: 3.0.3
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
json-stable-stringify-without-jsonify: 1.0.1
|
||||
levn: 0.4.1
|
||||
lodash.merge: 4.6.2
|
||||
@@ -15241,7 +15251,7 @@ snapshots:
|
||||
dependencies:
|
||||
is-callable: 1.2.7
|
||||
|
||||
foreground-child@3.1.1:
|
||||
foreground-child@3.3.1:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
signal-exit: 4.1.0
|
||||
@@ -15382,14 +15392,14 @@ snapshots:
|
||||
|
||||
glob-to-regexp@0.4.1: {}
|
||||
|
||||
glob@10.4.5:
|
||||
glob@12.0.0:
|
||||
dependencies:
|
||||
foreground-child: 3.1.1
|
||||
jackspeak: 3.2.3
|
||||
minimatch: 9.0.4
|
||||
foreground-child: 3.3.1
|
||||
jackspeak: 4.1.1
|
||||
minimatch: 10.1.1
|
||||
minipass: 7.1.2
|
||||
package-json-from-dist: 1.0.1
|
||||
path-scurry: 1.11.1
|
||||
path-scurry: 2.0.1
|
||||
|
||||
glob@7.1.7:
|
||||
dependencies:
|
||||
@@ -15912,11 +15922,9 @@ snapshots:
|
||||
reflect.getprototypeof: 1.0.8
|
||||
set-function-name: 2.0.2
|
||||
|
||||
jackspeak@3.2.3:
|
||||
jackspeak@4.1.1:
|
||||
dependencies:
|
||||
'@isaacs/cliui': 8.0.2
|
||||
optionalDependencies:
|
||||
'@pkgjs/parseargs': 0.11.0
|
||||
|
||||
jest-diff@29.7.0:
|
||||
dependencies:
|
||||
@@ -15973,7 +15981,7 @@ snapshots:
|
||||
|
||||
js-tokens@9.0.1: {}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
@@ -16210,7 +16218,7 @@ snapshots:
|
||||
devlop: 1.1.0
|
||||
highlight.js: 11.9.0
|
||||
|
||||
lru-cache@10.2.2: {}
|
||||
lru-cache@11.2.2: {}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
dependencies:
|
||||
@@ -16640,6 +16648,10 @@ snapshots:
|
||||
|
||||
mini-svg-data-uri@1.4.4: {}
|
||||
|
||||
minimatch@10.1.1:
|
||||
dependencies:
|
||||
'@isaacs/brace-expansion': 5.0.0
|
||||
|
||||
minimatch@3.1.2:
|
||||
dependencies:
|
||||
brace-expansion: 1.1.12
|
||||
@@ -16652,10 +16664,6 @@ snapshots:
|
||||
dependencies:
|
||||
brace-expansion: 2.0.2
|
||||
|
||||
minimatch@9.0.4:
|
||||
dependencies:
|
||||
brace-expansion: 2.0.2
|
||||
|
||||
minimatch@9.0.5:
|
||||
dependencies:
|
||||
brace-expansion: 2.0.2
|
||||
@@ -17094,9 +17102,9 @@ snapshots:
|
||||
dependencies:
|
||||
path-root-regex: 0.1.2
|
||||
|
||||
path-scurry@1.11.1:
|
||||
path-scurry@2.0.1:
|
||||
dependencies:
|
||||
lru-cache: 10.2.2
|
||||
lru-cache: 11.2.2
|
||||
minipass: 7.1.2
|
||||
|
||||
path-to-regexp@6.3.0: {}
|
||||
@@ -17942,7 +17950,7 @@ snapshots:
|
||||
dependencies:
|
||||
eastasianwidth: 0.2.0
|
||||
emoji-regex: 9.2.2
|
||||
strip-ansi: 7.1.0
|
||||
strip-ansi: 7.1.2
|
||||
|
||||
string-width@7.2.0:
|
||||
dependencies:
|
||||
@@ -18061,7 +18069,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.13
|
||||
commander: 4.1.1
|
||||
glob: 10.4.5
|
||||
glob: 12.0.0
|
||||
lines-and-columns: 1.2.4
|
||||
mz: 2.7.0
|
||||
pirates: 4.0.6
|
||||
@@ -18163,7 +18171,7 @@ snapshots:
|
||||
test-exclude@7.0.1:
|
||||
dependencies:
|
||||
'@istanbuljs/schema': 0.1.3
|
||||
glob: 10.4.5
|
||||
glob: 12.0.0
|
||||
minimatch: 9.0.5
|
||||
|
||||
text-table@0.2.0: {}
|
||||
@@ -18535,7 +18543,7 @@ snapshots:
|
||||
|
||||
v8-compile-cache-lib@3.0.1: {}
|
||||
|
||||
validator@13.12.0: {}
|
||||
validator@13.15.23: {}
|
||||
|
||||
vfile-message@4.0.2:
|
||||
dependencies:
|
||||
@@ -18789,9 +18797,9 @@ snapshots:
|
||||
|
||||
wrap-ansi@8.1.0:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.1
|
||||
ansi-styles: 6.2.3
|
||||
string-width: 5.1.2
|
||||
strip-ansi: 7.1.0
|
||||
strip-ansi: 7.1.2
|
||||
|
||||
wrap-ansi@9.0.0:
|
||||
dependencies:
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Button } from '@/components/ui/v3/button';
|
||||
import { Input } from '@/components/ui/v3/input';
|
||||
import {
|
||||
useDataGridFilter,
|
||||
type DataGridFilterOperator,
|
||||
} from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGrid/DataGridFilterProvider';
|
||||
import { cn, isNotEmptyValue } from '@/lib/utils';
|
||||
import { X } from 'lucide-react';
|
||||
import DataGridFilterColumn from './DataGridFilterColumn';
|
||||
import DataGridFilterOperators from './DataGridFilterOperators';
|
||||
|
||||
type FilterProps = {
|
||||
column: string;
|
||||
op: DataGridFilterOperator;
|
||||
value: string;
|
||||
index: number;
|
||||
columns: Array<{ id: string; dataType: string }>;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
function DataGridFilter({
|
||||
column,
|
||||
op,
|
||||
value,
|
||||
index,
|
||||
columns,
|
||||
error,
|
||||
}: FilterProps) {
|
||||
const { setColumn, setOp, setValue, removeFilter } = useDataGridFilter();
|
||||
|
||||
function handleOpChange(newOp: DataGridFilterOperator) {
|
||||
setOp(index, newOp);
|
||||
if (
|
||||
newOp === '$like' ||
|
||||
newOp === '$ilike' ||
|
||||
newOp === '$nlike' ||
|
||||
newOp === '$nilike'
|
||||
) {
|
||||
setValue(index, '%%');
|
||||
} else if (newOp === '$in' || newOp === '$nin') {
|
||||
setValue(index, '[]');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<DataGridFilterColumn
|
||||
value={column}
|
||||
onChange={(newColumn) => setColumn(index, newColumn)}
|
||||
columns={columns}
|
||||
/>
|
||||
<DataGridFilterOperators value={op} onChange={handleOpChange} />
|
||||
<div className="flex-1">
|
||||
<Input
|
||||
className={cn('h-8 p-2', {
|
||||
'border-destructive': isNotEmptyValue(error),
|
||||
})}
|
||||
placeholder="Enter a value"
|
||||
value={value}
|
||||
onChange={(event) => setValue(index, event.target.value)}
|
||||
/>
|
||||
<span
|
||||
className={`inline-flex h-[0.875rem] text-xs- text-destructive ${isNotEmptyValue(error) ? 'visible' : 'invisible'}`}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="flex-i h-8 w-8"
|
||||
onClick={() => removeFilter(index)}
|
||||
>
|
||||
<X width={12} height={12} />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DataGridFilter;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Badge } from '@/components/ui/v3/badge';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from '@/components/ui/v3/select';
|
||||
|
||||
type DataFilterColumnProps = {
|
||||
value: string;
|
||||
onChange: (newValue: string) => void;
|
||||
columns: Array<{ id: string; dataType: string }>;
|
||||
};
|
||||
|
||||
function DataGrdiFitlerColumn({
|
||||
value,
|
||||
onChange,
|
||||
columns,
|
||||
}: DataFilterColumnProps) {
|
||||
return (
|
||||
<Select value={value} onValueChange={onChange}>
|
||||
<SelectTrigger className="mp-2 h-8 max-w-[35%]">
|
||||
<span className="!inline-block w-4/5 justify-start overflow-ellipsis text-left">
|
||||
{value}
|
||||
</span>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{columns.map((column) => (
|
||||
<SelectItem key={column.id} value={column.id}>
|
||||
{column.id}{' '}
|
||||
<Badge className="rounded-sm+ bg-secondary p-1 text-[0.75rem] font-normal leading-[0.75]">
|
||||
{/* TODO: Fix type */}
|
||||
{(column as any).dataType}
|
||||
</Badge>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
export default DataGrdiFitlerColumn;
|
||||
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from '@/components/ui/v3/select';
|
||||
import type { DataGridFilterOperator } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGrid/DataGridFilterProvider';
|
||||
|
||||
const OPERATORS = [
|
||||
{ op: '$eq', label: '[$eq] equals' },
|
||||
{ op: '$ne', label: '[$ne] not equals' },
|
||||
{ op: '$in', label: '[$in] in' },
|
||||
{ op: '$nin', label: '[$nin] not in' },
|
||||
{ op: '$gt', label: '[$gt] >' },
|
||||
{ op: '$lt', label: '[$lt] <' },
|
||||
{ op: '$gte', label: '[$gte] >=' },
|
||||
{ op: '$lte', label: '[$lte] <=' },
|
||||
{ op: '$like', label: '[$like] like' },
|
||||
{ op: '$nlike', label: '[$nlike] not like' },
|
||||
{ op: '$ilike', label: '[$ilike] like (case-insensitive)' },
|
||||
{ op: '$nilike', label: '[$nilike] not like (case-insensitive)' },
|
||||
{ op: '$similar', label: '[$similar] similar' },
|
||||
{ op: '$nsimilar', label: '[$nsimilar] not similar' },
|
||||
{ op: '$regex', label: '[$regex] ~' },
|
||||
{ op: '$iregex', label: '[$iregex] ~*' },
|
||||
{ op: '$nregex', label: '[$nregex] !~' },
|
||||
{ op: '$niregex', label: '[$niregex] !~*' },
|
||||
];
|
||||
|
||||
type DataFilterProps = {
|
||||
value: DataGridFilterOperator;
|
||||
onChange: (newOp: DataGridFilterOperator) => void;
|
||||
};
|
||||
|
||||
function DataGridOperators({ value, onChange }: DataFilterProps) {
|
||||
return (
|
||||
<Select value={value} onValueChange={onChange}>
|
||||
<SelectTrigger className="h-8 w-[6rem] p-2">{value}</SelectTrigger>
|
||||
<SelectContent>
|
||||
{OPERATORS.map(({ op, label }) => (
|
||||
<SelectItem key={op} value={op}>
|
||||
<span>[{op}]</span> <span className="text-secondary">{label}</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
export default DataGridOperators;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Button, type ButtonProps } from '@/components/ui/v3/button';
|
||||
import { useDataGridFilter } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGrid/DataGridFilterProvider';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Funnel } from 'lucide-react';
|
||||
import { type ForwardedRef, forwardRef } from 'react';
|
||||
|
||||
function DataBrowserCustomizerTrigger(
|
||||
props: ButtonProps,
|
||||
ref: ForwardedRef<HTMLButtonElement>,
|
||||
) {
|
||||
const { appliedFilters } = useDataGridFilter();
|
||||
const numberOfAppliedFilters = appliedFilters.length;
|
||||
|
||||
const { className, ...buttonProps } = props;
|
||||
|
||||
return (
|
||||
<Button
|
||||
ref={ref}
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className={cn('relative', className)}
|
||||
{...buttonProps}
|
||||
>
|
||||
<Funnel />
|
||||
{numberOfAppliedFilters > 0 && (
|
||||
<span className="absolute bottom-[6px] right-[6px] w-[0.725rem] rounded-full bg-primary-text p-0 text-[0.725rem] leading-none text-paper">
|
||||
{numberOfAppliedFilters}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export default forwardRef(DataBrowserCustomizerTrigger);
|
||||
@@ -0,0 +1,104 @@
|
||||
import { Button } from '@/components/ui/v3/button';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/v3/popover';
|
||||
import {
|
||||
type DataGridFilter as Filter,
|
||||
useDataGridFilter,
|
||||
} from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGrid/DataGridFilterProvider';
|
||||
import { useDataGridConfig } from '@/features/orgs/projects/storage/dataGrid/components/DataGridConfigProvider';
|
||||
import { isEmptyValue, isNotEmptyValue } from '@/lib/utils';
|
||||
import { useState } from 'react';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
import DataGridFilter from './DataGridFilter';
|
||||
import DataGridFilterTrigger from './DataGridFilterTrigger';
|
||||
|
||||
function hasErrors(filters: Filter[]) {
|
||||
return filters.reduce((errors, { op, value, column }, index) => {
|
||||
if (isEmptyValue(value)) {
|
||||
return { ...errors, [`${column}.${index}`]: 'Empty filter' };
|
||||
}
|
||||
if (['$in', '$nin'].includes(op)) {
|
||||
try {
|
||||
JSON.parse(value);
|
||||
} catch {
|
||||
return {
|
||||
...errors,
|
||||
[`${column}.${index}`]: 'Invalid format. ["item1","item 2"]',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function DataGridFilters() {
|
||||
const { filters, addFilter, appliedFilters, setFilters, setAppliedFilters } =
|
||||
useDataGridFilter();
|
||||
const { columns } = useDataGridConfig();
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
function resetFilters() {
|
||||
setFilters(appliedFilters);
|
||||
}
|
||||
|
||||
function handleApplyFilter() {
|
||||
const filterErrors = hasErrors(filters);
|
||||
setErrors(filterErrors);
|
||||
if (isEmptyValue(filterErrors)) {
|
||||
setAppliedFilters(filters);
|
||||
}
|
||||
}
|
||||
|
||||
function handleOpenChange(newOpenState: boolean) {
|
||||
if (!newOpenState) {
|
||||
resetFilters();
|
||||
}
|
||||
}
|
||||
|
||||
function handleAddFilter() {
|
||||
addFilter({ column: columns[0].id, op: '$eq', value: '', id: uuidV4() });
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover onOpenChange={handleOpenChange}>
|
||||
<PopoverTrigger asChild>
|
||||
<DataGridFilterTrigger />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end" className="flex w-[40rem] flex-col gap-6 p-0">
|
||||
<div className="flex w-full flex-col gap-0 px-3 pb-0 pt-6">
|
||||
{isNotEmptyValue(filters) &&
|
||||
filters.map((filter, index) => (
|
||||
<DataGridFilter
|
||||
{...filter}
|
||||
key={filter.id}
|
||||
index={index}
|
||||
columns={columns as any}
|
||||
error={errors[`${filter.column}.${index}`]}
|
||||
/>
|
||||
))}
|
||||
{isEmptyValue(filters) && (
|
||||
<p>
|
||||
<strong>No filters applied to this table</strong>
|
||||
<br />
|
||||
Add a filter below to filter the table
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-t-1 border-t-[#e2e8f0] p-3 dark:border-t-[#2f363d]">
|
||||
<Button variant="outline" size="sm" onClick={handleAddFilter}>
|
||||
Add filter
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={handleApplyFilter}>
|
||||
Apply filter
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
export default DataGridFilters;
|
||||
@@ -0,0 +1 @@
|
||||
export { default as DataGridFilters } from './DataGridFilters';
|
||||
@@ -9,7 +9,6 @@ export default function useTablePath() {
|
||||
const {
|
||||
query: { dataSourceSlug, schemaSlug, tableSlug },
|
||||
} = useRouter();
|
||||
|
||||
if (!dataSourceSlug || !schemaSlug || !tableSlug) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { FormActivityIndicator } from '@/components/form/FormActivityIndicator';
|
||||
import { InlineCode } from '@/components/ui/v3/inline-code';
|
||||
import { useTablePath } from '@/features/orgs/projects/database/common/hooks/useTablePath';
|
||||
import { DataBrowserEmptyState } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserEmptyState';
|
||||
import { useDataGridFilter } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGrid/DataGridFilterProvider';
|
||||
import { DataBrowserGridControls } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGridControls';
|
||||
import { useTableQuery } from '@/features/orgs/projects/database/dataGrid/hooks/useTableQuery';
|
||||
import type { UpdateRecordVariables } from '@/features/orgs/projects/database/dataGrid/hooks/useUpdateRecordMutation';
|
||||
@@ -26,6 +27,7 @@ import { DataGridNumericCell } from '@/features/orgs/projects/storage/dataGrid/c
|
||||
import { DataGridTextCell } from '@/features/orgs/projects/storage/dataGrid/components/DataGridTextCell';
|
||||
import { isNotEmptyValue } from '@/lib/utils';
|
||||
|
||||
import { useTableRows } from '@/features/orgs/projects/database/dataGrid/hooks/useTableRows';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { KeyRound } from 'lucide-react';
|
||||
import dynamic from 'next/dynamic';
|
||||
@@ -157,16 +159,45 @@ export default function DataBrowserGrid({
|
||||
const [currentOffset, setCurrentOffset] = useState<number>(
|
||||
parseInt(page as string, 10) - 1 || 0,
|
||||
);
|
||||
|
||||
const { appliedFilters } = useDataGridFilter();
|
||||
const { mutateAsync: updateRow } = useUpdateRecordWithToastMutation();
|
||||
|
||||
const sortByString = isNotEmptyValue(sortBy?.[0])
|
||||
? `${sortBy[0].id}.${sortBy[0].desc}`
|
||||
: 'default-order';
|
||||
const filterString = isNotEmptyValue(appliedFilters)
|
||||
? appliedFilters
|
||||
.map((filter) => `${filter.column}-${filter.op}-${filter.value}`)
|
||||
.join('')
|
||||
: 'no-filter';
|
||||
|
||||
const { data, status, error, refetch } = useTableQuery(
|
||||
[currentTablePath, currentOffset, sortByString],
|
||||
const {
|
||||
data,
|
||||
status,
|
||||
error,
|
||||
refetch,
|
||||
isFetching: isTableDataFetching,
|
||||
} = useTableQuery([currentTablePath], {
|
||||
limit,
|
||||
offset: currentOffset * limit,
|
||||
orderBy:
|
||||
sortBy?.map(({ id, desc }) => ({
|
||||
columnName: id,
|
||||
mode: desc ? 'DESC' : 'ASC',
|
||||
})) || [],
|
||||
filters: appliedFilters,
|
||||
});
|
||||
|
||||
const { columns, metadata } = data || {
|
||||
columns: [] as NormalizedQueryDataRow[],
|
||||
};
|
||||
|
||||
const columnNames = columns?.map((column) => column.column_name);
|
||||
|
||||
const { data: tableRowsData, isFetching: isTableRowsFetching } = useTableRows(
|
||||
[currentTablePath, currentOffset, sortByString, filterString],
|
||||
{
|
||||
columnNames,
|
||||
limit,
|
||||
offset: currentOffset * limit,
|
||||
orderBy:
|
||||
@@ -174,15 +205,16 @@ export default function DataBrowserGrid({
|
||||
columnName: id,
|
||||
mode: desc ? 'DESC' : 'ASC',
|
||||
})) || [],
|
||||
filters: appliedFilters,
|
||||
queryOptions: {
|
||||
enabled: !isTableDataFetching,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const { columns, rows, numberOfRows, metadata } = data || {
|
||||
columns: [] as NormalizedQueryDataRow[],
|
||||
rows: [] as NormalizedQueryDataRow[],
|
||||
numberOfRows: 0,
|
||||
};
|
||||
|
||||
const rows = tableRowsData?.rows || ([] as NormalizedQueryDataRow[]);
|
||||
const numberOfRows = tableRowsData?.numberOfRows || 0;
|
||||
const tableRowsError = tableRowsData?.error;
|
||||
useEffect(() => {
|
||||
if (
|
||||
currentTablePath &&
|
||||
@@ -262,8 +294,6 @@ export default function DataBrowserGrid({
|
||||
[columns, currentTablePath, queryClient, updateRow],
|
||||
);
|
||||
|
||||
const memoizedData = useMemo(() => rows, [rows]);
|
||||
|
||||
async function handleInsertRowClick() {
|
||||
openDrawer({
|
||||
title: 'Insert a New Row',
|
||||
@@ -324,11 +354,17 @@ export default function DataBrowserGrid({
|
||||
<DataGrid
|
||||
ref={dataGridRef}
|
||||
columns={memoizedColumns}
|
||||
data={memoizedData}
|
||||
data={rows}
|
||||
allowSelection
|
||||
allowResize
|
||||
allowSort
|
||||
emptyStateMessage="No rows found."
|
||||
emptyStateMessage={
|
||||
tableRowsError ? (
|
||||
<span className="text-destructive">Error: {tableRowsError}</span>
|
||||
) : (
|
||||
'No rows found.'
|
||||
)
|
||||
}
|
||||
loading={status === 'loading'}
|
||||
sortBy={sortBy}
|
||||
className="pb-17 sm:pb-0"
|
||||
@@ -352,6 +388,7 @@ export default function DataBrowserGrid({
|
||||
refetchData={refetch}
|
||||
/>
|
||||
}
|
||||
isFetching={!!isTableRowsFetching}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import { useTablePath } from '@/features/orgs/projects/database/common/hooks/useTablePath';
|
||||
import PersistenDataGrdiFilterStorage from '@/features/orgs/projects/database/dataGrid/utils/PersistentDataGridFilterStorage';
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
type PropsWithChildren,
|
||||
} from 'react';
|
||||
|
||||
function updateFilterInArray(
|
||||
filters: DataGridFilter[],
|
||||
index: number,
|
||||
newValue: DataGridFilter,
|
||||
) {
|
||||
return [...filters.slice(0, index), newValue, ...filters.slice(index + 1)];
|
||||
}
|
||||
|
||||
function updateFilter(
|
||||
oldFilters: DataGridFilter[],
|
||||
index: number,
|
||||
filterKey: keyof DataGridFilter,
|
||||
newValue: string | DataGridFilterOperator,
|
||||
) {
|
||||
const filter = oldFilters[index];
|
||||
const filterToUpdate = {
|
||||
...filter,
|
||||
[filterKey]: newValue,
|
||||
};
|
||||
return updateFilterInArray(oldFilters, index, filterToUpdate);
|
||||
}
|
||||
|
||||
export type DataGridFilterOperator =
|
||||
| '$eq'
|
||||
| '$ne'
|
||||
| '$in'
|
||||
| '$nin'
|
||||
| '$gt'
|
||||
| '$lt'
|
||||
| '$gte'
|
||||
| '$lte'
|
||||
| '$like'
|
||||
| '$nlike'
|
||||
| '$ilike'
|
||||
| '$nilike'
|
||||
| '$similar'
|
||||
| '$nsimilar'
|
||||
| '$regex'
|
||||
| '$iregex'
|
||||
| '$nregex'
|
||||
| '$niregex';
|
||||
|
||||
export type DataGridFilter = {
|
||||
column: string;
|
||||
op: DataGridFilterOperator;
|
||||
value: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
type DataGridFilterContextProps = {
|
||||
appliedFilters: DataGridFilter[];
|
||||
setAppliedFilters: (filters: DataGridFilter[]) => void;
|
||||
filters: DataGridFilter[];
|
||||
setFilters: (filters: DataGridFilter[]) => void;
|
||||
addFilter: (newFilter: DataGridFilter) => void;
|
||||
removeFilter: (index: number) => void;
|
||||
setValue: (index: number, newValue: string) => void;
|
||||
setColumn: (index: number, newColumn: string) => void;
|
||||
setOp: (index: number, newOp: DataGridFilterOperator) => void;
|
||||
};
|
||||
|
||||
const DataGridFilterContext = createContext<DataGridFilterContextProps>({
|
||||
appliedFilters: [] as DataGridFilter[],
|
||||
setAppliedFilters: () => {},
|
||||
filters: [] as DataGridFilter[],
|
||||
setFilters: () => {},
|
||||
addFilter: () => {},
|
||||
removeFilter: () => {},
|
||||
setValue: () => {},
|
||||
setColumn: () => {},
|
||||
setOp: () => {},
|
||||
});
|
||||
|
||||
function DataGridFilterProvider({ children }: PropsWithChildren) {
|
||||
const tablePath = useTablePath();
|
||||
const [appliedFilters, _setAppliedFilters] = useState<DataGridFilter[]>(() =>
|
||||
PersistenDataGrdiFilterStorage.getDataGridFilters(tablePath),
|
||||
);
|
||||
const [filters, setFilters] = useState<DataGridFilter[]>(() =>
|
||||
PersistenDataGrdiFilterStorage.getDataGridFilters(tablePath),
|
||||
);
|
||||
// const [loadedFiltersTablePath, setLoadedFiltersTablePath] = useState(
|
||||
// () => tablePath,
|
||||
// );
|
||||
|
||||
// const test = useRef<string | null>(null);
|
||||
|
||||
function addFilter(newFilter: DataGridFilter) {
|
||||
setFilters((oldFilters) => oldFilters.concat(newFilter));
|
||||
}
|
||||
|
||||
function setAppliedFilters(newFilters: DataGridFilter[]) {
|
||||
_setAppliedFilters(newFilters);
|
||||
PersistenDataGrdiFilterStorage.saveDataGridFilters(tablePath, newFilters);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const filtersForTheTable =
|
||||
PersistenDataGrdiFilterStorage.getDataGridFilters(tablePath);
|
||||
setFilters(filtersForTheTable);
|
||||
_setAppliedFilters(filtersForTheTable);
|
||||
}, [tablePath]);
|
||||
|
||||
const contextValue: DataGridFilterContextProps = useMemo(
|
||||
() => ({
|
||||
filters,
|
||||
setFilters,
|
||||
appliedFilters,
|
||||
setAppliedFilters,
|
||||
addFilter,
|
||||
removeFilter(index: number) {
|
||||
setFilters((oldFilters) => {
|
||||
const newFilters = oldFilters.filter((_, i) => index !== i);
|
||||
PersistenDataGrdiFilterStorage.saveDataGridFilters(
|
||||
tablePath,
|
||||
newFilters,
|
||||
);
|
||||
return newFilters;
|
||||
});
|
||||
},
|
||||
|
||||
setColumn(index: number, newColumnValue: string) {
|
||||
setFilters((oldFilters) =>
|
||||
updateFilter(oldFilters, index, 'column', newColumnValue),
|
||||
);
|
||||
},
|
||||
|
||||
setValue(index: number, newValue: string) {
|
||||
setFilters((oldFilters) =>
|
||||
updateFilter(oldFilters, index, 'value', newValue),
|
||||
);
|
||||
},
|
||||
|
||||
setOp(index: number, newOp: DataGridFilterOperator) {
|
||||
setFilters((oldFilters) =>
|
||||
updateFilter(oldFilters, index, 'op', newOp),
|
||||
);
|
||||
},
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[appliedFilters, filters],
|
||||
);
|
||||
|
||||
return (
|
||||
<DataGridFilterContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</DataGridFilterContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export default DataGridFilterProvider;
|
||||
|
||||
export function useDataGridFilter() {
|
||||
const context = useContext(DataGridFilterContext);
|
||||
|
||||
return context;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as DataGridFilterProvider } from './DataGridFilterProvider';
|
||||
|
||||
export * from './DataGridFilterProvider';
|
||||
@@ -2,6 +2,7 @@ import { useDialog } from '@/components/common/DialogProvider';
|
||||
import { Badge } from '@/components/ui/v3/badge';
|
||||
import { ButtonWithLoading as Button } from '@/components/ui/v3/button';
|
||||
import { DataGridCustomizerControls } from '@/features/orgs/projects/common/components/DataGridCustomizerControls';
|
||||
import { DataGridFilters } from '@/features/orgs/projects/common/components/DataGridFilters';
|
||||
import { useDeleteRecordMutation } from '@/features/orgs/projects/database/dataGrid/hooks/useDeleteRecordMutation';
|
||||
import type { DataBrowserGridColumn } from '@/features/orgs/projects/database/dataGrid/types/dataBrowser';
|
||||
import { useDataGridConfig } from '@/features/orgs/projects/storage/dataGrid/components/DataGridConfigProvider';
|
||||
@@ -156,6 +157,7 @@ export default function DataBrowserGridControls({
|
||||
{...restPaginationProps}
|
||||
/>
|
||||
)}
|
||||
<DataGridFilters />
|
||||
<DataGridCustomizerControls />
|
||||
<Button onClick={onInsertRowClick} size="sm">
|
||||
<Plus className="h-4 w-4" /> Insert row
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { DataGridFilter } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGrid/DataGridFilterProvider';
|
||||
import type {
|
||||
ForeignKeyRelation,
|
||||
MutationOrQueryBaseOptions,
|
||||
@@ -9,7 +10,6 @@ import type {
|
||||
import { extractForeignKeyRelation } from '@/features/orgs/projects/database/dataGrid/utils/extractForeignKeyRelation';
|
||||
import { getPreparedReadOnlyHasuraQuery } from '@/features/orgs/projects/database/dataGrid/utils/hasuraQueryHelpers';
|
||||
import { POSTGRESQL_ERROR_CODES } from '@/features/orgs/projects/database/dataGrid/utils/postgresqlConstants';
|
||||
import { formatWithArray } from 'node-pg-format';
|
||||
|
||||
export interface FetchTableOptions extends MutationOrQueryBaseOptions {
|
||||
/**
|
||||
@@ -30,6 +30,12 @@ export interface FetchTableOptions extends MutationOrQueryBaseOptions {
|
||||
* Determines whether the query should fetch the rows or not.
|
||||
*/
|
||||
preventRowFetching?: boolean;
|
||||
/**
|
||||
* Filtering configuration.
|
||||
*
|
||||
* @default []
|
||||
*/
|
||||
filters?: DataGridFilter[];
|
||||
}
|
||||
|
||||
export interface FetchTableReturnType {
|
||||
@@ -37,18 +43,10 @@ export interface FetchTableReturnType {
|
||||
* List of columns in the table.
|
||||
*/
|
||||
columns: NormalizedQueryDataRow[];
|
||||
/**
|
||||
* List of rows in the table.
|
||||
*/
|
||||
rows: NormalizedQueryDataRow[];
|
||||
/**
|
||||
* Foreign key relations in the table.
|
||||
*/
|
||||
foreignKeyRelations: ForeignKeyRelation[];
|
||||
/**
|
||||
* Total number of rows in the table.
|
||||
*/
|
||||
numberOfRows: number;
|
||||
/**
|
||||
* Response metadata that usually contains information about the schema and
|
||||
* the table for which the query was run.
|
||||
@@ -68,43 +66,7 @@ export default async function fetchTable({
|
||||
table,
|
||||
appUrl,
|
||||
adminSecret,
|
||||
limit,
|
||||
offset,
|
||||
orderBy,
|
||||
preventRowFetching,
|
||||
}: FetchTableOptions): Promise<FetchTableReturnType> {
|
||||
let limitAndOffsetClause = '';
|
||||
|
||||
if (preventRowFetching) {
|
||||
limitAndOffsetClause = `LIMIT 0`;
|
||||
} else if (limit && offset) {
|
||||
limitAndOffsetClause = `LIMIT ${limit} OFFSET ${offset}`;
|
||||
} else if (limit) {
|
||||
limitAndOffsetClause = `LIMIT ${limit}`;
|
||||
}
|
||||
|
||||
let orderByClause = 'ORDER BY 1';
|
||||
|
||||
if (orderBy && orderBy.length > 0) {
|
||||
// Note: This part will be added to the SQL template
|
||||
const pgFormatTemplate = orderBy.map(() => '%I %s').join(' ');
|
||||
|
||||
// Note: We are flattening object values so that we can pass them to the
|
||||
// formatter function as arguments
|
||||
const flattenedOrderByValues = orderBy.reduce<OrderBy[]>(
|
||||
(values, currentOrderBy) => {
|
||||
const currentValues = Object.values(currentOrderBy) as OrderBy[];
|
||||
return [...values, ...currentValues];
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
orderByClause = formatWithArray(
|
||||
`ORDER BY ${pgFormatTemplate}`,
|
||||
flattenedOrderByValues,
|
||||
);
|
||||
}
|
||||
|
||||
const response = await fetch(`${appUrl}/v2/query`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -153,14 +115,6 @@ export default async function fetchTable({
|
||||
schema,
|
||||
table,
|
||||
),
|
||||
getPreparedReadOnlyHasuraQuery(
|
||||
dataSource,
|
||||
`SELECT ROW_TO_JSON(TABLE_DATA) FROM (SELECT * FROM %I.%I %s %s) TABLE_DATA`,
|
||||
schema,
|
||||
table,
|
||||
orderByClause,
|
||||
limitAndOffsetClause,
|
||||
),
|
||||
getPreparedReadOnlyHasuraQuery(
|
||||
dataSource,
|
||||
`SELECT ROW_TO_JSON(TABLE_DATA) FROM (\
|
||||
@@ -178,12 +132,6 @@ export default async function fetchTable({
|
||||
schema,
|
||||
table,
|
||||
),
|
||||
getPreparedReadOnlyHasuraQuery(
|
||||
dataSource,
|
||||
`SELECT COUNT(*) FROM %I.%I`,
|
||||
schema,
|
||||
table,
|
||||
),
|
||||
],
|
||||
type: 'bulk',
|
||||
version: 1,
|
||||
@@ -207,8 +155,6 @@ export default async function fetchTable({
|
||||
if (schemaNotFound || tableNotFound) {
|
||||
return {
|
||||
columns: [],
|
||||
rows: [],
|
||||
numberOfRows: 0,
|
||||
foreignKeyRelations: [],
|
||||
metadata: { schema, table, schemaNotFound, tableNotFound },
|
||||
};
|
||||
@@ -220,8 +166,6 @@ export default async function fetchTable({
|
||||
) {
|
||||
return {
|
||||
columns: [],
|
||||
rows: [],
|
||||
numberOfRows: 0,
|
||||
foreignKeyRelations: [],
|
||||
metadata: { schema, table, columnsNotFound: true },
|
||||
};
|
||||
@@ -237,9 +181,7 @@ export default async function fetchTable({
|
||||
}
|
||||
|
||||
const [, ...rawColumns] = responseData[0].result;
|
||||
const [, ...rawData] = responseData[1].result;
|
||||
const [, ...rawConstraints] = responseData[2].result;
|
||||
const [, ...[rawAggregate]] = responseData[3].result;
|
||||
const [, ...rawConstraints] = responseData[1].result;
|
||||
|
||||
const foreignKeyRelationMap = new Map<string, string>();
|
||||
const uniqueKeyConstraintMap = new Map<string, string[]>();
|
||||
@@ -323,13 +265,8 @@ export default async function fetchTable({
|
||||
} as NormalizedQueryDataRow;
|
||||
})
|
||||
.sort((a, b) => a.ordinal_position - b.ordinal_position);
|
||||
|
||||
return {
|
||||
columns,
|
||||
rows: rawData.map((rawRow) =>
|
||||
JSON.parse(rawRow),
|
||||
) as NormalizedQueryDataRow[],
|
||||
foreignKeyRelations: flatForeignKeyRelations,
|
||||
numberOfRows: rawAggregate ? parseInt(rawAggregate, 10) : 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import type { DataGridFilter } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGrid/DataGridFilterProvider';
|
||||
import type {
|
||||
MutationOrQueryBaseOptions,
|
||||
NormalizedQueryDataRow,
|
||||
OrderBy,
|
||||
} from '@/features/orgs/projects/database/dataGrid/types/dataBrowser';
|
||||
import { getPreparedReadOnlyHasuraQuery } from '@/features/orgs/projects/database/dataGrid/utils/hasuraQueryHelpers';
|
||||
import { isNotEmptyValue } from '@/lib/utils';
|
||||
|
||||
export interface FetchTableRowsOptions extends MutationOrQueryBaseOptions {
|
||||
/**
|
||||
* Name of the columns to fetch
|
||||
*/
|
||||
columnNames: string[];
|
||||
/**
|
||||
* Limit of rows to fetch.
|
||||
*/
|
||||
limit: number;
|
||||
/**
|
||||
* Offset of rows to fetch.
|
||||
*/
|
||||
offset: number;
|
||||
/**
|
||||
* Ordering configuration.
|
||||
*
|
||||
* @default []
|
||||
*/
|
||||
orderBy: OrderBy[];
|
||||
/**
|
||||
* Filtering configuration.
|
||||
*
|
||||
* @default []
|
||||
*/
|
||||
filters: DataGridFilter[];
|
||||
}
|
||||
|
||||
export type FetchTableRowsResult = {
|
||||
error?: string | null;
|
||||
rows: NormalizedQueryDataRow[];
|
||||
numberOfRows: number;
|
||||
};
|
||||
|
||||
function createRowQuery({
|
||||
columnNames,
|
||||
limit,
|
||||
offset,
|
||||
orderBy,
|
||||
filters,
|
||||
schema,
|
||||
table,
|
||||
dataSource,
|
||||
}: FetchTableRowsOptions) {
|
||||
return {
|
||||
type: 'select',
|
||||
args: {
|
||||
source: dataSource,
|
||||
table: { schema, name: table },
|
||||
columns: columnNames,
|
||||
// TODO: create function
|
||||
where: {
|
||||
$and: filters?.map(({ column, op, value }) => ({
|
||||
[column]: {
|
||||
[op]: op === '$in' || op === '$nin' ? JSON.parse(value) : value,
|
||||
},
|
||||
})),
|
||||
},
|
||||
offset,
|
||||
limit,
|
||||
order_by:
|
||||
orderBy?.map((ob) => ({
|
||||
column: ob.columnName,
|
||||
type: ob.mode.toLocaleLowerCase(),
|
||||
})) ?? [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchTableRows({
|
||||
columnNames,
|
||||
limit,
|
||||
offset,
|
||||
orderBy,
|
||||
filters,
|
||||
adminSecret,
|
||||
dataSource,
|
||||
appUrl,
|
||||
table,
|
||||
schema,
|
||||
}: FetchTableRowsOptions): Promise<FetchTableRowsResult> {
|
||||
const body = {
|
||||
type: 'bulk',
|
||||
args: [
|
||||
createRowQuery({
|
||||
columnNames,
|
||||
limit,
|
||||
offset,
|
||||
orderBy,
|
||||
filters,
|
||||
dataSource,
|
||||
table,
|
||||
schema,
|
||||
appUrl,
|
||||
adminSecret,
|
||||
}),
|
||||
getPreparedReadOnlyHasuraQuery(
|
||||
dataSource,
|
||||
`SELECT COUNT(*) FROM %I.%I`,
|
||||
schema,
|
||||
table,
|
||||
),
|
||||
],
|
||||
};
|
||||
const response = await fetch(`${appUrl}/v2/query`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-hasura-admin-secret': adminSecret,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
const responseBody = await response.json();
|
||||
|
||||
if (isNotEmptyValue(responseBody.error)) {
|
||||
return {
|
||||
rows: [],
|
||||
error: responseBody.error,
|
||||
numberOfRows: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const [
|
||||
rows,
|
||||
{
|
||||
result: [, [maxNumberOfRows]],
|
||||
},
|
||||
] = responseBody;
|
||||
|
||||
return {
|
||||
rows,
|
||||
error: null,
|
||||
numberOfRows: isNotEmptyValue(filters)
|
||||
? rows.length
|
||||
: Number(maxNumberOfRows),
|
||||
};
|
||||
}
|
||||
|
||||
export default fetchTableRows;
|
||||
@@ -0,0 +1 @@
|
||||
export { default as useTableRows } from './useTableRows';
|
||||
@@ -0,0 +1,66 @@
|
||||
import { generateAppServiceUrl } from '@/features/orgs/projects/common/utils/generateAppServiceUrl';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { isNotEmptyValue } from '@/lib/utils';
|
||||
import { getHasuraAdminSecret } from '@/utils/env';
|
||||
import {
|
||||
useQuery,
|
||||
type QueryKey,
|
||||
type UseQueryOptions,
|
||||
} from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/router';
|
||||
import type {
|
||||
FetchTableRowsOptions,
|
||||
FetchTableRowsResult,
|
||||
} from './fetchTableRows';
|
||||
import fetchTableRows from './fetchTableRows';
|
||||
|
||||
export interface UseTableRowsQueryOptions
|
||||
extends Pick<
|
||||
FetchTableRowsOptions,
|
||||
'limit' | 'filters' | 'offset' | 'orderBy' | 'columnNames'
|
||||
> {
|
||||
/**
|
||||
* Props passed to the underlying query hook.
|
||||
*/
|
||||
queryOptions?: UseQueryOptions;
|
||||
}
|
||||
|
||||
function useTableRows(
|
||||
queryKey: QueryKey,
|
||||
{ queryOptions, ...options }: UseTableRowsQueryOptions,
|
||||
) {
|
||||
const { project } = useProject();
|
||||
const {
|
||||
query: { dataSourceSlug, schemaSlug, tableSlug },
|
||||
isReady,
|
||||
} = useRouter();
|
||||
|
||||
const dependenciesLoaded =
|
||||
isNotEmptyValue(project) && isNotEmptyValue(options.columnNames) && isReady;
|
||||
return useQuery<FetchTableRowsResult>(queryKey, {
|
||||
queryFn: () => {
|
||||
const appUrl = isNotEmptyValue(project)
|
||||
? generateAppServiceUrl(project!.subdomain, project!.region, 'hasura')
|
||||
: '';
|
||||
return fetchTableRows({
|
||||
appUrl,
|
||||
dataSource: dataSourceSlug as string,
|
||||
schema: schemaSlug as string,
|
||||
table: tableSlug as string,
|
||||
adminSecret:
|
||||
process.env.NEXT_PUBLIC_ENV === 'dev'
|
||||
? getHasuraAdminSecret()
|
||||
: project!.config!.hasura.adminSecret,
|
||||
...options,
|
||||
});
|
||||
},
|
||||
retry: false,
|
||||
keepPreviousData: true,
|
||||
...(queryOptions && { queryOptions }),
|
||||
enabled: isNotEmptyValue(queryOptions?.enabled)
|
||||
? queryOptions.enabled && dependenciesLoaded
|
||||
: dependenciesLoaded,
|
||||
});
|
||||
}
|
||||
|
||||
export default useTableRows;
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { DataGridFilter } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGrid/DataGridFilterProvider';
|
||||
import { isEmptyValue } from '@/lib/utils';
|
||||
|
||||
export const DATA_GRID_FILTER_STORAGE_KEY = 'nhost_data_grid_filter_storage';
|
||||
|
||||
class PersistenDataGrdiFilterStorage {
|
||||
private static getAllStoredData(): Record<string, DataGridFilter[]> {
|
||||
const storedData = localStorage.getItem(DATA_GRID_FILTER_STORAGE_KEY);
|
||||
if (isEmptyValue(storedData)) {
|
||||
return {};
|
||||
}
|
||||
const allStoredData = JSON.parse(storedData as string);
|
||||
|
||||
return allStoredData;
|
||||
}
|
||||
|
||||
static getDataGridFilters(tablePath: string): DataGridFilter[] {
|
||||
const allStoredData = PersistenDataGrdiFilterStorage.getAllStoredData();
|
||||
return allStoredData[tablePath] ?? [];
|
||||
}
|
||||
|
||||
static saveDataGridFilters(tablePath: string, filters: DataGridFilter[]) {
|
||||
const allStoredData = PersistenDataGrdiFilterStorage.getAllStoredData();
|
||||
|
||||
const updatedAllStoredData: Record<string, DataGridFilter[]> = {
|
||||
...allStoredData,
|
||||
[tablePath]: filters,
|
||||
};
|
||||
|
||||
localStorage.setItem(
|
||||
DATA_GRID_FILTER_STORAGE_KEY,
|
||||
JSON.stringify(updatedAllStoredData),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PersistenDataGrdiFilterStorage;
|
||||
@@ -67,6 +67,8 @@ export function getPreparedReadOnlyHasuraQuery(
|
||||
...args,
|
||||
);
|
||||
|
||||
// console.log({ preparedHasuraQuery });
|
||||
|
||||
return {
|
||||
...preparedHasuraQuery,
|
||||
args: {
|
||||
|
||||
@@ -21,23 +21,22 @@ import { ReplicasFormSection } from '@/features/orgs/projects/services/component
|
||||
import { StorageFormSection } from '@/features/orgs/projects/services/components/ServiceForm/components/StorageFormSection';
|
||||
|
||||
import {
|
||||
defaultServiceFormValues,
|
||||
validationSchema,
|
||||
type Port,
|
||||
type ServiceFormProps,
|
||||
type ServiceFormValues,
|
||||
} from '@/features/orgs/projects/services/components/ServiceForm/ServiceFormTypes';
|
||||
|
||||
import { useLocalMimirClient } from '@/features/orgs/projects/hooks/useLocalMimirClient';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { getFormattedServiceConfig } from '@/features/orgs/projects/services/utils/getFormattedServiceConfig';
|
||||
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
|
||||
import {
|
||||
useInsertRunServiceConfigMutation,
|
||||
useReplaceRunServiceConfigMutation,
|
||||
type ConfigRunServiceConfigInsertInput,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { RESOURCE_VCPU_MULTIPLIER } from '@/utils/constants/common';
|
||||
import { copy } from '@/utils/copy';
|
||||
import { removeTypename } from '@/utils/helpers';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
@@ -69,14 +68,7 @@ export default function ServiceForm({
|
||||
useState<Error | null>(null);
|
||||
|
||||
const form = useForm<ServiceFormValues>({
|
||||
defaultValues: initialData ?? {
|
||||
compute: {
|
||||
cpu: 62,
|
||||
memory: 128,
|
||||
},
|
||||
replicas: 1,
|
||||
autoscaler: null,
|
||||
},
|
||||
defaultValues: initialData ?? defaultServiceFormValues,
|
||||
reValidateMode: 'onSubmit',
|
||||
resolver: yupResolver(validationSchema),
|
||||
});
|
||||
@@ -142,66 +134,8 @@ export default function ServiceForm({
|
||||
onDirtyStateChange(isDirty, location);
|
||||
}, [isDirty, location, onDirtyStateChange]);
|
||||
|
||||
const getFormattedConfig = (values: ServiceFormValues) => {
|
||||
// Remove any __typename property from the values
|
||||
const sanitizedValues = removeTypename(values) as ServiceFormValues;
|
||||
const sanitizedInitialDataPorts: Port[] = initialData?.ports
|
||||
? removeTypename(initialData.ports)
|
||||
: [];
|
||||
|
||||
const config: ConfigRunServiceConfigInsertInput = {
|
||||
name: sanitizedValues.name,
|
||||
image: {
|
||||
image: sanitizedValues.image,
|
||||
pullCredentials: sanitizedValues.pullCredentials,
|
||||
},
|
||||
command: sanitizedValues.command?.map((arg) => arg.argument),
|
||||
resources: {
|
||||
compute: {
|
||||
cpu: sanitizedValues.compute?.cpu,
|
||||
memory: sanitizedValues.compute?.memory,
|
||||
},
|
||||
storage: sanitizedValues.storage?.map((item) => ({
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
capacity: item.capacity,
|
||||
})),
|
||||
replicas: sanitizedValues.replicas,
|
||||
autoscaler: sanitizedValues.autoscaler
|
||||
? {
|
||||
maxReplicas: sanitizedValues.autoscaler?.maxReplicas,
|
||||
}
|
||||
: null,
|
||||
},
|
||||
environment: sanitizedValues.environment?.map((item) => ({
|
||||
name: item.name,
|
||||
value: item.value,
|
||||
})),
|
||||
ports: sanitizedValues.ports?.map((item) => ({
|
||||
port: item.port,
|
||||
type: item.type,
|
||||
publish: item.publish,
|
||||
ingresses: item.ingresses as any, // cannot be changed on the UI always null type checking can be skipped.
|
||||
rateLimit:
|
||||
sanitizedInitialDataPorts.find(
|
||||
(port) => port.port === item.port && port.type === item.type,
|
||||
)?.rateLimit ?? (null as any), // cannot be changed on the UI always null type checking can be skipped.
|
||||
})),
|
||||
healthCheck: sanitizedValues.healthCheck
|
||||
? {
|
||||
port: sanitizedValues.healthCheck?.port,
|
||||
initialDelaySeconds:
|
||||
sanitizedValues.healthCheck?.initialDelaySeconds,
|
||||
probePeriodSeconds: sanitizedValues.healthCheck?.probePeriodSeconds,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
const createOrUpdateService = async (values: ServiceFormValues) => {
|
||||
const config = getFormattedConfig(values);
|
||||
const config = getFormattedServiceConfig({ values, initialData });
|
||||
|
||||
if (serviceID) {
|
||||
// Update service config
|
||||
@@ -292,7 +226,10 @@ export default function ServiceForm({
|
||||
};
|
||||
|
||||
const copyConfig = () => {
|
||||
const config = getFormattedConfig(formValues);
|
||||
const config = getFormattedServiceConfig({
|
||||
values: formValues,
|
||||
initialData,
|
||||
});
|
||||
|
||||
const base64Config = btoa(JSON.stringify(config));
|
||||
|
||||
|
||||
@@ -87,6 +87,15 @@ export type ServiceFormInitialData = Omit<ServiceFormValues, 'ports'> & {
|
||||
}[];
|
||||
};
|
||||
|
||||
export const defaultServiceFormValues = {
|
||||
compute: {
|
||||
cpu: 62,
|
||||
memory: 128,
|
||||
},
|
||||
replicas: 1,
|
||||
autoscaler: null,
|
||||
};
|
||||
|
||||
export interface ServiceFormProps extends DialogFormProps {
|
||||
/**
|
||||
* To use in conjunction with initialData to allow for updating the service
|
||||
|
||||
@@ -15,7 +15,10 @@ import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatfo
|
||||
import { type RunService } from '@/features/orgs/projects/common/hooks/useRunServices';
|
||||
import { ServiceForm } from '@/features/orgs/projects/services/components/ServiceForm';
|
||||
import { type PortTypes } from '@/features/orgs/projects/services/components/ServiceForm/components/PortsFormSection/PortsFormSectionTypes';
|
||||
import type { ServiceFormInitialData } from '@/features/orgs/projects/services/components/ServiceForm/ServiceFormTypes';
|
||||
import {
|
||||
defaultServiceFormValues,
|
||||
type ServiceFormInitialData,
|
||||
} from '@/features/orgs/projects/services/components/ServiceForm/ServiceFormTypes';
|
||||
import { copy } from '@/utils/copy';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
|
||||
@@ -74,12 +77,15 @@ export default function ServicesList({
|
||||
ingresses: item.ingresses,
|
||||
rateLimit: item.rateLimit,
|
||||
})),
|
||||
compute: service.config?.resources?.compute ?? {
|
||||
cpu: 62,
|
||||
memory: 128,
|
||||
},
|
||||
replicas: service.config?.resources?.replicas,
|
||||
autoscaler: service?.config?.resources?.autoscaler,
|
||||
compute:
|
||||
service.config?.resources?.compute ??
|
||||
defaultServiceFormValues.compute,
|
||||
replicas:
|
||||
service.config?.resources?.replicas ??
|
||||
defaultServiceFormValues.replicas,
|
||||
autoscaler:
|
||||
service?.config?.resources?.autoscaler ??
|
||||
defaultServiceFormValues.autoscaler,
|
||||
storage: service.config?.resources?.storage,
|
||||
} as ServiceFormInitialData
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import { PortTypes } from '@/features/orgs/projects/services/components/ServiceForm/components/PortsFormSection/PortsFormSectionTypes';
|
||||
import getFormattedServiceConfig from './getFormattedServiceConfig';
|
||||
|
||||
describe('getFormattedServiceConfig', () => {
|
||||
it('pghero config should be formatted correctly', () => {
|
||||
const pgheroFormValues = {
|
||||
name: 'pghero',
|
||||
image: 'docker.io/ankane/pghero:latest',
|
||||
command: [],
|
||||
resources: {
|
||||
compute: {
|
||||
cpu: 125,
|
||||
memory: 256,
|
||||
},
|
||||
storage: [],
|
||||
replicas: 1,
|
||||
},
|
||||
environment: [
|
||||
{
|
||||
name: 'DATABASE_URL',
|
||||
value:
|
||||
'postgres://postgres:[PASSWORD]@postgres-service:5432/[SUBDOMAIN]?sslmode=disable',
|
||||
},
|
||||
{
|
||||
name: 'PGHERO_USERNAME',
|
||||
value: '[USER]',
|
||||
},
|
||||
{
|
||||
name: 'PGHERO_PASSWORD',
|
||||
value: '[PASSWORD]',
|
||||
},
|
||||
],
|
||||
ports: [
|
||||
{
|
||||
port: 8080,
|
||||
type: PortTypes.HTTP,
|
||||
publish: true,
|
||||
},
|
||||
],
|
||||
autoscaler: null,
|
||||
compute: {
|
||||
cpu: 125,
|
||||
memory: 256,
|
||||
},
|
||||
replicas: 1,
|
||||
storage: [],
|
||||
};
|
||||
|
||||
const formattedConfig = getFormattedServiceConfig({
|
||||
values: pgheroFormValues,
|
||||
});
|
||||
|
||||
const expected = {
|
||||
name: 'pghero',
|
||||
image: {
|
||||
image: 'docker.io/ankane/pghero:latest',
|
||||
},
|
||||
command: [],
|
||||
resources: {
|
||||
compute: {
|
||||
cpu: 125,
|
||||
memory: 256,
|
||||
},
|
||||
storage: [],
|
||||
replicas: 1,
|
||||
autoscaler: null,
|
||||
},
|
||||
environment: [
|
||||
{
|
||||
name: 'DATABASE_URL',
|
||||
value:
|
||||
'postgres://postgres:[PASSWORD]@postgres-service:5432/[SUBDOMAIN]?sslmode=disable',
|
||||
},
|
||||
{
|
||||
name: 'PGHERO_USERNAME',
|
||||
value: '[USER]',
|
||||
},
|
||||
{
|
||||
name: 'PGHERO_PASSWORD',
|
||||
value: '[PASSWORD]',
|
||||
},
|
||||
],
|
||||
ports: [
|
||||
{
|
||||
port: 8080,
|
||||
type: 'http',
|
||||
publish: true,
|
||||
rateLimit: null,
|
||||
},
|
||||
],
|
||||
healthCheck: null,
|
||||
};
|
||||
|
||||
expect(formattedConfig).toEqual(expected);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
import type {
|
||||
Port,
|
||||
ServiceFormInitialData,
|
||||
ServiceFormValues,
|
||||
} from '@/features/orgs/projects/services/components/ServiceForm/ServiceFormTypes';
|
||||
import type { ConfigRunServiceConfigInsertInput } from '@/utils/__generated__/graphql';
|
||||
import { removeTypename } from '@/utils/helpers';
|
||||
|
||||
export interface GetFormattedServiceConfigProps {
|
||||
values: ServiceFormValues;
|
||||
initialData?: ServiceFormInitialData;
|
||||
}
|
||||
|
||||
export default function getFormattedServiceConfig({
|
||||
values,
|
||||
initialData,
|
||||
}: GetFormattedServiceConfigProps) {
|
||||
// Remove any __typename property from the values
|
||||
const sanitizedValues = removeTypename(values) as ServiceFormValues;
|
||||
const sanitizedInitialDataPorts: Port[] = initialData?.ports
|
||||
? removeTypename(initialData.ports)
|
||||
: [];
|
||||
|
||||
const config: ConfigRunServiceConfigInsertInput = {
|
||||
name: sanitizedValues.name,
|
||||
image: {
|
||||
image: sanitizedValues.image,
|
||||
pullCredentials: sanitizedValues.pullCredentials,
|
||||
},
|
||||
command: sanitizedValues.command?.map((arg) => arg.argument),
|
||||
resources: {
|
||||
compute: {
|
||||
cpu: sanitizedValues.compute?.cpu,
|
||||
memory: sanitizedValues.compute?.memory,
|
||||
},
|
||||
storage: sanitizedValues.storage?.map((item) => ({
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
capacity: item.capacity,
|
||||
})),
|
||||
replicas: sanitizedValues.replicas,
|
||||
autoscaler: sanitizedValues.autoscaler
|
||||
? {
|
||||
maxReplicas: sanitizedValues.autoscaler?.maxReplicas,
|
||||
}
|
||||
: null,
|
||||
},
|
||||
environment: sanitizedValues.environment?.map((item) => ({
|
||||
name: item.name,
|
||||
value: item.value,
|
||||
})),
|
||||
ports: sanitizedValues.ports?.map((item) => ({
|
||||
port: item.port,
|
||||
type: item.type,
|
||||
publish: item.publish,
|
||||
ingresses: item.ingresses as any, // cannot be changed on the UI always null type checking can be skipped.
|
||||
rateLimit:
|
||||
sanitizedInitialDataPorts.find(
|
||||
(port) => port.port === item.port && port.type === item.type,
|
||||
)?.rateLimit ?? (null as any), // cannot be changed on the UI always null type checking can be skipped.
|
||||
})),
|
||||
healthCheck: sanitizedValues.healthCheck
|
||||
? {
|
||||
port: sanitizedValues.healthCheck?.port,
|
||||
initialDelaySeconds: sanitizedValues.healthCheck?.initialDelaySeconds,
|
||||
probePeriodSeconds: sanitizedValues.healthCheck?.probePeriodSeconds,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default as getFormattedServiceConfig } from './getFormattedServiceConfig';
|
||||
@@ -0,0 +1 @@
|
||||
export { default as parseConfigFromInstallLink } from './parseConfigFromInstallLink';
|
||||
@@ -0,0 +1,156 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import parseConfigFromInstallLink from './parseConfigFromInstallLink';
|
||||
|
||||
describe('parseConfigFromInstallLink', () => {
|
||||
it('pghero config without autoscaler should be formatted correctly', () => {
|
||||
const pgheroBase64Config =
|
||||
'eyJuYW1lIjoicGdoZXJvIiwiaW1hZ2UiOnsiaW1hZ2UiOiJkb2NrZXIuaW8vYW5rYW5lL3BnaGVybzpsYXRlc3QifSwiY29tbWFuZCI6W10sInJlc291cmNlcyI6eyJjb21wdXRlIjp7ImNwdSI6MTI1LCJtZW1vcnkiOjI1Nn0sInN0b3JhZ2UiOltdLCJyZXBsaWNhcyI6MX0sImVudmlyb25tZW50IjpbeyJuYW1lIjoiREFUQUJBU0VfVVJMIiwidmFsdWUiOiJwb3N0Z3JlczovL3Bvc3RncmVzOltQQVNTV09SRF1AcG9zdGdyZXMtc2VydmljZTo1NDMyL1tTVUJET01BSU5dP3NzbG1vZGU9ZGlzYWJsZSJ9LHsibmFtZSI6IlBHSEVST19VU0VSTkFNRSIsInZhbHVlIjoiW1VTRVJdIn0seyJuYW1lIjoiUEdIRVJPX1BBU1NXT1JEIiwidmFsdWUiOiJbUEFTU1dPUkRdIn1dLCJwb3J0cyI6W3sicG9ydCI6ODA4MCwidHlwZSI6Imh0dHAiLCJwdWJsaXNoIjp0cnVlfV19';
|
||||
|
||||
const config = parseConfigFromInstallLink(pgheroBase64Config);
|
||||
|
||||
const expected = {
|
||||
name: 'pghero',
|
||||
image: 'docker.io/ankane/pghero:latest',
|
||||
command: [],
|
||||
resources: {
|
||||
compute: {
|
||||
cpu: 125,
|
||||
memory: 256,
|
||||
},
|
||||
storage: [],
|
||||
replicas: 1,
|
||||
},
|
||||
environment: [
|
||||
{
|
||||
name: 'DATABASE_URL',
|
||||
value:
|
||||
'postgres://postgres:[PASSWORD]@postgres-service:5432/[SUBDOMAIN]?sslmode=disable',
|
||||
},
|
||||
{
|
||||
name: 'PGHERO_USERNAME',
|
||||
value: '[USER]',
|
||||
},
|
||||
{
|
||||
name: 'PGHERO_PASSWORD',
|
||||
value: '[PASSWORD]',
|
||||
},
|
||||
],
|
||||
ports: [
|
||||
{
|
||||
port: 8080,
|
||||
type: 'http',
|
||||
publish: true,
|
||||
},
|
||||
],
|
||||
autoscaler: null,
|
||||
compute: {
|
||||
cpu: 125,
|
||||
memory: 256,
|
||||
},
|
||||
replicas: 1,
|
||||
storage: [],
|
||||
};
|
||||
|
||||
expect(config).toEqual(expected);
|
||||
});
|
||||
|
||||
it('antivirus config without autoscaler should be formatted correctly', () => {
|
||||
const antivirusBase64Config =
|
||||
'eyJuYW1lIjoiY2xhbWF2IiwiaW1hZ2UiOnsiaW1hZ2UiOiJkb2NrZXIuaW8vbmhvc3QvY2xhbWF2OjAuMS4xIn0sImNvbW1hbmQiOltdLCJyZXNvdXJjZXMiOnsiY29tcHV0ZSI6eyJjcHUiOjEwMDAsIm1lbW9yeSI6MjA0OH0sInN0b3JhZ2UiOltdLCJyZXBsaWNhcyI6MX0sImVudmlyb25tZW50IjpbXSwicG9ydHMiOlt7InBvcnQiOiIzMzEwIiwidHlwZSI6InRjcCIsInB1Ymxpc2giOmZhbHNlfV19';
|
||||
|
||||
const config = parseConfigFromInstallLink(antivirusBase64Config);
|
||||
|
||||
const expected = {
|
||||
name: 'clamav',
|
||||
image: 'docker.io/nhost/clamav:0.1.1',
|
||||
command: [],
|
||||
resources: {
|
||||
compute: {
|
||||
cpu: 1000,
|
||||
memory: 2048,
|
||||
},
|
||||
storage: [],
|
||||
replicas: 1,
|
||||
},
|
||||
environment: [],
|
||||
ports: [
|
||||
{
|
||||
port: '3310',
|
||||
type: 'tcp',
|
||||
publish: false,
|
||||
},
|
||||
],
|
||||
autoscaler: null,
|
||||
compute: {
|
||||
cpu: 1000,
|
||||
memory: 2048,
|
||||
},
|
||||
replicas: 1,
|
||||
storage: [],
|
||||
};
|
||||
expect(config).toEqual(expected);
|
||||
});
|
||||
|
||||
it('invalid config should throw an error', () => {
|
||||
const invalidBase64Config = 'invalid';
|
||||
|
||||
expect(() => parseConfigFromInstallLink(invalidBase64Config)).toThrow();
|
||||
});
|
||||
|
||||
it('pghero config with autoscaler should be formatted correctly', () => {
|
||||
const pgheroWithAutoscalerBase64 =
|
||||
'eyJuYW1lIjoicGdoZXJvIiwiaW1hZ2UiOnsiaW1hZ2UiOiJkb2NrZXIuaW8vYW5rYW5lL3BnaGVybzpsYXRlc3QifSwiY29tbWFuZCI6W10sInJlc291cmNlcyI6eyJjb21wdXRlIjp7ImNwdSI6MTI1LCJtZW1vcnkiOjI1Nn0sInN0b3JhZ2UiOltdLCJyZXBsaWNhcyI6MSwiYXV0b3NjYWxlciI6eyJtYXhSZXBsaWNhcyI6MTF9fSwiZW52aXJvbm1lbnQiOlt7Im5hbWUiOiJEQVRBQkFTRV9VUkwiLCJ2YWx1ZSI6InBvc3RncmVzOi8vcG9zdGdyZXM6W1BBU1NXT1JEXUBwb3N0Z3Jlcy1zZXJ2aWNlOjU0MzIvW1NVQkRPTUFJTl0/c3NsbW9kZT1kaXNhYmxlIn0seyJuYW1lIjoiUEdIRVJPX1VTRVJOQU1FIiwidmFsdWUiOiJbVVNFUl0ifSx7Im5hbWUiOiJQR0hFUk9fUEFTU1dPUkQiLCJ2YWx1ZSI6IltQQVNTV09SRF0ifV0sInBvcnRzIjpbeyJwb3J0Ijo4MDgwLCJ0eXBlIjoiaHR0cCIsInB1Ymxpc2giOnRydWUsInJhdGVMaW1pdCI6bnVsbH1dLCJoZWFsdGhDaGVjayI6bnVsbH0=';
|
||||
|
||||
const config = parseConfigFromInstallLink(pgheroWithAutoscalerBase64);
|
||||
|
||||
const expected = {
|
||||
name: 'pghero',
|
||||
image: 'docker.io/ankane/pghero:latest',
|
||||
command: [],
|
||||
resources: {
|
||||
compute: {
|
||||
cpu: 125,
|
||||
memory: 256,
|
||||
},
|
||||
storage: [],
|
||||
replicas: 1,
|
||||
autoscaler: {
|
||||
maxReplicas: 11,
|
||||
},
|
||||
},
|
||||
environment: [
|
||||
{
|
||||
name: 'DATABASE_URL',
|
||||
value:
|
||||
'postgres://postgres:[PASSWORD]@postgres-service:5432/[SUBDOMAIN]?sslmode=disable',
|
||||
},
|
||||
{
|
||||
name: 'PGHERO_USERNAME',
|
||||
value: '[USER]',
|
||||
},
|
||||
{
|
||||
name: 'PGHERO_PASSWORD',
|
||||
value: '[PASSWORD]',
|
||||
},
|
||||
],
|
||||
ports: [
|
||||
{
|
||||
port: 8080,
|
||||
type: 'http',
|
||||
publish: true,
|
||||
},
|
||||
],
|
||||
autoscaler: {
|
||||
maxReplicas: 11,
|
||||
},
|
||||
compute: {
|
||||
cpu: 125,
|
||||
memory: 256,
|
||||
},
|
||||
replicas: 1,
|
||||
storage: [],
|
||||
};
|
||||
|
||||
expect(config).toEqual(expected);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import { type RunServiceConfig } from '@/features/orgs/projects/common/hooks/useRunServices';
|
||||
import { type PortTypes } from '@/features/orgs/projects/services/components/ServiceForm/components/PortsFormSection/PortsFormSectionTypes';
|
||||
import {
|
||||
defaultServiceFormValues,
|
||||
type ServiceFormInitialData,
|
||||
} from '@/features/orgs/projects/services/components/ServiceForm/ServiceFormTypes';
|
||||
|
||||
export default function parseConfigFromInstallLink(
|
||||
base64Config: string,
|
||||
): ServiceFormInitialData {
|
||||
const decodedConfig = atob(base64Config);
|
||||
const parsedConfig: RunServiceConfig = JSON.parse(decodedConfig);
|
||||
const initialData = {
|
||||
...parsedConfig,
|
||||
autoscaler:
|
||||
parsedConfig?.resources?.autoscaler ??
|
||||
defaultServiceFormValues.autoscaler,
|
||||
compute:
|
||||
parsedConfig?.resources?.compute ?? defaultServiceFormValues.compute,
|
||||
image: parsedConfig?.image?.image,
|
||||
command: parsedConfig?.command?.map((arg) => ({
|
||||
argument: arg,
|
||||
})),
|
||||
environment:
|
||||
parsedConfig?.environment?.map((env) => ({
|
||||
name: env.name,
|
||||
value: env.value,
|
||||
})) ?? undefined,
|
||||
healthCheck: parsedConfig?.healthCheck
|
||||
? {
|
||||
port: parsedConfig.healthCheck.port ?? 3000,
|
||||
initialDelaySeconds:
|
||||
parsedConfig.healthCheck.initialDelaySeconds ?? 30,
|
||||
probePeriodSeconds: parsedConfig.healthCheck.probePeriodSeconds ?? 60,
|
||||
}
|
||||
: undefined,
|
||||
ports:
|
||||
parsedConfig?.ports?.map((item) => ({
|
||||
port: item.port ?? 3000,
|
||||
type: item.type as PortTypes,
|
||||
publish: Boolean(item.publish),
|
||||
})) ?? [],
|
||||
replicas: parsedConfig?.resources?.replicas,
|
||||
storage: parsedConfig?.resources?.storage ?? undefined,
|
||||
};
|
||||
|
||||
return initialData;
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import type { DataGridHeaderProps } from '@/features/orgs/projects/storage/dataG
|
||||
import { DataGridHeader } from '@/features/orgs/projects/storage/dataGrid/components/DataGridHeader';
|
||||
import { DataTableDesignProvider } from '@/features/orgs/projects/storage/dataGrid/providers/DataTableDesignProvider';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import type { ForwardedRef, ReactNode } from 'react';
|
||||
import { forwardRef, useEffect, useRef } from 'react';
|
||||
import { mergeRefs } from 'react-merge-refs';
|
||||
import type { Column, Row, SortingRule, TableOptions } from 'react-table';
|
||||
@@ -31,7 +31,7 @@ export interface DataGridProps<TColumnData extends object>
|
||||
*
|
||||
* @default null
|
||||
*/
|
||||
emptyStateMessage?: string;
|
||||
emptyStateMessage?: ReactNode;
|
||||
/**
|
||||
* Additional configuration options for the `react-table` hook.
|
||||
*/
|
||||
@@ -71,6 +71,10 @@ export interface DataGridProps<TColumnData extends object>
|
||||
* Determines whether the Grid is used for displaying files.
|
||||
*/
|
||||
isFileDataGrid?: boolean;
|
||||
/**
|
||||
* Determines whether rows are being fetched or not
|
||||
*/
|
||||
isFetching?: boolean;
|
||||
}
|
||||
|
||||
function DataGrid<TColumnData extends object>(
|
||||
@@ -89,6 +93,7 @@ function DataGrid<TColumnData extends object>(
|
||||
loading,
|
||||
className,
|
||||
isFileDataGrid,
|
||||
isFetching,
|
||||
}: DataGridProps<TColumnData>,
|
||||
ref: ForwardedRef<HTMLDivElement>,
|
||||
) {
|
||||
@@ -151,12 +156,19 @@ function DataGrid<TColumnData extends object>(
|
||||
)}
|
||||
>
|
||||
<DataGridFrame>
|
||||
<DataGridHeader {...headerProps} />
|
||||
<DataGridBody
|
||||
isFileDataGrid={isFileDataGrid}
|
||||
emptyStateMessage={emptyStateMessage}
|
||||
loading={loading}
|
||||
/>
|
||||
<div className="relative h-full">
|
||||
<DataGridHeader {...headerProps} />
|
||||
{isFetching && (
|
||||
<div className="absolute top-0 z-50 flex h-full w-full justify-center bg-[rgba(0,0,0,.5)]">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
<DataGridBody
|
||||
isFileDataGrid={isFileDataGrid}
|
||||
emptyStateMessage={emptyStateMessage}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
</DataGridFrame>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -22,9 +22,9 @@ class PersistenDataTableConfigurationStorage {
|
||||
if (isEmptyValue(storedData)) {
|
||||
return {};
|
||||
}
|
||||
const allHiddenColumns = JSON.parse(storedData as string);
|
||||
const allStoredData = JSON.parse(storedData as string);
|
||||
|
||||
return allHiddenColumns;
|
||||
return allStoredData;
|
||||
}
|
||||
|
||||
static getHiddenColumns(tablePath: string): string[] {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { OrgLayout } from '@/features/orgs/layout/OrgLayout';
|
||||
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
|
||||
import { useTablePath } from '@/features/orgs/projects/database/common/hooks/useTablePath';
|
||||
import { DataBrowserGrid } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGrid';
|
||||
import { DataGridFilterProvider } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserGrid/DataGridFilterProvider';
|
||||
import { DataBrowserSidebar } from '@/features/orgs/projects/database/dataGrid/components/DataBrowserSidebar';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import type { ReactElement } from 'react';
|
||||
@@ -31,7 +32,9 @@ export default function DataBrowserTableDetailsPage() {
|
||||
|
||||
return (
|
||||
<RetryableErrorBoundary>
|
||||
<DataBrowserGrid sortBy={sortBy} onSort={handleSortByChange} />
|
||||
<DataGridFilterProvider>
|
||||
<DataBrowserGrid sortBy={sortBy} onSort={handleSortByChange} />
|
||||
</DataGridFilterProvider>
|
||||
</RetryableErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,16 +11,12 @@ import { ServicesIcon } from '@/components/ui/v2/icons/ServicesIcon';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { OrgLayout } from '@/features/orgs/layout/OrgLayout';
|
||||
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
|
||||
import {
|
||||
useRunServices,
|
||||
type RunServiceConfig,
|
||||
} from '@/features/orgs/projects/common/hooks/useRunServices';
|
||||
import { useRunServices } from '@/features/orgs/projects/common/hooks/useRunServices';
|
||||
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { ServiceForm } from '@/features/orgs/projects/services/components/ServiceForm';
|
||||
import { type PortTypes } from '@/features/orgs/projects/services/components/ServiceForm/components/PortsFormSection/PortsFormSectionTypes';
|
||||
import type { ServiceFormInitialData } from '@/features/orgs/projects/services/components/ServiceForm/ServiceFormTypes';
|
||||
import { ServicesList } from '@/features/orgs/projects/services/components/ServicesList';
|
||||
import { parseConfigFromInstallLink } from '@/features/orgs/projects/services/utils/parseConfigFromInstallLink';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback, useEffect, type ReactElement } from 'react';
|
||||
|
||||
@@ -48,29 +44,7 @@ export default function RunPage() {
|
||||
(base64Config: string) => {
|
||||
if (router.query?.config) {
|
||||
try {
|
||||
const decodedConfig = atob(base64Config);
|
||||
const parsedConfig: RunServiceConfig = JSON.parse(decodedConfig);
|
||||
const initialData = {
|
||||
...parsedConfig,
|
||||
autoscaler: parsedConfig?.resources?.autoscaler ?? {
|
||||
maxReplicas: 0,
|
||||
},
|
||||
compute: parsedConfig?.resources?.compute ?? {
|
||||
cpu: 62,
|
||||
memory: 128,
|
||||
},
|
||||
image: parsedConfig?.image?.image,
|
||||
command: parsedConfig?.command?.map((arg) => ({
|
||||
argument: arg,
|
||||
})),
|
||||
ports: parsedConfig?.ports?.map((item) => ({
|
||||
port: item.port,
|
||||
type: item.type as PortTypes,
|
||||
publish: item.publish,
|
||||
})),
|
||||
replicas: parsedConfig?.resources?.replicas,
|
||||
storage: parsedConfig?.resources?.storage,
|
||||
} as ServiceFormInitialData;
|
||||
const initialData = parseConfigFromInstallLink(base64Config);
|
||||
|
||||
openDrawer({
|
||||
title: (
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
"icon": "at",
|
||||
"pages": [
|
||||
"products/auth/providers/overview",
|
||||
"products/auth/providers/sign-in-provider",
|
||||
"products/auth/providers/tokens",
|
||||
"products/auth/providers/connect",
|
||||
"products/auth/providers/idtokens",
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
"openapi-types": "*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"overrides": {
|
||||
"js-yaml@<=4.1.0": ">=4.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ After authorizing the GitHub integration, you'll need to tell Nhost a couple of
|
||||
|
||||
### Base Directory
|
||||
|
||||
This is the folder in your repository where your Nhost folder lives. If your Nhost foder is in the root of your repository, you can leave this as `/`. If it is in a subfolder (like `/backend`), specify that path here.
|
||||
This is the folder in your repository where your Nhost folder lives. If your Nhost folder is in the root of your repository, you can leave this as `/`. If it is in a subfolder (like `/backend`), specify that path here.
|
||||
|
||||
### Deployment Branch
|
||||
|
||||
|
||||
48
docs/pnpm-lock.yaml
generated
48
docs/pnpm-lock.yaml
generated
@@ -4,6 +4,9 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
js-yaml@<=4.1.0: '>=4.1.1'
|
||||
|
||||
packageExtensionsChecksum: sha256-4+NJJHoeDEOtWI2UxgTNLimXyrOojBs00S85/9Babm0=
|
||||
|
||||
importers:
|
||||
@@ -936,9 +939,6 @@ packages:
|
||||
arg@5.0.2:
|
||||
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
||||
|
||||
argparse@1.0.10:
|
||||
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
@@ -2106,12 +2106,8 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@3.14.1:
|
||||
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
|
||||
hasBin: true
|
||||
|
||||
js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsep@1.4.0:
|
||||
@@ -3126,9 +3122,6 @@ packages:
|
||||
space-separated-tokens@2.0.2:
|
||||
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
|
||||
|
||||
sprintf-js@1.0.3:
|
||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -3599,7 +3592,7 @@ snapshots:
|
||||
ajv-errors: 3.0.0(ajv@8.17.1)
|
||||
ajv-formats: 2.1.1(ajv@8.17.1)
|
||||
avsc: 5.7.9
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
jsonpath-plus: 10.3.0
|
||||
node-fetch: 2.6.7
|
||||
transitivePeerDependencies:
|
||||
@@ -3937,7 +3930,7 @@ snapshots:
|
||||
gray-matter: 4.0.3
|
||||
ink: 6.3.1(@types/react@19.1.12)(react@19.2.0)
|
||||
inquirer: 12.9.6(@types/node@24.7.0)
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
mdast: 3.0.0
|
||||
mdast-util-mdx-jsx: 3.2.0
|
||||
react: 19.2.0
|
||||
@@ -3979,7 +3972,7 @@ snapshots:
|
||||
hast-util-to-html: 9.0.5
|
||||
hast-util-to-text: 4.0.2
|
||||
hex-rgb: 5.0.0
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
lodash: 4.17.21
|
||||
mdast: 3.0.0
|
||||
mdast-util-from-markdown: 2.0.2
|
||||
@@ -4094,7 +4087,7 @@ snapshots:
|
||||
favicons: 7.2.0
|
||||
fs-extra: 11.3.2
|
||||
gray-matter: 4.0.3
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
mdast: 3.0.0
|
||||
openapi-types: 12.1.3
|
||||
sharp: 0.33.5
|
||||
@@ -4131,7 +4124,7 @@ snapshots:
|
||||
ink: 6.3.1(@types/react@19.1.12)(react@19.2.0)
|
||||
ink-spinner: 5.0.0(ink@6.3.1(@types/react@19.1.12)(react@19.2.0))(react@19.2.0)
|
||||
is-online: 10.0.0
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
mdast: 3.0.0
|
||||
openapi-types: 12.1.3
|
||||
react: 19.2.0
|
||||
@@ -4161,7 +4154,7 @@ snapshots:
|
||||
'@mintlify/openapi-parser': 0.0.8
|
||||
fs-extra: 11.3.2
|
||||
hast-util-to-mdast: 10.1.2
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
mdast-util-mdx-jsx: 3.2.0
|
||||
neotraverse: 0.6.18
|
||||
openapi-types: 12.1.3
|
||||
@@ -4196,7 +4189,7 @@ snapshots:
|
||||
'@mintlify/mdx': 3.0.0(@radix-ui/react-popover@1.1.15(@types/react@19.1.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.1.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.2)
|
||||
'@mintlify/models': 0.0.233
|
||||
arktype: 2.1.22
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
lcm: 0.0.3
|
||||
lodash: 4.17.21
|
||||
object-hash: 3.0.0
|
||||
@@ -4791,10 +4784,6 @@ snapshots:
|
||||
|
||||
arg@5.0.2: {}
|
||||
|
||||
argparse@1.0.10:
|
||||
dependencies:
|
||||
sprintf-js: 1.0.3
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
aria-hidden@1.2.6:
|
||||
@@ -5095,7 +5084,7 @@ snapshots:
|
||||
dependencies:
|
||||
env-paths: 2.2.1
|
||||
import-fresh: 3.3.1
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
parse-json: 5.2.0
|
||||
optionalDependencies:
|
||||
typescript: 5.9.2
|
||||
@@ -5703,7 +5692,7 @@ snapshots:
|
||||
|
||||
gray-matter@4.0.3:
|
||||
dependencies:
|
||||
js-yaml: 3.14.1
|
||||
js-yaml: 4.1.1
|
||||
kind-of: 6.0.3
|
||||
section-matter: 1.0.0
|
||||
strip-bom-string: 1.0.0
|
||||
@@ -6185,12 +6174,7 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@3.14.1:
|
||||
dependencies:
|
||||
argparse: 1.0.10
|
||||
esprima: 4.0.1
|
||||
|
||||
js-yaml@4.1.0:
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
@@ -7673,8 +7657,6 @@ snapshots:
|
||||
|
||||
space-separated-tokens@2.0.2: {}
|
||||
|
||||
sprintf-js@1.0.3: {}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
dependencies:
|
||||
escape-string-regexp: 2.0.0
|
||||
|
||||
@@ -86,14 +86,4 @@ icon: apple
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'apple'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured Apple as an OAuth provider in Nhost, you can sign in users using the Apple provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -36,14 +36,4 @@ Find the Redirect URL in your project settings -> Sign In Methods after enabling
|
||||
|
||||
## User Sign-In
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'azuread'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured Azure AD as an OAuth provider in Nhost, you can sign in users using the Azure AD provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -39,13 +39,4 @@ Once saved, Bitbucket will show you a **Key (Client ID)** and a **Secret (Client
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users with Bitbucket:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: "bitbucket",
|
||||
});
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured Bitbucket as an OAuth provider in Nhost, you can sign in users using the Bitbucket provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -34,14 +34,4 @@ icon: discord
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'discord'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured Discord as an OAuth provider in Nhost, you can sign in users using the Discord provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -34,14 +34,4 @@ Find the Redirect URL in your project settings -> Sign In Methods after enabling
|
||||
|
||||
## User Sign-In
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'azuread'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured Entra ID as an OAuth provider in Nhost, you can sign in users using the Entra ID provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -57,14 +57,4 @@ To make sure we can fetch all user data (email, profile picture and name). For t
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'facebook'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured Facebook as an OAuth provider in Nhost, you can sign in users using the Facebook provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -42,14 +42,4 @@ icon: github
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: "github",
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured GitHub as an OAuth provider in Nhost, you can sign in users using the GitHub provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -34,14 +34,4 @@ icon: gitlab
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: "gitlab",
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured GitLab as an OAuth provider in Nhost, you can sign in users using the GitLab provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -66,14 +66,4 @@ icon: google
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the Nhost JavaScript client to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'google'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured Google as an OAuth provider in Nhost, you can sign in users using the Google provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -57,14 +57,4 @@ icon: linkedin
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'linkedin'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured LinkedIn as an OAuth provider in Nhost, you can sign in users using the LinkedIn provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
244
docs/products/auth/providers/sign-in-provider.mdx
Normal file
244
docs/products/auth/providers/sign-in-provider.mdx
Normal file
@@ -0,0 +1,244 @@
|
||||
---
|
||||
title: Sign In with OAuth Providers
|
||||
description: Learn how OAuth provider sign-in works in Nhost and how to implement it in your application.
|
||||
sidebarTitle: Sign In
|
||||
icon: user
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Nhost supports OAuth 2.0 authentication with various social providers including GitHub, Google, Apple, Discord, and more. This guide explains the OAuth sign-in flow and how to implement it in your application.
|
||||
|
||||
## OAuth Sign-In Flow
|
||||
|
||||
The OAuth authentication flow in Nhost involves several steps coordinating between your client application, Nhost Auth service, and the OAuth provider:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Client Application
|
||||
participant NhostAuth as Nhost Auth Service
|
||||
participant Provider as OAuth Provider
|
||||
|
||||
Client->>Client: User clicks "Sign in with Provider"
|
||||
Client->>Client: Call nhost.auth.signInProviderURL(provider, options)
|
||||
Client->>Client: Redirect to returned URL
|
||||
|
||||
Client->>NhostAuth: GET /v1/signin/provider/{provider}
|
||||
Note over NhostAuth: Generate OAuth state & store session
|
||||
NhostAuth->>Provider: 302 Redirect to provider authorization
|
||||
|
||||
Provider->>Provider: User authorizes application
|
||||
Provider->>NhostAuth: 302 Callback with authorization code
|
||||
Note over NhostAuth: Exchange code for tokens<br/>Create/update user<br/>Generate refresh token
|
||||
|
||||
NhostAuth->>Client: 302 Redirect to redirectTo URL
|
||||
Note over Client: URL contains refreshToken<br/>or error information
|
||||
|
||||
Client->>Client: Extract refreshToken from URL
|
||||
Client->>NhostAuth: POST /v1/token with refreshToken
|
||||
NhostAuth->>Client: Return session with accessToken
|
||||
|
||||
Client->>Client: Store session & authenticate user
|
||||
```
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### 1. Generate the Provider Sign-In URL
|
||||
|
||||
Use the `signInProviderURL()` method to generate the OAuth authorization URL. This method returns a URL that you'll redirect the user to:
|
||||
|
||||
```tsx
|
||||
import { nhost } from './lib/nhost';
|
||||
|
||||
const handleSocialSignIn = (provider: 'github' | 'google' | 'apple') => {
|
||||
// Get the current origin to build the callback URL
|
||||
const origin = window.location.origin;
|
||||
const redirectUrl = `${origin}/verify`;
|
||||
|
||||
// Generate the provider sign-in URL
|
||||
const url = nhost.auth.signInProviderURL(provider, {
|
||||
redirectTo: redirectUrl,
|
||||
});
|
||||
|
||||
// Redirect the user to the OAuth provider
|
||||
window.location.href = url;
|
||||
};
|
||||
```
|
||||
|
||||
### 2. OAuth Provider Authorization
|
||||
|
||||
When the user is redirected to the OAuth provider (e.g., GitHub, Google), they will:
|
||||
|
||||
1. See a consent screen asking to authorize your application
|
||||
2. Grant or deny permission to access their profile information
|
||||
3. Be redirected back to Nhost Auth's callback URL
|
||||
|
||||
### 3. Nhost Auth Callback Processing
|
||||
|
||||
Nhost Auth receives the callback from the OAuth provider at `/v1/signin/provider/{provider}/callback` and performs the following:
|
||||
|
||||
1. **Validates the OAuth state** to prevent CSRF attacks
|
||||
2. **Exchanges the authorization code** for access and refresh tokens from the provider
|
||||
3. **Fetches the user's profile** from the provider
|
||||
4. **Creates or updates the user** in your Nhost database
|
||||
5. **Generates a Nhost refresh token** for the session
|
||||
6. **Redirects to your client application** at the `redirectTo` URL
|
||||
|
||||
### 4. Handle the Redirect
|
||||
|
||||
After successful authentication, Nhost redirects back to your `redirectTo` URL with query parameters. You need to handle two scenarios:
|
||||
|
||||
#### Success - Extract the Refresh Token
|
||||
|
||||
On success, the URL will contain a `refreshToken` parameter:
|
||||
|
||||
```
|
||||
https://your-app.com/verify?refreshToken=abc123...
|
||||
```
|
||||
|
||||
Extract this token and exchange it for a session:
|
||||
|
||||
```tsx
|
||||
import type { ErrorResponse } from '@nhost/nhost-js/auth';
|
||||
import type { FetchError } from '@nhost/nhost-js/fetch';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { nhost } from './lib/nhost';
|
||||
|
||||
export default function Verify() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const [status, setStatus] = useState<'verifying' | 'success' | 'error'>('verifying');
|
||||
const [error, setError] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const refreshToken = params.get('refreshToken');
|
||||
|
||||
if (!refreshToken) {
|
||||
setStatus('error');
|
||||
setError('No refresh token found in URL');
|
||||
return;
|
||||
}
|
||||
|
||||
let isMounted = true;
|
||||
|
||||
async function processToken() {
|
||||
try {
|
||||
// Exchange refresh token for session
|
||||
await nhost.auth.refreshToken({ refreshToken });
|
||||
|
||||
if (!isMounted) return;
|
||||
|
||||
setStatus('success');
|
||||
|
||||
// Redirect to the application
|
||||
setTimeout(() => {
|
||||
if (isMounted) navigate('/profile');
|
||||
}, 1500);
|
||||
} catch (err) {
|
||||
const error = err as FetchError<ErrorResponse>;
|
||||
if (!isMounted) return;
|
||||
|
||||
setStatus('error');
|
||||
setError(`An error occurred during verification: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
processToken();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, [location.search, navigate]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{status === 'verifying' && <p>Verifying...</p>}
|
||||
{status === 'success' && <p>Successfully verified! Redirecting...</p>}
|
||||
{status === 'error' && (
|
||||
<div>
|
||||
<p>Verification failed: {error}</p>
|
||||
<button onClick={() => navigate('/signin')}>Back to Sign In</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Error - Handle Authentication Failure
|
||||
|
||||
On error, the URL will contain error parameters:
|
||||
|
||||
```
|
||||
https://your-app.com/verify?error=access_denied
|
||||
```
|
||||
|
||||
You can handle these errors by checking for the `error` query parameter:
|
||||
|
||||
```tsx
|
||||
const params = new URLSearchParams(location.search);
|
||||
const error = params.get('error');
|
||||
|
||||
if (error) {
|
||||
// Handle error - redirect to sign-in page with error message
|
||||
navigate(`/signin?error=${encodeURIComponent(error)}`);
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
Common error scenarios include:
|
||||
- User denied authorization at the OAuth provider
|
||||
- Invalid OAuth request configuration
|
||||
- Error from the OAuth provider
|
||||
- Provider account already linked to another user
|
||||
|
||||
### 5. Session Management
|
||||
|
||||
Once you've exchanged the refresh token for a session, the Nhost SDK automatically manages:
|
||||
|
||||
- **Access token** - Short-lived JWT for API requests (default: 15 minutes)
|
||||
- **Refresh token** - Used to obtain new access tokens (default: 30 days)
|
||||
- **Automatic token refresh** - The SDK refreshes tokens before expiration
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### CSRF Protection
|
||||
|
||||
Nhost automatically handles CSRF protection using the OAuth `state` parameter. Each sign-in request generates a unique state value that is validated during the callback.
|
||||
|
||||
### Redirect URL Validation
|
||||
|
||||
For security, Nhost validates that the `redirectTo` URL matches either your clientUrl or one of your configured allowed redirect URLs. Configure these in your Nhost project settings.
|
||||
|
||||
### Custom Domains
|
||||
|
||||
To use your own domain for the OAuth callback URL instead of the default Nhost domain, refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
|
||||
## Provider-Specific Setup
|
||||
|
||||
Each OAuth provider requires specific configuration. Refer to the provider-specific guides for detailed setup instructions:
|
||||
|
||||
- [Apple](/products/auth/providers/sign-in-apple)
|
||||
- [Azure AD / Entra ID](/products/auth/providers/sign-in-azuread)
|
||||
- [Bitbucket](/products/auth/providers/sign-in-bitbucket)
|
||||
- [Discord](/products/auth/providers/sign-in-discord)
|
||||
- [Facebook](/products/auth/providers/sign-in-facebook)
|
||||
- [GitHub](/products/auth/providers/sign-in-github)
|
||||
- [GitLab](/products/auth/providers/sign-in-gitlab)
|
||||
- [Google](/products/auth/providers/sign-in-google)
|
||||
- [LinkedIn](/products/auth/providers/sign-in-linkedin)
|
||||
- [Spotify](/products/auth/providers/sign-in-spotify)
|
||||
- [Strava](/products/auth/providers/sign-in-strava)
|
||||
- [Twitch](/products/auth/providers/sign-in-twitch)
|
||||
- [Windows Live](/products/auth/providers/sign-in-windowslive)
|
||||
- [WorkOS](/products/auth/providers/sign-in-workos)
|
||||
|
||||
## API Reference
|
||||
|
||||
For detailed API documentation, see:
|
||||
|
||||
- [signInProviderURL()](/reference/javascript/nhost-js/auth#signinproviderurl) in the JavaScript SDK reference
|
||||
- [GET /v1/signin/provider/{ '{provider}' }](/reference/auth/get-signin-provider-{provider}) in the API reference
|
||||
- [GET /v1/signin/provider/{ '{provider}' }/callback](/reference/auth/get-signin-provider-{provider}-callback) in the API reference
|
||||
@@ -42,14 +42,4 @@ icon: spotify
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'spotify'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured Spotify as an OAuth provider in Nhost, you can sign in users using the Spotify provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -33,14 +33,4 @@ Due to Strava API updates, email is no longer returned and is intentionally left
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: "strava",
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured Strava as an OAuth provider in Nhost, you can sign in users using the Strava provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -36,14 +36,4 @@ icon: twitch
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'twitch'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured Twitch as an OAuth provider in Nhost, you can sign in users using the Twitch provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -32,14 +32,4 @@ icon: windowslive
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: "windowslive",
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured WindowsLive as an OAuth provider in Nhost, you can sign in users using the WindowsLive provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -56,14 +56,4 @@ See the [WorkOS documentation](https://workos.com/docs/) to learn more about how
|
||||
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'workos'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
To use your own domain for the callback URL refer to the [custom domains](/platform/cloud/custom-domains) documentation.
|
||||
</Note>
|
||||
Once you've configured WorkOS as an OAuth provider in Nhost, you can sign in users using the WorkOS provider. See the [OAuth Provider Sign-In Guide](/products/auth/providers/sign-in-provider) for detailed implementation instructions including the complete OAuth flow, error handling, and session management.
|
||||
|
||||
@@ -254,7 +254,7 @@ Start local development environment
|
||||
|
||||
**--ca-certificates**="": Mounts and everrides path to CA certificates in the containers
|
||||
|
||||
**--dashboard-version**="": Dashboard version to use (default: nhost/dashboard:2.41.0)
|
||||
**--dashboard-version**="": Dashboard version to use (default: nhost/dashboard:2.42.0)
|
||||
|
||||
**--disable-tls**: Disable TLS
|
||||
|
||||
@@ -284,7 +284,7 @@ Start local development environment connected to an Nhost Cloud project (BETA)
|
||||
|
||||
**--ca-certificates**="": Mounts and everrides path to CA certificates in the containers
|
||||
|
||||
**--dashboard-version**="": Dashboard version to use (default: nhost/dashboard:2.41.0)
|
||||
**--dashboard-version**="": Dashboard version to use (default: nhost/dashboard:2.42.0)
|
||||
|
||||
**--disable-tls**: Disable TLS
|
||||
|
||||
|
||||
@@ -792,7 +792,7 @@ Maximum height to resize image to while maintaining aspect ratio. Only applies t
|
||||
optional q: number;
|
||||
```
|
||||
|
||||
Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
|
||||
#### w?
|
||||
|
||||
@@ -842,7 +842,7 @@ Maximum height to resize image to while maintaining aspect ratio. Only applies t
|
||||
optional q: number;
|
||||
```
|
||||
|
||||
Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
|
||||
#### w?
|
||||
|
||||
@@ -1068,7 +1068,7 @@ buildVersion: string
|
||||
## OutputImageFormat
|
||||
|
||||
```ts
|
||||
type OutputImageFormat = 'auto' | 'same' | 'jpeg' | 'webp' | 'png' | 'avif' | 'heic'
|
||||
type OutputImageFormat = 'auto' | 'same' | 'jpeg' | 'webp' | 'png' | 'avif'
|
||||
```
|
||||
|
||||
Output format for image files. Use 'auto' for content negotiation based on Accept header
|
||||
|
||||
@@ -148,7 +148,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
- name: q
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files"
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP and PNG files"
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
@@ -332,7 +332,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
- name: q
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files"
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP and PNG files"
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
@@ -614,7 +614,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
- name: q
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files"
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP and PNG files"
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
@@ -1178,5 +1178,4 @@ components:
|
||||
- webp
|
||||
- png
|
||||
- avif
|
||||
- heic
|
||||
example: same
|
||||
|
||||
@@ -54,5 +54,10 @@
|
||||
"@types/react": "~19.0.14",
|
||||
"@types/node": "^22.15.17"
|
||||
},
|
||||
"private": true
|
||||
"private": true,
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"js-yaml@<=4.1.0": ">=4.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
examples/demos/ReactNativeDemo/pnpm-lock.yaml
generated
43
examples/demos/ReactNativeDemo/pnpm-lock.yaml
generated
@@ -4,6 +4,9 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
js-yaml@<=4.1.0: '>=4.1.1'
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -1064,9 +1067,6 @@ packages:
|
||||
arg@5.0.2:
|
||||
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
||||
|
||||
argparse@1.0.10:
|
||||
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
@@ -1443,11 +1443,6 @@ packages:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
esprima@4.0.1:
|
||||
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
|
||||
etag@1.8.1:
|
||||
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -1877,12 +1872,8 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@3.14.1:
|
||||
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
|
||||
hasBin: true
|
||||
|
||||
js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsc-safe-url@0.2.4:
|
||||
@@ -2623,9 +2614,6 @@ packages:
|
||||
resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
sprintf-js@1.0.3:
|
||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -3825,7 +3813,7 @@ snapshots:
|
||||
'@babel/code-frame': 7.10.4
|
||||
chalk: 4.1.2
|
||||
find-up: 5.0.0
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
dependencies:
|
||||
@@ -3847,7 +3835,7 @@ snapshots:
|
||||
camelcase: 5.3.1
|
||||
find-up: 4.1.0
|
||||
get-package-type: 0.1.0
|
||||
js-yaml: 3.14.1
|
||||
js-yaml: 4.1.1
|
||||
resolve-from: 5.0.0
|
||||
|
||||
'@istanbuljs/schema@0.1.3': {}
|
||||
@@ -4296,10 +4284,6 @@ snapshots:
|
||||
|
||||
arg@5.0.2: {}
|
||||
|
||||
argparse@1.0.10:
|
||||
dependencies:
|
||||
sprintf-js: 1.0.3
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
asap@2.0.6: {}
|
||||
@@ -4615,7 +4599,7 @@ snapshots:
|
||||
dependencies:
|
||||
import-fresh: 2.0.0
|
||||
is-directory: 0.3.1
|
||||
js-yaml: 3.14.1
|
||||
js-yaml: 4.1.1
|
||||
parse-json: 4.0.0
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
@@ -4698,8 +4682,6 @@ snapshots:
|
||||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
||||
esprima@4.0.1: {}
|
||||
|
||||
etag@1.8.1: {}
|
||||
|
||||
event-target-shim@5.0.1: {}
|
||||
@@ -5178,12 +5160,7 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@3.14.1:
|
||||
dependencies:
|
||||
argparse: 1.0.10
|
||||
esprima: 4.0.1
|
||||
|
||||
js-yaml@4.1.0:
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
@@ -6011,8 +5988,6 @@ snapshots:
|
||||
|
||||
split-on-first@1.1.0: {}
|
||||
|
||||
sprintf-js@1.0.3: {}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
dependencies:
|
||||
escape-string-regexp: 2.0.0
|
||||
|
||||
@@ -32,5 +32,10 @@
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@vitejs/plugin-react": "^4.4.1"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"js-yaml@<=4.1.0": ">=4.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
examples/guides/react-apollo/pnpm-lock.yaml
generated
13
examples/guides/react-apollo/pnpm-lock.yaml
generated
@@ -4,6 +4,9 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
js-yaml@<=4.1.0: '>=4.1.1'
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -1551,8 +1554,8 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsesc@3.1.0:
|
||||
@@ -3103,7 +3106,7 @@ snapshots:
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
jose: 5.10.0
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
lodash: 4.17.21
|
||||
scuid: 1.1.0
|
||||
tslib: 2.8.1
|
||||
@@ -3613,7 +3616,7 @@ snapshots:
|
||||
cosmiconfig@8.3.6:
|
||||
dependencies:
|
||||
import-fresh: 3.3.1
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
parse-json: 5.2.0
|
||||
path-type: 4.0.0
|
||||
|
||||
@@ -3965,7 +3968,7 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
|
||||
@@ -33,5 +33,10 @@
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@vitejs/plugin-react": "^4.4.1"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"js-yaml@<=4.1.0": ">=4.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
examples/guides/react-query/pnpm-lock.yaml
generated
13
examples/guides/react-query/pnpm-lock.yaml
generated
@@ -4,6 +4,9 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
js-yaml@<=4.1.0: '>=4.1.1'
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -1534,8 +1537,8 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsesc@3.1.0:
|
||||
@@ -3029,7 +3032,7 @@ snapshots:
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
jose: 5.10.0
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
lodash: 4.17.21
|
||||
scuid: 1.1.0
|
||||
tslib: 2.8.1
|
||||
@@ -3538,7 +3541,7 @@ snapshots:
|
||||
cosmiconfig@8.3.6:
|
||||
dependencies:
|
||||
import-fresh: 3.3.1
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
parse-json: 5.2.0
|
||||
path-type: 4.0.0
|
||||
|
||||
@@ -3886,7 +3889,7 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
|
||||
@@ -33,5 +33,10 @@
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@vitejs/plugin-react": "^4.4.1"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"js-yaml@<=4.1.0": ">=4.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
examples/guides/react-urql/pnpm-lock.yaml
generated
13
examples/guides/react-urql/pnpm-lock.yaml
generated
@@ -4,6 +4,9 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
js-yaml@<=4.1.0: '>=4.1.1'
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -1294,8 +1297,8 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsesc@3.1.0:
|
||||
@@ -2500,7 +2503,7 @@ snapshots:
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
jose: 5.10.0
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
lodash: 4.17.21
|
||||
scuid: 1.1.0
|
||||
tslib: 2.8.1
|
||||
@@ -2940,7 +2943,7 @@ snapshots:
|
||||
cosmiconfig@8.3.6:
|
||||
dependencies:
|
||||
import-fresh: 3.3.1
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
parse-json: 5.2.0
|
||||
path-type: 4.0.0
|
||||
|
||||
@@ -3267,7 +3270,7 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
|
||||
@@ -3,8 +3,14 @@ table:
|
||||
schema: auth
|
||||
is_enum: true
|
||||
configuration:
|
||||
column_config: {}
|
||||
custom_column_names: {}
|
||||
column_config:
|
||||
comment:
|
||||
custom_name: comment
|
||||
value:
|
||||
custom_name: value
|
||||
custom_column_names:
|
||||
comment: comment
|
||||
value: value
|
||||
custom_name: authRefreshTokenTypes
|
||||
custom_root_fields:
|
||||
delete: deleteAuthRefreshTokenTypes
|
||||
|
||||
@@ -31,7 +31,7 @@ httpPoolSize = 100
|
||||
version = 22
|
||||
|
||||
[auth]
|
||||
version = '0.41.1'
|
||||
version = '0.43.1'
|
||||
|
||||
[auth.elevatedPrivileges]
|
||||
mode = 'disabled'
|
||||
@@ -183,7 +183,7 @@ capacity = 1
|
||||
[provider]
|
||||
|
||||
[storage]
|
||||
version = '0.8.0-beta5'
|
||||
version = '0.9.1'
|
||||
|
||||
[observability]
|
||||
[observability.grafana]
|
||||
|
||||
@@ -154,6 +154,11 @@ export default function Files() {
|
||||
const response = await nhost.storage.uploadFiles({
|
||||
"bucket-id": "personal",
|
||||
"file[]": [file as File],
|
||||
"metadata[]": [
|
||||
{
|
||||
metadata: { key1: "value1" },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Get the processed file data
|
||||
|
||||
@@ -467,7 +467,12 @@ export default function Todos() {
|
||||
)}
|
||||
|
||||
{showAddForm && (
|
||||
<View style={[commonStyles.card, { marginHorizontal: 16, width: undefined }]}>
|
||||
<View
|
||||
style={[
|
||||
commonStyles.card,
|
||||
{ marginHorizontal: 16, width: undefined },
|
||||
]}
|
||||
>
|
||||
<Text style={commonStyles.cardTitle}>Add New Todo</Text>
|
||||
<View style={commonStyles.formFields}>
|
||||
<View style={commonStyles.fieldGroup}>
|
||||
|
||||
10386
examples/tutorials/nhost-reactnative-tutorial/package-lock.json
generated
10386
examples/tutorials/nhost-reactnative-tutorial/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -17,9 +17,11 @@
|
||||
"expo-crypto": "14",
|
||||
"expo-document-picker": "13",
|
||||
"expo-file-system": "18",
|
||||
"expo-linking": "^8.0.8",
|
||||
"expo-router": "~6",
|
||||
"expo-sharing": "13",
|
||||
"expo-status-bar": "~3.0.8",
|
||||
"metro-minify-terser": "^0.83.3",
|
||||
"react": "19.1.0",
|
||||
"react-native": "0.81.4"
|
||||
},
|
||||
@@ -27,5 +29,10 @@
|
||||
"@types/react": "~19.1.0",
|
||||
"typescript": "~5.9.2"
|
||||
},
|
||||
"private": true
|
||||
"private": true,
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"js-yaml@<=4.1.0": ">=4.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
js-yaml@<=4.1.0: '>=4.1.1'
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -32,6 +35,9 @@ importers:
|
||||
expo-file-system:
|
||||
specifier: '18'
|
||||
version: 18.1.11(expo@54.0.9)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
|
||||
expo-linking:
|
||||
specifier: ^8.0.8
|
||||
version: 8.0.8(expo@54.0.9)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
|
||||
expo-router:
|
||||
specifier: ~6
|
||||
version: 6.0.7(@expo/metro-runtime@6.1.2)(@types/react@19.1.13)(expo-constants@18.0.9)(expo-linking@8.0.8)(expo@54.0.9)(react-dom@19.1.1(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
|
||||
@@ -41,6 +47,9 @@ importers:
|
||||
expo-status-bar:
|
||||
specifier: ~3.0.8
|
||||
version: 3.0.8(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
|
||||
metro-minify-terser:
|
||||
specifier: ^0.83.3
|
||||
version: 0.83.3
|
||||
react:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
@@ -1221,9 +1230,6 @@ packages:
|
||||
arg@5.0.2:
|
||||
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
||||
|
||||
argparse@1.0.10:
|
||||
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
@@ -1617,11 +1623,6 @@ packages:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
esprima@4.0.1:
|
||||
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
|
||||
etag@1.8.1:
|
||||
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -2021,12 +2022,8 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@3.14.1:
|
||||
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
|
||||
hasBin: true
|
||||
|
||||
js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsc-safe-url@0.2.4:
|
||||
@@ -2232,6 +2229,10 @@ packages:
|
||||
resolution: {integrity: sha512-zvIxnh7U0JQ7vT4quasKsijId3dOAWgq+ip2jF/8TMrPUqQabGrs04L2dd0haQJ+PA+d4VvK/bPOY8X/vL2PWw==}
|
||||
engines: {node: '>=20.19.4'}
|
||||
|
||||
metro-minify-terser@0.83.3:
|
||||
resolution: {integrity: sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ==}
|
||||
engines: {node: '>=20.19.4'}
|
||||
|
||||
metro-resolver@0.83.1:
|
||||
resolution: {integrity: sha512-t8j46kiILAqqFS5RNa+xpQyVjULxRxlvMidqUswPEk5nQVNdlJslqizDm/Et3v/JKwOtQGkYAQCHxP1zGStR/g==}
|
||||
engines: {node: '>=20.19.4'}
|
||||
@@ -2827,9 +2828,6 @@ packages:
|
||||
resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
sprintf-js@1.0.3:
|
||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -4121,7 +4119,7 @@ snapshots:
|
||||
'@babel/code-frame': 7.10.4
|
||||
chalk: 4.1.2
|
||||
find-up: 5.0.0
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
dependencies:
|
||||
@@ -4143,7 +4141,7 @@ snapshots:
|
||||
camelcase: 5.3.1
|
||||
find-up: 4.1.0
|
||||
get-package-type: 0.1.0
|
||||
js-yaml: 3.14.1
|
||||
js-yaml: 4.1.1
|
||||
resolve-from: 5.0.0
|
||||
|
||||
'@istanbuljs/schema@0.1.3': {}
|
||||
@@ -4726,10 +4724,6 @@ snapshots:
|
||||
|
||||
arg@5.0.2: {}
|
||||
|
||||
argparse@1.0.10:
|
||||
dependencies:
|
||||
sprintf-js: 1.0.3
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
aria-hidden@1.2.6:
|
||||
@@ -5061,7 +5055,7 @@ snapshots:
|
||||
dependencies:
|
||||
import-fresh: 2.0.0
|
||||
is-directory: 0.3.1
|
||||
js-yaml: 3.14.1
|
||||
js-yaml: 4.1.1
|
||||
parse-json: 4.0.0
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
@@ -5146,8 +5140,6 @@ snapshots:
|
||||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
||||
esprima@4.0.1: {}
|
||||
|
||||
etag@1.8.1: {}
|
||||
|
||||
event-target-shim@5.0.1: {}
|
||||
@@ -5597,12 +5589,7 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@3.14.1:
|
||||
dependencies:
|
||||
argparse: 1.0.10
|
||||
esprima: 4.0.1
|
||||
|
||||
js-yaml@4.1.0:
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
@@ -5840,6 +5827,11 @@ snapshots:
|
||||
flow-enums-runtime: 0.0.6
|
||||
terser: 5.44.0
|
||||
|
||||
metro-minify-terser@0.83.3:
|
||||
dependencies:
|
||||
flow-enums-runtime: 0.0.6
|
||||
terser: 5.44.0
|
||||
|
||||
metro-resolver@0.83.1:
|
||||
dependencies:
|
||||
flow-enums-runtime: 0.0.6
|
||||
@@ -6588,8 +6580,6 @@ snapshots:
|
||||
|
||||
split-on-first@1.1.0: {}
|
||||
|
||||
sprintf-js@1.0.3: {}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
dependencies:
|
||||
escape-string-regexp: 2.0.0
|
||||
|
||||
@@ -27,5 +27,10 @@
|
||||
"svelte-check": "^4.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^7.0.8"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"cookie@<0.7.0": ">=0.7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
cookie@<0.7.0: '>=0.7.0'
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -384,9 +387,9 @@ packages:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cookie@0.6.0:
|
||||
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
cookie@1.0.2:
|
||||
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
debug@4.4.3:
|
||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||
@@ -750,7 +753,7 @@ snapshots:
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.11)
|
||||
'@types/cookie': 0.6.0
|
||||
acorn: 8.15.0
|
||||
cookie: 0.6.0
|
||||
cookie: 1.0.2
|
||||
devalue: 5.3.2
|
||||
esm-env: 1.2.2
|
||||
kleur: 4.1.5
|
||||
@@ -799,7 +802,7 @@ snapshots:
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
cookie@0.6.0: {}
|
||||
cookie@1.0.2: {}
|
||||
|
||||
debug@4.4.3:
|
||||
dependencies:
|
||||
|
||||
@@ -125,7 +125,8 @@
|
||||
"form-data@<2.5.4": ">=2.5.4",
|
||||
"tmp@<=0.2.3": ">=0.2.4",
|
||||
"devalue@<5.3.2": ">=5.3.2",
|
||||
"axios@<1.12.0": ">=1.12.0"
|
||||
"axios@<1.12.0": ">=1.12.0",
|
||||
"js-yaml@<=4.1.0": ">=4.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,8 @@
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"rollup@<2.79.2": ">=2.79.2"
|
||||
"rollup@<2.79.2": ">=2.79.2",
|
||||
"js-yaml@<=4.1.0": ">=4.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
32
packages/nhost-js/pnpm-lock.yaml
generated
32
packages/nhost-js/pnpm-lock.yaml
generated
@@ -6,6 +6,7 @@ settings:
|
||||
|
||||
overrides:
|
||||
rollup@<2.79.2: '>=2.79.2'
|
||||
js-yaml@<=4.1.0: '>=4.1.1'
|
||||
|
||||
importers:
|
||||
|
||||
@@ -531,8 +532,8 @@ packages:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
argparse@1.0.10:
|
||||
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
async@3.2.6:
|
||||
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
|
||||
@@ -711,11 +712,6 @@ packages:
|
||||
resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
esprima@4.0.1:
|
||||
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
@@ -1013,8 +1009,8 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@3.14.1:
|
||||
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsesc@3.1.0:
|
||||
@@ -1250,9 +1246,6 @@ packages:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
sprintf-js@1.0.3:
|
||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -1614,7 +1607,7 @@ snapshots:
|
||||
camelcase: 5.3.1
|
||||
find-up: 4.1.0
|
||||
get-package-type: 0.1.0
|
||||
js-yaml: 3.14.1
|
||||
js-yaml: 4.1.1
|
||||
resolve-from: 5.0.0
|
||||
|
||||
'@istanbuljs/schema@0.1.3': {}
|
||||
@@ -1973,9 +1966,7 @@ snapshots:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
argparse@1.0.10:
|
||||
dependencies:
|
||||
sprintf-js: 1.0.3
|
||||
argparse@2.0.1: {}
|
||||
|
||||
async@3.2.6: {}
|
||||
|
||||
@@ -2158,8 +2149,6 @@ snapshots:
|
||||
|
||||
escape-string-regexp@2.0.0: {}
|
||||
|
||||
esprima@4.0.1: {}
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
execa@5.1.1:
|
||||
@@ -2635,10 +2624,9 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@3.14.1:
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 1.0.10
|
||||
esprima: 4.0.1
|
||||
argparse: 2.0.1
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
@@ -2849,8 +2837,6 @@ snapshots:
|
||||
|
||||
source-map@0.6.1: {}
|
||||
|
||||
sprintf-js@1.0.3: {}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
dependencies:
|
||||
escape-string-regexp: 2.0.0
|
||||
|
||||
@@ -275,8 +275,7 @@ export type OutputImageFormat =
|
||||
| "jpeg"
|
||||
| "webp"
|
||||
| "png"
|
||||
| "avif"
|
||||
| "heic";
|
||||
| "avif";
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -379,7 +378,7 @@ export interface ListOrphanedFilesResponse200 {
|
||||
|
||||
/**
|
||||
* Parameters for the getFile method.
|
||||
@property q? (number) - Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
@property q? (number) - Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
|
||||
@property h? (number) - Maximum height to resize image to while maintaining aspect ratio. Only applies to image files
|
||||
|
||||
@@ -392,7 +391,7 @@ export interface ListOrphanedFilesResponse200 {
|
||||
* Output format for image files. Use 'auto' for content negotiation based on Accept header*/
|
||||
export interface GetFileParams {
|
||||
/**
|
||||
* Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
* Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
|
||||
*/
|
||||
q?: number;
|
||||
@@ -420,7 +419,7 @@ export interface GetFileParams {
|
||||
}
|
||||
/**
|
||||
* Parameters for the getFileMetadataHeaders method.
|
||||
@property q? (number) - Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
@property q? (number) - Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
|
||||
@property h? (number) - Maximum height to resize image to while maintaining aspect ratio. Only applies to image files
|
||||
|
||||
@@ -433,7 +432,7 @@ export interface GetFileParams {
|
||||
* Output format for image files. Use 'auto' for content negotiation based on Accept header*/
|
||||
export interface GetFileMetadataHeadersParams {
|
||||
/**
|
||||
* Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
* Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
|
||||
*/
|
||||
q?: number;
|
||||
@@ -633,16 +632,27 @@ export const createAPIClient = (
|
||||
): Promise<FetchResponse<UploadFilesResponse201>> => {
|
||||
const url = `${baseURL}/files`;
|
||||
const formData = new FormData();
|
||||
const isReactNative =
|
||||
typeof navigator !== "undefined" &&
|
||||
(navigator as { product?: string }).product === "ReactNative";
|
||||
if (body["bucket-id"] !== undefined) {
|
||||
formData.append("bucket-id", body["bucket-id"]);
|
||||
}
|
||||
if (body["metadata[]"] !== undefined) {
|
||||
body["metadata[]"].forEach((value) => {
|
||||
formData.append(
|
||||
"metadata[]",
|
||||
new Blob([JSON.stringify(value)], { type: "application/json" }),
|
||||
"",
|
||||
);
|
||||
if (isReactNative) {
|
||||
formData.append("metadata[]", {
|
||||
string: JSON.stringify(value),
|
||||
type: "application/json",
|
||||
name: "",
|
||||
} as unknown as Blob);
|
||||
} else {
|
||||
formData.append(
|
||||
"metadata[]",
|
||||
new Blob([JSON.stringify(value)], { type: "application/json" }),
|
||||
"",
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (body["file[]"] !== undefined) {
|
||||
@@ -800,14 +810,25 @@ export const createAPIClient = (
|
||||
): Promise<FetchResponse<FileMetadata>> => {
|
||||
const url = `${baseURL}/files/${id}`;
|
||||
const formData = new FormData();
|
||||
const isReactNative =
|
||||
typeof navigator !== "undefined" &&
|
||||
(navigator as { product?: string }).product === "ReactNative";
|
||||
if (body["metadata"] !== undefined) {
|
||||
formData.append(
|
||||
"metadata",
|
||||
new Blob([JSON.stringify(body["metadata"])], {
|
||||
if (isReactNative) {
|
||||
formData.append("metadata", {
|
||||
string: JSON.stringify(body["metadata"]),
|
||||
type: "application/json",
|
||||
}),
|
||||
"",
|
||||
);
|
||||
name: "",
|
||||
} as unknown as Blob);
|
||||
} else {
|
||||
formData.append(
|
||||
"metadata",
|
||||
new Blob([JSON.stringify(body["metadata"])], {
|
||||
type: "application/json",
|
||||
}),
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
if (body["file"] !== undefined) {
|
||||
formData.append("file", body["file"]);
|
||||
|
||||
1
pnpm-lock.yaml
generated
1
pnpm-lock.yaml
generated
@@ -90,6 +90,7 @@ overrides:
|
||||
tmp@<=0.2.3: '>=0.2.4'
|
||||
devalue@<5.3.2: '>=5.3.2'
|
||||
axios@<1.12.0: '>=1.12.0'
|
||||
js-yaml@<=4.1.0: '>=4.1.1'
|
||||
|
||||
importers:
|
||||
|
||||
|
||||
@@ -2066,84 +2066,83 @@ func (sh *strictHandler) GetVersion(ctx *gin.Context) {
|
||||
// Base64 encoded, gzipped, json marshaled Swagger object
|
||||
var swaggerSpec = []string{
|
||||
|
||||
"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",
|
||||
"AA==",
|
||||
"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",
|
||||
}
|
||||
|
||||
// GetSwagger returns the content of the embedded swagger specification file
|
||||
|
||||
@@ -18,7 +18,6 @@ const (
|
||||
const (
|
||||
Auto OutputImageFormat = "auto"
|
||||
Avif OutputImageFormat = "avif"
|
||||
Heic OutputImageFormat = "heic"
|
||||
Jpeg OutputImageFormat = "jpeg"
|
||||
Png OutputImageFormat = "png"
|
||||
Same OutputImageFormat = "same"
|
||||
@@ -159,7 +158,7 @@ type UploadFilesMultipartBody struct {
|
||||
|
||||
// GetFileParams defines parameters for GetFile.
|
||||
type GetFileParams struct {
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
Q *int `form:"q,omitempty" json:"q,omitempty"`
|
||||
|
||||
// H Maximum height to resize image to while maintaining aspect ratio. Only applies to image files
|
||||
@@ -192,7 +191,7 @@ type GetFileParams struct {
|
||||
|
||||
// GetFileMetadataHeadersParams defines parameters for GetFileMetadataHeaders.
|
||||
type GetFileMetadataHeadersParams struct {
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
Q *int `form:"q,omitempty" json:"q,omitempty"`
|
||||
|
||||
// H Maximum height to resize image to while maintaining aspect ratio. Only applies to image files
|
||||
@@ -258,7 +257,7 @@ type GetFileWithPresignedURLParams struct {
|
||||
// XId Use presignedurl endpoint to generate this automatically
|
||||
XId string `form:"x-id" json:"x-id"`
|
||||
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
Q *int `form:"q,omitempty" json:"q,omitempty"`
|
||||
|
||||
// H Maximum height to resize image to while maintaining aspect ratio. Only applies to image files
|
||||
|
||||
@@ -28,7 +28,6 @@ const (
|
||||
const (
|
||||
Auto OutputImageFormat = "auto"
|
||||
Avif OutputImageFormat = "avif"
|
||||
Heic OutputImageFormat = "heic"
|
||||
Jpeg OutputImageFormat = "jpeg"
|
||||
Png OutputImageFormat = "png"
|
||||
Same OutputImageFormat = "same"
|
||||
@@ -169,7 +168,7 @@ type UploadFilesMultipartBody struct {
|
||||
|
||||
// GetFileParams defines parameters for GetFile.
|
||||
type GetFileParams struct {
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
Q *int `form:"q,omitempty" json:"q,omitempty"`
|
||||
|
||||
// H Maximum height to resize image to while maintaining aspect ratio. Only applies to image files
|
||||
@@ -202,7 +201,7 @@ type GetFileParams struct {
|
||||
|
||||
// GetFileMetadataHeadersParams defines parameters for GetFileMetadataHeaders.
|
||||
type GetFileMetadataHeadersParams struct {
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
Q *int `form:"q,omitempty" json:"q,omitempty"`
|
||||
|
||||
// H Maximum height to resize image to while maintaining aspect ratio. Only applies to image files
|
||||
@@ -268,7 +267,7 @@ type GetFileWithPresignedURLParams struct {
|
||||
// XId Use presignedurl endpoint to generate this automatically
|
||||
XId string `form:"x-id" json:"x-id"`
|
||||
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files
|
||||
// Q Image quality (1-100). Only applies to JPEG, WebP and PNG files
|
||||
Q *int `form:"q,omitempty" json:"q,omitempty"`
|
||||
|
||||
// H Maximum height to resize image to while maintaining aspect ratio. Only applies to image files
|
||||
|
||||
@@ -37,8 +37,6 @@ func mimeTypeToImageType(mimeType string) (image.ImageType, *APIError) {
|
||||
return image.ImageTypeJPEG, nil
|
||||
case "image/avif":
|
||||
return image.ImageTypeAVIF, nil
|
||||
case "image/heic", "image/heif":
|
||||
return image.ImageTypeHEIC, nil
|
||||
default:
|
||||
return 0, BadDataError(
|
||||
fmt.Errorf( //nolint: err113
|
||||
@@ -75,8 +73,6 @@ func chooseImageFormat( //nolint: cyclop
|
||||
return originalFormat, image.ImageTypeJPEG, nil
|
||||
case api.Avif:
|
||||
return originalFormat, image.ImageTypeAVIF, nil
|
||||
case api.Heic:
|
||||
return originalFormat, image.ImageTypeHEIC, nil
|
||||
case api.Auto:
|
||||
for _, acceptHeader := range acceptHeader {
|
||||
acceptedTypes := strings.Split(acceptHeader, ",")
|
||||
@@ -89,8 +85,6 @@ func chooseImageFormat( //nolint: cyclop
|
||||
return originalFormat, image.ImageTypeJPEG, nil
|
||||
case slices.Contains(acceptedTypes, "image/png"):
|
||||
return originalFormat, image.ImageTypePNG, nil
|
||||
case slices.Contains(acceptedTypes, "image/heic"):
|
||||
return originalFormat, image.ImageTypeHEIC, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,11 +92,8 @@ func chooseImageFormat( //nolint: cyclop
|
||||
default:
|
||||
return 0, 0, BadDataError(
|
||||
//nolint: err113
|
||||
fmt.Errorf(
|
||||
"format must be one of: same, webp, png, jpeg, avif, heic, auto. Got: %s",
|
||||
format,
|
||||
),
|
||||
"format must be one of: same, webp, png, jpeg, avif, heic, auto. Got: "+string(format),
|
||||
fmt.Errorf("format must be one of: same, webp, png, jpeg, avif, auto. Got: %s", format),
|
||||
"format must be one of: same, webp, png, jpeg, avif, auto. Got: "+string(format),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
- name: q
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files"
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP and PNG files"
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
@@ -332,7 +332,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
- name: q
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files"
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP and PNG files"
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
@@ -614,7 +614,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
- name: q
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP, PNG and HEIC files"
|
||||
description: "Image quality (1-100). Only applies to JPEG, WebP and PNG files"
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
@@ -1178,5 +1178,4 @@ components:
|
||||
- webp
|
||||
- png
|
||||
- avif
|
||||
- heic
|
||||
example: same
|
||||
|
||||
@@ -24,7 +24,6 @@ const (
|
||||
ImageTypePNG
|
||||
ImageTypeWEBP
|
||||
ImageTypeAVIF
|
||||
ImageTypeHEIC
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
@@ -55,8 +54,6 @@ func (o Options) FormatMimeType() string {
|
||||
return "image/webp"
|
||||
case ImageTypeAVIF:
|
||||
return "image/avif"
|
||||
case ImageTypeHEIC:
|
||||
return "image/heic"
|
||||
}
|
||||
|
||||
return ""
|
||||
@@ -72,8 +69,6 @@ func (o Options) FileExtension() string {
|
||||
return "webp"
|
||||
case ImageTypeAVIF:
|
||||
return "avif"
|
||||
case ImageTypeHEIC:
|
||||
return "heic"
|
||||
}
|
||||
|
||||
return ""
|
||||
@@ -132,10 +127,6 @@ func export(image *vips.ImageRef, opts Options) ([]byte, error) {
|
||||
ep.Quality = opts.Quality
|
||||
ep.Effort = 0
|
||||
b, _, err = image.ExportAvif(ep)
|
||||
case ImageTypeHEIC:
|
||||
ep := vips.NewHeifExportParams()
|
||||
ep.Quality = opts.Quality
|
||||
b, _, err = image.ExportHeif(ep)
|
||||
}
|
||||
|
||||
return b, err //nolint: wrapcheck
|
||||
|
||||
@@ -76,39 +76,6 @@ func TestManipulate(t *testing.T) {
|
||||
size: 17784,
|
||||
options: image.Options{Format: image.ImageTypeAVIF},
|
||||
},
|
||||
{
|
||||
name: "heic",
|
||||
filename: "testdata/nhost.heic",
|
||||
sum: "1a2ab1930eef77710d35254a6fbd3e59f60b929070c44e47d0c6043e05b5ab99",
|
||||
size: 12968,
|
||||
options: image.Options{
|
||||
Width: 300,
|
||||
Height: 100,
|
||||
Blur: 2,
|
||||
Format: image.ImageTypeHEIC,
|
||||
Quality: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jpeg to heic",
|
||||
filename: "testdata/nhost.jpg",
|
||||
sum: "0a86fa5dbfd545656fa97bfcace038440dba154c878b4cd002328d6cf8062249",
|
||||
size: 33399,
|
||||
options: image.Options{
|
||||
Width: 300,
|
||||
Height: 100,
|
||||
Blur: 2,
|
||||
Format: image.ImageTypeHEIC,
|
||||
Quality: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "webp to heic",
|
||||
filename: "testdata/nhost.webp",
|
||||
sum: "34f36705183310f9a88f147aca2905a872981920e93e9ab9714413185b395aa1",
|
||||
size: 17784,
|
||||
options: image.Options{Width: 300, Height: 100, Blur: 2, Format: image.ImageTypeHEIC},
|
||||
},
|
||||
}
|
||||
|
||||
transformer := image.NewTransformer()
|
||||
|
||||
BIN
services/storage/image/testdata/nhost.heic
vendored
BIN
services/storage/image/testdata/nhost.heic
vendored
Binary file not shown.
@@ -767,16 +767,30 @@ export const createAPIClient = (
|
||||
): Promise<FetchResponse<UploadFilesResponse201>> => {
|
||||
const url = `${ baseURL }/files/`;
|
||||
const formData = new FormData();
|
||||
const isReactNative =
|
||||
typeof navigator !== "undefined" &&
|
||||
(navigator as { product?: string }).product === "ReactNative";
|
||||
if (body["bucket-id"] !== undefined) {
|
||||
formData.append("bucket-id", body["bucket-id"]);
|
||||
}
|
||||
if (body["metadata[]"] !== undefined) {
|
||||
body["metadata[]"].forEach((value) => {
|
||||
formData.append(
|
||||
if (isReactNative) {
|
||||
formData.append(
|
||||
"metadata[]",
|
||||
{
|
||||
string: JSON.stringify(value),
|
||||
type: "application/json",
|
||||
name: "",
|
||||
} as unknown as Blob,
|
||||
);
|
||||
} else {
|
||||
formData.append(
|
||||
"metadata[]",
|
||||
new Blob([JSON.stringify(value)], { type: "application/json" }),
|
||||
"",
|
||||
)
|
||||
new Blob([JSON.stringify(value)], { type: "application/json" }),
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -912,12 +926,26 @@ export const createAPIClient = (
|
||||
): Promise<FetchResponse<FileMetadata>> => {
|
||||
const url = `${ baseURL }/files/${id}`;
|
||||
const formData = new FormData();
|
||||
const isReactNative =
|
||||
typeof navigator !== "undefined" &&
|
||||
(navigator as { product?: string }).product === "ReactNative";
|
||||
if (body["metadata"] !== undefined) {
|
||||
formData.append(
|
||||
if (isReactNative) {
|
||||
formData.append(
|
||||
"metadata",
|
||||
{
|
||||
string: JSON.stringify(body["metadata"]),
|
||||
type: "application/json",
|
||||
name: "",
|
||||
} as unknown as Blob,
|
||||
);
|
||||
} else {
|
||||
formData.append(
|
||||
"metadata",
|
||||
new Blob([JSON.stringify(body["metadata"])], { type: "application/json" }),
|
||||
"",
|
||||
);
|
||||
new Blob([JSON.stringify(body["metadata"])], { type: "application/json" }),
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
if (body["file"] !== undefined) {
|
||||
formData.append("file", body["file"]);
|
||||
|
||||
@@ -126,6 +126,9 @@ export const createAPIClient = (
|
||||
});
|
||||
{{- else if .RequestFormData }}
|
||||
const formData = new FormData();
|
||||
const isReactNative =
|
||||
typeof navigator !== "undefined" &&
|
||||
(navigator as { product?: string }).product === "ReactNative";
|
||||
|
||||
{{- range .RequestFormData.Properties }}
|
||||
{{- if eq .Type.Kind "scalar" }}
|
||||
@@ -138,11 +141,22 @@ export const createAPIClient = (
|
||||
{{- if eq .Type.Item.Kind "scalar" }}
|
||||
formData.append("{{ .Name }}", value)
|
||||
{{- else if eq .Type.Item.Kind "object" }}
|
||||
formData.append(
|
||||
if (isReactNative) {
|
||||
formData.append(
|
||||
"{{ .Name }}",
|
||||
{
|
||||
string: JSON.stringify(value),
|
||||
type: "application/json",
|
||||
name: "",
|
||||
} as unknown as Blob,
|
||||
);
|
||||
} else {
|
||||
formData.append(
|
||||
"{{ .Name }}",
|
||||
new Blob([JSON.stringify(value)], { type: "application/json" }),
|
||||
"",
|
||||
)
|
||||
new Blob([JSON.stringify(value)], { type: "application/json" }),
|
||||
"",
|
||||
);
|
||||
}
|
||||
{{- else }}
|
||||
TODO {{ .Type.Kind }} {{ .Type.Schema.Schema.Type }}
|
||||
{{- end }}
|
||||
@@ -151,11 +165,22 @@ export const createAPIClient = (
|
||||
}
|
||||
{{- else if eq .Type.Kind "object" }}
|
||||
if (body["{{ .Name }}"] !== undefined) {
|
||||
formData.append(
|
||||
if (isReactNative) {
|
||||
formData.append(
|
||||
"{{ .Name }}",
|
||||
{
|
||||
string: JSON.stringify(body["{{ .Name }}"]),
|
||||
type: "application/json",
|
||||
name: "",
|
||||
} as unknown as Blob,
|
||||
);
|
||||
} else {
|
||||
formData.append(
|
||||
"{{ .Name }}",
|
||||
new Blob([JSON.stringify(body["{{ .Name }}"])], { type: "application/json" }),
|
||||
"",
|
||||
);
|
||||
new Blob([JSON.stringify(body["{{ .Name }}"])], { type: "application/json" }),
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
{{- else }}
|
||||
TODO {{ .Type.Kind }} {{ .Type.Schema.Schema.Type }}
|
||||
|
||||
Reference in New Issue
Block a user