Compare commits
47 Commits
auth@0.43.
...
cli@1.34.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5956f1b2e | ||
|
|
f3b397b0d8 | ||
|
|
b7940087ee | ||
|
|
3dae655858 | ||
|
|
2aa269734b | ||
|
|
bc91836f83 | ||
|
|
6d8b243571 | ||
|
|
c9967b1a6d | ||
|
|
7f72aadff9 | ||
|
|
8faf9565bb | ||
|
|
7ac3f12852 | ||
|
|
184a3ed190 | ||
|
|
372c4e32d4 | ||
|
|
a68d261d8e | ||
|
|
55bda3f56b | ||
|
|
2311e1dd77 | ||
|
|
824ee142c4 | ||
|
|
c662d063a7 | ||
|
|
b518132349 | ||
|
|
b677d3768f | ||
|
|
51ec151752 | ||
|
|
223322d654 | ||
|
|
add2c20c95 | ||
|
|
961bc5feea | ||
|
|
0ca89974b9 | ||
|
|
e8d52859a3 | ||
|
|
67740ebe3d | ||
|
|
d6f7b01aee | ||
|
|
0fc65df78d | ||
|
|
52e3db7f61 | ||
|
|
235449d68c | ||
|
|
323834d212 | ||
|
|
f7bd250f73 | ||
|
|
579f9dbf31 | ||
|
|
9f2b93d44b | ||
|
|
1aeef26ec6 | ||
|
|
749bb4e637 | ||
|
|
accabc83f7 | ||
|
|
8c127d7b6b | ||
|
|
f9c614ef99 | ||
|
|
1d183f7fc4 | ||
|
|
46e740f060 | ||
|
|
0d30ab4eec | ||
|
|
d5fd3cb59c | ||
|
|
f36d360b9e | ||
|
|
61af5087fd | ||
|
|
7429d8ae3f |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,6 +7,8 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
> **Note:** Bug reports that are clearly AI-generated will not be accepted and will be closed immediately. Please write your bug report in your own words.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,6 +7,8 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
> **Note:** Feature requests that are clearly AI-generated will not be accepted and will be closed immediately. Please write your feature request in your own words.
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
|
||||
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -8,6 +8,8 @@
|
||||
|
||||
--- Delete everything below this line before submitting your PR ---
|
||||
|
||||
> **Note on AI-assisted contributions:** Contributions with the help of AI are permitted, but you are ultimately responsible for the quality of your submission and for ensuring it follows our contributing guidelines. **The PR description must be written in your own words and be clear and concise**. Please ensure you remove any superfluous code comments introduced by AI tools before submitting. PRs that clearly violate this rule will be closed without further review.
|
||||
|
||||
### PR title format
|
||||
|
||||
The PR title must follow the following pattern:
|
||||
@@ -30,6 +32,7 @@ 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
|
||||
|
||||
@@ -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|mintlify-openapi|nhost-js|nixops|storage"
|
||||
VALID_PKGS="auth|ci|cli|codegen|dashboard|deps|docs|examples|internal\/lib|mintlify-openapi|nhost-js|nixops|storage"
|
||||
|
||||
# Check if title matches the pattern TYPE(PKG): SUMMARY
|
||||
if [[ ! "$PR_TITLE" =~ ^(${VALID_TYPES})\((${VALID_PKGS})\):\ .+ ]]; then
|
||||
|
||||
8
.github/workflows/auth_checks.yaml
vendored
8
.github/workflows/auth_checks.yaml
vendored
@@ -1,8 +1,7 @@
|
||||
---
|
||||
name: "auth: check and build"
|
||||
on:
|
||||
# pull_request_target:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/auth_checks.yaml'
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
@@ -18,6 +17,7 @@ on:
|
||||
- '.golangci.yaml'
|
||||
- 'go.mod'
|
||||
- 'go.sum'
|
||||
- 'internal/lib/**'
|
||||
- 'vendor/**'
|
||||
|
||||
# auth
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
with:
|
||||
NAME: auth
|
||||
PATH: services/auth
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
with:
|
||||
NAME: auth
|
||||
PATH: services/auth
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: true
|
||||
secrets:
|
||||
|
||||
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 -q "$TAG_NAME@$VERSION"; then
|
||||
if git tag | grep -qx "$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"
|
||||
|
||||
9
.github/workflows/cli_checks.yaml
vendored
9
.github/workflows/cli_checks.yaml
vendored
@@ -1,8 +1,7 @@
|
||||
---
|
||||
name: "cli: check and build"
|
||||
on:
|
||||
# pull_request_target:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/cli_checks.yaml'
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
@@ -50,7 +49,7 @@ jobs:
|
||||
with:
|
||||
NAME: cli
|
||||
PATH: cli
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
@@ -65,7 +64,7 @@ jobs:
|
||||
with:
|
||||
NAME: cli
|
||||
PATH: cli
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: true
|
||||
secrets:
|
||||
@@ -81,7 +80,7 @@ jobs:
|
||||
with:
|
||||
NAME: cli
|
||||
PATH: cli
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: "Get artifacts"
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
path: ~/artifacts
|
||||
|
||||
|
||||
7
.github/workflows/codegen_checks.yaml
vendored
7
.github/workflows/codegen_checks.yaml
vendored
@@ -1,8 +1,7 @@
|
||||
---
|
||||
name: "codegen: check and build"
|
||||
on:
|
||||
# pull_request_target:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
- '.github/workflows/codegen_checks.yaml'
|
||||
@@ -48,7 +47,7 @@ jobs:
|
||||
with:
|
||||
NAME: codegen
|
||||
PATH: tools/codegen
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
@@ -62,7 +61,7 @@ jobs:
|
||||
with:
|
||||
NAME: codegen
|
||||
PATH: tools/codegen
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: false
|
||||
secrets:
|
||||
|
||||
10
.github/workflows/dashboard_checks.yaml
vendored
10
.github/workflows/dashboard_checks.yaml
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: "dashboard: check and build"
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/wf_build_artifacts.yaml'
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
- check-permissions
|
||||
with:
|
||||
NAME: dashboard
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
ENVIRONMENT: preview
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
with:
|
||||
NAME: dashboard
|
||||
PATH: dashboard
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: true
|
||||
OS_MATRIX: '["blacksmith-2vcpu-ubuntu-2404"]'
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
with:
|
||||
NAME: dashboard
|
||||
PATH: dashboard
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
NIX_CACHE_PUB_KEY: ${{ secrets.NIX_CACHE_PUB_KEY }}
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
with:
|
||||
NAME: dashboard
|
||||
PATH: dashboard
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
NHOST_TEST_DASHBOARD_URL: ${{ needs.deploy-vercel.outputs.preview-url }}
|
||||
NHOST_TEST_PROJECT_NAME: ${{ vars.NHOST_TEST_PROJECT_NAME }}
|
||||
NHOST_TEST_ORGANIZATION_NAME: ${{ vars.NHOST_TEST_ORGANIZATION_NAME }}
|
||||
|
||||
@@ -148,7 +148,7 @@ jobs:
|
||||
rm playwright-report.tar.gz
|
||||
|
||||
- name: Upload encrypted Playwright report
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
if: failure()
|
||||
with:
|
||||
name: encrypted-playwright-report-${{ github.run_id }}
|
||||
|
||||
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
|
||||
|
||||
4
.github/workflows/docs_checks.yaml
vendored
4
.github/workflows/docs_checks.yaml
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: "docs: check and build"
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
- '.github/workflows/dashboard_checks.yaml'
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
with:
|
||||
NAME: docs
|
||||
PATH: docs
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
NIX_CACHE_PUB_KEY: ${{ secrets.NIX_CACHE_PUB_KEY }}
|
||||
|
||||
7
.github/workflows/examples_demos_checks.yaml
vendored
7
.github/workflows/examples_demos_checks.yaml
vendored
@@ -1,8 +1,7 @@
|
||||
---
|
||||
name: "examples/demos: check and build"
|
||||
on:
|
||||
# pull_request_target:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
- '.github/workflows/examples_demos_checks.yaml'
|
||||
@@ -64,7 +63,7 @@ jobs:
|
||||
with:
|
||||
NAME: demos
|
||||
PATH: examples/demos
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
@@ -78,7 +77,7 @@ jobs:
|
||||
with:
|
||||
NAME: demos
|
||||
PATH: examples/demos
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: false
|
||||
OS_MATRIX: '["blacksmith-2vcpu-ubuntu-2404"]'
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
---
|
||||
name: "examples/guides: check and build"
|
||||
on:
|
||||
# pull_request_target:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
- '.github/workflows/examples_guides_checks.yaml'
|
||||
@@ -64,7 +63,7 @@ jobs:
|
||||
with:
|
||||
NAME: guides
|
||||
PATH: examples/guides
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
@@ -78,7 +77,7 @@ jobs:
|
||||
with:
|
||||
NAME: guides
|
||||
PATH: examples/guides
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: false
|
||||
OS_MATRIX: '["blacksmith-2vcpu-ubuntu-2404"]'
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
---
|
||||
name: "examples/tutorials: check and build"
|
||||
on:
|
||||
# pull_request_target:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
- '.github/workflows/examples_tutorials_checks.yaml'
|
||||
@@ -64,7 +63,7 @@ jobs:
|
||||
with:
|
||||
NAME: tutorials
|
||||
PATH: examples/tutorials
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
@@ -78,7 +77,7 @@ jobs:
|
||||
with:
|
||||
NAME: tutorials
|
||||
PATH: examples/tutorials
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: false
|
||||
OS_MATRIX: '["blacksmith-2vcpu-ubuntu-2404"]'
|
||||
|
||||
2
.github/workflows/gen_ai_review.yaml
vendored
2
.github/workflows/gen_ai_review.yaml
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: "gen: AI review"
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, ready_for_review]
|
||||
issue_comment:
|
||||
jobs:
|
||||
|
||||
7
.github/workflows/nhost-js_checks.yaml
vendored
7
.github/workflows/nhost-js_checks.yaml
vendored
@@ -1,8 +1,7 @@
|
||||
---
|
||||
name: "nhost-js: check and build"
|
||||
on:
|
||||
# pull_request_target:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
- '.github/workflows/nhost-js_checks.yaml'
|
||||
@@ -65,7 +64,7 @@ jobs:
|
||||
with:
|
||||
NAME: nhost-js
|
||||
PATH: packages/nhost-js
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
@@ -79,7 +78,7 @@ jobs:
|
||||
with:
|
||||
NAME: nhost-js
|
||||
PATH: packages/nhost-js
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: false
|
||||
secrets:
|
||||
|
||||
7
.github/workflows/nixops_checks.yaml
vendored
7
.github/workflows/nixops_checks.yaml
vendored
@@ -1,8 +1,7 @@
|
||||
---
|
||||
name: "nixops: check and build"
|
||||
on:
|
||||
# pull_request_target:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
- '.github/workflows/nixops_checks.yaml'
|
||||
@@ -40,7 +39,7 @@ jobs:
|
||||
with:
|
||||
NAME: nixops
|
||||
PATH: nixops
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
@@ -54,7 +53,7 @@ jobs:
|
||||
with:
|
||||
NAME: nixops
|
||||
PATH: nixops
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: true
|
||||
secrets:
|
||||
|
||||
8
.github/workflows/storage_checks.yaml
vendored
8
.github/workflows/storage_checks.yaml
vendored
@@ -1,8 +1,7 @@
|
||||
---
|
||||
name: "storage: check and build"
|
||||
on:
|
||||
# pull_request_target:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.github/workflows/storage_checks.yaml'
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
@@ -18,6 +17,7 @@ on:
|
||||
- '.golangci.yaml'
|
||||
- 'go.mod'
|
||||
- 'go.sum'
|
||||
- 'internal/lib/**'
|
||||
- 'vendor/**'
|
||||
|
||||
# storage
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
with:
|
||||
NAME: storage
|
||||
PATH: services/storage
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
with:
|
||||
NAME: storage
|
||||
PATH: services/storage
|
||||
GIT_REF: ${{ github.sha }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: true
|
||||
secrets:
|
||||
|
||||
4
.github/workflows/wf_build_artifacts.yaml
vendored
4
.github/workflows/wf_build_artifacts.yaml
vendored
@@ -85,7 +85,7 @@ jobs:
|
||||
zip -r result.zip result
|
||||
|
||||
- name: "Push artifact to artifact repository"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: ${{ inputs.NAME }}-artifact-${{ steps.vars.outputs.ARCH }}-${{ steps.vars.outputs.VERSION }}
|
||||
path: ${{ inputs.PATH }}/result.zip
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
if: ${{ ( inputs.DOCKER ) }}
|
||||
|
||||
- name: "Push docker image to artifact repository"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: ${{ inputs.NAME }}-docker-image-${{ steps.vars.outputs.ARCH }}-${{ steps.vars.outputs.VERSION }}
|
||||
path: ${{ inputs.PATH }}/result
|
||||
|
||||
2
.github/workflows/wf_docker_push_image.yaml
vendored
2
.github/workflows/wf_docker_push_image.yaml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
echo "VERSION=$(make get-version VER=${{ inputs.VERSION }})" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: "Get artifacts"
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
path: ~/artifacts
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
echo "VERSION=$(make get-version VER=${{ inputs.VERSION }})" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: "Get artifacts"
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
path: ~/artifacts
|
||||
|
||||
|
||||
@@ -16,6 +16,15 @@ Contributions are made to Nhost repos via Issues and Pull Requests (PRs). A few
|
||||
- We work hard to make sure issues are handled on time, but it could take a while to investigate the root cause depending on the impact. A friendly ping in the comment thread to the submitter or a contributor can help draw attention if your issue is blocking.
|
||||
- If you've never contributed before, see [the first-timer's guide](https://github.com/firstcontributions/first-contributions) for resources and tips on getting started.
|
||||
|
||||
### AI-Assisted Contributions
|
||||
|
||||
We have specific policies regarding AI-assisted contributions:
|
||||
|
||||
- **Issues**: Bug reports and feature requests that are clearly AI-generated will not be accepted and will be closed immediately. Please write your issues in your own words to ensure they are clear, specific, and contain the necessary context.
|
||||
- **Pull Requests**: Contributions with the help of AI are permitted, but you are ultimately responsible for the quality of your submission and for ensuring it follows our contributing guidelines. The PR description must be written in your own words. Additionally, please remove any superfluous code comments introduced by AI tools before submitting. PRs that clearly violate this rule will be closed without further review.
|
||||
|
||||
In all cases, contributors must ensure their submissions are thoughtful, well-tested, and meet the project's quality standards.
|
||||
|
||||
### Issues
|
||||
|
||||
Issues should be used to report problems with Nhost, request a new feature, or discuss potential changes before a PR is created.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"$schema": "https://github.com/IBM/audit-ci/raw/main/docs/schema.json",
|
||||
"moderate": true,
|
||||
"allowlist": [
|
||||
"GHSA-9965-vmph-33xx" // https://github.com/advisories/GHSA-9965-vmph-33xx Update package once have a fix
|
||||
"GHSA-9965-vmph-33xx", // https://github.com/advisories/GHSA-9965-vmph-33xx Update package once have a fix
|
||||
"GHSA-7mvr-c777-76hp" // https://github.com/advisories/GHSA-7mvr-c777-76hp Update package once Nix side is also updated
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,35 @@
|
||||
## [cli@1.34.5] - 2025-11-06
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(nixops)* Bump go to 1.25.3 and nixpkgs due to CVEs (#3652)
|
||||
- *(cli)* Udpate certs and schema (#3675)
|
||||
- *(cli)* Bump nhost/dashboard to 2.41.0 (#3669)
|
||||
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [cli@1.34.4] - 2025-10-28
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(cli)* Update NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL correctly (#3643)
|
||||
|
||||
## [cli@1.34.3] - 2025-10-27
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(cli)* Update schema (#3622)
|
||||
- *(cli)* Bump nhost/dashboard to 2.40.0 (#3629)
|
||||
|
||||
## [cli@1.34.2] - 2025-10-20
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(cli)* Minor fix to download script when specifying version (#3602)
|
||||
- *(cli)* Update schema (#3613)
|
||||
|
||||
## [cli@1.34.1] - 2025-10-13
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
@@ -56,7 +56,7 @@ func CommandCloud() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagDashboardVersion,
|
||||
Usage: "Dashboard version to use",
|
||||
Value: "nhost/dashboard:2.38.4",
|
||||
Value: "nhost/dashboard:2.41.0",
|
||||
Sources: cli.EnvVars("NHOST_DASHBOARD_VERSION"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
|
||||
@@ -111,7 +111,7 @@ func CommandUp() *cli.Command { //nolint:funlen
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagDashboardVersion,
|
||||
Usage: "Dashboard version to use",
|
||||
Value: "nhost/dashboard:2.38.4",
|
||||
Value: "nhost/dashboard:2.41.0",
|
||||
Sources: cli.EnvVars("NHOST_DASHBOARD_VERSION"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
|
||||
@@ -56,6 +56,7 @@ func auth( //nolint:funlen
|
||||
false,
|
||||
false,
|
||||
"00000000-0000-0000-0000-000000000000",
|
||||
"5181f67e2844e4b60d571fa346cac9c37fc00d1ff519212eae6cead138e639ba",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get hasura env vars: %w", err)
|
||||
|
||||
@@ -33,6 +33,7 @@ func expectedAuth() *Service {
|
||||
"AUTH_DISABLE_SIGNUP": "false",
|
||||
"AUTH_EMAIL_PASSWORDLESS_ENABLED": "true",
|
||||
"AUTH_EMAIL_SIGNIN_EMAIL_VERIFIED_REQUIRED": "true",
|
||||
"AUTH_ENCRYPTION_KEY": "5181f67e2844e4b60d571fa346cac9c37fc00d1ff519212eae6cead138e639ba",
|
||||
"AUTH_GRAVATAR_DEFAULT": "gravatarDefault",
|
||||
"AUTH_GRAVATAR_ENABLED": "true",
|
||||
"AUTH_GRAVATAR_RATING": "gravatarRating",
|
||||
@@ -52,6 +53,7 @@ func expectedAuth() *Service {
|
||||
"AUTH_PROVIDER_APPLE_ENABLED": "true",
|
||||
"AUTH_PROVIDER_APPLE_KEY_ID": "appleKeyId",
|
||||
"AUTH_PROVIDER_APPLE_PRIVATE_KEY": "applePrivateKey",
|
||||
"AUTH_PROVIDER_APPLE_SCOPE": "",
|
||||
"AUTH_PROVIDER_APPLE_TEAM_ID": "appleTeamId",
|
||||
"AUTH_PROVIDER_AZUREAD_CLIENT_ID": "azureadClientId",
|
||||
"AUTH_PROVIDER_AZUREAD_CLIENT_SECRET": "azureadClientSecret",
|
||||
@@ -74,9 +76,12 @@ func expectedAuth() *Service {
|
||||
"AUTH_PROVIDER_FACEBOOK_CLIENT_SECRET": "facebookClientSecret",
|
||||
"AUTH_PROVIDER_FACEBOOK_ENABLED": "true",
|
||||
"AUTH_PROVIDER_FACEBOOK_SCOPE": "email",
|
||||
"AUTH_PROVIDER_GITHUB_AUDIENCE": "audience",
|
||||
"AUTH_PROVIDER_GITHUB_CLIENT_ID": "githubClientId",
|
||||
"AUTH_PROVIDER_GITHUB_CLIENT_SECRET": "githubClientSecret",
|
||||
"AUTH_PROVIDER_GITHUB_ENABLED": "true",
|
||||
"AUTH_PROVIDER_GITHUB_SCOPE": "user:email",
|
||||
"AUTH_PROVIDER_GITLAB_AUDIENCE": "audience",
|
||||
"AUTH_PROVIDER_GITLAB_CLIENT_ID": "gitlabClientId",
|
||||
"AUTH_PROVIDER_GITLAB_CLIENT_SECRET": "gitlabClientSecret",
|
||||
"AUTH_PROVIDER_GITLAB_ENABLED": "true",
|
||||
@@ -96,6 +101,7 @@ func expectedAuth() *Service {
|
||||
"AUTH_PROVIDER_SPOTIFY_CLIENT_SECRET": "spotifyClientSecret",
|
||||
"AUTH_PROVIDER_SPOTIFY_ENABLED": "true",
|
||||
"AUTH_PROVIDER_SPOTIFY_SCOPE": "user-read-email",
|
||||
"AUTH_PROVIDER_STRAVA_AUDIENCE": "audience",
|
||||
"AUTH_PROVIDER_STRAVA_CLIENT_ID": "stravaClientId",
|
||||
"AUTH_PROVIDER_STRAVA_CLIENT_SECRET": "stravaClientSecret",
|
||||
"AUTH_PROVIDER_STRAVA_ENABLED": "true",
|
||||
|
||||
@@ -344,7 +344,7 @@ func dashboard(
|
||||
subdomain, "hasura", httpPort, useTLS,
|
||||
) + "/console",
|
||||
"NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL": URL(
|
||||
subdomain, "hasura", httpPort, useTLS),
|
||||
subdomain, "hasura", httpPort, useTLS) + "/apis/migrate",
|
||||
"NEXT_PUBLIC_NHOST_STORAGE_URL": URL(
|
||||
subdomain, "storage", httpPort, useTLS) + "/v1",
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Package auth provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version 2.4.1 DO NOT EDIT.
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version 2.5.0 DO NOT EDIT.
|
||||
package auth
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Package graphql provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version 2.4.1 DO NOT EDIT.
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version 2.5.0 DO NOT EDIT.
|
||||
package graphql
|
||||
|
||||
import (
|
||||
|
||||
@@ -148,7 +148,7 @@ import (
|
||||
#Hasura: {
|
||||
// Version of hasura, you can see available versions in the URL below:
|
||||
// https://hub.docker.com/r/hasura/graphql-engine/tags
|
||||
version: string | *"v2.46.0-ce"
|
||||
version: string | *"v2.48.5-ce"
|
||||
|
||||
// JWT Secrets configuration
|
||||
jwtSecrets: [#JWTSecret]
|
||||
@@ -223,7 +223,7 @@ import (
|
||||
// Releases:
|
||||
//
|
||||
// https://github.com/nhost/hasura-storage/releases
|
||||
version: string | *"0.7.2"
|
||||
version: string | *"0.9.1"
|
||||
|
||||
// Networking (custom domains at the moment) are not allowed as we need to do further
|
||||
// configurations in the CDN. We will enable it again in the future.
|
||||
@@ -311,7 +311,7 @@ import (
|
||||
// Releases:
|
||||
//
|
||||
// https://github.com/nhost/hasura-auth/releases
|
||||
version: string | *"0.38.1"
|
||||
version: string | *"0.43.0"
|
||||
|
||||
// Resources for the service
|
||||
resources?: #Resources
|
||||
@@ -651,6 +651,9 @@ import (
|
||||
iops: uint32 | *3000
|
||||
tput: uint32 | *125
|
||||
}
|
||||
|
||||
encryptColumnKey?: string & =~"^[0-9a-fA-F]{64}$" // 32 bytes hex-encoded key
|
||||
oldEncryptColumnKey?: string & =~"^[0-9a-fA-F]{64}$" // for key rotation
|
||||
}
|
||||
|
||||
persistentVolumesEncrypted: bool | *false
|
||||
|
||||
@@ -70,18 +70,28 @@ type ConfigAIUpdateInput struct {
|
||||
WebhookSecret *string `json:"webhookSecret,omitempty"`
|
||||
}
|
||||
|
||||
// Configuration for auth service
|
||||
// You can find more information about the configuration here:
|
||||
// https://github.com/nhost/hasura-auth/blob/main/docs/environment-variables.md
|
||||
type ConfigAuth struct {
|
||||
ElevatedPrivileges *ConfigAuthElevatedPrivileges `json:"elevatedPrivileges,omitempty"`
|
||||
Method *ConfigAuthMethod `json:"method,omitempty"`
|
||||
Misc *ConfigAuthMisc `json:"misc,omitempty"`
|
||||
RateLimit *ConfigAuthRateLimit `json:"rateLimit,omitempty"`
|
||||
Redirections *ConfigAuthRedirections `json:"redirections,omitempty"`
|
||||
Resources *ConfigResources `json:"resources,omitempty"`
|
||||
Session *ConfigAuthSession `json:"session,omitempty"`
|
||||
SignUp *ConfigAuthSignUp `json:"signUp,omitempty"`
|
||||
Totp *ConfigAuthTotp `json:"totp,omitempty"`
|
||||
User *ConfigAuthUser `json:"user,omitempty"`
|
||||
Version *string `json:"version,omitempty"`
|
||||
// Resources for the service
|
||||
Resources *ConfigResources `json:"resources,omitempty"`
|
||||
Session *ConfigAuthSession `json:"session,omitempty"`
|
||||
SignUp *ConfigAuthSignUp `json:"signUp,omitempty"`
|
||||
Totp *ConfigAuthTotp `json:"totp,omitempty"`
|
||||
User *ConfigAuthUser `json:"user,omitempty"`
|
||||
// Version of auth, you can see available versions in the URL below:
|
||||
// https://hub.docker.com/r/nhost/hasura-auth/tags
|
||||
//
|
||||
// Releases:
|
||||
//
|
||||
// https://github.com/nhost/hasura-auth/releases
|
||||
Version *string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigAuthElevatedPrivileges struct {
|
||||
@@ -111,9 +121,11 @@ type ConfigAuthMethodAnonymousUpdateInput struct {
|
||||
}
|
||||
|
||||
type ConfigAuthMethodEmailPassword struct {
|
||||
EmailVerificationRequired *bool `json:"emailVerificationRequired,omitempty"`
|
||||
HibpEnabled *bool `json:"hibpEnabled,omitempty"`
|
||||
PasswordMinLength *uint32 `json:"passwordMinLength,omitempty"`
|
||||
EmailVerificationRequired *bool `json:"emailVerificationRequired,omitempty"`
|
||||
// Disabling email+password sign in is not implmented yet
|
||||
// enabled: bool | *true
|
||||
HibpEnabled *bool `json:"hibpEnabled,omitempty"`
|
||||
PasswordMinLength *uint32 `json:"passwordMinLength,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigAuthMethodEmailPasswordUpdateInput struct {
|
||||
@@ -335,8 +347,10 @@ type ConfigAuthRateLimitUpdateInput struct {
|
||||
}
|
||||
|
||||
type ConfigAuthRedirections struct {
|
||||
// AUTH_ACCESS_CONTROL_ALLOWED_REDIRECT_URLS
|
||||
AllowedUrls []string `json:"allowedUrls,omitempty"`
|
||||
ClientURL *string `json:"clientUrl,omitempty"`
|
||||
// AUTH_CLIENT_URL
|
||||
ClientURL *string `json:"clientUrl,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigAuthRedirectionsUpdateInput struct {
|
||||
@@ -350,8 +364,10 @@ type ConfigAuthSession struct {
|
||||
}
|
||||
|
||||
type ConfigAuthSessionAccessToken struct {
|
||||
// AUTH_JWT_CUSTOM_CLAIMS
|
||||
CustomClaims []*ConfigAuthsessionaccessTokenCustomClaims `json:"customClaims,omitempty"`
|
||||
ExpiresIn *uint32 `json:"expiresIn,omitempty"`
|
||||
// AUTH_ACCESS_TOKEN_EXPIRES_IN
|
||||
ExpiresIn *uint32 `json:"expiresIn,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigAuthSessionAccessTokenUpdateInput struct {
|
||||
@@ -360,6 +376,7 @@ type ConfigAuthSessionAccessTokenUpdateInput struct {
|
||||
}
|
||||
|
||||
type ConfigAuthSessionRefreshToken struct {
|
||||
// AUTH_REFRESH_TOKEN_EXPIRES_IN
|
||||
ExpiresIn *uint32 `json:"expiresIn,omitempty"`
|
||||
}
|
||||
|
||||
@@ -373,9 +390,11 @@ type ConfigAuthSessionUpdateInput struct {
|
||||
}
|
||||
|
||||
type ConfigAuthSignUp struct {
|
||||
DisableNewUsers *bool `json:"disableNewUsers,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
Turnstile *ConfigAuthSignUpTurnstile `json:"turnstile,omitempty"`
|
||||
// AUTH_DISABLE_NEW_USERS
|
||||
DisableNewUsers *bool `json:"disableNewUsers,omitempty"`
|
||||
// Inverse of AUTH_DISABLE_SIGNUP
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
Turnstile *ConfigAuthSignUpTurnstile `json:"turnstile,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigAuthSignUpTurnstile struct {
|
||||
@@ -425,12 +444,16 @@ type ConfigAuthUser struct {
|
||||
}
|
||||
|
||||
type ConfigAuthUserEmail struct {
|
||||
// AUTH_ACCESS_CONTROL_ALLOWED_EMAILS
|
||||
Allowed []string `json:"allowed,omitempty"`
|
||||
// AUTH_ACCESS_CONTROL_BLOCKED_EMAILS
|
||||
Blocked []string `json:"blocked,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigAuthUserEmailDomains struct {
|
||||
// AUTH_ACCESS_CONTROL_ALLOWED_EMAIL_DOMAINS
|
||||
Allowed []string `json:"allowed,omitempty"`
|
||||
// AUTH_ACCESS_CONTROL_BLOCKED_EMAIL_DOMAINS
|
||||
Blocked []string `json:"blocked,omitempty"`
|
||||
}
|
||||
|
||||
@@ -446,6 +469,7 @@ type ConfigAuthUserEmailUpdateInput struct {
|
||||
|
||||
type ConfigAuthUserGravatar struct {
|
||||
Default *string `json:"default,omitempty"`
|
||||
// AUTH_GRAVATAR_ENABLED
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
Rating *string `json:"rating,omitempty"`
|
||||
}
|
||||
@@ -457,8 +481,10 @@ type ConfigAuthUserGravatarUpdateInput struct {
|
||||
}
|
||||
|
||||
type ConfigAuthUserLocale struct {
|
||||
// AUTH_LOCALE_ALLOWED_LOCALES
|
||||
Allowed []string `json:"allowed,omitempty"`
|
||||
Default *string `json:"default,omitempty"`
|
||||
// AUTH_LOCALE_DEFAULT
|
||||
Default *string `json:"default,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigAuthUserLocaleUpdateInput struct {
|
||||
@@ -467,8 +493,10 @@ type ConfigAuthUserLocaleUpdateInput struct {
|
||||
}
|
||||
|
||||
type ConfigAuthUserRoles struct {
|
||||
// AUTH_USER_DEFAULT_ALLOWED_ROLES
|
||||
Allowed []string `json:"allowed,omitempty"`
|
||||
Default *string `json:"default,omitempty"`
|
||||
// AUTH_USER_DEFAULT_ROLE
|
||||
Default *string `json:"default,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigAuthUserRolesUpdateInput struct {
|
||||
@@ -484,6 +512,7 @@ type ConfigAuthUserUpdateInput struct {
|
||||
Roles *ConfigAuthUserRolesUpdateInput `json:"roles,omitempty"`
|
||||
}
|
||||
|
||||
// AUTH_JWT_CUSTOM_CLAIMS
|
||||
type ConfigAuthsessionaccessTokenCustomClaims struct {
|
||||
Default *string `json:"default,omitempty"`
|
||||
Key string `json:"key"`
|
||||
@@ -522,8 +551,11 @@ type ConfigClaimMapUpdateInput struct {
|
||||
Value *string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// Resource configuration for a service
|
||||
type ConfigComputeResources struct {
|
||||
CPU uint32 `json:"cpu"`
|
||||
// milicpus, 1000 milicpus = 1 cpu
|
||||
CPU uint32 `json:"cpu"`
|
||||
// MiB: 128MiB to 30GiB
|
||||
Memory uint32 `json:"memory"`
|
||||
}
|
||||
|
||||
@@ -537,17 +569,28 @@ type ConfigComputeResourcesUpdateInput struct {
|
||||
Memory *uint32 `json:"memory,omitempty"`
|
||||
}
|
||||
|
||||
// main entrypoint to the configuration
|
||||
type ConfigConfig struct {
|
||||
Ai *ConfigAi `json:"ai,omitempty"`
|
||||
Auth *ConfigAuth `json:"auth,omitempty"`
|
||||
Functions *ConfigFunctions `json:"functions,omitempty"`
|
||||
Global *ConfigGlobal `json:"global,omitempty"`
|
||||
Graphql *ConfigGraphql `json:"graphql,omitempty"`
|
||||
Hasura *ConfigHasura `json:"hasura"`
|
||||
// Configuration for graphite service
|
||||
Ai *ConfigAi `json:"ai,omitempty"`
|
||||
// Configuration for auth service
|
||||
Auth *ConfigAuth `json:"auth,omitempty"`
|
||||
// Configuration for functions service
|
||||
Functions *ConfigFunctions `json:"functions,omitempty"`
|
||||
// Global configuration that applies to all services
|
||||
Global *ConfigGlobal `json:"global,omitempty"`
|
||||
// Advanced configuration for GraphQL
|
||||
Graphql *ConfigGraphql `json:"graphql,omitempty"`
|
||||
// Configuration for hasura
|
||||
Hasura *ConfigHasura `json:"hasura"`
|
||||
// Configuration for observability service
|
||||
Observability *ConfigObservability `json:"observability"`
|
||||
Postgres *ConfigPostgres `json:"postgres"`
|
||||
Provider *ConfigProvider `json:"provider,omitempty"`
|
||||
Storage *ConfigStorage `json:"storage,omitempty"`
|
||||
// Configuration for postgres service
|
||||
Postgres *ConfigPostgres `json:"postgres"`
|
||||
// Configuration for third party providers like SMTP, SMS, etc.
|
||||
Provider *ConfigProvider `json:"provider,omitempty"`
|
||||
// Configuration for storage service
|
||||
Storage *ConfigStorage `json:"storage,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigConfigUpdateInput struct {
|
||||
@@ -564,7 +607,8 @@ type ConfigConfigUpdateInput struct {
|
||||
}
|
||||
|
||||
type ConfigEnvironmentVariable struct {
|
||||
Name string `json:"name"`
|
||||
Name string `json:"name"`
|
||||
// Value of the environment variable
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
@@ -578,6 +622,7 @@ type ConfigEnvironmentVariableUpdateInput struct {
|
||||
Value *string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// Configuration for functions service
|
||||
type ConfigFunctions struct {
|
||||
Node *ConfigFunctionsNode `json:"node,omitempty"`
|
||||
RateLimit *ConfigRateLimit `json:"rateLimit,omitempty"`
|
||||
@@ -606,12 +651,15 @@ type ConfigFunctionsUpdateInput struct {
|
||||
Resources *ConfigFunctionsResourcesUpdateInput `json:"resources,omitempty"`
|
||||
}
|
||||
|
||||
// Global configuration that applies to all services
|
||||
type ConfigGlobal struct {
|
||||
// User-defined environment variables that are spread over all services
|
||||
Environment []*ConfigGlobalEnvironmentVariable `json:"environment,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigGlobalEnvironmentVariable struct {
|
||||
Name string `json:"name"`
|
||||
Name string `json:"name"`
|
||||
// Value of the environment variable
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
@@ -768,23 +816,34 @@ type ConfigGraphqlUpdateInput struct {
|
||||
Security *ConfigGraphqlSecurityUpdateInput `json:"security,omitempty"`
|
||||
}
|
||||
|
||||
// Configuration for hasura service
|
||||
type ConfigHasura struct {
|
||||
AdminSecret string `json:"adminSecret"`
|
||||
AuthHook *ConfigHasuraAuthHook `json:"authHook,omitempty"`
|
||||
Events *ConfigHasuraEvents `json:"events,omitempty"`
|
||||
JwtSecrets []*ConfigJWTSecret `json:"jwtSecrets,omitempty"`
|
||||
Logs *ConfigHasuraLogs `json:"logs,omitempty"`
|
||||
RateLimit *ConfigRateLimit `json:"rateLimit,omitempty"`
|
||||
Resources *ConfigResources `json:"resources,omitempty"`
|
||||
Settings *ConfigHasuraSettings `json:"settings,omitempty"`
|
||||
Version *string `json:"version,omitempty"`
|
||||
WebhookSecret string `json:"webhookSecret"`
|
||||
// Admin secret
|
||||
AdminSecret string `json:"adminSecret"`
|
||||
AuthHook *ConfigHasuraAuthHook `json:"authHook,omitempty"`
|
||||
Events *ConfigHasuraEvents `json:"events,omitempty"`
|
||||
// JWT Secrets configuration
|
||||
JwtSecrets []*ConfigJWTSecret `json:"jwtSecrets,omitempty"`
|
||||
Logs *ConfigHasuraLogs `json:"logs,omitempty"`
|
||||
RateLimit *ConfigRateLimit `json:"rateLimit,omitempty"`
|
||||
// Resources for the service
|
||||
Resources *ConfigResources `json:"resources,omitempty"`
|
||||
// Configuration for hasura services
|
||||
// Reference: https://hasura.io/docs/latest/deployment/graphql-engine-flags/reference/
|
||||
Settings *ConfigHasuraSettings `json:"settings,omitempty"`
|
||||
// Version of hasura, you can see available versions in the URL below:
|
||||
// https://hub.docker.com/r/hasura/graphql-engine/tags
|
||||
Version *string `json:"version,omitempty"`
|
||||
// Webhook secret
|
||||
WebhookSecret string `json:"webhookSecret"`
|
||||
}
|
||||
|
||||
type ConfigHasuraAuthHook struct {
|
||||
Mode *string `json:"mode,omitempty"`
|
||||
SendRequestBody *bool `json:"sendRequestBody,omitempty"`
|
||||
URL string `json:"url"`
|
||||
Mode *string `json:"mode,omitempty"`
|
||||
// HASURA_GRAPHQL_AUTH_HOOK_SEND_REQUEST_BODY
|
||||
SendRequestBody *bool `json:"sendRequestBody,omitempty"`
|
||||
// HASURA_GRAPHQL_AUTH_HOOK
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type ConfigHasuraAuthHookUpdateInput struct {
|
||||
@@ -794,6 +853,7 @@ type ConfigHasuraAuthHookUpdateInput struct {
|
||||
}
|
||||
|
||||
type ConfigHasuraEvents struct {
|
||||
// HASURA_GRAPHQL_EVENTS_HTTP_POOL_SIZE
|
||||
HTTPPoolSize *uint32 `json:"httpPoolSize,omitempty"`
|
||||
}
|
||||
|
||||
@@ -809,16 +869,27 @@ type ConfigHasuraLogsUpdateInput struct {
|
||||
Level *string `json:"level,omitempty"`
|
||||
}
|
||||
|
||||
// Configuration for hasura services
|
||||
// Reference: https://hasura.io/docs/latest/deployment/graphql-engine-flags/reference/
|
||||
type ConfigHasuraSettings struct {
|
||||
CorsDomain []string `json:"corsDomain,omitempty"`
|
||||
DevMode *bool `json:"devMode,omitempty"`
|
||||
EnableAllowList *bool `json:"enableAllowList,omitempty"`
|
||||
EnableConsole *bool `json:"enableConsole,omitempty"`
|
||||
EnableRemoteSchemaPermissions *bool `json:"enableRemoteSchemaPermissions,omitempty"`
|
||||
EnabledAPIs []string `json:"enabledAPIs,omitempty"`
|
||||
InferFunctionPermissions *bool `json:"inferFunctionPermissions,omitempty"`
|
||||
LiveQueriesMultiplexedRefetchInterval *uint32 `json:"liveQueriesMultiplexedRefetchInterval,omitempty"`
|
||||
StringifyNumericTypes *bool `json:"stringifyNumericTypes,omitempty"`
|
||||
// HASURA_GRAPHQL_CORS_DOMAIN
|
||||
CorsDomain []string `json:"corsDomain,omitempty"`
|
||||
// HASURA_GRAPHQL_DEV_MODE
|
||||
DevMode *bool `json:"devMode,omitempty"`
|
||||
// HASURA_GRAPHQL_ENABLE_ALLOWLIST
|
||||
EnableAllowList *bool `json:"enableAllowList,omitempty"`
|
||||
// HASURA_GRAPHQL_ENABLE_CONSOLE
|
||||
EnableConsole *bool `json:"enableConsole,omitempty"`
|
||||
// HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS
|
||||
EnableRemoteSchemaPermissions *bool `json:"enableRemoteSchemaPermissions,omitempty"`
|
||||
// HASURA_GRAPHQL_ENABLED_APIS
|
||||
EnabledAPIs []string `json:"enabledAPIs,omitempty"`
|
||||
// HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS
|
||||
InferFunctionPermissions *bool `json:"inferFunctionPermissions,omitempty"`
|
||||
// HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_REFETCH_INTERVAL
|
||||
LiveQueriesMultiplexedRefetchInterval *uint32 `json:"liveQueriesMultiplexedRefetchInterval,omitempty"`
|
||||
// HASURA_GRAPHQL_STRINGIFY_NUMERIC_TYPES
|
||||
StringifyNumericTypes *bool `json:"stringifyNumericTypes,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigHasuraSettingsUpdateInput struct {
|
||||
@@ -891,6 +962,7 @@ type ConfigIngressUpdateInput struct {
|
||||
TLS *ConfigIngressTLSUpdateInput `json:"tls,omitempty"`
|
||||
}
|
||||
|
||||
// See https://hasura.io/docs/latest/auth/authentication/jwt/
|
||||
type ConfigJWTSecret struct {
|
||||
AllowedSkew *uint32 `json:"allowed_skew,omitempty"`
|
||||
Audience *string `json:"audience,omitempty"`
|
||||
@@ -939,11 +1011,15 @@ type ConfigObservabilityUpdateInput struct {
|
||||
Grafana *ConfigGrafanaUpdateInput `json:"grafana,omitempty"`
|
||||
}
|
||||
|
||||
// Configuration for postgres service
|
||||
type ConfigPostgres struct {
|
||||
Pitr *ConfigPostgresPitr `json:"pitr,omitempty"`
|
||||
Pitr *ConfigPostgresPitr `json:"pitr,omitempty"`
|
||||
// Resources for the service
|
||||
Resources *ConfigPostgresResources `json:"resources"`
|
||||
Settings *ConfigPostgresSettings `json:"settings,omitempty"`
|
||||
Version *string `json:"version,omitempty"`
|
||||
// Version of postgres, you can see available versions in the URL below:
|
||||
// https://hub.docker.com/r/nhost/postgres/tags
|
||||
Version *string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigPostgresPitr struct {
|
||||
@@ -954,6 +1030,7 @@ type ConfigPostgresPitrUpdateInput struct {
|
||||
Retention *uint32 `json:"retention,omitempty"`
|
||||
}
|
||||
|
||||
// Resources for the service
|
||||
type ConfigPostgresResources struct {
|
||||
Compute *ConfigResourcesCompute `json:"compute,omitempty"`
|
||||
EnablePublicAccess *bool `json:"enablePublicAccess,omitempty"`
|
||||
@@ -1060,15 +1137,19 @@ type ConfigRateLimitUpdateInput struct {
|
||||
Limit *uint32 `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
// Resource configuration for a service
|
||||
type ConfigResources struct {
|
||||
Autoscaler *ConfigAutoscaler `json:"autoscaler,omitempty"`
|
||||
Compute *ConfigResourcesCompute `json:"compute,omitempty"`
|
||||
Networking *ConfigNetworking `json:"networking,omitempty"`
|
||||
Replicas *uint32 `json:"replicas,omitempty"`
|
||||
// Number of replicas for a service
|
||||
Replicas *uint32 `json:"replicas,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigResourcesCompute struct {
|
||||
CPU uint32 `json:"cpu"`
|
||||
// milicpus, 1000 milicpus = 1 cpu
|
||||
CPU uint32 `json:"cpu"`
|
||||
// MiB: 128MiB to 30GiB
|
||||
Memory uint32 `json:"memory"`
|
||||
}
|
||||
|
||||
@@ -1120,7 +1201,8 @@ type ConfigRunServiceConfigWithID struct {
|
||||
}
|
||||
|
||||
type ConfigRunServiceImage struct {
|
||||
Image string `json:"image"`
|
||||
Image string `json:"image"`
|
||||
// content of "auths", i.e., { "auths": $THIS }
|
||||
PullCredentials *string `json:"pullCredentials,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1158,11 +1240,13 @@ type ConfigRunServicePortUpdateInput struct {
|
||||
Type *string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// Resource configuration for a service
|
||||
type ConfigRunServiceResources struct {
|
||||
Autoscaler *ConfigAutoscaler `json:"autoscaler,omitempty"`
|
||||
Compute *ConfigComputeResources `json:"compute"`
|
||||
Replicas uint32 `json:"replicas"`
|
||||
Storage []*ConfigRunServiceResourcesStorage `json:"storage,omitempty"`
|
||||
Autoscaler *ConfigAutoscaler `json:"autoscaler,omitempty"`
|
||||
Compute *ConfigComputeResources `json:"compute"`
|
||||
// Number of replicas for a service
|
||||
Replicas uint32 `json:"replicas"`
|
||||
Storage []*ConfigRunServiceResourcesStorage `json:"storage,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigRunServiceResourcesInsertInput struct {
|
||||
@@ -1173,9 +1257,11 @@ type ConfigRunServiceResourcesInsertInput struct {
|
||||
}
|
||||
|
||||
type ConfigRunServiceResourcesStorage struct {
|
||||
// GiB
|
||||
Capacity uint32 `json:"capacity"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
// name of the volume, changing it will cause data loss
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type ConfigRunServiceResourcesStorageInsertInput struct {
|
||||
@@ -1259,11 +1345,20 @@ type ConfigStandardOauthProviderWithScopeUpdateInput struct {
|
||||
Scope []string `json:"scope,omitempty"`
|
||||
}
|
||||
|
||||
// Configuration for storage service
|
||||
type ConfigStorage struct {
|
||||
Antivirus *ConfigStorageAntivirus `json:"antivirus,omitempty"`
|
||||
RateLimit *ConfigRateLimit `json:"rateLimit,omitempty"`
|
||||
Resources *ConfigResources `json:"resources,omitempty"`
|
||||
Version *string `json:"version,omitempty"`
|
||||
// Networking (custom domains at the moment) are not allowed as we need to do further
|
||||
// configurations in the CDN. We will enable it again in the future.
|
||||
Resources *ConfigResources `json:"resources,omitempty"`
|
||||
// Version of storage service, you can see available versions in the URL below:
|
||||
// https://hub.docker.com/r/nhost/hasura-storage/tags
|
||||
//
|
||||
// Releases:
|
||||
//
|
||||
// https://github.com/nhost/hasura-storage/releases
|
||||
Version *string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigStorageAntivirus struct {
|
||||
@@ -1301,6 +1396,8 @@ type ConfigSystemConfigAuthEmailTemplates struct {
|
||||
}
|
||||
|
||||
type ConfigSystemConfigGraphql struct {
|
||||
// manually enable graphi on a per-service basis
|
||||
// by default it follows the plan
|
||||
FeatureAdvancedGraphql *bool `json:"featureAdvancedGraphql,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1718,7 +1815,8 @@ type Apps struct {
|
||||
AppStates []*AppStateHistory `json:"appStates"`
|
||||
AutomaticDeploys bool `json:"automaticDeploys"`
|
||||
// An array relationship
|
||||
Backups []*Backups `json:"backups"`
|
||||
Backups []*Backups `json:"backups"`
|
||||
// main entrypoint to the configuration
|
||||
Config *ConfigConfig `json:"config,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
// An object relationship
|
||||
@@ -2725,6 +2823,7 @@ type Deployments struct {
|
||||
CommitSha string `json:"commitSHA"`
|
||||
CommitUserAvatarURL *string `json:"commitUserAvatarUrl,omitempty"`
|
||||
CommitUserName *string `json:"commitUserName,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
DeploymentEndedAt *time.Time `json:"deploymentEndedAt,omitempty"`
|
||||
// An array relationship
|
||||
DeploymentLogs []*DeploymentLogs `json:"deploymentLogs"`
|
||||
@@ -2767,6 +2866,7 @@ type DeploymentsBoolExp struct {
|
||||
CommitSha *StringComparisonExp `json:"commitSHA,omitempty"`
|
||||
CommitUserAvatarURL *StringComparisonExp `json:"commitUserAvatarUrl,omitempty"`
|
||||
CommitUserName *StringComparisonExp `json:"commitUserName,omitempty"`
|
||||
CreatedAt *TimestamptzComparisonExp `json:"createdAt,omitempty"`
|
||||
DeploymentEndedAt *TimestamptzComparisonExp `json:"deploymentEndedAt,omitempty"`
|
||||
DeploymentLogs *DeploymentLogsBoolExp `json:"deploymentLogs,omitempty"`
|
||||
DeploymentStartedAt *TimestamptzComparisonExp `json:"deploymentStartedAt,omitempty"`
|
||||
@@ -2801,6 +2901,7 @@ type DeploymentsMaxOrderBy struct {
|
||||
CommitSha *OrderBy `json:"commitSHA,omitempty"`
|
||||
CommitUserAvatarURL *OrderBy `json:"commitUserAvatarUrl,omitempty"`
|
||||
CommitUserName *OrderBy `json:"commitUserName,omitempty"`
|
||||
CreatedAt *OrderBy `json:"createdAt,omitempty"`
|
||||
DeploymentEndedAt *OrderBy `json:"deploymentEndedAt,omitempty"`
|
||||
DeploymentStartedAt *OrderBy `json:"deploymentStartedAt,omitempty"`
|
||||
DeploymentStatus *OrderBy `json:"deploymentStatus,omitempty"`
|
||||
@@ -2823,6 +2924,7 @@ type DeploymentsMinOrderBy struct {
|
||||
CommitSha *OrderBy `json:"commitSHA,omitempty"`
|
||||
CommitUserAvatarURL *OrderBy `json:"commitUserAvatarUrl,omitempty"`
|
||||
CommitUserName *OrderBy `json:"commitUserName,omitempty"`
|
||||
CreatedAt *OrderBy `json:"createdAt,omitempty"`
|
||||
DeploymentEndedAt *OrderBy `json:"deploymentEndedAt,omitempty"`
|
||||
DeploymentStartedAt *OrderBy `json:"deploymentStartedAt,omitempty"`
|
||||
DeploymentStatus *OrderBy `json:"deploymentStatus,omitempty"`
|
||||
@@ -2861,6 +2963,7 @@ type DeploymentsOrderBy struct {
|
||||
CommitSha *OrderBy `json:"commitSHA,omitempty"`
|
||||
CommitUserAvatarURL *OrderBy `json:"commitUserAvatarUrl,omitempty"`
|
||||
CommitUserName *OrderBy `json:"commitUserName,omitempty"`
|
||||
CreatedAt *OrderBy `json:"createdAt,omitempty"`
|
||||
DeploymentEndedAt *OrderBy `json:"deploymentEndedAt,omitempty"`
|
||||
DeploymentLogsAggregate *DeploymentLogsAggregateOrderBy `json:"deploymentLogs_aggregate,omitempty"`
|
||||
DeploymentStartedAt *OrderBy `json:"deploymentStartedAt,omitempty"`
|
||||
@@ -2892,6 +2995,7 @@ type DeploymentsStreamCursorValueInput struct {
|
||||
CommitSha *string `json:"commitSHA,omitempty"`
|
||||
CommitUserAvatarURL *string `json:"commitUserAvatarUrl,omitempty"`
|
||||
CommitUserName *string `json:"commitUserName,omitempty"`
|
||||
CreatedAt *time.Time `json:"createdAt,omitempty"`
|
||||
DeploymentEndedAt *time.Time `json:"deploymentEndedAt,omitempty"`
|
||||
DeploymentStartedAt *time.Time `json:"deploymentStartedAt,omitempty"`
|
||||
DeploymentStatus *string `json:"deploymentStatus,omitempty"`
|
||||
@@ -6885,6 +6989,8 @@ const (
|
||||
// column name
|
||||
DeploymentsSelectColumnCommitUserName DeploymentsSelectColumn = "commitUserName"
|
||||
// column name
|
||||
DeploymentsSelectColumnCreatedAt DeploymentsSelectColumn = "createdAt"
|
||||
// column name
|
||||
DeploymentsSelectColumnDeploymentEndedAt DeploymentsSelectColumn = "deploymentEndedAt"
|
||||
// column name
|
||||
DeploymentsSelectColumnDeploymentStartedAt DeploymentsSelectColumn = "deploymentStartedAt"
|
||||
@@ -6918,6 +7024,7 @@ var AllDeploymentsSelectColumn = []DeploymentsSelectColumn{
|
||||
DeploymentsSelectColumnCommitSha,
|
||||
DeploymentsSelectColumnCommitUserAvatarURL,
|
||||
DeploymentsSelectColumnCommitUserName,
|
||||
DeploymentsSelectColumnCreatedAt,
|
||||
DeploymentsSelectColumnDeploymentEndedAt,
|
||||
DeploymentsSelectColumnDeploymentStartedAt,
|
||||
DeploymentsSelectColumnDeploymentStatus,
|
||||
@@ -6935,7 +7042,7 @@ var AllDeploymentsSelectColumn = []DeploymentsSelectColumn{
|
||||
|
||||
func (e DeploymentsSelectColumn) IsValid() bool {
|
||||
switch e {
|
||||
case DeploymentsSelectColumnAppID, DeploymentsSelectColumnCommitMessage, DeploymentsSelectColumnCommitSha, DeploymentsSelectColumnCommitUserAvatarURL, DeploymentsSelectColumnCommitUserName, DeploymentsSelectColumnDeploymentEndedAt, DeploymentsSelectColumnDeploymentStartedAt, DeploymentsSelectColumnDeploymentStatus, DeploymentsSelectColumnFunctionsEndedAt, DeploymentsSelectColumnFunctionsStartedAt, DeploymentsSelectColumnFunctionsStatus, DeploymentsSelectColumnID, DeploymentsSelectColumnMetadataEndedAt, DeploymentsSelectColumnMetadataStartedAt, DeploymentsSelectColumnMetadataStatus, DeploymentsSelectColumnMigrationsEndedAt, DeploymentsSelectColumnMigrationsStartedAt, DeploymentsSelectColumnMigrationsStatus:
|
||||
case DeploymentsSelectColumnAppID, DeploymentsSelectColumnCommitMessage, DeploymentsSelectColumnCommitSha, DeploymentsSelectColumnCommitUserAvatarURL, DeploymentsSelectColumnCommitUserName, DeploymentsSelectColumnCreatedAt, DeploymentsSelectColumnDeploymentEndedAt, DeploymentsSelectColumnDeploymentStartedAt, DeploymentsSelectColumnDeploymentStatus, DeploymentsSelectColumnFunctionsEndedAt, DeploymentsSelectColumnFunctionsStartedAt, DeploymentsSelectColumnFunctionsStatus, DeploymentsSelectColumnID, DeploymentsSelectColumnMetadataEndedAt, DeploymentsSelectColumnMetadataStartedAt, DeploymentsSelectColumnMetadataStatus, DeploymentsSelectColumnMigrationsEndedAt, DeploymentsSelectColumnMigrationsStartedAt, DeploymentsSelectColumnMigrationsStatus:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIERDCCA8mgAwIBAgISBmRex3kpZ4Mz1/1kq05iqja/MAoGCCqGSM49BAMDMDIx
|
||||
MIIERTCCA8ugAwIBAgISBWD/E+b14mP5jv4DGWRVYv8fMAoGCCqGSM49BAMDMDIx
|
||||
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
|
||||
ODAeFw0yNTEwMDIxMDUxNDBaFw0yNTEyMzExMDUxMzlaMB8xHTAbBgNVBAMTFGxv
|
||||
Y2FsLmF1dGgubmhvc3QucnVuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2cVM
|
||||
ojf8iXZGLneNfnke5LMJIxyTEeGbNOfCv4SOR4K/N4OkpvkUVbH2bRvX99uE9jaK
|
||||
515Y48PzPA/4+W1zTKOCAtAwggLMMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAU
|
||||
BggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUQqan
|
||||
raZoU5klAxsgkEVEMIkxmMQwHwYDVR0jBBgwFoAUjw0TovYuftFQbDMYOF1ZjiNy
|
||||
ODAeFw0yNTExMDYxMDUxMTBaFw0yNjAyMDQxMDUxMDlaMB8xHTAbBgNVBAMTFGxv
|
||||
Y2FsLmF1dGgubmhvc3QucnVuMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOah5
|
||||
ZLuUQp3pdMBxBWnT6E6/amW9LerKKEEdy3Nc8iAwG9LlnPH0z3m7a9wgEhpFEdlL
|
||||
Rr+qO+NhSRnv6+UF5KOCAtIwggLOMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAU
|
||||
BggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUGyb1
|
||||
TVK/0vf3uHO4x3R094aG2rEwHwYDVR0jBBgwFoAUjw0TovYuftFQbDMYOF1ZjiNy
|
||||
kcowMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAChhZodHRwOi8vZTguaS5sZW5j
|
||||
ci5vcmcvMIHOBgNVHREEgcYwgcOCFGxvY2FsLmF1dGgubmhvc3QucnVughlsb2Nh
|
||||
bC5kYXNoYm9hcmQubmhvc3QucnVughJsb2NhbC5kYi5uaG9zdC5ydW6CGWxvY2Fs
|
||||
LmZ1bmN0aW9ucy5uaG9zdC5ydW6CF2xvY2FsLmdyYXBocWwubmhvc3QucnVughZs
|
||||
b2NhbC5oYXN1cmEubmhvc3QucnVughdsb2NhbC5tYWlsaG9nLm5ob3N0LnJ1boIX
|
||||
bG9jYWwuc3RvcmFnZS5uaG9zdC5ydW4wEwYDVR0gBAwwCjAIBgZngQwBAgEwLQYD
|
||||
VR0fBCYwJDAioCCgHoYcaHR0cDovL2U4LmMubGVuY3Iub3JnLzY0LmNybDCCAQIG
|
||||
CisGAQQB1nkCBAIEgfMEgfAA7gB1AO08S9boBsKkogBX28sk4jgB31Ev7cSGxXAP
|
||||
IN23Pj/gAAABmaTCI4YAAAQDAEYwRAIgXLRFL1EAXfvN6kd5m6udqlxfz4+5B6rq
|
||||
Cdhp/ZwDAZ8CIFYvalTkl5NEBEMD3vpPvrj8s1Yy2xsropEh/AvpavvLAHUAGYbU
|
||||
xyiqb/66A294Kk0BkarOLXIxD67OXXBBLSVMx9QAAAGZpMIjhwAABAMARjBEAiBk
|
||||
H1vqU9HNuBcf4UYL/xZ42BeUAARHStiFaIZtnR1kEgIgbIJ0CGqIpxmWuwCunl9p
|
||||
ar+rGLdQrCk9BZXq/VjPPAAwCgYIKoZIzj0EAwMDaQAwZgIxAKvk5a2zQsv7JLNj
|
||||
NO1ly+DI8qiy5nf4HQrOrHOjtmx5RUu0HSO9P0J0u069qAqXMgIxAMLdME9JUo2c
|
||||
TJo3pwWv5MRyg/MkOJ4ImKdDJXfIZNkEIUyP3vwTqImvZe07gJDsYg==
|
||||
VR0fBCYwJDAioCCgHoYcaHR0cDovL2U4LmMubGVuY3Iub3JnLzMyLmNybDCCAQQG
|
||||
CisGAQQB1nkCBAIEgfUEgfIA8AB2ABmG1Mcoqm/+ugNveCpNAZGqzi1yMQ+uzl1w
|
||||
QS0lTMfUAAABmlkAQokAAAQDAEcwRQIgWDtSxJfM2xcjvScVHOkn8bipzBhNhTnm
|
||||
B89TDh1/4XUCIQDe08W33PCx2D+akCdW9U9mZKQpIW6deLZSI3ZWpSNKMAB2AA5X
|
||||
lLzzrqk+MxssmQez95Dfm8I9cTIl3SGpJaxhxU4hAAABmlkAQn8AAAQDAEcwRQIg
|
||||
KnojmNTpNk1OFTQI0EnlPa2bpwqmUgmUCLeqE6SWfgoCIQCrhZbxYPHbGLF/HpRq
|
||||
vCTcOh24SRCuxlkqtaowbbfmKjAKBggqhkjOPQQDAwNoADBlAjEArstFIC+KAsfQ
|
||||
nLhtqsaNzkhftN5adDyr2CoE0WUPF1sLDi+xDnDO+JgIPL0YKAFNAjATJ4omhpc+
|
||||
I6/kWcef2RyO9YCGQQE9pdez5CYKb9o8YAntDSHM3b5nXXj3AX/USdQ=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEVjCCAj6gAwIBAgIQY5WTY8JOcIJxWRi/w9ftVjANBgkqhkiG9w0BAQsFADBP
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgfJZOkvawA0vBMw9W
|
||||
ph8i1Z+SJQrFscPbqSYpxngzEDahRANCAATZxUyiN/yJdkYud41+eR7kswkjHJMR
|
||||
4Zs058K/hI5Hgr83g6Sm+RRVsfZtG9f324T2NornXljjw/M8D/j5bXNM
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgInXN4JRnXNTjx7rM
|
||||
avurZrN1EV1iebQeNUlMlFp7VJ+hRANCAAQ5qHlku5RCnel0wHEFadPoTr9qZb0t
|
||||
6sooQR3Lc1zyIDAb0uWc8fTPebtr3CASGkUR2UtGv6o742FJGe/r5QXk
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEWDCCA96gAwIBAgISBbvrSsjDQm4zevwwjxFGmeTMMAoGCCqGSM49BAMDMDIx
|
||||
MIIEVzCCA92gAwIBAgISBm54VdkoqD8s8efq7ceHaTihMAoGCCqGSM49BAMDMDIx
|
||||
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
|
||||
NzAeFw0yNTEwMDIxMDUyNTdaFw0yNTEyMzExMDUyNTZaMCExHzAdBgNVBAMMFiou
|
||||
YXV0aC5sb2NhbC5uaG9zdC5ydW4wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATG
|
||||
x0o7t0pSrOoFc+pljtqJVxgaSW+w9D9C2WdysMeSKKOU+0MzaM4ynLUhETOpBs8E
|
||||
612mdcoeak+G1Emj6UVwo4IC4zCCAt8wDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQW
|
||||
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQ+
|
||||
lVsLiXSRLAECs9OgkCEBS7jMmzAfBgNVHSMEGDAWgBSuSJ7chx1EoG/aouVgdAR4
|
||||
wpwAgDAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly9lNy5pLmxl
|
||||
ODAeFw0yNTExMDYxMDUyMjBaFw0yNjAyMDQxMDUyMTlaMCExHzAdBgNVBAMMFiou
|
||||
YXV0aC5sb2NhbC5uaG9zdC5ydW4wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASI
|
||||
rTkZOM4ip42DCyDADXGc7oV3+OkimyTM3st2RIZWG28rFRwH0LebJV2cduq1Hdtl
|
||||
VxIEr+RhvyIL7gllueXUo4IC4jCCAt4wDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQW
|
||||
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTw
|
||||
bM86O381+aljU3oTUvwhZ90PCDAfBgNVHSMEGDAWgBSPDROi9i5+0VBsMxg4XVmO
|
||||
I3KRyjAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly9lOC5pLmxl
|
||||
bmNyLm9yZy8wgd4GA1UdEQSB1jCB04IWKi5hdXRoLmxvY2FsLm5ob3N0LnJ1boIb
|
||||
Ki5kYXNoYm9hcmQubG9jYWwubmhvc3QucnVughQqLmRiLmxvY2FsLm5ob3N0LnJ1
|
||||
boIbKi5mdW5jdGlvbnMubG9jYWwubmhvc3QucnVughkqLmdyYXBocWwubG9jYWwu
|
||||
bmhvc3QucnVughgqLmhhc3VyYS5sb2NhbC5uaG9zdC5ydW6CGSoubWFpbGhvZy5s
|
||||
b2NhbC5uaG9zdC5ydW6CGSouc3RvcmFnZS5sb2NhbC5uaG9zdC5ydW4wEwYDVR0g
|
||||
BAwwCjAIBgZngQwBAgEwLQYDVR0fBCYwJDAioCCgHoYcaHR0cDovL2U3LmMubGVu
|
||||
Y3Iub3JnLzc3LmNybDCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB2AN3cyjSV1+EW
|
||||
BeeVMvrHn/g9HFDf2wA6FBJ2Ciysu8gqAAABmaTDUHkAAAQDAEcwRQIgWudJ8XKA
|
||||
BT5jq5Tl0xQLNb953pBi22Tb0TIWk+RSqHgCIQDsTrLVMFaQTV7EFCY1tFhi5qae
|
||||
SCpEwwdFcnom/nz6EAB3AO08S9boBsKkogBX28sk4jgB31Ev7cSGxXAPIN23Pj/g
|
||||
AAABmaTDWAsAAAQDAEgwRgIhALxIgIiutEwgNcGw7/cAdjFqUugct4HlZezIOLLP
|
||||
rg69AiEA8YCaK41rJDYztEKUIJEq2J2ktSqGYcl9gNKC+SiR4acwCgYIKoZIzj0E
|
||||
AwMDaAAwZQIwVG9yOiMRfKFFyFj1R8X/5U67QD84OhZ0oM0SZsVhezLedG5b8eFf
|
||||
/cWraREi8xbFAjEA/6RXweGzl08F7EtqBDoiqitScI2rbwGtP6s/evL0zXTABZD2
|
||||
ih7AGxjtg80IqIRe
|
||||
BAwwCjAIBgZngQwBAgEwLQYDVR0fBCYwJDAioCCgHoYcaHR0cDovL2U4LmMubGVu
|
||||
Y3Iub3JnLzM0LmNybDCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AEmcm2neHXzs
|
||||
/DbezYdkprhbrwqHgBnRVVL76esp3fjDAAABmlkBVgkAAAQDAEcwRQIhANH6Ml3u
|
||||
IM4nAzwAIjIjBjn8EWbn1ZHfgwO+rlSo5rzpAiATPKE8Mx5LK1IayG5VCK1eCDyc
|
||||
rzt1HNbP9WSrpuHx+gB2ABmG1Mcoqm/+ugNveCpNAZGqzi1yMQ+uzl1wQS0lTMfU
|
||||
AAABmlkBVgcAAAQDAEcwRQIgIT/DhsIj9Aw7qf/2lknJCr907dEqC3/+QN3zlcOj
|
||||
iKoCIQCTguinYjJPZwU2dblaRQ2q7MTCMT2ZENExltxwYG3GzjAKBggqhkjOPQQD
|
||||
AwNoADBlAjEA5nFoNrLyeC079YpRvdah/HZIA/lUBh+LOo/NcEBD3aTGs2z8hU8z
|
||||
H4vMy3OnfQ9TAjBxigm7zE5/3CAcGoSOr/P0TL52nh+lO4SUVxcbKgYB8A2yo6o/
|
||||
kUkG7PiRB0uUpNw=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEVzCCAj+gAwIBAgIRAKp18eYrjwoiCWbTi7/UuqEwDQYJKoZIhvcNAQELBQAw
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw
|
||||
WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
|
||||
RW5jcnlwdDELMAkGA1UEAxMCRTcwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARB6AST
|
||||
CFh/vjcwDMCgQer+VtqEkz7JANurZxLP+U9TCeioL6sp5Z8VRvRbYk4P1INBmbef
|
||||
QHJFHCxcSjKmwtvGBWpl/9ra8HW0QDsUaJW2qOJqceJ0ZVFT3hbUHifBM/2jgfgw
|
||||
gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
|
||||
ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSuSJ7chx1EoG/aouVgdAR4
|
||||
wpwAgDAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB
|
||||
AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g
|
||||
BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu
|
||||
Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAjx66fDdLk5ywFn3CzA1w1qfylHUD
|
||||
aEf0QZpXcJseddJGSfbUUOvbNR9N/QQ16K1lXl4VFyhmGXDT5Kdfcr0RvIIVrNxF
|
||||
h4lqHtRRCP6RBRstqbZ2zURgqakn/Xip0iaQL0IdfHBZr396FgknniRYFckKORPG
|
||||
yM3QKnd66gtMst8I5nkRQlAg/Jb+Gc3egIvuGKWboE1G89NTsN9LTDD3PLj0dUMr
|
||||
OIuqVjLB8pEC6yk9enrlrqjXQgkLEYhXzq7dLafv5Vkig6Gl0nuuqjqfp0Q1bi1o
|
||||
yVNAlXe6aUXw92CcghC9bNsKEO1+M52YY5+ofIXlS/SEQbvVYYBLZ5yeiglV6t3S
|
||||
M6H+vTG0aP9YHzLn/KVOHzGQfXDP7qM5tkf+7diZe7o2fw6O7IvN6fsQXEQQj8TJ
|
||||
UXJxv2/uJhcuy/tSDgXwHM8Uk34WNbRT7zGTGkQRX0gsbjAea/jYAoWv0ZvQRwpq
|
||||
Pe79D/i7Cep8qWnA+7AE/3B3S/3dEEYmc0lpe1366A/6GEgk3ktr9PEoQrLChs6I
|
||||
tu3wnNLB2euC8IKGLQFpGtOO/2/hiAKjyajaBP25w1jF0Wl8Bbqne3uZ2q1GyPFJ
|
||||
YRmT7/OXpmOH/FVLtwS+8ng1cAmpCujPwteJZNcDG0sF2n/sc0+SQf49fdyUK0ty
|
||||
+VUwFj9tmWxyR/M=
|
||||
MIIEVjCCAj6gAwIBAgIQY5WTY8JOcIJxWRi/w9ftVjANBgkqhkiG9w0BAQsFADBP
|
||||
MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy
|
||||
Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNDAzMTMwMDAwMDBa
|
||||
Fw0yNzAzMTIyMzU5NTlaMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBF
|
||||
bmNyeXB0MQswCQYDVQQDEwJFODB2MBAGByqGSM49AgEGBSuBBAAiA2IABNFl8l7c
|
||||
S7QMApzSsvru6WyrOq44ofTUOTIzxULUzDMMNMchIJBwXOhiLxxxs0LXeb5GDcHb
|
||||
R6EToMffgSZjO9SNHfY9gjMy9vQr5/WWOrQTZxh7az6NSNnq3u2ubT6HTKOB+DCB
|
||||
9TAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB
|
||||
MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI8NE6L2Ln7RUGwzGDhdWY4j
|
||||
cpHKMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEB
|
||||
BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAE
|
||||
DDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5j
|
||||
ci5vcmcvMA0GCSqGSIb3DQEBCwUAA4ICAQBnE0hGINKsCYWi0Xx1ygxD5qihEjZ0
|
||||
RI3tTZz1wuATH3ZwYPIp97kWEayanD1j0cDhIYzy4CkDo2jB8D5t0a6zZWzlr98d
|
||||
AQFNh8uKJkIHdLShy+nUyeZxc5bNeMp1Lu0gSzE4McqfmNMvIpeiwWSYO9w82Ob8
|
||||
otvXcO2JUYi3svHIWRm3+707DUbL51XMcY2iZdlCq4Wa9nbuk3WTU4gr6LY8MzVA
|
||||
aDQG2+4U3eJ6qUF10bBnR1uuVyDYs9RhrwucRVnfuDj29CMLTsplM5f5wSV5hUpm
|
||||
Uwp/vV7M4w4aGunt74koX71n4EdagCsL/Yk5+mAQU0+tue0JOfAV/R6t1k+Xk9s2
|
||||
HMQFeoxppfzAVC04FdG9M+AC2JWxmFSt6BCuh3CEey3fE52Qrj9YM75rtvIjsm/1
|
||||
Hl+u//Wqxnu1ZQ4jpa+VpuZiGOlWrqSP9eogdOhCGisnyewWJwRQOqK16wiGyZeR
|
||||
xs/Bekw65vwSIaVkBruPiTfMOo0Zh4gVa8/qJgMbJbyrwwG97z/PRgmLKCDl8z3d
|
||||
tA0Z7qq7fta0Gl24uyuB05dqI5J1LvAzKuWdIjT1tP8qCoxSE/xpix8hX2dt3h+/
|
||||
jujUgFPFZ0EVZ0xSyBNRF3MboGZnYXFUxpNjTWPKpagDHJQmqrAcDmWJnMsFY3jS
|
||||
u1igv3OefnWjSQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgrfNUSjLV/7j7LSBf
|
||||
zL/hvGEuv+uvf3/aimqjecO7vcShRANCAATGx0o7t0pSrOoFc+pljtqJVxgaSW+w
|
||||
9D9C2WdysMeSKKOU+0MzaM4ynLUhETOpBs8E612mdcoeak+G1Emj6UVw
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgcrhROXQT85e+S8h8
|
||||
RE3Z7TPo3+WA2RmzJsXJbXkbi5qhRANCAASIrTkZOM4ip42DCyDADXGc7oV3+Oki
|
||||
myTM3st2RIZWG28rFRwH0LebJV2cduq1HdtlVxIEr+RhvyIL7gllueXU
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
@@ -3,12 +3,13 @@ NEXT_PUBLIC_ENV=dev
|
||||
NEXT_PUBLIC_NHOST_PLATFORM=false
|
||||
|
||||
# Environment Variables for Self Hosting and Local Development
|
||||
NEXT_PUBLIC_NHOST_AUTH_URL=https://local.auth.nhost.local.run/v1
|
||||
NEXT_PUBLIC_NHOST_AUTH_URL=https://local.auth.local.nhost.run/v1
|
||||
NEXT_PUBLIC_NHOST_CONFIGSERVER_URL=https://local.dashboard.local.nhost.run/v1/configserver/graphql
|
||||
NEXT_PUBLIC_NHOST_FUNCTIONS_URL=https://local.functions.local.nhost.run/v1
|
||||
NEXT_PUBLIC_NHOST_GRAPHQL_URL=https://local.graphql.local.nhost.run/v1
|
||||
NEXT_PUBLIC_NHOST_STORAGE_URL=https://local.storage.local.nhost.run/v1
|
||||
NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL=https://local.hasura.local.nhost.run
|
||||
NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL=https://local.hasura.local.nhost.run/v1/migrations
|
||||
NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL=https://local.hasura.local.nhost.run/console
|
||||
NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL=https://local.hasura.local.nhost.run/apis/migrate
|
||||
NEXT_PUBLIC_NHOST_HASURA_API_URL=https://local.hasura.local.nhost.run
|
||||
|
||||
# Environment Variables when running the Nhost Dashboard against the Nhost Backend
|
||||
@@ -18,13 +19,13 @@ NEXT_PUBLIC_ANALYTICS_WRITE_KEY=<analytics_write_key>
|
||||
NEXT_PUBLIC_SEGMENT_CDN_URL=<segment_cdn_url>
|
||||
NEXT_PUBLIC_NHOST_BRAGI_WEBSOCKET=<nhost_bragi_websocket>
|
||||
|
||||
NEXT_PUBLIC_ZENDESK_URL=
|
||||
NEXT_PUBLIC_ZENDESK_API_KEY=
|
||||
NEXT_PUBLIC_ZENDESK_USER_EMAIL=
|
||||
NEXT_ZENDESK_URL=
|
||||
NEXT_ZENDESK_API_KEY=
|
||||
NEXT_ZENDESK_USER_EMAIL=
|
||||
|
||||
|
||||
CODEGEN_GRAPHQL_URL=https://local.graphql.local.nhost.run/v1
|
||||
CODEGEN_HASURA_ADMIN_SECRET=nhost-admin-secret
|
||||
NEXT_PUBLIC_TURNSTILE_SITE_KEY=FIXME
|
||||
|
||||
NEXT_PUBLIC_SOC2_REPORT_FILE_ID=
|
||||
NEXT_PUBLIC_SOC2_REPORT_FILE_ID=
|
||||
|
||||
@@ -1,7 +1,47 @@
|
||||
## [@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.
|
||||
|
||||
## [@nhost/dashboard@2.40.0] - 2025-10-27
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- *(dashboard)* Allow configuring CSP header (#3627)
|
||||
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(dashboard)* Various improvements to support ticket page (#3630)
|
||||
|
||||
## [@nhost/dashboard@2.39.0] - 2025-10-22
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- *(dashboard)* Move zendesk request to API route (#3628)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(dashboard)* Fix flaky e2e tests (#3536)
|
||||
- *(dashboard)* Run audit and lint in dashboard (#3578)
|
||||
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(dashboard)* Cleanup e2e remote schemas test before run (#3581)
|
||||
|
||||
## [@nhost/dashboard@2.38.4] - 2025-10-09
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
@@ -82,6 +82,15 @@ This will connect the Nhost Dashboard to your locally running Nhost backend.
|
||||
| `NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL` | The URL of Hasura's Migrations service. When working locally, point it to the Migrations service started by the CLI. |
|
||||
| `NEXT_PUBLIC_NHOST_HASURA_API_URL` | The URL of Hasura's Schema and Metadata API. When working locally, point it to the Schema and Metadata API started by the CLI. When self-hosting, point it to the self-hosted Schema and Metadata API. |
|
||||
|
||||
### Content Security Policy (CSP) Configuration
|
||||
|
||||
The dashboard supports build-time CSP configuration to enable self-hosted deployments on custom domains.
|
||||
|
||||
| Name | Description |
|
||||
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `CSP_MODE` | Controls CSP behavior. Options: `nhost` (default, uses Nhost Cloud CSP), `disabled` (no CSP headers), `custom` (use custom CSP via `CSP_HEADER`). For self-hosted deployments on custom domains, set to `disabled` or `custom`. |
|
||||
| `CSP_HEADER` | Custom Content Security Policy header value. Only used when `CSP_MODE=custom`. Should be a complete CSP string (e.g., `default-src 'self'; script-src 'self' 'unsafe-eval'; ...`). |
|
||||
|
||||
### Other Environment Variables
|
||||
|
||||
| Name | Description |
|
||||
|
||||
@@ -4,21 +4,31 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||
});
|
||||
const { version } = require('./package.json');
|
||||
|
||||
const cspHeader = `
|
||||
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;
|
||||
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:;
|
||||
object-src 'none';
|
||||
base-uri 'self';
|
||||
form-action 'self';
|
||||
frame-ancestors 'none';
|
||||
frame-src 'self' js.stripe.com challenges.cloudflare.com;
|
||||
block-all-mixed-content;
|
||||
upgrade-insecure-requests;
|
||||
`;
|
||||
function getCspHeader() {
|
||||
switch (process.env.CSP_MODE) {
|
||||
case 'disabled':
|
||||
return null;
|
||||
case 'custom':
|
||||
return process.env.CSP_HEADER || null;
|
||||
case 'nhost':
|
||||
default:
|
||||
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",
|
||||
"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:",
|
||||
"object-src 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
"frame-src 'self' js.stripe.com challenges.cloudflare.com",
|
||||
"block-all-mixed-content",
|
||||
"upgrade-insecure-requests",
|
||||
].join('; ') + ';';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = withBundleAnalyzer({
|
||||
reactStrictMode: false,
|
||||
@@ -34,13 +44,19 @@ module.exports = withBundleAnalyzer({
|
||||
dirs: ['src'],
|
||||
},
|
||||
async headers() {
|
||||
const cspHeader = getCspHeader();
|
||||
|
||||
if (!cspHeader) {
|
||||
return []; // No CSP headers
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
source: '/(.*)',
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'Content-Security-Policy',
|
||||
value: cspHeader.replace(/\s+/g, ' ').trim(),
|
||||
value: cspHeader,
|
||||
},
|
||||
{
|
||||
key: 'X-Frame-Options',
|
||||
@@ -110,4 +126,4 @@ module.exports = withBundleAnalyzer({
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -196,7 +196,7 @@
|
||||
"tailwindcss": "^3.4.12",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths-webpack-plugin": "^4.1.0",
|
||||
"vite": "^5.4.20",
|
||||
"vite": "^5.4.21",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
|
||||
34
dashboard/pnpm-lock.yaml
generated
34
dashboard/pnpm-lock.yaml
generated
@@ -415,7 +415,7 @@ importers:
|
||||
version: 6.21.0(eslint@8.57.0)(typescript@5.8.3)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^4.7.0
|
||||
version: 4.7.0(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0))
|
||||
version: 4.7.0(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0))
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.14.8)(jsdom@22.1.0)(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(terser@5.44.0))
|
||||
@@ -525,11 +525,11 @@ importers:
|
||||
specifier: ^4.1.0
|
||||
version: 4.2.0
|
||||
vite:
|
||||
specifier: ^5.4.20
|
||||
version: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
specifier: ^5.4.21
|
||||
version: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite-tsconfig-paths:
|
||||
specifier: ^4.3.2
|
||||
version: 4.3.2(typescript@5.8.3)(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0))
|
||||
version: 4.3.2(typescript@5.8.3)(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0))
|
||||
vitest:
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.14.8)(jsdom@22.1.0)(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(terser@5.44.0)
|
||||
@@ -8576,13 +8576,13 @@ packages:
|
||||
vite-tsconfig-paths@4.3.2:
|
||||
resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==}
|
||||
peerDependencies:
|
||||
vite: '*'
|
||||
vite: '>=4.5.14'
|
||||
peerDependenciesMeta:
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
vite@5.4.20:
|
||||
resolution: {integrity: sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==}
|
||||
vite@5.4.21:
|
||||
resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -13299,7 +13299,7 @@ snapshots:
|
||||
|
||||
'@ungap/structured-clone@1.2.0': {}
|
||||
|
||||
'@vitejs/plugin-react@4.7.0(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0))':
|
||||
'@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.4
|
||||
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
|
||||
@@ -13307,7 +13307,7 @@ snapshots:
|
||||
'@rolldown/pluginutils': 1.0.0-beta.27
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.17.0
|
||||
vite: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -13338,14 +13338,14 @@ snapshots:
|
||||
chai: 5.3.3
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/mocker@3.2.4(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0))':
|
||||
'@vitest/mocker@3.2.4(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.2.4
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.19
|
||||
optionalDependencies:
|
||||
msw: 2.11.4(@types/node@20.14.8)(typescript@5.8.3)
|
||||
vite: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
|
||||
'@vitest/pretty-format@3.2.4':
|
||||
dependencies:
|
||||
@@ -18484,7 +18484,7 @@ snapshots:
|
||||
debug: 4.4.3
|
||||
es-module-lexer: 1.7.0
|
||||
pathe: 2.0.3
|
||||
vite: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- less
|
||||
@@ -18496,18 +18496,18 @@ snapshots:
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0)):
|
||||
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0)):
|
||||
dependencies:
|
||||
debug: 4.4.1
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.1.6(typescript@5.8.3)
|
||||
optionalDependencies:
|
||||
vite: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
vite@5.4.20(@types/node@20.14.8)(terser@5.44.0):
|
||||
vite@5.4.21(@types/node@20.14.8)(terser@5.44.0):
|
||||
dependencies:
|
||||
esbuild: 0.25.10
|
||||
postcss: 8.5.3
|
||||
@@ -18521,7 +18521,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/chai': 5.2.2
|
||||
'@vitest/expect': 3.2.4
|
||||
'@vitest/mocker': 3.2.4(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0))
|
||||
'@vitest/mocker': 3.2.4(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0))
|
||||
'@vitest/pretty-format': 3.2.4
|
||||
'@vitest/runner': 3.2.4
|
||||
'@vitest/snapshot': 3.2.4
|
||||
@@ -18539,7 +18539,7 @@ snapshots:
|
||||
tinyglobby: 0.2.15
|
||||
tinypool: 1.1.1
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite-node: 3.2.4(@types/node@20.14.8)(terser@5.44.0)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
|
||||
@@ -166,6 +166,12 @@ rec {
|
||||
'';
|
||||
};
|
||||
|
||||
packageWithDisabledCSP = package.overrideAttrs (oldAttrs: {
|
||||
configurePhase = oldAttrs.configurePhase + ''
|
||||
export CSP_MODE=disabled
|
||||
'';
|
||||
});
|
||||
|
||||
dockerImage = pkgs.runCommand "image-as-dir" { } ''
|
||||
${(nix2containerPkgs.nix2container.buildImage {
|
||||
inherit name created;
|
||||
@@ -175,7 +181,7 @@ rec {
|
||||
copyToRoot = pkgs.buildEnv {
|
||||
name = "image";
|
||||
paths = [
|
||||
package
|
||||
packageWithDisabledCSP
|
||||
(pkgs.writeTextFile {
|
||||
name = "tmp-file";
|
||||
text = ''
|
||||
|
||||
@@ -127,14 +127,14 @@ export default function AuthenticatedLayout({
|
||||
className="relative flex h-full flex-row overflow-hidden"
|
||||
ref={setMainNavContainer}
|
||||
>
|
||||
{mainNavPinned && isMdOrLarger && <PinnedMainNav />}
|
||||
{withMainNav && mainNavPinned && isMdOrLarger && <PinnedMainNav />}
|
||||
|
||||
<div
|
||||
className={cn('relative flex h-full w-full flex-row bg-accent', {
|
||||
'overflow-x-auto': mainNavPinned && isMdOrLarger && withMainNav,
|
||||
})}
|
||||
>
|
||||
{(!mainNavPinned || !isMdOrLarger) && (
|
||||
{withMainNav && (!mainNavPinned || !isMdOrLarger) && (
|
||||
<div className="flex h-full w-6 justify-center">
|
||||
<MainNav container={mainNavContainer} />
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function SocialProvidersSettings() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return nhost.auth.signInProviderURL('github', {
|
||||
connect: token,
|
||||
redirectTo: `${window.location.origin}/account`,
|
||||
redirectTo: `${window.location.origin}/account?signinProvider=github`,
|
||||
});
|
||||
}
|
||||
return '';
|
||||
|
||||
@@ -3,18 +3,21 @@ 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,
|
||||
@@ -22,7 +25,10 @@ function GithubAuthButton({
|
||||
});
|
||||
return (
|
||||
<Button
|
||||
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"
|
||||
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,
|
||||
)}
|
||||
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();
|
||||
const redirectTo = `${useHostName()}?signinProvider=github`;
|
||||
return (
|
||||
<GithubAuthButton
|
||||
redirectTo={redirectTo}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
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."
|
||||
/>
|
||||
|
||||
@@ -176,7 +176,7 @@ export default function AppleProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/products/auth/social/sign-in-apple"
|
||||
docsLink="https://docs.nhost.io/products/auth/providers/sign-in-apple"
|
||||
docsTitle="how to sign in users with Apple"
|
||||
icon={
|
||||
theme.palette.mode === 'dark'
|
||||
|
||||
@@ -141,7 +141,7 @@ export default function DiscordProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/products/auth/social/sign-in-discord"
|
||||
docsLink="https://docs.nhost.io/products/auth/providers/sign-in-discord"
|
||||
docsTitle="how to sign in users with Discord"
|
||||
icon="/assets/brands/discord.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -142,7 +142,7 @@ export default function FacebookProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/products/auth/social/sign-in-facebook"
|
||||
docsLink="https://docs.nhost.io/products/auth/providers/sign-in-facebook"
|
||||
docsTitle="how to sign in users with Facebook"
|
||||
icon="/assets/brands/facebook.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -144,7 +144,7 @@ export default function GitHubProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/products/auth/social/sign-in-github"
|
||||
docsLink="https://docs.nhost.io/products/auth/providers/sign-in-github"
|
||||
docsTitle="how to sign in users with GitHub"
|
||||
icon={
|
||||
theme.palette.mode === 'dark'
|
||||
|
||||
@@ -162,7 +162,7 @@ export default function GoogleProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/products/auth/social/sign-in-google"
|
||||
docsLink="https://docs.nhost.io/products/auth/providers/sign-in-google"
|
||||
docsTitle="how to sign in users with Google"
|
||||
icon="/assets/brands/google.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -142,7 +142,7 @@ export default function LinkedInProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/products/auth/social/sign-in-linkedin"
|
||||
docsLink="https://docs.nhost.io/products/auth/providers/sign-in-linkedin"
|
||||
docsTitle="how to sign in users with LinkedIn"
|
||||
icon="/assets/brands/linkedin.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -142,7 +142,7 @@ export default function SpotifyProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/products/auth/social/sign-in-spotify"
|
||||
docsLink="https://docs.nhost.io/products/auth/providers/sign-in-spotify"
|
||||
docsTitle="how to sign in users with Spotify"
|
||||
icon="/assets/brands/spotify.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -144,7 +144,7 @@ export default function TwitchProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/products/auth/social/sign-in-twitch"
|
||||
docsLink="https://docs.nhost.io/products/auth/providers/sign-in-twitch"
|
||||
docsTitle="how to sign in users with Twitch"
|
||||
icon={
|
||||
theme.palette.mode === 'dark'
|
||||
|
||||
@@ -177,7 +177,7 @@ export default function WorkOsProviderSettings() {
|
||||
loading: formState.isSubmitting,
|
||||
},
|
||||
}}
|
||||
docsLink="https://docs.nhost.io/products/auth/social/sign-in-workos"
|
||||
docsLink="https://docs.nhost.io/products/auth/providers/sign-in-workos"
|
||||
docsTitle="how to sign in users with WorkOS"
|
||||
icon="/assets/brands/workos.svg"
|
||||
switchId="enabled"
|
||||
|
||||
@@ -3,7 +3,7 @@ import { generateAppServiceUrl } from '@/features/orgs/projects/common/utils/gen
|
||||
import { useDatabaseQuery } from '@/features/orgs/projects/database/dataGrid/hooks/useDatabaseQuery';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { getToastStyleProps } from '@/utils/constants/settings';
|
||||
import { getHasuraAdminSecret } from '@/utils/env';
|
||||
import { getHasuraAdminSecret, getHasuraMigrationsApiUrl } from '@/utils/env';
|
||||
import { parseIdentifiersFromSQL } from '@/utils/sql';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
@@ -53,7 +53,10 @@ export default function useRunSQL(
|
||||
isCascade: boolean,
|
||||
) => {
|
||||
try {
|
||||
const migrationApiResponse = await fetch(`${appUrl}/apis/migrate`, {
|
||||
const url = isPlatform
|
||||
? `${appUrl}/apis/migrate`
|
||||
: getHasuraMigrationsApiUrl();
|
||||
const migrationApiResponse = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'x-hasura-admin-secret': adminSecret },
|
||||
body: JSON.stringify({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
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';
|
||||
@@ -11,14 +12,33 @@ 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 { useGetGithubRepositoriesQuery } from '@/generated/graphql';
|
||||
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 { 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';
|
||||
export type ConnectGitHubModalState =
|
||||
| 'CONNECTING'
|
||||
| 'EDITING'
|
||||
| 'EXPIRED_GITHUB_SESSION'
|
||||
| 'GITHUB_CONNECTION_REQUIRED';
|
||||
|
||||
export interface ConnectGitHubModalProps {
|
||||
/**
|
||||
@@ -28,18 +48,153 @@ 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 { data, loading, error, startPolling } =
|
||||
useGetGithubRepositoriesQuery();
|
||||
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 '';
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
startPolling(2000);
|
||||
}, [startPolling]);
|
||||
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]);
|
||||
|
||||
const handleSelectAnotherRepository = () => {
|
||||
setSelectedRepoId(null);
|
||||
@@ -56,13 +211,91 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
|
||||
useEffect(() => () => handleFilterChange.cancel(), [handleFilterChange]);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
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 (loading) {
|
||||
if (loading || loadingProject || loadingOrg || loadingGithubConnected) {
|
||||
return (
|
||||
<ActivityIndicator delay={500} label="Loading GitHub repositories..." />
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -78,25 +311,27 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
);
|
||||
}
|
||||
|
||||
const { githubAppInstallations } = data || {};
|
||||
const { githubAppInstallations } = githubData || {};
|
||||
|
||||
const filteredGitHubAppInstallations = data?.githubAppInstallations.filter(
|
||||
(githubApp) => !!githubApp.accountLogin,
|
||||
);
|
||||
const filteredGitHubAppInstallations =
|
||||
githubData?.githubAppInstallations.filter(
|
||||
(githubApp) => !!githubApp.accountLogin,
|
||||
);
|
||||
|
||||
const filteredGitHubRepositories = data?.githubRepositories.filter(
|
||||
const filteredGitHubRepositories = githubData?.githubRepositories.filter(
|
||||
(repo) => !!repo.githubAppInstallation,
|
||||
);
|
||||
|
||||
const filteredGitHubAppInstallationsNullValues =
|
||||
data?.githubAppInstallations.filter((githubApp) => !!githubApp.accountLogin)
|
||||
.length === 0;
|
||||
githubData?.githubAppInstallations.filter(
|
||||
(githubApp) => !!githubApp.accountLogin,
|
||||
).length === 0;
|
||||
|
||||
const faultyGitHubInstallation =
|
||||
githubAppInstallations?.length === 0 ||
|
||||
filteredGitHubAppInstallationsNullValues;
|
||||
|
||||
const noRepositoriesAdded = data?.githubRepositories.length === 0;
|
||||
const noRepositoriesAdded = githubData?.githubRepositories.length === 0;
|
||||
|
||||
if (faultyGitHubInstallation) {
|
||||
return (
|
||||
@@ -115,11 +350,7 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
</div>
|
||||
|
||||
<Button
|
||||
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"
|
||||
href={`${process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}?state=install-github-app:${org.slug}:${project!.subdomain}`}
|
||||
rel="noreferrer noopener"
|
||||
endIcon={<ArrowSquareOutIcon className="h-4 w-4" />}
|
||||
>
|
||||
@@ -179,8 +410,7 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
</List>
|
||||
|
||||
<Link
|
||||
href={process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}
|
||||
target="_blank"
|
||||
href={`${process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}?state=install-github-app:${org.slug}:${project!.subdomain}`}
|
||||
rel="noreferrer noopener"
|
||||
underline="hover"
|
||||
className="grid grid-flow-col items-center justify-start gap-1"
|
||||
@@ -199,8 +429,8 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
className="text-center text-xs font-normal"
|
||||
color="secondary"
|
||||
>
|
||||
Showing repositories from {data?.githubAppInstallations.length}{' '}
|
||||
GitHub account(s)
|
||||
Showing repositories from{' '}
|
||||
{githubData?.githubAppInstallations.length} GitHub account(s)
|
||||
</Text>
|
||||
<div className="mb-2 mt-6 flex w-full">
|
||||
<Input
|
||||
@@ -226,7 +456,7 @@ export default function ConnectGitHubModal({ close }: ConnectGitHubModalProps) {
|
||||
<Button
|
||||
variant="borderless"
|
||||
color="primary"
|
||||
onClick={() => setSelectedRepoId(repo.id)}
|
||||
onClick={() => setSelectedRepoId(repo.node_id)}
|
||||
>
|
||||
Connect
|
||||
</Button>
|
||||
@@ -268,8 +498,7 @@ 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}
|
||||
target="_blank"
|
||||
href={`${process.env.NEXT_PUBLIC_GITHUB_APP_INSTALL_URL}?state=install-github-app:${org.slug}:${project!.subdomain}`}
|
||||
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 { useUpdateApplicationMutation } from '@/generated/graphql';
|
||||
import { useConnectGithubRepoMutation } 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,45 +33,29 @@ export default function EditRepositorySettingsModal({
|
||||
|
||||
const { project, refetch: refetchProject } = useProject();
|
||||
|
||||
const [updateApp, { loading }] = useUpdateApplicationMutation();
|
||||
const [connectGithubRepo, { loading }] = useConnectGithubRepoMutation();
|
||||
|
||||
const handleEditGitHubIntegration = async (
|
||||
data: EditRepositorySettingsFormData,
|
||||
) => {
|
||||
try {
|
||||
if (!project?.githubRepository || selectedRepoId) {
|
||||
await updateApp({
|
||||
variables: {
|
||||
appId: project?.id,
|
||||
app: {
|
||||
githubRepositoryId: selectedRepoId,
|
||||
repositoryProductionBranch: data.productionBranch,
|
||||
nhostBaseFolder: data.repoBaseFolder,
|
||||
},
|
||||
},
|
||||
});
|
||||
await connectGithubRepo({
|
||||
variables: {
|
||||
appID: project?.id,
|
||||
githubNodeID: 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,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
analytics.track('Project Connected to GitHub', {
|
||||
projectId: project?.id,
|
||||
projectName: project?.name,
|
||||
projectSubdomain: project?.subdomain,
|
||||
repositoryId: selectedRepoId,
|
||||
productionBranch: data.productionBranch,
|
||||
baseFolder: data.repoBaseFolder,
|
||||
});
|
||||
|
||||
await refetchProject();
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
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 } = useDialog();
|
||||
const { openAlertDialog, closeAlertDialog } = useDialog();
|
||||
|
||||
function openGitHubModal() {
|
||||
openAlertDialog({
|
||||
title: 'Connect GitHub Repository',
|
||||
payload: <ConnectGitHubModal />,
|
||||
payload: <ConnectGitHubModal close={closeAlertDialog} />,
|
||||
props: {
|
||||
hidePrimaryAction: true,
|
||||
hideSecondaryAction: true,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './githubTokens';
|
||||
99
dashboard/src/lib/github.ts
Normal file
99
dashboard/src/lib/github.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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}>
|
||||
<AuthProvider>
|
||||
<NhostApolloProvider
|
||||
fetchPolicy="cache-and-network"
|
||||
nhost={nhost}
|
||||
connectToDevTools={process.env.NEXT_PUBLIC_ENV === 'dev'}
|
||||
>
|
||||
<NhostApolloProvider
|
||||
fetchPolicy="cache-and-network"
|
||||
nhost={nhost}
|
||||
connectToDevTools={process.env.NEXT_PUBLIC_ENV === 'dev'}
|
||||
>
|
||||
<AuthProvider>
|
||||
<UIProvider>
|
||||
<Toaster position="bottom-center" />
|
||||
<ThemeProvider
|
||||
@@ -106,8 +106,8 @@ function MyApp({
|
||||
</RetryableErrorBoundary>
|
||||
</ThemeProvider>
|
||||
</UIProvider>
|
||||
</NhostApolloProvider>
|
||||
</AuthProvider>
|
||||
</AuthProvider>
|
||||
</NhostApolloProvider>
|
||||
</NhostProvider>
|
||||
</CacheProvider>
|
||||
</QueryClientProvider>
|
||||
|
||||
165
dashboard/src/pages/api/support/create-ticket.ts
Normal file
165
dashboard/src/pages/api/support/create-ticket.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { nhostRoutesClient } from '@/utils/nhost';
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
export type CreateTicketRequest = {
|
||||
project: string;
|
||||
services: Array<{ label: string; value: string }>;
|
||||
priority: string;
|
||||
subject: string;
|
||||
description: string;
|
||||
userName: string;
|
||||
userEmail: string;
|
||||
};
|
||||
|
||||
export type CreateTicketResponse = {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
type GetProjectResponse = {
|
||||
apps: Array<{
|
||||
id: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<CreateTicketResponse>,
|
||||
) {
|
||||
if (req.method !== 'POST') {
|
||||
return res
|
||||
.status(405)
|
||||
.json({ success: false, error: 'Method not allowed' });
|
||||
}
|
||||
|
||||
try {
|
||||
const {
|
||||
project,
|
||||
services,
|
||||
priority,
|
||||
subject,
|
||||
description,
|
||||
userName,
|
||||
userEmail,
|
||||
} = req.body as CreateTicketRequest;
|
||||
|
||||
// Validate required environment variables
|
||||
if (
|
||||
!process.env.NEXT_ZENDESK_USER_EMAIL ||
|
||||
!process.env.NEXT_ZENDESK_API_KEY ||
|
||||
!process.env.NEXT_ZENDESK_URL
|
||||
) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'Zendesk configuration is missing',
|
||||
});
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (
|
||||
!project ||
|
||||
!services ||
|
||||
!priority ||
|
||||
!subject ||
|
||||
!description ||
|
||||
!userName ||
|
||||
!userEmail
|
||||
) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Missing required fields',
|
||||
});
|
||||
}
|
||||
|
||||
const token = req.headers.authorization?.split(' ')[1];
|
||||
|
||||
try {
|
||||
// we use this to verify the owner of the JWT token has access to the project
|
||||
const resp = await nhostRoutesClient.graphql.request<GetProjectResponse>(
|
||||
{
|
||||
query: `query GetProject($subdomain: String!){
|
||||
apps(where: {subdomain: {_eq: $subdomain}}) {
|
||||
id
|
||||
}
|
||||
}`,
|
||||
variables: {
|
||||
subdomain: project,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (resp.body.data?.apps.length !== 1) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Invalid project subdomain',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Invalid project subdomain',
|
||||
});
|
||||
}
|
||||
|
||||
const auth = btoa(
|
||||
`${process.env.NEXT_ZENDESK_USER_EMAIL}/token:${process.env.NEXT_ZENDESK_API_KEY}`,
|
||||
);
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_ZENDESK_URL}/api/v2/requests.json`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Basic ${auth}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
request: {
|
||||
subject,
|
||||
comment: {
|
||||
body: description,
|
||||
},
|
||||
priority,
|
||||
requester: {
|
||||
name: userName,
|
||||
email: userEmail,
|
||||
},
|
||||
custom_fields: [
|
||||
// these custom field IDs come from zendesk
|
||||
{
|
||||
id: 19502784542098,
|
||||
value: project,
|
||||
},
|
||||
{
|
||||
id: 19922709880978,
|
||||
value: services.map((service) => service.value?.toLowerCase()),
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('Zendesk API error:', errorText);
|
||||
return res.status(response.status).json({
|
||||
success: false,
|
||||
error: `Failed to create ticket: ${response.statusText}`,
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error creating ticket:', error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'An unexpected error occurred',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
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,12 +1,38 @@
|
||||
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 type { ReactElement } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback, useEffect, 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"
|
||||
|
||||
@@ -12,7 +12,9 @@ function SupportPage() {
|
||||
return (
|
||||
<Box className="h-full overflow-auto pb-4">
|
||||
<Box className="flex w-full justify-start border-b-1 px-4 py-3">
|
||||
<Logo className="w-6 cursor-pointer" />
|
||||
<Link href="https://app.nhost.io" rel="noopener noreferrer">
|
||||
<Logo className="w-6" />
|
||||
</Link>
|
||||
</Box>
|
||||
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
|
||||
@@ -5,11 +5,11 @@ import { AuthenticatedLayout } from '@/components/layout/AuthenticatedLayout';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { Divider } from '@/components/ui/v2/Divider';
|
||||
import { EnvelopeIcon } from '@/components/ui/v2/icons/EnvelopeIcon';
|
||||
import { Input, inputClasses } from '@/components/ui/v2/Input';
|
||||
import { Option } from '@/components/ui/v2/Option';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
|
||||
import { useAccessToken } from '@/hooks/useAccessToken';
|
||||
import { useUserData } from '@/hooks/useUserData';
|
||||
import {
|
||||
useGetOrganizationsQuery,
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { styled } from '@mui/material';
|
||||
import { Mail } from 'lucide-react';
|
||||
import { type ReactElement } from 'react';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import * as Yup from 'yup';
|
||||
@@ -27,7 +28,7 @@ type Organization = Omit<
|
||||
>;
|
||||
|
||||
const validationSchema = Yup.object({
|
||||
organization: Yup.string().label('Organization'),
|
||||
organization: Yup.string().label('Organization').required(),
|
||||
project: Yup.string().label('Project').required(),
|
||||
services: Yup.array()
|
||||
.of(Yup.object({ label: Yup.string(), value: Yup.string() }))
|
||||
@@ -36,7 +37,6 @@ const validationSchema = Yup.object({
|
||||
priority: Yup.string().label('Priority').required(),
|
||||
subject: Yup.string().label('Subject').required(),
|
||||
description: Yup.string().label('Description').required(),
|
||||
ccs: Yup.string().label('CCs').optional(),
|
||||
});
|
||||
|
||||
export type CreateTicketFormValues = Yup.InferType<typeof validationSchema>;
|
||||
@@ -58,7 +58,6 @@ function TicketPage() {
|
||||
priority: '',
|
||||
subject: '',
|
||||
description: '',
|
||||
ccs: '',
|
||||
},
|
||||
resolver: yupResolver(validationSchema),
|
||||
});
|
||||
@@ -71,6 +70,7 @@ function TicketPage() {
|
||||
|
||||
const selectedOrganization = watch('organization');
|
||||
const user = useUserData();
|
||||
const token = useAccessToken();
|
||||
|
||||
const { data: organizationsData } = useGetOrganizationsQuery({
|
||||
variables: {
|
||||
@@ -91,56 +91,32 @@ function TicketPage() {
|
||||
};
|
||||
|
||||
const handleSubmit = async (formValues: CreateTicketFormValues) => {
|
||||
const { project, services, priority, subject, description, ccs } =
|
||||
formValues;
|
||||
|
||||
const auth = btoa(
|
||||
`${process.env.NEXT_PUBLIC_ZENDESK_USER_EMAIL}/token:${process.env.NEXT_PUBLIC_ZENDESK_API_KEY}`,
|
||||
);
|
||||
const emails = ccs
|
||||
?.replace(/ /g, '')
|
||||
.split(',')
|
||||
.map((email) => ({ user_email: email }));
|
||||
const { project, services, priority, subject, description } = formValues;
|
||||
|
||||
await execPromiseWithErrorToast(
|
||||
async () => {
|
||||
await fetch(
|
||||
`${process.env.NEXT_PUBLIC_ZENDESK_URL}/api/v2/requests.json`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Basic ${auth}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
request: {
|
||||
subject,
|
||||
comment: {
|
||||
body: description,
|
||||
},
|
||||
priority,
|
||||
requester: {
|
||||
name: user?.displayName,
|
||||
email: user?.email,
|
||||
},
|
||||
email_ccs: emails,
|
||||
custom_fields: [
|
||||
// these custom field IDs come from zendesk
|
||||
{
|
||||
id: 19502784542098,
|
||||
value: project,
|
||||
},
|
||||
{
|
||||
id: 19922709880978,
|
||||
value: services.map((service) =>
|
||||
service.value?.toLowerCase(),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
const response = await fetch('/api/support/create-ticket', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
);
|
||||
body: JSON.stringify({
|
||||
project,
|
||||
services,
|
||||
priority,
|
||||
subject,
|
||||
description,
|
||||
userName: user?.displayName,
|
||||
userEmail: user?.email,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Failed to create ticket');
|
||||
}
|
||||
|
||||
form.reset();
|
||||
},
|
||||
{
|
||||
@@ -191,7 +167,7 @@ function TicketPage() {
|
||||
>
|
||||
{organizations.map((organization) => (
|
||||
<Option
|
||||
key={organization.name}
|
||||
key={organization.id}
|
||||
value={organization.id}
|
||||
label={organization.name}
|
||||
>
|
||||
@@ -261,6 +237,8 @@ function TicketPage() {
|
||||
slotProps={{
|
||||
root: { className: 'grid grid-flow-col gap-1 mb-4' },
|
||||
}}
|
||||
error={!!errors.priority}
|
||||
helperText={errors.priority?.message}
|
||||
renderValue={(option) => (
|
||||
<span className="inline-grid grid-flow-col items-center gap-2">
|
||||
{option?.label}
|
||||
@@ -310,7 +288,6 @@ function TicketPage() {
|
||||
label="Subject"
|
||||
placeholder="Summary of the problem you are experiencing"
|
||||
fullWidth
|
||||
autoFocus
|
||||
inputProps={{ min: 2, max: 128 }}
|
||||
error={!!errors.subject}
|
||||
helperText={errors.subject?.message}
|
||||
@@ -330,31 +307,16 @@ function TicketPage() {
|
||||
helperText={errors.description?.message}
|
||||
/>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Text className="mt-4 font-bold">Notifications</Text>
|
||||
|
||||
<StyledInput
|
||||
{...register('ccs')}
|
||||
id="ccs"
|
||||
label="CCs"
|
||||
placeholder="Comma separated list of emails you want to share this ticket with."
|
||||
fullWidth
|
||||
inputProps={{ min: 2, max: 128 }}
|
||||
error={!!errors.ccs}
|
||||
helperText={errors.ccs?.message}
|
||||
/>
|
||||
|
||||
<Box className="ml-auto flex w-80 flex-col gap-4">
|
||||
<Box className="ml-auto flex flex-col gap-4 lg:w-80">
|
||||
<Text color="secondary" className="text-right text-sm">
|
||||
We will contact you at <strong>{user?.email}</strong>
|
||||
</Text>
|
||||
<Button
|
||||
variant="outlined"
|
||||
className="hover:!bg-white hover:!bg-opacity-10 focus:ring-0"
|
||||
className="text-base hover:!bg-white hover:!bg-opacity-10 focus:ring-0"
|
||||
size="large"
|
||||
type="submit"
|
||||
startIcon={<EnvelopeIcon />}
|
||||
startIcon={<Mail className="size-4" />}
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
@@ -373,7 +335,7 @@ function TicketPage() {
|
||||
|
||||
TicketPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<AuthenticatedLayout title="Help & Support | Nhost">
|
||||
<AuthenticatedLayout title="Help & Support | Nhost" withMainNav={false}>
|
||||
{page}
|
||||
</AuthenticatedLayout>
|
||||
);
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
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,
|
||||
@@ -21,7 +29,15 @@ function AuthProvider({ children }: PropsWithChildren) {
|
||||
pathname,
|
||||
push,
|
||||
} = useRouter();
|
||||
const { refreshToken, error, errorDescription, ...remainingQuery } = query;
|
||||
const {
|
||||
refreshToken,
|
||||
error,
|
||||
errorDescription,
|
||||
signinProvider,
|
||||
state,
|
||||
provider_state: providerState,
|
||||
...remainingQuery
|
||||
} = query;
|
||||
const [session, setSession] = useState<Session | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSigningOut, setIsSigningOut] = useState(false);
|
||||
@@ -55,7 +71,6 @@ 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({
|
||||
@@ -63,24 +78,84 @@ 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);
|
||||
}
|
||||
|
||||
// handle OAuth redirect errors (e.g., error=unverified-user)
|
||||
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`,
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof error === 'string') {
|
||||
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');
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +178,7 @@ 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}>
|
||||
<AuthProvider>
|
||||
<ApolloProvider client={mockClient}>
|
||||
<ApolloProvider client={mockClient}>
|
||||
<AuthProvider>
|
||||
<UIProvider>
|
||||
<Toaster position="bottom-center" />
|
||||
<ThemeProvider theme={theme}>
|
||||
<DialogProvider>{children}</DialogProvider>
|
||||
</ThemeProvider>
|
||||
</UIProvider>
|
||||
</ApolloProvider>
|
||||
</AuthProvider>
|
||||
</AuthProvider>
|
||||
</ApolloProvider>
|
||||
</NhostProvider>
|
||||
</CacheProvider>
|
||||
</QueryClientProvider>
|
||||
|
||||
82
dashboard/src/utils/__generated__/graphql.ts
generated
82
dashboard/src/utils/__generated__/graphql.ts
generated
@@ -3118,7 +3118,9 @@ 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 = {
|
||||
@@ -3129,7 +3131,9 @@ export type ConfigSystemConfigPostgresComparisonExp = {
|
||||
database?: InputMaybe<ConfigStringComparisonExp>;
|
||||
disk?: InputMaybe<ConfigSystemConfigPostgresDiskComparisonExp>;
|
||||
enabled?: InputMaybe<ConfigBooleanComparisonExp>;
|
||||
encryptColumnKey?: InputMaybe<ConfigStringComparisonExp>;
|
||||
majorVersion?: InputMaybe<ConfigStringComparisonExp>;
|
||||
oldEncryptColumnKey?: InputMaybe<ConfigStringComparisonExp>;
|
||||
};
|
||||
|
||||
export type ConfigSystemConfigPostgresConnectionString = {
|
||||
@@ -3193,7 +3197,9 @@ 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 = {
|
||||
@@ -3201,7 +3207,9 @@ 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 = {
|
||||
@@ -4426,6 +4434,7 @@ 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 */
|
||||
@@ -11094,6 +11103,7 @@ 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>;
|
||||
@@ -11191,6 +11201,7 @@ 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>;
|
||||
@@ -11222,6 +11233,7 @@ 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']>;
|
||||
@@ -11246,6 +11258,7 @@ 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']>;
|
||||
@@ -11268,6 +11281,7 @@ 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>;
|
||||
@@ -11291,6 +11305,7 @@ 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']>;
|
||||
@@ -11313,6 +11328,7 @@ 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>;
|
||||
@@ -11359,6 +11375,7 @@ 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>;
|
||||
@@ -11393,6 +11410,8 @@ export enum Deployments_Select_Column {
|
||||
/** column name */
|
||||
CommitUserName = 'commitUserName',
|
||||
/** column name */
|
||||
CreatedAt = 'createdAt',
|
||||
/** column name */
|
||||
DeploymentEndedAt = 'deploymentEndedAt',
|
||||
/** column name */
|
||||
DeploymentStartedAt = 'deploymentStartedAt',
|
||||
@@ -11427,6 +11446,7 @@ 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']>;
|
||||
@@ -11457,6 +11477,7 @@ 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']>;
|
||||
@@ -11485,6 +11506,8 @@ export enum Deployments_Update_Column {
|
||||
/** column name */
|
||||
CommitUserName = 'commitUserName',
|
||||
/** column name */
|
||||
CreatedAt = 'createdAt',
|
||||
/** column name */
|
||||
DeploymentEndedAt = 'deploymentEndedAt',
|
||||
/** column name */
|
||||
DeploymentStartedAt = 'deploymentStartedAt',
|
||||
@@ -13067,6 +13090,7 @@ 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" */
|
||||
@@ -13968,6 +13992,15 @@ 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'];
|
||||
@@ -27517,6 +27550,16 @@ 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'];
|
||||
}>;
|
||||
@@ -29446,6 +29489,45 @@ 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) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
getGraphqlServiceUrl,
|
||||
getStorageServiceUrl,
|
||||
} from '@/utils/env';
|
||||
import { createClient } from '@nhost/nhost-js';
|
||||
import { createClient, createNhostClient } from '@nhost/nhost-js';
|
||||
import { type Session, type SessionStorageBackend } from '@nhost/nhost-js/session';
|
||||
|
||||
const nhost = createClient({
|
||||
@@ -14,6 +14,13 @@ const nhost = createClient({
|
||||
storageUrl: getStorageServiceUrl(),
|
||||
});
|
||||
|
||||
const nhostRoutesClient = createNhostClient({
|
||||
authUrl: getAuthServiceUrl(),
|
||||
graphqlUrl: getGraphqlServiceUrl(),
|
||||
functionsUrl: getFunctionsServiceUrl(),
|
||||
storageUrl: getStorageServiceUrl(),
|
||||
});
|
||||
|
||||
export class DummySessionStorage implements SessionStorageBackend {
|
||||
private session: Session | null = null;
|
||||
|
||||
@@ -41,4 +48,5 @@ export class DummySessionStorage implements SessionStorageBackend {
|
||||
}
|
||||
}
|
||||
|
||||
export { nhostRoutesClient };
|
||||
export default nhost;
|
||||
|
||||
@@ -122,29 +122,37 @@
|
||||
"group": "Sign In Methods",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Social Providers",
|
||||
"group": "Providers",
|
||||
"icon": "at",
|
||||
"pages": [
|
||||
"products/auth/social/sign-in-apple",
|
||||
"products/auth/social/sign-in-azuread",
|
||||
"products/auth/social/sign-in-discord",
|
||||
"products/auth/social/sign-in-entraid",
|
||||
"products/auth/social/sign-in-facebook",
|
||||
"products/auth/social/sign-in-github",
|
||||
"products/auth/social/sign-in-google",
|
||||
"products/auth/social/sign-in-linkedin",
|
||||
"products/auth/social/sign-in-spotify",
|
||||
"products/auth/social/sign-in-twitch",
|
||||
"products/auth/social/sign-in-workos"
|
||||
"products/auth/providers/overview",
|
||||
"products/auth/providers/tokens",
|
||||
"products/auth/providers/connect",
|
||||
"products/auth/providers/idtokens",
|
||||
{
|
||||
"group": "Configuration",
|
||||
"icon": "gear",
|
||||
"pages": [
|
||||
"products/auth/providers/sign-in-apple",
|
||||
"products/auth/providers/sign-in-azuread",
|
||||
"products/auth/providers/sign-in-discord",
|
||||
"products/auth/providers/sign-in-entraid",
|
||||
"products/auth/providers/sign-in-facebook",
|
||||
"products/auth/providers/sign-in-github",
|
||||
"products/auth/providers/sign-in-google",
|
||||
"products/auth/providers/sign-in-linkedin",
|
||||
"products/auth/providers/sign-in-spotify",
|
||||
"products/auth/providers/sign-in-twitch",
|
||||
"products/auth/providers/sign-in-workos"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"/products/auth/social-connect",
|
||||
"/products/auth/sign-in-email-password",
|
||||
"/products/auth/sign-in-otp",
|
||||
"/products/auth/sign-in-magic-link",
|
||||
"/products/auth/sign-in-sms-otp",
|
||||
"/products/auth/webauthn",
|
||||
"/products/auth/idtokens"
|
||||
"/products/auth/webauthn"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -152,7 +160,6 @@
|
||||
"icon": "diagram-project",
|
||||
"pages": [
|
||||
"/products/auth/workflows/email-password",
|
||||
"/products/auth/workflows/oauth-providers",
|
||||
"/products/auth/workflows/passwordless-email",
|
||||
"/products/auth/workflows/passwordless-sms",
|
||||
"/products/auth/workflows/webauthn",
|
||||
@@ -352,6 +359,7 @@
|
||||
"reference/auth/post-signin-pat",
|
||||
"reference/auth/get-signin-provider-{provider}",
|
||||
"reference/auth/get-signin-provider-{provider}-callback",
|
||||
"reference/auth/get-signin-provider-{provider}-callback-tokens",
|
||||
"reference/auth/post-signin-provider-{provider}-callback",
|
||||
"reference/auth/post-signin-webauthn",
|
||||
"reference/auth/post-signin-webauthn-verify",
|
||||
@@ -360,6 +368,7 @@
|
||||
"reference/auth/post-signup-webauthn",
|
||||
"reference/auth/post-signup-webauthn-verify",
|
||||
"reference/auth/post-token",
|
||||
"reference/auth/post-token-provider-{provider}",
|
||||
"reference/auth/post-token-verify",
|
||||
"reference/auth/get-user",
|
||||
"reference/auth/post-user-deanonymize",
|
||||
@@ -704,6 +713,20 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"redirects": [
|
||||
{
|
||||
"source": "/products/auth/social/:slug*",
|
||||
"destination": "/products/auth/providers/:slug*"
|
||||
},
|
||||
{
|
||||
"source": "/products/auth/social-connect",
|
||||
"destination": "products/auth/providers/connect"
|
||||
},
|
||||
{
|
||||
"source": "/products/auth/idtokens",
|
||||
"destination": "products/auth/providers/idtokens"
|
||||
}
|
||||
],
|
||||
"logo": {
|
||||
"light": "/images/logo/light.svg",
|
||||
"dark": "/images/logo/dark.svg"
|
||||
|
||||
@@ -62,6 +62,7 @@ function build_typedoc() {
|
||||
function build_cli_docs() {
|
||||
echo "⚒️⚒️⚒️ Building CLI documentation..."
|
||||
cli docs > reference/cli/commands.mdx
|
||||
cat reference/cli/commands.mdx
|
||||
}
|
||||
|
||||
build_openapi
|
||||
|
||||
|
Before Width: | Height: | Size: 245 KiB After Width: | Height: | Size: 245 KiB |
@@ -120,21 +120,26 @@ sender = 'myapp@mydomain.com'
|
||||
It is very important that the `host` is set exactly to `postmark`.
|
||||
</Warning>
|
||||
|
||||
2. In postmark you need to create thre templates for each locale with the following alias:
|
||||
2. In postmark you need to create three templates for each locale with the following alias:
|
||||
|
||||
- $locale.email-change
|
||||
- $locale.email-confirm-change
|
||||
- $locale.email-verify
|
||||
- $locale.password-reset
|
||||
|
||||
For instance:
|
||||
|
||||
- en.email-change
|
||||
- en.email-confirm-change
|
||||
- en.email-verify
|
||||
- en.password-reset
|
||||
- se.email-change
|
||||
- se.email-confirm-change
|
||||
- se.email-verify
|
||||
- se.password-reset
|
||||
|
||||
Additionally, if you want to use the passwordless sign-in or OTP sign-in emails, you need to create the following templates as well:
|
||||
|
||||
- $locale.signin-passwordless
|
||||
- $locale.signin-otp
|
||||
|
||||
After these two steps have been completed, the Auth service will leverage these templates instead of the ones described in the previous section.
|
||||
|
||||
<Note>
|
||||
|
||||
@@ -19,7 +19,7 @@ Nhost Auth is a ready-to-use authentication service seamlessly integrated with t
|
||||
</Card>
|
||||
<Card title="Security Keys (WebAuthn)" icon="square-5" href="/products/auth/webauthn">
|
||||
</Card>
|
||||
<Card title="ID Tokens" icon="square-6" href="/products/auth/idtokens">
|
||||
<Card title="ID Tokens" icon="square-6" href="/products/auth/providers/idtokens">
|
||||
</Card>
|
||||
<Card title="Elevated Permissions" icon="square-7" href="/products/auth/elevated-permissions">
|
||||
</Card>
|
||||
@@ -28,24 +28,24 @@ Nhost Auth is a ready-to-use authentication service seamlessly integrated with t
|
||||
### OAuth Providers
|
||||
|
||||
<CardGroup cols={4}>
|
||||
<Card title="Apple" icon="apple" href="/products/auth/social/sign-in-apple">
|
||||
<Card title="Apple" icon="apple" href="/products/auth/providers/sign-in-apple">
|
||||
</Card>
|
||||
<Card title="Discord" icon="discord" href="/products/auth/social/sign-in-discord">
|
||||
<Card title="Discord" icon="discord" href="/products/auth/providers/sign-in-discord">
|
||||
</Card>
|
||||
<Card title="Entra ID" icon="microsoft" href="/products/auth/social/sign-in-entraid">
|
||||
<Card title="Entra ID" icon="microsoft" href="/products/auth/providers/sign-in-entraid">
|
||||
</Card>
|
||||
<Card title="Facebook" icon="facebook" href="/products/auth/social/sign-in-facebook">
|
||||
<Card title="Facebook" icon="facebook" href="/products/auth/providers/sign-in-facebook">
|
||||
</Card>
|
||||
<Card title="GitHub" icon="github" href="/products/auth/social/sign-in-github">
|
||||
<Card title="GitHub" icon="github" href="/products/auth/providers/sign-in-github">
|
||||
</Card>
|
||||
<Card title="Google" icon="google" href="/products/auth/social/sign-in-google">
|
||||
<Card title="Google" icon="google" href="/products/auth/providers/sign-in-google">
|
||||
</Card>
|
||||
<Card title="Linkedin" icon="linkedin" href="/products/auth/social/sign-in-linkedin">
|
||||
<Card title="Linkedin" icon="linkedin" href="/products/auth/providers/sign-in-linkedin">
|
||||
</Card>
|
||||
<Card title="Spotify" icon="spotify" href="/products/auth/social/sign-in-spotify">
|
||||
<Card title="Spotify" icon="spotify" href="/products/auth/providers/sign-in-spotify">
|
||||
</Card>
|
||||
<Card title="Twitch" icon="twitch" href="/products/auth/social/sign-in-twitch">
|
||||
<Card title="Twitch" icon="twitch" href="/products/auth/providers/sign-in-twitch">
|
||||
</Card>
|
||||
<Card title="WorkOS" icon="square-9" href="/products/auth/social/sign-in-workos">
|
||||
<Card title="WorkOS" icon="square-9" href="/products/auth/providers/sign-in-workos">
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
41
docs/products/auth/providers/connect.mdx
Normal file
41
docs/products/auth/providers/connect.mdx
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: Connecting existing users
|
||||
sidebarTitle: Connecting existing users
|
||||
description: Connect a provider to existing user accounts
|
||||
icon: link
|
||||
---
|
||||
|
||||
With the provider connect feature, users can link configured providers with their account, regardless of the initial sign-up method. It enables users to link different providers to their accounts, even if the email addresses do not match (e.g., linking a GitHub profile to an account registered with a different email). This feature offers flexibility, allowing users to streamline their login process by connecting multiple authentication methods.
|
||||
|
||||
To add a provider authentication method to an existing user you need to call the url `https://${subdomain}.auth.${region}.nhost.run/v1/signin/provider/${provider}?connect=${jwt}`. This is very easy to achieve with our SDK:
|
||||
|
||||
``` js
|
||||
nhost.auth.connectProvider({
|
||||
provider: 'github'
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
Keep in mind that as we need a `JWT` the user needs to be logged in.
|
||||
</Note>
|
||||
|
||||
## Viewing and Deleting Provider Authentication Mechanisms
|
||||
|
||||
If you want to allow your users to view and/or delete provider authentication mechanisms, you can provide the necessary permissions to the table `auth.user_providers` (i.e. `select` and/or `delete`) and then use the appropriate GraphQL query. For example, the following permissions should allow users to list their own providers:
|
||||
|
||||

|
||||
|
||||
Using the following GraphQL query:
|
||||
|
||||
``` js
|
||||
const { error, data } = await nhost.graphql.request(
|
||||
gql`
|
||||
query getAuthUserProviders {
|
||||
authUserProviders {
|
||||
id
|
||||
providerId
|
||||
}
|
||||
}
|
||||
`,
|
||||
)
|
||||
```
|
||||
@@ -13,7 +13,9 @@ ID tokens serve as a secure proof that a user has already been authenticated by
|
||||
|
||||
## Usage
|
||||
|
||||
To use ID tokens, you need to configure supported identity providers (currently [apple](/products/auth/social/sign-in-apple) and [google](/products/auth/social/sign-in-google)) and make sure the `audience` is set correctly.
|
||||
<Note>
|
||||
To use ID tokens, you need to configure supported identity providers (currently [apple](/products/auth/providers/sign-in-apple) and [google](/products/auth/providers/sign-in-google)) and make sure the `audience` is set correctly.
|
||||
</Note>
|
||||
|
||||
### Sign in
|
||||
|
||||
@@ -41,7 +43,7 @@ Once everything is configured you can use an ID token to authenticate users with
|
||||
|
||||
### Link Provider to existing user
|
||||
|
||||
Similarly to the [Social Connect](/products/auth/social-connect) feature, you can link an identity provider to an existing user:
|
||||
Similarly to the [Provider Connect](/products/auth/providers/connect) feature, you can link an identity provider to an existing user:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="javascript">
|
||||
@@ -69,10 +71,4 @@ Below you can find some examples on how to extract an ID Token from various iden
|
||||
|
||||
### React Native
|
||||
|
||||
#### Apple
|
||||
|
||||
For an example on how to authenticate using "Sign in with Apple" on iOS using React Native you can refer to our [sample component](https://github.com/nhost/nhost/blob/main/examples/react_native/src/components/SignInWithAppleButton.tsx).
|
||||
|
||||
#### Google
|
||||
|
||||
For an example on how to authenticate using "Sign in with Google" on Android using React Native you can refer to our [sample component](https://github.com/nhost/nhost/blob/main/examples/react_native/src/components/SignInWithGoogleButton.tsx).
|
||||
Please, refer to our [React Native Tutorial](/getting-started/tutorials/reactnative/1-introduction) on how to implement Sign in with Apple and Google Sign-In in your React Native app.
|
||||
61
docs/products/auth/providers/overview.mdx
Normal file
61
docs/products/auth/providers/overview.mdx
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Overview
|
||||
sidebarTitle: Overview
|
||||
description: Learn how OAuth2 authentication works with Nhost
|
||||
icon: at
|
||||
---
|
||||
|
||||
OAuth2 providers allow users to sign in to your Nhost application using their existing accounts from popular services like Google, GitHub, Apple, and many others. This eliminates the need for users to create and remember new credentials while providing a secure authentication method.
|
||||
|
||||
## How OAuth2 Authentication Works
|
||||
|
||||
When a user authenticates with an OAuth2 provider through Nhost, the following workflow occurs:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor U as User
|
||||
participant A as Auth
|
||||
participant P as Oauth Provider
|
||||
participant F as Frontend
|
||||
U->>+A: HTTP GET /signin/provider/{provider}
|
||||
A->>+P: Provider's authentication
|
||||
deactivate A
|
||||
P->>-A: HTTP GET /signin/provider/{provider}/callback
|
||||
activate A
|
||||
opt No user found
|
||||
A->>A: Create user
|
||||
end
|
||||
A->>A: Flag user email as verified
|
||||
A->>+F: HTTP redirect with refresh token
|
||||
deactivate A
|
||||
F->>-U: HTTP OK response
|
||||
opt
|
||||
U->>+A: HTTP POST /token
|
||||
A->>-U: HTTP OK response
|
||||
Note left of A: Refresh token + access token
|
||||
end
|
||||
```
|
||||
|
||||
<Snippet file="workflows/oauth-providers.mdx" />
|
||||
|
||||
### The Authentication Flow
|
||||
|
||||
1. **Initiation**: The user starts the authentication process by making a request to `/signin/provider/{provider}` (e.g., `/signin/provider/google`)
|
||||
|
||||
2. **Provider Authentication**: Auth redirects the user to the OAuth2 provider's authentication page where they log in and grant permissions
|
||||
|
||||
3. **Callback**: After successful authentication, the provider redirects back to Auth at `/signin/provider/{provider}/callback` with an authorization code
|
||||
|
||||
4. **User Management**: Auth processes the callback:
|
||||
- If this is a new user, a user account is automatically created
|
||||
- The user's email is flagged as verified (since the OAuth2 provider has already verified it)
|
||||
|
||||
5. **Token Issuance**: The user is redirected back to your frontend application with a refresh token
|
||||
|
||||
## Benefits of OAuth2 Authentication
|
||||
|
||||
- **Improved User Experience**: Users can sign in with accounts they already have
|
||||
- **Enhanced Security**: No need to manage passwords; authentication is handled by established providers
|
||||
- **Verified Emails**: Email addresses are automatically verified through the OAuth2 provider
|
||||
- **Reduced Registration Friction**: Faster onboarding with one-click sign-in
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user