Compare commits

...

21 Commits

Author SHA1 Message Date
github-actions[bot]
d6f7b01aee release(dashboard): 2.40.0 (#3631)
Co-authored-by: dbarrosop <dbarrosop@users.noreply.github.com>
2025-10-27 15:23:14 +01:00
dependabot[bot]
0fc65df78d chore(ci): bump actions/upload-artifact from 4 to 5 (#3638)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-27 09:50:12 +01:00
dependabot[bot]
52e3db7f61 chore(ci): bump actions/download-artifact from 5 to 6 (#3639)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-27 08:51:05 +01:00
David Barroso
235449d68c chore(docs): update guidelines on the use of AI for contributions (#3637) 2025-10-27 08:46:10 +01:00
Jason Overmier
323834d212 feat(dashboard): allow configuring CSP header (#3627)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: David Barroso <dbarrosop@dravetech.com>
2025-10-23 12:05:04 +02:00
David Barroso
f7bd250f73 chore(ci): changed pull_request to pull_request_target for access to secrets (#3632) 2025-10-23 11:15:29 +02:00
David BM
579f9dbf31 chore(dashboard): various improvements to support ticket page (#3630)
Co-authored-by: robertkasza <robert.kasza@bishop-co.com>
2025-10-23 09:38:45 +02:00
github-actions[bot]
9f2b93d44b release(dashboard): 2.39.0 (#3600)
Co-authored-by: dbarrosop <dbarrosop@users.noreply.github.com>
2025-10-22 10:59:29 +02:00
David Barroso
1aeef26ec6 feat(dashboard): move zendesk request to API route (#3628) 2025-10-22 10:54:42 +02:00
David Barroso
749bb4e637 feat(nhost-js): added various middlewares to work with headers and customizable createNhostClient (#3612) 2025-10-22 10:32:23 +02:00
David Barroso
accabc83f7 chore(cli): update schema (#3622) 2025-10-21 16:53:07 +02:00
David Barroso
8c127d7b6b chore(docs): fix broken link in openapi spec and minor mistakes in postmark integration info (#3621) 2025-10-21 12:32:44 +02:00
David BM
f9c614ef99 chore(deps): update Vite to address security advisory (#3620) 2025-10-21 12:18:29 +02:00
David Barroso
1d183f7fc4 feat(auth): encrypt TOTP secret (#3619) 2025-10-21 09:04:31 +02:00
github-actions[bot]
46e740f060 release(cli): 1.34.2 (#3603)
Co-authored-by: dbarrosop <dbarrosop@users.noreply.github.com>
2025-10-20 14:30:47 +02:00
David Barroso
0d30ab4eec chore(cli): update schema (#3613) 2025-10-20 14:27:49 +02:00
github-actions[bot]
d5fd3cb59c release(services/auth): 0.42.4 (#3618)
Co-authored-by: dbarrosop <dbarrosop@users.noreply.github.com>
2025-10-20 13:25:21 +02:00
David Barroso
f36d360b9e fix(auth): apply relationships on new projects (#3617) 2025-10-20 13:23:33 +02:00
github-actions[bot]
61af5087fd release(services/auth): 0.42.3 (#3608)
Co-authored-by: dbarrosop <dbarrosop@users.noreply.github.com>
2025-10-20 12:42:03 +02:00
David Barroso
7429d8ae3f fix(auth): always apply expected metadata (#3616) 2025-10-20 12:37:52 +02:00
github-actions[bot]
8ce9705b17 release(services/storage): 0.8.2 (#3585)
Co-authored-by: dbarrosop <dbarrosop@users.noreply.github.com>
2025-10-15 14:51:31 +02:00
128 changed files with 5948 additions and 3637 deletions

View File

@@ -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.

View File

@@ -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 [...]

View File

@@ -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:

View File

@@ -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'

View File

@@ -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'

View File

@@ -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

View File

@@ -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'

View File

@@ -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'

View File

@@ -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 }}

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -1,7 +1,7 @@
---
name: "gen: AI review"
on:
pull_request:
pull_request_target:
types: [opened, reopened, ready_for_review]
issue_comment:
jobs:

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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
]
}

View File

@@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file.
## [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

View File

@@ -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)

View File

@@ -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",

View File

@@ -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.8.2"
// 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.42.4"
// 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

View File

@@ -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

View File

@@ -18,13 +18,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=

View File

@@ -2,6 +2,34 @@
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

View File

@@ -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 |

View File

@@ -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",
"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',

View File

@@ -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"
},

View File

@@ -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:

View File

@@ -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 = ''

View File

@@ -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>

View 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',
});
}
}

View File

@@ -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">

View File

@@ -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>
);

View File

@@ -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;

View File

@@ -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>

View File

@@ -1308,7 +1308,7 @@ components:
BearerAuthElevated:
type: http
scheme: bearer
description: "Bearer authentication that requires elevated permissions. Used for sensitive operations that may require additional security measures such as recent authentication. For details see https://docs.nhost.io/guides/auth/elevated-permissions"
description: "Bearer authentication that requires elevated permissions. Used for sensitive operations that may require additional security measures such as recent authentication. For details see https://docs.nhost.io/products/auth/elevated-permissions"
schemas:
AttestationFormat:

View File

@@ -69,6 +69,51 @@ Error.constructor
# Interfaces
## AdminSessionOptions
Configuration options for admin session middleware
### Properties
#### adminSecret
```ts
adminSecret: string
```
Hasura admin secret for elevated permissions (sets x-hasura-admin-secret header)
#### role?
```ts
optional role: string;
```
Hasura role to use for the request (sets x-hasura-role header)
#### sessionVariables?
```ts
optional sessionVariables: Record<string, string>;
```
Additional Hasura session variables to attach to requests.
Keys will be automatically prefixed with 'x-hasura-' if not already present.
##### Example
```ts
{
'user-id': '123',
'org-id': '456'
}
// Results in headers:
// x-hasura-user-id: 123
// x-hasura-org-id: 456
```
---
## FetchResponse&lt;T&gt;
Interface representing a structured API response.
@@ -301,3 +346,151 @@ that create or invalidate sessions.
[`ChainFunction`](#chainfunction)
A middleware function that can be used in the fetch chain
---
## withAdminSessionMiddleware()
```ts
function withAdminSessionMiddleware(options: AdminSessionOptions): ChainFunction
```
Creates a fetch middleware that attaches the Hasura admin secret and optional session variables to requests.
This middleware:
1. Sets the x-hasura-admin-secret header, which grants full admin access to Hasura
2. Optionally sets the x-hasura-role header if a role is provided
3. Optionally sets additional x-hasura-\* headers for custom session variables
**Security Warning**: Never use this middleware in client-side code or expose
the admin secret to end users. Admin secrets grant unrestricted access to your
entire database. This should only be used in trusted server-side environments.
The middleware preserves request-specific headers when they conflict with the
admin session configuration.
### Parameters
| Parameter | Type | Description |
| --------- | --------------------------------------------- | ------------------------------------------------------------------------- |
| `options` | [`AdminSessionOptions`](#adminsessionoptions) | Admin session options including admin secret, role, and session variables |
### Returns
[`ChainFunction`](#chainfunction)
A middleware function that can be used in the fetch chain
### Example
```ts
// Create middleware with admin secret only
const adminMiddleware = withAdminSessionMiddleware({
adminSecret: process.env.NHOST_ADMIN_SECRET
})
// Create middleware with admin secret and role
const adminUserMiddleware = withAdminSessionMiddleware({
adminSecret: process.env.NHOST_ADMIN_SECRET,
role: 'user'
})
// Create middleware with admin secret, role, and custom session variables
const fullMiddleware = withAdminSessionMiddleware({
adminSecret: process.env.NHOST_ADMIN_SECRET,
role: 'user',
sessionVariables: {
'user-id': '123',
'org-id': '456'
}
})
// Use with createCustomClient for an admin client
const adminClient = createCustomClient({
subdomain: 'myproject',
region: 'eu-central-1',
chainFunctions: [adminMiddleware]
})
```
---
## withHeadersMiddleware()
```ts
function withHeadersMiddleware(defaultHeaders: HeadersInit): ChainFunction
```
Creates a fetch middleware that attaches default headers to requests.
This middleware:
1. Merges default headers with request-specific headers
2. Preserves request-specific headers when they conflict with defaults
The middleware ensures consistent headers across requests while allowing
individual requests to override defaults as needed.
### Parameters
| Parameter | Type | Description |
| ---------------- | ------------- | ----------------------------------------- |
| `defaultHeaders` | `HeadersInit` | Default headers to attach to all requests |
### Returns
[`ChainFunction`](#chainfunction)
A middleware function that can be used in the fetch chain
---
## withRoleMiddleware()
```ts
function withRoleMiddleware(role: string): ChainFunction
```
Creates a fetch middleware that sets the Hasura role header.
This middleware sets the x-hasura-role header for all requests, allowing
you to specify which role's permissions should be used. This works with
authenticated sessions where the user has access to the specified role.
Unlike `withAdminSessionMiddleware`, this does not bypass permission rules
but instead uses the permission rules defined for the specified role.
The middleware preserves request-specific headers when they conflict with
the role configuration.
### Parameters
| Parameter | Type | Description |
| --------- | -------- | ----------------------------------- |
| `role` | `string` | The Hasura role to use for requests |
### Returns
[`ChainFunction`](#chainfunction)
A middleware function that can be used in the fetch chain
### Example
```ts
// Use with createClient to default all requests to a specific role
const nhost = createClient({
subdomain: 'myproject',
region: 'eu-central-1',
chainFunctions: [withRoleMiddleware('moderator')]
})
// Use with createServerClient for server-side requests
const serverNhost = createServerClient({
subdomain: 'myproject',
region: 'eu-central-1',
storage: myServerStorage,
chainFunctions: [withRoleMiddleware('moderator')]
})
```

View File

@@ -54,7 +54,7 @@ console.log(JSON.stringify(uplFilesResp, null, 2))
// "updatedAt": "2025-05-09T17:26:04.589692+00:00",
// "isUploaded": true,
// "mimeType": "text/plain",
// "uploadedByUserId": "",
// "uploadedByUserId": "3357aada-b6c7-4af1-9655-1307ca2883a2",
// "metadata": null
// },
// {
@@ -67,7 +67,7 @@ console.log(JSON.stringify(uplFilesResp, null, 2))
// "updatedAt": "2025-05-09T17:26:04.596831+00:00",
// "isUploaded": true,
// "mimeType": "text/plain",
// "uploadedByUserId": "",
// "uploadedByUserId": "3357aada-b6c7-4af1-9655-1307ca2883a2",
// "metadata": null
// }
// ]
@@ -125,6 +125,77 @@ console.log(JSON.stringify(funcResp.body, null, 2))
// }
```
## Creating an admin client
You can also create an admin client if needed. This client will have admin access to the database
and will bypass permissions. Additionally, it can impersonate users and set any role or session
variable.
IMPORTANT!!! Keep your admin secret safe and never expose it in client-side code.
```ts
const nhost = createNhostClient({
subdomain,
region,
configure: [
withAdminSession({
adminSecret: 'nhost-admin-secret',
role: 'user',
sessionVariables: {
'user-id': '54058C42-51F7-4B37-8B69-C89A841D2221'
}
})
]
})
// upload a couple of files
const uplFilesResp = await nhost.storage.uploadFiles({
'file[]': [
new File(['test1'], 'file-1', { type: 'text/plain' }),
new File(['test2 is larger'], 'file-2', { type: 'text/plain' })
]
})
console.log(JSON.stringify(uplFilesResp, null, 2))
// {
// "data": {
// "processedFiles": [
// {
// "id": "c0e83185-0ce5-435c-bd46-9841adc30699",
// "name": "file-1",
// "size": 5,
// "bucketId": "default",
// "etag": "\"5a105e8b9d40e1329780d62ea2265d8a\"",
// "createdAt": "2025-05-09T17:26:04.579839+00:00",
// "updatedAt": "2025-05-09T17:26:04.589692+00:00",
// "isUploaded": true,
// "mimeType": "text/plain",
// "uploadedByUserId": "54058c42-51f7-4b37-8b69-c89a841d2221",
// "metadata": null
// },
// {
// "id": "3f189004-21fd-42d0-be1d-1ead021ab167",
// "name": "file-2",
// "size": 15,
// "bucketId": "default",
// "etag": "\"302e888c5e289fe6b02115b748771ee9\"",
// "createdAt": "2025-05-09T17:26:04.59245+00:00",
// "updatedAt": "2025-05-09T17:26:04.596831+00:00",
// "isUploaded": true,
// "mimeType": "text/plain",
// "uploadedByUserId": "54058c42-51f7-4b37-8b69-c89a841d2221",
// "metadata": null
// }
// ]
// },
// "status": 201,
// "headers": {
// "content-length": "644",
// "content-type": "application/json; charset=utf-8",
// "date": "Fri, 09 May 2025 17:26:04 GMT"
// }
// }
```
# Interfaces
## NhostClient
@@ -287,6 +358,15 @@ optional authUrl: string;
Complete base URL for the auth service (overrides subdomain/region)
#### configure?
```ts
optional configure: ClientConfigurationFn[];
```
Configuration functions to be applied to the client after initialization.
These functions receive all clients and can attach middleware or perform other setup.
#### functionsUrl?
```ts
@@ -360,6 +440,19 @@ Complete base URL for the auth service (overrides subdomain/region)
[`NhostClientOptions`](#nhostclientoptions).[`authUrl`](#authurl)
#### configure?
```ts
optional configure: ClientConfigurationFn[];
```
Configuration functions to be applied to the client after initialization.
These functions receive all clients and can attach middleware or perform other setup.
##### Inherited from
[`NhostClientOptions`](#nhostclientoptions).[`configure`](#configure)
#### functionsUrl?
```ts
@@ -434,6 +527,55 @@ Nhost project subdomain (e.g., 'abcdefgh'). Used to construct the base URL for s
[`NhostClientOptions`](#nhostclientoptions).[`subdomain`](#subdomain)
# Type Aliases
## ClientConfigurationFn()
```ts
type ClientConfigurationFn = (clients: object) => void
```
Configuration function that receives all clients and can configure them
(e.g., by attaching middleware, setting up interceptors, etc.)
### Parameters
| Parameter | Type |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clients` | \{ `auth`: [`Client`](./auth#client); `functions`: [`Client`](./functions#client); `graphql`: [`Client`](./graphql#client); `sessionStorage`: [`SessionStorage`](./session#sessionstorage); `storage`: [`Client`](./storage#client); \} |
| `clients.auth` | [`Client`](./auth#client) |
| `clients.functions` | [`Client`](./functions#client) |
| `clients.graphql` | [`Client`](./graphql#client) |
| `clients.sessionStorage` | [`SessionStorage`](./session#sessionstorage) |
| `clients.storage` | [`Client`](./storage#client) |
### Returns
`void`
# Variables
## withClientSideSessionMiddleware
```ts
const withClientSideSessionMiddleware: ClientConfigurationFn
```
Built-in configuration for client-side applications.
Includes automatic session refresh, token attachment, and session updates.
---
## withServerSideSessionMiddleware
```ts
const withServerSideSessionMiddleware: ClientConfigurationFn
```
Built-in configuration for server-side applications.
Includes token attachment and session updates, but NOT automatic session refresh
to prevent race conditions in server contexts.
# Functions
## createClient()
@@ -495,6 +637,72 @@ const nhost = createClient({
secure: import.meta.env.ENVIRONMENT === 'production'
})
})
// Create client with additional custom middleware
const nhost = createClient({
subdomain: 'abcdefgh',
region: 'eu-central-1',
configure: [customLoggingMiddleware]
})
```
---
## createNhostClient()
```ts
function createNhostClient(options: NhostClientOptions): NhostClient
```
Creates and configures a new Nhost client instance with custom configuration.
This is the main factory function for creating Nhost clients. It instantiates
all service clients (auth, storage, graphql, functions) and applies the provided
configuration functions to set up middleware and other customizations.
### Parameters
| Parameter | Type | Description |
| --------- | ------------------------------------------- | ------------------------------------ |
| `options` | [`NhostClientOptions`](#nhostclientoptions) | Configuration options for the client |
### Returns
[`NhostClient`](#nhostclient)
A configured Nhost client
### Example
```ts
// Create a basic client with no middleware
const nhost = createNhostClient({
subdomain: 'abcdefgh',
region: 'eu-central-1',
configure: []
})
// Create a client with custom configuration
const nhost = createNhostClient({
subdomain: 'abcdefgh',
region: 'eu-central-1',
configure: [withClientSideSessionMiddleware, withChainFunctions([customLoggingMiddleware])]
})
// Create an admin client
const nhost = createNhostClient({
subdomain,
region,
configure: [
withAdminSession({
adminSecret: 'nhost-admin-secret',
role: 'user',
sessionVariables: {
'user-id': '54058C42-51F7-4B37-8B69-C89A841D2221'
}
})
]
})
```
---
@@ -623,6 +831,14 @@ const nhostClientFromCookies = (req: Request) => {
}
})
}
// Example with additional custom middleware
const nhost = createServerClient({
region: process.env['NHOST_REGION'] || 'local',
subdomain: process.env['NHOST_SUBDOMAIN'] || 'local',
storage: myStorage,
configure: [customLoggingMiddleware]
})
```
---
@@ -631,7 +847,7 @@ const nhostClientFromCookies = (req: Request) => {
```ts
function generateServiceUrl(
serviceType: 'storage' | 'auth' | 'graphql' | 'functions',
serviceType: 'auth' | 'storage' | 'graphql' | 'functions',
subdomain?: string,
region?: string,
customUrl?: string
@@ -644,7 +860,7 @@ Generates a base URL for a Nhost service based on configuration
| Parameter | Type | Description |
| ------------- | ------------------------------------------------------- | --------------------------------------------------- |
| `serviceType` | `"storage"` \| `"auth"` \| `"graphql"` \| `"functions"` | Type of service (auth, storage, graphql, functions) |
| `serviceType` | `"auth"` \| `"storage"` \| `"graphql"` \| `"functions"` | Type of service (auth, storage, graphql, functions) |
| `subdomain?` | `string` | Nhost project subdomain |
| `region?` | `string` | Nhost region |
| `customUrl?` | `string` | Custom URL override if provided |
@@ -654,3 +870,29 @@ Generates a base URL for a Nhost service based on configuration
`string`
The base URL for the service
---
## withAdminSession()
```ts
function withAdminSession(adminSession: AdminSessionOptions): ClientConfigurationFn
```
Configuration for admin clients with elevated privileges.
Applies admin session middleware to storage, graphql, and functions clients only.
**Security Warning**: Never use this in client-side code. Admin secrets grant
unrestricted access to your entire database.
### Parameters
| Parameter | Type | Description |
| -------------- | ------------------------------------------------------ | ------------------------------------------------------------------------- |
| `adminSession` | [`AdminSessionOptions`](./fetch#adminsessionoptions) | Admin session options including admin secret, role, and session variables |
### Returns
[`ClientConfigurationFn`](#clientconfigurationfn)
Configuration function that sets up admin middleware

View File

@@ -1,70 +0,0 @@
table:
name: attachments
schema: public
configuration:
column_config:
task_id:
custom_name: taskID
custom_column_names:
task_id: taskID
custom_root_fields:
delete: deleteAttachments
delete_by_pk: deleteAttachment
insert: insertAttachments
insert_one: insertAttachment
select: attachments
select_aggregate: attachmentsAggregate
select_by_pk: attachment
select_stream: attachmentsStream
update: updateAttachments
update_by_pk: updateAttachment
update_many: updateAttachmentsMany
object_relationships:
- name: file
using:
foreign_key_constraint_on: file_id
- name: task
using:
foreign_key_constraint_on: task_id
insert_permissions:
- role: user
permission:
check:
_and:
- file:
uploaded_by_user_id:
_eq: X-Hasura-User-Id
- task:
user_id:
_eq: X-Hasura-User-Id
columns:
- file_id
- task_id
comment: ""
select_permissions:
- role: user
permission:
columns:
- file_id
- task_id
filter:
_and:
- file:
uploaded_by_user_id:
_eq: X-Hasura-User-Id
- task:
user_id:
_eq: X-Hasura-User-Id
comment: ""
delete_permissions:
- role: user
permission:
filter:
_and:
- file:
uploaded_by_user_id:
_eq: X-Hasura-User-Id
- task:
user_id:
_eq: X-Hasura-User-Id
comment: ""

View File

@@ -1,86 +0,0 @@
table:
name: comments
schema: public
configuration:
column_config:
comment:
custom_name: comment
created_at:
custom_name: createdAt
id:
custom_name: id
ninja_turtle_id:
custom_name: ninjaTurtleId
updated_at:
custom_name: updatedAt
user_id:
custom_name: userId
custom_column_names:
comment: comment
created_at: createdAt
id: id
ninja_turtle_id: ninjaTurtleId
updated_at: updatedAt
user_id: userId
custom_name: comments
custom_root_fields:
delete: deleteComments
delete_by_pk: deleteComment
insert: insertComments
insert_one: insertComment
select: comments
select_aggregate: commentsAggregate
select_by_pk: comment
select_stream: commentsStream
update: updateComments
update_by_pk: updateComment
update_many: updateCommentsMany
object_relationships:
- name: ninjaTurtle
using:
foreign_key_constraint_on: ninja_turtle_id
- name: user
using:
foreign_key_constraint_on: user_id
insert_permissions:
- role: user
permission:
check:
user_id:
_eq: X-Hasura-User-Id
set:
user_id: x-hasura-User-Id
columns:
- ninja_turtle_id
- comment
comment: Allow users to add comments, automatically setting their user_id
select_permissions:
- role: user
permission:
columns:
- id
- created_at
- updated_at
- ninja_turtle_id
- comment
- user_id
filter: {}
allow_aggregations: true
comment: Allow users to view all comments
update_permissions:
- role: user
permission:
columns:
- comment
filter:
user_id:
_eq: X-Hasura-User-Id
check: {}
comment: Allow users to update only their own comments
delete_permissions:
- role: user
permission:
filter:
user_id:
_eq: X-Hasura-User-Id
comment: Allow users to delete only their own comments

View File

@@ -1,16 +0,0 @@
table:
name: movies
schema: public
select_permissions:
- role: public
permission:
columns:
- id
- title
- director
- release_year
- genre
- rating
- created_at
- updated_at
filter: {}

View File

@@ -1,50 +0,0 @@
table:
name: ninja_turtles
schema: public
configuration:
column_config:
created_at:
custom_name: createdAt
id:
custom_name: id
name:
custom_name: name
updated_at:
custom_name: updatedAt
custom_column_names:
created_at: createdAt
id: id
name: name
updated_at: updatedAt
custom_name: ninjaTurtles
custom_root_fields:
delete: deleteNinjaTurtles
delete_by_pk: deleteNinjaTurtle
insert: insertNinjaTurtles
insert_one: insertNinjaTurtle
select: ninjaTurtles
select_aggregate: ninjaTurtlesAggregate
select_by_pk: ninjaTurtle
select_stream: ninjaTurtlesStream
update: updateNinjaTurtles
update_by_pk: updateNinjaTurtle
update_many: updateNinjaTurtlesMany
array_relationships:
- name: comments
using:
foreign_key_constraint_on:
column: ninja_turtle_id
table:
name: comments
schema: public
select_permissions:
- role: user
permission:
columns:
- created_at
- description
- id
- name
- updated_at
filter: {}
comment: ""

View File

@@ -1,74 +0,0 @@
table:
name: tasks
schema: public
configuration:
column_config:
created_at:
custom_name: createdAt
updated_at:
custom_name: updatedAt
user_id:
custom_name: userID
custom_column_names:
created_at: createdAt
updated_at: updatedAt
user_id: userID
custom_root_fields:
delete: deleteTasks
delete_by_pk: deleteTask
insert: insertTasks
insert_one: insertTask
select: tasks
select_aggregate: tasksAggregate
select_by_pk: task
select_stream: tasksStream
update: updateTasks
update_by_pk: updateTask
update_many: updateTasksMany
insert_permissions:
- role: user
permission:
check:
user_id:
_eq: X-Hasura-User-Id
set:
user_id: x-hasura-User-Id
columns:
- completed
- description
- title
comment: ""
select_permissions:
- role: user
permission:
columns:
- completed
- description
- title
- created_at
- updated_at
- id
- user_id
filter:
user_id:
_eq: X-Hasura-User-Id
comment: ""
update_permissions:
- role: user
permission:
columns:
- completed
- description
- title
filter: {}
check:
user_id:
_eq: X-Hasura-User-Id
comment: ""
delete_permissions:
- role: user
permission:
filter:
user_id:
_eq: X-Hasura-User-Id
comment: ""

View File

@@ -0,0 +1,47 @@
table:
name: todos
schema: public
insert_permissions:
- role: user
permission:
check: {}
set:
user_id: X-Hasura-User-Id
columns:
- created_at
- updated_at
- title
- details
- completed
- user_id
select_permissions:
- role: user
permission:
columns:
- id
- created_at
- updated_at
- title
- details
- completed
- user_id
filter:
user_id:
_eq: X-Hasura-User-Id
update_permissions:
- role: user
permission:
columns:
- title
- details
- completed
filter:
user_id:
_eq: X-Hasura-User-Id
check: null
delete_permissions:
- role: user
permission:
filter:
user_id:
_eq: X-Hasura-User-Id

View File

@@ -57,7 +57,7 @@ insert_permissions:
permission:
check:
bucket_id:
_eq: personal
_eq: default
set:
uploaded_by_user_id: X-Hasura-User-Id
columns:
@@ -84,7 +84,7 @@ select_permissions:
filter:
_and:
- bucket_id:
_eq: personal
_eq: default
- uploaded_by_user_id:
_eq: X-Hasura-User-Id
delete_permissions:

View File

@@ -1,18 +0,0 @@
CREATE TABLE "public"."tasks" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "title" text NOT NULL, "description" text NOT NULL, "completed" boolean NOT NULL DEFAULT false, "user_id" uuid NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON UPDATE cascade ON DELETE cascade);
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
RETURNS TRIGGER AS $$
DECLARE
_new record;
BEGIN
_new := NEW;
_new."updated_at" = NOW();
RETURN _new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "set_public_tasks_updated_at"
BEFORE UPDATE ON "public"."tasks"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_tasks_updated_at" ON "public"."tasks"
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
CREATE EXTENSION IF NOT EXISTS pgcrypto;

View File

@@ -1 +0,0 @@
CREATE TABLE "public"."attachments" ("task_id" uuid NOT NULL, "file_id" uuid NOT NULL, PRIMARY KEY ("task_id","file_id") , FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("file_id") REFERENCES "storage"."files"("id") ON UPDATE restrict ON DELETE restrict);

View File

@@ -1,6 +0,0 @@
CREATE TABLE public.ninja_turtles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
name TEXT NOT NULL
);

View File

@@ -1,8 +0,0 @@
CREATE TABLE public.comments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
ninja_turtle_id UUID NOT NULL REFERENCES public.ninja_turtles(id) ON DELETE CASCADE,
comment TEXT NOT NULL,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE
);

View File

@@ -1 +0,0 @@
DELETE FROM public.ninja_turtles WHERE name IN ('Leonardo', 'Raphael', 'Donatello', 'Michelangelo');

View File

@@ -1,5 +0,0 @@
INSERT INTO public.ninja_turtles (id, name, created_at, updated_at) VALUES
(gen_random_uuid(), 'Leonardo', NOW(), NOW()),
(gen_random_uuid(), 'Raphael', NOW(), NOW()),
(gen_random_uuid(), 'Donatello', NOW(), NOW()),
(gen_random_uuid(), 'Michelangelo', NOW(), NOW());

View File

@@ -1 +0,0 @@
ALTER TABLE public.ninja_turtles DROP COLUMN description;

View File

@@ -1,5 +0,0 @@
ALTER TABLE public.ninja_turtles ADD COLUMN description TEXT;
UPDATE public.ninja_turtles SET description = 'Leader of the team who wears a blue mask and uses twin katana blades. Known for his strategic mind and unwavering discipline.' WHERE name = 'Leonardo';
UPDATE public.ninja_turtles SET description = 'Hot-headed member who wears a red mask and fights with twin sai. Known for his strength, aggression, and intense loyalty to his family.' WHERE name = 'Raphael';
UPDATE public.ninja_turtles SET description = 'Technical genius who wears a purple mask and wields a bo staff. Known for his intelligence, inventions, and problem-solving abilities.' WHERE name = 'Donatello';
UPDATE public.ninja_turtles SET description = 'Fun-loving member who wears an orange mask and uses nunchaku. Known for his optimism, sense of humor, and love for pizza.' WHERE name = 'Michelangelo';

View File

@@ -1,2 +0,0 @@
CREATE TABLE movies (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title TEXT NOT NULL, director TEXT, release_year INTEGER, genre TEXT, rating DECIMAL(3,1), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW());
INSERT INTO movies (id, title, director, release_year, genre, rating) VALUES ('3d67a6d0-bfb5-444a-9152-aea543ebd171', 'The Matrix', 'Lana Wachowski, Lilly Wachowski', 1999, 'Sci-Fi', 8.7), ('90f374db-16c1-4db5-ba55-643bf38953d3', 'Inception', 'Christopher Nolan', 2010, 'Sci-Fi', 8.8), ('900fa76c-fc79-470d-817b-4dc4412a79e8', 'The Godfather', 'Francis Ford Coppola', 1972, 'Crime', 9.2), ('2867cadd-2904-482f-b43c-f77ce8412a93', 'Pulp Fiction', 'Quentin Tarantino', 1994, 'Crime', 8.9), ('8c06c70a-872e-49a7-8770-29355dcd05c6', 'The Dark Knight', 'Christopher Nolan', 2008, 'Action', 9.0);

View File

@@ -31,7 +31,7 @@ httpPoolSize = 100
version = 22
[auth]
version = '0.41.1'
version = '0.42.2'
[auth.elevatedPrivileges]
mode = 'disabled'
@@ -191,7 +191,7 @@ capacity = 1
[provider]
[storage]
version = '0.8.0-beta3'
version = '0.8.2'
[observability]
[observability.grafana]

View File

@@ -32,14 +32,10 @@ importers:
version: 19.1.9(@types/react@19.1.12)
'@vitejs/plugin-react':
specifier: ^4.4.1
version: 4.7.0(vite@7.1.4)
version: 4.7.0(vite@7.1.11)
packages:
'@ampproject/remapping@2.3.0':
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
'@babel/code-frame@7.27.1':
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
engines: {node: '>=6.9.0'}
@@ -48,8 +44,8 @@ packages:
resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==}
engines: {node: '>=6.9.0'}
'@babel/core@7.28.3':
resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==}
'@babel/core@7.28.4':
resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==}
engines: {node: '>=6.9.0'}
'@babel/generator@7.28.3':
@@ -90,12 +86,12 @@ packages:
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
engines: {node: '>=6.9.0'}
'@babel/helpers@7.28.3':
resolution: {integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==}
'@babel/helpers@7.28.4':
resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
engines: {node: '>=6.9.0'}
'@babel/parser@7.28.3':
resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==}
'@babel/parser@7.28.4':
resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -115,12 +111,12 @@ packages:
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
engines: {node: '>=6.9.0'}
'@babel/traverse@7.28.3':
resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==}
'@babel/traverse@7.28.4':
resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
engines: {node: '>=6.9.0'}
'@babel/types@7.28.2':
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
'@babel/types@7.28.4':
resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
engines: {node: '>=6.9.0'}
'@esbuild/aix-ppc64@0.25.9':
@@ -282,6 +278,9 @@ packages:
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
'@jridgewell/remapping@2.3.5':
resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
'@jridgewell/resolve-uri@3.1.2':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
@@ -576,8 +575,8 @@ packages:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
tinyglobby@0.2.14:
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
update-browserslist-db@1.1.3:
@@ -586,8 +585,8 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
vite@7.1.4:
resolution: {integrity: sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==}
vite@7.1.11:
resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -631,11 +630,6 @@ packages:
snapshots:
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.30
'@babel/code-frame@7.27.1':
dependencies:
'@babel/helper-validator-identifier': 7.27.1
@@ -644,18 +638,18 @@ snapshots:
'@babel/compat-data@7.28.0': {}
'@babel/core@7.28.3':
'@babel/core@7.28.4':
dependencies:
'@ampproject/remapping': 2.3.0
'@babel/code-frame': 7.27.1
'@babel/generator': 7.28.3
'@babel/helper-compilation-targets': 7.27.2
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3)
'@babel/helpers': 7.28.3
'@babel/parser': 7.28.3
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
'@babel/helpers': 7.28.4
'@babel/parser': 7.28.4
'@babel/template': 7.27.2
'@babel/traverse': 7.28.3
'@babel/types': 7.28.2
'@babel/traverse': 7.28.4
'@babel/types': 7.28.4
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
debug: 4.4.1
gensync: 1.0.0-beta.2
@@ -666,8 +660,8 @@ snapshots:
'@babel/generator@7.28.3':
dependencies:
'@babel/parser': 7.28.3
'@babel/types': 7.28.2
'@babel/parser': 7.28.4
'@babel/types': 7.28.4
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.30
jsesc: 3.1.0
@@ -684,17 +678,17 @@ snapshots:
'@babel/helper-module-imports@7.27.1':
dependencies:
'@babel/traverse': 7.28.3
'@babel/types': 7.28.2
'@babel/traverse': 7.28.4
'@babel/types': 7.28.4
transitivePeerDependencies:
- supports-color
'@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)':
'@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
dependencies:
'@babel/core': 7.28.3
'@babel/core': 7.28.4
'@babel/helper-module-imports': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
'@babel/traverse': 7.28.3
'@babel/traverse': 7.28.4
transitivePeerDependencies:
- supports-color
@@ -706,44 +700,44 @@ snapshots:
'@babel/helper-validator-option@7.27.1': {}
'@babel/helpers@7.28.3':
'@babel/helpers@7.28.4':
dependencies:
'@babel/template': 7.27.2
'@babel/types': 7.28.2
'@babel/types': 7.28.4
'@babel/parser@7.28.3':
'@babel/parser@7.28.4':
dependencies:
'@babel/types': 7.28.2
'@babel/types': 7.28.4
'@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.3)':
'@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)':
dependencies:
'@babel/core': 7.28.3
'@babel/core': 7.28.4
'@babel/helper-plugin-utils': 7.27.1
'@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.3)':
'@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)':
dependencies:
'@babel/core': 7.28.3
'@babel/core': 7.28.4
'@babel/helper-plugin-utils': 7.27.1
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/parser': 7.28.3
'@babel/types': 7.28.2
'@babel/parser': 7.28.4
'@babel/types': 7.28.4
'@babel/traverse@7.28.3':
'@babel/traverse@7.28.4':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/generator': 7.28.3
'@babel/helper-globals': 7.28.0
'@babel/parser': 7.28.3
'@babel/parser': 7.28.4
'@babel/template': 7.27.2
'@babel/types': 7.28.2
'@babel/types': 7.28.4
debug: 4.4.1
transitivePeerDependencies:
- supports-color
'@babel/types@7.28.2':
'@babel/types@7.28.4':
dependencies:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
@@ -831,6 +825,11 @@ snapshots:
'@jridgewell/sourcemap-codec': 1.5.5
'@jridgewell/trace-mapping': 0.3.30
'@jridgewell/remapping@2.3.5':
dependencies:
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.30
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/sourcemap-codec@1.5.5': {}
@@ -909,24 +908,24 @@ snapshots:
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.28.3
'@babel/types': 7.28.2
'@babel/parser': 7.28.4
'@babel/types': 7.28.4
'@types/babel__generator': 7.27.0
'@types/babel__template': 7.4.4
'@types/babel__traverse': 7.28.0
'@types/babel__generator@7.27.0':
dependencies:
'@babel/types': 7.28.2
'@babel/types': 7.28.4
'@types/babel__template@7.4.4':
dependencies:
'@babel/parser': 7.28.3
'@babel/types': 7.28.2
'@babel/parser': 7.28.4
'@babel/types': 7.28.4
'@types/babel__traverse@7.28.0':
dependencies:
'@babel/types': 7.28.2
'@babel/types': 7.28.4
'@types/estree@1.0.8': {}
@@ -938,15 +937,15 @@ snapshots:
dependencies:
csstype: 3.1.3
'@vitejs/plugin-react@4.7.0(vite@7.1.4)':
'@vitejs/plugin-react@4.7.0(vite@7.1.11)':
dependencies:
'@babel/core': 7.28.3
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.3)
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.3)
'@babel/core': 7.28.4
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4)
'@rolldown/pluginutils': 1.0.0-beta.27
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
vite: 7.1.4
vite: 7.1.11
transitivePeerDependencies:
- supports-color
@@ -1095,7 +1094,7 @@ snapshots:
source-map-js@1.2.1: {}
tinyglobby@0.2.14:
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
@@ -1106,14 +1105,14 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
vite@7.1.4:
vite@7.1.11:
dependencies:
esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.50.0
tinyglobby: 0.2.14
tinyglobby: 0.2.15
optionalDependencies:
fsevents: 2.3.3

View File

@@ -30,6 +30,6 @@
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"vite": "^7.0.4"
"vite": "^7.0.8"
}
}

View File

@@ -17,13 +17,13 @@ importers:
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^6.0.0
version: 6.1.0(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.4))(svelte@5.38.6)(vite@7.1.4))
version: 6.1.0(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.11))(svelte@5.38.6)(vite@7.1.11))
'@sveltejs/kit':
specifier: ^2.22.0
version: 2.37.0(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.4))(svelte@5.38.6)(vite@7.1.4)
version: 2.37.0(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.11))(svelte@5.38.6)(vite@7.1.11)
'@sveltejs/vite-plugin-svelte':
specifier: ^6.0.0
version: 6.1.4(svelte@5.38.6)(vite@7.1.4)
version: 6.1.4(svelte@5.38.6)(vite@7.1.11)
globals:
specifier: ^16.0.0
version: 16.3.0
@@ -43,8 +43,8 @@ importers:
specifier: ^5.0.0
version: 5.9.2
vite:
specifier: ^7.0.4
version: 7.1.4
specifier: ^7.0.8
version: 7.1.11
packages:
@@ -535,8 +535,8 @@ packages:
resolution: {integrity: sha512-ltBPlkvqk3bgCK7/N323atUpP3O3Y+DrGV4dcULrsSn4fZaaNnOmdplNznwfdWclAgvSr5rxjtzn/zJhRm6TKg==}
engines: {node: '>=18'}
tinyglobby@0.2.14:
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
totalist@3.0.1:
@@ -548,8 +548,8 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
vite@7.1.4:
resolution: {integrity: sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==}
vite@7.1.11:
resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -771,15 +771,15 @@ snapshots:
dependencies:
acorn: 8.15.0
'@sveltejs/adapter-auto@6.1.0(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.4))(svelte@5.38.6)(vite@7.1.4))':
'@sveltejs/adapter-auto@6.1.0(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.11))(svelte@5.38.6)(vite@7.1.11))':
dependencies:
'@sveltejs/kit': 2.37.0(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.4))(svelte@5.38.6)(vite@7.1.4)
'@sveltejs/kit': 2.37.0(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.11))(svelte@5.38.6)(vite@7.1.11)
'@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.4))(svelte@5.38.6)(vite@7.1.4)':
'@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.11))(svelte@5.38.6)(vite@7.1.11)':
dependencies:
'@standard-schema/spec': 1.0.0
'@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
'@sveltejs/vite-plugin-svelte': 6.1.4(svelte@5.38.6)(vite@7.1.4)
'@sveltejs/vite-plugin-svelte': 6.1.4(svelte@5.38.6)(vite@7.1.11)
'@types/cookie': 0.6.0
acorn: 8.15.0
cookie: 0.6.0
@@ -792,26 +792,26 @@ snapshots:
set-cookie-parser: 2.7.1
sirv: 3.0.2
svelte: 5.38.6
vite: 7.1.4
vite: 7.1.11
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.4))(svelte@5.38.6)(vite@7.1.4)':
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.11))(svelte@5.38.6)(vite@7.1.11)':
dependencies:
'@sveltejs/vite-plugin-svelte': 6.1.4(svelte@5.38.6)(vite@7.1.4)
'@sveltejs/vite-plugin-svelte': 6.1.4(svelte@5.38.6)(vite@7.1.11)
debug: 4.4.1
svelte: 5.38.6
vite: 7.1.4
vite: 7.1.11
transitivePeerDependencies:
- supports-color
'@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.4)':
'@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.11)':
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.4))(svelte@5.38.6)(vite@7.1.4)
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.6)(vite@7.1.11))(svelte@5.38.6)(vite@7.1.11)
debug: 4.4.1
deepmerge: 4.3.1
magic-string: 0.30.18
svelte: 5.38.6
vite: 7.1.4
vitefu: 1.1.1(vite@7.1.4)
vite: 7.1.11
vitefu: 1.1.1(vite@7.1.11)
transitivePeerDependencies:
- supports-color
@@ -994,7 +994,7 @@ snapshots:
magic-string: 0.30.18
zimmerframe: 1.1.2
tinyglobby@0.2.14:
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
@@ -1003,19 +1003,19 @@ snapshots:
typescript@5.9.2: {}
vite@7.1.4:
vite@7.1.11:
dependencies:
esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.50.0
tinyglobby: 0.2.14
tinyglobby: 0.2.15
optionalDependencies:
fsevents: 2.3.3
vitefu@1.1.1(vite@7.1.4):
vitefu@1.1.1(vite@7.1.11):
optionalDependencies:
vite: 7.1.4
vite: 7.1.11
zimmerframe@1.1.2: {}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,6 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"vite": "^7.1.2"
"vite": "^7.1.11"
}
}

View File

@@ -29,7 +29,7 @@ importers:
version: 19.1.9(@types/react@19.1.12)
'@vitejs/plugin-react':
specifier: ^5.0.0
version: 5.0.2(vite@7.1.5)
version: 5.0.2(vite@7.1.11)
eslint:
specifier: ^9.33.0
version: 9.35.0
@@ -43,8 +43,8 @@ importers:
specifier: ^16.3.0
version: 16.3.0
vite:
specifier: ^7.1.2
version: 7.1.5
specifier: ^7.1.11
version: 7.1.11
packages:
@@ -890,8 +890,8 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
vite@7.1.5:
resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
vite@7.1.11:
resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -1310,7 +1310,7 @@ snapshots:
dependencies:
csstype: 3.1.3
'@vitejs/plugin-react@5.0.2(vite@7.1.5)':
'@vitejs/plugin-react@5.0.2(vite@7.1.11)':
dependencies:
'@babel/core': 7.28.4
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
@@ -1318,7 +1318,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-beta.34
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
vite: 7.1.5
vite: 7.1.11
transitivePeerDependencies:
- supports-color
@@ -1717,7 +1717,7 @@ snapshots:
dependencies:
punycode: 2.3.1
vite@7.1.5:
vite@7.1.11:
dependencies:
esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3)

View File

@@ -14,6 +14,6 @@
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^6.1.1",
"svelte": "^5.38.1",
"vite": "^7.1.2"
"vite": "^7.1.11"
}
}

View File

@@ -14,13 +14,13 @@ importers:
devDependencies:
'@sveltejs/vite-plugin-svelte':
specifier: ^6.1.1
version: 6.1.4(svelte@5.38.7)(vite@7.1.5)
version: 6.1.4(svelte@5.38.7)(vite@7.1.11)
svelte:
specifier: ^5.38.1
version: 5.38.7
vite:
specifier: ^7.1.2
version: 7.1.5
specifier: ^7.1.11
version: 7.1.11
packages:
@@ -424,8 +424,8 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
vite@7.1.5:
resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
vite@7.1.11:
resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -641,24 +641,24 @@ snapshots:
dependencies:
acorn: 8.15.0
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.7)(vite@7.1.5))(svelte@5.38.7)(vite@7.1.5)':
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.7)(vite@7.1.11))(svelte@5.38.7)(vite@7.1.11)':
dependencies:
'@sveltejs/vite-plugin-svelte': 6.1.4(svelte@5.38.7)(vite@7.1.5)
'@sveltejs/vite-plugin-svelte': 6.1.4(svelte@5.38.7)(vite@7.1.11)
debug: 4.4.1
svelte: 5.38.7
vite: 7.1.5
vite: 7.1.11
transitivePeerDependencies:
- supports-color
'@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.7)(vite@7.1.5)':
'@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.7)(vite@7.1.11)':
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.7)(vite@7.1.5))(svelte@5.38.7)(vite@7.1.5)
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.1.4(svelte@5.38.7)(vite@7.1.11))(svelte@5.38.7)(vite@7.1.11)
debug: 4.4.1
deepmerge: 4.3.1
magic-string: 0.30.18
svelte: 5.38.7
vite: 7.1.5
vitefu: 1.1.1(vite@7.1.5)
vite: 7.1.11
vitefu: 1.1.1(vite@7.1.11)
transitivePeerDependencies:
- supports-color
@@ -795,7 +795,7 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
vite@7.1.5:
vite@7.1.11:
dependencies:
esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3)
@@ -806,8 +806,8 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
vitefu@1.1.1(vite@7.1.5):
vitefu@1.1.1(vite@7.1.11):
optionalDependencies:
vite: 7.1.5
vite: 7.1.11
zimmerframe@1.1.2: {}

View File

@@ -14,6 +14,6 @@
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"vite": "^7.1.2"
"vite": "^7.1.11"
}
}

View File

@@ -17,10 +17,10 @@ importers:
devDependencies:
'@vitejs/plugin-vue':
specifier: ^6.0.1
version: 6.0.1(vite@7.1.5)(vue@3.5.21)
version: 6.0.1(vite@7.1.11)(vue@3.5.21)
vite:
specifier: ^7.1.2
version: 7.1.5
specifier: ^7.1.11
version: 7.1.11
packages:
@@ -408,8 +408,8 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
vite@7.1.5:
resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
vite@7.1.11:
resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -618,10 +618,10 @@ snapshots:
'@types/estree@1.0.8': {}
'@vitejs/plugin-vue@6.0.1(vite@7.1.5)(vue@3.5.21)':
'@vitejs/plugin-vue@6.0.1(vite@7.1.11)(vue@3.5.21)':
dependencies:
'@rolldown/pluginutils': 1.0.0-beta.29
vite: 7.1.5
vite: 7.1.11
vue: 3.5.21
'@vue/compiler-core@3.5.21':
@@ -770,7 +770,7 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
vite@7.1.5:
vite@7.1.11:
dependencies:
esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3)

View File

@@ -64,7 +64,7 @@ rating = 'g'
[auth.session]
[auth.session.accessToken]
expiresIn = 900
expiresIn = 65
[auth.session.refreshToken]
expiresIn = 2592000

View File

@@ -25,6 +25,6 @@
"@vitejs/plugin-react": "^5.0.0",
"globals": "^16.3.0",
"typescript": "~5.8.3",
"vite": "^7.1.2"
"vite": "^7.1.11"
}
}

View File

@@ -29,7 +29,7 @@ importers:
version: 19.1.9(@types/react@19.1.12)
'@vitejs/plugin-react':
specifier: ^5.0.0
version: 5.0.2(vite@7.1.5)
version: 5.0.2(vite@7.1.11)
globals:
specifier: ^16.3.0
version: 16.4.0
@@ -37,8 +37,8 @@ importers:
specifier: ~5.8.3
version: 5.8.3
vite:
specifier: ^7.1.2
version: 7.1.5
specifier: ^7.1.11
version: 7.1.11
packages:
@@ -597,8 +597,8 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
vite@7.1.5:
resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
vite@7.1.11:
resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -947,7 +947,7 @@ snapshots:
dependencies:
csstype: 3.1.3
'@vitejs/plugin-react@5.0.2(vite@7.1.5)':
'@vitejs/plugin-react@5.0.2(vite@7.1.11)':
dependencies:
'@babel/core': 7.28.4
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
@@ -955,7 +955,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-beta.34
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
vite: 7.1.5
vite: 7.1.11
transitivePeerDependencies:
- supports-color
@@ -1119,7 +1119,7 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
vite@7.1.5:
vite@7.1.11:
dependencies:
esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3)

View File

@@ -26,6 +26,6 @@
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"vite": "^7.0.4"
"vite": "^7.0.8"
}
}

View File

@@ -14,13 +14,13 @@ importers:
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^6.0.0
version: 6.1.0(@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5))
version: 6.1.0(@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.11))(svelte@5.38.10)(vite@7.1.11))
'@sveltejs/kit':
specifier: ^2.22.0
version: 2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5)
version: 2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.11))(svelte@5.38.10)(vite@7.1.11)
'@sveltejs/vite-plugin-svelte':
specifier: ^6.0.0
version: 6.2.0(svelte@5.38.10)(vite@7.1.5)
version: 6.2.0(svelte@5.38.10)(vite@7.1.11)
svelte:
specifier: ^5.0.0
version: 5.38.10
@@ -31,8 +31,8 @@ importers:
specifier: ^5.0.0
version: 5.9.2
vite:
specifier: ^7.0.4
version: 7.1.5
specifier: ^7.0.8
version: 7.1.11
packages:
@@ -518,8 +518,8 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
vite@7.1.5:
resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
vite@7.1.11:
resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -739,15 +739,15 @@ snapshots:
dependencies:
acorn: 8.15.0
'@sveltejs/adapter-auto@6.1.0(@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5))':
'@sveltejs/adapter-auto@6.1.0(@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.11))(svelte@5.38.10)(vite@7.1.11))':
dependencies:
'@sveltejs/kit': 2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5)
'@sveltejs/kit': 2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.11))(svelte@5.38.10)(vite@7.1.11)
'@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5)':
'@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.11))(svelte@5.38.10)(vite@7.1.11)':
dependencies:
'@standard-schema/spec': 1.0.0
'@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
'@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.5)
'@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.11)
'@types/cookie': 0.6.0
acorn: 8.15.0
cookie: 0.6.0
@@ -760,26 +760,26 @@ snapshots:
set-cookie-parser: 2.7.1
sirv: 3.0.2
svelte: 5.38.10
vite: 7.1.5
vite: 7.1.11
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5)':
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.11))(svelte@5.38.10)(vite@7.1.11)':
dependencies:
'@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.5)
'@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.11)
debug: 4.4.3
svelte: 5.38.10
vite: 7.1.5
vite: 7.1.11
transitivePeerDependencies:
- supports-color
'@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5)':
'@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.11)':
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5)
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.11))(svelte@5.38.10)(vite@7.1.11)
debug: 4.4.3
deepmerge: 4.3.1
magic-string: 0.30.19
svelte: 5.38.10
vite: 7.1.5
vitefu: 1.1.1(vite@7.1.5)
vite: 7.1.11
vitefu: 1.1.1(vite@7.1.11)
transitivePeerDependencies:
- supports-color
@@ -962,7 +962,7 @@ snapshots:
typescript@5.9.2: {}
vite@7.1.5:
vite@7.1.11:
dependencies:
esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3)
@@ -973,8 +973,8 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
vitefu@1.1.1(vite@7.1.5):
vitefu@1.1.1(vite@7.1.11):
optionalDependencies:
vite: 7.1.5
vite: 7.1.11
zimmerframe@1.1.4: {}

View File

@@ -28,7 +28,7 @@
"@vue/tsconfig": "^0.7.0",
"npm-run-all2": "^8.0.4",
"typescript": "~5.8.0",
"vite": "^7.0.6",
"vite": "^7.0.8",
"vite-plugin-vue-devtools": "^8.0.0",
"vue-tsc": "^3.0.4"
}

View File

@@ -26,7 +26,7 @@ importers:
version: 22.18.3
'@vitejs/plugin-vue':
specifier: ^6.0.1
version: 6.0.1(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))
version: 6.0.1(vite@7.1.11(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))
'@vue/tsconfig':
specifier: ^0.7.0
version: 0.7.0(typescript@5.8.3)(vue@3.5.21(typescript@5.8.3))
@@ -37,11 +37,11 @@ importers:
specifier: ~5.8.0
version: 5.8.3
vite:
specifier: ^7.0.6
version: 7.1.5(@types/node@22.18.3)
specifier: ^7.0.8
version: 7.1.11(@types/node@22.18.3)
vite-plugin-vue-devtools:
specifier: ^8.0.0
version: 8.0.2(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))
version: 8.0.2(vite@7.1.11(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))
vue-tsc:
specifier: ^3.0.4
version: 3.0.7(typescript@5.8.3)
@@ -986,8 +986,8 @@ packages:
peerDependencies:
vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
vite@7.1.5:
resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
vite@7.1.11:
resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -1438,10 +1438,10 @@ snapshots:
dependencies:
undici-types: 6.21.0
'@vitejs/plugin-vue@6.0.1(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))':
'@vitejs/plugin-vue@6.0.1(vite@7.1.11(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-beta.29
vite: 7.1.5(@types/node@22.18.3)
vite: 7.1.11(@types/node@22.18.3)
vue: 3.5.21(typescript@5.8.3)
'@volar/language-core@2.4.23':
@@ -1522,14 +1522,14 @@ snapshots:
'@vue/devtools-api@6.6.4': {}
'@vue/devtools-core@8.0.2(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))':
'@vue/devtools-core@8.0.2(vite@7.1.11(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))':
dependencies:
'@vue/devtools-kit': 8.0.2
'@vue/devtools-shared': 8.0.2
mitt: 3.0.1
nanoid: 5.1.5
pathe: 2.0.3
vite-hot-client: 2.1.0(vite@7.1.5(@types/node@22.18.3))
vite-hot-client: 2.1.0(vite@7.1.11(@types/node@22.18.3))
vue: 3.5.21(typescript@5.8.3)
transitivePeerDependencies:
- vite
@@ -1920,17 +1920,17 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
vite-dev-rpc@1.1.0(vite@7.1.5(@types/node@22.18.3)):
vite-dev-rpc@1.1.0(vite@7.1.11(@types/node@22.18.3)):
dependencies:
birpc: 2.5.0
vite: 7.1.5(@types/node@22.18.3)
vite-hot-client: 2.1.0(vite@7.1.5(@types/node@22.18.3))
vite: 7.1.11(@types/node@22.18.3)
vite-hot-client: 2.1.0(vite@7.1.11(@types/node@22.18.3))
vite-hot-client@2.1.0(vite@7.1.5(@types/node@22.18.3)):
vite-hot-client@2.1.0(vite@7.1.11(@types/node@22.18.3)):
dependencies:
vite: 7.1.5(@types/node@22.18.3)
vite: 7.1.11(@types/node@22.18.3)
vite-plugin-inspect@11.3.3(vite@7.1.5(@types/node@22.18.3)):
vite-plugin-inspect@11.3.3(vite@7.1.11(@types/node@22.18.3)):
dependencies:
ansis: 4.1.0
debug: 4.4.3
@@ -1940,27 +1940,27 @@ snapshots:
perfect-debounce: 2.0.0
sirv: 3.0.2
unplugin-utils: 0.3.0
vite: 7.1.5(@types/node@22.18.3)
vite-dev-rpc: 1.1.0(vite@7.1.5(@types/node@22.18.3))
vite: 7.1.11(@types/node@22.18.3)
vite-dev-rpc: 1.1.0(vite@7.1.11(@types/node@22.18.3))
transitivePeerDependencies:
- supports-color
vite-plugin-vue-devtools@8.0.2(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3)):
vite-plugin-vue-devtools@8.0.2(vite@7.1.11(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3)):
dependencies:
'@vue/devtools-core': 8.0.2(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))
'@vue/devtools-core': 8.0.2(vite@7.1.11(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))
'@vue/devtools-kit': 8.0.2
'@vue/devtools-shared': 8.0.2
execa: 9.6.0
sirv: 3.0.2
vite: 7.1.5(@types/node@22.18.3)
vite-plugin-inspect: 11.3.3(vite@7.1.5(@types/node@22.18.3))
vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.18.3))
vite: 7.1.11(@types/node@22.18.3)
vite-plugin-inspect: 11.3.3(vite@7.1.11(@types/node@22.18.3))
vite-plugin-vue-inspector: 5.3.2(vite@7.1.11(@types/node@22.18.3))
transitivePeerDependencies:
- '@nuxt/kit'
- supports-color
- vue
vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.18.3)):
vite-plugin-vue-inspector@5.3.2(vite@7.1.11(@types/node@22.18.3)):
dependencies:
'@babel/core': 7.28.4
'@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.4)
@@ -1971,11 +1971,11 @@ snapshots:
'@vue/compiler-dom': 3.5.21
kolorist: 1.8.0
magic-string: 0.30.19
vite: 7.1.5(@types/node@22.18.3)
vite: 7.1.11(@types/node@22.18.3)
transitivePeerDependencies:
- supports-color
vite@7.1.5(@types/node@22.18.3):
vite@7.1.11(@types/node@22.18.3):
dependencies:
esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3)

2
go.mod
View File

@@ -29,7 +29,7 @@ require (
github.com/jackc/pgx/v5 v5.7.2
github.com/lmittmann/tint v1.0.7
github.com/mark3labs/mcp-go v0.41.1
github.com/nhost/be v0.0.0-20250929153845-6db3e5249d33
github.com/nhost/be v0.0.0-20251021065906-8abc7d8dfa48
github.com/oapi-codegen/gin-middleware v1.0.2
github.com/oapi-codegen/runtime v1.1.1
github.com/pb33f/libopenapi v0.21.12

8
go.sum
View File

@@ -340,6 +340,14 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nhost/be v0.0.0-20250929153845-6db3e5249d33 h1:BNFN3Mw4zY6LEmVc7RXkHSVvHtovDSm7Oesb7IUt27o=
github.com/nhost/be v0.0.0-20250929153845-6db3e5249d33/go.mod h1:feVvqP3dft8hWbp9zNZExdGKbFEYv8aLYohfyAeINNQ=
github.com/nhost/be v0.0.0-20251015125528-facecfb60cea h1:QQMCKMdkOkVN5PlZB/iiBSdQL6u84JIVppWf7hC5OoU=
github.com/nhost/be v0.0.0-20251015125528-facecfb60cea/go.mod h1:feVvqP3dft8hWbp9zNZExdGKbFEYv8aLYohfyAeINNQ=
github.com/nhost/be v0.0.0-20251020104454-acc8934d2c11 h1:5uPnBt2gjVkRpjUEJ8uS/q+FpvpTQJ+CN6zLCBYcfPA=
github.com/nhost/be v0.0.0-20251020104454-acc8934d2c11/go.mod h1:feVvqP3dft8hWbp9zNZExdGKbFEYv8aLYohfyAeINNQ=
github.com/nhost/be v0.0.0-20251020115144-85e0542ecec0 h1:MRWnaC1Aoir6JPr4v4C2TAVG5KBIgsOdWiDeII5HQYg=
github.com/nhost/be v0.0.0-20251020115144-85e0542ecec0/go.mod h1:feVvqP3dft8hWbp9zNZExdGKbFEYv8aLYohfyAeINNQ=
github.com/nhost/be v0.0.0-20251021065906-8abc7d8dfa48 h1:+Oh4Rbr1psWlBaQTakoBYFNB8jBioiXuimNMaNPLTHk=
github.com/nhost/be v0.0.0-20251021065906-8abc7d8dfa48/go.mod h1:feVvqP3dft8hWbp9zNZExdGKbFEYv8aLYohfyAeINNQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oapi-codegen/gin-middleware v1.0.2 h1:/H99UzvHQAUxXK8pzdcGAZgjCVeXdFDAUUWaJT0k0eI=
github.com/oapi-codegen/gin-middleware v1.0.2/go.mod h1:2HJDQjH8jzK2/k/VKcWl+/T41H7ai2bKa6dN3AA2GpA=

View File

@@ -27,7 +27,7 @@
"audit-ci": "^6.6.1",
"turbo": "2.3.3",
"typescript": "5.8.3",
"vite": "^5.4.20",
"vite": "^5.4.21",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^0.32.4"
},
@@ -60,7 +60,6 @@
"zod@<=3.22.2": ">=3.22.3",
"@antfu/utils@<0.7.3": ">=0.7.3",
"next@>=0.9.9 <13.4.20-canary.13": ">=13.4.20-canary.13",
"vite@<=4.5.13": ">=4.5.14",
"yaml@>=2.0.0-5 <2.2.2": ">=2.2.2",
"pnpm@<7.33.4": ">=7.33.4",
"graphql@>=16.3.0 <16.8.1": ">=16.8.1",
@@ -109,8 +108,11 @@
"cookie@<0.7.0": ">=0.7.0",
"prismjs@<1.30.0": ">=1.30.0",
"axios@<1.8.2": ">=1.8.2",
"vite@>=5.0.0 <=5.4.18": ">=5.4.19",
"vite@>=6.2.0 <=6.2.6": ">=6.2.6",
"vite@<=4.5.13": ">=4.5.14",
"vite@>=5.0.0 <=5.4.20": ">=5.4.21",
"vite@>=6.0.0 <=6.4.0": ">=6.4.1",
"vite@>=7.0.0 <=7.0.7": "7.0.8",
"vite@>=7.1.0 <=7.1.10": "7.1.11",
"@sveltejs/kit@>=2.0.0 <2.20.6": ">=2.20.6",
"react-router@<7.5.2": ">=7.5.2",
"undici@<5.29.0": ">=5.29.0",

View File

@@ -1,5 +1,9 @@
import { expect, test } from "@jest/globals";
import { createClient } from "@nhost/nhost-js";
import {
createClient,
createNhostClient,
withAdminSession,
} from "@nhost/nhost-js";
const subdomain = "local";
const region = "local";
@@ -44,7 +48,7 @@ test("mainExample", async () => {
// "updatedAt": "2025-05-09T17:26:04.589692+00:00",
// "isUploaded": true,
// "mimeType": "text/plain",
// "uploadedByUserId": "",
// "uploadedByUserId": "3357aada-b6c7-4af1-9655-1307ca2883a2",
// "metadata": null
// },
// {
@@ -57,7 +61,133 @@ test("mainExample", async () => {
// "updatedAt": "2025-05-09T17:26:04.596831+00:00",
// "isUploaded": true,
// "mimeType": "text/plain",
// "uploadedByUserId": "",
// "uploadedByUserId": "3357aada-b6c7-4af1-9655-1307ca2883a2",
// "metadata": null
// }
// ]
// },
// "status": 201,
// "headers": {
// "content-length": "644",
// "content-type": "application/json; charset=utf-8",
// "date": "Fri, 09 May 2025 17:26:04 GMT"
// }
// }
// make a GraphQL request to list the files
const graphResp = await nhost.graphql.request({
query: `
query {
files {
name
size
mimeType
}
}
`,
});
console.log(JSON.stringify(graphResp, null, 2));
// {
// "body": {
// "data": {
// "files": [
// {
// "name": "file-1",
// "size": 5,
// "mimeType": "text/plain"
// },
// {
// "name": "file-2",
// "size": 15,
// "mimeType": "text/plain"
// }
// ]
// }
// },
// "status": 200,
// "headers": {}
// }
// make a request to a serverless function
const funcResp = await nhost.functions.post("/helloworld", {
message: "Hello, World!",
});
console.log(JSON.stringify(funcResp.body, null, 2));
// {
// "message": "Hello, World!"
// }
expect(uplFilesResp.status).toBe(201);
expect(graphResp.status).toBe(200);
expect(graphResp.body.errors).toBeUndefined();
expect(graphResp.body.data).toStrictEqual({
files: [
{
name: "file-1",
size: 5,
mimeType: "text/plain",
},
{
name: "file-2",
size: 15,
mimeType: "text/plain",
},
],
});
});
test("adminClient", async () => {
const nhost = createNhostClient({
subdomain,
region,
configure: [
withAdminSession({
adminSecret: "nhost-admin-secret",
role: "user",
sessionVariables: {
"user-id": "54058C42-51F7-4B37-8B69-C89A841D2221",
},
}),
],
});
// upload a couple of files
const uplFilesResp = await nhost.storage.uploadFiles({
"file[]": [
new File(["test1"], "file-1", { type: "text/plain" }),
new File(["test2 is larger"], "file-2", { type: "text/plain" }),
],
});
console.log(JSON.stringify(uplFilesResp, null, 2));
// {
// "data": {
// "processedFiles": [
// {
// "id": "c0e83185-0ce5-435c-bd46-9841adc30699",
// "name": "file-1",
// "size": 5,
// "bucketId": "default",
// "etag": "\"5a105e8b9d40e1329780d62ea2265d8a\"",
// "createdAt": "2025-05-09T17:26:04.579839+00:00",
// "updatedAt": "2025-05-09T17:26:04.589692+00:00",
// "isUploaded": true,
// "mimeType": "text/plain",
// "uploadedByUserId": "54058c42-51f7-4b37-8b69-c89a841d2221",
// "metadata": null
// },
// {
// "id": "3f189004-21fd-42d0-be1d-1ead021ab167",
// "name": "file-2",
// "size": 15,
// "bucketId": "default",
// "etag": "\"302e888c5e289fe6b02115b748771ee9\"",
// "createdAt": "2025-05-09T17:26:04.59245+00:00",
// "updatedAt": "2025-05-09T17:26:04.596831+00:00",
// "isUploaded": true,
// "mimeType": "text/plain",
// "uploadedByUserId": "54058c42-51f7-4b37-8b69-c89a841d2221",
// "metadata": null
// }
// ]

View File

@@ -45,3 +45,22 @@ export { sessionRefreshMiddleware } from "./middlewareSessionRefresh";
* @returns A middleware function that updates session storage
*/
export { updateSessionFromResponseMiddleware } from "./middlewareUpdateSessionFromResponse";
/**
* Middleware for attaching Hasura admin secret for elevated permissions.
*
* @param options - Admin session options including the admin secret
* @returns A middleware function that adds x-hasura-admin-secret header
*/
export {
type AdminSessionOptions,
withAdminSessionMiddleware,
} from "./middlewareWithAdminSession";
export { withHeadersMiddleware } from "./middlewareWithHeaders";
/**
* Middleware for setting the Hasura role header for requests.
*
* @param role - The Hasura role to use for requests
* @returns A middleware function that adds x-hasura-role header
*/
export { withRoleMiddleware } from "./middlewareWithRole";

View File

@@ -0,0 +1,121 @@
/**
* Admin session middleware for the Nhost SDK.
*
* This module provides middleware functionality to automatically attach
* Hasura admin secret for admin permissions in requests.
*/
import type { ChainFunction, FetchFunction } from "./fetch";
/**
* Configuration options for admin session middleware
*/
export interface AdminSessionOptions {
/**
* Hasura admin secret for elevated permissions (sets x-hasura-admin-secret header)
*/
adminSecret: string;
/**
* Hasura role to use for the request (sets x-hasura-role header)
*/
role?: string;
/**
* Additional Hasura session variables to attach to requests.
* Keys will be automatically prefixed with 'x-hasura-' if not already present.
*
* @example
* ```ts
* {
* 'user-id': '123',
* 'org-id': '456'
* }
* // Results in headers:
* // x-hasura-user-id: 123
* // x-hasura-org-id: 456
* ```
*/
sessionVariables?: Record<string, string>;
}
/**
* Creates a fetch middleware that attaches the Hasura admin secret and optional session variables to requests.
*
* This middleware:
* 1. Sets the x-hasura-admin-secret header, which grants full admin access to Hasura
* 2. Optionally sets the x-hasura-role header if a role is provided
* 3. Optionally sets additional x-hasura-* headers for custom session variables
*
* **Security Warning**: Never use this middleware in client-side code or expose
* the admin secret to end users. Admin secrets grant unrestricted access to your
* entire database. This should only be used in trusted server-side environments.
*
* The middleware preserves request-specific headers when they conflict with the
* admin session configuration.
*
* @param options - Admin session options including admin secret, role, and session variables
* @returns A middleware function that can be used in the fetch chain
*
* @example
* ```ts
* // Create middleware with admin secret only
* const adminMiddleware = withAdminSessionMiddleware({
* adminSecret: process.env.NHOST_ADMIN_SECRET
* });
*
* // Create middleware with admin secret and role
* const adminUserMiddleware = withAdminSessionMiddleware({
* adminSecret: process.env.NHOST_ADMIN_SECRET,
* role: 'user'
* });
*
* // Create middleware with admin secret, role, and custom session variables
* const fullMiddleware = withAdminSessionMiddleware({
* adminSecret: process.env.NHOST_ADMIN_SECRET,
* role: 'user',
* sessionVariables: {
* 'user-id': '123',
* 'org-id': '456'
* }
* });
*
* // Use with createCustomClient for an admin client
* const adminClient = createCustomClient({
* subdomain: 'myproject',
* region: 'eu-central-1',
* chainFunctions: [adminMiddleware]
* });
* ```
*/
export const withAdminSessionMiddleware =
(options: AdminSessionOptions): ChainFunction =>
(next: FetchFunction): FetchFunction =>
async (url: string, requestOptions: RequestInit = {}): Promise<Response> => {
const headers = new Headers(requestOptions.headers || {});
// Set x-hasura-admin-secret if not already present
if (!headers.has("x-hasura-admin-secret")) {
headers.set("x-hasura-admin-secret", options.adminSecret);
}
// Set x-hasura-role if provided and not already present
if (options.role && !headers.has("x-hasura-role")) {
headers.set("x-hasura-role", options.role);
}
// Set custom session variables
if (options.sessionVariables) {
for (const [key, value] of Object.entries(options.sessionVariables)) {
// Ensure the key has the x-hasura- prefix
const headerKey = key.startsWith("x-hasura-") ? key : `x-hasura-${key}`;
// Only set if not already present in the request
if (!headers.has(headerKey)) {
headers.set(headerKey, value);
}
}
}
return next(url, { ...requestOptions, headers });
};

View File

@@ -0,0 +1,38 @@
/**
* Headers middleware for the Nhost SDK.
*
* This module provides middleware functionality to automatically attach
* default headers to all outgoing requests, while allowing request-specific
* headers to take precedence.
*/
import type { ChainFunction, FetchFunction } from "./fetch";
/**
* Creates a fetch middleware that attaches default headers to requests.
*
* This middleware:
* 1. Merges default headers with request-specific headers
* 2. Preserves request-specific headers when they conflict with defaults
*
* The middleware ensures consistent headers across requests while allowing
* individual requests to override defaults as needed.
*
* @param defaultHeaders - Default headers to attach to all requests
* @returns A middleware function that can be used in the fetch chain
*/
export const withHeadersMiddleware =
(defaultHeaders: HeadersInit): ChainFunction =>
(next: FetchFunction): FetchFunction =>
async (url: string, options: RequestInit = {}): Promise<Response> => {
const headers = new Headers(options.headers || {});
const defaults = new Headers(defaultHeaders);
defaults.forEach((value, key) => {
if (!headers.has(key)) {
headers.set(key, value);
}
});
return next(url, { ...options, headers });
};

View File

@@ -0,0 +1,57 @@
/**
* Role middleware for the Nhost SDK.
*
* This module provides middleware functionality to automatically set
* the Hasura role for all requests. This is useful when you want to
* make requests as a specific role without using the admin secret.
*/
import type { ChainFunction, FetchFunction } from "./fetch";
/**
* Creates a fetch middleware that sets the Hasura role header.
*
* This middleware sets the x-hasura-role header for all requests, allowing
* you to specify which role's permissions should be used. This works with
* authenticated sessions where the user has access to the specified role.
*
* Unlike `withAdminSessionMiddleware`, this does not bypass permission rules
* but instead uses the permission rules defined for the specified role.
*
* The middleware preserves request-specific headers when they conflict with
* the role configuration.
*
* @param role - The Hasura role to use for requests
* @returns A middleware function that can be used in the fetch chain
*
* @example
* ```ts
* // Use with createClient to default all requests to a specific role
* const nhost = createClient({
* subdomain: 'myproject',
* region: 'eu-central-1',
* chainFunctions: [withRoleMiddleware('moderator')]
* });
*
* // Use with createServerClient for server-side requests
* const serverNhost = createServerClient({
* subdomain: 'myproject',
* region: 'eu-central-1',
* storage: myServerStorage,
* chainFunctions: [withRoleMiddleware('moderator')]
* });
* ```
*/
export const withRoleMiddleware =
(role: string): ChainFunction =>
(next: FetchFunction): FetchFunction =>
async (url: string, requestOptions: RequestInit = {}): Promise<Response> => {
const headers = new Headers(requestOptions.headers || {});
// Set x-hasura-role if not already present
if (!headers.has("x-hasura-role")) {
headers.set("x-hasura-role", role);
}
return next(url, { ...requestOptions, headers });
};

View File

@@ -17,17 +17,32 @@
*
* Create a client instance to interact with Nhost services:
*
* {@includeCode ./__tests__/docstrings.test.ts:11-115}
* {@includeCode ./__tests__/docstrings.test.ts:15-119}
*
* ### Creating an admin client
*
* You can also create an admin client if needed. This client will have admin access to the database
* and will bypass permissions. Additionally, it can impersonate users and set any role or session
* variable.
*
* IMPORTANT!!! Keep your admin secret safe and never expose it in client-side code.
*
* {@includeCode ./__tests__/docstrings.test.ts:142-201}
*
* @packageDocumentation
*/
export {
type ClientConfigurationFn,
createClient,
createNhostClient,
createServerClient,
type NhostClient,
type NhostClientOptions,
type NhostServerClientOptions,
withAdminSession,
withClientSideSessionMiddleware,
withServerSideSessionMiddleware,
} from "./nhost";
/**

View File

@@ -4,10 +4,12 @@ import {
createAPIClient as createAuthClient,
} from "./auth";
import {
type AdminSessionOptions,
attachAccessTokenMiddleware,
type ChainFunction,
sessionRefreshMiddleware,
updateSessionFromResponseMiddleware,
withAdminSessionMiddleware,
} from "./fetch";
import {
createAPIClient as createFunctionsClient,
@@ -29,6 +31,110 @@ import {
type Client as StorageClient,
} from "./storage";
/**
* Configuration function that receives all clients and can configure them
* (e.g., by attaching middleware, setting up interceptors, etc.)
*/
export type ClientConfigurationFn = (clients: {
auth: AuthClient;
storage: StorageClient;
graphql: GraphQLClient;
functions: FunctionsClient;
sessionStorage: SessionStorage;
}) => void;
/**
* Built-in configuration for client-side applications.
* Includes automatic session refresh, token attachment, and session updates.
*/
export const withClientSideSessionMiddleware: ClientConfigurationFn = ({
auth,
storage,
graphql,
functions,
sessionStorage,
}) => {
const mwChain: ChainFunction[] = [
sessionRefreshMiddleware(auth, sessionStorage),
updateSessionFromResponseMiddleware(sessionStorage),
attachAccessTokenMiddleware(sessionStorage),
];
for (const mw of mwChain) {
auth.pushChainFunction(mw);
storage.pushChainFunction(mw);
graphql.pushChainFunction(mw);
functions.pushChainFunction(mw);
}
};
/**
* Built-in configuration for server-side applications.
* Includes token attachment and session updates, but NOT automatic session refresh
* to prevent race conditions in server contexts.
*/
export const withServerSideSessionMiddleware: ClientConfigurationFn = ({
auth,
storage,
graphql,
functions,
sessionStorage,
}) => {
const mwChain: ChainFunction[] = [
updateSessionFromResponseMiddleware(sessionStorage),
attachAccessTokenMiddleware(sessionStorage),
];
for (const mw of mwChain) {
auth.pushChainFunction(mw);
storage.pushChainFunction(mw);
graphql.pushChainFunction(mw);
functions.pushChainFunction(mw);
}
};
/**
* Configuration for admin clients with elevated privileges.
* Applies admin session middleware to storage, graphql, and functions clients only.
*
* **Security Warning**: Never use this in client-side code. Admin secrets grant
* unrestricted access to your entire database.
*
* @param adminSession - Admin session options including admin secret, role, and session variables
* @returns Configuration function that sets up admin middleware
*/
export function withAdminSession(
adminSession: AdminSessionOptions,
): ClientConfigurationFn {
return ({ storage, graphql, functions }) => {
const adminMiddleware = withAdminSessionMiddleware(adminSession);
storage.pushChainFunction(adminMiddleware);
graphql.pushChainFunction(adminMiddleware);
functions.pushChainFunction(adminMiddleware);
};
}
/**
* Configuration for adding custom chain functions to all clients.
* Useful for adding custom middleware like logging, caching, or custom headers.
*
* @param chainFunctions - Array of chain functions to apply to all clients
* @returns Configuration function that sets up custom middleware
*/
export function withChainFunctions(
chainFunctions: ChainFunction[],
): ClientConfigurationFn {
return ({ auth, storage, graphql, functions }) => {
for (const mw of chainFunctions) {
auth.pushChainFunction(mw);
storage.pushChainFunction(mw);
graphql.pushChainFunction(mw);
functions.pushChainFunction(mw);
}
};
}
/**
* Main client class that provides unified access to all Nhost services.
* This class serves as the central interface for interacting with Nhost's
@@ -192,6 +298,122 @@ export interface NhostClientOptions {
* default to localStorage in the browser or memory in other environments.
*/
storage?: SessionStorageBackend;
/**
* Configuration functions to be applied to the client after initialization.
* These functions receive all clients and can attach middleware or perform other setup.
*/
configure?: ClientConfigurationFn[];
}
/**
* Creates and configures a new Nhost client instance with custom configuration.
*
* This is the main factory function for creating Nhost clients. It instantiates
* all service clients (auth, storage, graphql, functions) and applies the provided
* configuration functions to set up middleware and other customizations.
*
* @param options - Configuration options for the client
* @returns A configured Nhost client
*
* @example
* ```ts
* // Create a basic client with no middleware
* const nhost = createNhostClient({
* subdomain: 'abcdefgh',
* region: 'eu-central-1',
* configure: []
* });
*
* // Create a client with custom configuration
* const nhost = createNhostClient({
* subdomain: 'abcdefgh',
* region: 'eu-central-1',
* configure: [
* withClientSideSessionMiddleware,
* withChainFunctions([customLoggingMiddleware])
* ]
* });
*
* // Create an admin client
* const nhost = createNhostClient({
* subdomain,
* region,
* configure: [
* withAdminSession({
* adminSecret: "nhost-admin-secret",
* role: "user",
* sessionVariables: {
* "user-id": "54058C42-51F7-4B37-8B69-C89A841D2221",
* },
* }),
* ],
* });
* ```
*/
export function createNhostClient(
options: NhostClientOptions = {},
): NhostClient {
const {
subdomain,
region,
authUrl,
storageUrl,
graphqlUrl,
functionsUrl,
storage = detectStorage(),
configure = [],
} = options;
const sessionStorage = new SessionStorage(storage);
// Determine base URLs for each service
const authBaseUrl = generateServiceUrl("auth", subdomain, region, authUrl);
const storageBaseUrl = generateServiceUrl(
"storage",
subdomain,
region,
storageUrl,
);
const graphqlBaseUrl = generateServiceUrl(
"graphql",
subdomain,
region,
graphqlUrl,
);
const functionsBaseUrl = generateServiceUrl(
"functions",
subdomain,
region,
functionsUrl,
);
// Create all clients
const auth = createAuthClient(authBaseUrl);
const storageClient = createStorageClient(storageBaseUrl, []);
const graphqlClient = createGraphQLClient(graphqlBaseUrl, []);
const functionsClient = createFunctionsClient(functionsBaseUrl, []);
// Apply configuration functions
for (const configFn of configure) {
configFn({
auth,
storage: storageClient,
graphql: graphqlClient,
functions: functionsClient,
sessionStorage,
});
}
// Return an initialized NhostClient
return new NhostClient(
auth,
storageClient,
graphqlClient,
functionsClient,
sessionStorage,
);
}
/**
@@ -237,65 +459,23 @@ export interface NhostClientOptions {
* secure: import.meta.env.ENVIRONMENT === 'production',
* })
* });
*
* // Create client with additional custom middleware
* const nhost = createClient({
* subdomain: 'abcdefgh',
* region: 'eu-central-1',
* configure: [customLoggingMiddleware]
* });
* ```
*/
export function createClient(options: NhostClientOptions = {}): NhostClient {
const {
subdomain,
region,
authUrl,
storageUrl,
graphqlUrl,
functionsUrl,
storage = detectStorage(),
} = options;
const storage = options.storage ?? detectStorage();
const sessionStorage = new SessionStorage(storage);
// Determine base URLs for each service
const authBaseUrl = generateServiceUrl("auth", subdomain, region, authUrl);
const storageBaseUrl = generateServiceUrl(
"storage",
subdomain,
region,
storageUrl,
);
const graphqlBaseUrl = generateServiceUrl(
"graphql",
subdomain,
region,
graphqlUrl,
);
const functionsBaseUrl = generateServiceUrl(
"functions",
subdomain,
region,
functionsUrl,
);
// Create auth client
const auth = createAuthClient(authBaseUrl);
const mwChain = getMiddlewareChain(auth, sessionStorage, true);
for (const mw of mwChain) {
auth.pushChainFunction(mw);
}
// Create storage and graphql clients with the refresh and attach token middlewares
const storageClient = createStorageClient(storageBaseUrl, mwChain);
const graphqlClient = createGraphQLClient(graphqlBaseUrl, mwChain);
const functionsClient = createFunctionsClient(functionsBaseUrl, mwChain);
// Return an initialized NhostClient
return new NhostClient(
auth,
storageClient,
graphqlClient,
functionsClient,
sessionStorage,
);
return createNhostClient({
...options,
storage,
configure: [withClientSideSessionMiddleware, ...(options.configure ?? [])],
});
}
export interface NhostServerClientOptions extends NhostClientOptions {
@@ -414,81 +594,21 @@ export interface NhostServerClientOptions extends NhostClientOptions {
* },
* });
* };
*
* // Example with additional custom middleware
* const nhost = createServerClient({
* region: process.env["NHOST_REGION"] || "local",
* subdomain: process.env["NHOST_SUBDOMAIN"] || "local",
* storage: myStorage,
* configure: [customLoggingMiddleware]
* });
* ```
*/
export function createServerClient(
options: NhostServerClientOptions,
): NhostClient {
const {
subdomain,
region,
authUrl,
storageUrl,
graphqlUrl,
functionsUrl,
storage,
} = options;
const sessionStorage = new SessionStorage(storage);
// Determine base URLs for each service
const authBaseUrl = generateServiceUrl("auth", subdomain, region, authUrl);
const storageBaseUrl = generateServiceUrl(
"storage",
subdomain,
region,
storageUrl,
);
const graphqlBaseUrl = generateServiceUrl(
"graphql",
subdomain,
region,
graphqlUrl,
);
const functionsBaseUrl = generateServiceUrl(
"functions",
subdomain,
region,
functionsUrl,
);
// Create auth client
const auth = createAuthClient(authBaseUrl);
const mwChain = getMiddlewareChain(auth, sessionStorage, false);
for (const mw of mwChain) {
auth.pushChainFunction(mw);
}
// Create storage and graphql clients with the refresh and attach token middlewares
const storageClient = createStorageClient(storageBaseUrl, mwChain);
const graphqlClient = createGraphQLClient(graphqlBaseUrl, mwChain);
const functionsClient = createFunctionsClient(functionsBaseUrl, mwChain);
// Return an initialized NhostClient
return new NhostClient(
auth,
storageClient,
graphqlClient,
functionsClient,
sessionStorage,
);
}
function getMiddlewareChain(
auth: AuthClient,
storage: SessionStorage,
autoRefresh: boolean,
): ChainFunction[] {
const mwChain = [
updateSessionFromResponseMiddleware(storage),
attachAccessTokenMiddleware(storage),
];
if (autoRefresh) {
mwChain.unshift(sessionRefreshMiddleware(auth, storage));
}
return mwChain;
return createNhostClient({
...options,
configure: [withServerSideSessionMiddleware, ...(options.configure ?? [])],
});
}

View File

@@ -25,6 +25,7 @@ interface Lock {
}
const lock: Lock =
// biome-ignore lint/complexity/useOptionalChain: this check breaks non-browser environments
typeof navigator !== "undefined" && navigator.locks
? navigator.locks
: new DummyLock();

28
pnpm-lock.yaml generated
View File

@@ -25,7 +25,6 @@ overrides:
zod@<=3.22.2: '>=3.22.3'
'@antfu/utils@<0.7.3': '>=0.7.3'
next@>=0.9.9 <13.4.20-canary.13: '>=13.4.20-canary.13'
vite@<=4.5.13: '>=4.5.14'
yaml@>=2.0.0-5 <2.2.2: '>=2.2.2'
pnpm@<7.33.4: '>=7.33.4'
graphql@>=16.3.0 <16.8.1: '>=16.8.1'
@@ -74,8 +73,11 @@ overrides:
cookie@<0.7.0: '>=0.7.0'
prismjs@<1.30.0: '>=1.30.0'
axios@<1.8.2: '>=1.8.2'
vite@>=5.0.0 <=5.4.18: '>=5.4.19'
vite@>=6.2.0 <=6.2.6: '>=6.2.6'
vite@<=4.5.13: '>=4.5.14'
vite@>=5.0.0 <=5.4.20: '>=5.4.21'
vite@>=6.0.0 <=6.4.0: '>=6.4.1'
vite@>=7.0.0 <=7.0.7: 7.0.8
vite@>=7.1.0 <=7.1.10: 7.1.11
'@sveltejs/kit@>=2.0.0 <2.20.6': '>=2.20.6'
react-router@<7.5.2: '>=7.5.2'
undici@<5.29.0: '>=5.29.0'
@@ -103,11 +105,11 @@ importers:
specifier: 5.8.3
version: 5.8.3
vite:
specifier: ^5.4.20
version: 5.4.20(@types/node@24.3.1)
specifier: ^5.4.21
version: 5.4.21(@types/node@24.3.1)
vite-tsconfig-paths:
specifier: ^4.3.2
version: 4.3.2(typescript@5.8.3)(vite@5.4.20(@types/node@24.3.1))
version: 4.3.2(typescript@5.8.3)(vite@5.4.21(@types/node@24.3.1))
vitest:
specifier: ^0.32.4
version: 0.32.4
@@ -750,8 +752,8 @@ packages:
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:
@@ -1362,7 +1364,7 @@ snapshots:
mlly: 1.8.0
pathe: 1.1.2
picocolors: 1.1.1
vite: 5.4.20(@types/node@24.3.1)
vite: 5.4.21(@types/node@24.3.1)
transitivePeerDependencies:
- '@types/node'
- less
@@ -1374,18 +1376,18 @@ snapshots:
- supports-color
- terser
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.20(@types/node@24.3.1)):
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.21(@types/node@24.3.1)):
dependencies:
debug: 4.4.1
globrex: 0.1.2
tsconfck: 3.1.6(typescript@5.8.3)
optionalDependencies:
vite: 5.4.20(@types/node@24.3.1)
vite: 5.4.21(@types/node@24.3.1)
transitivePeerDependencies:
- supports-color
- typescript
vite@5.4.20(@types/node@24.3.1):
vite@5.4.21(@types/node@24.3.1):
dependencies:
esbuild: 0.25.9
postcss: 8.5.6
@@ -1417,7 +1419,7 @@ snapshots:
strip-literal: 1.3.0
tinybench: 2.9.0
tinypool: 0.5.0
vite: 5.4.20(@types/node@24.3.1)
vite: 5.4.21(@types/node@24.3.1)
vite-node: 0.32.4(@types/node@24.3.1)
why-is-node-running: 2.3.0
transitivePeerDependencies:

View File

@@ -1,5 +1,6 @@
NODE_ENV=development
AUTH_API_PREFIX=""
AUTH_ENCRYPTION_KEY="5181f67e2844e4b60d571fa346cac9c37fc00d1ff519212eae6cead138e639ba"
HASURA_GRAPHQL_DATABASE_URL="postgres://postgres:postgres@127.0.0.1:5432/local"
HASURA_GRAPHQL_JWT_SECRET='{"type":"HS256", "key":"5152fa850c02dc222631cca898ed1485821a70912a6e3649c49076912daa3b62182ba013315915d64f40cddfbb8b58eb5bd11ba225336a6af45bbae07ca873f3","issuer":"hasura-auth"}'
# HASURA_GRAPHQL_JWT_SECRET='{"type":"RS256", "key":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0M9zJVMJV8TgNPT9p737\nKR1XyWL9PSePXdcyk9aWcusG/m2OGsvuAgbMuDQx+Pns9PH9hd1kl53yfpTXOp3T\n13Uqox3IuHMlx8JyS+raqURSAew8/RkQ8nq+68XBBr9atDvVcSknS+jtZGH6Du1V\ntUy7Sz8VTVQuXDURF1Aa2wwNkRkC/wg5X0BXfN4Felh+mpK05PZbqJcRA5dbDlWk\n5nkdKbCG3urmtfF8brh6SlV7xBODJbqQws6E9WTclDQXWjmjcVNVSJE7OblZj1JB\nNGMN9rjUSS1kiRozhMtSlRv9MHAsz3e05MnvbyMWtoRpH8iLs0jmf8uucEBkMS/6\nBQIDAQAB\n-----END PUBLIC KEY-----","signing_key":"-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDQz3MlUwlXxOA0\n9P2nvfspHVfJYv09J49d1zKT1pZy6wb+bY4ay+4CBsy4NDH4+ez08f2F3WSXnfJ+\nlNc6ndPXdSqjHci4cyXHwnJL6tqpRFIB7Dz9GRDyer7rxcEGv1q0O9VxKSdL6O1k\nYfoO7VW1TLtLPxVNVC5cNREXUBrbDA2RGQL/CDlfQFd83gV6WH6akrTk9luolxED\nl1sOVaTmeR0psIbe6ua18XxuuHpKVXvEE4MlupDCzoT1ZNyUNBdaOaNxU1VIkTs5\nuVmPUkE0Yw32uNRJLWSJGjOEy1KVG/0wcCzPd7Tkye9vIxa2hGkfyIuzSOZ/y65w\nQGQxL/oFAgMBAAECggEBAKO3g1hoWpLuUbwzug2i7yG1V/cWHees9MNmzskLHrrL\ne5hQ3XI1Ik+EdU6X3faQPjFu6o/indQiitakbHwQ8t+jheKOn6m+3ohY9LSBVyAP\n6RyI1Oi8loUHqFnDmyxnK/7USu2GjsD9x+NEzoKVovWbS94bf+A5eH/jO5tDu0qy\nJMoe2jLx5IQQwLuvmQ+5ccZOUSQLG4/9Bw7g4xdroSvyVH27g9hqmEuY2orhcMm3\nlvlAx3PIH/kjKtpnP6MIBDhznlJ/VqAHhT/zC9xIwohyVGymoyDhdNSpn5N2QmWK\nKsneTdYdWz/oMB+HtcmvSoI4saEv1tlGEOTTINyBpwECgYEA+vrMFKhIoS3EbNiS\n5mSw/ejHl+60hsORGahPaYoXDfbDr2lyG58OfGu+l0qIe5SX4ueHo+ydhN70GR4l\nrtX9R+8uV9hb4B9e/hTZZnnAyFZrZSPfx4ZtPeyAlL+zhIGwxTy4BrbuxgynGict\nu+7XgbOYesDGRCvc4/1HKOgnf9kCgYEA1Py22UkY1iQQ4JNkfI1U6XqrydlMy1BG\nYSglI37UkVGUWG1hry+HCdhxHupBeUspZ/4vzKCLO1A1RlkCi9+TcOpY8UBqo1uq\nBaBPSlWD7T4oTOEPvANqT43SJ+RFkjtfVvDDtXBImO0+Nu57FmD6pMZJtvsxFkQB\nZKP6Mx3Q3A0CgYEAmGQJ4I0htIQmnXSPFceT1EgwUOdGxAEhLHQO6+VGBFuODAc5\nmt3kHNYLHq/J2UerRcIRkQ4NwuzhSBMPDG6wYKow+HPNrXM+6YXdTySkUsBuazXy\nHaNY112v4SHZLZ7Vp50rnCAdMTHjkLSzR0ZJol5bOkWs3R3I/MIAIC1+NlkCgYEA\n0kevP1er7cAt6Yub6lyfOOSkNuUTrKfU1JeOEz1lIRQqIiPcDdoeuNm41Yzyl45d\nkw6ioqTe0fCeqJW4reBO8Wxt48J1hlM5ydQ8dGd5mQGFSGHr5vR4QZMDqd710SpN\nNsj+cGQrSNDyW8mYPMACtiwPG1llXVZHCdXbcBw/2QECgYABLllfHVarmMwKTLWo\n8rRdEsIwcD/VxOxG4BxL6rZNQ4ADd8pL+9TohZeWp8wvlDGU7yB9wgyaD62Mhyog\nAGJt5Y/twAPTiVEi+0jVCXraLwPZpQFafw7p/x33eHkhwOTqlwlkRNlvrVk6A29I\nU0PVQZdMwEj/CPZVeCJ4DX63qQ==\n-----END PRIVATE KEY-----","issuer":"hasura-auth"}'

View File

@@ -2,6 +2,23 @@
All notable changes to this project will be documented in this file.
## [auth@0.42.4] - 2025-10-20
### 🐛 Bug Fixes
- *(auth)* Apply relationships on new projects (#3617)
## [auth@0.42.3] - 2025-10-20
### 🐛 Bug Fixes
- *(auth)* Always apply expected metadata (#3616)
### ⚙️ Miscellaneous Tasks
- *(storage)* Migrate to urfave and slog libraries (#3606)
## [auth@0.42.2] - 2025-10-13
### ⚙️ Miscellaneous Tasks

View File

@@ -20,6 +20,7 @@ services:
HASURA_GRAPHQL_JWT_SECRET: '{"type":"HS256", "key":"5152fa850c02dc222631cca898ed1485821a70912a6e3649c49076912daa3b62182ba013315915d64f40cddfbb8b58eb5bd11ba225336a6af45bbae07ca873f3","issuer":"hasura-auth"}'
HASURA_GRAPHQL_ADMIN_SECRET: nhost-admin-secret
HASURA_GRAPHQL_GRAPHQL_URL: http://graphql:8080/v1/graphql
AUTH_ENCRYPTION_KEY: 5181f67e2844e4b60d571fa346cac9c37fc00d1ff519212eae6cead138e639ba
AUTH_PORT: 4000
AUTH_SERVER_URL: http://127.0.0.2:4000
AUTH_USER_DEFAULT_ALLOWED_ROLES: 'me,user,editor'

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