diff --git a/go.mod b/go.mod index 076ee3333..d1c28a08d 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/golang-jwt/jwt/v5 v5.3.1 github.com/google/go-github/v62 v62.0.0 github.com/google/go-querystring v1.2.0 + github.com/google/jsonschema-go v0.4.2 github.com/google/uuid v1.6.0 github.com/h2non/gock v1.2.0 github.com/jackc/pgconn v1.14.3 diff --git a/go.sum b/go.sum index 35bb7a8b2..30a8f520b 100644 --- a/go.sum +++ b/go.sum @@ -499,6 +499,8 @@ github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= +github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= diff --git a/internal/functions/serve/serve.go b/internal/functions/serve/serve.go index b0db41ffd..730e626da 100644 --- a/internal/functions/serve/serve.go +++ b/internal/functions/serve/serve.go @@ -241,6 +241,13 @@ func parseEnvFile(envFilePath string, fsys afero.Fs) ([]string, error) { return env, err } +type dockerFunction struct { + VerifyJWT bool `json:"verifyJWT"` + EntrypointPath string `json:"entrypointPath,omitempty"` + ImportMapPath string `json:"importMapPath,omitempty"` + StaticFiles []string `json:"staticFiles,omitempty"` +} + func PopulatePerFunctionConfigs(cwd, importMapPath string, noVerifyJWT *bool, fsys afero.Fs) ([]string, string, error) { slugs, err := deploy.GetFunctionSlugs(fsys) if err != nil { @@ -251,10 +258,10 @@ func PopulatePerFunctionConfigs(cwd, importMapPath string, noVerifyJWT *bool, fs return nil, "", err } binds := []string{} + enabledFunctions := map[string]dockerFunction{} for slug, fc := range functionsConfig { if !fc.Enabled { fmt.Fprintln(os.Stderr, "Skipped serving Function:", slug) - delete(functionsConfig, slug) continue } modules, err := deploy.GetBindMounts(cwd, utils.FunctionsDir, "", fc.Entrypoint, fc.ImportMap, fsys) @@ -262,14 +269,18 @@ func PopulatePerFunctionConfigs(cwd, importMapPath string, noVerifyJWT *bool, fs return nil, "", err } binds = append(binds, modules...) - fc.ImportMap = utils.ToDockerPath(fc.ImportMap) - fc.Entrypoint = utils.ToDockerPath(fc.Entrypoint) - functionsConfig[slug] = fc + enabled := dockerFunction{ + VerifyJWT: fc.VerifyJWT, + EntrypointPath: utils.ToDockerPath(fc.Entrypoint), + ImportMapPath: utils.ToDockerPath(fc.ImportMap), + StaticFiles: make([]string, len(fc.StaticFiles)), + } for i, val := range fc.StaticFiles { - fc.StaticFiles[i] = utils.ToDockerPath(val) + enabled.StaticFiles[i] = utils.ToDockerPath(val) } + enabledFunctions[slug] = enabled } - functionsConfigBytes, err := json.Marshal(functionsConfig) + functionsConfigBytes, err := json.Marshal(enabledFunctions) if err != nil { return nil, "", errors.Errorf("failed to marshal config json: %w", err) } diff --git a/internal/functions/serve/templates/main.ts b/internal/functions/serve/templates/main.ts index f6676c163..60ea51271 100644 --- a/internal/functions/serve/templates/main.ts +++ b/internal/functions/serve/templates/main.ts @@ -53,6 +53,7 @@ const GENERIC_FUNCTION_SERVE_MESSAGE = `Serving functions on http://127.0.0.1:${ interface FunctionConfig { entrypointPath: string; importMapPath: string; + staticFiles: string[]; verifyJWT: boolean; } diff --git a/internal/start/start.go b/internal/start/start.go index c35efa2e3..aa409d44a 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -1147,6 +1147,10 @@ EOF } // Mount snippets directory for Studio to access + studioConfig, err := json.Marshal(utils.Config) + if err != nil { + return errors.Errorf("failed to marshal config: %w", err) + } hostSnippetsPath := filepath.Join(workdir, utils.SnippetsDir) containerSnippetsPath := utils.ToDockerPath(hostSnippetsPath) binds = append(binds, fmt.Sprintf("%s:%s:rw", hostSnippetsPath, containerSnippetsPath)) @@ -1171,6 +1175,7 @@ EOF fmt.Sprintf("NEXT_ANALYTICS_BACKEND_PROVIDER=%v", utils.Config.Analytics.Backend), "EDGE_FUNCTIONS_MANAGEMENT_FOLDER=" + utils.ToDockerPath(filepath.Join(workdir, utils.FunctionsDir)), "SNIPPETS_MANAGEMENT_FOLDER=" + containerSnippetsPath, + "SUPABASE_CONFIG=" + string(studioConfig), // Ref: https://github.com/vercel/next.js/issues/51684#issuecomment-1612834913 "HOSTNAME=0.0.0.0", }, diff --git a/pkg/config/api.go b/pkg/config/api.go index e294a38c6..3fd3b6f18 100644 --- a/pkg/config/api.go +++ b/pkg/config/api.go @@ -10,25 +10,25 @@ import ( type ( api struct { - Enabled bool `toml:"enabled"` - Schemas []string `toml:"schemas"` - ExtraSearchPath []string `toml:"extra_search_path"` - MaxRows uint `toml:"max_rows"` + Enabled bool `toml:"enabled" json:"enabled"` + Schemas []string `toml:"schemas" json:"schemas"` + ExtraSearchPath []string `toml:"extra_search_path" json:"extra_search_path"` + MaxRows uint `toml:"max_rows" json:"max_rows"` // Local only config - Image string `toml:"-"` - KongImage string `toml:"-"` - Port uint16 `toml:"port"` - Tls tlsKong `toml:"tls"` + Image string `toml:"-" json:"-"` + KongImage string `toml:"-" json:"-"` + Port uint16 `toml:"port" json:"port"` + Tls tlsKong `toml:"tls" json:"tls"` // TODO: replace [auth|studio].api_url - ExternalUrl string `toml:"external_url"` + ExternalUrl string `toml:"external_url" json:"external_url"` } tlsKong struct { - Enabled bool `toml:"enabled"` - CertPath string `toml:"cert_path"` - CertContent []byte `toml:"-"` - KeyPath string `toml:"key_path"` - KeyContent []byte `toml:"-"` + Enabled bool `toml:"enabled" json:"enabled"` + CertPath string `toml:"cert_path" json:"cert_path"` + CertContent []byte `toml:"-" json:"-"` + KeyPath string `toml:"key_path" json:"key_path"` + KeyContent []byte `toml:"-" json:"-"` } ) diff --git a/pkg/config/auth.go b/pkg/config/auth.go index 3c6ee2fd4..46df702d6 100644 --- a/pkg/config/auth.go +++ b/pkg/config/auth.go @@ -86,26 +86,26 @@ func (p *Algorithm) UnmarshalText(text []byte) error { } type JWK struct { - KeyType string `json:"kty"` - KeyID string `json:"kid,omitempty"` - Use string `json:"use,omitempty"` - KeyOps []string `json:"key_ops,omitempty"` - Algorithm Algorithm `json:"alg,omitempty"` - Extractable *bool `json:"ext,omitempty"` + KeyType string `toml:"kty" json:"kty"` + KeyID string `toml:"kid,omitempty" json:"kid,omitempty"` + Use string `toml:"use,omitempty" json:"use,omitempty"` + KeyOps []string `toml:"key_ops,omitempty" json:"key_ops,omitempty"` + Algorithm Algorithm `toml:"alg,omitempty" json:"alg,omitempty"` + Extractable *bool `toml:"ext,omitempty" json:"ext,omitempty"` // RSA specific fields - Modulus string `json:"n,omitempty"` - Exponent string `json:"e,omitempty"` + Modulus string `toml:"n,omitempty" json:"n,omitempty"` + Exponent string `toml:"e,omitempty" json:"e,omitempty"` // RSA private key fields - PrivateExponent string `json:"d,omitempty"` - FirstPrimeFactor string `json:"p,omitempty"` - SecondPrimeFactor string `json:"q,omitempty"` - FirstFactorCRTExponent string `json:"dp,omitempty"` - SecondFactorCRTExponent string `json:"dq,omitempty"` - FirstCRTCoefficient string `json:"qi,omitempty"` + PrivateExponent string `toml:"d,omitempty" json:"d,omitempty"` + FirstPrimeFactor string `toml:"p,omitempty" json:"p,omitempty"` + SecondPrimeFactor string `toml:"q,omitempty" json:"q,omitempty"` + FirstFactorCRTExponent string `toml:"dp,omitempty" json:"dp,omitempty"` + SecondFactorCRTExponent string `toml:"dq,omitempty" json:"dq,omitempty"` + FirstCRTCoefficient string `toml:"qi,omitempty" json:"qi,omitempty"` // EC specific fields - Curve string `json:"crv,omitempty"` - X string `json:"x,omitempty"` - Y string `json:"y,omitempty"` + Curve string `toml:"crv,omitempty" json:"crv,omitempty"` + X string `toml:"x,omitempty" json:"x,omitempty"` + Y string `toml:"y,omitempty" json:"y,omitempty"` } // ToPublicJWK converts a JWK to a public-only version by removing private key components @@ -146,242 +146,242 @@ func (j JWK) ToPublicJWK() JWK { type ( auth struct { - Enabled bool `toml:"enabled"` - Image string `toml:"-"` - - SiteUrl string `toml:"site_url"` - AdditionalRedirectUrls []string `toml:"additional_redirect_urls"` - JwtExpiry uint `toml:"jwt_expiry"` - JwtIssuer string `toml:"jwt_issuer"` - EnableRefreshTokenRotation bool `toml:"enable_refresh_token_rotation"` - RefreshTokenReuseInterval uint `toml:"refresh_token_reuse_interval"` - EnableManualLinking bool `toml:"enable_manual_linking"` - EnableSignup bool `toml:"enable_signup"` - EnableAnonymousSignIns bool `toml:"enable_anonymous_sign_ins"` - MinimumPasswordLength uint `toml:"minimum_password_length"` - PasswordRequirements PasswordRequirements `toml:"password_requirements"` - SigningKeysPath string `toml:"signing_keys_path"` - SigningKeys []JWK `toml:"-"` - - RateLimit rateLimit `toml:"rate_limit"` - Captcha *captcha `toml:"captcha"` - Hook hook `toml:"hook"` - MFA mfa `toml:"mfa"` - Sessions sessions `toml:"sessions"` - Email email `toml:"email"` - Sms sms `toml:"sms"` - External external `toml:"external"` - Web3 web3 `toml:"web3"` - OAuthServer OAuthServer `toml:"oauth_server"` + Enabled bool `toml:"enabled" json:"enabled"` + Image string `toml:"-" json:"-"` + + SiteUrl string `toml:"site_url" json:"site_url"` + AdditionalRedirectUrls []string `toml:"additional_redirect_urls" json:"additional_redirect_urls"` + JwtExpiry uint `toml:"jwt_expiry" json:"jwt_expiry"` + JwtIssuer string `toml:"jwt_issuer" json:"jwt_issuer"` + EnableRefreshTokenRotation bool `toml:"enable_refresh_token_rotation" json:"enable_refresh_token_rotation"` + RefreshTokenReuseInterval uint `toml:"refresh_token_reuse_interval" json:"refresh_token_reuse_interval"` + EnableManualLinking bool `toml:"enable_manual_linking" json:"enable_manual_linking"` + EnableSignup bool `toml:"enable_signup" json:"enable_signup"` + EnableAnonymousSignIns bool `toml:"enable_anonymous_sign_ins" json:"enable_anonymous_sign_ins"` + MinimumPasswordLength uint `toml:"minimum_password_length" json:"minimum_password_length"` + PasswordRequirements PasswordRequirements `toml:"password_requirements" json:"password_requirements"` + SigningKeysPath string `toml:"signing_keys_path" json:"signing_keys_path"` + SigningKeys []JWK `toml:"-" json:"-"` + + RateLimit rateLimit `toml:"rate_limit" json:"rate_limit"` + Captcha *captcha `toml:"captcha" json:"captcha"` + Hook hook `toml:"hook" json:"hook"` + MFA mfa `toml:"mfa" json:"mfa"` + Sessions sessions `toml:"sessions" json:"sessions"` + Email email `toml:"email" json:"email"` + Sms sms `toml:"sms" json:"sms"` + External external `toml:"external" json:"external"` + Web3 web3 `toml:"web3" json:"web3"` + OAuthServer OAuthServer `toml:"oauth_server" json:"oauth_server"` // Custom secrets can be injected from .env file - PublishableKey Secret `toml:"publishable_key"` - SecretKey Secret `toml:"secret_key"` - JwtSecret Secret `toml:"jwt_secret"` - AnonKey Secret `toml:"anon_key"` - ServiceRoleKey Secret `toml:"service_role_key"` + PublishableKey Secret `toml:"publishable_key" json:"publishable_key"` + SecretKey Secret `toml:"secret_key" json:"secret_key"` + JwtSecret Secret `toml:"jwt_secret" json:"jwt_secret"` + AnonKey Secret `toml:"anon_key" json:"anon_key"` + ServiceRoleKey Secret `toml:"service_role_key" json:"service_role_key"` - ThirdParty thirdParty `toml:"third_party"` + ThirdParty thirdParty `toml:"third_party" json:"third_party"` } external map[string]provider thirdParty struct { - Firebase tpaFirebase `toml:"firebase"` - Auth0 tpaAuth0 `toml:"auth0"` - Cognito tpaCognito `toml:"aws_cognito"` - Clerk tpaClerk `toml:"clerk"` - WorkOs tpaWorkOs `toml:"workos"` + Firebase tpaFirebase `toml:"firebase" json:"firebase"` + Auth0 tpaAuth0 `toml:"auth0" json:"auth0"` + Cognito tpaCognito `toml:"aws_cognito" json:"aws_cognito"` + Clerk tpaClerk `toml:"clerk" json:"clerk"` + WorkOs tpaWorkOs `toml:"workos" json:"workos"` } rateLimit struct { - AnonymousUsers uint `toml:"anonymous_users"` - TokenRefresh uint `toml:"token_refresh"` - SignInSignUps uint `toml:"sign_in_sign_ups"` - TokenVerifications uint `toml:"token_verifications"` - EmailSent uint `toml:"email_sent"` - SmsSent uint `toml:"sms_sent"` - Web3 uint `toml:"web3"` + AnonymousUsers uint `toml:"anonymous_users" json:"anonymous_users"` + TokenRefresh uint `toml:"token_refresh" json:"token_refresh"` + SignInSignUps uint `toml:"sign_in_sign_ups" json:"sign_in_sign_ups"` + TokenVerifications uint `toml:"token_verifications" json:"token_verifications"` + EmailSent uint `toml:"email_sent" json:"email_sent"` + SmsSent uint `toml:"sms_sent" json:"sms_sent"` + Web3 uint `toml:"web3" json:"web3"` } tpaFirebase struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` - ProjectID string `toml:"project_id"` + ProjectID string `toml:"project_id" json:"project_id"` } tpaAuth0 struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` - Tenant string `toml:"tenant"` - TenantRegion string `toml:"tenant_region"` + Tenant string `toml:"tenant" json:"tenant"` + TenantRegion string `toml:"tenant_region" json:"tenant_region"` } tpaCognito struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` - UserPoolID string `toml:"user_pool_id"` - UserPoolRegion string `toml:"user_pool_region"` + UserPoolID string `toml:"user_pool_id" json:"user_pool_id"` + UserPoolRegion string `toml:"user_pool_region" json:"user_pool_region"` } tpaClerk struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` - Domain string `toml:"domain"` + Domain string `toml:"domain" json:"domain"` } tpaWorkOs struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` - IssuerUrl string `toml:"issuer_url"` + IssuerUrl string `toml:"issuer_url" json:"issuer_url"` } email struct { - EnableSignup bool `toml:"enable_signup"` - DoubleConfirmChanges bool `toml:"double_confirm_changes"` - EnableConfirmations bool `toml:"enable_confirmations"` - SecurePasswordChange bool `toml:"secure_password_change"` - Template map[string]emailTemplate `toml:"template"` - Notification map[string]notification `toml:"notification"` - Smtp *smtp `toml:"smtp"` - MaxFrequency time.Duration `toml:"max_frequency"` - OtpLength uint `toml:"otp_length"` - OtpExpiry uint `toml:"otp_expiry"` + EnableSignup bool `toml:"enable_signup" json:"enable_signup"` + DoubleConfirmChanges bool `toml:"double_confirm_changes" json:"double_confirm_changes"` + EnableConfirmations bool `toml:"enable_confirmations" json:"enable_confirmations"` + SecurePasswordChange bool `toml:"secure_password_change" json:"secure_password_change"` + Template map[string]emailTemplate `toml:"template" json:"template"` + Notification map[string]notification `toml:"notification" json:"notification"` + Smtp *smtp `toml:"smtp" json:"smtp"` + MaxFrequency time.Duration `toml:"max_frequency" json:"max_frequency"` + OtpLength uint `toml:"otp_length" json:"otp_length"` + OtpExpiry uint `toml:"otp_expiry" json:"otp_expiry"` } smtp struct { - Enabled bool `toml:"enabled"` - Host string `toml:"host"` - Port uint16 `toml:"port"` - User string `toml:"user"` - Pass Secret `toml:"pass"` - AdminEmail openapi_types.Email `toml:"admin_email"` - SenderName string `toml:"sender_name"` + Enabled bool `toml:"enabled" json:"enabled"` + Host string `toml:"host" json:"host"` + Port uint16 `toml:"port" json:"port"` + User string `toml:"user" json:"user"` + Pass Secret `toml:"pass" json:"pass"` + AdminEmail openapi_types.Email `toml:"admin_email" json:"admin_email"` + SenderName string `toml:"sender_name" json:"sender_name"` } emailTemplate struct { - Subject *string `toml:"subject"` - Content *string `toml:"content"` + Subject *string `toml:"subject" json:"subject"` + Content *string `toml:"content" json:"content"` // Only content path is accepted in config.toml - ContentPath string `toml:"content_path"` + ContentPath string `toml:"content_path" json:"content_path"` } notification struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` emailTemplate } sms struct { - EnableSignup bool `toml:"enable_signup"` - EnableConfirmations bool `toml:"enable_confirmations"` - Template string `toml:"template"` - Twilio twilioConfig `toml:"twilio"` - TwilioVerify twilioConfig `toml:"twilio_verify"` - Messagebird messagebirdConfig `toml:"messagebird"` - Textlocal textlocalConfig `toml:"textlocal"` - Vonage vonageConfig `toml:"vonage"` - TestOTP map[string]string `toml:"test_otp"` - MaxFrequency time.Duration `toml:"max_frequency"` + EnableSignup bool `toml:"enable_signup" json:"enable_signup"` + EnableConfirmations bool `toml:"enable_confirmations" json:"enable_confirmations"` + Template string `toml:"template" json:"template"` + Twilio twilioConfig `toml:"twilio" json:"twilio"` + TwilioVerify twilioConfig `toml:"twilio_verify" json:"twilio_verify"` + Messagebird messagebirdConfig `toml:"messagebird" json:"messagebird"` + Textlocal textlocalConfig `toml:"textlocal" json:"textlocal"` + Vonage vonageConfig `toml:"vonage" json:"vonage"` + TestOTP map[string]string `toml:"test_otp" json:"test_otp"` + MaxFrequency time.Duration `toml:"max_frequency" json:"max_frequency"` } captcha struct { - Enabled bool `toml:"enabled"` - Provider CaptchaProvider `toml:"provider"` - Secret Secret `toml:"secret"` + Enabled bool `toml:"enabled" json:"enabled"` + Provider CaptchaProvider `toml:"provider" json:"provider"` + Secret Secret `toml:"secret" json:"secret"` } hook struct { - MFAVerificationAttempt *hookConfig `toml:"mfa_verification_attempt"` - PasswordVerificationAttempt *hookConfig `toml:"password_verification_attempt"` - CustomAccessToken *hookConfig `toml:"custom_access_token"` - SendSMS *hookConfig `toml:"send_sms"` - SendEmail *hookConfig `toml:"send_email"` - BeforeUserCreated *hookConfig `toml:"before_user_created"` + MFAVerificationAttempt *hookConfig `toml:"mfa_verification_attempt" json:"mfa_verification_attempt"` + PasswordVerificationAttempt *hookConfig `toml:"password_verification_attempt" json:"password_verification_attempt"` + CustomAccessToken *hookConfig `toml:"custom_access_token" json:"custom_access_token"` + SendSMS *hookConfig `toml:"send_sms" json:"send_sms"` + SendEmail *hookConfig `toml:"send_email" json:"send_email"` + BeforeUserCreated *hookConfig `toml:"before_user_created" json:"before_user_created"` } factorTypeConfiguration struct { - EnrollEnabled bool `toml:"enroll_enabled"` - VerifyEnabled bool `toml:"verify_enabled"` + EnrollEnabled bool `toml:"enroll_enabled" json:"enroll_enabled"` + VerifyEnabled bool `toml:"verify_enabled" json:"verify_enabled"` } phoneFactorTypeConfiguration struct { factorTypeConfiguration - OtpLength uint `toml:"otp_length"` - Template string `toml:"template"` - MaxFrequency time.Duration `toml:"max_frequency"` + OtpLength uint `toml:"otp_length" json:"otp_length"` + Template string `toml:"template" json:"template"` + MaxFrequency time.Duration `toml:"max_frequency" json:"max_frequency"` } mfa struct { - TOTP factorTypeConfiguration `toml:"totp"` - Phone phoneFactorTypeConfiguration `toml:"phone"` - WebAuthn factorTypeConfiguration `toml:"web_authn"` - MaxEnrolledFactors uint `toml:"max_enrolled_factors"` + TOTP factorTypeConfiguration `toml:"totp" json:"totp"` + Phone phoneFactorTypeConfiguration `toml:"phone" json:"phone"` + WebAuthn factorTypeConfiguration `toml:"web_authn" json:"web_authn"` + MaxEnrolledFactors uint `toml:"max_enrolled_factors" json:"max_enrolled_factors"` } hookConfig struct { - Enabled bool `toml:"enabled"` - URI string `toml:"uri"` - Secrets Secret `toml:"secrets"` + Enabled bool `toml:"enabled" json:"enabled"` + URI string `toml:"uri" json:"uri"` + Secrets Secret `toml:"secrets" json:"secrets"` } sessions struct { - Timebox time.Duration `toml:"timebox"` - InactivityTimeout time.Duration `toml:"inactivity_timeout"` + Timebox time.Duration `toml:"timebox" json:"timebox"` + InactivityTimeout time.Duration `toml:"inactivity_timeout" json:"inactivity_timeout"` } twilioConfig struct { - Enabled bool `toml:"enabled"` - AccountSid string `toml:"account_sid"` - MessageServiceSid string `toml:"message_service_sid"` - AuthToken Secret `toml:"auth_token"` + Enabled bool `toml:"enabled" json:"enabled"` + AccountSid string `toml:"account_sid" json:"account_sid"` + MessageServiceSid string `toml:"message_service_sid" json:"message_service_sid"` + AuthToken Secret `toml:"auth_token" json:"auth_token"` } messagebirdConfig struct { - Enabled bool `toml:"enabled"` - Originator string `toml:"originator"` - AccessKey Secret `toml:"access_key"` + Enabled bool `toml:"enabled" json:"enabled"` + Originator string `toml:"originator" json:"originator"` + AccessKey Secret `toml:"access_key" json:"access_key"` } textlocalConfig struct { - Enabled bool `toml:"enabled"` - Sender string `toml:"sender"` - ApiKey Secret `toml:"api_key"` + Enabled bool `toml:"enabled" json:"enabled"` + Sender string `toml:"sender" json:"sender"` + ApiKey Secret `toml:"api_key" json:"api_key"` } vonageConfig struct { - Enabled bool `toml:"enabled"` - From string `toml:"from"` - ApiKey string `toml:"api_key"` - ApiSecret Secret `toml:"api_secret"` + Enabled bool `toml:"enabled" json:"enabled"` + From string `toml:"from" json:"from"` + ApiKey string `toml:"api_key" json:"api_key"` + ApiSecret Secret `toml:"api_secret" json:"api_secret"` } provider struct { - Enabled bool `toml:"enabled"` - ClientId string `toml:"client_id"` - Secret Secret `toml:"secret"` - Url string `toml:"url"` - RedirectUri string `toml:"redirect_uri"` - SkipNonceCheck bool `toml:"skip_nonce_check"` - EmailOptional bool `toml:"email_optional"` + Enabled bool `toml:"enabled" json:"enabled"` + ClientId string `toml:"client_id" json:"client_id"` + Secret Secret `toml:"secret" json:"secret"` + Url string `toml:"url" json:"url"` + RedirectUri string `toml:"redirect_uri" json:"redirect_uri"` + SkipNonceCheck bool `toml:"skip_nonce_check" json:"skip_nonce_check"` + EmailOptional bool `toml:"email_optional" json:"email_optional"` } solana struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` } ethereum struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` } web3 struct { - Solana solana `toml:"solana"` - Ethereum ethereum `toml:"ethereum"` + Solana solana `toml:"solana" json:"solana"` + Ethereum ethereum `toml:"ethereum" json:"ethereum"` } OAuthServer struct { - Enabled bool `toml:"enabled"` - AllowDynamicRegistration bool `toml:"allow_dynamic_registration"` - AuthorizationUrlPath string `toml:"authorization_url_path"` + Enabled bool `toml:"enabled" json:"enabled"` + AllowDynamicRegistration bool `toml:"allow_dynamic_registration" json:"allow_dynamic_registration"` + AuthorizationUrlPath string `toml:"authorization_url_path" json:"authorization_url_path"` } ) diff --git a/pkg/config/config.go b/pkg/config/config.go index 8cf2e4a27..a49e84d7d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -138,112 +138,112 @@ func (g Glob) Files(fsys fs.FS) ([]string, error) { type ( // Common config fields between our "base" config and any "remote" branch specific baseConfig struct { - ProjectId string `toml:"project_id"` - Hostname string `toml:"-"` - Api api `toml:"api"` - Db db `toml:"db"` - Realtime realtime `toml:"realtime"` - Studio studio `toml:"studio"` - Inbucket inbucket `toml:"inbucket"` - Storage storage `toml:"storage"` - Auth auth `toml:"auth"` - EdgeRuntime edgeRuntime `toml:"edge_runtime"` - Functions FunctionConfig `toml:"functions"` - Analytics analytics `toml:"analytics"` - Experimental experimental `toml:"experimental"` + ProjectId string `toml:"project_id" json:"project_id"` + Hostname string `toml:"-" json:"-"` + Api api `toml:"api" json:"api"` + Db db `toml:"db" json:"db"` + Realtime realtime `toml:"realtime" json:"realtime"` + Studio studio `toml:"studio" json:"studio"` + Inbucket inbucket `toml:"inbucket" json:"inbucket"` + Storage storage `toml:"storage" json:"storage"` + Auth auth `toml:"auth" json:"auth"` + EdgeRuntime edgeRuntime `toml:"edge_runtime" json:"edge_runtime"` + Functions FunctionConfig `toml:"functions" json:"functions"` + Analytics analytics `toml:"analytics" json:"analytics"` + Experimental experimental `toml:"experimental" json:"experimental"` } config struct { baseConfig - Remotes map[string]baseConfig `toml:"remotes"` + Remotes map[string]baseConfig `toml:"remotes" json:"remotes"` } realtime struct { - Enabled bool `toml:"enabled"` - Image string `toml:"-"` - IpVersion AddressFamily `toml:"ip_version"` - MaxHeaderLength uint `toml:"max_header_length"` - TenantId string `toml:"-"` - EncryptionKey string `toml:"-"` - SecretKeyBase string `toml:"-"` + Enabled bool `toml:"enabled" json:"enabled"` + Image string `toml:"-" json:"-"` + IpVersion AddressFamily `toml:"ip_version" json:"ip_version"` + MaxHeaderLength uint `toml:"max_header_length" json:"max_header_length"` + TenantId string `toml:"-" json:"-"` + EncryptionKey string `toml:"-" json:"-"` + SecretKeyBase string `toml:"-" json:"-"` } studio struct { - Enabled bool `toml:"enabled"` - Image string `toml:"-"` - Port uint16 `toml:"port"` - ApiUrl string `toml:"api_url"` - OpenaiApiKey Secret `toml:"openai_api_key"` - PgmetaImage string `toml:"-"` + Enabled bool `toml:"enabled" json:"enabled"` + Image string `toml:"-" json:"-"` + Port uint16 `toml:"port" json:"port"` + ApiUrl string `toml:"api_url" json:"api_url"` + OpenaiApiKey Secret `toml:"openai_api_key" json:"openai_api_key"` + PgmetaImage string `toml:"-" json:"-"` } inbucket struct { - Enabled bool `toml:"enabled"` - Image string `toml:"-"` - Port uint16 `toml:"port"` - SmtpPort uint16 `toml:"smtp_port"` - Pop3Port uint16 `toml:"pop3_port"` - AdminEmail string `toml:"admin_email"` - SenderName string `toml:"sender_name"` + Enabled bool `toml:"enabled" json:"enabled"` + Image string `toml:"-" json:"-"` + Port uint16 `toml:"port" json:"port"` + SmtpPort uint16 `toml:"smtp_port" json:"smtp_port"` + Pop3Port uint16 `toml:"pop3_port" json:"pop3_port"` + AdminEmail string `toml:"admin_email" json:"admin_email"` + SenderName string `toml:"sender_name" json:"sender_name"` } edgeRuntime struct { - Enabled bool `toml:"enabled"` - Image string `toml:"-"` - Policy RequestPolicy `toml:"policy"` - InspectorPort uint16 `toml:"inspector_port"` - Secrets SecretsConfig `toml:"secrets"` - DenoVersion uint `toml:"deno_version"` + Enabled bool `toml:"enabled" json:"enabled"` + Image string `toml:"-" json:"-"` + Policy RequestPolicy `toml:"policy" json:"policy"` + InspectorPort uint16 `toml:"inspector_port" json:"inspector_port"` + Secrets SecretsConfig `toml:"secrets" json:"secrets"` + DenoVersion uint `toml:"deno_version" json:"deno_version"` } SecretsConfig map[string]Secret FunctionConfig map[string]function function struct { - Enabled bool `toml:"enabled" json:"-"` - VerifyJWT bool `toml:"verify_jwt" json:"verifyJWT"` - ImportMap string `toml:"import_map" json:"importMapPath,omitempty"` - Entrypoint string `toml:"entrypoint" json:"entrypointPath,omitempty"` - StaticFiles Glob `toml:"static_files" json:"staticFiles,omitempty"` + Enabled bool `toml:"enabled" json:"enabled"` + VerifyJWT bool `toml:"verify_jwt" json:"verify_jwt"` + ImportMap string `toml:"import_map" json:"import_map"` + Entrypoint string `toml:"entrypoint" json:"entrypoint"` + StaticFiles Glob `toml:"static_files" json:"static_files"` } analytics struct { - Enabled bool `toml:"enabled"` - Image string `toml:"-"` - VectorImage string `toml:"-"` - Port uint16 `toml:"port"` - Backend LogflareBackend `toml:"backend"` - GcpProjectId string `toml:"gcp_project_id"` - GcpProjectNumber string `toml:"gcp_project_number"` - GcpJwtPath string `toml:"gcp_jwt_path"` - ApiKey string `toml:"-"` + Enabled bool `toml:"enabled" json:"enabled"` + Image string `toml:"-" json:"-"` + VectorImage string `toml:"-" json:"-"` + Port uint16 `toml:"port" json:"port"` + Backend LogflareBackend `toml:"backend" json:"backend"` + GcpProjectId string `toml:"gcp_project_id" json:"gcp_project_id"` + GcpProjectNumber string `toml:"gcp_project_number" json:"gcp_project_number"` + GcpJwtPath string `toml:"gcp_jwt_path" json:"gcp_jwt_path"` + ApiKey string `toml:"-" json:"-"` // Deprecated together with syslog - VectorPort uint16 `toml:"vector_port"` + VectorPort uint16 `toml:"vector_port" json:"vector_port"` } webhooks struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` } inspect struct { - Rules []rule `toml:"rules"` + Rules []rule `toml:"rules" json:"rules"` } rule struct { - Query string `toml:"query"` - Name string `toml:"name"` - Pass string `toml:"pass"` - Fail string `toml:"fail"` + Query string `toml:"query" json:"query"` + Name string `toml:"name" json:"name"` + Pass string `toml:"pass" json:"pass"` + Fail string `toml:"fail" json:"fail"` } experimental struct { - OrioleDBVersion string `toml:"orioledb_version"` - S3Host string `toml:"s3_host"` - S3Region string `toml:"s3_region"` - S3AccessKey string `toml:"s3_access_key"` - S3SecretKey string `toml:"s3_secret_key"` - Webhooks *webhooks `toml:"webhooks"` - Inspect inspect `toml:"inspect"` + OrioleDBVersion string `toml:"orioledb_version" json:"orioledb_version"` + S3Host string `toml:"s3_host" json:"s3_host"` + S3Region string `toml:"s3_region" json:"s3_region"` + S3AccessKey string `toml:"s3_access_key" json:"s3_access_key"` + S3SecretKey string `toml:"s3_secret_key" json:"s3_secret_key"` + Webhooks *webhooks `toml:"webhooks" json:"webhooks"` + Inspect inspect `toml:"inspect" json:"inspect"` } ) @@ -551,7 +551,7 @@ func (c *config) load(v *viper.Viper) error { } } if err := v.UnmarshalExact(c, func(dc *mapstructure.DecoderConfig) { - dc.TagName = "toml" + dc.TagName = "json" dc.Squash = true dc.ZeroFields = true dc.DecodeHook = c.newDecodeHook(LoadEnvHook, ValidateFunctionsHook) diff --git a/pkg/config/db.go b/pkg/config/db.go index 122f5364b..84d4dc504 100644 --- a/pkg/config/db.go +++ b/pkg/config/db.go @@ -44,74 +44,74 @@ func (r *SessionReplicationRole) UnmarshalText(text []byte) error { type ( settings struct { - EffectiveCacheSize *string `toml:"effective_cache_size"` - LogicalDecodingWorkMem *string `toml:"logical_decoding_work_mem"` - MaintenanceWorkMem *string `toml:"maintenance_work_mem"` - MaxConnections *uint `toml:"max_connections"` - MaxLocksPerTransaction *uint `toml:"max_locks_per_transaction"` - MaxParallelMaintenanceWorkers *uint `toml:"max_parallel_maintenance_workers"` - MaxParallelWorkers *uint `toml:"max_parallel_workers"` - MaxParallelWorkersPerGather *uint `toml:"max_parallel_workers_per_gather"` - MaxReplicationSlots *uint `toml:"max_replication_slots"` - MaxSlotWalKeepSize *string `toml:"max_slot_wal_keep_size"` - MaxStandbyArchiveDelay *string `toml:"max_standby_archive_delay"` - MaxStandbyStreamingDelay *string `toml:"max_standby_streaming_delay"` - MaxWalSize *string `toml:"max_wal_size"` - MaxWalSenders *uint `toml:"max_wal_senders"` - MaxWorkerProcesses *uint `toml:"max_worker_processes"` - SessionReplicationRole *SessionReplicationRole `toml:"session_replication_role"` - SharedBuffers *string `toml:"shared_buffers"` - StatementTimeout *string `toml:"statement_timeout"` - TrackActivityQuerySize *string `toml:"track_activity_query_size"` - TrackCommitTimestamp *bool `toml:"track_commit_timestamp"` - WalKeepSize *string `toml:"wal_keep_size"` - WalSenderTimeout *string `toml:"wal_sender_timeout"` - WorkMem *string `toml:"work_mem"` + EffectiveCacheSize *string `toml:"effective_cache_size" json:"effective_cache_size"` + LogicalDecodingWorkMem *string `toml:"logical_decoding_work_mem" json:"logical_decoding_work_mem"` + MaintenanceWorkMem *string `toml:"maintenance_work_mem" json:"maintenance_work_mem"` + MaxConnections *uint `toml:"max_connections" json:"max_connections"` + MaxLocksPerTransaction *uint `toml:"max_locks_per_transaction" json:"max_locks_per_transaction"` + MaxParallelMaintenanceWorkers *uint `toml:"max_parallel_maintenance_workers" json:"max_parallel_maintenance_workers"` + MaxParallelWorkers *uint `toml:"max_parallel_workers" json:"max_parallel_workers"` + MaxParallelWorkersPerGather *uint `toml:"max_parallel_workers_per_gather" json:"max_parallel_workers_per_gather"` + MaxReplicationSlots *uint `toml:"max_replication_slots" json:"max_replication_slots"` + MaxSlotWalKeepSize *string `toml:"max_slot_wal_keep_size" json:"max_slot_wal_keep_size"` + MaxStandbyArchiveDelay *string `toml:"max_standby_archive_delay" json:"max_standby_archive_delay"` + MaxStandbyStreamingDelay *string `toml:"max_standby_streaming_delay" json:"max_standby_streaming_delay"` + MaxWalSize *string `toml:"max_wal_size" json:"max_wal_size"` + MaxWalSenders *uint `toml:"max_wal_senders" json:"max_wal_senders"` + MaxWorkerProcesses *uint `toml:"max_worker_processes" json:"max_worker_processes"` + SessionReplicationRole *SessionReplicationRole `toml:"session_replication_role" json:"session_replication_role"` + SharedBuffers *string `toml:"shared_buffers" json:"shared_buffers"` + StatementTimeout *string `toml:"statement_timeout" json:"statement_timeout"` + TrackActivityQuerySize *string `toml:"track_activity_query_size" json:"track_activity_query_size"` + TrackCommitTimestamp *bool `toml:"track_commit_timestamp" json:"track_commit_timestamp"` + WalKeepSize *string `toml:"wal_keep_size" json:"wal_keep_size"` + WalSenderTimeout *string `toml:"wal_sender_timeout" json:"wal_sender_timeout"` + WorkMem *string `toml:"work_mem" json:"work_mem"` } networkRestrictions struct { - Enabled bool `toml:"enabled"` - AllowedCidrs []string `toml:"allowed_cidrs"` - AllowedCidrsV6 []string `toml:"allowed_cidrs_v6"` + Enabled bool `toml:"enabled" json:"enabled"` + AllowedCidrs []string `toml:"allowed_cidrs" json:"allowed_cidrs"` + AllowedCidrsV6 []string `toml:"allowed_cidrs_v6" json:"allowed_cidrs_v6"` } db struct { - Image string `toml:"-"` - Port uint16 `toml:"port"` - ShadowPort uint16 `toml:"shadow_port"` - HealthTimeout time.Duration `toml:"health_timeout"` - MajorVersion uint `toml:"major_version"` - Password string `toml:"-"` - RootKey Secret `toml:"root_key"` - Pooler pooler `toml:"pooler"` - Migrations migrations `toml:"migrations"` - Seed seed `toml:"seed"` - Settings settings `toml:"settings"` - NetworkRestrictions networkRestrictions `toml:"network_restrictions"` - Vault map[string]Secret `toml:"vault"` + Image string `toml:"-" json:"-"` + Port uint16 `toml:"port" json:"port"` + ShadowPort uint16 `toml:"shadow_port" json:"shadow_port"` + HealthTimeout time.Duration `toml:"health_timeout" json:"health_timeout"` + MajorVersion uint `toml:"major_version" json:"major_version"` + Password string `toml:"-" json:"-"` + RootKey Secret `toml:"root_key" json:"root_key"` + Pooler pooler `toml:"pooler" json:"pooler"` + Migrations migrations `toml:"migrations" json:"migrations"` + Seed seed `toml:"seed" json:"seed"` + Settings settings `toml:"settings" json:"settings"` + NetworkRestrictions networkRestrictions `toml:"network_restrictions" json:"network_restrictions"` + Vault map[string]Secret `toml:"vault" json:"vault"` } migrations struct { - Enabled bool `toml:"enabled"` - SchemaPaths Glob `toml:"schema_paths"` + Enabled bool `toml:"enabled" json:"enabled"` + SchemaPaths Glob `toml:"schema_paths" json:"schema_paths"` } seed struct { - Enabled bool `toml:"enabled"` - SqlPaths Glob `toml:"sql_paths"` + Enabled bool `toml:"enabled" json:"enabled"` + SqlPaths Glob `toml:"sql_paths" json:"sql_paths"` } pooler struct { - Enabled bool `toml:"enabled"` - Image string `toml:"-"` - Port uint16 `toml:"port"` - PoolMode PoolMode `toml:"pool_mode"` - DefaultPoolSize uint `toml:"default_pool_size"` - MaxClientConn uint `toml:"max_client_conn"` - ConnectionString string `toml:"-"` - TenantId string `toml:"-"` - EncryptionKey string `toml:"-"` - SecretKeyBase string `toml:"-"` + Enabled bool `toml:"enabled" json:"enabled"` + Image string `toml:"-" json:"-"` + Port uint16 `toml:"port" json:"port"` + PoolMode PoolMode `toml:"pool_mode" json:"pool_mode"` + DefaultPoolSize uint `toml:"default_pool_size" json:"default_pool_size"` + MaxClientConn uint `toml:"max_client_conn" json:"max_client_conn"` + ConnectionString string `toml:"-" json:"-"` + TenantId string `toml:"-" json:"-"` + EncryptionKey string `toml:"-" json:"-"` + SecretKeyBase string `toml:"-" json:"-"` } ) diff --git a/pkg/config/storage.go b/pkg/config/storage.go index 7fdffb3a8..740c525f4 100644 --- a/pkg/config/storage.go +++ b/pkg/config/storage.go @@ -8,55 +8,55 @@ import ( type ( storage struct { - Enabled bool `toml:"enabled"` - Image string `toml:"-"` - TargetMigration string `toml:"-"` - ImgProxyImage string `toml:"-"` - FileSizeLimit sizeInBytes `toml:"file_size_limit"` - ImageTransformation *imageTransformation `toml:"image_transformation"` - S3Protocol *s3Protocol `toml:"s3_protocol"` - S3Credentials storageS3Credentials `toml:"-"` - Buckets BucketConfig `toml:"buckets"` - AnalyticsBuckets analyticsBuckets `toml:"analytics"` - VectorBuckets vectorBuckets `toml:"vector"` + Enabled bool `toml:"enabled" json:"enabled"` + Image string `toml:"-" json:"-"` + TargetMigration string `toml:"-" json:"-"` + ImgProxyImage string `toml:"-" json:"-"` + FileSizeLimit sizeInBytes `toml:"file_size_limit" json:"file_size_limit"` + ImageTransformation *imageTransformation `toml:"image_transformation" json:"image_transformation"` + S3Protocol *s3Protocol `toml:"s3_protocol" json:"s3_protocol"` + S3Credentials storageS3Credentials `toml:"-" json:"-"` + Buckets BucketConfig `toml:"buckets" json:"buckets"` + AnalyticsBuckets analyticsBuckets `toml:"analytics" json:"analytics"` + VectorBuckets vectorBuckets `toml:"vector" json:"vector"` } imageTransformation struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` } analyticsBuckets struct { - Enabled bool `toml:"enabled"` - MaxNamespaces uint `toml:"max_namespaces"` - MaxTables uint `toml:"max_tables"` - MaxCatalogs uint `toml:"max_catalogs"` - Buckets map[string]struct{} `toml:"buckets"` + Enabled bool `toml:"enabled" json:"enabled"` + MaxNamespaces uint `toml:"max_namespaces" json:"max_namespaces"` + MaxTables uint `toml:"max_tables" json:"max_tables"` + MaxCatalogs uint `toml:"max_catalogs" json:"max_catalogs"` + Buckets map[string]struct{} `toml:"buckets" json:"buckets"` } vectorBuckets struct { - Enabled bool `toml:"enabled"` - MaxBuckets uint `toml:"max_buckets"` - MaxIndexes uint `toml:"max_indexes"` - Buckets map[string]struct{} `toml:"buckets"` + Enabled bool `toml:"enabled" json:"enabled"` + MaxBuckets uint `toml:"max_buckets" json:"max_buckets"` + MaxIndexes uint `toml:"max_indexes" json:"max_indexes"` + Buckets map[string]struct{} `toml:"buckets" json:"buckets"` } s3Protocol struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled" json:"enabled"` } storageS3Credentials struct { - AccessKeyId string `toml:"-"` - SecretAccessKey string `toml:"-"` - Region string `toml:"-"` + AccessKeyId string `toml:"-" json:"-"` + SecretAccessKey string `toml:"-" json:"-"` + Region string `toml:"-" json:"-"` } BucketConfig map[string]bucket bucket struct { - Public *bool `toml:"public"` - FileSizeLimit sizeInBytes `toml:"file_size_limit"` - AllowedMimeTypes []string `toml:"allowed_mime_types"` - ObjectsPath string `toml:"objects_path"` + Public *bool `toml:"public" json:"public"` + FileSizeLimit sizeInBytes `toml:"file_size_limit" json:"file_size_limit"` + AllowedMimeTypes []string `toml:"allowed_mime_types" json:"allowed_mime_types"` + ObjectsPath string `toml:"objects_path" json:"objects_path"` } ) diff --git a/tools/jsonschema/main.go b/tools/jsonschema/main.go new file mode 100644 index 000000000..507ae338e --- /dev/null +++ b/tools/jsonschema/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + + "github.com/google/jsonschema-go/jsonschema" + "github.com/supabase/cli/pkg/config" +) + +func main() { + if err := run(); err != nil { + panic(err) + } +} + +func run() error { + secret, err := jsonschema.For[config.Secret](&jsonschema.ForOptions{}) + if err != nil { + return err + } + secretSchema, err := json.Marshal(secret) + if err != nil { + return err + } + // Replace secret schema because TypeSchemas is not working + opts := &jsonschema.ForOptions{ + TypeSchemas: map[reflect.Type]*jsonschema.Schema{ + reflect.TypeFor[config.Secret](): {Type: "string"}, + }, + } + js, err := jsonschema.For[config.Config](opts) + if err != nil { + return err + } + data, err := json.Marshal(js) + if err != nil { + return err + } + result := bytes.ReplaceAll(data, secretSchema, []byte(`{"type":"string"}`)) + _, err = fmt.Println(string(result)) + return err +}