Compare commits
1 Commits
storage@0.
...
stripe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adfec57d63 |
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -32,11 +32,11 @@ Where `PKG` is:
|
||||
- `deps`: For changes to dependencies
|
||||
- `docs`: For changes to the documentation
|
||||
- `examples`: For changes to the examples
|
||||
- `internal/lib`: For changes to Nhost's common libraries (internal)
|
||||
- `mintlify-openapi`: For changes to the Mintlify OpenAPI tool
|
||||
- `nhost-js`: For changes to the Nhost JavaScript SDK
|
||||
- `nixops`: For changes to the NixOps
|
||||
- `storage`: For changes to the Nhost Storage service
|
||||
- `stripe-graphql-js`: For changes to the Stripe GraphQL JS SDK
|
||||
|
||||
Where `SUMMARY` is a short description of what the PR does.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ runs:
|
||||
|
||||
# Define valid types and packages
|
||||
VALID_TYPES="feat|fix|chore"
|
||||
VALID_PKGS="auth|ci|cli|codegen|dashboard|deps|docs|examples|internal\/lib|mintlify-openapi|nhost-js|nixops|storage"
|
||||
VALID_PKGS="auth|ci|cli|codegen|dashboard|deps|docs|examples|mintlify-openapi|nhost-js|nixops|storage|stripe-graphql-js"
|
||||
|
||||
# Check if title matches the pattern TYPE(PKG): SUMMARY
|
||||
if [[ ! "$PR_TITLE" =~ ^(${VALID_TYPES})\((${VALID_PKGS})\):\ .+ ]]; then
|
||||
|
||||
1
.github/workflows/auth_checks.yaml
vendored
1
.github/workflows/auth_checks.yaml
vendored
@@ -17,7 +17,6 @@ on:
|
||||
- '.golangci.yaml'
|
||||
- 'go.mod'
|
||||
- 'go.sum'
|
||||
- 'internal/lib/**'
|
||||
- 'vendor/**'
|
||||
|
||||
# auth
|
||||
|
||||
2
.github/workflows/ci_update_changelog.yaml
vendored
2
.github/workflows/ci_update_changelog.yaml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
cd ${{ matrix.project }}
|
||||
TAG_NAME=$(make release-tag-name)
|
||||
VERSION=$(nix develop .\#cliff -c make changelog-next-version)
|
||||
if git tag | grep -qx "$TAG_NAME@$VERSION"; then
|
||||
if git tag | grep -q "$TAG_NAME@$VERSION"; then
|
||||
echo "Tag $TAG_NAME@$VERSION already exists, skipping release preparation"
|
||||
else
|
||||
echo "Tag $TAG_NAME@$VERSION does not exist, proceeding with release preparation"
|
||||
|
||||
2
.github/workflows/dashboard_wf_release.yaml
vendored
2
.github/workflows/dashboard_wf_release.yaml
vendored
@@ -88,7 +88,7 @@ jobs:
|
||||
- name: Bump version in source code
|
||||
run: |
|
||||
find cli -type f -exec sed -i 's/"nhost\/dashboard:[^"]*"/"nhost\/dashboard:${{ inputs.VERSION }}"/g' {} +
|
||||
sed -i 's/nhost\/dashboard:[^)]*/nhost\/dashboard:${{ inputs.VERSION }}/g' docs/reference/cli/commands.mdx
|
||||
sed -i 's/"nhost\/dashboard:[^"]*"/"nhost\/dashboard:${{ inputs.VERSION }}"/g' docs/reference/cli/commands.mdx
|
||||
|
||||
- name: "Create Pull Request"
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
|
||||
1
.github/workflows/storage_checks.yaml
vendored
1
.github/workflows/storage_checks.yaml
vendored
@@ -17,7 +17,6 @@ on:
|
||||
- '.golangci.yaml'
|
||||
- 'go.mod'
|
||||
- 'go.sum'
|
||||
- 'internal/lib/**'
|
||||
- 'vendor/**'
|
||||
|
||||
# storage
|
||||
|
||||
18
cli/examples/myproject/functions/graphql/stripe.ts
Normal file
18
cli/examples/myproject/functions/graphql/stripe.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Context, createStripeGraphQLServer } from '@nhost/stripe-graphql-js';
|
||||
|
||||
const isAllowed = async (_stripeCustomerId: string, context: Context) => {
|
||||
const { isAdmin } = context;
|
||||
|
||||
if (isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//TODO: Make sure the user can only access their own stripe customer id
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
process.env.NODE_ENVIRONMENT = 'development';
|
||||
const server = createStripeGraphQLServer({ isAllowed });
|
||||
|
||||
export default server;
|
||||
616
cli/examples/myproject/functions/package-lock.json
generated
616
cli/examples/myproject/functions/package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@nhost/stripe-graphql-js": "^1.3.0-beta.6",
|
||||
"@swc/core": "^1.6.5",
|
||||
"graphql-yoga": "^5.16.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
@@ -63,12 +64,12 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@graphql-tools/executor": {
|
||||
"version": "1.4.9",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.4.9.tgz",
|
||||
"integrity": "sha512-SAUlDT70JAvXeqV87gGzvDzUGofn39nvaVcVhNf12Dt+GfWHtNNO/RCn/Ea4VJaSLGzraUd41ObnN3i80EBU7w==",
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.4.10.tgz",
|
||||
"integrity": "sha512-/o7QScMdJpx/qIJlQcYs9ohB2qU2jSpuMyPStQy30kKTLHKyMETWpbljvRsuQxHJ2MJmEF3bYZgBHzdNAQHhug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@graphql-tools/utils": "^10.9.1",
|
||||
"@graphql-tools/utils": "^10.10.0",
|
||||
"@graphql-typed-document-node/core": "^3.2.0",
|
||||
"@repeaterjs/repeater": "^3.0.4",
|
||||
"@whatwg-node/disposablestack": "^0.0.6",
|
||||
@@ -83,12 +84,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/merge": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.1.tgz",
|
||||
"integrity": "sha512-BJ5/7Y7GOhTuvzzO5tSBFL4NGr7PVqTJY3KeIDlVTT8YLcTXtBR+hlrC3uyEym7Ragn+zyWdHeJ9ev+nRX1X2w==",
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.2.tgz",
|
||||
"integrity": "sha512-Ny9YhWKv+KxZFdXYt+wlyEW55GzhFiq4daV4wYgpP0aRbwQaczNJd1L3VjjBsPKjmW8lctZXUoqYTqU5QPcBGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@graphql-tools/utils": "^10.9.1",
|
||||
"@graphql-tools/utils": "^10.10.0",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -99,13 +100,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/schema": {
|
||||
"version": "10.0.25",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.25.tgz",
|
||||
"integrity": "sha512-/PqE8US8kdQ7lB9M5+jlW8AyVjRGCKU7TSktuW3WNKSKmDO0MK1wakvb5gGdyT49MjAIb4a3LWxIpwo5VygZuw==",
|
||||
"version": "10.0.26",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.26.tgz",
|
||||
"integrity": "sha512-KOmjuiWa9poP/Lza4HV0ZBPYGJI3VE3QzXA/8e0+wjcsRuEmxMLP82re1PUg0QRzp2UzifAB/gd7DoXmVGG9Fg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@graphql-tools/merge": "^9.1.1",
|
||||
"@graphql-tools/utils": "^10.9.1",
|
||||
"@graphql-tools/merge": "^9.1.2",
|
||||
"@graphql-tools/utils": "^10.10.0",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -116,9 +117,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/utils": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.9.1.tgz",
|
||||
"integrity": "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw==",
|
||||
"version": "10.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.10.0.tgz",
|
||||
"integrity": "sha512-OOeab5Y9qeKq0zfoJCSScMcDfGcIxp05+LW2xYVCS2l3su+K3lYcg5+cAAx9n0SFxpJl8zF5denq2QDsfM7NnQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@graphql-typed-document-node/core": "^3.1.1",
|
||||
@@ -183,6 +184,38 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nhost/stripe-graphql-js": {
|
||||
"version": "1.3.0-beta.6",
|
||||
"resolved": "https://registry.npmjs.org/@nhost/stripe-graphql-js/-/stripe-graphql-js-1.3.0-beta.6.tgz",
|
||||
"integrity": "sha512-ImGZNn66UNelL+MtFyZOUB2djdBg9jbIMBRysdCeGgalbpQO/e4f5APcEiJKyZh0EESqpvnj9xSWYjdurEq3Rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pothos/core": "^3.41.0",
|
||||
"graphql": "16.8.1",
|
||||
"graphql-scalars": "^1.23.0",
|
||||
"graphql-yoga": "^5.16.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"stripe": "^11.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nhost/stripe-graphql-js/node_modules/graphql": {
|
||||
"version": "16.8.1",
|
||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz",
|
||||
"integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@pothos/core": {
|
||||
"version": "3.41.2",
|
||||
"resolved": "https://registry.npmjs.org/@pothos/core/-/core-3.41.2.tgz",
|
||||
"integrity": "sha512-iR1gqd93IyD/snTW47HwKSsRCrvnJaYwjVNcUG8BztZPqMxyJKPAnjPHAgu1XB82KEdysrNqIUnXqnzZIs08QA==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"graphql": ">=15.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@repeaterjs/repeater": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz",
|
||||
@@ -190,14 +223,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@swc/core": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.9.2.tgz",
|
||||
"integrity": "sha512-dYyEkO6mRYtZFpnOsnYzv9rY69fHAHoawYOjGOEcxk9WYtaJhowMdP/w6NcOKnz2G7GlZaenjkzkMa6ZeQeMsg==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.0.tgz",
|
||||
"integrity": "sha512-8SnJV+JV0rYbfSiEiUvYOmf62E7QwsEG+aZueqSlKoxFt0pw333+bgZSQXGUV6etXU88nxur0afVMaINujBMSw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3",
|
||||
"@swc/types": "^0.1.15"
|
||||
"@swc/types": "^0.1.25"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -207,19 +240,19 @@
|
||||
"url": "https://opencollective.com/swc"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-darwin-arm64": "1.9.2",
|
||||
"@swc/core-darwin-x64": "1.9.2",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.9.2",
|
||||
"@swc/core-linux-arm64-gnu": "1.9.2",
|
||||
"@swc/core-linux-arm64-musl": "1.9.2",
|
||||
"@swc/core-linux-x64-gnu": "1.9.2",
|
||||
"@swc/core-linux-x64-musl": "1.9.2",
|
||||
"@swc/core-win32-arm64-msvc": "1.9.2",
|
||||
"@swc/core-win32-ia32-msvc": "1.9.2",
|
||||
"@swc/core-win32-x64-msvc": "1.9.2"
|
||||
"@swc/core-darwin-arm64": "1.15.0",
|
||||
"@swc/core-darwin-x64": "1.15.0",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.15.0",
|
||||
"@swc/core-linux-arm64-gnu": "1.15.0",
|
||||
"@swc/core-linux-arm64-musl": "1.15.0",
|
||||
"@swc/core-linux-x64-gnu": "1.15.0",
|
||||
"@swc/core-linux-x64-musl": "1.15.0",
|
||||
"@swc/core-win32-arm64-msvc": "1.15.0",
|
||||
"@swc/core-win32-ia32-msvc": "1.15.0",
|
||||
"@swc/core-win32-x64-msvc": "1.15.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/helpers": "*"
|
||||
"@swc/helpers": ">=0.5.17"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/helpers": {
|
||||
@@ -228,9 +261,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-darwin-arm64": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.9.2.tgz",
|
||||
"integrity": "sha512-nETmsCoY29krTF2PtspEgicb3tqw7Ci5sInTI03EU5zpqYbPjoPH99BVTjj0OsF53jP5MxwnLI5Hm21lUn1d6A==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.0.tgz",
|
||||
"integrity": "sha512-TBKWkbnShnEjlIbO4/gfsrIgAqHBVqgPWLbWmPdZ80bF393yJcLgkrb7bZEnJs6FCbSSuGwZv2rx1jDR2zo6YA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -244,9 +277,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-darwin-x64": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.9.2.tgz",
|
||||
"integrity": "sha512-9gD+bwBz8ZByjP6nZTXe/hzd0tySIAjpDHgkFiUrc+5zGF+rdTwhcNrzxNHJmy6mw+PW38jqII4uspFHUqqxuQ==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.0.tgz",
|
||||
"integrity": "sha512-f5JKL1v1H56CIZc1pVn4RGPOfnWqPwmuHdpf4wesvXunF1Bx85YgcspW5YxwqG5J9g3nPU610UFuExJXVUzOiQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -260,9 +293,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.9.2.tgz",
|
||||
"integrity": "sha512-kYq8ief1Qrn+WmsTWAYo4r+Coul4dXN6cLFjiPZ29Cv5pyU+GFvSPAB4bEdMzwy99rCR0u2P10UExaeCjurjvg==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.0.tgz",
|
||||
"integrity": "sha512-duK6nG+WyuunnfsfiTUQdzC9Fk8cyDLqT9zyXvY2i2YgDu5+BH5W6wM5O4mDNCU5MocyB/SuF5YDF7XySnowiQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -276,9 +309,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.9.2.tgz",
|
||||
"integrity": "sha512-n0W4XiXlmEIVqxt+rD3ZpkogsEWUk1jJ+i5bQNgB+1JuWh0fBE8c/blDgTQXa0GB5lTPVDZQussgdNOCnAZwiA==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.0.tgz",
|
||||
"integrity": "sha512-ITe9iDtTRXM98B91rvyPP6qDVbhUBnmA/j4UxrHlMQ0RlwpqTjfZYZkD0uclOxSZ6qIrOj/X5CaoJlDUuQ0+Cw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -292,9 +325,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-musl": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.9.2.tgz",
|
||||
"integrity": "sha512-8xzrOmsyCC1zrx2Wzx/h8dVsdewO1oMCwBTLc1gSJ/YllZYTb04pNm6NsVbzUX2tKddJVRgSJXV10j/NECLwpA==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.0.tgz",
|
||||
"integrity": "sha512-Q5ldc2bzriuzYEoAuqJ9Vr3FyZhakk5hiwDbniZ8tlEXpbjBhbOleGf9/gkhLaouDnkNUEazFW9mtqwUTRdh7Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -308,9 +341,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-x64-gnu": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.9.2.tgz",
|
||||
"integrity": "sha512-kZrNz/PjRQKcchWF6W292jk3K44EoVu1ad5w+zbS4jekIAxsM8WwQ1kd+yjUlN9jFcF8XBat5NKIs9WphJCVXg==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.0.tgz",
|
||||
"integrity": "sha512-pY4is+jEpOxlYCSnI+7N8Oxbap9TmTz5YT84tUvRTlOlTBwFAUlWFCX0FRwWJlsfP0TxbqhIe8dNNzlsEmJbXQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -324,9 +357,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-x64-musl": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.9.2.tgz",
|
||||
"integrity": "sha512-TTIpR4rjMkhX1lnFR+PSXpaL83TrQzp9znRdp2TzYrODlUd/R20zOwSo9vFLCyH6ZoD47bccY7QeGZDYT3nlRg==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.0.tgz",
|
||||
"integrity": "sha512-zYEt5eT8y8RUpoe7t5pjpoOdGu+/gSTExj8PV86efhj6ugB3bPlj3Y85ogdW3WMVXr4NvwqvzdaYGCZfXzSyVg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -340,9 +373,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.9.2.tgz",
|
||||
"integrity": "sha512-+Eg2d4icItKC0PMjZxH7cSYFLWk0aIp94LNmOw6tPq0e69ax6oh10upeq0D1fjWsKLmOJAWEvnXlayZcijEXDw==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.0.tgz",
|
||||
"integrity": "sha512-zC1rmOgFH5v2BCbByOazEqs0aRNpTdLRchDExfcCfgKgeaD+IdpUOqp7i3VG1YzkcnbuZjMlXfM0ugpt+CddoA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -356,9 +389,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.9.2.tgz",
|
||||
"integrity": "sha512-nLWBi4vZDdM/LkiQmPCakof8Dh1/t5EM7eudue04V1lIcqx9YHVRS3KMwEaCoHLGg0c312Wm4YgrWQd9vwZ5zQ==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.0.tgz",
|
||||
"integrity": "sha512-7t9U9KwMwQblkdJIH+zX1V4q1o3o41i0HNO+VlnAHT5o+5qHJ963PHKJ/pX3P2UlZnBCY465orJuflAN4rAP9A==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -372,9 +405,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-x64-msvc": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.9.2.tgz",
|
||||
"integrity": "sha512-ik/k+JjRJBFkXARukdU82tSVx0CbExFQoQ78qTO682esbYXzjdB5eLVkoUbwen299pnfr88Kn4kyIqFPTje8Xw==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.0.tgz",
|
||||
"integrity": "sha512-VE0Zod5vcs8iMLT64m5QS1DlTMXJFI/qSgtMDRx8rtZrnjt6/9NW8XUaiPJuRu8GluEO1hmHoyf1qlbY19gGSQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -394,18 +427,18 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@swc/types": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.15.tgz",
|
||||
"integrity": "sha512-XKaZ+dzDIQ9Ot9o89oJQ/aluI17+VvUnIpYJTcZtvv1iYX6MzHh3Ik2CSR7MdPKpPwfZXHBeCingb2b4PoDVdw==",
|
||||
"version": "0.1.25",
|
||||
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz",
|
||||
"integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
|
||||
"version": "1.19.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
|
||||
"integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/connect": "*",
|
||||
@@ -422,21 +455,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
|
||||
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
|
||||
"version": "4.17.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz",
|
||||
"integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^4.17.33",
|
||||
"@types/qs": "*",
|
||||
"@types/serve-static": "*"
|
||||
"@types/serve-static": "^1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "4.19.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz",
|
||||
"integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==",
|
||||
"version": "4.19.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz",
|
||||
"integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
@@ -446,17 +479,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-errors": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
|
||||
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
|
||||
"integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/jsonwebtoken": {
|
||||
"version": "9.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz",
|
||||
"integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==",
|
||||
"version": "9.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
|
||||
"integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/ms": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
@@ -466,19 +500,25 @@
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
|
||||
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
|
||||
"integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
|
||||
"version": "24.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz",
|
||||
"integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.8"
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.9.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz",
|
||||
"integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==",
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/range-parser": {
|
||||
@@ -488,24 +528,33 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
||||
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
|
||||
"integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "1.15.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
|
||||
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
|
||||
"version": "1.15.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz",
|
||||
"integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/node": "*",
|
||||
"@types/send": "*"
|
||||
"@types/send": "<1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static/node_modules/@types/send": {
|
||||
"version": "0.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz",
|
||||
"integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@whatwg-node/disposablestack": {
|
||||
@@ -547,9 +596,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@whatwg-node/node-fetch": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.8.0.tgz",
|
||||
"integrity": "sha512-+z00GpWxKV/q8eMETwbdi80TcOoVEVZ4xSRkxYOZpn3kbV3nej5iViNzXVke/j3v4y1YpO5zMS/CVDIASvJnZQ==",
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.8.1.tgz",
|
||||
"integrity": "sha512-cQmQEo7IsI0EPX9VrwygXVzrVlX43Jb7/DBZSmpnC7xH4xkyOnn/HykHpTaQk7TUs7zh59A5uTGqx3p2Ouzffw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^3.1.1",
|
||||
@@ -574,9 +623,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@whatwg-node/server": {
|
||||
"version": "0.10.12",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/server/-/server-0.10.12.tgz",
|
||||
"integrity": "sha512-MQIvvQyPvKGna586MzXhgwnEbGtbm7QtOgJ/KPd/tC70M/jbhd1xHdIQQbh3okBw+MrDF/EvaC2vB5oRC7QdlQ==",
|
||||
"version": "0.10.13",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/server/-/server-0.10.13.tgz",
|
||||
"integrity": "sha512-Otmxo+0mp8az3B48pLI1I4msNOXPIoP7TLm6h5wOEQmynqHt8oP9nR6NJUeJk6iI5OtFpQtkbJFwfGkmplvc3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@envelop/instrumentation": "^1.0.0",
|
||||
@@ -595,6 +644,35 @@
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"get-intrinsic": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-inspect": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz",
|
||||
@@ -608,9 +686,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@@ -633,6 +711,20 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
@@ -642,16 +734,119 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/graphql": {
|
||||
"version": "16.11.0",
|
||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz",
|
||||
"integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==",
|
||||
"version": "16.12.0",
|
||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz",
|
||||
"integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/graphql-scalars": {
|
||||
"version": "1.25.0",
|
||||
"resolved": "https://registry.npmjs.org/graphql-scalars/-/graphql-scalars-1.25.0.tgz",
|
||||
"integrity": "sha512-b0xyXZeRFkne4Eq7NAnL400gStGqG/Sx9VqX0A05nHyEbv57UJnWKsjNnrpVqv5e/8N1MUxkt0wwcRXbiyKcFg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/graphql-yoga": {
|
||||
"version": "5.16.0",
|
||||
"resolved": "https://registry.npmjs.org/graphql-yoga/-/graphql-yoga-5.16.0.tgz",
|
||||
@@ -679,11 +874,29 @@
|
||||
"graphql": "^15.2.0 || ^16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/graphql-yoga/node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "4.15.9",
|
||||
@@ -717,26 +930,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jwa": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
||||
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
|
||||
"integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"buffer-equal-constant-time": "^1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jwks-rsa": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz",
|
||||
"integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz",
|
||||
"integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"@types/express": "^4.17.20",
|
||||
"@types/jsonwebtoken": "^9.0.4",
|
||||
"debug": "^4.3.4",
|
||||
"jose": "^4.14.6",
|
||||
"jose": "^4.15.4",
|
||||
"limiter": "^1.1.5",
|
||||
"lru-memoizer": "^2.2.0"
|
||||
},
|
||||
@@ -808,16 +1021,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/lru-memoizer": {
|
||||
"version": "2.3.0",
|
||||
@@ -829,12 +1036,60 @@
|
||||
"lru-cache": "6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-memoizer/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
@@ -856,9 +1111,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -867,6 +1122,91 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-list": "^1.0.0",
|
||||
"side-channel-map": "^1.0.1",
|
||||
"side-channel-weakmap": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-list": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-map": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-weakmap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-map": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/stripe": {
|
||||
"version": "11.18.0",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-11.18.0.tgz",
|
||||
"integrity": "sha512-OUA32uhNoSoM6wOodyFbV+3IBCoO140uzdXmBArQ0S88D4EbH91xl2v+Ml1sKalcFKUBadHLeHfU/p9AbsOfGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": ">=8.1.0",
|
||||
"qs": "^6.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.*"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
@@ -874,9 +1214,9 @@
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/urlpattern-polyfill": {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@nhost/stripe-graphql-js": "^1.3.0-beta.6",
|
||||
"@swc/core": "^1.6.5",
|
||||
"graphql-yoga": "^5.16.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
|
||||
653
cli/examples/myproject/functions/pnpm-lock.yaml
generated
653
cli/examples/myproject/functions/pnpm-lock.yaml
generated
@@ -1,653 +0,0 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@swc/core':
|
||||
specifier: ^1.6.5
|
||||
version: 1.6.5
|
||||
graphql-yoga:
|
||||
specifier: ^5.16.0
|
||||
version: 5.16.0(graphql@16.11.0)
|
||||
jsonwebtoken:
|
||||
specifier: ^9.0.2
|
||||
version: 9.0.2
|
||||
jwks-rsa:
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0
|
||||
|
||||
packages:
|
||||
|
||||
'@envelop/core@5.3.2':
|
||||
resolution: {integrity: sha512-06Mu7fmyKzk09P2i2kHpGfItqLLgCq7uO5/nX4fc/iHMplWPNuAx4iYR+WXUQoFHDnP6EUbceQNQ5iyeMz9f3g==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@envelop/instrumentation@1.0.0':
|
||||
resolution: {integrity: sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@envelop/types@5.2.1':
|
||||
resolution: {integrity: sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@fastify/busboy@3.2.0':
|
||||
resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==}
|
||||
|
||||
'@graphql-tools/executor@1.4.9':
|
||||
resolution: {integrity: sha512-SAUlDT70JAvXeqV87gGzvDzUGofn39nvaVcVhNf12Dt+GfWHtNNO/RCn/Ea4VJaSLGzraUd41ObnN3i80EBU7w==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
peerDependencies:
|
||||
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
|
||||
'@graphql-tools/merge@9.1.1':
|
||||
resolution: {integrity: sha512-BJ5/7Y7GOhTuvzzO5tSBFL4NGr7PVqTJY3KeIDlVTT8YLcTXtBR+hlrC3uyEym7Ragn+zyWdHeJ9ev+nRX1X2w==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
peerDependencies:
|
||||
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
|
||||
'@graphql-tools/schema@10.0.25':
|
||||
resolution: {integrity: sha512-/PqE8US8kdQ7lB9M5+jlW8AyVjRGCKU7TSktuW3WNKSKmDO0MK1wakvb5gGdyT49MjAIb4a3LWxIpwo5VygZuw==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
peerDependencies:
|
||||
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
|
||||
'@graphql-tools/utils@10.9.1':
|
||||
resolution: {integrity: sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
peerDependencies:
|
||||
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
|
||||
'@graphql-typed-document-node/core@3.2.0':
|
||||
resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==}
|
||||
peerDependencies:
|
||||
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
|
||||
'@graphql-yoga/logger@2.0.1':
|
||||
resolution: {integrity: sha512-Nv0BoDGLMg9QBKy9cIswQ3/6aKaKjlTh87x3GiBg2Z4RrjyrM48DvOOK0pJh1C1At+b0mUIM67cwZcFTDLN4sA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@graphql-yoga/subscription@5.0.5':
|
||||
resolution: {integrity: sha512-oCMWOqFs6QV96/NZRt/ZhTQvzjkGB4YohBOpKM4jH/lDT4qb7Lex/aGCxpi/JD9njw3zBBtMqxbaC22+tFHVvw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@graphql-yoga/typed-event-target@3.0.2':
|
||||
resolution: {integrity: sha512-ZpJxMqB+Qfe3rp6uszCQoag4nSw42icURnBRfFYSOmTgEeOe4rD0vYlbA8spvCu2TlCesNTlEN9BLWtQqLxabA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@repeaterjs/repeater@3.0.6':
|
||||
resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==}
|
||||
|
||||
'@swc/core-darwin-arm64@1.6.5':
|
||||
resolution: {integrity: sha512-RGQhMdni2v1/ANQ/2K+F+QYdzaucekYBewZcX1ogqJ8G5sbPaBdYdDN1qQ4kHLCIkPtGP6qC7c71qPEqL2RidQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@swc/core-darwin-x64@1.6.5':
|
||||
resolution: {integrity: sha512-/pSN0/Jtcbbb9+ovS9rKxR3qertpFAM3OEJr/+Dh/8yy7jK5G5EFPIrfsw/7Q5987ERPIJIH6BspK2CBB2tgcg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@swc/core-linux-arm-gnueabihf@1.6.5':
|
||||
resolution: {integrity: sha512-B0g/dROCE747RRegs/jPHuKJgwXLracDhnqQa80kFdgWEMjlcb7OMCgs5OX86yJGRS4qcYbiMGD0Pp7Kbqn3yw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-arm64-gnu@1.6.5':
|
||||
resolution: {integrity: sha512-W8meapgXTq8AOtSvDG4yKR8ant2WWD++yOjgzAleB5VAC+oC+aa8YJROGxj8HepurU8kurqzcialwoMeq5SZZQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-arm64-musl@1.6.5':
|
||||
resolution: {integrity: sha512-jyCKqoX50Fg8rJUQqh4u5PqnE7nqYKXHjVH2WcYr114/MU21zlsI+YL6aOQU1XP8bJQ2gPQ1rnlnGJdEHiKS/w==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-x64-gnu@1.6.5':
|
||||
resolution: {integrity: sha512-G6HmUn/RRIlXC0YYFfBz2qh6OZkHS/KUPkhoG4X9ADcgWXXjOFh6JrefwsYj8VBAJEnr5iewzjNfj+nztwHaeA==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-x64-musl@1.6.5':
|
||||
resolution: {integrity: sha512-AQpBjBnelQDSbeTJA50AXdS6+CP66LsXIMNTwhPSgUfE7Bx1ggZV11Fsi4Q5SGcs6a8Qw1cuYKN57ZfZC5QOuA==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-win32-arm64-msvc@1.6.5':
|
||||
resolution: {integrity: sha512-MZTWM8kUwS30pVrtbzSGEXtek46aXNb/mT9D6rsS7NvOuv2w+qZhjR1rzf4LNbbn5f8VnR4Nac1WIOYZmfC5ng==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core-win32-ia32-msvc@1.6.5':
|
||||
resolution: {integrity: sha512-WZdu4gISAr3yOm1fVwKhhk6+MrP7kVX0KMP7+ZQFTN5zXQEiDSDunEJKVgjMVj3vlR+6mnAqa/L0V9Qa8+zKlQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core-win32-x64-msvc@1.6.5':
|
||||
resolution: {integrity: sha512-ezXgucnMTzlFIxQZw7ls/5r2hseFaRoDL04cuXUOs97E8r+nJSmFsRQm/ygH5jBeXNo59nyZCalrjJAjwfgACA==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core@1.6.5':
|
||||
resolution: {integrity: sha512-tyVvUK/HDOUUsK6/GmWvnqUtD9oDpPUA4f7f7JCOV8hXxtfjMtAZeBKf93yrB1XZet69TDR7EN0hFC6i4MF0Ig==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@swc/helpers': '*'
|
||||
peerDependenciesMeta:
|
||||
'@swc/helpers':
|
||||
optional: true
|
||||
|
||||
'@swc/counter@0.1.3':
|
||||
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
||||
|
||||
'@swc/types@0.1.9':
|
||||
resolution: {integrity: sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==}
|
||||
|
||||
'@types/body-parser@1.19.5':
|
||||
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
|
||||
|
||||
'@types/connect@3.4.38':
|
||||
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
|
||||
|
||||
'@types/express-serve-static-core@4.19.6':
|
||||
resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==}
|
||||
|
||||
'@types/express@4.17.21':
|
||||
resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==}
|
||||
|
||||
'@types/http-errors@2.0.4':
|
||||
resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
|
||||
|
||||
'@types/jsonwebtoken@9.0.7':
|
||||
resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==}
|
||||
|
||||
'@types/mime@1.3.5':
|
||||
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
|
||||
|
||||
'@types/node@22.9.0':
|
||||
resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==}
|
||||
|
||||
'@types/qs@6.9.17':
|
||||
resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==}
|
||||
|
||||
'@types/range-parser@1.2.7':
|
||||
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
|
||||
|
||||
'@types/send@0.17.4':
|
||||
resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
|
||||
|
||||
'@types/serve-static@1.15.7':
|
||||
resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==}
|
||||
|
||||
'@whatwg-node/disposablestack@0.0.6':
|
||||
resolution: {integrity: sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@whatwg-node/events@0.1.2':
|
||||
resolution: {integrity: sha512-ApcWxkrs1WmEMS2CaLLFUEem/49erT3sxIVjpzU5f6zmVcnijtDSrhoK2zVobOIikZJdH63jdAXOrvjf6eOUNQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@whatwg-node/fetch@0.10.11':
|
||||
resolution: {integrity: sha512-eR8SYtf9Nem1Tnl0IWrY33qJ5wCtIWlt3Fs3c6V4aAaTFLtkEQErXu3SSZg/XCHrj9hXSJ8/8t+CdMk5Qec/ZA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@whatwg-node/node-fetch@0.8.0':
|
||||
resolution: {integrity: sha512-+z00GpWxKV/q8eMETwbdi80TcOoVEVZ4xSRkxYOZpn3kbV3nej5iViNzXVke/j3v4y1YpO5zMS/CVDIASvJnZQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@whatwg-node/promise-helpers@1.3.2':
|
||||
resolution: {integrity: sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
'@whatwg-node/server@0.10.12':
|
||||
resolution: {integrity: sha512-MQIvvQyPvKGna586MzXhgwnEbGtbm7QtOgJ/KPd/tC70M/jbhd1xHdIQQbh3okBw+MrDF/EvaC2vB5oRC7QdlQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||
|
||||
cross-inspect@1.0.1:
|
||||
resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
debug@4.3.7:
|
||||
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
dset@3.1.4:
|
||||
resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
||||
|
||||
graphql-yoga@5.16.0:
|
||||
resolution: {integrity: sha512-/R2dJea7WgvNlXRU4F8iFwWd95Qn1mN+R+yC8XBs1wKjUzr0Pvv8cGYtt6UUcVHw5CiDEtu7iQY5oOe3sDAWCQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
graphql: ^15.2.0 || ^16.0.0
|
||||
|
||||
graphql@16.11.0:
|
||||
resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==}
|
||||
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
|
||||
|
||||
jose@4.15.9:
|
||||
resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==}
|
||||
|
||||
jsonwebtoken@9.0.2:
|
||||
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
|
||||
engines: {node: '>=12', npm: '>=6'}
|
||||
|
||||
jwa@1.4.1:
|
||||
resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==}
|
||||
|
||||
jwks-rsa@3.1.0:
|
||||
resolution: {integrity: sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
jws@3.2.2:
|
||||
resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
|
||||
|
||||
limiter@1.1.5:
|
||||
resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==}
|
||||
|
||||
lodash.clonedeep@4.5.0:
|
||||
resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==}
|
||||
|
||||
lodash.includes@4.3.0:
|
||||
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
||||
|
||||
lodash.isboolean@3.0.3:
|
||||
resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
|
||||
|
||||
lodash.isinteger@4.0.4:
|
||||
resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}
|
||||
|
||||
lodash.isnumber@3.0.3:
|
||||
resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}
|
||||
|
||||
lodash.isplainobject@4.0.6:
|
||||
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||
|
||||
lodash.isstring@4.0.1:
|
||||
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
|
||||
|
||||
lodash.once@4.1.1:
|
||||
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
||||
|
||||
lru-cache@10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||
|
||||
lru-cache@6.0.0:
|
||||
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
lru-memoizer@2.3.0:
|
||||
resolution: {integrity: sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
|
||||
semver@7.6.3:
|
||||
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
undici-types@6.19.8:
|
||||
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
|
||||
|
||||
urlpattern-polyfill@10.1.0:
|
||||
resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==}
|
||||
|
||||
yallist@4.0.0:
|
||||
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@envelop/core@5.3.2':
|
||||
dependencies:
|
||||
'@envelop/instrumentation': 1.0.0
|
||||
'@envelop/types': 5.2.1
|
||||
'@whatwg-node/promise-helpers': 1.3.2
|
||||
tslib: 2.8.1
|
||||
|
||||
'@envelop/instrumentation@1.0.0':
|
||||
dependencies:
|
||||
'@whatwg-node/promise-helpers': 1.3.2
|
||||
tslib: 2.8.1
|
||||
|
||||
'@envelop/types@5.2.1':
|
||||
dependencies:
|
||||
'@whatwg-node/promise-helpers': 1.3.2
|
||||
tslib: 2.8.1
|
||||
|
||||
'@fastify/busboy@3.2.0': {}
|
||||
|
||||
'@graphql-tools/executor@1.4.9(graphql@16.11.0)':
|
||||
dependencies:
|
||||
'@graphql-tools/utils': 10.9.1(graphql@16.11.0)
|
||||
'@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0)
|
||||
'@repeaterjs/repeater': 3.0.6
|
||||
'@whatwg-node/disposablestack': 0.0.6
|
||||
'@whatwg-node/promise-helpers': 1.3.2
|
||||
graphql: 16.11.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@graphql-tools/merge@9.1.1(graphql@16.11.0)':
|
||||
dependencies:
|
||||
'@graphql-tools/utils': 10.9.1(graphql@16.11.0)
|
||||
graphql: 16.11.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@graphql-tools/schema@10.0.25(graphql@16.11.0)':
|
||||
dependencies:
|
||||
'@graphql-tools/merge': 9.1.1(graphql@16.11.0)
|
||||
'@graphql-tools/utils': 10.9.1(graphql@16.11.0)
|
||||
graphql: 16.11.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@graphql-tools/utils@10.9.1(graphql@16.11.0)':
|
||||
dependencies:
|
||||
'@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0)
|
||||
'@whatwg-node/promise-helpers': 1.3.2
|
||||
cross-inspect: 1.0.1
|
||||
dset: 3.1.4
|
||||
graphql: 16.11.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@graphql-typed-document-node/core@3.2.0(graphql@16.11.0)':
|
||||
dependencies:
|
||||
graphql: 16.11.0
|
||||
|
||||
'@graphql-yoga/logger@2.0.1':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@graphql-yoga/subscription@5.0.5':
|
||||
dependencies:
|
||||
'@graphql-yoga/typed-event-target': 3.0.2
|
||||
'@repeaterjs/repeater': 3.0.6
|
||||
'@whatwg-node/events': 0.1.2
|
||||
tslib: 2.8.1
|
||||
|
||||
'@graphql-yoga/typed-event-target@3.0.2':
|
||||
dependencies:
|
||||
'@repeaterjs/repeater': 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@repeaterjs/repeater@3.0.6': {}
|
||||
|
||||
'@swc/core-darwin-arm64@1.6.5':
|
||||
optional: true
|
||||
|
||||
'@swc/core-darwin-x64@1.6.5':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-arm-gnueabihf@1.6.5':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-arm64-gnu@1.6.5':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-arm64-musl@1.6.5':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-x64-gnu@1.6.5':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-x64-musl@1.6.5':
|
||||
optional: true
|
||||
|
||||
'@swc/core-win32-arm64-msvc@1.6.5':
|
||||
optional: true
|
||||
|
||||
'@swc/core-win32-ia32-msvc@1.6.5':
|
||||
optional: true
|
||||
|
||||
'@swc/core-win32-x64-msvc@1.6.5':
|
||||
optional: true
|
||||
|
||||
'@swc/core@1.6.5':
|
||||
dependencies:
|
||||
'@swc/counter': 0.1.3
|
||||
'@swc/types': 0.1.9
|
||||
optionalDependencies:
|
||||
'@swc/core-darwin-arm64': 1.6.5
|
||||
'@swc/core-darwin-x64': 1.6.5
|
||||
'@swc/core-linux-arm-gnueabihf': 1.6.5
|
||||
'@swc/core-linux-arm64-gnu': 1.6.5
|
||||
'@swc/core-linux-arm64-musl': 1.6.5
|
||||
'@swc/core-linux-x64-gnu': 1.6.5
|
||||
'@swc/core-linux-x64-musl': 1.6.5
|
||||
'@swc/core-win32-arm64-msvc': 1.6.5
|
||||
'@swc/core-win32-ia32-msvc': 1.6.5
|
||||
'@swc/core-win32-x64-msvc': 1.6.5
|
||||
|
||||
'@swc/counter@0.1.3': {}
|
||||
|
||||
'@swc/types@0.1.9':
|
||||
dependencies:
|
||||
'@swc/counter': 0.1.3
|
||||
|
||||
'@types/body-parser@1.19.5':
|
||||
dependencies:
|
||||
'@types/connect': 3.4.38
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/connect@3.4.38':
|
||||
dependencies:
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/express-serve-static-core@4.19.6':
|
||||
dependencies:
|
||||
'@types/node': 22.9.0
|
||||
'@types/qs': 6.9.17
|
||||
'@types/range-parser': 1.2.7
|
||||
'@types/send': 0.17.4
|
||||
|
||||
'@types/express@4.17.21':
|
||||
dependencies:
|
||||
'@types/body-parser': 1.19.5
|
||||
'@types/express-serve-static-core': 4.19.6
|
||||
'@types/qs': 6.9.17
|
||||
'@types/serve-static': 1.15.7
|
||||
|
||||
'@types/http-errors@2.0.4': {}
|
||||
|
||||
'@types/jsonwebtoken@9.0.7':
|
||||
dependencies:
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/mime@1.3.5': {}
|
||||
|
||||
'@types/node@22.9.0':
|
||||
dependencies:
|
||||
undici-types: 6.19.8
|
||||
|
||||
'@types/qs@6.9.17': {}
|
||||
|
||||
'@types/range-parser@1.2.7': {}
|
||||
|
||||
'@types/send@0.17.4':
|
||||
dependencies:
|
||||
'@types/mime': 1.3.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/serve-static@1.15.7':
|
||||
dependencies:
|
||||
'@types/http-errors': 2.0.4
|
||||
'@types/node': 22.9.0
|
||||
'@types/send': 0.17.4
|
||||
|
||||
'@whatwg-node/disposablestack@0.0.6':
|
||||
dependencies:
|
||||
'@whatwg-node/promise-helpers': 1.3.2
|
||||
tslib: 2.8.1
|
||||
|
||||
'@whatwg-node/events@0.1.2':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@whatwg-node/fetch@0.10.11':
|
||||
dependencies:
|
||||
'@whatwg-node/node-fetch': 0.8.0
|
||||
urlpattern-polyfill: 10.1.0
|
||||
|
||||
'@whatwg-node/node-fetch@0.8.0':
|
||||
dependencies:
|
||||
'@fastify/busboy': 3.2.0
|
||||
'@whatwg-node/disposablestack': 0.0.6
|
||||
'@whatwg-node/promise-helpers': 1.3.2
|
||||
tslib: 2.8.1
|
||||
|
||||
'@whatwg-node/promise-helpers@1.3.2':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@whatwg-node/server@0.10.12':
|
||||
dependencies:
|
||||
'@envelop/instrumentation': 1.0.0
|
||||
'@whatwg-node/disposablestack': 0.0.6
|
||||
'@whatwg-node/fetch': 0.10.11
|
||||
'@whatwg-node/promise-helpers': 1.3.2
|
||||
tslib: 2.8.1
|
||||
|
||||
buffer-equal-constant-time@1.0.1: {}
|
||||
|
||||
cross-inspect@1.0.1:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
debug@4.3.7:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
dset@3.1.4: {}
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
graphql-yoga@5.16.0(graphql@16.11.0):
|
||||
dependencies:
|
||||
'@envelop/core': 5.3.2
|
||||
'@envelop/instrumentation': 1.0.0
|
||||
'@graphql-tools/executor': 1.4.9(graphql@16.11.0)
|
||||
'@graphql-tools/schema': 10.0.25(graphql@16.11.0)
|
||||
'@graphql-tools/utils': 10.9.1(graphql@16.11.0)
|
||||
'@graphql-yoga/logger': 2.0.1
|
||||
'@graphql-yoga/subscription': 5.0.5
|
||||
'@whatwg-node/fetch': 0.10.11
|
||||
'@whatwg-node/promise-helpers': 1.3.2
|
||||
'@whatwg-node/server': 0.10.12
|
||||
dset: 3.1.4
|
||||
graphql: 16.11.0
|
||||
lru-cache: 10.4.3
|
||||
tslib: 2.8.1
|
||||
|
||||
graphql@16.11.0: {}
|
||||
|
||||
jose@4.15.9: {}
|
||||
|
||||
jsonwebtoken@9.0.2:
|
||||
dependencies:
|
||||
jws: 3.2.2
|
||||
lodash.includes: 4.3.0
|
||||
lodash.isboolean: 3.0.3
|
||||
lodash.isinteger: 4.0.4
|
||||
lodash.isnumber: 3.0.3
|
||||
lodash.isplainobject: 4.0.6
|
||||
lodash.isstring: 4.0.1
|
||||
lodash.once: 4.1.1
|
||||
ms: 2.1.3
|
||||
semver: 7.6.3
|
||||
|
||||
jwa@1.4.1:
|
||||
dependencies:
|
||||
buffer-equal-constant-time: 1.0.1
|
||||
ecdsa-sig-formatter: 1.0.11
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
jwks-rsa@3.1.0:
|
||||
dependencies:
|
||||
'@types/express': 4.17.21
|
||||
'@types/jsonwebtoken': 9.0.7
|
||||
debug: 4.3.7
|
||||
jose: 4.15.9
|
||||
limiter: 1.1.5
|
||||
lru-memoizer: 2.3.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
jws@3.2.2:
|
||||
dependencies:
|
||||
jwa: 1.4.1
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
limiter@1.1.5: {}
|
||||
|
||||
lodash.clonedeep@4.5.0: {}
|
||||
|
||||
lodash.includes@4.3.0: {}
|
||||
|
||||
lodash.isboolean@3.0.3: {}
|
||||
|
||||
lodash.isinteger@4.0.4: {}
|
||||
|
||||
lodash.isnumber@3.0.3: {}
|
||||
|
||||
lodash.isplainobject@4.0.6: {}
|
||||
|
||||
lodash.isstring@4.0.1: {}
|
||||
|
||||
lodash.once@4.1.1: {}
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
lru-cache@6.0.0:
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
|
||||
lru-memoizer@2.3.0:
|
||||
dependencies:
|
||||
lodash.clonedeep: 4.5.0
|
||||
lru-cache: 6.0.0
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
safe-buffer@5.2.1: {}
|
||||
|
||||
semver@7.6.3: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
undici-types@6.19.8: {}
|
||||
|
||||
urlpattern-polyfill@10.1.0: {}
|
||||
|
||||
yallist@4.0.0: {}
|
||||
@@ -18,3 +18,9 @@
|
||||
timeout_seconds: 60
|
||||
customization: {}
|
||||
comment: Remote schema example
|
||||
- name: stripe
|
||||
definition:
|
||||
url: '{{NHOST_FUNCTIONS_URL}}/graphql/stripe'
|
||||
timeout_seconds: 60
|
||||
customization: {}
|
||||
comment: ""
|
||||
|
||||
@@ -7,6 +7,10 @@ value = 'Sayonara'
|
||||
name = 'NODE_ENV'
|
||||
value = 'production'
|
||||
|
||||
[[global.environment]]
|
||||
name = 'STRIPE_SECRET_KEY'
|
||||
value = '{{ secrets.STRIPE_SECRET_KEY }}'
|
||||
|
||||
[hasura]
|
||||
version = 'v2.46.0-ce'
|
||||
adminSecret = '{{ secrets.HASURA_GRAPHQL_ADMIN_SECRET }}'
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
## [@nhost/dashboard@2.41.0] - 2025-11-04
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- *(auth)* Added endpoints to retrieve and refresh oauth2 providers' tokens (#3614)
|
||||
- *(dashboard)* Get github repositories from github itself (#3640)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(dashboard)* Update SQL editor to use correct hasura migrations API URL (#3645)
|
||||
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
@@ -15,7 +15,7 @@ function getCspHeader() {
|
||||
return [
|
||||
"default-src 'self' *.nhost.run wss://*.nhost.run nhost.run wss://nhost.run",
|
||||
"script-src 'self' 'unsafe-eval' cdn.segment.com js.stripe.com challenges.cloudflare.com googletagmanager.com",
|
||||
"connect-src 'self' *.nhost.run wss://*.nhost.run nhost.run wss://nhost.run discord.com api.segment.io api.segment.com cdn.segment.com nhost.zendesk.com api.github.com",
|
||||
"connect-src 'self' *.nhost.run wss://*.nhost.run nhost.run wss://nhost.run discord.com api.segment.io api.segment.com cdn.segment.com nhost.zendesk.com",
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' blob: data: github.com avatars.githubusercontent.com s.gravatar.com *.nhost.run nhost.run",
|
||||
"font-src 'self' data:",
|
||||
@@ -126,4 +126,4 @@ module.exports = withBundleAnalyzer({
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function SocialProvidersSettings() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return nhost.auth.signInProviderURL('github', {
|
||||
connect: token,
|
||||
redirectTo: `${window.location.origin}/account?signinProvider=github`,
|
||||
redirectTo: `${window.location.origin}/account`,
|
||||
});
|
||||
}
|
||||
return '';
|
||||
|
||||
@@ -3,21 +3,18 @@ import {
|
||||
useGithubAuthentication,
|
||||
type UseGithubAuthenticationHookProps,
|
||||
} from '@/features/auth/AuthProviders/Github/hooks/useGithubAuthentication';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { SiGithub } from '@icons-pack/react-simple-icons';
|
||||
|
||||
interface Props extends UseGithubAuthenticationHookProps {
|
||||
buttonText?: string;
|
||||
withAnonId?: boolean;
|
||||
redirectTo?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function GithubAuthButton({
|
||||
buttonText = 'Continue with GitHub',
|
||||
withAnonId = false,
|
||||
redirectTo,
|
||||
className,
|
||||
}: Props) {
|
||||
const { mutate: signInWithGithub, isLoading } = useGithubAuthentication({
|
||||
withAnonId,
|
||||
@@ -25,10 +22,7 @@ function GithubAuthButton({
|
||||
});
|
||||
return (
|
||||
<Button
|
||||
className={cn(
|
||||
'gap-2 !bg-white text-sm+ !text-black hover:ring-2 hover:ring-white hover:ring-opacity-50 disabled:!text-black disabled:!text-opacity-60',
|
||||
className,
|
||||
)}
|
||||
className="gap-2 !bg-white text-sm+ !text-black hover:ring-2 hover:ring-white hover:ring-opacity-50 disabled:!text-black disabled:!text-opacity-60"
|
||||
disabled={isLoading}
|
||||
loading={isLoading}
|
||||
onClick={() => signInWithGithub()}
|
||||
|
||||
@@ -30,8 +30,8 @@ function useGithubAuthentication({
|
||||
};
|
||||
}
|
||||
|
||||
const redirectURL = nhost.auth.signInProviderURL('github', options);
|
||||
window.location.href = redirectURL;
|
||||
const redirectURl = nhost.auth.signInProviderURL('github', options);
|
||||
window.location.href = redirectURl;
|
||||
},
|
||||
{
|
||||
onError: () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { GithubAuthButton } from '@/features/auth/AuthProviders/Github/component
|
||||
import { useHostName } from '@/features/orgs/projects/common/hooks/useHostName';
|
||||
|
||||
function SignInWithGithub() {
|
||||
const redirectTo = `${useHostName()}?signinProvider=github`;
|
||||
const redirectTo = useHostName();
|
||||
return (
|
||||
<GithubAuthButton
|
||||
redirectTo={redirectTo}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { GithubAuthButton } from '@/features/auth/AuthProviders/Github/components/GithubAuthButton';
|
||||
import { useHostName } from '@/features/orgs/projects/common/hooks/useHostName';
|
||||
|
||||
function SignUpWithGithub() {
|
||||
const redirectTo = `${useHostName()}?signinProvider=github`;
|
||||
return (
|
||||
<GithubAuthButton
|
||||
redirectTo={redirectTo}
|
||||
buttonText="Sign Up with GitHub"
|
||||
errorText="An error occurred while trying to sign up using GitHub. Please try again."
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ErrorMessage } from '@/components/presentational/ErrorMessage';
|
||||
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { Avatar } from '@/components/ui/v2/Avatar';
|
||||
@@ -12,33 +11,14 @@ import { Link } from '@/components/ui/v2/Link';
|
||||
import { List } from '@/components/ui/v2/List';
|
||||
import { ListItem } from '@/components/ui/v2/ListItem';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { GithubAuthButton } from '@/features/auth/AuthProviders/Github/components/GithubAuthButton';
|
||||
import { useHostName } from '@/features/orgs/projects/common/hooks/useHostName';
|
||||
import { EditRepositorySettings } from '@/features/orgs/projects/git/common/components/EditRepositorySettings';
|
||||
import {
|
||||
getGitHubToken,
|
||||
saveGitHubToken,
|
||||
} from '@/features/orgs/projects/git/common/utils';
|
||||
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { useGetAuthUserProvidersQuery } from '@/generated/graphql';
|
||||
import { useAccessToken } from '@/hooks/useAccessToken';
|
||||
import { GitHubAPIError, listGitHubInstallationRepos } from '@/lib/github';
|
||||
import { isEmptyValue } from '@/lib/utils';
|
||||
import { getToastStyleProps } from '@/utils/constants/settings';
|
||||
import { nhost } from '@/utils/nhost';
|
||||
import { useGetGithubRepositoriesQuery } from '@/generated/graphql';
|
||||
import { Divider } from '@mui/material';
|
||||
import debounce from 'lodash.debounce';
|
||||
import NavLink from 'next/link';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { Fragment, useEffect, useMemo, useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
export type ConnectGitHubModalState =
|
||||
| 'CONNECTING'
|
||||
| 'EDITING'
|
||||
| 'EXPIRED_GITHUB_SESSION'
|
||||
| 'GITHUB_CONNECTION_REQUIRED';
|
||||
export type ConnectGitHubModalState = 'CONNECTING' | 'EDITING';
|
||||
|
||||
export interface ConnectGitHubModalProps {
|
||||
/**
|
||||
@@ -48,153 +28,18 @@ export interface ConnectGitHubModalProps {
|
||||
close?: VoidFunction;
|
||||
}
|
||||
|
||||
interface GitHubData {
|
||||
githubAppInstallations: Array<{
|
||||
id: number;
|
||||
accountLogin?: string;
|
||||
accountAvatarUrl?: string;
|
||||
}>;
|
||||
githubRepositories: Array<{
|
||||
id: number;
|
||||
node_id: string;
|
||||
name: string;
|
||||
fullName: string;
|
||||
githubAppInstallation: {
|
||||
accountLogin?: string;
|
||||
accountAvatarUrl?: string;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
const [filter, setFilter] = useState('');
|
||||
const [ConnectGitHubModalState, setConnectGitHubModalState] =
|
||||
useState<ConnectGitHubModalState>('CONNECTING');
|
||||
const [selectedRepoId, setSelectedRepoId] = useState<string | null>(null);
|
||||
const [githubData, setGithubData] = useState<GitHubData | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { project, loading: loadingProject } = useProject();
|
||||
const { org, loading: loadingOrg } = useCurrentOrg();
|
||||
const hostname = useHostName();
|
||||
const token = useAccessToken();
|
||||
const {
|
||||
data,
|
||||
loading: loadingGithubConnected,
|
||||
error: errorGithubConnected,
|
||||
} = useGetAuthUserProvidersQuery();
|
||||
|
||||
const githubProvider = data?.authUserProviders?.find(
|
||||
(item) => item.providerId === 'github',
|
||||
);
|
||||
|
||||
const getGitHubConnectUrl = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
return nhost.auth.signInProviderURL('github', {
|
||||
connect: token,
|
||||
redirectTo: `${window.location.origin}?signinProvider=github&state=signin-refresh:${org.slug}:${project?.subdomain}`,
|
||||
});
|
||||
}
|
||||
return '';
|
||||
};
|
||||
const { data, loading, error, startPolling } =
|
||||
useGetGithubRepositoriesQuery();
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingGithubConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchGitHubData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
if (isEmptyValue(githubProvider)) {
|
||||
setConnectGitHubModalState('GITHUB_CONNECTION_REQUIRED');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const githubToken = getGitHubToken();
|
||||
|
||||
if (
|
||||
!githubToken?.authUserProviderId ||
|
||||
githubProvider!.id !== githubToken.authUserProviderId
|
||||
) {
|
||||
setConnectGitHubModalState('EXPIRED_GITHUB_SESSION');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const { refreshToken, expiresAt: expiresAtString } = githubToken;
|
||||
let accessToken = githubToken?.accessToken;
|
||||
|
||||
const expiresAt = new Date(expiresAtString).getTime();
|
||||
|
||||
const currentTime = Date.now();
|
||||
const expiresAtMargin = 60 * 1000;
|
||||
if (expiresAt - currentTime < expiresAtMargin) {
|
||||
if (!refreshToken) {
|
||||
setConnectGitHubModalState('EXPIRED_GITHUB_SESSION');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const refreshResponse = await nhost.auth.refreshProviderToken(
|
||||
'github',
|
||||
{ refreshToken },
|
||||
);
|
||||
|
||||
if (!refreshResponse.body) {
|
||||
setConnectGitHubModalState('EXPIRED_GITHUB_SESSION');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
saveGitHubToken({
|
||||
...refreshResponse.body,
|
||||
authUserProviderId: githubProvider!.id,
|
||||
});
|
||||
|
||||
accessToken = refreshResponse.body.accessToken;
|
||||
}
|
||||
|
||||
const installations = await listGitHubInstallationRepos(accessToken);
|
||||
|
||||
const transformedData = {
|
||||
githubAppInstallations: installations.map((item) => ({
|
||||
id: item.installation.id,
|
||||
accountLogin: item.installation.account?.login,
|
||||
accountAvatarUrl: item.installation.account?.avatar_url,
|
||||
})),
|
||||
githubRepositories: installations.flatMap((item) =>
|
||||
item.repositories.map((repo) => ({
|
||||
id: repo.id,
|
||||
node_id: repo.node_id,
|
||||
name: repo.name,
|
||||
fullName: repo.full_name,
|
||||
githubAppInstallation: {
|
||||
accountLogin: item.installation.account?.login,
|
||||
accountAvatarUrl: item.installation.account?.avatar_url,
|
||||
},
|
||||
})),
|
||||
),
|
||||
};
|
||||
|
||||
setGithubData(transformedData);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error('Error fetching GitHub data:', err);
|
||||
if (err instanceof GitHubAPIError && err.status === 401) {
|
||||
setConnectGitHubModalState('EXPIRED_GITHUB_SESSION');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
toast.error(err?.message, getToastStyleProps());
|
||||
close?.();
|
||||
}
|
||||
};
|
||||
|
||||
fetchGitHubData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [githubProvider, loadingGithubConnected]);
|
||||
startPolling(2000);
|
||||
}, [startPolling]);
|
||||
|
||||
const handleSelectAnotherRepository = () => {
|
||||
setSelectedRepoId(null);
|
||||
@@ -211,91 +56,13 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
|
||||
useEffect(() => () => handleFilterChange.cancel(), [handleFilterChange]);
|
||||
|
||||
if (errorGithubConnected instanceof Error) {
|
||||
return (
|
||||
<div className="px-1 md:w-[653px]">
|
||||
<div className="flex flex-col">
|
||||
<div className="mx-auto text-center">
|
||||
<div className="mx-auto h-8 w-8">
|
||||
<GitHubIcon className="h-8 w-8" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Text className="mt-2.5 text-center text-lg font-medium">
|
||||
Error fetching GitHub data
|
||||
</Text>
|
||||
<ErrorMessage>{errorGithubConnected.message}</ErrorMessage>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (loading || loadingProject || loadingOrg || loadingGithubConnected) {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="px-1 md:w-[653px]">
|
||||
<div className="flex flex-col">
|
||||
<div className="mx-auto text-center">
|
||||
<div className="mx-auto h-8 w-8">
|
||||
<GitHubIcon className="h-8 w-8" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="mt-2.5 text-center text-lg font-medium">
|
||||
Loading repositories...
|
||||
</Text>
|
||||
<Text className="text-center text-xs font-normal" color="secondary">
|
||||
Fetching your GitHub repositories
|
||||
</Text>
|
||||
<div className="mb-2 mt-6 flex w-full">
|
||||
<Input placeholder="Search..." fullWidth disabled value="" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex h-import items-center justify-center border-y">
|
||||
<ActivityIndicator delay={0} label="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (ConnectGitHubModalState === 'GITHUB_CONNECTION_REQUIRED') {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center gap-5 px-1 py-1 md:w-[653px]">
|
||||
<p className="text-center text-foreground">
|
||||
You need to connect your GitHub account to continue.
|
||||
</p>
|
||||
<NavLink
|
||||
href={getGitHubConnectUrl()}
|
||||
passHref
|
||||
rel="noreferrer noopener"
|
||||
legacyBehavior
|
||||
>
|
||||
<Button
|
||||
className="w-full max-w-72"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
startIcon={<GitHubIcon />}
|
||||
>
|
||||
Connect to GitHub
|
||||
</Button>
|
||||
</NavLink>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (ConnectGitHubModalState === 'EXPIRED_GITHUB_SESSION') {
|
||||
return (
|
||||
<div className="flex w-full flex-col items-center justify-center gap-5 px-1 py-1 md:w-[653px]">
|
||||
<p className="text-center text-foreground">
|
||||
Please sign in with GitHub to continue.
|
||||
</p>
|
||||
<GithubAuthButton
|
||||
redirectTo={`${hostname}?signinProvider=github&state=signin-refresh:${org.slug}:${project!.subdomain}`}
|
||||
buttonText="Sign in with GitHub"
|
||||
className="w-full max-w-72 gap-2 !bg-primary !text-white disabled:!text-white disabled:!text-opacity-60 dark:!bg-white dark:!text-black dark:disabled:!text-black"
|
||||
/>
|
||||
</div>
|
||||
<ActivityIndicator delay={500} label="Loading GitHub repositories..." />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -311,27 +78,25 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
);
|
||||
}
|
||||
|
||||
const { githubAppInstallations } = githubData || {};
|
||||
const { githubAppInstallations } = data || {};
|
||||
|
||||
const filteredGitHubAppInstallations =
|
||||
githubData?.githubAppInstallations.filter(
|
||||
(githubApp) => !!githubApp.accountLogin,
|
||||
);
|
||||
const filteredGitHubAppInstallations = data?.githubAppInstallations.filter(
|
||||
(githubApp) => !!githubApp.accountLogin,
|
||||
);
|
||||
|
||||
const filteredGitHubRepositories = githubData?.githubRepositories.filter(
|
||||
const filteredGitHubRepositories = data?.githubRepositories.filter(
|
||||
(repo) => !!repo.githubAppInstallation,
|
||||
);
|
||||
|
||||
const filteredGitHubAppInstallationsNullValues =
|
||||
githubData?.githubAppInstallations.filter(
|
||||
(githubApp) => !!githubApp.accountLogin,
|
||||
).length === 0;
|
||||
data?.githubAppInstallations.filter((githubApp) => !!githubApp.accountLogin)
|
||||
.length === 0;
|
||||
|
||||
const faultyGitHubInstallation =
|
||||
githubAppInstallations?.length === 0 ||
|
||||
filteredGitHubAppInstallationsNullValues;
|
||||
|
||||
const noRepositoriesAdded = githubData?.githubRepositories.length === 0;
|
||||
const noRepositoriesAdded = data?.githubRepositories.length === 0;
|
||||
|
||||
if (faultyGitHubInstallation) {
|
||||
return (
|
||||
@@ -350,7 +115,11 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
</div>
|
||||
|
||||
<Button
|
||||
href={`${process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}?state=install-github-app:${org.slug}:${project!.subdomain}`}
|
||||
href={process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}
|
||||
// Both `target` and `rel` are available when `href` is set. This is
|
||||
// a limitation of MUI.
|
||||
// @ts-ignore
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
endIcon={<ArrowSquareOutIcon className="h-4 w-4" />}
|
||||
>
|
||||
@@ -410,7 +179,8 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
</List>
|
||||
|
||||
<Link
|
||||
href={`${process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}?state=install-github-app:${org.slug}:${project!.subdomain}`}
|
||||
href={process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
underline="hover"
|
||||
className="grid grid-flow-col items-center justify-start gap-1"
|
||||
@@ -429,8 +199,8 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
className="text-center text-xs font-normal"
|
||||
color="secondary"
|
||||
>
|
||||
Showing repositories from{' '}
|
||||
{githubData?.githubAppInstallations.length} GitHub account(s)
|
||||
Showing repositories from {data?.githubAppInstallations.length}{' '}
|
||||
GitHub account(s)
|
||||
</Text>
|
||||
<div className="mb-2 mt-6 flex w-full">
|
||||
<Input
|
||||
@@ -456,7 +226,7 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
<Button
|
||||
variant="borderless"
|
||||
color="primary"
|
||||
onClick={() => setSelectedRepoId(repo.node_id)}
|
||||
onClick={() => setSelectedRepoId(repo.id)}
|
||||
>
|
||||
Connect
|
||||
</Button>
|
||||
@@ -498,7 +268,8 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
Do you miss a repository, or do you need to connect another GitHub
|
||||
account?{' '}
|
||||
<Link
|
||||
href={`${process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}?state=install-github-app:${org.slug}:${project!.subdomain}`}
|
||||
href={process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="text-xs font-medium"
|
||||
underline="hover"
|
||||
|
||||
@@ -6,7 +6,7 @@ import { FormProvider, useForm } from 'react-hook-form';
|
||||
export interface EditRepositorySettingsProps {
|
||||
close?: () => void;
|
||||
openConnectGithubModal?: () => void;
|
||||
selectedRepoId: string;
|
||||
selectedRepoId?: string;
|
||||
connectGithubModalState?: ConnectGitHubModalState;
|
||||
handleSelectAnotherRepository?: () => void;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ import { Text } from '@/components/ui/v2/Text';
|
||||
import { EditRepositoryAndBranchSettings } from '@/features/orgs/projects/git/common/components/EditRepositoryAndBranchSettings';
|
||||
import type { EditRepositorySettingsFormData } from '@/features/orgs/projects/git/common/components/EditRepositorySettings';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { useConnectGithubRepoMutation } from '@/generated/graphql';
|
||||
import { useUpdateApplicationMutation } from '@/generated/graphql';
|
||||
import { analytics } from '@/lib/segment';
|
||||
import { discordAnnounce } from '@/utils/discordAnnounce';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
export interface EditRepositorySettingsModalProps {
|
||||
selectedRepoId: string;
|
||||
selectedRepoId?: string;
|
||||
close?: () => void;
|
||||
handleSelectAnotherRepository?: () => void;
|
||||
}
|
||||
@@ -33,29 +33,45 @@ export default function EditRepositorySettingsModal({
|
||||
|
||||
const { project, refetch: refetchProject } = useProject();
|
||||
|
||||
const [connectGithubRepo, { loading }] = useConnectGithubRepoMutation();
|
||||
const [updateApp, { loading }] = useUpdateApplicationMutation();
|
||||
|
||||
const handleEditGitHubIntegration = async (
|
||||
data: EditRepositorySettingsFormData,
|
||||
) => {
|
||||
try {
|
||||
await connectGithubRepo({
|
||||
variables: {
|
||||
appID: project?.id,
|
||||
githubNodeID: selectedRepoId,
|
||||
productionBranch: data.productionBranch,
|
||||
baseFolder: data.repoBaseFolder,
|
||||
},
|
||||
});
|
||||
if (!project?.githubRepository || selectedRepoId) {
|
||||
await updateApp({
|
||||
variables: {
|
||||
appId: project?.id,
|
||||
app: {
|
||||
githubRepositoryId: selectedRepoId,
|
||||
repositoryProductionBranch: data.productionBranch,
|
||||
nhostBaseFolder: data.repoBaseFolder,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
analytics.track('Project Connected to GitHub', {
|
||||
projectId: project?.id,
|
||||
projectName: project?.name,
|
||||
projectSubdomain: project?.subdomain,
|
||||
repositoryId: selectedRepoId,
|
||||
productionBranch: data.productionBranch,
|
||||
baseFolder: data.repoBaseFolder,
|
||||
});
|
||||
if (selectedRepoId) {
|
||||
analytics.track('Project Connected to GitHub', {
|
||||
projectId: project?.id,
|
||||
projectName: project?.name,
|
||||
projectSubdomain: project?.subdomain,
|
||||
repositoryId: selectedRepoId,
|
||||
productionBranch: data.productionBranch,
|
||||
baseFolder: data.repoBaseFolder,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await updateApp({
|
||||
variables: {
|
||||
appId: project.id,
|
||||
app: {
|
||||
repositoryProductionBranch: data.productionBranch,
|
||||
nhostBaseFolder: data.repoBaseFolder,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await refetchProject();
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
mutation ConnectGithubRepo(
|
||||
$appID: uuid!
|
||||
$githubNodeID: String!
|
||||
$productionBranch: String!
|
||||
$baseFolder: String!
|
||||
) {
|
||||
connectGithubRepo(
|
||||
appID: $appID
|
||||
githubNodeID: $githubNodeID
|
||||
productionBranch: $productionBranch
|
||||
baseFolder: $baseFolder
|
||||
)
|
||||
}
|
||||
@@ -2,12 +2,12 @@ import { useDialog } from '@/components/common/DialogProvider';
|
||||
import { ConnectGitHubModal } from '@/features/orgs/projects/git/common/components/ConnectGitHubModal';
|
||||
|
||||
export default function useGitHubModal() {
|
||||
const { openAlertDialog, closeAlertDialog } = useDialog();
|
||||
const { openAlertDialog } = useDialog();
|
||||
|
||||
function openGitHubModal() {
|
||||
openAlertDialog({
|
||||
title: 'Connect GitHub Repository',
|
||||
payload: <ConnectGitHubModal close={closeAlertDialog} />,
|
||||
payload: <ConnectGitHubModal />,
|
||||
props: {
|
||||
hidePrimaryAction: true,
|
||||
hideSecondaryAction: true,
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { isNotEmptyValue } from '@/lib/utils';
|
||||
import type { ProviderSession } from '@nhost/nhost-js/auth';
|
||||
|
||||
const githubProviderTokenKey = 'nhost_provider_tokens_github';
|
||||
|
||||
export type GitHubProviderToken = ProviderSession & {
|
||||
authUserProviderId?: string;
|
||||
};
|
||||
|
||||
export function saveGitHubToken(token: GitHubProviderToken) {
|
||||
localStorage.setItem(githubProviderTokenKey, JSON.stringify(token));
|
||||
}
|
||||
|
||||
export function getGitHubToken() {
|
||||
const token = localStorage.getItem(githubProviderTokenKey);
|
||||
return isNotEmptyValue(token)
|
||||
? (JSON.parse(token) as GitHubProviderToken)
|
||||
: null;
|
||||
}
|
||||
|
||||
export function clearGitHubToken() {
|
||||
localStorage.removeItem(githubProviderTokenKey);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from './githubTokens';
|
||||
@@ -1,99 +0,0 @@
|
||||
/**
|
||||
* Custom error class for GitHub API errors that preserves HTTP status codes
|
||||
*/
|
||||
export class GitHubAPIError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public status: number,
|
||||
public statusText: string,
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'GitHubAPIError';
|
||||
}
|
||||
}
|
||||
|
||||
interface GitHubAppInstallation {
|
||||
id: number;
|
||||
account?: {
|
||||
login: string;
|
||||
avatar_url: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all GitHub App installations accessible to the user
|
||||
* @param accessToken - The GitHub OAuth access token
|
||||
* @returns Array of app installations
|
||||
*/
|
||||
export async function listGitHubAppInstallations(accessToken: string): Promise<GitHubAppInstallation[]> {
|
||||
const response = await fetch('https://api.github.com/user/installations', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
},
|
||||
cache: 'no-cache',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new GitHubAPIError(
|
||||
`Failed to list installations: ${response.statusText}`,
|
||||
response.status,
|
||||
response.statusText
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.installations;
|
||||
}
|
||||
|
||||
interface GitHubRepo {
|
||||
id: number;
|
||||
node_id: string;
|
||||
name: string;
|
||||
full_name: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all repositories accessible through GitHub App installations
|
||||
* @param accessToken - The GitHub OAuth access token
|
||||
* @returns Array of repositories grouped by installation
|
||||
*/
|
||||
export async function listGitHubInstallationRepos(accessToken: string) {
|
||||
const installations = await listGitHubAppInstallations(accessToken);
|
||||
|
||||
const reposByInstallation = await Promise.all(
|
||||
installations.map(async (installation) => {
|
||||
const response = await fetch(
|
||||
`https://api.github.com/user/installations/${installation.id}/repositories`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
},
|
||||
cache: 'no-cache',
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new GitHubAPIError(
|
||||
`Failed to list repos for installation ${installation.id}: ${response.statusText}`,
|
||||
response.status,
|
||||
response.statusText
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return {
|
||||
installation,
|
||||
repositories: data.repositories as GitHubRepo[],
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return reposByInstallation;
|
||||
}
|
||||
@@ -77,12 +77,12 @@ function MyApp({
|
||||
|
||||
<CacheProvider value={emotionCache}>
|
||||
<NhostProvider nhost={nhost}>
|
||||
<NhostApolloProvider
|
||||
fetchPolicy="cache-and-network"
|
||||
nhost={nhost}
|
||||
connectToDevTools={process.env.NEXT_PUBLIC_ENV === 'dev'}
|
||||
>
|
||||
<AuthProvider>
|
||||
<AuthProvider>
|
||||
<NhostApolloProvider
|
||||
fetchPolicy="cache-and-network"
|
||||
nhost={nhost}
|
||||
connectToDevTools={process.env.NEXT_PUBLIC_ENV === 'dev'}
|
||||
>
|
||||
<UIProvider>
|
||||
<Toaster position="bottom-center" />
|
||||
<ThemeProvider
|
||||
@@ -106,8 +106,8 @@ function MyApp({
|
||||
</RetryableErrorBoundary>
|
||||
</ThemeProvider>
|
||||
</UIProvider>
|
||||
</AuthProvider>
|
||||
</NhostApolloProvider>
|
||||
</NhostApolloProvider>
|
||||
</AuthProvider>
|
||||
</NhostProvider>
|
||||
</CacheProvider>
|
||||
</QueryClientProvider>
|
||||
|
||||
87
dashboard/src/pages/github-app-installation/index.tsx
Normal file
87
dashboard/src/pages/github-app-installation/index.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { LoadingScreen } from '@/components/presentational/LoadingScreen';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { nhost } from '@/utils/nhost';
|
||||
|
||||
import { useAuth } from '@/providers/Auth';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { ComponentType } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export function authProtected<P extends JSX.IntrinsicAttributes>(
|
||||
Comp: ComponentType<P>,
|
||||
) {
|
||||
return function AuthProtected(props: P) {
|
||||
const router = useRouter();
|
||||
const { isAuthenticated, isLoading } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading || isAuthenticated) {
|
||||
return;
|
||||
}
|
||||
|
||||
router.push('/signin');
|
||||
}, [isLoading, isAuthenticated, router]);
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingScreen />;
|
||||
}
|
||||
|
||||
return <Comp {...props} />;
|
||||
};
|
||||
}
|
||||
|
||||
function Page() {
|
||||
const [state, setState] = useState({
|
||||
error: null,
|
||||
loading: true,
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const { installation_id: installationId } = router.query;
|
||||
|
||||
useEffect(() => {
|
||||
async function installGithubApp() {
|
||||
try {
|
||||
await nhost.functions.fetch('/client/github-app-installation', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
installationId,
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
setState({
|
||||
error,
|
||||
loading: false,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setState({
|
||||
error: null,
|
||||
loading: false,
|
||||
});
|
||||
|
||||
window.close();
|
||||
}
|
||||
|
||||
// run in async manner
|
||||
installGithubApp();
|
||||
}, [installationId]);
|
||||
|
||||
if (state.loading) {
|
||||
return <ActivityIndicator delay={500} label="Loading..." />;
|
||||
}
|
||||
|
||||
if (state.error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw state.error;
|
||||
}
|
||||
|
||||
return <div>GitHub connection completed. You can close this tab.</div>;
|
||||
}
|
||||
|
||||
export default authProtected(Page);
|
||||
@@ -1,38 +1,12 @@
|
||||
import { Container } from '@/components/layout/Container';
|
||||
import { OrgLayout } from '@/features/orgs/layout/OrgLayout';
|
||||
import { SettingsLayout } from '@/features/orgs/layout/SettingsLayout';
|
||||
import { useGitHubModal } from '@/features/orgs/projects/git/common/hooks/useGitHubModal';
|
||||
import { BaseDirectorySettings } from '@/features/orgs/projects/git/settings/components/BaseDirectorySettings';
|
||||
import { DeploymentBranchSettings } from '@/features/orgs/projects/git/settings/components/DeploymentBranchSettings';
|
||||
import { GitConnectionSettings } from '@/features/orgs/projects/git/settings/components/GitConnectionSettings';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback, useEffect, type ReactElement } from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
export default function GitSettingsPage() {
|
||||
const router = useRouter();
|
||||
const { pathname, replace, isReady: isRouterReady } = router;
|
||||
const { 'github-modal': githubModal, ...remainingQuery } = router.query;
|
||||
const { openGitHubModal } = useGitHubModal();
|
||||
|
||||
const removeQueryParamsFromURL = useCallback(() => {
|
||||
replace({ pathname, query: remainingQuery }, undefined, {
|
||||
shallow: true,
|
||||
});
|
||||
}, [replace, remainingQuery, pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isRouterReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof githubModal === 'string') {
|
||||
removeQueryParamsFromURL();
|
||||
|
||||
openGitHubModal();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [githubModal, isRouterReady]);
|
||||
|
||||
return (
|
||||
<Container
|
||||
className="grid max-w-5xl grid-flow-row gap-y-6 bg-transparent"
|
||||
|
||||
@@ -1,27 +1,19 @@
|
||||
import {
|
||||
clearGitHubToken,
|
||||
saveGitHubToken,
|
||||
type GitHubProviderToken,
|
||||
} from '@/features/orgs/projects/git/common/utils';
|
||||
import { isNotEmptyValue } from '@/lib/utils';
|
||||
import { useNhostClient } from '@/providers/nhost/';
|
||||
import { useGetAuthUserProvidersLazyQuery } from '@/utils/__generated__/graphql';
|
||||
import { getToastStyleProps } from '@/utils/constants/settings';
|
||||
import { type Session } from '@nhost/nhost-js/auth';
|
||||
import { useRouter } from 'next/router';
|
||||
import {
|
||||
type PropsWithChildren,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
type PropsWithChildren,
|
||||
} from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { AuthContext, type AuthContextType } from './AuthContext';
|
||||
|
||||
function AuthProvider({ children }: PropsWithChildren) {
|
||||
const nhost = useNhostClient();
|
||||
const [getAuthUserProviders] = useGetAuthUserProvidersLazyQuery();
|
||||
const {
|
||||
query,
|
||||
isReady: isRouterReady,
|
||||
@@ -29,15 +21,7 @@ function AuthProvider({ children }: PropsWithChildren) {
|
||||
pathname,
|
||||
push,
|
||||
} = useRouter();
|
||||
const {
|
||||
refreshToken,
|
||||
error,
|
||||
errorDescription,
|
||||
signinProvider,
|
||||
state,
|
||||
provider_state: providerState,
|
||||
...remainingQuery
|
||||
} = query;
|
||||
const { refreshToken, error, errorDescription, ...remainingQuery } = query;
|
||||
const [session, setSession] = useState<Session | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSigningOut, setIsSigningOut] = useState(false);
|
||||
@@ -71,6 +55,7 @@ function AuthProvider({ children }: PropsWithChildren) {
|
||||
return;
|
||||
}
|
||||
setIsLoading(true);
|
||||
// reset state if we have just signed out
|
||||
setIsSigningOut(false);
|
||||
if (refreshToken && typeof refreshToken === 'string') {
|
||||
const sessionResponse = await nhost.auth.refreshToken({
|
||||
@@ -78,84 +63,24 @@ function AuthProvider({ children }: PropsWithChildren) {
|
||||
});
|
||||
setSession(sessionResponse.body);
|
||||
removeQueryParamsFromURL();
|
||||
|
||||
if (sessionResponse.body && signinProvider === 'github') {
|
||||
try {
|
||||
const providerTokensResponse =
|
||||
await nhost.auth.getProviderTokens(signinProvider);
|
||||
if (providerTokensResponse.body) {
|
||||
const { data } = await getAuthUserProviders();
|
||||
const githubProvider = data?.authUserProviders?.find(
|
||||
(provider) => provider.providerId === 'github',
|
||||
);
|
||||
const newGitHubToken: GitHubProviderToken =
|
||||
providerTokensResponse.body;
|
||||
if (isNotEmptyValue(githubProvider?.id)) {
|
||||
newGitHubToken.authUserProviderId = githubProvider!.id;
|
||||
}
|
||||
saveGitHubToken(newGitHubToken);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch provider tokens:', err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const currentSession = nhost.getUserSession();
|
||||
setSession(currentSession);
|
||||
}
|
||||
|
||||
if (
|
||||
state &&
|
||||
typeof state === 'string' &&
|
||||
state.startsWith('signin-refresh:')
|
||||
) {
|
||||
const [, orgSlug, projectSubdomain] = state.split(':');
|
||||
removeQueryParamsFromURL();
|
||||
await push(
|
||||
`/orgs/${orgSlug}/projects/${projectSubdomain}/settings/git?github-modal`,
|
||||
);
|
||||
}
|
||||
|
||||
// handle OAuth redirect errors (e.g., error=unverified-user)
|
||||
if (typeof error === 'string') {
|
||||
switch (error) {
|
||||
case 'unverified-user': {
|
||||
removeQueryParamsFromURL();
|
||||
await push('/email/verify');
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the state isn't handled by Hasura auth, it returns `invalid-state`.
|
||||
* However, we check the provider_state search param to see if it has this format:
|
||||
* `install-github-app:<org-slug>:<project-subdomain>`.
|
||||
* If it has this format, that means that we connected to GitHub in `/settings/git`,
|
||||
* thus we need to show the connect GitHub modal again.
|
||||
* Otherwise, we fall through to default error handling.
|
||||
*/
|
||||
case 'invalid-state': {
|
||||
if (
|
||||
isNotEmptyValue(providerState) &&
|
||||
typeof providerState === 'string' &&
|
||||
providerState.startsWith('install-github-app:')
|
||||
) {
|
||||
const [, orgSlug, projectSubdomain] = providerState.split(':');
|
||||
removeQueryParamsFromURL();
|
||||
await push(
|
||||
`/orgs/${orgSlug}/projects/${projectSubdomain}/settings/git?github-modal`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
// Fall through to default error handling if state search param is invalid
|
||||
}
|
||||
default: {
|
||||
const description =
|
||||
typeof errorDescription === 'string'
|
||||
? errorDescription
|
||||
: 'An error occurred during the sign-in process. Please try again.';
|
||||
toast.error(description, getToastStyleProps());
|
||||
removeQueryParamsFromURL();
|
||||
await push('/signin');
|
||||
}
|
||||
if (error === 'unverified-user') {
|
||||
removeQueryParamsFromURL();
|
||||
await push('/email/verify');
|
||||
} else {
|
||||
const description =
|
||||
typeof errorDescription === 'string'
|
||||
? errorDescription
|
||||
: 'An error occurred during the sign-in process. Please try again.';
|
||||
toast.error(description, getToastStyleProps());
|
||||
removeQueryParamsFromURL();
|
||||
await push('/signin');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +103,6 @@ function AuthProvider({ children }: PropsWithChildren) {
|
||||
nhost.auth.signOut({
|
||||
refreshToken: session!.refreshToken,
|
||||
});
|
||||
clearGitHubToken();
|
||||
|
||||
await push('/signin');
|
||||
},
|
||||
|
||||
@@ -108,16 +108,16 @@ function Providers({ children }: PropsWithChildren<{}>) {
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CacheProvider value={emotionCache}>
|
||||
<NhostProvider nhost={nhost}>
|
||||
<ApolloProvider client={mockClient}>
|
||||
<AuthProvider>
|
||||
<AuthProvider>
|
||||
<ApolloProvider client={mockClient}>
|
||||
<UIProvider>
|
||||
<Toaster position="bottom-center" />
|
||||
<ThemeProvider theme={theme}>
|
||||
<DialogProvider>{children}</DialogProvider>
|
||||
</ThemeProvider>
|
||||
</UIProvider>
|
||||
</AuthProvider>
|
||||
</ApolloProvider>
|
||||
</ApolloProvider>
|
||||
</AuthProvider>
|
||||
</NhostProvider>
|
||||
</CacheProvider>
|
||||
</QueryClientProvider>
|
||||
|
||||
82
dashboard/src/utils/__generated__/graphql.ts
generated
82
dashboard/src/utils/__generated__/graphql.ts
generated
@@ -3118,9 +3118,7 @@ export type ConfigSystemConfigPostgres = {
|
||||
database: Scalars['String'];
|
||||
disk?: Maybe<ConfigSystemConfigPostgresDisk>;
|
||||
enabled?: Maybe<Scalars['Boolean']>;
|
||||
encryptColumnKey?: Maybe<Scalars['String']>;
|
||||
majorVersion?: Maybe<Scalars['String']>;
|
||||
oldEncryptColumnKey?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type ConfigSystemConfigPostgresComparisonExp = {
|
||||
@@ -3131,9 +3129,7 @@ export type ConfigSystemConfigPostgresComparisonExp = {
|
||||
database?: InputMaybe<ConfigStringComparisonExp>;
|
||||
disk?: InputMaybe<ConfigSystemConfigPostgresDiskComparisonExp>;
|
||||
enabled?: InputMaybe<ConfigBooleanComparisonExp>;
|
||||
encryptColumnKey?: InputMaybe<ConfigStringComparisonExp>;
|
||||
majorVersion?: InputMaybe<ConfigStringComparisonExp>;
|
||||
oldEncryptColumnKey?: InputMaybe<ConfigStringComparisonExp>;
|
||||
};
|
||||
|
||||
export type ConfigSystemConfigPostgresConnectionString = {
|
||||
@@ -3197,9 +3193,7 @@ export type ConfigSystemConfigPostgresInsertInput = {
|
||||
database: Scalars['String'];
|
||||
disk?: InputMaybe<ConfigSystemConfigPostgresDiskInsertInput>;
|
||||
enabled?: InputMaybe<Scalars['Boolean']>;
|
||||
encryptColumnKey?: InputMaybe<Scalars['String']>;
|
||||
majorVersion?: InputMaybe<Scalars['String']>;
|
||||
oldEncryptColumnKey?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type ConfigSystemConfigPostgresUpdateInput = {
|
||||
@@ -3207,9 +3201,7 @@ export type ConfigSystemConfigPostgresUpdateInput = {
|
||||
database?: InputMaybe<Scalars['String']>;
|
||||
disk?: InputMaybe<ConfigSystemConfigPostgresDiskUpdateInput>;
|
||||
enabled?: InputMaybe<Scalars['Boolean']>;
|
||||
encryptColumnKey?: InputMaybe<Scalars['String']>;
|
||||
majorVersion?: InputMaybe<Scalars['String']>;
|
||||
oldEncryptColumnKey?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type ConfigSystemConfigUpdateInput = {
|
||||
@@ -4434,7 +4426,6 @@ export type Apps = {
|
||||
billingDedicatedCompute?: Maybe<Billing_Dedicated_Compute>;
|
||||
/** An object relationship */
|
||||
billingSubscriptions?: Maybe<Billing_Subscriptions>;
|
||||
/** main entrypoint to the configuration */
|
||||
config?: Maybe<ConfigConfig>;
|
||||
createdAt: Scalars['timestamptz'];
|
||||
/** An object relationship */
|
||||
@@ -11103,7 +11094,6 @@ export type Deployments = {
|
||||
commitSHA: Scalars['String'];
|
||||
commitUserAvatarUrl?: Maybe<Scalars['String']>;
|
||||
commitUserName?: Maybe<Scalars['String']>;
|
||||
createdAt: Scalars['timestamptz'];
|
||||
deploymentEndedAt?: Maybe<Scalars['timestamptz']>;
|
||||
/** An array relationship */
|
||||
deploymentLogs: Array<DeploymentLogs>;
|
||||
@@ -11201,7 +11191,6 @@ export type Deployments_Bool_Exp = {
|
||||
commitSHA?: InputMaybe<String_Comparison_Exp>;
|
||||
commitUserAvatarUrl?: InputMaybe<String_Comparison_Exp>;
|
||||
commitUserName?: InputMaybe<String_Comparison_Exp>;
|
||||
createdAt?: InputMaybe<Timestamptz_Comparison_Exp>;
|
||||
deploymentEndedAt?: InputMaybe<Timestamptz_Comparison_Exp>;
|
||||
deploymentLogs?: InputMaybe<DeploymentLogs_Bool_Exp>;
|
||||
deploymentLogs_aggregate?: InputMaybe<DeploymentLogs_Aggregate_Bool_Exp>;
|
||||
@@ -11233,7 +11222,6 @@ export type Deployments_Insert_Input = {
|
||||
commitSHA?: InputMaybe<Scalars['String']>;
|
||||
commitUserAvatarUrl?: InputMaybe<Scalars['String']>;
|
||||
commitUserName?: InputMaybe<Scalars['String']>;
|
||||
createdAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
deploymentEndedAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
deploymentLogs?: InputMaybe<DeploymentLogs_Arr_Rel_Insert_Input>;
|
||||
deploymentStartedAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
@@ -11258,7 +11246,6 @@ export type Deployments_Max_Fields = {
|
||||
commitSHA?: Maybe<Scalars['String']>;
|
||||
commitUserAvatarUrl?: Maybe<Scalars['String']>;
|
||||
commitUserName?: Maybe<Scalars['String']>;
|
||||
createdAt?: Maybe<Scalars['timestamptz']>;
|
||||
deploymentEndedAt?: Maybe<Scalars['timestamptz']>;
|
||||
deploymentStartedAt?: Maybe<Scalars['timestamptz']>;
|
||||
deploymentStatus?: Maybe<Scalars['String']>;
|
||||
@@ -11281,7 +11268,6 @@ export type Deployments_Max_Order_By = {
|
||||
commitSHA?: InputMaybe<Order_By>;
|
||||
commitUserAvatarUrl?: InputMaybe<Order_By>;
|
||||
commitUserName?: InputMaybe<Order_By>;
|
||||
createdAt?: InputMaybe<Order_By>;
|
||||
deploymentEndedAt?: InputMaybe<Order_By>;
|
||||
deploymentStartedAt?: InputMaybe<Order_By>;
|
||||
deploymentStatus?: InputMaybe<Order_By>;
|
||||
@@ -11305,7 +11291,6 @@ export type Deployments_Min_Fields = {
|
||||
commitSHA?: Maybe<Scalars['String']>;
|
||||
commitUserAvatarUrl?: Maybe<Scalars['String']>;
|
||||
commitUserName?: Maybe<Scalars['String']>;
|
||||
createdAt?: Maybe<Scalars['timestamptz']>;
|
||||
deploymentEndedAt?: Maybe<Scalars['timestamptz']>;
|
||||
deploymentStartedAt?: Maybe<Scalars['timestamptz']>;
|
||||
deploymentStatus?: Maybe<Scalars['String']>;
|
||||
@@ -11328,7 +11313,6 @@ export type Deployments_Min_Order_By = {
|
||||
commitSHA?: InputMaybe<Order_By>;
|
||||
commitUserAvatarUrl?: InputMaybe<Order_By>;
|
||||
commitUserName?: InputMaybe<Order_By>;
|
||||
createdAt?: InputMaybe<Order_By>;
|
||||
deploymentEndedAt?: InputMaybe<Order_By>;
|
||||
deploymentStartedAt?: InputMaybe<Order_By>;
|
||||
deploymentStatus?: InputMaybe<Order_By>;
|
||||
@@ -11375,7 +11359,6 @@ export type Deployments_Order_By = {
|
||||
commitSHA?: InputMaybe<Order_By>;
|
||||
commitUserAvatarUrl?: InputMaybe<Order_By>;
|
||||
commitUserName?: InputMaybe<Order_By>;
|
||||
createdAt?: InputMaybe<Order_By>;
|
||||
deploymentEndedAt?: InputMaybe<Order_By>;
|
||||
deploymentLogs_aggregate?: InputMaybe<DeploymentLogs_Aggregate_Order_By>;
|
||||
deploymentStartedAt?: InputMaybe<Order_By>;
|
||||
@@ -11410,8 +11393,6 @@ export enum Deployments_Select_Column {
|
||||
/** column name */
|
||||
CommitUserName = 'commitUserName',
|
||||
/** column name */
|
||||
CreatedAt = 'createdAt',
|
||||
/** column name */
|
||||
DeploymentEndedAt = 'deploymentEndedAt',
|
||||
/** column name */
|
||||
DeploymentStartedAt = 'deploymentStartedAt',
|
||||
@@ -11446,7 +11427,6 @@ export type Deployments_Set_Input = {
|
||||
commitSHA?: InputMaybe<Scalars['String']>;
|
||||
commitUserAvatarUrl?: InputMaybe<Scalars['String']>;
|
||||
commitUserName?: InputMaybe<Scalars['String']>;
|
||||
createdAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
deploymentEndedAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
deploymentStartedAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
deploymentStatus?: InputMaybe<Scalars['String']>;
|
||||
@@ -11477,7 +11457,6 @@ export type Deployments_Stream_Cursor_Value_Input = {
|
||||
commitSHA?: InputMaybe<Scalars['String']>;
|
||||
commitUserAvatarUrl?: InputMaybe<Scalars['String']>;
|
||||
commitUserName?: InputMaybe<Scalars['String']>;
|
||||
createdAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
deploymentEndedAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
deploymentStartedAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
deploymentStatus?: InputMaybe<Scalars['String']>;
|
||||
@@ -11506,8 +11485,6 @@ export enum Deployments_Update_Column {
|
||||
/** column name */
|
||||
CommitUserName = 'commitUserName',
|
||||
/** column name */
|
||||
CreatedAt = 'createdAt',
|
||||
/** column name */
|
||||
DeploymentEndedAt = 'deploymentEndedAt',
|
||||
/** column name */
|
||||
DeploymentStartedAt = 'deploymentStartedAt',
|
||||
@@ -13090,7 +13067,6 @@ export type Mutation_Root = {
|
||||
billingUpgradeFreeOrganization: Scalars['String'];
|
||||
billingUploadReports: Scalars['Boolean'];
|
||||
changeDatabaseVersion: Scalars['Boolean'];
|
||||
connectGithubRepo: Scalars['Boolean'];
|
||||
/** delete single row from the table: "announcements_read" */
|
||||
deleteAnnouncementRead?: Maybe<Announcements_Read>;
|
||||
/** delete data from the table: "announcements_read" */
|
||||
@@ -13992,15 +13968,6 @@ export type Mutation_RootChangeDatabaseVersionArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootConnectGithubRepoArgs = {
|
||||
appID: Scalars['uuid'];
|
||||
baseFolder: Scalars['String'];
|
||||
githubNodeID: Scalars['String'];
|
||||
productionBranch: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootDeleteAnnouncementReadArgs = {
|
||||
id: Scalars['uuid'];
|
||||
@@ -27550,16 +27517,6 @@ export type ResetDatabasePasswordMutationVariables = Exact<{
|
||||
|
||||
export type ResetDatabasePasswordMutation = { __typename?: 'mutation_root', resetPostgresPassword: boolean };
|
||||
|
||||
export type ConnectGithubRepoMutationVariables = Exact<{
|
||||
appID: Scalars['uuid'];
|
||||
githubNodeID: Scalars['String'];
|
||||
productionBranch: Scalars['String'];
|
||||
baseFolder: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ConnectGithubRepoMutation = { __typename?: 'mutation_root', connectGithubRepo: boolean };
|
||||
|
||||
export type GetHasuraSettingsQueryVariables = Exact<{
|
||||
appId: Scalars['uuid'];
|
||||
}>;
|
||||
@@ -29489,45 +29446,6 @@ export function useResetDatabasePasswordMutation(baseOptions?: Apollo.MutationHo
|
||||
export type ResetDatabasePasswordMutationHookResult = ReturnType<typeof useResetDatabasePasswordMutation>;
|
||||
export type ResetDatabasePasswordMutationResult = Apollo.MutationResult<ResetDatabasePasswordMutation>;
|
||||
export type ResetDatabasePasswordMutationOptions = Apollo.BaseMutationOptions<ResetDatabasePasswordMutation, ResetDatabasePasswordMutationVariables>;
|
||||
export const ConnectGithubRepoDocument = gql`
|
||||
mutation ConnectGithubRepo($appID: uuid!, $githubNodeID: String!, $productionBranch: String!, $baseFolder: String!) {
|
||||
connectGithubRepo(
|
||||
appID: $appID
|
||||
githubNodeID: $githubNodeID
|
||||
productionBranch: $productionBranch
|
||||
baseFolder: $baseFolder
|
||||
)
|
||||
}
|
||||
`;
|
||||
export type ConnectGithubRepoMutationFn = Apollo.MutationFunction<ConnectGithubRepoMutation, ConnectGithubRepoMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useConnectGithubRepoMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useConnectGithubRepoMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useConnectGithubRepoMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [connectGithubRepoMutation, { data, loading, error }] = useConnectGithubRepoMutation({
|
||||
* variables: {
|
||||
* appID: // value for 'appID'
|
||||
* githubNodeID: // value for 'githubNodeID'
|
||||
* productionBranch: // value for 'productionBranch'
|
||||
* baseFolder: // value for 'baseFolder'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useConnectGithubRepoMutation(baseOptions?: Apollo.MutationHookOptions<ConnectGithubRepoMutation, ConnectGithubRepoMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<ConnectGithubRepoMutation, ConnectGithubRepoMutationVariables>(ConnectGithubRepoDocument, options);
|
||||
}
|
||||
export type ConnectGithubRepoMutationHookResult = ReturnType<typeof useConnectGithubRepoMutation>;
|
||||
export type ConnectGithubRepoMutationResult = Apollo.MutationResult<ConnectGithubRepoMutation>;
|
||||
export type ConnectGithubRepoMutationOptions = Apollo.BaseMutationOptions<ConnectGithubRepoMutation, ConnectGithubRepoMutationVariables>;
|
||||
export const GetHasuraSettingsDocument = gql`
|
||||
query GetHasuraSettings($appId: uuid!) {
|
||||
config(appID: $appId, resolve: false) {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"extends": "../../config/tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
@@ -206,7 +206,8 @@ paths:
|
||||
Last-Modified:
|
||||
description: "Date and time the file was last modified"
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
type: string
|
||||
format: date-time
|
||||
Surrogate-Key:
|
||||
description: "Cache key for surrogate caching"
|
||||
schema:
|
||||
@@ -247,7 +248,8 @@ paths:
|
||||
Last-Modified:
|
||||
description: "Date and time the file was last modified"
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
type: string
|
||||
format: date-time
|
||||
Surrogate-Key:
|
||||
description: "Cache key for surrogate caching"
|
||||
schema:
|
||||
@@ -389,7 +391,8 @@ paths:
|
||||
Last-Modified:
|
||||
description: "Date and time the file was last modified"
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
type: string
|
||||
format: date-time
|
||||
Accept-Ranges:
|
||||
description: "Always set to bytes. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Ranges"
|
||||
schema:
|
||||
@@ -672,7 +675,8 @@ paths:
|
||||
Last-Modified:
|
||||
description: "Date and time the file was last modified"
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
type: string
|
||||
format: date-time
|
||||
Surrogate-Key:
|
||||
description: "Cache key for surrogate caching"
|
||||
schema:
|
||||
@@ -713,7 +717,8 @@ paths:
|
||||
Last-Modified:
|
||||
description: "Date and time the file was last modified"
|
||||
schema:
|
||||
$ref: '#/components/schemas/RFC2822Date'
|
||||
type: string
|
||||
format: date-time
|
||||
Surrogate-Key:
|
||||
description: "Cache key for surrogate caching"
|
||||
schema:
|
||||
|
||||
@@ -119,7 +119,6 @@
|
||||
gofumpt
|
||||
golangci-lint
|
||||
gqlgenc
|
||||
oapi-codegen
|
||||
|
||||
# internal packages
|
||||
self.packages.${system}.codegen
|
||||
|
||||
4
go.mod
4
go.mod
@@ -16,6 +16,7 @@ require (
|
||||
github.com/davidbyttow/govips/v2 v2.16.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.8
|
||||
github.com/getkin/kin-openapi v0.133.0
|
||||
github.com/gin-contrib/cors v1.7.3
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/go-webauthn/webauthn v0.12.2
|
||||
@@ -29,6 +30,7 @@ require (
|
||||
github.com/lmittmann/tint v1.0.7
|
||||
github.com/mark3labs/mcp-go v0.41.1
|
||||
github.com/nhost/be v0.0.0-20251021065906-8abc7d8dfa48
|
||||
github.com/oapi-codegen/gin-middleware v1.0.2
|
||||
github.com/oapi-codegen/runtime v1.1.1
|
||||
github.com/pb33f/libopenapi v0.21.12
|
||||
github.com/pelletier/go-toml/v2 v2.2.4
|
||||
@@ -154,7 +156,7 @@ require (
|
||||
github.com/prometheus/common v0.63.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.54.1 // indirect
|
||||
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rs/cors v1.11.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
|
||||
8
go.sum
8
go.sum
@@ -162,6 +162,8 @@ github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3G
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ=
|
||||
github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE=
|
||||
github.com/gin-contrib/cors v1.7.3 h1:hV+a5xp8hwJoTw7OY+a70FsL8JkVVFTXw9EcfrYUdns=
|
||||
github.com/gin-contrib/cors v1.7.3/go.mod h1:M3bcKZhxzsvI+rlRSkkxHyljJt1ESd93COUvemZ79j4=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
@@ -339,6 +341,8 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
|
||||
github.com/nhost/be v0.0.0-20251021065906-8abc7d8dfa48 h1:+Oh4Rbr1psWlBaQTakoBYFNB8jBioiXuimNMaNPLTHk=
|
||||
github.com/nhost/be v0.0.0-20251021065906-8abc7d8dfa48/go.mod h1:feVvqP3dft8hWbp9zNZExdGKbFEYv8aLYohfyAeINNQ=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oapi-codegen/gin-middleware v1.0.2 h1:/H99UzvHQAUxXK8pzdcGAZgjCVeXdFDAUUWaJT0k0eI=
|
||||
github.com/oapi-codegen/gin-middleware v1.0.2/go.mod h1:2HJDQjH8jzK2/k/VKcWl+/T41H7ai2bKa6dN3AA2GpA=
|
||||
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
|
||||
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
||||
@@ -381,8 +385,8 @@ github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d h1:HWfigq
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.54.1 h1:4ZAWm0AhCb6+hE+l5Q1NAL0iRn/ZrMwqHRGQiFwj2eg=
|
||||
github.com/quic-go/quic-go v0.54.1/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package oapi
|
||||
|
||||
import "fmt"
|
||||
|
||||
type AuthenticatorError struct {
|
||||
Scheme string
|
||||
Code string
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *AuthenticatorError) Error() string {
|
||||
return fmt.Sprintf("security error [%s]: %s", e.Code, e.Message)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
//go:generate oapi-codegen -config server.cfg.yaml openapi.yaml
|
||||
//go:generate oapi-codegen -config types.cfg.yaml openapi.yaml
|
||||
package api
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed openapi.yaml
|
||||
var OpenAPISchema []byte
|
||||
@@ -1,200 +0,0 @@
|
||||
openapi: "3.0.0"
|
||||
|
||||
paths:
|
||||
/signin/email-password:
|
||||
post:
|
||||
summary: Sign in with email and password
|
||||
description: Authenticate a user with their email and password. Returns a session object or MFA challenge if two-factor authentication is enabled.
|
||||
operationId: signInEmailPassword
|
||||
requestBody:
|
||||
description: User credentials for email and password authentication
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/SignInEmailPasswordRequest"
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/SignInEmailPasswordResponse"
|
||||
description: "Authentication successful. If MFA is enabled, a challenge will be returned instead of a session."
|
||||
default:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
description: "An error occurred while processing the request"
|
||||
|
||||
/user/email/change:
|
||||
post:
|
||||
summary: Change user email
|
||||
description: Request to change the authenticated user's email address. A verification email will be sent to the new address to confirm the change. Requires elevated permissions.
|
||||
operationId: changeUserEmail
|
||||
tags:
|
||||
- user
|
||||
security:
|
||||
- BearerAuthElevated: []
|
||||
requestBody:
|
||||
description: New email address and optional redirect URL for email change
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UserEmailChangeRequest"
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: >-
|
||||
Email change requested. An email with a verification link has been sent to the new address
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/OKResponse"
|
||||
default:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
description: "An error occurred while processing the request"
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
BearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
description: "Bearer authentication with JWT access token. Used to authenticate requests to protected endpoints."
|
||||
BearerAuthElevated:
|
||||
type: http
|
||||
scheme: bearer
|
||||
description: "Bearer authentication that requires elevated permissions. Used for sensitive operations that may require additional security measures such as recent authentication. For details see https://docs.nhost.io/products/auth/elevated-permissions"
|
||||
|
||||
schemas:
|
||||
SignInEmailPasswordRequest:
|
||||
type: object
|
||||
description: "Request to authenticate using email and password"
|
||||
additionalProperties: false
|
||||
properties:
|
||||
email:
|
||||
description: "User's email address"
|
||||
example: "john.smith@nhost.io"
|
||||
format: email
|
||||
type: string
|
||||
password:
|
||||
description: "User's password"
|
||||
example: "Str0ngPassw#ord-94|%"
|
||||
minLength: 3
|
||||
maxLength: 50
|
||||
type: string
|
||||
required:
|
||||
- email
|
||||
- password
|
||||
|
||||
SignInEmailPasswordResponse:
|
||||
type: object
|
||||
description: "Response for email-password authentication that may include a session or MFA challenge"
|
||||
additionalProperties: false
|
||||
properties:
|
||||
session:
|
||||
$ref: "#/components/schemas/Session"
|
||||
|
||||
Session:
|
||||
type: object
|
||||
description: "User authentication session containing tokens and user information"
|
||||
additionalProperties: false
|
||||
properties:
|
||||
accessToken:
|
||||
type: string
|
||||
description: "JWT token for authenticating API requests"
|
||||
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
accessTokenExpiresIn:
|
||||
type: integer
|
||||
format: int64
|
||||
description: "Expiration time of the access token in seconds"
|
||||
example: 900
|
||||
refreshTokenId:
|
||||
description: "Identifier for the refresh token"
|
||||
example: "2c35b6f3-c4b9-48e3-978a-d4d0f1d42e24"
|
||||
pattern: \b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b
|
||||
type: string
|
||||
refreshToken:
|
||||
description: "Token used to refresh the access token"
|
||||
example: "2c35b6f3-c4b9-48e3-978a-d4d0f1d42e24"
|
||||
pattern: \b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b
|
||||
type: string
|
||||
required:
|
||||
- accessToken
|
||||
- accessTokenExpiresIn
|
||||
- refreshToken
|
||||
- refreshTokenId
|
||||
|
||||
UserEmailChangeRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
properties:
|
||||
newEmail:
|
||||
description: A valid email
|
||||
example: john.smith@nhost.io
|
||||
format: email
|
||||
type: string
|
||||
required:
|
||||
- newEmail
|
||||
|
||||
OKResponse:
|
||||
type: string
|
||||
additionalProperties: false
|
||||
enum:
|
||||
- OK
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
description: "Standardized error response"
|
||||
additionalProperties: false
|
||||
properties:
|
||||
status:
|
||||
description: "HTTP status error code"
|
||||
type: integer
|
||||
example: 400
|
||||
message:
|
||||
description: "Human-friendly error message"
|
||||
type: string
|
||||
example: "Invalid email format"
|
||||
error:
|
||||
description: "Error code identifying the specific application error"
|
||||
type: string
|
||||
enum:
|
||||
- default-role-must-be-in-allowed-roles
|
||||
- disabled-endpoint
|
||||
- disabled-user
|
||||
- email-already-in-use
|
||||
- email-already-verified
|
||||
- forbidden-anonymous
|
||||
- internal-server-error
|
||||
- invalid-email-password
|
||||
- invalid-request
|
||||
- locale-not-allowed
|
||||
- password-too-short
|
||||
- password-in-hibp-database
|
||||
- redirectTo-not-allowed
|
||||
- role-not-allowed
|
||||
- signup-disabled
|
||||
- unverified-user
|
||||
- user-not-anonymous
|
||||
- invalid-pat
|
||||
- invalid-refresh-token
|
||||
- invalid-ticket
|
||||
- disabled-mfa-totp
|
||||
- no-totp-secret
|
||||
- invalid-totp
|
||||
- mfa-type-not-found
|
||||
- totp-already-active
|
||||
- invalid-state
|
||||
- oauth-token-echange-failed
|
||||
- oauth-profile-fetch-failed
|
||||
- oauth-provider-error
|
||||
- invalid-otp
|
||||
- cannot-send-sms
|
||||
required:
|
||||
- status
|
||||
- message
|
||||
- error
|
||||
@@ -1,6 +0,0 @@
|
||||
package: api
|
||||
generate:
|
||||
gin-server: true
|
||||
embedded-spec: true
|
||||
strict-server: true
|
||||
output: server.gen.go
|
||||
@@ -1,351 +0,0 @@
|
||||
// Package api provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version 2.5.0 DO NOT EDIT.
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/gin-gonic/gin"
|
||||
strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin"
|
||||
)
|
||||
|
||||
// ServerInterface represents all server handlers.
|
||||
type ServerInterface interface {
|
||||
// Sign in with email and password
|
||||
// (POST /signin/email-password)
|
||||
SignInEmailPassword(c *gin.Context)
|
||||
// Change user email
|
||||
// (POST /user/email/change)
|
||||
ChangeUserEmail(c *gin.Context)
|
||||
}
|
||||
|
||||
// ServerInterfaceWrapper converts contexts to parameters.
|
||||
type ServerInterfaceWrapper struct {
|
||||
Handler ServerInterface
|
||||
HandlerMiddlewares []MiddlewareFunc
|
||||
ErrorHandler func(*gin.Context, error, int)
|
||||
}
|
||||
|
||||
type MiddlewareFunc func(c *gin.Context)
|
||||
|
||||
// SignInEmailPassword operation middleware
|
||||
func (siw *ServerInterfaceWrapper) SignInEmailPassword(c *gin.Context) {
|
||||
|
||||
for _, middleware := range siw.HandlerMiddlewares {
|
||||
middleware(c)
|
||||
if c.IsAborted() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
siw.Handler.SignInEmailPassword(c)
|
||||
}
|
||||
|
||||
// ChangeUserEmail operation middleware
|
||||
func (siw *ServerInterfaceWrapper) ChangeUserEmail(c *gin.Context) {
|
||||
|
||||
c.Set(BearerAuthElevatedScopes, []string{})
|
||||
|
||||
for _, middleware := range siw.HandlerMiddlewares {
|
||||
middleware(c)
|
||||
if c.IsAborted() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
siw.Handler.ChangeUserEmail(c)
|
||||
}
|
||||
|
||||
// GinServerOptions provides options for the Gin server.
|
||||
type GinServerOptions struct {
|
||||
BaseURL string
|
||||
Middlewares []MiddlewareFunc
|
||||
ErrorHandler func(*gin.Context, error, int)
|
||||
}
|
||||
|
||||
// RegisterHandlers creates http.Handler with routing matching OpenAPI spec.
|
||||
func RegisterHandlers(router gin.IRouter, si ServerInterface) {
|
||||
RegisterHandlersWithOptions(router, si, GinServerOptions{})
|
||||
}
|
||||
|
||||
// RegisterHandlersWithOptions creates http.Handler with additional options
|
||||
func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options GinServerOptions) {
|
||||
errorHandler := options.ErrorHandler
|
||||
if errorHandler == nil {
|
||||
errorHandler = func(c *gin.Context, err error, statusCode int) {
|
||||
c.JSON(statusCode, gin.H{"msg": err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
wrapper := ServerInterfaceWrapper{
|
||||
Handler: si,
|
||||
HandlerMiddlewares: options.Middlewares,
|
||||
ErrorHandler: errorHandler,
|
||||
}
|
||||
|
||||
router.POST(options.BaseURL+"/signin/email-password", wrapper.SignInEmailPassword)
|
||||
router.POST(options.BaseURL+"/user/email/change", wrapper.ChangeUserEmail)
|
||||
}
|
||||
|
||||
type SignInEmailPasswordRequestObject struct {
|
||||
Body *SignInEmailPasswordJSONRequestBody
|
||||
}
|
||||
|
||||
type SignInEmailPasswordResponseObject interface {
|
||||
VisitSignInEmailPasswordResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type SignInEmailPassword200JSONResponse SignInEmailPasswordResponse
|
||||
|
||||
func (response SignInEmailPassword200JSONResponse) VisitSignInEmailPasswordResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type SignInEmailPassworddefaultJSONResponse struct {
|
||||
Body ErrorResponse
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
func (response SignInEmailPassworddefaultJSONResponse) VisitSignInEmailPasswordResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(response.StatusCode)
|
||||
|
||||
return json.NewEncoder(w).Encode(response.Body)
|
||||
}
|
||||
|
||||
type ChangeUserEmailRequestObject struct {
|
||||
Body *ChangeUserEmailJSONRequestBody
|
||||
}
|
||||
|
||||
type ChangeUserEmailResponseObject interface {
|
||||
VisitChangeUserEmailResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type ChangeUserEmail200JSONResponse OKResponse
|
||||
|
||||
func (response ChangeUserEmail200JSONResponse) VisitChangeUserEmailResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type ChangeUserEmaildefaultJSONResponse struct {
|
||||
Body ErrorResponse
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
func (response ChangeUserEmaildefaultJSONResponse) VisitChangeUserEmailResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(response.StatusCode)
|
||||
|
||||
return json.NewEncoder(w).Encode(response.Body)
|
||||
}
|
||||
|
||||
// StrictServerInterface represents all server handlers.
|
||||
type StrictServerInterface interface {
|
||||
// Sign in with email and password
|
||||
// (POST /signin/email-password)
|
||||
SignInEmailPassword(ctx context.Context, request SignInEmailPasswordRequestObject) (SignInEmailPasswordResponseObject, error)
|
||||
// Change user email
|
||||
// (POST /user/email/change)
|
||||
ChangeUserEmail(ctx context.Context, request ChangeUserEmailRequestObject) (ChangeUserEmailResponseObject, error)
|
||||
}
|
||||
|
||||
type StrictHandlerFunc = strictgin.StrictGinHandlerFunc
|
||||
type StrictMiddlewareFunc = strictgin.StrictGinMiddlewareFunc
|
||||
|
||||
func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
|
||||
return &strictHandler{ssi: ssi, middlewares: middlewares}
|
||||
}
|
||||
|
||||
type strictHandler struct {
|
||||
ssi StrictServerInterface
|
||||
middlewares []StrictMiddlewareFunc
|
||||
}
|
||||
|
||||
// SignInEmailPassword operation middleware
|
||||
func (sh *strictHandler) SignInEmailPassword(ctx *gin.Context) {
|
||||
var request SignInEmailPasswordRequestObject
|
||||
|
||||
var body SignInEmailPasswordJSONRequestBody
|
||||
if err := ctx.ShouldBindJSON(&body); err != nil {
|
||||
ctx.Status(http.StatusBadRequest)
|
||||
ctx.Error(err)
|
||||
return
|
||||
}
|
||||
request.Body = &body
|
||||
|
||||
handler := func(ctx *gin.Context, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.SignInEmailPassword(ctx, request.(SignInEmailPasswordRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "SignInEmailPassword")
|
||||
}
|
||||
|
||||
response, err := handler(ctx, request)
|
||||
|
||||
if err != nil {
|
||||
ctx.Error(err)
|
||||
ctx.Status(http.StatusInternalServerError)
|
||||
} else if validResponse, ok := response.(SignInEmailPasswordResponseObject); ok {
|
||||
if err := validResponse.VisitSignInEmailPasswordResponse(ctx.Writer); err != nil {
|
||||
ctx.Error(err)
|
||||
}
|
||||
} else if response != nil {
|
||||
ctx.Error(fmt.Errorf("unexpected response type: %T", response))
|
||||
}
|
||||
}
|
||||
|
||||
// ChangeUserEmail operation middleware
|
||||
func (sh *strictHandler) ChangeUserEmail(ctx *gin.Context) {
|
||||
var request ChangeUserEmailRequestObject
|
||||
|
||||
var body ChangeUserEmailJSONRequestBody
|
||||
if err := ctx.ShouldBindJSON(&body); err != nil {
|
||||
ctx.Status(http.StatusBadRequest)
|
||||
ctx.Error(err)
|
||||
return
|
||||
}
|
||||
request.Body = &body
|
||||
|
||||
handler := func(ctx *gin.Context, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.ChangeUserEmail(ctx, request.(ChangeUserEmailRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "ChangeUserEmail")
|
||||
}
|
||||
|
||||
response, err := handler(ctx, request)
|
||||
|
||||
if err != nil {
|
||||
ctx.Error(err)
|
||||
ctx.Status(http.StatusInternalServerError)
|
||||
} else if validResponse, ok := response.(ChangeUserEmailResponseObject); ok {
|
||||
if err := validResponse.VisitChangeUserEmailResponse(ctx.Writer); err != nil {
|
||||
ctx.Error(err)
|
||||
}
|
||||
} else if response != nil {
|
||||
ctx.Error(fmt.Errorf("unexpected response type: %T", response))
|
||||
}
|
||||
}
|
||||
|
||||
// Base64 encoded, gzipped, json marshaled Swagger object
|
||||
var swaggerSpec = []string{
|
||||
|
||||
"H4sIAAAAAAAC/9RYUXPbuBH+KxhcO30RJMVW0lhP9WV8rXLXS8Z22s7YfoCApYiYBHhY0IrO1X/vLEBK",
|
||||
"pEQ1vs5dp32yTBCL3f2+b3fBZ65cWTkLNiCfP3NUOZQy/rzy3vlrwMpZBHogtTbBOCuLj95V4IMB5PNM",
|
||||
"FggjrgGVNxWt8zm/CdJq6bX5GTQDMsR8a2nEq872Zx6X6UffRDyeKaeBGQ02mGxj7IqFHBhWoExmFJNV",
|
||||
"VRglaUc6hY842Lrk8zuuIZN1EYR3BYiyxiCWIIwVsijcGnR8jnzEtUG5LEALsLpyxobusxoh2iylKYQs",
|
||||
"PEi9ISN1jKP/+Am8yQxoPuKZ80ujNVghrbOb0tV0krEBvJWFQPBP4EXrsbFPsjBaJHOVRFw7rzsLHn6q",
|
||||
"AcmxwilZgLAutHFQOpsdIjgnMHc+dB8aK3KzrISWQS5l9NuDNh5UuHUHlmKu+o/QrGxdiTYjfMRr20ba",
|
||||
"pof+pG29aJPzlQy9UDIPmIvgHsF2ngejHqGX+jKTIrhQ8RG3Lv4SCMpD11qzHl/dVMn1zNWW3Iw7Wmyk",
|
||||
"CuYJOjsxyED/O1mHxhsBKpd2BSKTJkWaFivvMlOAyCCofGDxyegBMJNnSlryCcFqgSXyhxEnR/mcY/DG",
|
||||
"rvh2xEtAlCs4VsBf6lJakXkDVhebRkbt2yMOX2RZFWRrkc5kkUAsc76MOT86iYKuceCg29uPLC02p5Ds",
|
||||
"ukfMptOdPaLxCjzfbolJP9XGgybBNdb3AY0aae+DdsvPoAK58uH7F1eWVtAfvh9M3w0gxjB+UYH6hOAZ",
|
||||
"IUiVpakgmCwx5WyQxsZqQ8RAJq1mRHJmbMouGTmsY1IpQLyNxD5K8fu/3yZjBE/vYLtilx8XrNE49oCF",
|
||||
"zft8+WdlPpj3i08/L179aBa4sNev1bvFm8Vj9Y+/vXt/MR6Ph7DueHP1pTIecDHgVlxK0QdTAnNZLLBp",
|
||||
"c+OwocwoZ3XPtwtiREO1yIk3M35MEWJIFPyJtMTHlFrNgmPNu0cu9HJyps5fL99k50LNlhdi9hbOxcUf",
|
||||
"30qhZ3qavdKzMzibxfoXqNryOb+/X95NxYUU2cPz2+39/VLs/p1tT/7u7np1RtuGstyNbqGP41ukzmXA",
|
||||
"R9wpsF2Q/8uRHUi7S+0T1DpA+ig1Q0Xgxqzswl5R1frYtKvrptX9MjU3u4hDHWkBq5HklcoiabjTWw9m",
|
||||
"EHrlGD6qEn/A1oDWHrAv0M8ut2MsTcj/ZHOHYWwc78gimR3gzc6RU0d2PN2fdhP81K5iqr6h1n4x++fv",
|
||||
"qd7KLz+AXYWcz19PR7w0tv33/GvAtg7ujnsxTP/RWNhui2LojzuHxTjkMrBSbpixqqg1MLmrz86zv353",
|
||||
"yVQuiwLs6niixH1L+J2HjM/5N5P9mDtpZtxJ2zkoKUdBEw4x5HdxKHgpL/uOWFhfDTPrknVa9q9AqQNc",
|
||||
"dwcf40lzAKjam7C5oUQkT78F6cFf1sSaQ1/T2iFAaxNyRm2tW6nH7FNTy3s6bJsbLVTeBVCB7gXNwI3U",
|
||||
"wCIo5OcynraPMA+hIkD2Hl4V8CQD6Jd6GqnUZAcZNLtZBb40kQHYuE2sRLBoaFhkBGQ0gHsyNlbYngOs",
|
||||
"TSYrQWJNJ2CtciaReVBgw4E3Y/ad80xDkKZAhgCMAsT5ZKKdwnEL+aTyTtcq4IS2T1qnRcfpryeNsKaJ",
|
||||
"hc9tXRQj7iqwsjJ8zs/H0/E09ZI84j+hOd/YycEdZP7MK5dof8DfLrwyDUeRESEH4wdK7phdQ6g9DVN7",
|
||||
"IUdKHumZmYyFtROZVMEdoWmQgY1XBKLNDiTqvkOViidhAIZvnd5QIDTegU1S3t8fJ58xVYxUHb5aO063",
|
||||
"rpj2gWlTeYjTgCxwX/96OToIlXc1HXwNUeSpgkbQzqbT3zagpsgPRHR5MDrXsQhkdTFmiyzCucdpxGQH",
|
||||
"3LUpCrakmkB0AM2MxQBS0+y5o8aYxxPjDf5XC7H/QWMoqOYrAnNK1d6DZuvcFEAli6JrPz/4HdAjjnVZ",
|
||||
"Sr9puEezclTBwMhBb09IJ0ljk3TZPK2wzkyTXk1jcUd36U5yOJ+M2SVLF/T2w0hcbdOOVJKCi8YsrNtd",
|
||||
"8RhnM+PLuJSOJNH+u7J5JMDULHe98zcS34nePIDoj7DuZydi4qqmdrcfQtin6x86mmyg+W/Kr3MfHgjj",
|
||||
"quNWSz/QY3a5RzfkTPZxL4x9ZLlEtgSwp3D/v9FZ02X5/O55cBS4e9g+dOWYqJEa025mkiuk6Sh+tnrY",
|
||||
"brfbfwUAAP//3ciGL/8UAAA=",
|
||||
}
|
||||
|
||||
// GetSwagger returns the content of the embedded swagger specification file
|
||||
// or error if failed to decode
|
||||
func decodeSpec() ([]byte, error) {
|
||||
zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error base64 decoding spec: %w", err)
|
||||
}
|
||||
zr, err := gzip.NewReader(bytes.NewReader(zipped))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decompressing spec: %w", err)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
_, err = buf.ReadFrom(zr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decompressing spec: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var rawSpec = decodeSpecCached()
|
||||
|
||||
// a naive cached of a decoded swagger spec
|
||||
func decodeSpecCached() func() ([]byte, error) {
|
||||
data, err := decodeSpec()
|
||||
return func() ([]byte, error) {
|
||||
return data, err
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
|
||||
func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
|
||||
res := make(map[string]func() ([]byte, error))
|
||||
if len(pathToFile) > 0 {
|
||||
res[pathToFile] = rawSpec
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// GetSwagger returns the Swagger specification corresponding to the generated code
|
||||
// in this file. The external references of Swagger specification are resolved.
|
||||
// The logic of resolving external references is tightly connected to "import-mapping" feature.
|
||||
// Externally referenced files must be embedded in the corresponding golang packages.
|
||||
// Urls can be supported but this task was out of the scope.
|
||||
func GetSwagger() (swagger *openapi3.T, err error) {
|
||||
resolvePath := PathToRawSpec("")
|
||||
|
||||
loader := openapi3.NewLoader()
|
||||
loader.IsExternalRefsAllowed = true
|
||||
loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
|
||||
pathToFile := url.String()
|
||||
pathToFile = path.Clean(pathToFile)
|
||||
getSpec, ok := resolvePath[pathToFile]
|
||||
if !ok {
|
||||
err1 := fmt.Errorf("path not found: %s", pathToFile)
|
||||
return nil, err1
|
||||
}
|
||||
return getSpec()
|
||||
}
|
||||
var specData []byte
|
||||
specData, err = rawSpec()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
swagger, err = loader.LoadFromData(specData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package: api
|
||||
generate:
|
||||
models: true
|
||||
output: types.gen.go
|
||||
@@ -1,112 +0,0 @@
|
||||
// Package api provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version 2.5.0 DO NOT EDIT.
|
||||
package api
|
||||
|
||||
import (
|
||||
openapi_types "github.com/oapi-codegen/runtime/types"
|
||||
)
|
||||
|
||||
const (
|
||||
BearerAuthElevatedScopes = "BearerAuthElevated.Scopes"
|
||||
)
|
||||
|
||||
// Defines values for ErrorResponseError.
|
||||
const (
|
||||
CannotSendSms ErrorResponseError = "cannot-send-sms"
|
||||
DefaultRoleMustBeInAllowedRoles ErrorResponseError = "default-role-must-be-in-allowed-roles"
|
||||
DisabledEndpoint ErrorResponseError = "disabled-endpoint"
|
||||
DisabledMfaTotp ErrorResponseError = "disabled-mfa-totp"
|
||||
DisabledUser ErrorResponseError = "disabled-user"
|
||||
EmailAlreadyInUse ErrorResponseError = "email-already-in-use"
|
||||
EmailAlreadyVerified ErrorResponseError = "email-already-verified"
|
||||
ForbiddenAnonymous ErrorResponseError = "forbidden-anonymous"
|
||||
InternalServerError ErrorResponseError = "internal-server-error"
|
||||
InvalidEmailPassword ErrorResponseError = "invalid-email-password"
|
||||
InvalidOtp ErrorResponseError = "invalid-otp"
|
||||
InvalidPat ErrorResponseError = "invalid-pat"
|
||||
InvalidRefreshToken ErrorResponseError = "invalid-refresh-token"
|
||||
InvalidRequest ErrorResponseError = "invalid-request"
|
||||
InvalidState ErrorResponseError = "invalid-state"
|
||||
InvalidTicket ErrorResponseError = "invalid-ticket"
|
||||
InvalidTotp ErrorResponseError = "invalid-totp"
|
||||
LocaleNotAllowed ErrorResponseError = "locale-not-allowed"
|
||||
MfaTypeNotFound ErrorResponseError = "mfa-type-not-found"
|
||||
NoTotpSecret ErrorResponseError = "no-totp-secret"
|
||||
OauthProfileFetchFailed ErrorResponseError = "oauth-profile-fetch-failed"
|
||||
OauthProviderError ErrorResponseError = "oauth-provider-error"
|
||||
OauthTokenEchangeFailed ErrorResponseError = "oauth-token-echange-failed"
|
||||
PasswordInHibpDatabase ErrorResponseError = "password-in-hibp-database"
|
||||
PasswordTooShort ErrorResponseError = "password-too-short"
|
||||
RedirectToNotAllowed ErrorResponseError = "redirectTo-not-allowed"
|
||||
RoleNotAllowed ErrorResponseError = "role-not-allowed"
|
||||
SignupDisabled ErrorResponseError = "signup-disabled"
|
||||
TotpAlreadyActive ErrorResponseError = "totp-already-active"
|
||||
UnverifiedUser ErrorResponseError = "unverified-user"
|
||||
UserNotAnonymous ErrorResponseError = "user-not-anonymous"
|
||||
)
|
||||
|
||||
// Defines values for OKResponse.
|
||||
const (
|
||||
OK OKResponse = "OK"
|
||||
)
|
||||
|
||||
// ErrorResponse Standardized error response
|
||||
type ErrorResponse struct {
|
||||
// Error Error code identifying the specific application error
|
||||
Error ErrorResponseError `json:"error"`
|
||||
|
||||
// Message Human-friendly error message
|
||||
Message string `json:"message"`
|
||||
|
||||
// Status HTTP status error code
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// ErrorResponseError Error code identifying the specific application error
|
||||
type ErrorResponseError string
|
||||
|
||||
// OKResponse defines model for OKResponse.
|
||||
type OKResponse string
|
||||
|
||||
// Session User authentication session containing tokens and user information
|
||||
type Session struct {
|
||||
// AccessToken JWT token for authenticating API requests
|
||||
AccessToken string `json:"accessToken"`
|
||||
|
||||
// AccessTokenExpiresIn Expiration time of the access token in seconds
|
||||
AccessTokenExpiresIn int64 `json:"accessTokenExpiresIn"`
|
||||
|
||||
// RefreshToken Token used to refresh the access token
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
|
||||
// RefreshTokenId Identifier for the refresh token
|
||||
RefreshTokenId string `json:"refreshTokenId"`
|
||||
}
|
||||
|
||||
// SignInEmailPasswordRequest Request to authenticate using email and password
|
||||
type SignInEmailPasswordRequest struct {
|
||||
// Email User's email address
|
||||
Email openapi_types.Email `json:"email"`
|
||||
|
||||
// Password User's password
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// SignInEmailPasswordResponse Response for email-password authentication that may include a session or MFA challenge
|
||||
type SignInEmailPasswordResponse struct {
|
||||
// Session User authentication session containing tokens and user information
|
||||
Session *Session `json:"session,omitempty"`
|
||||
}
|
||||
|
||||
// UserEmailChangeRequest defines model for UserEmailChangeRequest.
|
||||
type UserEmailChangeRequest struct {
|
||||
// NewEmail A valid email
|
||||
NewEmail openapi_types.Email `json:"newEmail"`
|
||||
}
|
||||
|
||||
// SignInEmailPasswordJSONRequestBody defines body for SignInEmailPassword for application/json ContentType.
|
||||
type SignInEmailPasswordJSONRequestBody = SignInEmailPasswordRequest
|
||||
|
||||
// ChangeUserEmailJSONRequestBody defines body for ChangeUserEmail for application/json ContentType.
|
||||
type ChangeUserEmailJSONRequestBody = UserEmailChangeRequest
|
||||
@@ -1,49 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/nhost/nhost/internal/lib/oapi/example/api"
|
||||
)
|
||||
|
||||
type Controller struct{}
|
||||
|
||||
func NewController() *Controller {
|
||||
return &Controller{}
|
||||
}
|
||||
|
||||
func (c *Controller) SignInEmailPassword( //nolint:ireturn
|
||||
_ context.Context, req api.SignInEmailPasswordRequestObject,
|
||||
) (api.SignInEmailPasswordResponseObject, error) {
|
||||
switch req.Body.Email {
|
||||
case "bad@email.com":
|
||||
return api.SignInEmailPassworddefaultJSONResponse{
|
||||
Body: api.ErrorResponse{
|
||||
Error: api.DisabledUser,
|
||||
Message: "The user account is disabled.",
|
||||
Status: http.StatusConflict,
|
||||
},
|
||||
StatusCode: http.StatusConflict,
|
||||
}, nil
|
||||
case "crash@email.com":
|
||||
return nil, errors.New("simulated server crash") //nolint:err113
|
||||
}
|
||||
|
||||
return api.SignInEmailPassword200JSONResponse{
|
||||
Session: &api.Session{
|
||||
AccessToken: "access_token_example",
|
||||
AccessTokenExpiresIn: 900, //nolint:mnd
|
||||
RefreshToken: "refresh_token_example",
|
||||
RefreshTokenId: "refresh_token_id_example",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Controller) ChangeUserEmail( //nolint:ireturn
|
||||
_ context.Context,
|
||||
_ api.ChangeUserEmailRequestObject,
|
||||
) (api.ChangeUserEmailResponseObject, error) {
|
||||
return api.ChangeUserEmail200JSONResponse(api.OK), nil
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3filter"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/lmittmann/tint"
|
||||
"github.com/nhost/nhost/internal/lib/oapi"
|
||||
"github.com/nhost/nhost/internal/lib/oapi/example/api"
|
||||
"github.com/nhost/nhost/internal/lib/oapi/example/controller"
|
||||
"github.com/nhost/nhost/internal/lib/oapi/middleware"
|
||||
)
|
||||
|
||||
const apiPrefix = "/"
|
||||
|
||||
func getLogger() *slog.Logger {
|
||||
handler := tint.NewHandler(os.Stdout, &tint.Options{
|
||||
AddSource: true,
|
||||
Level: slog.LevelDebug,
|
||||
TimeFormat: time.StampMilli,
|
||||
NoColor: false,
|
||||
ReplaceAttr: nil,
|
||||
})
|
||||
|
||||
return slog.New(handler)
|
||||
}
|
||||
|
||||
func authFn(
|
||||
ctx context.Context,
|
||||
input *openapi3filter.AuthenticationInput,
|
||||
) error {
|
||||
_, ok := ctx.Value(oapi.GinContextKey).(*gin.Context)
|
||||
if !ok {
|
||||
return &oapi.AuthenticatorError{
|
||||
Scheme: input.SecuritySchemeName,
|
||||
Code: "unauthorized",
|
||||
Message: "unable to get context",
|
||||
}
|
||||
}
|
||||
|
||||
return &oapi.AuthenticatorError{
|
||||
Scheme: input.SecuritySchemeName,
|
||||
Code: "unauthorized",
|
||||
Message: "your access token is invalid",
|
||||
}
|
||||
}
|
||||
|
||||
func setupRouter(logger *slog.Logger) (*gin.Engine, error) {
|
||||
ctrl := controller.NewController()
|
||||
handler := api.NewStrictHandler(ctrl, []api.StrictMiddlewareFunc{})
|
||||
|
||||
router, mw, err := oapi.NewRouter(
|
||||
api.OpenAPISchema,
|
||||
apiPrefix,
|
||||
authFn,
|
||||
middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"*"},
|
||||
},
|
||||
logger,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create oapi router: %w", err)
|
||||
}
|
||||
|
||||
api.RegisterHandlersWithOptions(
|
||||
router,
|
||||
handler,
|
||||
api.GinServerOptions{
|
||||
BaseURL: apiPrefix,
|
||||
Middlewares: []api.MiddlewareFunc{mw},
|
||||
ErrorHandler: nil,
|
||||
},
|
||||
)
|
||||
|
||||
return router, nil
|
||||
}
|
||||
|
||||
func run(ctx context.Context) error {
|
||||
logger := getLogger()
|
||||
|
||||
router, err := setupRouter(logger) //nolint:contextcheck
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
server := &http.Server{ //nolint:exhaustruct
|
||||
Addr: ":8080",
|
||||
Handler: router,
|
||||
ReadHeaderTimeout: 5 * time.Second, //nolint:mnd
|
||||
}
|
||||
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
logger.ErrorContext(ctx, "server failed", slog.String("error", err.Error()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := run(context.Background()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func makeRequest(
|
||||
router *gin.Engine,
|
||||
method, path string,
|
||||
headers map[string]string,
|
||||
body io.Reader,
|
||||
) *httptest.ResponseRecorder {
|
||||
req := httptest.NewRequest(method, path, body)
|
||||
|
||||
for key, value := range headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func TestRequests(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
logger := getLogger()
|
||||
|
||||
router, err := setupRouter(logger)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set up router: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
method string
|
||||
path string
|
||||
headers map[string]string
|
||||
body io.Reader
|
||||
expectedStatus int
|
||||
expectedResponse string
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
method: http.MethodPost,
|
||||
path: "/signin/email-password",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: strings.NewReader(`{"email": "asd@asd.com", "password": "p4ssw0rd"}`),
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedResponse: "{\"session\":{\"accessToken\":\"access_token_example\",\"accessTokenExpiresIn\":900,\"refreshToken\":\"refresh_token_example\",\"refreshTokenId\":\"refresh_token_id_example\"}}\n", //nolint:lll
|
||||
},
|
||||
|
||||
{
|
||||
name: "expected error",
|
||||
method: http.MethodPost,
|
||||
path: "/signin/email-password",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: strings.NewReader(
|
||||
`{"email": "bad@email.com", "password": "p4ssw0rd"}`,
|
||||
),
|
||||
expectedStatus: http.StatusConflict,
|
||||
expectedResponse: "{\"error\":\"disabled-user\",\"message\":\"The user account is disabled.\",\"status\":409}\n", //nolint:lll
|
||||
},
|
||||
|
||||
{
|
||||
name: "unexpected error",
|
||||
method: http.MethodPost,
|
||||
path: "/signin/email-password",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: strings.NewReader(
|
||||
`{"email": "crash@email.com", "password": "p4ssw0rd"}`,
|
||||
),
|
||||
expectedStatus: http.StatusInternalServerError,
|
||||
expectedResponse: `{"errors":"internal-server-error","message":"simulated server crash"}`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "missing body",
|
||||
method: http.MethodPost,
|
||||
path: "/signin/email-password",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: nil,
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
expectedResponse: `{"error":"request-validation-error","reason":"value is required but missing"}`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "wrong param",
|
||||
method: http.MethodPost,
|
||||
path: "/signin/email-password",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: strings.NewReader(
|
||||
`{"wrong":"asd", "email": "asd@asd.com", "password": "p4ssw0rd"}`,
|
||||
),
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
expectedResponse: `{"error":"schema-validation-error","reason":"property \"wrong\" is unsupported"}`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "missing param",
|
||||
method: http.MethodPost,
|
||||
path: "/signin/email-password",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: strings.NewReader(`{"email": "asd@asd.com"}`),
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
expectedResponse: `{"error":"schema-validation-error","reason":"property \"password\" is missing"}`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "invalid param",
|
||||
method: http.MethodPost,
|
||||
path: "/signin/email-password",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: strings.NewReader(`{"email": "asdasd.com", "password": "p4ssw0rd"}`),
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
expectedResponse: `{"errors":"bad-request","message":"email: failed to pass regex validation"}`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "needs security",
|
||||
method: http.MethodPost,
|
||||
path: "/user/email/change",
|
||||
headers: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: strings.NewReader(`{"newEmail": "new@asd.com"`),
|
||||
expectedStatus: http.StatusUnauthorized,
|
||||
expectedResponse: `{"error":"unauthorized","reason":"your access token is invalid","securityScheme":"BearerAuthElevated"}`, //nolint:lll
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
w := makeRequest(router, tc.method, tc.path, tc.headers, tc.body)
|
||||
|
||||
resp := w.Result()
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read response body: %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != tc.expectedStatus {
|
||||
t.Errorf("Expected status %d, got %d", tc.expectedStatus, resp.StatusCode)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(string(body), tc.expectedResponse); diff != "" {
|
||||
t.Errorf("Response body mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CORSOptions configures the CORS middleware behavior.
|
||||
//
|
||||
// The middleware supports three strategies for handling Access-Control-Allow-Headers:
|
||||
// - nil (default): Reflects the Access-Control-Request-Headers from the client
|
||||
// - empty slice: Denies all headers (no Access-Control-Allow-Headers header is set)
|
||||
// - non-empty slice: Uses the specified headers
|
||||
type CORSOptions struct {
|
||||
// AllowedOrigins is a list of origins permitted to make cross-origin requests.
|
||||
// Use "*" or nil slice to allow all origins.
|
||||
AllowedOrigins []string
|
||||
|
||||
// AllowedMethods is a list of HTTP methods the client is permitted to use.
|
||||
// Common values: GET, POST, PUT, DELETE, PATCH, OPTIONS.
|
||||
AllowedMethods []string
|
||||
|
||||
// AllowedHeaders controls which headers clients can use in requests.
|
||||
// - nil: reflects client's Access-Control-Request-Headers (permissive)
|
||||
// - empty slice: denies all headers
|
||||
// - non-empty: allows only specified headers
|
||||
AllowedHeaders []string
|
||||
|
||||
// ExposedHeaders lists headers that browsers are allowed to access.
|
||||
// By default, browsers only expose simple response headers.
|
||||
ExposedHeaders []string
|
||||
|
||||
// AllowCredentials indicates whether the request can include credentials
|
||||
// (cookies, authorization headers, or TLS client certificates).
|
||||
AllowCredentials bool
|
||||
|
||||
// MaxAge indicates how long (in seconds) the results of a preflight request
|
||||
// can be cached. Empty string means no caching directive is sent.
|
||||
MaxAge string
|
||||
}
|
||||
|
||||
// CORS returns a Gin middleware handler that implements Cross-Origin Resource Sharing (CORS).
|
||||
//
|
||||
// The middleware handles both preflight (OPTIONS) requests and actual requests, setting
|
||||
// appropriate CORS headers based on the provided configuration. It automatically adds
|
||||
// the "Vary: Origin, Access-Control-Request-Method" header for proper cache behavior.
|
||||
//
|
||||
// For preflight requests (OPTIONS), the middleware responds with 204 No Content and
|
||||
// prevents further request processing. For actual requests, it sets CORS headers and
|
||||
// continues the middleware chain.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// router.Use(middleware.CORS(middleware.CORSOptions{
|
||||
// AllowedOrigins: []string{"https://example.com", "https://app.example.com"},
|
||||
// AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
|
||||
// AllowedHeaders: nil, // reflects client headers
|
||||
// AllowCredentials: true,
|
||||
// MaxAge: "3600",
|
||||
// }))
|
||||
func CORS(opts CORSOptions) gin.HandlerFunc { //nolint:cyclop,funlen
|
||||
allowedMethods := strings.Join(opts.AllowedMethods, ", ")
|
||||
exposedHeaders := strings.Join(opts.ExposedHeaders, ", ")
|
||||
|
||||
allowCredentials := "false"
|
||||
if opts.AllowCredentials {
|
||||
allowCredentials = "true"
|
||||
}
|
||||
|
||||
var (
|
||||
headerStrategy string // "reflect", "specific", or "deny"
|
||||
allowedHeaders string
|
||||
)
|
||||
switch {
|
||||
case opts.AllowedHeaders == nil:
|
||||
headerStrategy = "reflect"
|
||||
case len(opts.AllowedHeaders) == 0:
|
||||
headerStrategy = "deny"
|
||||
default:
|
||||
headerStrategy = "specific"
|
||||
allowedHeaders = strings.Join(opts.AllowedHeaders, ", ")
|
||||
}
|
||||
|
||||
f := func(c *gin.Context, origin string) {
|
||||
if opts.AllowedOrigins != nil &&
|
||||
!slices.Contains(opts.AllowedOrigins, origin) &&
|
||||
!slices.Contains(opts.AllowedOrigins, "*") {
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Access-Control-Allow-Origin", origin)
|
||||
c.Header("Access-Control-Allow-Methods", allowedMethods)
|
||||
|
||||
// Handle allowed headers based on strategy
|
||||
switch headerStrategy {
|
||||
case "specific":
|
||||
c.Header("Access-Control-Allow-Headers", allowedHeaders)
|
||||
case "reflect":
|
||||
headers := c.Request.Header.Get("Access-Control-Request-Headers")
|
||||
if headers != "" {
|
||||
c.Header("Access-Control-Allow-Headers", headers)
|
||||
}
|
||||
case "deny":
|
||||
// Don't set the header at all
|
||||
}
|
||||
|
||||
if exposedHeaders != "" {
|
||||
c.Header("Access-Control-Expose-Headers", exposedHeaders)
|
||||
}
|
||||
|
||||
c.Header("Access-Control-Allow-Credentials", allowCredentials)
|
||||
|
||||
if opts.MaxAge != "" {
|
||||
c.Header("Access-Control-Max-Age", opts.MaxAge)
|
||||
}
|
||||
|
||||
c.Writer.Header().Add("Vary", "Origin, Access-Control-Request-Method")
|
||||
}
|
||||
|
||||
return func(c *gin.Context) {
|
||||
origin := c.Request.Header.Get("Origin")
|
||||
if c.Request.Method == http.MethodOptions {
|
||||
f(c, origin)
|
||||
|
||||
c.Header("Content-Length", "0")
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if origin != "" {
|
||||
f(c, origin)
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
package middleware_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/nhost/nhost/internal/lib/oapi/middleware"
|
||||
)
|
||||
|
||||
func TestCORS(t *testing.T) { //nolint:maintidx
|
||||
t.Parallel()
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
opts middleware.CORSOptions
|
||||
requestMethod string
|
||||
requestOrigin string
|
||||
requestHeaders map[string]string
|
||||
wantStatus int
|
||||
wantHeaders http.Header
|
||||
expectNext bool
|
||||
}{
|
||||
{
|
||||
name: "OPTIONS request with allowed origin",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"GET", "POST"},
|
||||
AllowedHeaders: []string{"Content-Type", "Authorization"},
|
||||
},
|
||||
requestMethod: "OPTIONS",
|
||||
requestHeaders: map[string]string{},
|
||||
requestOrigin: "https://example.com",
|
||||
wantStatus: http.StatusNoContent,
|
||||
wantHeaders: http.Header{
|
||||
"Access-Control-Allow-Origin": []string{"https://example.com"},
|
||||
"Access-Control-Allow-Methods": []string{"GET, POST"},
|
||||
"Access-Control-Allow-Headers": []string{"Content-Type, Authorization"},
|
||||
"Access-Control-Allow-Credentials": []string{"false"},
|
||||
"Vary": []string{
|
||||
"Origin, Access-Control-Request-Method",
|
||||
},
|
||||
"Content-Length": []string{"0"},
|
||||
},
|
||||
expectNext: false,
|
||||
},
|
||||
{
|
||||
name: "OPTIONS request with wildcard origin",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
|
||||
},
|
||||
requestMethod: "OPTIONS",
|
||||
requestHeaders: map[string]string{},
|
||||
requestOrigin: "https://any-origin.com",
|
||||
wantStatus: http.StatusNoContent,
|
||||
wantHeaders: http.Header{
|
||||
"Access-Control-Allow-Origin": []string{"https://any-origin.com"},
|
||||
"Access-Control-Allow-Methods": []string{"GET, POST, PUT, DELETE"},
|
||||
},
|
||||
expectNext: false,
|
||||
},
|
||||
{
|
||||
name: "OPTIONS request with disallowed origin",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"GET", "POST"},
|
||||
},
|
||||
requestMethod: "OPTIONS",
|
||||
requestHeaders: map[string]string{},
|
||||
requestOrigin: "https://malicious.com",
|
||||
wantStatus: http.StatusNoContent,
|
||||
wantHeaders: http.Header{},
|
||||
expectNext: false,
|
||||
},
|
||||
{
|
||||
name: "OPTIONS request with reflected headers (nil)",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"POST"},
|
||||
AllowedHeaders: nil,
|
||||
},
|
||||
requestMethod: "OPTIONS",
|
||||
requestOrigin: "https://example.com",
|
||||
requestHeaders: map[string]string{
|
||||
"Access-Control-Request-Headers": "X-Custom-Header, X-Another-Header",
|
||||
},
|
||||
wantStatus: http.StatusNoContent,
|
||||
wantHeaders: http.Header{
|
||||
"Access-Control-Allow-Headers": []string{"X-Custom-Header, X-Another-Header"},
|
||||
},
|
||||
expectNext: false,
|
||||
},
|
||||
{
|
||||
name: "OPTIONS request with denied headers (empty slice)",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"POST"},
|
||||
AllowedHeaders: []string{},
|
||||
},
|
||||
requestMethod: "OPTIONS",
|
||||
requestOrigin: "https://example.com",
|
||||
requestHeaders: map[string]string{
|
||||
"Access-Control-Request-Headers": "X-Custom-Header, X-Another-Header",
|
||||
},
|
||||
wantStatus: http.StatusNoContent,
|
||||
wantHeaders: http.Header{},
|
||||
expectNext: false,
|
||||
},
|
||||
{
|
||||
name: "OPTIONS request with nil headers and no request headers",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"GET"},
|
||||
AllowedHeaders: nil,
|
||||
},
|
||||
requestMethod: "OPTIONS",
|
||||
requestOrigin: "https://example.com",
|
||||
requestHeaders: map[string]string{},
|
||||
wantStatus: http.StatusNoContent,
|
||||
wantHeaders: http.Header{},
|
||||
expectNext: false,
|
||||
},
|
||||
{
|
||||
name: "OPTIONS request with credentials enabled",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"GET"},
|
||||
AllowCredentials: true,
|
||||
},
|
||||
requestMethod: "OPTIONS",
|
||||
requestOrigin: "https://example.com",
|
||||
requestHeaders: map[string]string{},
|
||||
wantStatus: http.StatusNoContent,
|
||||
wantHeaders: http.Header{
|
||||
"Access-Control-Allow-Credentials": []string{"true"},
|
||||
},
|
||||
expectNext: false,
|
||||
},
|
||||
{
|
||||
name: "OPTIONS request with MaxAge",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"GET"},
|
||||
MaxAge: "3600",
|
||||
},
|
||||
requestMethod: "OPTIONS",
|
||||
requestOrigin: "https://example.com",
|
||||
requestHeaders: map[string]string{},
|
||||
wantStatus: http.StatusNoContent,
|
||||
wantHeaders: http.Header{
|
||||
"Access-Control-Max-Age": []string{"3600"},
|
||||
},
|
||||
expectNext: false,
|
||||
},
|
||||
{
|
||||
name: "OPTIONS request with exposed headers",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"GET"},
|
||||
ExposedHeaders: []string{"X-Custom-Response", "X-Total-Count"},
|
||||
},
|
||||
requestMethod: "OPTIONS",
|
||||
requestOrigin: "https://example.com",
|
||||
requestHeaders: map[string]string{},
|
||||
wantStatus: http.StatusNoContent,
|
||||
wantHeaders: http.Header{
|
||||
"Access-Control-Expose-Headers": []string{"X-Custom-Response, X-Total-Count"},
|
||||
},
|
||||
expectNext: false,
|
||||
},
|
||||
{
|
||||
name: "GET request with allowed origin",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"GET", "POST"},
|
||||
AllowedHeaders: []string{"Content-Type"},
|
||||
},
|
||||
requestMethod: "GET",
|
||||
requestOrigin: "https://example.com",
|
||||
requestHeaders: map[string]string{},
|
||||
wantStatus: http.StatusOK,
|
||||
wantHeaders: http.Header{
|
||||
"Access-Control-Allow-Origin": []string{"https://example.com"},
|
||||
"Access-Control-Allow-Methods": []string{"GET, POST"},
|
||||
"Access-Control-Allow-Headers": []string{"Content-Type"},
|
||||
},
|
||||
expectNext: true,
|
||||
},
|
||||
{
|
||||
name: "POST request with disallowed origin",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"GET", "POST"},
|
||||
},
|
||||
requestMethod: "POST",
|
||||
requestOrigin: "https://malicious.com",
|
||||
requestHeaders: map[string]string{},
|
||||
wantStatus: http.StatusOK,
|
||||
wantHeaders: http.Header{},
|
||||
expectNext: true,
|
||||
},
|
||||
{
|
||||
name: "GET request without origin header",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"GET"},
|
||||
},
|
||||
requestMethod: "GET",
|
||||
requestOrigin: "",
|
||||
requestHeaders: map[string]string{},
|
||||
wantStatus: http.StatusOK,
|
||||
wantHeaders: http.Header{},
|
||||
expectNext: true,
|
||||
},
|
||||
{
|
||||
name: "GET request with empty allowed origins (denies all)",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{},
|
||||
AllowedMethods: []string{"GET"},
|
||||
},
|
||||
requestMethod: "GET",
|
||||
requestOrigin: "https://any-origin.com",
|
||||
requestHeaders: map[string]string{},
|
||||
wantStatus: http.StatusOK,
|
||||
wantHeaders: http.Header{},
|
||||
expectNext: true,
|
||||
},
|
||||
{
|
||||
name: "GET request with nil allowed origins (allows all)",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: nil,
|
||||
AllowedMethods: []string{"GET"},
|
||||
},
|
||||
requestMethod: "GET",
|
||||
requestOrigin: "https://any-origin.com",
|
||||
requestHeaders: map[string]string{},
|
||||
wantStatus: http.StatusOK,
|
||||
wantHeaders: http.Header{
|
||||
"Access-Control-Allow-Origin": []string{"https://any-origin.com"},
|
||||
},
|
||||
expectNext: true,
|
||||
},
|
||||
{
|
||||
name: "GET request with multiple allowed origins",
|
||||
opts: middleware.CORSOptions{ //nolint:exhaustruct
|
||||
AllowedOrigins: []string{
|
||||
"https://example.com",
|
||||
"https://another-example.com",
|
||||
"https://third-example.com",
|
||||
},
|
||||
AllowedMethods: []string{"GET"},
|
||||
},
|
||||
requestMethod: "GET",
|
||||
requestHeaders: map[string]string{},
|
||||
requestOrigin: "https://another-example.com",
|
||||
wantStatus: http.StatusOK,
|
||||
wantHeaders: http.Header{
|
||||
"Access-Control-Allow-Origin": []string{"https://another-example.com"},
|
||||
},
|
||||
expectNext: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup router with CORS middleware
|
||||
router := gin.New()
|
||||
nextCalled := false
|
||||
|
||||
router.Use(middleware.CORS(tc.opts))
|
||||
router.Any("/test", func(c *gin.Context) {
|
||||
nextCalled = true
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
})
|
||||
|
||||
// Create request
|
||||
req := httptest.NewRequest(tc.requestMethod, "/test", nil)
|
||||
if tc.requestOrigin != "" {
|
||||
req.Header.Set("Origin", tc.requestOrigin)
|
||||
}
|
||||
|
||||
// Add any additional request headers
|
||||
for key, value := range tc.requestHeaders {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
|
||||
// Record response
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// Check status code
|
||||
if w.Code != tc.wantStatus {
|
||||
t.Errorf("expected status %d, got %d", tc.wantStatus, w.Code)
|
||||
}
|
||||
|
||||
// Check expected headers using cmp.Diff
|
||||
// Only compare headers that are expected
|
||||
gotHeaders := make(http.Header)
|
||||
for key := range tc.wantHeaders {
|
||||
if values := w.Header().Values(key); len(values) > 0 {
|
||||
gotHeaders[key] = values
|
||||
}
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tc.wantHeaders, gotHeaders); diff != "" {
|
||||
t.Errorf("response headers mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
// Check if Next() was called
|
||||
if nextCalled != tc.expectNext {
|
||||
t.Errorf("expected Next() called to be %v, got %v", tc.expectNext, nextCalled)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package oapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/getkin/kin-openapi/openapi3filter"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/nhost/nhost/internal/lib/oapi/example/api"
|
||||
"github.com/nhost/nhost/internal/lib/oapi/middleware"
|
||||
)
|
||||
|
||||
func surfaceErrorsMiddleWare(c *gin.Context) {
|
||||
// this captures two cases as far as I can see:
|
||||
// 1. request validation errors where the strict generated code fails
|
||||
// to bind the request to the struct (i.e. "invalid param" test)
|
||||
// 2. when a handler returns an error instead of a response
|
||||
c.Next()
|
||||
|
||||
if len(c.Errors) > 0 && !c.IsAborted() {
|
||||
var errorCode string
|
||||
switch c.Writer.Status() {
|
||||
case http.StatusBadRequest:
|
||||
errorCode = "bad-request"
|
||||
default:
|
||||
errorCode = "internal-server-error"
|
||||
}
|
||||
|
||||
c.JSON(
|
||||
c.Writer.Status(),
|
||||
gin.H{"errors": errorCode, "message": c.Errors[0].Error()},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// NewRouter creates a Gin router with OpenAPI request validation middleware.
|
||||
func NewRouter(
|
||||
schema []byte,
|
||||
apiPrefix string,
|
||||
authenticationFunc openapi3filter.AuthenticationFunc,
|
||||
corsOptions middleware.CORSOptions,
|
||||
logger *slog.Logger,
|
||||
) (*gin.Engine, func(c *gin.Context), error) {
|
||||
router := gin.New()
|
||||
|
||||
loader := openapi3.NewLoader()
|
||||
|
||||
doc, err := loader.LoadFromData(schema)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to load OpenAPI schema: %w", err)
|
||||
}
|
||||
|
||||
doc.AddServer(&openapi3.Server{ //nolint:exhaustruct
|
||||
URL: apiPrefix,
|
||||
})
|
||||
|
||||
router.Use(
|
||||
gin.Recovery(),
|
||||
surfaceErrorsMiddleWare,
|
||||
middleware.Logger(logger),
|
||||
middleware.CORS(corsOptions),
|
||||
)
|
||||
|
||||
mw := api.MiddlewareFunc(requestValidatorWithOptions(doc, authenticationFunc))
|
||||
|
||||
return router, mw, nil
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package oapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/getkin/kin-openapi/openapi3filter"
|
||||
"github.com/getkin/kin-openapi/routers"
|
||||
"github.com/getkin/kin-openapi/routers/gorillamux"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type ContextKey string
|
||||
|
||||
const (
|
||||
GinContextKey ContextKey = "nhost-oapi/gin-context"
|
||||
)
|
||||
|
||||
func handleError(c *gin.Context, err error) {
|
||||
var (
|
||||
errReq *openapi3filter.RequestError
|
||||
errSchema *openapi3.SchemaError
|
||||
errAuth *AuthenticatorError
|
||||
errSec *openapi3filter.SecurityRequirementsError
|
||||
)
|
||||
switch {
|
||||
case errors.As(err, &errSchema):
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"error": "schema-validation-error",
|
||||
"reason": errSchema.Reason,
|
||||
})
|
||||
case errors.As(err, &errReq):
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"error": "request-validation-error",
|
||||
"reason": errReq.Err.Error(),
|
||||
})
|
||||
case errors.As(err, &errAuth):
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
||||
"error": errAuth.Code,
|
||||
"reason": errAuth.Message,
|
||||
"securityScheme": errAuth.Scheme,
|
||||
})
|
||||
case errors.As(err, &errSec):
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
||||
"error": "unauthorized",
|
||||
"reason": errSec.Error(),
|
||||
})
|
||||
default:
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
func requestValidatorWithOptions(
|
||||
swagger *openapi3.T,
|
||||
authFn openapi3filter.AuthenticationFunc,
|
||||
) gin.HandlerFunc {
|
||||
router, err := gorillamux.NewRouter(swagger)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return func(c *gin.Context) {
|
||||
if err := validateRequestFromContext(c, router, authFn); err != nil {
|
||||
handleError(c, err)
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func validateRequestFromContext(
|
||||
c *gin.Context,
|
||||
router routers.Router,
|
||||
authFn openapi3filter.AuthenticationFunc,
|
||||
) error {
|
||||
route, pathParams, err := router.FindRoute(c.Request)
|
||||
if err != nil {
|
||||
var e *routers.RouteError
|
||||
switch {
|
||||
case errors.As(err, &e):
|
||||
return e
|
||||
default:
|
||||
return fmt.Errorf("error validating route: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
validationInput := &openapi3filter.RequestValidationInput{ //nolint:exhaustruct
|
||||
Request: c.Request,
|
||||
PathParams: pathParams,
|
||||
Route: route,
|
||||
Options: &openapi3filter.Options{
|
||||
AuthenticationFunc: authFn,
|
||||
ExcludeRequestBody: false,
|
||||
ExcludeRequestQueryParams: false,
|
||||
ExcludeResponseBody: false,
|
||||
ExcludeReadOnlyValidations: false,
|
||||
ExcludeWriteOnlyValidations: false,
|
||||
IncludeResponseStatus: false,
|
||||
MultiError: false,
|
||||
RegexCompiler: nil,
|
||||
SkipSettingDefaults: false,
|
||||
},
|
||||
}
|
||||
|
||||
requestContext := context.WithValue(c.Request.Context(), GinContextKey, c)
|
||||
if err := openapi3filter.ValidateRequest(requestContext, validationInput); err != nil {
|
||||
return err //nolint:wrapcheck
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetGinContext(c context.Context) *gin.Context {
|
||||
v := c.Value(GinContextKey)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ginCtx, ok := v.(*gin.Context)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ginCtx
|
||||
}
|
||||
@@ -114,13 +114,13 @@ in
|
||||
echo "➜ Running golangci-lint"
|
||||
golangci-lint run \
|
||||
--timeout 600s \
|
||||
./...
|
||||
./${submodule}/...
|
||||
|
||||
echo "➜ Running tests"
|
||||
richgo test \
|
||||
-tags="${pkgs.lib.strings.concatStringsSep " " tags}" \
|
||||
-ldflags="${pkgs.lib.strings.concatStringsSep " " ldflags}" \
|
||||
-v ${goTestFlags} ./...
|
||||
-v ${goTestFlags} ./${submodule}/...
|
||||
|
||||
${extraCheck}
|
||||
|
||||
|
||||
@@ -1,24 +1,3 @@
|
||||
## [@nhost/nhost-js@4.1.0] - 2025-11-04
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- *(nhost-js)* Added pushChainFunction to functions and graphql clients (#3610)
|
||||
- *(nhost-js)* Added various middlewares to work with headers and customizable createNhostClient (#3612)
|
||||
- *(auth)* Added endpoints to retrieve and refresh oauth2 providers' tokens (#3614)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(dashboard)* Run audit and lint in dashboard (#3578)
|
||||
- *(nhost-js)* Improvements to Session guard to avoid conflict with ProviderSession (#3662)
|
||||
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(nhost-js)* Generate code from local API definitions (#3583)
|
||||
- *(docs)* Udpated README.md and CONTRIBUTING.md (#3587)
|
||||
- *(nhost-js)* Regenerate types (#3648)
|
||||
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
|
Before Width: | Height: | Size: 559 KiB After Width: | Height: | Size: 559 KiB |
@@ -1,7 +1,7 @@
|
||||
import { createServer } from 'http'
|
||||
import { Context, createStripeGraphQLServer } from '../src/index'
|
||||
import { type Context, createStripeGraphQLServer } from '../src/index'
|
||||
|
||||
const isAllowed = (stripeCustomerId: string, context: Context) => {
|
||||
const isAllowed = (_stripeCustomerId: string, context: Context) => {
|
||||
const { isAdmin } = context
|
||||
|
||||
if (isAdmin) {
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/stripe-graphql-js",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.0-beta.6",
|
||||
"description": "Stripe GraphQL API",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -17,8 +17,8 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/nhost/nhost.git"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"main": "dist/src/index.js",
|
||||
"types": "dist/src/index.d.ts",
|
||||
"source": "src/index.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -43,15 +43,16 @@
|
||||
"@pothos/core": "^3.41.0",
|
||||
"graphql": "16.8.1",
|
||||
"graphql-scalars": "^1.23.0",
|
||||
"graphql-yoga": "^3.9.1",
|
||||
"graphql-yoga": "^5.16.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"stripe": "^11.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jsonwebtoken": "^9.0.6",
|
||||
"@types/node": "^18.19.28",
|
||||
"@types/node": "^20.14.8",
|
||||
"dotenv": "^16.4.5",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^4.9.5"
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
1096
packages/stripe-graphql-js/pnpm-lock.yaml
generated
Normal file
1096
packages/stripe-graphql-js/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user