Compare commits
13 Commits
auth@0.43.
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f2b93d44b | ||
|
|
1aeef26ec6 | ||
|
|
749bb4e637 | ||
|
|
accabc83f7 | ||
|
|
8c127d7b6b | ||
|
|
f9c614ef99 | ||
|
|
1d183f7fc4 | ||
|
|
46e740f060 | ||
|
|
0d30ab4eec | ||
|
|
d5fd3cb59c | ||
|
|
f36d360b9e | ||
|
|
61af5087fd | ||
|
|
7429d8ae3f |
@@ -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
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -56,6 +56,7 @@ func auth( //nolint:funlen
|
||||
false,
|
||||
false,
|
||||
"00000000-0000-0000-0000-000000000000",
|
||||
"5181f67e2844e4b60d571fa346cac9c37fc00d1ff519212eae6cead138e639ba",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get hasura env vars: %w", err)
|
||||
|
||||
@@ -33,6 +33,7 @@ func expectedAuth() *Service {
|
||||
"AUTH_DISABLE_SIGNUP": "false",
|
||||
"AUTH_EMAIL_PASSWORDLESS_ENABLED": "true",
|
||||
"AUTH_EMAIL_SIGNIN_EMAIL_VERIFIED_REQUIRED": "true",
|
||||
"AUTH_ENCRYPTION_KEY": "5181f67e2844e4b60d571fa346cac9c37fc00d1ff519212eae6cead138e639ba",
|
||||
"AUTH_GRAVATAR_DEFAULT": "gravatarDefault",
|
||||
"AUTH_GRAVATAR_ENABLED": "true",
|
||||
"AUTH_GRAVATAR_RATING": "gravatarRating",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -2,6 +2,23 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [@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
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
"tailwindcss": "^3.4.12",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths-webpack-plugin": "^4.1.0",
|
||||
"vite": "^5.4.20",
|
||||
"vite": "^5.4.21",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
|
||||
34
dashboard/pnpm-lock.yaml
generated
34
dashboard/pnpm-lock.yaml
generated
@@ -415,7 +415,7 @@ importers:
|
||||
version: 6.21.0(eslint@8.57.0)(typescript@5.8.3)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^4.7.0
|
||||
version: 4.7.0(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0))
|
||||
version: 4.7.0(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0))
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.14.8)(jsdom@22.1.0)(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(terser@5.44.0))
|
||||
@@ -525,11 +525,11 @@ importers:
|
||||
specifier: ^4.1.0
|
||||
version: 4.2.0
|
||||
vite:
|
||||
specifier: ^5.4.20
|
||||
version: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
specifier: ^5.4.21
|
||||
version: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite-tsconfig-paths:
|
||||
specifier: ^4.3.2
|
||||
version: 4.3.2(typescript@5.8.3)(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0))
|
||||
version: 4.3.2(typescript@5.8.3)(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0))
|
||||
vitest:
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.14.8)(jsdom@22.1.0)(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(terser@5.44.0)
|
||||
@@ -8576,13 +8576,13 @@ packages:
|
||||
vite-tsconfig-paths@4.3.2:
|
||||
resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==}
|
||||
peerDependencies:
|
||||
vite: '*'
|
||||
vite: '>=4.5.14'
|
||||
peerDependenciesMeta:
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
vite@5.4.20:
|
||||
resolution: {integrity: sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==}
|
||||
vite@5.4.21:
|
||||
resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -13299,7 +13299,7 @@ snapshots:
|
||||
|
||||
'@ungap/structured-clone@1.2.0': {}
|
||||
|
||||
'@vitejs/plugin-react@4.7.0(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0))':
|
||||
'@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.4
|
||||
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
|
||||
@@ -13307,7 +13307,7 @@ snapshots:
|
||||
'@rolldown/pluginutils': 1.0.0-beta.27
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.17.0
|
||||
vite: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -13338,14 +13338,14 @@ snapshots:
|
||||
chai: 5.3.3
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/mocker@3.2.4(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0))':
|
||||
'@vitest/mocker@3.2.4(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.2.4
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.19
|
||||
optionalDependencies:
|
||||
msw: 2.11.4(@types/node@20.14.8)(typescript@5.8.3)
|
||||
vite: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
|
||||
'@vitest/pretty-format@3.2.4':
|
||||
dependencies:
|
||||
@@ -18484,7 +18484,7 @@ snapshots:
|
||||
debug: 4.4.3
|
||||
es-module-lexer: 1.7.0
|
||||
pathe: 2.0.3
|
||||
vite: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- less
|
||||
@@ -18496,18 +18496,18 @@ snapshots:
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0)):
|
||||
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0)):
|
||||
dependencies:
|
||||
debug: 4.4.1
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.1.6(typescript@5.8.3)
|
||||
optionalDependencies:
|
||||
vite: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
vite@5.4.20(@types/node@20.14.8)(terser@5.44.0):
|
||||
vite@5.4.21(@types/node@20.14.8)(terser@5.44.0):
|
||||
dependencies:
|
||||
esbuild: 0.25.10
|
||||
postcss: 8.5.3
|
||||
@@ -18521,7 +18521,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/chai': 5.2.2
|
||||
'@vitest/expect': 3.2.4
|
||||
'@vitest/mocker': 3.2.4(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(vite@5.4.20(@types/node@20.14.8)(terser@5.44.0))
|
||||
'@vitest/mocker': 3.2.4(msw@2.11.4(@types/node@20.14.8)(typescript@5.8.3))(vite@5.4.21(@types/node@20.14.8)(terser@5.44.0))
|
||||
'@vitest/pretty-format': 3.2.4
|
||||
'@vitest/runner': 3.2.4
|
||||
'@vitest/snapshot': 3.2.4
|
||||
@@ -18539,7 +18539,7 @@ snapshots:
|
||||
tinyglobby: 0.2.15
|
||||
tinypool: 1.1.1
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 5.4.20(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite: 5.4.21(@types/node@20.14.8)(terser@5.44.0)
|
||||
vite-node: 3.2.4(@types/node@20.14.8)(terser@5.44.0)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
|
||||
165
dashboard/src/pages/api/support/create-ticket.ts
Normal file
165
dashboard/src/pages/api/support/create-ticket.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { nhostRoutesClient } from '@/utils/nhost';
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
export type CreateTicketRequest = {
|
||||
project: string;
|
||||
services: Array<{ label: string; value: string }>;
|
||||
priority: string;
|
||||
subject: string;
|
||||
description: string;
|
||||
userName: string;
|
||||
userEmail: string;
|
||||
};
|
||||
|
||||
export type CreateTicketResponse = {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
type GetProjectResponse = {
|
||||
apps: Array<{
|
||||
id: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<CreateTicketResponse>,
|
||||
) {
|
||||
if (req.method !== 'POST') {
|
||||
return res
|
||||
.status(405)
|
||||
.json({ success: false, error: 'Method not allowed' });
|
||||
}
|
||||
|
||||
try {
|
||||
const {
|
||||
project,
|
||||
services,
|
||||
priority,
|
||||
subject,
|
||||
description,
|
||||
userName,
|
||||
userEmail,
|
||||
} = req.body as CreateTicketRequest;
|
||||
|
||||
// Validate required environment variables
|
||||
if (
|
||||
!process.env.NEXT_ZENDESK_USER_EMAIL ||
|
||||
!process.env.NEXT_ZENDESK_API_KEY ||
|
||||
!process.env.NEXT_ZENDESK_URL
|
||||
) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'Zendesk configuration is missing',
|
||||
});
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (
|
||||
!project ||
|
||||
!services ||
|
||||
!priority ||
|
||||
!subject ||
|
||||
!description ||
|
||||
!userName ||
|
||||
!userEmail
|
||||
) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Missing required fields',
|
||||
});
|
||||
}
|
||||
|
||||
const token = req.headers.authorization?.split(' ')[1];
|
||||
|
||||
try {
|
||||
// we use this to verify the owner of the JWT token has access to the project
|
||||
const resp = await nhostRoutesClient.graphql.request<GetProjectResponse>(
|
||||
{
|
||||
query: `query GetProject($subdomain: String!){
|
||||
apps(where: {subdomain: {_eq: $subdomain}}) {
|
||||
id
|
||||
}
|
||||
}`,
|
||||
variables: {
|
||||
subdomain: project,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (resp.body.data?.apps.length !== 1) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Invalid project subdomain',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Invalid project subdomain',
|
||||
});
|
||||
}
|
||||
|
||||
const auth = btoa(
|
||||
`${process.env.NEXT_ZENDESK_USER_EMAIL}/token:${process.env.NEXT_ZENDESK_API_KEY}`,
|
||||
);
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_ZENDESK_URL}/api/v2/requests.json`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Basic ${auth}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
request: {
|
||||
subject,
|
||||
comment: {
|
||||
body: description,
|
||||
},
|
||||
priority,
|
||||
requester: {
|
||||
name: userName,
|
||||
email: userEmail,
|
||||
},
|
||||
custom_fields: [
|
||||
// these custom field IDs come from zendesk
|
||||
{
|
||||
id: 19502784542098,
|
||||
value: project,
|
||||
},
|
||||
{
|
||||
id: 19922709880978,
|
||||
value: services.map((service) => service.value?.toLowerCase()),
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('Zendesk API error:', errorText);
|
||||
return res.status(response.status).json({
|
||||
success: false,
|
||||
error: `Failed to create ticket: ${response.statusText}`,
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error creating ticket:', error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'An unexpected error occurred',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ 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,
|
||||
@@ -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();
|
||||
},
|
||||
{
|
||||
@@ -330,21 +306,6 @@ 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">
|
||||
<Text color="secondary" className="text-right text-sm">
|
||||
We will contact you at <strong>{user?.email}</strong>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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<T>
|
||||
|
||||
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')]
|
||||
})
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: ""
|
||||
@@ -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
|
||||
@@ -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: {}
|
||||
@@ -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: ""
|
||||
@@ -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: ""
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
DROP TABLE "public"."tasks";
|
||||
@@ -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;
|
||||
@@ -1 +0,0 @@
|
||||
DROP TABLE "public"."attachments";
|
||||
@@ -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);
|
||||
@@ -1 +0,0 @@
|
||||
DROP TABLE public.ninja_turtles;
|
||||
@@ -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
|
||||
);
|
||||
@@ -1 +0,0 @@
|
||||
DROP TABLE public.comments;
|
||||
@@ -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
|
||||
);
|
||||
@@ -1 +0,0 @@
|
||||
DELETE FROM public.ninja_turtles WHERE name IN ('Leonardo', 'Raphael', 'Donatello', 'Michelangelo');
|
||||
@@ -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());
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE public.ninja_turtles DROP COLUMN description;
|
||||
@@ -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';
|
||||
@@ -1 +0,0 @@
|
||||
DROP TABLE IF EXISTS movies CASCADE;
|
||||
@@ -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);
|
||||
@@ -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]
|
||||
|
||||
131
examples/demos/react-demo/pnpm-lock.yaml
generated
131
examples/demos/react-demo/pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
52
examples/demos/sveltekit-demo/pnpm-lock.yaml
generated
52
examples/demos/sveltekit-demo/pnpm-lock.yaml
generated
@@ -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: {}
|
||||
|
||||
955
examples/demos/vue-demo/pnpm-lock.yaml
generated
955
examples/demos/vue-demo/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
1836
examples/guides/react-apollo/pnpm-lock.yaml
generated
1836
examples/guides/react-apollo/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
1150
examples/guides/react-query/pnpm-lock.yaml
generated
1150
examples/guides/react-query/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
926
examples/guides/react-urql/pnpm-lock.yaml
generated
926
examples/guides/react-urql/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
16
examples/quickstarts/react/pnpm-lock.yaml
generated
16
examples/quickstarts/react/pnpm-lock.yaml
generated
@@ -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)
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^6.1.1",
|
||||
"svelte": "^5.38.1",
|
||||
"vite": "^7.1.2"
|
||||
"vite": "^7.1.11"
|
||||
}
|
||||
}
|
||||
|
||||
30
examples/quickstarts/svelte/pnpm-lock.yaml
generated
30
examples/quickstarts/svelte/pnpm-lock.yaml
generated
@@ -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: {}
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"vite": "^7.1.2"
|
||||
"vite": "^7.1.11"
|
||||
}
|
||||
}
|
||||
|
||||
16
examples/quickstarts/vue/pnpm-lock.yaml
generated
16
examples/quickstarts/vue/pnpm-lock.yaml
generated
@@ -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)
|
||||
|
||||
@@ -64,7 +64,7 @@ rating = 'g'
|
||||
|
||||
[auth.session]
|
||||
[auth.session.accessToken]
|
||||
expiresIn = 900
|
||||
expiresIn = 65
|
||||
|
||||
[auth.session.refreshToken]
|
||||
expiresIn = 2592000
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: {}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
52
examples/tutorials/nhost-vue-tutorial/pnpm-lock.yaml
generated
52
examples/tutorials/nhost-vue-tutorial/pnpm-lock.yaml
generated
@@ -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
2
go.mod
@@ -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
8
go.sum
@@ -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=
|
||||
|
||||
10
package.json
10
package.json
@@ -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",
|
||||
|
||||
@@ -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
|
||||
// }
|
||||
// ]
|
||||
|
||||
@@ -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";
|
||||
|
||||
121
packages/nhost-js/src/fetch/middlewareWithAdminSession.ts
Normal file
121
packages/nhost-js/src/fetch/middlewareWithAdminSession.ts
Normal 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 });
|
||||
};
|
||||
38
packages/nhost-js/src/fetch/middlewareWithHeaders.ts
Normal file
38
packages/nhost-js/src/fetch/middlewareWithHeaders.ts
Normal 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 });
|
||||
};
|
||||
57
packages/nhost-js/src/fetch/middlewareWithRole.ts
Normal file
57
packages/nhost-js/src/fetch/middlewareWithRole.ts
Normal 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 });
|
||||
};
|
||||
@@ -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";
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 ?? [])],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
28
pnpm-lock.yaml
generated
@@ -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:
|
||||
|
||||
@@ -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"}'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -3643,84 +3643,84 @@ var swaggerSpec = []string{
|
||||
"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",
|
||||
"AP//J2Dqrmj4AAA=",
|
||||
"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",
|
||||
"fwAAAP//NPNLB2r4AAA=",
|
||||
}
|
||||
|
||||
// GetSwagger returns the content of the embedded swagger specification file
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
crypto "github.com/nhost/nhost/services/auth/go/cryto"
|
||||
"github.com/nhost/nhost/services/auth/go/migrations"
|
||||
"github.com/nhost/nhost/services/auth/go/sql"
|
||||
"github.com/urfave/cli/v3"
|
||||
@@ -48,7 +49,11 @@ func insertRoles(
|
||||
}
|
||||
|
||||
func applyMigrations(
|
||||
ctx context.Context, cmd *cli.Command, db *sql.Queries, logger *slog.Logger,
|
||||
ctx context.Context,
|
||||
cmd *cli.Command,
|
||||
db *sql.Queries,
|
||||
encrypter *crypto.Encrypter,
|
||||
logger *slog.Logger,
|
||||
) error {
|
||||
postgresURL := cmd.String(flagPostgresMigrationsConnection)
|
||||
if postgresURL == "" {
|
||||
@@ -71,5 +76,15 @@ func applyMigrations(
|
||||
return fmt.Errorf("failed to apply hasura metadata: %w", err)
|
||||
}
|
||||
|
||||
if err := migrations.EncryptTOTPSecrets(ctx, db, encrypter, logger); err != nil {
|
||||
logger.ErrorContext(
|
||||
ctx,
|
||||
"failed to encrypt TOTP secrets",
|
||||
slog.String("error", err.Error()),
|
||||
)
|
||||
|
||||
return fmt.Errorf("failed to encrypt TOTP secrets: %w", err)
|
||||
}
|
||||
|
||||
return insertRoles(ctx, cmd, db, logger)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/nhost/nhost/services/auth/docs"
|
||||
"github.com/nhost/nhost/services/auth/go/api"
|
||||
"github.com/nhost/nhost/services/auth/go/controller"
|
||||
crypto "github.com/nhost/nhost/services/auth/go/cryto"
|
||||
"github.com/nhost/nhost/services/auth/go/hibp"
|
||||
"github.com/nhost/nhost/services/auth/go/middleware"
|
||||
"github.com/nhost/nhost/services/auth/go/middleware/ratelimit"
|
||||
@@ -29,6 +30,7 @@ const (
|
||||
flagPort = "port"
|
||||
flagDebug = "debug"
|
||||
flagLogFormatTEXT = "log-format-text"
|
||||
flagEncryptionKey = "encryption-key"
|
||||
flagTrustedProxies = "trusted-proxies"
|
||||
flagPostgresConnection = "postgres"
|
||||
flagPostgresMigrationsConnection = "postgres-migrations"
|
||||
@@ -211,6 +213,13 @@ func CommandServe() *cli.Command { //nolint:funlen,maintidx
|
||||
Category: "general",
|
||||
Sources: cli.EnvVars("AUTH_LOG_FORMAT_TEXT"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint: exhaustruct
|
||||
Name: flagEncryptionKey,
|
||||
Usage: "32 bytes encryption key used to encrypt sensitive data. Must be a hex-encoded string",
|
||||
Category: "security",
|
||||
Sources: cli.EnvVars("AUTH_ENCRYPTION_KEY"),
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{ //nolint: exhaustruct
|
||||
Name: flagPostgresConnection,
|
||||
Usage: "PostgreSQL connection URI: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING",
|
||||
@@ -1288,7 +1297,11 @@ func getDependencies( //nolint:ireturn
|
||||
}
|
||||
|
||||
func getGoServer( //nolint:funlen
|
||||
ctx context.Context, cmd *cli.Command, db *sql.Queries, logger *slog.Logger,
|
||||
ctx context.Context,
|
||||
cmd *cli.Command,
|
||||
db *sql.Queries,
|
||||
encrypter *crypto.Encrypter,
|
||||
logger *slog.Logger,
|
||||
) (*http.Server, error) {
|
||||
router := gin.New()
|
||||
|
||||
@@ -1347,6 +1360,7 @@ func getGoServer( //nolint:funlen
|
||||
oauthProviders,
|
||||
idTokenValidator,
|
||||
controller.NewTotp(cmd.String(flagMfaTotpIssuer), time.Now),
|
||||
encrypter,
|
||||
cmd.Root().Version,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -1410,12 +1424,17 @@ func serve(ctx context.Context, cmd *cli.Command) error {
|
||||
}
|
||||
defer pool.Close()
|
||||
|
||||
encrypter, err := crypto.NewEncrypterFromString(cmd.String(flagEncryptionKey))
|
||||
if err != nil {
|
||||
return fmt.Errorf("problem creating encrypter: %w", err)
|
||||
}
|
||||
|
||||
db := sql.New(pool)
|
||||
if err := applyMigrations(servCtx, cmd, db, logger); err != nil {
|
||||
if err := applyMigrations(servCtx, cmd, db, encrypter, logger); err != nil {
|
||||
return fmt.Errorf("failed to apply migrations: %w", err)
|
||||
}
|
||||
|
||||
server, err := getGoServer(ctx, cmd, db, logger)
|
||||
server, err := getGoServer(ctx, cmd, db, encrypter, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create server: %w", err)
|
||||
}
|
||||
|
||||
@@ -32,16 +32,22 @@ func (ctrl *Controller) ChangeUserMfa( //nolint:ireturn
|
||||
accountName = user.ID.String()
|
||||
}
|
||||
|
||||
secret, imgBase64, err := ctrl.totp.Generate(accountName)
|
||||
plainSecret, imgBase64, err := ctrl.totp.Generate(accountName)
|
||||
if err != nil {
|
||||
logger.ErrorContext(ctx, "failed to generate TOTP: %v", logError(err))
|
||||
return ctrl.sendError(ErrInternalServerError), nil
|
||||
}
|
||||
|
||||
secret, err := ctrl.encrypter.Encrypt([]byte(plainSecret))
|
||||
if err != nil {
|
||||
logger.ErrorContext(ctx, "failed to encrypt TOTP secret: %v", logError(err))
|
||||
return ctrl.sendError(ErrInternalServerError), nil
|
||||
}
|
||||
|
||||
if err := ctrl.wf.db.UpdateUserTotpSecret(
|
||||
ctx, sql.UpdateUserTotpSecretParams{
|
||||
ID: user.ID,
|
||||
TotpSecret: sql.Text(secret),
|
||||
TotpSecret: sql.Text(string(secret)),
|
||||
},
|
||||
); err != nil {
|
||||
logger.ErrorContext(ctx, "failed to update TOTP secret: %v", logError(err))
|
||||
@@ -50,6 +56,6 @@ func (ctrl *Controller) ChangeUserMfa( //nolint:ireturn
|
||||
|
||||
return api.ChangeUserMfa200JSONResponse{
|
||||
ImageUrl: "data:image/png;base64," + imgBase64,
|
||||
TotpSecret: secret,
|
||||
TotpSecret: plainSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -13,10 +13,19 @@ import (
|
||||
"github.com/nhost/nhost/services/auth/go/controller"
|
||||
"github.com/nhost/nhost/services/auth/go/controller/mock"
|
||||
"github.com/nhost/nhost/services/auth/go/sql"
|
||||
"github.com/nhost/nhost/services/auth/go/testhelpers"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
type fakeEncrypter struct{}
|
||||
|
||||
func (e *fakeEncrypter) Encrypt(_ []byte) ([]byte, error) {
|
||||
return []byte("encrypted-data"), nil
|
||||
}
|
||||
|
||||
func (e *fakeEncrypter) Decrypt(_ []byte) ([]byte, error) {
|
||||
return []byte("decrypted-data"), nil
|
||||
}
|
||||
|
||||
func ImageURLComparer() cmp.Option {
|
||||
return cmp.FilterPath(func(p cmp.Path) bool {
|
||||
if last := p.Last(); last != nil {
|
||||
@@ -78,15 +87,9 @@ func TestGetMfaTotpGenerate(t *testing.T) {
|
||||
gomock.Any(),
|
||||
cmpDBParams(
|
||||
sql.UpdateUserTotpSecretParams{
|
||||
ID: userID,
|
||||
TotpSecret: sql.Text(
|
||||
"FEWCQAIILM6UOYZCPFYRAPAUCIFUUUK3JUZXWKJIN4ORQNK4EQCQ",
|
||||
),
|
||||
ID: userID,
|
||||
TotpSecret: sql.Text("encrypted-data"),
|
||||
},
|
||||
testhelpers.FilterPathLast(
|
||||
[]string{".TotpSecret", "text()"},
|
||||
cmp.Comparer(cmpTicket),
|
||||
),
|
||||
),
|
||||
).Return(nil)
|
||||
|
||||
@@ -97,9 +100,11 @@ func TestGetMfaTotpGenerate(t *testing.T) {
|
||||
ImageUrl: "data:image/png;base64,",
|
||||
TotpSecret: "AIRVH6M4V422LZI6IRBN5SCEO6BWVIW3G6PLKKTENHMGZYRALPOQ",
|
||||
},
|
||||
expectedJWT: nil,
|
||||
jwtTokenFn: jwtTokenFn,
|
||||
getControllerOpts: nil,
|
||||
expectedJWT: nil,
|
||||
jwtTokenFn: jwtTokenFn,
|
||||
getControllerOpts: []getControllerOptsFunc{
|
||||
withEncrypter(&fakeEncrypter{}),
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
@@ -130,8 +130,14 @@ type DBClient interface { //nolint:interfacebloat
|
||||
) ([]sql.RefreshTokenAndGetUserRolesRow, error)
|
||||
}
|
||||
|
||||
type Encrypter interface {
|
||||
Encrypt(plainText []byte) ([]byte, error)
|
||||
Decrypt(cipherText []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type Controller struct {
|
||||
totp *Totp
|
||||
encrypter Encrypter
|
||||
idTokenValidator *oidc.IDTokenValidatorProviders
|
||||
wf *Workflows
|
||||
config Config
|
||||
@@ -150,6 +156,7 @@ func New(
|
||||
providers providers.Map,
|
||||
idTokenValidator *oidc.IDTokenValidatorProviders,
|
||||
totp *Totp,
|
||||
encrypter Encrypter,
|
||||
version string,
|
||||
) (*Controller, error) {
|
||||
validator, err := NewWorkflows(
|
||||
@@ -182,6 +189,7 @@ func New(
|
||||
Webauthn: wa,
|
||||
idTokenValidator: idTokenValidator,
|
||||
totp: totp,
|
||||
encrypter: encrypter,
|
||||
version: version,
|
||||
Providers: providers,
|
||||
}, nil
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/nhost/nhost/services/auth/go/api"
|
||||
"github.com/nhost/nhost/services/auth/go/controller"
|
||||
"github.com/nhost/nhost/services/auth/go/controller/mock"
|
||||
crypto "github.com/nhost/nhost/services/auth/go/cryto"
|
||||
"github.com/nhost/nhost/services/auth/go/oidc"
|
||||
"github.com/nhost/nhost/services/auth/go/providers"
|
||||
"github.com/nhost/nhost/services/auth/go/testhelpers"
|
||||
@@ -180,6 +181,7 @@ type getControllerOpts struct {
|
||||
hibp func(*gomock.Controller) *mock.MockHIBPClient
|
||||
idTokenValidatorProviders func(t *testing.T) *oidc.IDTokenValidatorProviders
|
||||
totp *controller.Totp
|
||||
encrypter controller.Encrypter
|
||||
}
|
||||
|
||||
type getControllerOptsFunc func(*getControllerOpts)
|
||||
@@ -222,7 +224,13 @@ func withTotp(totp *controller.Totp) getControllerOptsFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func getController(
|
||||
func withEncrypter(encrypter controller.Encrypter) getControllerOptsFunc {
|
||||
return func(o *getControllerOpts) {
|
||||
o.encrypter = encrypter
|
||||
}
|
||||
}
|
||||
|
||||
func getController( //nolint:cyclop
|
||||
t *testing.T,
|
||||
ctrl *gomock.Controller,
|
||||
configFn func() *controller.Config,
|
||||
@@ -278,6 +286,17 @@ func getController(
|
||||
controllerOpts.totp = controller.NewTotp("auth", time.Now)
|
||||
}
|
||||
|
||||
if controllerOpts.encrypter == nil {
|
||||
encrypter, err := crypto.NewEncrypterFromString(
|
||||
"41e7109ea7cfff9e4100d29bbd58bacab0258d0fc4c0495746ed0cf166650f9d",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create encrypter: %v", err)
|
||||
}
|
||||
|
||||
controllerOpts.encrypter = encrypter
|
||||
}
|
||||
|
||||
c, err := controller.New(
|
||||
db(ctrl),
|
||||
config,
|
||||
@@ -295,6 +314,7 @@ func getController(
|
||||
},
|
||||
idTokenValidator,
|
||||
controllerOpts.totp,
|
||||
controllerOpts.encrypter,
|
||||
"dev",
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -1124,3 +1124,57 @@ func (mr *MockDBClientMockRecorder) UpdateUserVerifyEmail(ctx, id any) *gomock.C
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserVerifyEmail", reflect.TypeOf((*MockDBClient)(nil).UpdateUserVerifyEmail), ctx, id)
|
||||
}
|
||||
|
||||
// MockEncrypter is a mock of Encrypter interface.
|
||||
type MockEncrypter struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockEncrypterMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockEncrypterMockRecorder is the mock recorder for MockEncrypter.
|
||||
type MockEncrypterMockRecorder struct {
|
||||
mock *MockEncrypter
|
||||
}
|
||||
|
||||
// NewMockEncrypter creates a new mock instance.
|
||||
func NewMockEncrypter(ctrl *gomock.Controller) *MockEncrypter {
|
||||
mock := &MockEncrypter{ctrl: ctrl}
|
||||
mock.recorder = &MockEncrypterMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockEncrypter) EXPECT() *MockEncrypterMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Decrypt mocks base method.
|
||||
func (m *MockEncrypter) Decrypt(cipherText []byte) ([]byte, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Decrypt", cipherText)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Decrypt indicates an expected call of Decrypt.
|
||||
func (mr *MockEncrypterMockRecorder) Decrypt(cipherText any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decrypt", reflect.TypeOf((*MockEncrypter)(nil).Decrypt), cipherText)
|
||||
}
|
||||
|
||||
// Encrypt mocks base method.
|
||||
func (m *MockEncrypter) Encrypt(plainText []byte) ([]byte, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Encrypt", plainText)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Encrypt indicates an expected call of Encrypt.
|
||||
func (mr *MockEncrypterMockRecorder) Encrypt(plainText any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Encrypt", reflect.TypeOf((*MockEncrypter)(nil).Encrypt), plainText)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,13 @@ func (ctrl *Controller) postUserMfaDeactivate( //nolint:ireturn
|
||||
return ctrl.sendError(ErrNoTotpSecret)
|
||||
}
|
||||
|
||||
valid := ctrl.totp.Validate(req.Body.Code, user.TotpSecret.String)
|
||||
totpSecret, err := ctrl.encrypter.Decrypt([]byte(user.TotpSecret.String))
|
||||
if err != nil {
|
||||
logger.ErrorContext(ctx, "failed to decrypt totp secret", logError(err))
|
||||
return ctrl.sendError(ErrInternalServerError)
|
||||
}
|
||||
|
||||
valid := ctrl.totp.Validate(req.Body.Code, string(totpSecret))
|
||||
if !valid {
|
||||
logger.WarnContext(ctx, "invalid totp")
|
||||
return ctrl.sendError(ErrInvalidTotp)
|
||||
@@ -65,7 +71,13 @@ func (ctrl *Controller) postUserMfaActivate( //nolint:ireturn
|
||||
return ctrl.sendError(ErrNoTotpSecret)
|
||||
}
|
||||
|
||||
valid := ctrl.totp.Validate(req.Body.Code, user.TotpSecret.String)
|
||||
totpSecret, err := ctrl.encrypter.Decrypt([]byte(user.TotpSecret.String))
|
||||
if err != nil {
|
||||
logger.ErrorContext(ctx, "failed to decrypt totp secret", logError(err))
|
||||
return ctrl.sendError(ErrInternalServerError)
|
||||
}
|
||||
|
||||
valid := ctrl.totp.Validate(req.Body.Code, string(totpSecret))
|
||||
if !valid {
|
||||
logger.WarnContext(ctx, "invalid totp")
|
||||
return ctrl.sendError(ErrInvalidTotp)
|
||||
|
||||
@@ -58,7 +58,7 @@ func TestVerifyChangeUserMfa(t *testing.T) { //nolint:maintidx
|
||||
).Return(sql.AuthUser{ //nolint:exhaustruct
|
||||
ID: userID,
|
||||
Email: sql.Text("user@acme.local"),
|
||||
TotpSecret: sql.Text("FEWCQAIILM6UOYZCPFYRAPAUCIFUUUK3JUZXWKJIN4ORQNK4EQCQ"),
|
||||
TotpSecret: sql.Text(encryptedTotpSecret),
|
||||
ActiveMfaType: pgtype.Text{}, //nolint:exhaustruct
|
||||
}, nil)
|
||||
|
||||
@@ -101,7 +101,7 @@ func TestVerifyChangeUserMfa(t *testing.T) { //nolint:maintidx
|
||||
).Return(sql.AuthUser{ //nolint:exhaustruct
|
||||
ID: userID,
|
||||
Email: sql.Text("user@acme.local"),
|
||||
TotpSecret: sql.Text("FEWCQAIILM6UOYZCPFYRAPAUCIFUUUK3JUZXWKJIN4ORQNK4EQCQ"),
|
||||
TotpSecret: sql.Text(encryptedTotpSecret),
|
||||
ActiveMfaType: sql.Text("totp"),
|
||||
}, nil)
|
||||
|
||||
@@ -140,7 +140,7 @@ func TestVerifyChangeUserMfa(t *testing.T) { //nolint:maintidx
|
||||
).Return(sql.AuthUser{ //nolint:exhaustruct
|
||||
ID: userID,
|
||||
Email: sql.Text("user@acme.local"),
|
||||
TotpSecret: sql.Text("FEWCQAIILM6UOYZCPFYRAPAUCIFUUUK3JUZXWKJIN4ORQNK4EQCQ"),
|
||||
TotpSecret: sql.Text(encryptedTotpSecret),
|
||||
ActiveMfaType: pgtype.Text{}, //nolint:exhaustruct
|
||||
}, nil)
|
||||
|
||||
@@ -179,7 +179,7 @@ func TestVerifyChangeUserMfa(t *testing.T) { //nolint:maintidx
|
||||
).Return(sql.AuthUser{ //nolint:exhaustruct
|
||||
ID: userID,
|
||||
Email: sql.Text("user@acme.local"),
|
||||
TotpSecret: sql.Text("FEWCQAIILM6UOYZCPFYRAPAUCIFUUUK3JUZXWKJIN4ORQNK4EQCQ"),
|
||||
TotpSecret: sql.Text(encryptedTotpSecret),
|
||||
ActiveMfaType: sql.Text("totp"),
|
||||
}, nil)
|
||||
|
||||
@@ -261,7 +261,7 @@ func TestVerifyChangeUserMfa(t *testing.T) { //nolint:maintidx
|
||||
).Return(sql.AuthUser{ //nolint:exhaustruct
|
||||
ID: userID,
|
||||
Email: sql.Text("user@acme.local"),
|
||||
TotpSecret: sql.Text("FEWCQAIILM6UOYZCPFYRAPAUCIFUUUK3JUZXWKJIN4ORQNK4EQCQ"),
|
||||
TotpSecret: sql.Text(encryptedTotpSecret),
|
||||
ActiveMfaType: sql.Text("totp"),
|
||||
}, nil)
|
||||
|
||||
|
||||
@@ -32,7 +32,13 @@ func (ctrl *Controller) VerifySignInMfaTotp( //nolint:ireturn
|
||||
return ctrl.sendError(ErrNoTotpSecret), nil
|
||||
}
|
||||
|
||||
valid := ctrl.totp.Validate(req.Body.Otp, user.TotpSecret.String)
|
||||
totpSecret, err := ctrl.encrypter.Decrypt([]byte(user.TotpSecret.String))
|
||||
if err != nil {
|
||||
logger.ErrorContext(ctx, "failed to decrypt totp secret", logError(err))
|
||||
return ctrl.sendError(ErrInternalServerError), nil
|
||||
}
|
||||
|
||||
valid := ctrl.totp.Validate(req.Body.Otp, string(totpSecret))
|
||||
if !valid {
|
||||
logger.WarnContext(ctx, "invalid totp")
|
||||
return ctrl.sendError(ErrInvalidTotp), nil
|
||||
|
||||
@@ -16,6 +16,8 @@ import (
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
const encryptedTotpSecret = "fcceaa61a188729ca5e57108030cd5fde120365a986d3f4e1af1fafa598e8e99092128318cfb8fc1facbb870cbd2c90c5714b2914170bd69c868e906576f2a516da9e48c4237d87432342b6cf45392a2" //nolint:lll,gosec
|
||||
|
||||
func fakeNow(t time.Time) func() time.Time {
|
||||
return func() time.Time {
|
||||
return t
|
||||
@@ -40,7 +42,7 @@ func getUserSigninMfaTotp(userID uuid.UUID) sql.AuthUser {
|
||||
"$2a$10$pyv7eu9ioQcFnLSz7u/enex22P3ORdh6z6116Vj5a3vSjo0oxFa1u",
|
||||
),
|
||||
EmailVerified: true,
|
||||
TotpSecret: sql.Text("FEWCQAIILM6UOYZCPFYRAPAUCIFUUUK3JUZXWKJIN4ORQNK4EQCQ"),
|
||||
TotpSecret: sql.Text(encryptedTotpSecret),
|
||||
ActiveMfaType: sql.Text("totp"),
|
||||
}
|
||||
}
|
||||
|
||||
117
services/auth/go/cryto/crypto.go
Normal file
117
services/auth/go/cryto/crypto.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCiphertextTooShort = errors.New("ciphertext too short")
|
||||
ErrDecryptionFailed = errors.New("decryption failed")
|
||||
ErrWrongKeySize = errors.New(
|
||||
"key must be 16, 24, or 32 bytes (AES-128, AES-192, or AES-256)",
|
||||
)
|
||||
)
|
||||
|
||||
type Encrypter struct {
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
// NewEncrypter creates a new encrypter using AES-GCM (AEAD mode).
|
||||
func NewEncrypter(key []byte) (*Encrypter, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
|
||||
}
|
||||
|
||||
aead, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create GCM: %w", err)
|
||||
}
|
||||
|
||||
return &Encrypter{
|
||||
aead: aead,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewEncrypterFromString creates a new encrypter from a hex string key.
|
||||
func NewEncrypterFromString(hexKey string) (*Encrypter, error) {
|
||||
key, err := KeyFromString(hexKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewEncrypter(key)
|
||||
}
|
||||
|
||||
// Encrypt encrypts the plaintext using AES-GCM
|
||||
// The result includes the nonce and authentication tag.
|
||||
func (enc *Encrypter) Encrypt(plaintext []byte) ([]byte, error) {
|
||||
nonce := make([]byte, enc.aead.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return nil, fmt.Errorf("failed to generate nonce: %w", err)
|
||||
}
|
||||
|
||||
ciphertext := enc.aead.Seal(nonce, nonce, plaintext, nil)
|
||||
|
||||
encoded := make([]byte, hex.EncodedLen(len(ciphertext)))
|
||||
hex.Encode(encoded, ciphertext)
|
||||
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts the ciphertext using AES-GCM
|
||||
// Automatically verifies authentication tag.
|
||||
func (enc *Encrypter) Decrypt(ciphertext []byte) ([]byte, error) {
|
||||
decoded := make([]byte, hex.DecodedLen(len(ciphertext)))
|
||||
|
||||
_, err := hex.Decode(decoded, ciphertext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode hex string: %w", err)
|
||||
}
|
||||
|
||||
if len(decoded) < enc.aead.NonceSize() {
|
||||
return nil, ErrCiphertextTooShort
|
||||
}
|
||||
|
||||
nonce := decoded[:enc.aead.NonceSize()]
|
||||
encryptedData := decoded[enc.aead.NonceSize():]
|
||||
|
||||
plaintext, err := enc.aead.Open(nil, nonce, encryptedData, nil)
|
||||
if err != nil {
|
||||
return nil, ErrDecryptionFailed
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// GenerateKey generates a random 256-bit key for AES-256.
|
||||
func GenerateKey() ([]byte, error) {
|
||||
key := make([]byte, 32) //nolint:mnd
|
||||
|
||||
_, err := io.ReadFull(rand.Reader, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate key: %w", err)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// KeyFromString creates a key from a hex string.
|
||||
func KeyFromString(hexKey string) ([]byte, error) {
|
||||
key, err := hex.DecodeString(hexKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid hex key: %w", err)
|
||||
}
|
||||
|
||||
if len(key) != 16 && len(key) != 24 && len(key) != 32 {
|
||||
return nil, ErrWrongKeySize
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
415
services/auth/go/cryto/crypto_test.go
Normal file
415
services/auth/go/cryto/crypto_test.go
Normal file
@@ -0,0 +1,415 @@
|
||||
package crypto_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
crypto "github.com/nhost/nhost/services/auth/go/cryto"
|
||||
)
|
||||
|
||||
func TestGenerateKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKey failed: %v", err)
|
||||
}
|
||||
|
||||
if len(key) != 32 {
|
||||
t.Errorf("expected key length 32, got %d", len(key))
|
||||
}
|
||||
|
||||
// Generate another key to ensure randomness
|
||||
key2, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKey failed on second call: %v", err)
|
||||
}
|
||||
|
||||
if bytes.Equal(key, key2) {
|
||||
t.Error("expected different keys, got identical keys")
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyFromString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
hexKey string
|
||||
expectError error
|
||||
expectLen int
|
||||
}{
|
||||
{
|
||||
name: "valid AES-128 key",
|
||||
hexKey: "0123456789abcdef0123456789abcdef",
|
||||
expectError: nil,
|
||||
expectLen: 16,
|
||||
},
|
||||
{
|
||||
name: "valid AES-192 key",
|
||||
hexKey: "0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
expectError: nil,
|
||||
expectLen: 24,
|
||||
},
|
||||
{
|
||||
name: "valid AES-256 key",
|
||||
hexKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
expectError: nil,
|
||||
expectLen: 32,
|
||||
},
|
||||
{
|
||||
name: "invalid hex string",
|
||||
hexKey: "not-a-hex-string",
|
||||
expectError: hex.InvalidByteError('n'),
|
||||
expectLen: 0,
|
||||
},
|
||||
{
|
||||
name: "wrong key size - too short",
|
||||
hexKey: "0123456789abcdef",
|
||||
expectError: crypto.ErrWrongKeySize,
|
||||
expectLen: 0,
|
||||
},
|
||||
{
|
||||
name: "wrong key size - invalid length",
|
||||
hexKey: "0123456789abcdef0123456789abcdef01",
|
||||
expectError: crypto.ErrWrongKeySize,
|
||||
expectLen: 0,
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
hexKey: "",
|
||||
expectError: crypto.ErrWrongKeySize,
|
||||
expectLen: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key, err := crypto.KeyFromString(tc.hexKey)
|
||||
|
||||
if tc.expectError != nil { //nolint:nestif
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got nil")
|
||||
}
|
||||
|
||||
if !errors.Is(err, tc.expectError) && err.Error() != tc.expectError.Error() {
|
||||
t.Errorf("expected error %v, got %v", tc.expectError, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(key) != tc.expectLen {
|
||||
t.Errorf("expected key length %d, got %d", tc.expectLen, len(key))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEncrypter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
keySize int
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "AES-128",
|
||||
keySize: 16,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "AES-192",
|
||||
keySize: 24,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "AES-256",
|
||||
keySize: 32,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "invalid key size",
|
||||
keySize: 15,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "empty key",
|
||||
keySize: 0,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key := make([]byte, tc.keySize)
|
||||
enc, err := crypto.NewEncrypter(key)
|
||||
|
||||
if tc.expectError { //nolint:nestif
|
||||
if err == nil {
|
||||
t.Error("expected error, got nil")
|
||||
}
|
||||
|
||||
if enc != nil {
|
||||
t.Error("expected nil encrypter on error")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if enc == nil {
|
||||
t.Fatal("expected non-nil encrypter")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptDecrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
enc, err := crypto.NewEncrypterFromString(
|
||||
"41e7109ea7cfff9e4100d29bbd58bacab0258d0fc4c0495746ed0cf166650f9d",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create encrypter: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
plaintext []byte
|
||||
}{
|
||||
{
|
||||
name: "simple string",
|
||||
plaintext: []byte("FEWCQAIILM6UOYZCPFYRAPAUCIFUUUK3JUZXWKJIN4ORQNK4EQCQ"),
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
plaintext: []byte(""),
|
||||
},
|
||||
{
|
||||
name: "unicode characters",
|
||||
plaintext: []byte("こんにちは世界 🌍"), //nolint:gosmopolitan
|
||||
},
|
||||
{
|
||||
name: "long text",
|
||||
plaintext: []byte(
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", //nolint:lll
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "binary data",
|
||||
plaintext: []byte{0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Encrypt
|
||||
ciphertext, err := enc.Encrypt(tc.plaintext)
|
||||
if err != nil {
|
||||
t.Fatalf("encryption failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify ciphertext is hex encoded
|
||||
_, err = hex.DecodeString(string(ciphertext))
|
||||
if err != nil {
|
||||
t.Errorf("ciphertext is not valid hex: %v", err)
|
||||
}
|
||||
|
||||
// Decrypt
|
||||
decrypted, err := enc.Decrypt(ciphertext)
|
||||
if err != nil {
|
||||
t.Fatalf("decryption failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify plaintext matches
|
||||
if !bytes.Equal(tc.plaintext, decrypted) {
|
||||
t.Errorf(
|
||||
"decrypted text doesn't match original.\nExpected: %v\nGot: %v",
|
||||
tc.plaintext,
|
||||
decrypted,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptRandomness(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key: %v", err)
|
||||
}
|
||||
|
||||
enc, err := crypto.NewEncrypter(key)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create encrypter: %v", err)
|
||||
}
|
||||
|
||||
plaintext := []byte("test message")
|
||||
|
||||
// Encrypt the same message twice
|
||||
ciphertext1, err := enc.Encrypt(plaintext)
|
||||
if err != nil {
|
||||
t.Fatalf("first encryption failed: %v", err)
|
||||
}
|
||||
|
||||
ciphertext2, err := enc.Encrypt(plaintext)
|
||||
if err != nil {
|
||||
t.Fatalf("second encryption failed: %v", err)
|
||||
}
|
||||
|
||||
// Ciphertexts should be different due to random nonce
|
||||
if bytes.Equal(ciphertext1, ciphertext2) {
|
||||
t.Error("expected different ciphertexts for same plaintext, got identical")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecryptErrors(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key: %v", err)
|
||||
}
|
||||
|
||||
enc, err := crypto.NewEncrypter(key)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create encrypter: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
ciphertext []byte
|
||||
expectError error
|
||||
}{
|
||||
{
|
||||
name: "invalid hex",
|
||||
ciphertext: []byte("not-valid-hex"),
|
||||
expectError: hex.InvalidByteError('n'),
|
||||
},
|
||||
{
|
||||
name: "ciphertext too short",
|
||||
ciphertext: []byte("0123"),
|
||||
expectError: crypto.ErrCiphertextTooShort,
|
||||
},
|
||||
{
|
||||
name: "tampered ciphertext",
|
||||
ciphertext: []byte("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
|
||||
expectError: crypto.ErrDecryptionFailed,
|
||||
},
|
||||
{
|
||||
name: "empty ciphertext",
|
||||
ciphertext: []byte(""),
|
||||
expectError: crypto.ErrCiphertextTooShort,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := enc.Decrypt(tc.ciphertext)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
if !errors.Is(err, tc.expectError) && err.Error() != tc.expectError.Error() {
|
||||
t.Errorf("expected error %v, got %v", tc.expectError, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecryptWithWrongKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key1, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key1: %v", err)
|
||||
}
|
||||
|
||||
key2, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key2: %v", err)
|
||||
}
|
||||
|
||||
enc1, err := crypto.NewEncrypter(key1)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create encrypter1: %v", err)
|
||||
}
|
||||
|
||||
enc2, err := crypto.NewEncrypter(key2)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create encrypter2: %v", err)
|
||||
}
|
||||
|
||||
plaintext := []byte("secret message")
|
||||
|
||||
// Encrypt with first key
|
||||
ciphertext, err := enc1.Encrypt(plaintext)
|
||||
if err != nil {
|
||||
t.Fatalf("encryption failed: %v", err)
|
||||
}
|
||||
|
||||
// Try to decrypt with second key
|
||||
_, err = enc2.Decrypt(ciphertext)
|
||||
if err == nil {
|
||||
t.Fatal("expected decryption to fail with wrong key")
|
||||
}
|
||||
|
||||
if !errors.Is(err, crypto.ErrDecryptionFailed) {
|
||||
t.Errorf("expected ErrDecryptionFailed, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleEncrypters(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key: %v", err)
|
||||
}
|
||||
|
||||
enc1, err := crypto.NewEncrypter(key)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create encrypter1: %v", err)
|
||||
}
|
||||
|
||||
enc2, err := crypto.NewEncrypter(key)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create encrypter2: %v", err)
|
||||
}
|
||||
|
||||
plaintext := []byte("test message")
|
||||
|
||||
// Encrypt with first encrypter
|
||||
ciphertext, err := enc1.Encrypt(plaintext)
|
||||
if err != nil {
|
||||
t.Fatalf("encryption failed: %v", err)
|
||||
}
|
||||
|
||||
// Decrypt with second encrypter (same key)
|
||||
decrypted, err := enc2.Decrypt(ciphertext)
|
||||
if err != nil {
|
||||
t.Fatalf("decryption failed: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(plaintext, decrypted) {
|
||||
t.Errorf("decrypted text doesn't match.\nExpected: %v\nGot: %v", plaintext, decrypted)
|
||||
}
|
||||
}
|
||||
44
services/auth/go/migrations/app.go
Normal file
44
services/auth/go/migrations/app.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
crypto "github.com/nhost/nhost/services/auth/go/cryto"
|
||||
"github.com/nhost/nhost/services/auth/go/sql"
|
||||
)
|
||||
|
||||
func EncryptTOTPSecrets(
|
||||
ctx context.Context, db *sql.Queries, encrypter *crypto.Encrypter, logger *slog.Logger,
|
||||
) error {
|
||||
users, err := db.GetUsersWithUnencryptedTOTPSecret(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get users with unencrypted TOTP secret: %w", err)
|
||||
}
|
||||
|
||||
if len(users) == 0 {
|
||||
logger.InfoContext(ctx, "No users with unencrypted TOTP secret found")
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.InfoContext(ctx, "Encrypting TOTP secrets for users", slog.Int("count", len(users)))
|
||||
|
||||
for _, user := range users {
|
||||
encryptedTOTPSecret, err := encrypter.Encrypt([]byte(user.TotpSecret.String))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt TOTP secret for user %s: %w", user.ID, err)
|
||||
}
|
||||
|
||||
if err := db.UpdateUserTotpSecret(ctx, sql.UpdateUserTotpSecretParams{
|
||||
ID: user.ID,
|
||||
TotpSecret: sql.Text(string(encryptedTOTPSecret)),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed to update TOTP secret for user %s: %w", user.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
logger.InfoContext(ctx, "Successfully encrypted TOTP secrets for all users")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -11,8 +12,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
timeout = 10
|
||||
hasuraDBName = "default"
|
||||
timeout = 10
|
||||
hasuraDBName = "default"
|
||||
errorCodeAlreadyTracked = "already-tracked"
|
||||
errorCodeAlreadyExists = "already-exists"
|
||||
)
|
||||
|
||||
type hasuraErrResponse struct {
|
||||
@@ -21,6 +24,19 @@ type hasuraErrResponse struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type metadataError struct {
|
||||
code string
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *metadataError) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func (e *metadataError) Code() string {
|
||||
return e.code
|
||||
}
|
||||
|
||||
func postMetadata(ctx context.Context, url, hasuraSecret string, data any) error {
|
||||
client := &http.Client{ //nolint: exhaustruct
|
||||
Timeout: time.Second * timeout,
|
||||
@@ -57,8 +73,12 @@ func postMetadata(ctx context.Context, url, hasuraSecret string, data any) error
|
||||
)
|
||||
}
|
||||
|
||||
if errResponse.Code == "already-tracked" || errResponse.Code == "already-exists" {
|
||||
return nil
|
||||
if errResponse.Code == errorCodeAlreadyTracked ||
|
||||
errResponse.Code == errorCodeAlreadyExists {
|
||||
return &metadataError{
|
||||
code: errResponse.Code,
|
||||
msg: errResponse.Error,
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("status_code: %d\nresponse: %s", resp.StatusCode, b) //nolint: err113
|
||||
@@ -68,8 +88,9 @@ func postMetadata(ctx context.Context, url, hasuraSecret string, data any) error
|
||||
}
|
||||
|
||||
type TrackTable struct {
|
||||
Type string `json:"type"`
|
||||
Args PgTrackTableArgs `json:"args"`
|
||||
Type string `json:"type"`
|
||||
Args PgTrackTableArgs `json:"args"`
|
||||
IsEnum bool `json:"is_enum,omitempty"` //nolint: tagliatelle
|
||||
}
|
||||
|
||||
type Table struct {
|
||||
@@ -96,9 +117,29 @@ type Configuration struct {
|
||||
}
|
||||
|
||||
type PgTrackTableArgs struct {
|
||||
Source string `json:"source"`
|
||||
Table Table `json:"table"`
|
||||
Configuration Configuration `json:"configuration"`
|
||||
Source string `json:"source"`
|
||||
Table Table `json:"table"`
|
||||
Configuration Configuration `json:"configuration"`
|
||||
ObjectRelationships []ObjectRelationshipConfig `json:"object_relationships,omitempty"` //nolint: tagliatelle
|
||||
ArrayRelationships []ArrayRelationshipConfig `json:"array_relationships,omitempty"` //nolint: tagliatelle
|
||||
}
|
||||
|
||||
type ObjectRelationshipConfig struct {
|
||||
Name string `json:"name"`
|
||||
Using ObjectRelationshipConfigUsing `json:"using"`
|
||||
}
|
||||
|
||||
type ObjectRelationshipConfigUsing struct {
|
||||
ForeignKeyConstraintOn any `json:"foreign_key_constraint_on"` //nolint: tagliatelle
|
||||
}
|
||||
|
||||
type ArrayRelationshipConfig struct {
|
||||
Name string `json:"name"`
|
||||
Using ArrayRelationshipConfigUsing `json:"using"`
|
||||
}
|
||||
|
||||
type ArrayRelationshipConfigUsing struct {
|
||||
ForeignKeyConstraintOn ForeignKeyConstraintOn `json:"foreign_key_constraint_on"` //nolint: tagliatelle
|
||||
}
|
||||
|
||||
type CreateObjectRelationship struct {
|
||||
@@ -150,14 +191,126 @@ type DropRelationshipArgs struct {
|
||||
Relationship string `json:"relationship"`
|
||||
}
|
||||
|
||||
type SetTableCustomization struct {
|
||||
Type string `json:"type"`
|
||||
Args SetTableCustomizationArgs `json:"args"`
|
||||
}
|
||||
|
||||
type SetTableCustomizationArgs struct {
|
||||
Source string `json:"source"`
|
||||
Table Table `json:"table"`
|
||||
Configuration Configuration `json:"configuration"`
|
||||
}
|
||||
|
||||
func applyTableCustomization(
|
||||
ctx context.Context,
|
||||
url, hasuraSecret string,
|
||||
table TrackTable,
|
||||
) error {
|
||||
customization := SetTableCustomization{
|
||||
Type: "pg_set_table_customization",
|
||||
Args: SetTableCustomizationArgs{
|
||||
Source: table.Args.Source,
|
||||
Table: table.Args.Table,
|
||||
Configuration: table.Args.Configuration,
|
||||
},
|
||||
}
|
||||
|
||||
return postMetadata(ctx, url, hasuraSecret, customization)
|
||||
}
|
||||
|
||||
func applyObjectRelationships(
|
||||
ctx context.Context,
|
||||
url, hasuraSecret string,
|
||||
table TrackTable,
|
||||
) error {
|
||||
for _, rel := range table.Args.ObjectRelationships {
|
||||
relationship := CreateObjectRelationship{
|
||||
Type: "pg_create_object_relationship",
|
||||
Args: CreateObjectRelationshipArgs{
|
||||
Source: table.Args.Source,
|
||||
Table: table.Args.Table,
|
||||
Name: rel.Name,
|
||||
Using: CreateObjectRelationshipUsing{
|
||||
ForeignKeyConstraintOn: func() []string {
|
||||
// Handle both string and array cases
|
||||
switch v := rel.Using.ForeignKeyConstraintOn.(type) {
|
||||
case string:
|
||||
return []string{v}
|
||||
case []string:
|
||||
return v
|
||||
default:
|
||||
return []string{}
|
||||
}
|
||||
}(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := postMetadata(ctx, url, hasuraSecret, relationship); err != nil {
|
||||
var metaErr *metadataError
|
||||
if ok := errors.As(err, &metaErr); ok && metaErr.Code() == errorCodeAlreadyExists {
|
||||
continue // Skip if relationship already exists
|
||||
}
|
||||
|
||||
return fmt.Errorf(
|
||||
"problem creating object relationship %s for table %s.%s: %w",
|
||||
rel.Name,
|
||||
table.Args.Table.Schema,
|
||||
table.Args.Table.Name,
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyArrayRelationships(
|
||||
ctx context.Context,
|
||||
url, hasuraSecret string,
|
||||
table TrackTable,
|
||||
) error {
|
||||
for _, rel := range table.Args.ArrayRelationships {
|
||||
relationship := CreateArrayRelationship{
|
||||
Type: "pg_create_array_relationship",
|
||||
Args: CreateArrayRelationshipArgs{
|
||||
Source: table.Args.Source,
|
||||
Table: table.Args.Table,
|
||||
Name: rel.Name,
|
||||
Using: CreateArrayRelationshipUsing{
|
||||
ForeignKeyConstraintOn: rel.Using.ForeignKeyConstraintOn,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := postMetadata(ctx, url, hasuraSecret, relationship); err != nil {
|
||||
var metaErr *metadataError
|
||||
if ok := errors.As(err, &metaErr); ok && metaErr.Code() == errorCodeAlreadyExists {
|
||||
continue // Skip if relationship already exists
|
||||
}
|
||||
|
||||
return fmt.Errorf(
|
||||
"problem creating array relationship %s for table %s.%s: %w",
|
||||
rel.Name,
|
||||
table.Args.Table.Schema,
|
||||
table.Args.Table.Name,
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ApplyHasuraMetadata( //nolint: funlen,maintidx
|
||||
ctx context.Context,
|
||||
url, hasuraSecret string,
|
||||
) error {
|
||||
authTables := []TrackTable{
|
||||
{
|
||||
{ //nolint:exhaustruct
|
||||
Type: "pg_track_table",
|
||||
Args: PgTrackTableArgs{
|
||||
Args: PgTrackTableArgs{ //nolint:exhaustruct
|
||||
Source: hasuraDBName,
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
@@ -184,8 +337,9 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "pg_track_table",
|
||||
Args: PgTrackTableArgs{
|
||||
Type: "pg_track_table",
|
||||
IsEnum: true,
|
||||
Args: PgTrackTableArgs{ //nolint:exhaustruct
|
||||
Source: hasuraDBName,
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
@@ -209,11 +363,25 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
|
||||
"comment": "comment",
|
||||
},
|
||||
},
|
||||
ArrayRelationships: []ArrayRelationshipConfig{
|
||||
{
|
||||
Name: "refreshTokens",
|
||||
Using: ArrayRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
Name: "refresh_tokens",
|
||||
},
|
||||
Columns: []string{"type"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
{ //nolint:exhaustruct
|
||||
Type: "pg_track_table",
|
||||
Args: PgTrackTableArgs{
|
||||
Args: PgTrackTableArgs{ //nolint:exhaustruct
|
||||
Source: hasuraDBName,
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
@@ -239,11 +407,19 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
|
||||
"user_id": "userId",
|
||||
},
|
||||
},
|
||||
ObjectRelationships: []ObjectRelationshipConfig{
|
||||
{
|
||||
Name: "user",
|
||||
Using: ObjectRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: "user_id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
{ //nolint:exhaustruct
|
||||
Type: "pg_track_table",
|
||||
Args: PgTrackTableArgs{
|
||||
Args: PgTrackTableArgs{ //nolint:exhaustruct
|
||||
Source: hasuraDBName,
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
@@ -266,11 +442,37 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
|
||||
"role": "role",
|
||||
},
|
||||
},
|
||||
ArrayRelationships: []ArrayRelationshipConfig{
|
||||
{
|
||||
Name: "userRoles",
|
||||
Using: ArrayRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
Name: "user_roles",
|
||||
},
|
||||
Columns: []string{"role"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "usersByDefaultRole",
|
||||
Using: ArrayRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
Name: "users",
|
||||
},
|
||||
Columns: []string{"default_role"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
{ //nolint:exhaustruct
|
||||
Type: "pg_track_table",
|
||||
Args: PgTrackTableArgs{
|
||||
Args: PgTrackTableArgs{ //nolint:exhaustruct
|
||||
Source: hasuraDBName,
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
@@ -300,11 +502,25 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
|
||||
"provider_user_id": "providerUserId",
|
||||
},
|
||||
},
|
||||
ObjectRelationships: []ObjectRelationshipConfig{
|
||||
{
|
||||
Name: "user",
|
||||
Using: ObjectRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: "user_id",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "provider",
|
||||
Using: ObjectRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: "provider_id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
{ //nolint:exhaustruct
|
||||
Type: "pg_track_table",
|
||||
Args: PgTrackTableArgs{
|
||||
Args: PgTrackTableArgs{ //nolint:exhaustruct
|
||||
Source: hasuraDBName,
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
@@ -330,9 +546,23 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
|
||||
"role": "role",
|
||||
},
|
||||
},
|
||||
ObjectRelationships: []ObjectRelationshipConfig{
|
||||
{
|
||||
Name: "user",
|
||||
Using: ObjectRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: "user_id",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "roleByRole",
|
||||
Using: ObjectRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: "role",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
{ //nolint:exhaustruct
|
||||
Type: "pg_track_table",
|
||||
Args: PgTrackTableArgs{
|
||||
Source: hasuraDBName,
|
||||
@@ -380,11 +610,69 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
|
||||
"webauthn_current_challenge": "currentChallenge",
|
||||
},
|
||||
},
|
||||
ObjectRelationships: []ObjectRelationshipConfig{
|
||||
{
|
||||
Name: "defaultRoleByRole",
|
||||
Using: ObjectRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: "default_role",
|
||||
},
|
||||
},
|
||||
},
|
||||
ArrayRelationships: []ArrayRelationshipConfig{
|
||||
{
|
||||
Name: "userProviders",
|
||||
Using: ArrayRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
Name: "user_providers",
|
||||
},
|
||||
Columns: []string{"user_id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "roles",
|
||||
Using: ArrayRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
Name: "user_roles",
|
||||
},
|
||||
Columns: []string{"user_id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "refreshTokens",
|
||||
Using: ArrayRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
Name: "refresh_tokens",
|
||||
},
|
||||
Columns: []string{"user_id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "securityKeys",
|
||||
Using: ArrayRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
Name: "user_security_keys",
|
||||
},
|
||||
Columns: []string{"user_id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
{ //nolint:exhaustruct
|
||||
Type: "pg_track_table",
|
||||
Args: PgTrackTableArgs{
|
||||
Args: PgTrackTableArgs{ //nolint:exhaustruct
|
||||
Source: hasuraDBName,
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
@@ -407,11 +695,25 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
|
||||
"id": "id",
|
||||
},
|
||||
},
|
||||
ArrayRelationships: []ArrayRelationshipConfig{
|
||||
{
|
||||
Name: "userProviders",
|
||||
Using: ArrayRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
Name: "user_providers",
|
||||
},
|
||||
Columns: []string{"provider_id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
{ //nolint:exhaustruct
|
||||
Type: "pg_track_table",
|
||||
Args: PgTrackTableArgs{
|
||||
Args: PgTrackTableArgs{ //nolint:exhaustruct
|
||||
Source: hasuraDBName,
|
||||
Table: Table{
|
||||
Schema: "auth",
|
||||
@@ -437,15 +739,49 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
|
||||
"credential_public_key": "credentialPublicKey",
|
||||
},
|
||||
},
|
||||
ObjectRelationships: []ObjectRelationshipConfig{
|
||||
{
|
||||
Name: "user",
|
||||
Using: ObjectRelationshipConfigUsing{
|
||||
ForeignKeyConstraintOn: "user_id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Track each table (will skip if already tracked due to existing error handling)
|
||||
for _, table := range authTables {
|
||||
if err := postMetadata(ctx, url, hasuraSecret, table); err != nil {
|
||||
return fmt.Errorf("problem adding metadata for table %s.%s: %w",
|
||||
table.Args.Table.Schema, table.Args.Table.Name, err)
|
||||
err := postMetadata(ctx, url, hasuraSecret, table)
|
||||
if err != nil {
|
||||
var metaErr *metadataError
|
||||
if ok := errors.As(err, &metaErr); ok && metaErr.Code() == errorCodeAlreadyTracked {
|
||||
if err := applyTableCustomization(ctx, url, hasuraSecret, table); err != nil {
|
||||
return fmt.Errorf(
|
||||
"problem updating customization for table %s.%s: %w",
|
||||
table.Args.Table.Schema,
|
||||
table.Args.Table.Name,
|
||||
err,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf(
|
||||
"problem adding metadata for table %s.%s: %w",
|
||||
table.Args.Table.Schema,
|
||||
table.Args.Table.Name,
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, table := range authTables {
|
||||
if err := applyObjectRelationships(ctx, url, hasuraSecret, table); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := applyArrayRelationships(ctx, url, hasuraSecret, table); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -402,3 +402,7 @@ INSERT INTO auth.roles (role)
|
||||
SELECT unnest(@roles::TEXT[])
|
||||
ON CONFLICT (role) DO NOTHING
|
||||
RETURNING role;
|
||||
|
||||
-- name: GetUsersWithUnencryptedTOTPSecret :many
|
||||
SELECT * FROM auth.users
|
||||
WHERE LENGTH(totp_secret) < 64;
|
||||
|
||||
@@ -494,6 +494,57 @@ func (q *Queries) GetUserRoles(ctx context.Context, userID uuid.UUID) ([]AuthUse
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getUsersWithUnencryptedTOTPSecret = `-- name: GetUsersWithUnencryptedTOTPSecret :many
|
||||
SELECT id, created_at, updated_at, last_seen, disabled, display_name, avatar_url, locale, email, phone_number, password_hash, email_verified, phone_number_verified, new_email, otp_method_last_used, otp_hash, otp_hash_expires_at, default_role, is_anonymous, totp_secret, active_mfa_type, ticket, ticket_expires_at, metadata, webauthn_current_challenge FROM auth.users
|
||||
WHERE LENGTH(totp_secret) < 64
|
||||
`
|
||||
|
||||
func (q *Queries) GetUsersWithUnencryptedTOTPSecret(ctx context.Context) ([]AuthUser, error) {
|
||||
rows, err := q.db.Query(ctx, getUsersWithUnencryptedTOTPSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []AuthUser
|
||||
for rows.Next() {
|
||||
var i AuthUser
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastSeen,
|
||||
&i.Disabled,
|
||||
&i.DisplayName,
|
||||
&i.AvatarUrl,
|
||||
&i.Locale,
|
||||
&i.Email,
|
||||
&i.PhoneNumber,
|
||||
&i.PasswordHash,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneNumberVerified,
|
||||
&i.NewEmail,
|
||||
&i.OtpMethodLastUsed,
|
||||
&i.OtpHash,
|
||||
&i.OtpHashExpiresAt,
|
||||
&i.DefaultRole,
|
||||
&i.IsAnonymous,
|
||||
&i.TotpSecret,
|
||||
&i.ActiveMfaType,
|
||||
&i.Ticket,
|
||||
&i.TicketExpiresAt,
|
||||
&i.Metadata,
|
||||
&i.WebauthnCurrentChallenge,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const insertRefreshtoken = `-- name: InsertRefreshtoken :one
|
||||
INSERT INTO auth.refresh_tokens (user_id, refresh_token_hash, expires_at, type, metadata)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
|
||||
30
vendor/github.com/nhost/be/services/mimir/graph/config_validate.go
generated
vendored
30
vendor/github.com/nhost/be/services/mimir/graph/config_validate.go
generated
vendored
@@ -21,30 +21,6 @@ const (
|
||||
pitrMinVersion = 20250311
|
||||
)
|
||||
|
||||
func (r *mutationResolver) configValidateVerifyPersistentVolumeEncryption(
|
||||
desiredState int32,
|
||||
oldApp *App,
|
||||
newApp *App,
|
||||
) error {
|
||||
if oldApp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
oldEncrypted := deptr(oldApp.SystemConfig.GetPersistentVolumesEncrypted())
|
||||
newEncrypted := deptr(newApp.SystemConfig.GetPersistentVolumesEncrypted())
|
||||
|
||||
if oldEncrypted && !newEncrypted {
|
||||
return ErrPersVolEncryptionCantBeDis
|
||||
}
|
||||
|
||||
if !oldEncrypted && newEncrypted &&
|
||||
!slices.Contains([]int32{appPaused, appEmpty}, desiredState) {
|
||||
return ErrPersVolEncryptionCantBeChanged
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) configValidateVerifyPostgresVersionChange(
|
||||
desiredState int32,
|
||||
oldApp *App,
|
||||
@@ -135,12 +111,6 @@ func (r *mutationResolver) configValidate(
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.configValidateVerifyPersistentVolumeEncryption(
|
||||
desiredState, oldApp, newApp,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.verifyPostgresVersionForPitr(newApp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
232
vendor/github.com/nhost/be/services/mimir/graph/generated/generated.go
generated
vendored
232
vendor/github.com/nhost/be/services/mimir/graph/generated/generated.go
generated
vendored
@@ -688,11 +688,13 @@ type ComplexityRoot struct {
|
||||
}
|
||||
|
||||
ConfigSystemConfigPostgres struct {
|
||||
ConnectionString func(childComplexity int) int
|
||||
Database func(childComplexity int) int
|
||||
Disk func(childComplexity int) int
|
||||
Enabled func(childComplexity int) int
|
||||
MajorVersion func(childComplexity int) int
|
||||
ConnectionString func(childComplexity int) int
|
||||
Database func(childComplexity int) int
|
||||
Disk func(childComplexity int) int
|
||||
Enabled func(childComplexity int) int
|
||||
EncryptColumnKey func(childComplexity int) int
|
||||
MajorVersion func(childComplexity int) int
|
||||
OldEncryptColumnKey func(childComplexity int) int
|
||||
}
|
||||
|
||||
ConfigSystemConfigPostgresConnectionString struct {
|
||||
@@ -3208,6 +3210,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
|
||||
return e.complexity.ConfigSystemConfigPostgres.Enabled(childComplexity), true
|
||||
|
||||
case "ConfigSystemConfigPostgres.encryptColumnKey":
|
||||
if e.complexity.ConfigSystemConfigPostgres.EncryptColumnKey == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.ConfigSystemConfigPostgres.EncryptColumnKey(childComplexity), true
|
||||
|
||||
case "ConfigSystemConfigPostgres.majorVersion":
|
||||
if e.complexity.ConfigSystemConfigPostgres.MajorVersion == nil {
|
||||
break
|
||||
@@ -3215,6 +3224,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
|
||||
return e.complexity.ConfigSystemConfigPostgres.MajorVersion(childComplexity), true
|
||||
|
||||
case "ConfigSystemConfigPostgres.oldEncryptColumnKey":
|
||||
if e.complexity.ConfigSystemConfigPostgres.OldEncryptColumnKey == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.ConfigSystemConfigPostgres.OldEncryptColumnKey(childComplexity), true
|
||||
|
||||
case "ConfigSystemConfigPostgresConnectionString.auth":
|
||||
if e.complexity.ConfigSystemConfigPostgresConnectionString.Auth == nil {
|
||||
break
|
||||
@@ -8234,28 +8250,40 @@ type ConfigSystemConfigPostgres {
|
||||
connectionString: ConfigSystemConfigPostgresConnectionString!
|
||||
"""
|
||||
|
||||
"""
|
||||
disk: ConfigSystemConfigPostgresDisk
|
||||
"""
|
||||
|
||||
"""
|
||||
encryptColumnKey: String
|
||||
"""
|
||||
|
||||
"""
|
||||
database: String!
|
||||
"""
|
||||
|
||||
"""
|
||||
disk: ConfigSystemConfigPostgresDisk
|
||||
oldEncryptColumnKey: String
|
||||
}
|
||||
|
||||
input ConfigSystemConfigPostgresUpdateInput {
|
||||
enabled: Boolean
|
||||
majorVersion: String
|
||||
connectionString: ConfigSystemConfigPostgresConnectionStringUpdateInput
|
||||
database: String
|
||||
disk: ConfigSystemConfigPostgresDiskUpdateInput
|
||||
encryptColumnKey: String
|
||||
database: String
|
||||
oldEncryptColumnKey: String
|
||||
}
|
||||
|
||||
input ConfigSystemConfigPostgresInsertInput {
|
||||
enabled: Boolean
|
||||
majorVersion: String
|
||||
connectionString: ConfigSystemConfigPostgresConnectionStringInsertInput!
|
||||
database: String!
|
||||
disk: ConfigSystemConfigPostgresDiskInsertInput
|
||||
encryptColumnKey: String
|
||||
database: String!
|
||||
oldEncryptColumnKey: String
|
||||
}
|
||||
|
||||
input ConfigSystemConfigPostgresComparisonExp {
|
||||
@@ -8265,8 +8293,10 @@ input ConfigSystemConfigPostgresComparisonExp {
|
||||
enabled: ConfigBooleanComparisonExp
|
||||
majorVersion: ConfigStringComparisonExp
|
||||
connectionString: ConfigSystemConfigPostgresConnectionStringComparisonExp
|
||||
database: ConfigStringComparisonExp
|
||||
disk: ConfigSystemConfigPostgresDiskComparisonExp
|
||||
encryptColumnKey: ConfigStringComparisonExp
|
||||
database: ConfigStringComparisonExp
|
||||
oldEncryptColumnKey: ConfigStringComparisonExp
|
||||
}
|
||||
|
||||
"""
|
||||
@@ -25447,10 +25477,14 @@ func (ec *executionContext) fieldContext_ConfigSystemConfig_postgres(_ context.C
|
||||
return ec.fieldContext_ConfigSystemConfigPostgres_majorVersion(ctx, field)
|
||||
case "connectionString":
|
||||
return ec.fieldContext_ConfigSystemConfigPostgres_connectionString(ctx, field)
|
||||
case "database":
|
||||
return ec.fieldContext_ConfigSystemConfigPostgres_database(ctx, field)
|
||||
case "disk":
|
||||
return ec.fieldContext_ConfigSystemConfigPostgres_disk(ctx, field)
|
||||
case "encryptColumnKey":
|
||||
return ec.fieldContext_ConfigSystemConfigPostgres_encryptColumnKey(ctx, field)
|
||||
case "database":
|
||||
return ec.fieldContext_ConfigSystemConfigPostgres_database(ctx, field)
|
||||
case "oldEncryptColumnKey":
|
||||
return ec.fieldContext_ConfigSystemConfigPostgres_oldEncryptColumnKey(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type ConfigSystemConfigPostgres", field.Name)
|
||||
},
|
||||
@@ -25807,6 +25841,94 @@ func (ec *executionContext) fieldContext_ConfigSystemConfigPostgres_connectionSt
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _ConfigSystemConfigPostgres_disk(ctx context.Context, field graphql.CollectedField, obj *model.ConfigSystemConfigPostgres) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_ConfigSystemConfigPostgres_disk(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Disk, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*model.ConfigSystemConfigPostgresDisk)
|
||||
fc.Result = res
|
||||
return ec.marshalOConfigSystemConfigPostgresDisk2ᚖgithubᚗcomᚋnhostᚋbeᚋservicesᚋmimirᚋmodelᚐConfigSystemConfigPostgresDisk(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_ConfigSystemConfigPostgres_disk(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "ConfigSystemConfigPostgres",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "iops":
|
||||
return ec.fieldContext_ConfigSystemConfigPostgresDisk_iops(ctx, field)
|
||||
case "tput":
|
||||
return ec.fieldContext_ConfigSystemConfigPostgresDisk_tput(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type ConfigSystemConfigPostgresDisk", field.Name)
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _ConfigSystemConfigPostgres_encryptColumnKey(ctx context.Context, field graphql.CollectedField, obj *model.ConfigSystemConfigPostgres) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_ConfigSystemConfigPostgres_encryptColumnKey(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.EncryptColumnKey, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*string)
|
||||
fc.Result = res
|
||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_ConfigSystemConfigPostgres_encryptColumnKey(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "ConfigSystemConfigPostgres",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type String does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _ConfigSystemConfigPostgres_database(ctx context.Context, field graphql.CollectedField, obj *model.ConfigSystemConfigPostgres) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_ConfigSystemConfigPostgres_database(ctx, field)
|
||||
if err != nil {
|
||||
@@ -25851,8 +25973,8 @@ func (ec *executionContext) fieldContext_ConfigSystemConfigPostgres_database(_ c
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _ConfigSystemConfigPostgres_disk(ctx context.Context, field graphql.CollectedField, obj *model.ConfigSystemConfigPostgres) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_ConfigSystemConfigPostgres_disk(ctx, field)
|
||||
func (ec *executionContext) _ConfigSystemConfigPostgres_oldEncryptColumnKey(ctx context.Context, field graphql.CollectedField, obj *model.ConfigSystemConfigPostgres) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_ConfigSystemConfigPostgres_oldEncryptColumnKey(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
@@ -25865,7 +25987,7 @@ func (ec *executionContext) _ConfigSystemConfigPostgres_disk(ctx context.Context
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Disk, nil
|
||||
return obj.OldEncryptColumnKey, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
@@ -25874,25 +25996,19 @@ func (ec *executionContext) _ConfigSystemConfigPostgres_disk(ctx context.Context
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*model.ConfigSystemConfigPostgresDisk)
|
||||
res := resTmp.(*string)
|
||||
fc.Result = res
|
||||
return ec.marshalOConfigSystemConfigPostgresDisk2ᚖgithubᚗcomᚋnhostᚋbeᚋservicesᚋmimirᚋmodelᚐConfigSystemConfigPostgresDisk(ctx, field.Selections, res)
|
||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_ConfigSystemConfigPostgres_disk(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
func (ec *executionContext) fieldContext_ConfigSystemConfigPostgres_oldEncryptColumnKey(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "ConfigSystemConfigPostgres",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "iops":
|
||||
return ec.fieldContext_ConfigSystemConfigPostgresDisk_iops(ctx, field)
|
||||
case "tput":
|
||||
return ec.fieldContext_ConfigSystemConfigPostgresDisk_tput(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type ConfigSystemConfigPostgresDisk", field.Name)
|
||||
return nil, errors.New("field of type String does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
@@ -41018,7 +41134,7 @@ func (ec *executionContext) unmarshalInputConfigSystemConfigPostgresComparisonEx
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"_and", "_not", "_or", "enabled", "majorVersion", "connectionString", "database", "disk"}
|
||||
fieldsInOrder := [...]string{"_and", "_not", "_or", "enabled", "majorVersion", "connectionString", "disk", "encryptColumnKey", "database", "oldEncryptColumnKey"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
@@ -41067,13 +41183,6 @@ func (ec *executionContext) unmarshalInputConfigSystemConfigPostgresComparisonEx
|
||||
return it, err
|
||||
}
|
||||
it.ConnectionString = data
|
||||
case "database":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("database"))
|
||||
data, err := ec.unmarshalOConfigStringComparisonExp2ᚖgithubᚗcomᚋnhostᚋbeᚋservicesᚋmimirᚋmodelᚐGenericComparisonExp(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Database = data
|
||||
case "disk":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("disk"))
|
||||
data, err := ec.unmarshalOConfigSystemConfigPostgresDiskComparisonExp2ᚖgithubᚗcomᚋnhostᚋbeᚋservicesᚋmimirᚋmodelᚐConfigSystemConfigPostgresDiskComparisonExp(ctx, v)
|
||||
@@ -41081,6 +41190,27 @@ func (ec *executionContext) unmarshalInputConfigSystemConfigPostgresComparisonEx
|
||||
return it, err
|
||||
}
|
||||
it.Disk = data
|
||||
case "encryptColumnKey":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("encryptColumnKey"))
|
||||
data, err := ec.unmarshalOConfigStringComparisonExp2ᚖgithubᚗcomᚋnhostᚋbeᚋservicesᚋmimirᚋmodelᚐGenericComparisonExp(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.EncryptColumnKey = data
|
||||
case "database":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("database"))
|
||||
data, err := ec.unmarshalOConfigStringComparisonExp2ᚖgithubᚗcomᚋnhostᚋbeᚋservicesᚋmimirᚋmodelᚐGenericComparisonExp(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Database = data
|
||||
case "oldEncryptColumnKey":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("oldEncryptColumnKey"))
|
||||
data, err := ec.unmarshalOConfigStringComparisonExp2ᚖgithubᚗcomᚋnhostᚋbeᚋservicesᚋmimirᚋmodelᚐGenericComparisonExp(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.OldEncryptColumnKey = data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41300,7 +41430,7 @@ func (ec *executionContext) unmarshalInputConfigSystemConfigPostgresInsertInput(
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"enabled", "majorVersion", "connectionString", "database", "disk"}
|
||||
fieldsInOrder := [...]string{"enabled", "majorVersion", "connectionString", "disk", "encryptColumnKey", "database", "oldEncryptColumnKey"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
@@ -41328,13 +41458,6 @@ func (ec *executionContext) unmarshalInputConfigSystemConfigPostgresInsertInput(
|
||||
return it, err
|
||||
}
|
||||
it.ConnectionString = data
|
||||
case "database":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("database"))
|
||||
data, err := ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Database = data
|
||||
case "disk":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("disk"))
|
||||
data, err := ec.unmarshalOConfigSystemConfigPostgresDiskInsertInput2ᚖgithubᚗcomᚋnhostᚋbeᚋservicesᚋmimirᚋmodelᚐConfigSystemConfigPostgresDiskInsertInput(ctx, v)
|
||||
@@ -41342,6 +41465,27 @@ func (ec *executionContext) unmarshalInputConfigSystemConfigPostgresInsertInput(
|
||||
return it, err
|
||||
}
|
||||
it.Disk = data
|
||||
case "encryptColumnKey":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("encryptColumnKey"))
|
||||
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.EncryptColumnKey = data
|
||||
case "database":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("database"))
|
||||
data, err := ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Database = data
|
||||
case "oldEncryptColumnKey":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("oldEncryptColumnKey"))
|
||||
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.OldEncryptColumnKey = data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46042,13 +46186,17 @@ func (ec *executionContext) _ConfigSystemConfigPostgres(ctx context.Context, sel
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
case "disk":
|
||||
out.Values[i] = ec._ConfigSystemConfigPostgres_disk(ctx, field, obj)
|
||||
case "encryptColumnKey":
|
||||
out.Values[i] = ec._ConfigSystemConfigPostgres_encryptColumnKey(ctx, field, obj)
|
||||
case "database":
|
||||
out.Values[i] = ec._ConfigSystemConfigPostgres_database(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
case "disk":
|
||||
out.Values[i] = ec._ConfigSystemConfigPostgres_disk(ctx, field, obj)
|
||||
case "oldEncryptColumnKey":
|
||||
out.Values[i] = ec._ConfigSystemConfigPostgres_oldEncryptColumnKey(ctx, field, obj)
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
|
||||
214
vendor/github.com/nhost/be/services/mimir/model/cuegraph_gen.go
generated
vendored
214
vendor/github.com/nhost/be/services/mimir/model/cuegraph_gen.go
generated
vendored
@@ -27181,9 +27181,13 @@ type ConfigSystemConfigPostgres struct {
|
||||
|
||||
ConnectionString *ConfigSystemConfigPostgresConnectionString `json:"connectionString,omitempty" toml:"connectionString,omitempty"`
|
||||
|
||||
Disk *ConfigSystemConfigPostgresDisk `json:"disk,omitempty" toml:"disk,omitempty"`
|
||||
|
||||
EncryptColumnKey *string `json:"encryptColumnKey" toml:"encryptColumnKey"`
|
||||
|
||||
Database string `json:"database" toml:"database"`
|
||||
|
||||
Disk *ConfigSystemConfigPostgresDisk `json:"disk,omitempty" toml:"disk,omitempty"`
|
||||
OldEncryptColumnKey *string `json:"oldEncryptColumnKey" toml:"oldEncryptColumnKey"`
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgres) MarshalJSON() ([]byte, error) {
|
||||
@@ -27197,10 +27201,16 @@ func (o *ConfigSystemConfigPostgres) MarshalJSON() ([]byte, error) {
|
||||
if o.ConnectionString != nil {
|
||||
m["connectionString"] = o.ConnectionString
|
||||
}
|
||||
m["database"] = o.Database
|
||||
if o.Disk != nil {
|
||||
m["disk"] = o.Disk
|
||||
}
|
||||
if o.EncryptColumnKey != nil {
|
||||
m["encryptColumnKey"] = o.EncryptColumnKey
|
||||
}
|
||||
m["database"] = o.Database
|
||||
if o.OldEncryptColumnKey != nil {
|
||||
m["oldEncryptColumnKey"] = o.OldEncryptColumnKey
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
@@ -27225,13 +27235,6 @@ func (o *ConfigSystemConfigPostgres) GetConnectionString() *ConfigSystemConfigPo
|
||||
return o.ConnectionString
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgres) GetDatabase() string {
|
||||
if o == nil {
|
||||
o = &ConfigSystemConfigPostgres{}
|
||||
}
|
||||
return o.Database
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgres) GetDisk() *ConfigSystemConfigPostgresDisk {
|
||||
if o == nil {
|
||||
return nil
|
||||
@@ -27239,17 +27242,42 @@ func (o *ConfigSystemConfigPostgres) GetDisk() *ConfigSystemConfigPostgresDisk {
|
||||
return o.Disk
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgres) GetEncryptColumnKey() *string {
|
||||
if o == nil {
|
||||
o = &ConfigSystemConfigPostgres{}
|
||||
}
|
||||
return o.EncryptColumnKey
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgres) GetDatabase() string {
|
||||
if o == nil {
|
||||
o = &ConfigSystemConfigPostgres{}
|
||||
}
|
||||
return o.Database
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgres) GetOldEncryptColumnKey() *string {
|
||||
if o == nil {
|
||||
o = &ConfigSystemConfigPostgres{}
|
||||
}
|
||||
return o.OldEncryptColumnKey
|
||||
}
|
||||
|
||||
type ConfigSystemConfigPostgresUpdateInput struct {
|
||||
Enabled *bool `json:"enabled,omitempty" toml:"enabled,omitempty"`
|
||||
IsSetEnabled bool `json:"-"`
|
||||
MajorVersion *string `json:"majorVersion,omitempty" toml:"majorVersion,omitempty"`
|
||||
IsSetMajorVersion bool `json:"-"`
|
||||
ConnectionString *ConfigSystemConfigPostgresConnectionStringUpdateInput `json:"connectionString,omitempty" toml:"connectionString,omitempty"`
|
||||
IsSetConnectionString bool `json:"-"`
|
||||
Database *string `json:"database,omitempty" toml:"database,omitempty"`
|
||||
IsSetDatabase bool `json:"-"`
|
||||
Disk *ConfigSystemConfigPostgresDiskUpdateInput `json:"disk,omitempty" toml:"disk,omitempty"`
|
||||
IsSetDisk bool `json:"-"`
|
||||
Enabled *bool `json:"enabled,omitempty" toml:"enabled,omitempty"`
|
||||
IsSetEnabled bool `json:"-"`
|
||||
MajorVersion *string `json:"majorVersion,omitempty" toml:"majorVersion,omitempty"`
|
||||
IsSetMajorVersion bool `json:"-"`
|
||||
ConnectionString *ConfigSystemConfigPostgresConnectionStringUpdateInput `json:"connectionString,omitempty" toml:"connectionString,omitempty"`
|
||||
IsSetConnectionString bool `json:"-"`
|
||||
Disk *ConfigSystemConfigPostgresDiskUpdateInput `json:"disk,omitempty" toml:"disk,omitempty"`
|
||||
IsSetDisk bool `json:"-"`
|
||||
EncryptColumnKey *string `json:"encryptColumnKey,omitempty" toml:"encryptColumnKey,omitempty"`
|
||||
IsSetEncryptColumnKey bool `json:"-"`
|
||||
Database *string `json:"database,omitempty" toml:"database,omitempty"`
|
||||
IsSetDatabase bool `json:"-"`
|
||||
OldEncryptColumnKey *string `json:"oldEncryptColumnKey,omitempty" toml:"oldEncryptColumnKey,omitempty"`
|
||||
IsSetOldEncryptColumnKey bool `json:"-"`
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgresUpdateInput) UnmarshalGQL(v interface{}) error {
|
||||
@@ -27301,6 +27329,33 @@ func (o *ConfigSystemConfigPostgresUpdateInput) UnmarshalGQL(v interface{}) erro
|
||||
}
|
||||
o.IsSetConnectionString = true
|
||||
}
|
||||
if x, ok := m["disk"]; ok {
|
||||
if x != nil {
|
||||
t := &ConfigSystemConfigPostgresDiskUpdateInput{}
|
||||
if err := t.UnmarshalGQL(x); err != nil {
|
||||
return err
|
||||
}
|
||||
o.Disk = t
|
||||
}
|
||||
o.IsSetDisk = true
|
||||
}
|
||||
if v, ok := m["encryptColumnKey"]; ok {
|
||||
if v == nil {
|
||||
o.EncryptColumnKey = nil
|
||||
} else {
|
||||
// clearly a not very efficient shortcut
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var x string
|
||||
if err := json.Unmarshal(b, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
o.EncryptColumnKey = &x
|
||||
}
|
||||
o.IsSetEncryptColumnKey = true
|
||||
}
|
||||
if v, ok := m["database"]; ok {
|
||||
if v == nil {
|
||||
o.Database = nil
|
||||
@@ -27318,15 +27373,22 @@ func (o *ConfigSystemConfigPostgresUpdateInput) UnmarshalGQL(v interface{}) erro
|
||||
}
|
||||
o.IsSetDatabase = true
|
||||
}
|
||||
if x, ok := m["disk"]; ok {
|
||||
if x != nil {
|
||||
t := &ConfigSystemConfigPostgresDiskUpdateInput{}
|
||||
if err := t.UnmarshalGQL(x); err != nil {
|
||||
if v, ok := m["oldEncryptColumnKey"]; ok {
|
||||
if v == nil {
|
||||
o.OldEncryptColumnKey = nil
|
||||
} else {
|
||||
// clearly a not very efficient shortcut
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Disk = t
|
||||
var x string
|
||||
if err := json.Unmarshal(b, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
o.OldEncryptColumnKey = &x
|
||||
}
|
||||
o.IsSetDisk = true
|
||||
o.IsSetOldEncryptColumnKey = true
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -27360,6 +27422,20 @@ func (o *ConfigSystemConfigPostgresUpdateInput) GetConnectionString() *ConfigSys
|
||||
return o.ConnectionString
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgresUpdateInput) GetDisk() *ConfigSystemConfigPostgresDiskUpdateInput {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
return o.Disk
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgresUpdateInput) GetEncryptColumnKey() *string {
|
||||
if o == nil {
|
||||
o = &ConfigSystemConfigPostgresUpdateInput{}
|
||||
}
|
||||
return o.EncryptColumnKey
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgresUpdateInput) GetDatabase() *string {
|
||||
if o == nil {
|
||||
o = &ConfigSystemConfigPostgresUpdateInput{}
|
||||
@@ -27367,11 +27443,11 @@ func (o *ConfigSystemConfigPostgresUpdateInput) GetDatabase() *string {
|
||||
return o.Database
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgresUpdateInput) GetDisk() *ConfigSystemConfigPostgresDiskUpdateInput {
|
||||
func (o *ConfigSystemConfigPostgresUpdateInput) GetOldEncryptColumnKey() *string {
|
||||
if o == nil {
|
||||
return nil
|
||||
o = &ConfigSystemConfigPostgresUpdateInput{}
|
||||
}
|
||||
return o.Disk
|
||||
return o.OldEncryptColumnKey
|
||||
}
|
||||
|
||||
func (s *ConfigSystemConfigPostgres) Update(v *ConfigSystemConfigPostgresUpdateInput) {
|
||||
@@ -27394,11 +27470,6 @@ func (s *ConfigSystemConfigPostgres) Update(v *ConfigSystemConfigPostgresUpdateI
|
||||
s.ConnectionString.Update(v.ConnectionString)
|
||||
}
|
||||
}
|
||||
if v.IsSetDatabase || v.Database != nil {
|
||||
if v.Database != nil {
|
||||
s.Database = *v.Database
|
||||
}
|
||||
}
|
||||
if v.IsSetDisk || v.Disk != nil {
|
||||
if v.Disk == nil {
|
||||
s.Disk = nil
|
||||
@@ -27409,14 +27480,27 @@ func (s *ConfigSystemConfigPostgres) Update(v *ConfigSystemConfigPostgresUpdateI
|
||||
s.Disk.Update(v.Disk)
|
||||
}
|
||||
}
|
||||
if v.IsSetEncryptColumnKey || v.EncryptColumnKey != nil {
|
||||
s.EncryptColumnKey = v.EncryptColumnKey
|
||||
}
|
||||
if v.IsSetDatabase || v.Database != nil {
|
||||
if v.Database != nil {
|
||||
s.Database = *v.Database
|
||||
}
|
||||
}
|
||||
if v.IsSetOldEncryptColumnKey || v.OldEncryptColumnKey != nil {
|
||||
s.OldEncryptColumnKey = v.OldEncryptColumnKey
|
||||
}
|
||||
}
|
||||
|
||||
type ConfigSystemConfigPostgresInsertInput struct {
|
||||
Enabled *bool `json:"enabled,omitempty" toml:"enabled,omitempty"`
|
||||
MajorVersion *string `json:"majorVersion,omitempty" toml:"majorVersion,omitempty"`
|
||||
ConnectionString *ConfigSystemConfigPostgresConnectionStringInsertInput `json:"connectionString,omitempty" toml:"connectionString,omitempty"`
|
||||
Database string `json:"database,omitempty" toml:"database,omitempty"`
|
||||
Disk *ConfigSystemConfigPostgresDiskInsertInput `json:"disk,omitempty" toml:"disk,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty" toml:"enabled,omitempty"`
|
||||
MajorVersion *string `json:"majorVersion,omitempty" toml:"majorVersion,omitempty"`
|
||||
ConnectionString *ConfigSystemConfigPostgresConnectionStringInsertInput `json:"connectionString,omitempty" toml:"connectionString,omitempty"`
|
||||
Disk *ConfigSystemConfigPostgresDiskInsertInput `json:"disk,omitempty" toml:"disk,omitempty"`
|
||||
EncryptColumnKey *string `json:"encryptColumnKey,omitempty" toml:"encryptColumnKey,omitempty"`
|
||||
Database string `json:"database,omitempty" toml:"database,omitempty"`
|
||||
OldEncryptColumnKey *string `json:"oldEncryptColumnKey,omitempty" toml:"oldEncryptColumnKey,omitempty"`
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgresInsertInput) GetEnabled() *bool {
|
||||
@@ -27440,6 +27524,20 @@ func (o *ConfigSystemConfigPostgresInsertInput) GetConnectionString() *ConfigSys
|
||||
return o.ConnectionString
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgresInsertInput) GetDisk() *ConfigSystemConfigPostgresDiskInsertInput {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
return o.Disk
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgresInsertInput) GetEncryptColumnKey() *string {
|
||||
if o == nil {
|
||||
o = &ConfigSystemConfigPostgresInsertInput{}
|
||||
}
|
||||
return o.EncryptColumnKey
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgresInsertInput) GetDatabase() string {
|
||||
if o == nil {
|
||||
o = &ConfigSystemConfigPostgresInsertInput{}
|
||||
@@ -27447,11 +27545,11 @@ func (o *ConfigSystemConfigPostgresInsertInput) GetDatabase() string {
|
||||
return o.Database
|
||||
}
|
||||
|
||||
func (o *ConfigSystemConfigPostgresInsertInput) GetDisk() *ConfigSystemConfigPostgresDiskInsertInput {
|
||||
func (o *ConfigSystemConfigPostgresInsertInput) GetOldEncryptColumnKey() *string {
|
||||
if o == nil {
|
||||
return nil
|
||||
o = &ConfigSystemConfigPostgresInsertInput{}
|
||||
}
|
||||
return o.Disk
|
||||
return o.OldEncryptColumnKey
|
||||
}
|
||||
|
||||
func (s *ConfigSystemConfigPostgres) Insert(v *ConfigSystemConfigPostgresInsertInput) {
|
||||
@@ -27463,13 +27561,15 @@ func (s *ConfigSystemConfigPostgres) Insert(v *ConfigSystemConfigPostgresInsertI
|
||||
}
|
||||
s.ConnectionString.Insert(v.ConnectionString)
|
||||
}
|
||||
s.Database = v.Database
|
||||
if v.Disk != nil {
|
||||
if s.Disk == nil {
|
||||
s.Disk = &ConfigSystemConfigPostgresDisk{}
|
||||
}
|
||||
s.Disk.Insert(v.Disk)
|
||||
}
|
||||
s.EncryptColumnKey = v.EncryptColumnKey
|
||||
s.Database = v.Database
|
||||
s.OldEncryptColumnKey = v.OldEncryptColumnKey
|
||||
}
|
||||
|
||||
func (s *ConfigSystemConfigPostgres) Clone() *ConfigSystemConfigPostgres {
|
||||
@@ -27481,20 +27581,24 @@ func (s *ConfigSystemConfigPostgres) Clone() *ConfigSystemConfigPostgres {
|
||||
v.Enabled = s.Enabled
|
||||
v.MajorVersion = s.MajorVersion
|
||||
v.ConnectionString = s.ConnectionString.Clone()
|
||||
v.Database = s.Database
|
||||
v.Disk = s.Disk.Clone()
|
||||
v.EncryptColumnKey = s.EncryptColumnKey
|
||||
v.Database = s.Database
|
||||
v.OldEncryptColumnKey = s.OldEncryptColumnKey
|
||||
return v
|
||||
}
|
||||
|
||||
type ConfigSystemConfigPostgresComparisonExp struct {
|
||||
And []*ConfigSystemConfigPostgresComparisonExp `json:"_and,omitempty"`
|
||||
Not *ConfigSystemConfigPostgresComparisonExp `json:"_not,omitempty"`
|
||||
Or []*ConfigSystemConfigPostgresComparisonExp `json:"_or,omitempty"`
|
||||
Enabled *ConfigBooleanComparisonExp `json:"enabled,omitempty"`
|
||||
MajorVersion *ConfigStringComparisonExp `json:"majorVersion,omitempty"`
|
||||
ConnectionString *ConfigSystemConfigPostgresConnectionStringComparisonExp `json:"connectionString,omitempty"`
|
||||
Database *ConfigStringComparisonExp `json:"database,omitempty"`
|
||||
Disk *ConfigSystemConfigPostgresDiskComparisonExp `json:"disk,omitempty"`
|
||||
And []*ConfigSystemConfigPostgresComparisonExp `json:"_and,omitempty"`
|
||||
Not *ConfigSystemConfigPostgresComparisonExp `json:"_not,omitempty"`
|
||||
Or []*ConfigSystemConfigPostgresComparisonExp `json:"_or,omitempty"`
|
||||
Enabled *ConfigBooleanComparisonExp `json:"enabled,omitempty"`
|
||||
MajorVersion *ConfigStringComparisonExp `json:"majorVersion,omitempty"`
|
||||
ConnectionString *ConfigSystemConfigPostgresConnectionStringComparisonExp `json:"connectionString,omitempty"`
|
||||
Disk *ConfigSystemConfigPostgresDiskComparisonExp `json:"disk,omitempty"`
|
||||
EncryptColumnKey *ConfigStringComparisonExp `json:"encryptColumnKey,omitempty"`
|
||||
Database *ConfigStringComparisonExp `json:"database,omitempty"`
|
||||
OldEncryptColumnKey *ConfigStringComparisonExp `json:"oldEncryptColumnKey,omitempty"`
|
||||
}
|
||||
|
||||
func (exp *ConfigSystemConfigPostgresComparisonExp) Matches(o *ConfigSystemConfigPostgres) bool {
|
||||
@@ -27517,10 +27621,16 @@ func (exp *ConfigSystemConfigPostgresComparisonExp) Matches(o *ConfigSystemConfi
|
||||
if !exp.ConnectionString.Matches(o.ConnectionString) {
|
||||
return false
|
||||
}
|
||||
if !exp.Disk.Matches(o.Disk) {
|
||||
return false
|
||||
}
|
||||
if o.EncryptColumnKey != nil && !exp.EncryptColumnKey.Matches(*o.EncryptColumnKey) {
|
||||
return false
|
||||
}
|
||||
if !exp.Database.Matches(o.Database) {
|
||||
return false
|
||||
}
|
||||
if !exp.Disk.Matches(o.Disk) {
|
||||
if o.OldEncryptColumnKey != nil && !exp.OldEncryptColumnKey.Matches(*o.OldEncryptColumnKey) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
8
vendor/github.com/nhost/be/services/mimir/schema/appconfig/hasura_auth.go
generated
vendored
8
vendor/github.com/nhost/be/services/mimir/schema/appconfig/hasura_auth.go
generated
vendored
@@ -14,6 +14,7 @@ const (
|
||||
secretHasuraAuthHasuraAdminSecret = "adminSecret"
|
||||
secretHasuraAuthJWTSecret = "jwtSecret"
|
||||
secretHasuraAuthSMTPPassword = "smtpPassword"
|
||||
secretHasuraAuthEncryptionKey = "encryptionKey"
|
||||
)
|
||||
|
||||
type oauthsettings interface {
|
||||
@@ -85,6 +86,7 @@ func HasuraAuthEnv( //nolint:funlen,cyclop,maintidx,gocyclo,gocognit
|
||||
isCustomSMTP bool,
|
||||
autoScalerEnabled bool,
|
||||
appID string,
|
||||
encryptionKey string,
|
||||
) ([]EnvVar, error) {
|
||||
customClaims := make(
|
||||
map[string]string,
|
||||
@@ -162,6 +164,12 @@ func HasuraAuthEnv( //nolint:funlen,cyclop,maintidx,gocyclo,gocognit
|
||||
IsSecret: false,
|
||||
SecretName: "",
|
||||
},
|
||||
{
|
||||
Name: "AUTH_ENCRYPTION_KEY",
|
||||
SecretName: secretHasuraAuthEncryptionKey,
|
||||
Value: encryptionKey,
|
||||
IsSecret: true,
|
||||
},
|
||||
{
|
||||
Name: "AUTH_ACCESS_TOKEN_EXPIRES_IN",
|
||||
Value: Stringify(
|
||||
|
||||
9
vendor/github.com/nhost/be/services/mimir/schema/schema.cue
generated
vendored
9
vendor/github.com/nhost/be/services/mimir/schema/schema.cue
generated
vendored
@@ -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
|
||||
|
||||
22
vendor/github.com/nhost/be/services/mimir/schema/schema_gen.graphqls
generated
vendored
22
vendor/github.com/nhost/be/services/mimir/schema/schema_gen.graphqls
generated
vendored
@@ -4193,28 +4193,40 @@ type ConfigSystemConfigPostgres {
|
||||
connectionString: ConfigSystemConfigPostgresConnectionString!
|
||||
"""
|
||||
|
||||
"""
|
||||
disk: ConfigSystemConfigPostgresDisk
|
||||
"""
|
||||
|
||||
"""
|
||||
encryptColumnKey: String
|
||||
"""
|
||||
|
||||
"""
|
||||
database: String!
|
||||
"""
|
||||
|
||||
"""
|
||||
disk: ConfigSystemConfigPostgresDisk
|
||||
oldEncryptColumnKey: String
|
||||
}
|
||||
|
||||
input ConfigSystemConfigPostgresUpdateInput {
|
||||
enabled: Boolean
|
||||
majorVersion: String
|
||||
connectionString: ConfigSystemConfigPostgresConnectionStringUpdateInput
|
||||
database: String
|
||||
disk: ConfigSystemConfigPostgresDiskUpdateInput
|
||||
encryptColumnKey: String
|
||||
database: String
|
||||
oldEncryptColumnKey: String
|
||||
}
|
||||
|
||||
input ConfigSystemConfigPostgresInsertInput {
|
||||
enabled: Boolean
|
||||
majorVersion: String
|
||||
connectionString: ConfigSystemConfigPostgresConnectionStringInsertInput!
|
||||
database: String!
|
||||
disk: ConfigSystemConfigPostgresDiskInsertInput
|
||||
encryptColumnKey: String
|
||||
database: String!
|
||||
oldEncryptColumnKey: String
|
||||
}
|
||||
|
||||
input ConfigSystemConfigPostgresComparisonExp {
|
||||
@@ -4224,8 +4236,10 @@ input ConfigSystemConfigPostgresComparisonExp {
|
||||
enabled: ConfigBooleanComparisonExp
|
||||
majorVersion: ConfigStringComparisonExp
|
||||
connectionString: ConfigSystemConfigPostgresConnectionStringComparisonExp
|
||||
database: ConfigStringComparisonExp
|
||||
disk: ConfigSystemConfigPostgresDiskComparisonExp
|
||||
encryptColumnKey: ConfigStringComparisonExp
|
||||
database: ConfigStringComparisonExp
|
||||
oldEncryptColumnKey: ConfigStringComparisonExp
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -719,7 +719,7 @@ github.com/muesli/termenv
|
||||
# github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
|
||||
## explicit
|
||||
github.com/munnerz/goautoneg
|
||||
# github.com/nhost/be v0.0.0-20250929153845-6db3e5249d33
|
||||
# github.com/nhost/be v0.0.0-20251021065906-8abc7d8dfa48
|
||||
## explicit; go 1.24.2
|
||||
github.com/nhost/be/lib/graphql
|
||||
github.com/nhost/be/lib/graphql/context
|
||||
|
||||
Reference in New Issue
Block a user