feat(storage): finish initial basic implementation
- support both s3 & file storage backends - support imgproxy to scale images - manually tested with MinIO & local storage - fixed service discovery issue in APIGatey reconciler not detecting service changes - refactored defaults and env variable code to make it manageable again - add repo link to docs
This commit is contained in:
parent
604525de38
commit
0014927ca9
46 changed files with 16170 additions and 606 deletions
86
internal/supabase/auth.go
Normal file
86
internal/supabase/auth.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
Copyright 2025 Peter Kurfer.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package supabase
|
||||
|
||||
type authEnvKeys struct {
|
||||
ApiHost fixedEnv
|
||||
ApiPort fixedEnv
|
||||
ApiExternalUrl stringEnv
|
||||
DBDriver fixedEnv
|
||||
DatabaseUrl string
|
||||
SiteUrl stringEnv
|
||||
AdditionalRedirectURLs stringSliceEnv
|
||||
DisableSignup boolEnv
|
||||
JWTIssuer fixedEnv
|
||||
JWTAdminRoles fixedEnv
|
||||
JWTAudience fixedEnv
|
||||
JwtDefaultGroup fixedEnv
|
||||
JwtExpiry intEnv[int]
|
||||
JwtSecret secretEnv
|
||||
EmailSignupDisabled boolEnv
|
||||
MailerUrlPathsInvite stringEnv
|
||||
MailerUrlPathsConfirmation stringEnv
|
||||
MailerUrlPathsRecovery stringEnv
|
||||
MailerUrlPathsEmailChange stringEnv
|
||||
AnonymousUsersEnabled boolEnv
|
||||
}
|
||||
|
||||
type authConfigDefaults struct {
|
||||
MailerUrlPathsInvite string
|
||||
MailerUrlPathsConfirmation string
|
||||
MailerUrlPathsRecovery string
|
||||
MailerUrlPathsEmailChange string
|
||||
APIPort int32
|
||||
UID, GID int64
|
||||
}
|
||||
|
||||
func authServiceConfig() serviceConfig[authEnvKeys, authConfigDefaults] {
|
||||
return serviceConfig[authEnvKeys, authConfigDefaults]{
|
||||
Name: "auth",
|
||||
EnvKeys: authEnvKeys{
|
||||
ApiHost: fixedEnvOf("GOTRUE_API_HOST", "0.0.0.0"),
|
||||
ApiPort: fixedEnvOf("GOTRUE_API_PORT", "9999"),
|
||||
ApiExternalUrl: "API_EXTERNAL_URL",
|
||||
DBDriver: fixedEnvOf("GOTRUE_DB_DRIVER", "postgres"),
|
||||
DatabaseUrl: "GOTRUE_DB_DATABASE_URL",
|
||||
SiteUrl: "GOTRUE_SITE_URL",
|
||||
AdditionalRedirectURLs: stringSliceEnv{key: "GOTRUE_URI_ALLOW_LIST", separator: ","},
|
||||
DisableSignup: "GOTRUE_DISABLE_SIGNUP",
|
||||
JWTIssuer: fixedEnvOf("GOTRUE_JWT_ISSUER", "supabase"),
|
||||
JWTAdminRoles: fixedEnvOf("GOTRUE_JWT_ADMIN_ROLES", "service_role"),
|
||||
JWTAudience: fixedEnvOf("GOTRUE_JWT_AUD", "authenticated"),
|
||||
JwtDefaultGroup: fixedEnvOf("GOTRUE_JWT_DEFAULT_GROUP_NAME", "authenticated"),
|
||||
JwtExpiry: "GOTRUE_JWT_EXP",
|
||||
JwtSecret: "GOTRUE_JWT_SECRET",
|
||||
EmailSignupDisabled: "GOTRUE_EXTERNAL_EMAIL_ENABLED",
|
||||
MailerUrlPathsInvite: "MAILER_URLPATHS_INVITE",
|
||||
MailerUrlPathsConfirmation: "MAILER_URLPATHS_CONFIRMATION",
|
||||
MailerUrlPathsRecovery: "MAILER_URLPATHS_RECOVERY",
|
||||
MailerUrlPathsEmailChange: "MAILER_URLPATHS_EMAIL_CHANGE",
|
||||
AnonymousUsersEnabled: "GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED",
|
||||
},
|
||||
Defaults: authConfigDefaults{
|
||||
MailerUrlPathsInvite: "/auth/v1/verify",
|
||||
MailerUrlPathsConfirmation: "/auth/v1/verify",
|
||||
MailerUrlPathsRecovery: "/auth/v1/verify",
|
||||
MailerUrlPathsEmailChange: "/auth/v1/verify",
|
||||
APIPort: 9999,
|
||||
UID: 1000,
|
||||
GID: 1000,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -36,294 +36,22 @@ func (cfg serviceConfig[TEnvKeys, TDefaults]) ObjectMeta(obj metav1.Object) meta
|
|||
return metav1.ObjectMeta{Name: cfg.ObjectName(obj), Namespace: obj.GetNamespace()}
|
||||
}
|
||||
|
||||
type postgrestEnvKeys struct {
|
||||
Host fixedEnv
|
||||
DBUri string
|
||||
Schemas stringSliceEnv
|
||||
AnonRole stringEnv
|
||||
JWTSecret secretEnv
|
||||
UseLegacyGucs boolEnv
|
||||
ExtraSearchPath stringSliceEnv
|
||||
AppSettingsJWTSecret secretEnv
|
||||
AppSettingsJWTExpiry intEnv[int]
|
||||
AdminServerPort intEnv[int32]
|
||||
MaxRows intEnv[int]
|
||||
OpenAPIProxyURI stringEnv
|
||||
}
|
||||
|
||||
type postgrestConfigDefaults struct {
|
||||
AnonRole string
|
||||
Schemas []string
|
||||
ExtraSearchPath []string
|
||||
UID, GID int64
|
||||
ServerPort, AdminPort int32
|
||||
}
|
||||
|
||||
type authEnvKeys struct {
|
||||
ApiHost fixedEnv
|
||||
ApiPort fixedEnv
|
||||
ApiExternalUrl stringEnv
|
||||
DBDriver fixedEnv
|
||||
DatabaseUrl string
|
||||
SiteUrl stringEnv
|
||||
AdditionalRedirectURLs stringSliceEnv
|
||||
DisableSignup boolEnv
|
||||
JWTIssuer fixedEnv
|
||||
JWTAdminRoles fixedEnv
|
||||
JWTAudience fixedEnv
|
||||
JwtDefaultGroup fixedEnv
|
||||
JwtExpiry intEnv[int]
|
||||
JwtSecret secretEnv
|
||||
EmailSignupDisabled boolEnv
|
||||
MailerUrlPathsInvite stringEnv
|
||||
MailerUrlPathsConfirmation stringEnv
|
||||
MailerUrlPathsRecovery stringEnv
|
||||
MailerUrlPathsEmailChange stringEnv
|
||||
AnonymousUsersEnabled boolEnv
|
||||
}
|
||||
|
||||
type authConfigDefaults struct {
|
||||
MailerUrlPathsInvite string
|
||||
MailerUrlPathsConfirmation string
|
||||
MailerUrlPathsRecovery string
|
||||
MailerUrlPathsEmailChange string
|
||||
APIPort int32
|
||||
UID, GID int64
|
||||
}
|
||||
|
||||
type pgMetaEnvKeys struct {
|
||||
APIPort intEnv[int32]
|
||||
DBHost stringEnv
|
||||
DBPort intEnv[int]
|
||||
DBName stringEnv
|
||||
DBUser secretEnv
|
||||
DBPassword secretEnv
|
||||
}
|
||||
|
||||
type pgMetaDefaults struct {
|
||||
APIPort int32
|
||||
DBPort string
|
||||
NodeUID int64
|
||||
NodeGID int64
|
||||
}
|
||||
|
||||
type studioEnvKeys struct {
|
||||
PGMetaURL stringEnv
|
||||
DBPassword secretEnv
|
||||
ApiUrl stringEnv
|
||||
APIExternalURL stringEnv
|
||||
JwtSecret secretEnv
|
||||
AnonKey secretEnv
|
||||
ServiceKey secretEnv
|
||||
Host fixedEnv
|
||||
LogsEnabled fixedEnv
|
||||
}
|
||||
|
||||
type studioDefaults struct {
|
||||
NodeUID int64
|
||||
NodeGID int64
|
||||
APIPort int32
|
||||
}
|
||||
|
||||
type storageEnvApiKeys struct {
|
||||
AnonKey secretEnv
|
||||
ServiceKey secretEnv
|
||||
JwtSecret secretEnv
|
||||
JwtJwks secretEnv
|
||||
DatabaseDSN stringEnv
|
||||
FileSizeLimit intEnv[uint64]
|
||||
UploadFileSizeLimit intEnv[uint64]
|
||||
UploadFileSizeLimitStandard intEnv[uint64]
|
||||
StorageBackend stringEnv
|
||||
TenantID fixedEnv
|
||||
StorageS3Region stringEnv
|
||||
GlobalS3Bucket fixedEnv
|
||||
EnableImaageTransformation boolEnv
|
||||
ImgProxyURL stringEnv
|
||||
TusUrlPath fixedEnv
|
||||
S3AccessKeyId secretEnv
|
||||
S3AccessKeySecret secretEnv
|
||||
S3ProtocolPrefix fixedEnv
|
||||
S3AllowForwardedHeader boolEnv
|
||||
}
|
||||
|
||||
type storageApiDefaults struct{}
|
||||
|
||||
type envoyDefaults struct {
|
||||
ConfigKey string
|
||||
UID, GID int64
|
||||
}
|
||||
|
||||
type envoyServiceConfig struct {
|
||||
Defaults envoyDefaults
|
||||
}
|
||||
|
||||
func (envoyServiceConfig) ObjectName(obj metav1.Object) string {
|
||||
return fmt.Sprintf("%s-envoy", obj.GetName())
|
||||
}
|
||||
|
||||
type jwtDefaults struct {
|
||||
SecretKey string
|
||||
JwksKey string
|
||||
AnonKey string
|
||||
ServiceKey string
|
||||
SecretLength int
|
||||
Expiry int
|
||||
}
|
||||
|
||||
type jwtConfig struct {
|
||||
Defaults jwtDefaults
|
||||
}
|
||||
|
||||
func (jwtConfig) ObjectName(obj metav1.Object) string {
|
||||
return fmt.Sprintf("%s-jwt", obj.GetName())
|
||||
}
|
||||
|
||||
var ServiceConfig = struct {
|
||||
Postgrest serviceConfig[postgrestEnvKeys, postgrestConfigDefaults]
|
||||
Auth serviceConfig[authEnvKeys, authConfigDefaults]
|
||||
PGMeta serviceConfig[pgMetaEnvKeys, pgMetaDefaults]
|
||||
Studio serviceConfig[studioEnvKeys, studioDefaults]
|
||||
Storage serviceConfig[storageEnvApiKeys, storageApiDefaults]
|
||||
ImgProxy serviceConfig[imgProxyEnvKeys, imgProxyDefaults]
|
||||
Envoy envoyServiceConfig
|
||||
JWT jwtConfig
|
||||
}{
|
||||
Postgrest: serviceConfig[postgrestEnvKeys, postgrestConfigDefaults]{
|
||||
Name: "postgrest",
|
||||
EnvKeys: postgrestEnvKeys{
|
||||
Host: fixedEnvOf("PGRST_SERVER_HOST", "*"),
|
||||
DBUri: "PGRST_DB_URI",
|
||||
Schemas: stringSliceEnv{key: "PGRST_DB_SCHEMAS", separator: ","},
|
||||
AnonRole: "PGRST_DB_ANON_ROLE",
|
||||
JWTSecret: "PGRST_JWT_SECRET",
|
||||
UseLegacyGucs: "PGRST_DB_USE_LEGACY_GUCS",
|
||||
AppSettingsJWTSecret: "PGRST_APP_SETTINGS_JWT_SECRET",
|
||||
AppSettingsJWTExpiry: "PGRST_APP_SETTINGS_JWT_EXP",
|
||||
AdminServerPort: "PGRST_ADMIN_SERVER_PORT",
|
||||
ExtraSearchPath: stringSliceEnv{key: "PGRST_DB_EXTRA_SEARCH_PATH", separator: ","},
|
||||
MaxRows: "PGRST_DB_MAX_ROWS",
|
||||
OpenAPIProxyURI: "PGRST_OPENAPI_SERVER_PROXY_URI",
|
||||
},
|
||||
Defaults: postgrestConfigDefaults{
|
||||
AnonRole: "anon",
|
||||
Schemas: []string{"public", "graphql_public"},
|
||||
ExtraSearchPath: []string{"public", "extensions"},
|
||||
UID: 1000,
|
||||
GID: 1000,
|
||||
ServerPort: 3000,
|
||||
AdminPort: 3001,
|
||||
},
|
||||
},
|
||||
Auth: serviceConfig[authEnvKeys, authConfigDefaults]{
|
||||
Name: "auth",
|
||||
EnvKeys: authEnvKeys{
|
||||
ApiHost: fixedEnvOf("GOTRUE_API_HOST", "0.0.0.0"),
|
||||
ApiPort: fixedEnvOf("GOTRUE_API_PORT", "9999"),
|
||||
ApiExternalUrl: "API_EXTERNAL_URL",
|
||||
DBDriver: fixedEnvOf("GOTRUE_DB_DRIVER", "postgres"),
|
||||
DatabaseUrl: "GOTRUE_DB_DATABASE_URL",
|
||||
SiteUrl: "GOTRUE_SITE_URL",
|
||||
AdditionalRedirectURLs: stringSliceEnv{key: "GOTRUE_URI_ALLOW_LIST", separator: ","},
|
||||
DisableSignup: "GOTRUE_DISABLE_SIGNUP",
|
||||
JWTIssuer: fixedEnvOf("GOTRUE_JWT_ISSUER", "supabase"),
|
||||
JWTAdminRoles: fixedEnvOf("GOTRUE_JWT_ADMIN_ROLES", "service_role"),
|
||||
JWTAudience: fixedEnvOf("GOTRUE_JWT_AUD", "authenticated"),
|
||||
JwtDefaultGroup: fixedEnvOf("GOTRUE_JWT_DEFAULT_GROUP_NAME", "authenticated"),
|
||||
JwtExpiry: "GOTRUE_JWT_EXP",
|
||||
JwtSecret: "GOTRUE_JWT_SECRET",
|
||||
EmailSignupDisabled: "GOTRUE_EXTERNAL_EMAIL_ENABLED",
|
||||
MailerUrlPathsInvite: "MAILER_URLPATHS_INVITE",
|
||||
MailerUrlPathsConfirmation: "MAILER_URLPATHS_CONFIRMATION",
|
||||
MailerUrlPathsRecovery: "MAILER_URLPATHS_RECOVERY",
|
||||
MailerUrlPathsEmailChange: "MAILER_URLPATHS_EMAIL_CHANGE",
|
||||
AnonymousUsersEnabled: "GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED",
|
||||
},
|
||||
Defaults: authConfigDefaults{
|
||||
MailerUrlPathsInvite: "/auth/v1/verify",
|
||||
MailerUrlPathsConfirmation: "/auth/v1/verify",
|
||||
MailerUrlPathsRecovery: "/auth/v1/verify",
|
||||
MailerUrlPathsEmailChange: "/auth/v1/verify",
|
||||
APIPort: 9999,
|
||||
UID: 1000,
|
||||
GID: 1000,
|
||||
},
|
||||
},
|
||||
PGMeta: serviceConfig[pgMetaEnvKeys, pgMetaDefaults]{
|
||||
Name: "pg-meta",
|
||||
EnvKeys: pgMetaEnvKeys{
|
||||
APIPort: "PG_META_PORT",
|
||||
DBHost: "PG_META_DB_HOST",
|
||||
DBPort: "PG_META_DB_PORT",
|
||||
DBName: "PG_META_DB_NAME",
|
||||
DBUser: "PG_META_DB_USER",
|
||||
DBPassword: "PG_META_DB_PASSWORD",
|
||||
},
|
||||
Defaults: pgMetaDefaults{
|
||||
APIPort: 8080,
|
||||
DBPort: "5432",
|
||||
NodeUID: 1000,
|
||||
NodeGID: 1000,
|
||||
},
|
||||
},
|
||||
Studio: serviceConfig[studioEnvKeys, studioDefaults]{
|
||||
Name: "studio",
|
||||
EnvKeys: studioEnvKeys{
|
||||
PGMetaURL: "STUDIO_PG_META_URL",
|
||||
DBPassword: "POSTGRES_PASSWORD",
|
||||
ApiUrl: "SUPABASE_URL",
|
||||
APIExternalURL: "SUPABASE_PUBLIC_URL",
|
||||
JwtSecret: "AUTH_JWT_SECRET",
|
||||
AnonKey: "SUPABASE_ANON_KEY",
|
||||
ServiceKey: "SUPABASE_SERVICE_KEY",
|
||||
Host: fixedEnvOf("HOSTNAME", "0.0.0.0"),
|
||||
LogsEnabled: fixedEnvOf("NEXT_PUBLIC_ENABLE_LOGS", "true"),
|
||||
},
|
||||
Defaults: studioDefaults{
|
||||
NodeUID: 1000,
|
||||
NodeGID: 1000,
|
||||
APIPort: 3000,
|
||||
},
|
||||
},
|
||||
Storage: serviceConfig[storageEnvApiKeys, storageApiDefaults]{
|
||||
Name: "storage-api",
|
||||
EnvKeys: storageEnvApiKeys{
|
||||
AnonKey: "ANON_KEY",
|
||||
ServiceKey: "SERVICE_KEY",
|
||||
JwtSecret: "AUTH_JWT_SECRET",
|
||||
JwtJwks: "AUTH_JWT_JWKS",
|
||||
StorageBackend: "STORAGE_BACKEND",
|
||||
DatabaseDSN: "DATABASE_URL",
|
||||
FileSizeLimit: "FILE_SIZE_LIMIT",
|
||||
UploadFileSizeLimit: "UPLOAD_FILE_SIZE_LIMIT",
|
||||
UploadFileSizeLimitStandard: "UPLOAD_FILE_SIZE_LIMIT_STANDARD",
|
||||
TenantID: fixedEnvOf("TENANT_ID", "stub"),
|
||||
StorageS3Region: "STORAGE_S3_REGION",
|
||||
GlobalS3Bucket: fixedEnvOf("GLOBAL_S3_BUCKET", "stub"),
|
||||
EnableImaageTransformation: "ENABLE_IMAGE_TRANSFORMATION",
|
||||
ImgProxyURL: "IMGPROXY_URL",
|
||||
TusUrlPath: fixedEnvOf("TUS_URL_PATH", "/storage/v1/upload/resumable"),
|
||||
S3AccessKeyId: "S3_PROTOCOL_ACCESS_KEY_ID",
|
||||
S3AccessKeySecret: "S3_PROTOCOL_ACCESS_KEY_SECRET",
|
||||
S3ProtocolPrefix: fixedEnvOf("S3_PROTOCOL_PREFIX", "/storage/v1"),
|
||||
S3AllowForwardedHeader: "S3_ALLOW_FORWARDED_HEADER",
|
||||
},
|
||||
Defaults: storageApiDefaults{},
|
||||
},
|
||||
Envoy: envoyServiceConfig{
|
||||
Defaults: envoyDefaults{
|
||||
ConfigKey: "config.yaml",
|
||||
UID: 65532,
|
||||
GID: 65532,
|
||||
},
|
||||
},
|
||||
JWT: jwtConfig{
|
||||
Defaults: jwtDefaults{
|
||||
SecretKey: "secret",
|
||||
JwksKey: "jwks.json",
|
||||
AnonKey: "anon_key",
|
||||
ServiceKey: "service_key",
|
||||
SecretLength: 40,
|
||||
Expiry: 3600,
|
||||
},
|
||||
},
|
||||
Postgrest: postgrestServiceConfig(),
|
||||
Auth: authServiceConfig(),
|
||||
PGMeta: pgMetaServiceConfig(),
|
||||
Studio: studioServiceConfig(),
|
||||
Storage: storageServiceConfig(),
|
||||
ImgProxy: imgProxyServiceConfig(),
|
||||
Envoy: newEnvoyServiceConfig(),
|
||||
JWT: newJwtConfig(),
|
||||
}
|
||||
|
|
46
internal/supabase/envoy.go
Normal file
46
internal/supabase/envoy.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright 2025 Peter Kurfer.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package supabase
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func newEnvoyServiceConfig() envoyServiceConfig {
|
||||
return envoyServiceConfig{
|
||||
Defaults: envoyDefaults{
|
||||
ConfigKey: "config.yaml",
|
||||
UID: 65532,
|
||||
GID: 65532,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type envoyDefaults struct {
|
||||
ConfigKey string
|
||||
UID, GID int64
|
||||
}
|
||||
|
||||
type envoyServiceConfig struct {
|
||||
Defaults envoyDefaults
|
||||
}
|
||||
|
||||
func (envoyServiceConfig) ObjectName(obj metav1.Object) string {
|
||||
return fmt.Sprintf("%s-envoy", obj.GetName())
|
||||
}
|
48
internal/supabase/img_proxy.go
Normal file
48
internal/supabase/img_proxy.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Copyright 2025 Peter Kurfer.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package supabase
|
||||
|
||||
type imgProxyEnvKeys struct {
|
||||
Bind fixedEnv
|
||||
LocalFileSystemRoot stringEnv
|
||||
UseETag fixedEnv
|
||||
EnableWebPDetection boolEnv
|
||||
}
|
||||
|
||||
type imgProxyDefaults struct {
|
||||
ApiPort int32
|
||||
ApiPortName string
|
||||
UID, GID int64
|
||||
}
|
||||
|
||||
func imgProxyServiceConfig() serviceConfig[imgProxyEnvKeys, imgProxyDefaults] {
|
||||
return serviceConfig[imgProxyEnvKeys, imgProxyDefaults]{
|
||||
Name: "imgproxy",
|
||||
EnvKeys: imgProxyEnvKeys{
|
||||
Bind: fixedEnvOf("IMGPROXY_BIND", ":5001"),
|
||||
LocalFileSystemRoot: "IMGPROXY_LOCAL_FILESYSTEM_ROOT",
|
||||
UseETag: fixedEnvOf("IMGPROXY_USE_ETAG", "true"),
|
||||
EnableWebPDetection: "IMGPROXY_ENABLE_WEBP_DETECTION",
|
||||
},
|
||||
Defaults: imgProxyDefaults{
|
||||
ApiPort: 5001,
|
||||
ApiPortName: "api",
|
||||
UID: 999,
|
||||
GID: 999,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -19,8 +19,41 @@ package supabase
|
|||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type jwtDefaults struct {
|
||||
SecretKey string
|
||||
JwksKey string
|
||||
AnonKey string
|
||||
ServiceKey string
|
||||
SecretLength int
|
||||
Expiry int
|
||||
}
|
||||
|
||||
type jwtConfig struct {
|
||||
Defaults jwtDefaults
|
||||
}
|
||||
|
||||
func newJwtConfig() jwtConfig {
|
||||
return jwtConfig{
|
||||
Defaults: jwtDefaults{
|
||||
SecretKey: "secret",
|
||||
JwksKey: "jwks.json",
|
||||
AnonKey: "anon_key",
|
||||
ServiceKey: "service_key",
|
||||
SecretLength: 40,
|
||||
Expiry: 3600,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (jwtConfig) ObjectName(obj metav1.Object) string {
|
||||
return fmt.Sprintf("%s-jwt", obj.GetName())
|
||||
}
|
||||
|
||||
func RandomJWTSecret() ([]byte, error) {
|
||||
jwtSecretBytes := make([]byte, ServiceConfig.JWT.Defaults.SecretLength)
|
||||
|
||||
|
|
53
internal/supabase/pg_meta.go
Normal file
53
internal/supabase/pg_meta.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright 2025 Peter Kurfer.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package supabase
|
||||
|
||||
type pgMetaEnvKeys struct {
|
||||
APIPort intEnv[int32]
|
||||
DBHost stringEnv
|
||||
DBPort intEnv[int]
|
||||
DBName stringEnv
|
||||
DBUser secretEnv
|
||||
DBPassword secretEnv
|
||||
}
|
||||
|
||||
type pgMetaDefaults struct {
|
||||
APIPort int32
|
||||
DBPort string
|
||||
NodeUID int64
|
||||
NodeGID int64
|
||||
}
|
||||
|
||||
func pgMetaServiceConfig() serviceConfig[pgMetaEnvKeys, pgMetaDefaults] {
|
||||
return serviceConfig[pgMetaEnvKeys, pgMetaDefaults]{
|
||||
Name: "pg-meta",
|
||||
EnvKeys: pgMetaEnvKeys{
|
||||
APIPort: "PG_META_PORT",
|
||||
DBHost: "PG_META_DB_HOST",
|
||||
DBPort: "PG_META_DB_PORT",
|
||||
DBName: "PG_META_DB_NAME",
|
||||
DBUser: "PG_META_DB_USER",
|
||||
DBPassword: "PG_META_DB_PASSWORD",
|
||||
},
|
||||
Defaults: pgMetaDefaults{
|
||||
APIPort: 8080,
|
||||
DBPort: "5432",
|
||||
NodeUID: 1000,
|
||||
NodeGID: 1000,
|
||||
},
|
||||
}
|
||||
}
|
72
internal/supabase/postgrest.go
Normal file
72
internal/supabase/postgrest.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Copyright 2025 Peter Kurfer.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package supabase
|
||||
|
||||
type postgrestEnvKeys struct {
|
||||
Host fixedEnv
|
||||
DBUri string
|
||||
Schemas stringSliceEnv
|
||||
AnonRole stringEnv
|
||||
JWTSecret secretEnv
|
||||
UseLegacyGucs boolEnv
|
||||
ExtraSearchPath stringSliceEnv
|
||||
AppSettingsJWTSecret secretEnv
|
||||
AppSettingsJWTExpiry intEnv[int]
|
||||
AdminServerPort intEnv[int32]
|
||||
MaxRows intEnv[int]
|
||||
OpenAPIProxyURI stringEnv
|
||||
}
|
||||
|
||||
type postgrestConfigDefaults struct {
|
||||
AnonRole string
|
||||
Schemas []string
|
||||
ExtraSearchPath []string
|
||||
UID, GID int64
|
||||
ServerPort, AdminPort int32
|
||||
ServerPortName, AdminPortName string
|
||||
}
|
||||
|
||||
func postgrestServiceConfig() serviceConfig[postgrestEnvKeys, postgrestConfigDefaults] {
|
||||
return serviceConfig[postgrestEnvKeys, postgrestConfigDefaults]{
|
||||
Name: "postgrest",
|
||||
EnvKeys: postgrestEnvKeys{
|
||||
Host: fixedEnvOf("PGRST_SERVER_HOST", "*"),
|
||||
DBUri: "PGRST_DB_URI",
|
||||
Schemas: stringSliceEnv{key: "PGRST_DB_SCHEMAS", separator: ","},
|
||||
AnonRole: "PGRST_DB_ANON_ROLE",
|
||||
JWTSecret: "PGRST_JWT_SECRET",
|
||||
UseLegacyGucs: "PGRST_DB_USE_LEGACY_GUCS",
|
||||
AppSettingsJWTSecret: "PGRST_APP_SETTINGS_JWT_SECRET",
|
||||
AppSettingsJWTExpiry: "PGRST_APP_SETTINGS_JWT_EXP",
|
||||
AdminServerPort: "PGRST_ADMIN_SERVER_PORT",
|
||||
ExtraSearchPath: stringSliceEnv{key: "PGRST_DB_EXTRA_SEARCH_PATH", separator: ","},
|
||||
MaxRows: "PGRST_DB_MAX_ROWS",
|
||||
OpenAPIProxyURI: "PGRST_OPENAPI_SERVER_PROXY_URI",
|
||||
},
|
||||
Defaults: postgrestConfigDefaults{
|
||||
AnonRole: "anon",
|
||||
Schemas: []string{"public", "graphql_public"},
|
||||
ExtraSearchPath: []string{"public", "extensions"},
|
||||
UID: 1000,
|
||||
GID: 1000,
|
||||
ServerPort: 3000,
|
||||
AdminPort: 3001,
|
||||
ServerPortName: "rest",
|
||||
AdminPortName: "admin",
|
||||
},
|
||||
}
|
||||
}
|
92
internal/supabase/storage.go
Normal file
92
internal/supabase/storage.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
Copyright 2025 Peter Kurfer.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package supabase
|
||||
|
||||
type storageEnvApiKeys struct {
|
||||
AnonKey secretEnv
|
||||
ServiceKey secretEnv
|
||||
JwtSecret secretEnv
|
||||
JwtJwks secretEnv
|
||||
DatabaseDSN stringEnv
|
||||
FileSizeLimit intEnv[uint64]
|
||||
UploadFileSizeLimit intEnv[uint64]
|
||||
UploadFileSizeLimitStandard intEnv[uint64]
|
||||
StorageBackend stringEnv
|
||||
FileStorageBackendPath stringEnv
|
||||
FileStorageRegion fixedEnv
|
||||
TenantID fixedEnv
|
||||
StorageS3Bucket stringEnv
|
||||
StorageS3MaxSockets intEnv[uint8]
|
||||
StorageS3Endpoint stringEnv
|
||||
StorageS3ForcePathStyle boolEnv
|
||||
StorageS3Region stringEnv
|
||||
StorageS3AccessKeyId secretEnv
|
||||
StorageS3AccessSecretKey secretEnv
|
||||
EnableImaageTransformation boolEnv
|
||||
ImgProxyURL stringEnv
|
||||
TusUrlPath fixedEnv
|
||||
S3ProtocolAccessKeyId secretEnv
|
||||
S3ProtocolAccessKeySecret secretEnv
|
||||
S3ProtocolAllowForwardedHeader boolEnv
|
||||
S3ProtocolPrefix fixedEnv
|
||||
}
|
||||
|
||||
type storageApiDefaults struct {
|
||||
ApiPort int32
|
||||
ApiPortName string
|
||||
UID, GID int64
|
||||
}
|
||||
|
||||
func storageServiceConfig() serviceConfig[storageEnvApiKeys, storageApiDefaults] {
|
||||
return serviceConfig[storageEnvApiKeys, storageApiDefaults]{
|
||||
Name: "storage-api",
|
||||
EnvKeys: storageEnvApiKeys{
|
||||
AnonKey: "ANON_KEY",
|
||||
ServiceKey: "SERVICE_KEY",
|
||||
JwtSecret: "AUTH_JWT_SECRET",
|
||||
JwtJwks: "AUTH_JWT_JWKS",
|
||||
StorageBackend: "STORAGE_BACKEND",
|
||||
FileStorageBackendPath: "FILE_STORAGE_BACKEND_PATH",
|
||||
FileStorageRegion: fixedEnvOf("REGION", "stub"),
|
||||
DatabaseDSN: "DATABASE_URL",
|
||||
FileSizeLimit: "FILE_SIZE_LIMIT",
|
||||
UploadFileSizeLimit: "UPLOAD_FILE_SIZE_LIMIT",
|
||||
UploadFileSizeLimitStandard: "UPLOAD_FILE_SIZE_LIMIT_STANDARD",
|
||||
TenantID: fixedEnvOf("TENANT_ID", "stub"),
|
||||
StorageS3Bucket: "STORAGE_S3_BUCKET",
|
||||
StorageS3MaxSockets: "STORAGE_S3_MAX_SOCKETS",
|
||||
StorageS3Endpoint: "STORAGE_S3_ENDPOINT",
|
||||
StorageS3ForcePathStyle: "STORAGE_S3_FORCE_PATH_STYLE",
|
||||
StorageS3Region: "STORAGE_S3_REGION",
|
||||
StorageS3AccessKeyId: "AWS_ACCESS_KEY_ID",
|
||||
StorageS3AccessSecretKey: "AWS_SECRET_ACCESS_KEY",
|
||||
EnableImaageTransformation: "ENABLE_IMAGE_TRANSFORMATION",
|
||||
ImgProxyURL: "IMGPROXY_URL",
|
||||
TusUrlPath: fixedEnvOf("TUS_URL_PATH", "/storage/v1/upload/resumable"),
|
||||
S3ProtocolAccessKeyId: "S3_PROTOCOL_ACCESS_KEY_ID",
|
||||
S3ProtocolAccessKeySecret: "S3_PROTOCOL_ACCESS_KEY_SECRET",
|
||||
S3ProtocolPrefix: fixedEnvOf("S3_PROTOCOL_PREFIX", "/storage/v1"),
|
||||
S3ProtocolAllowForwardedHeader: "S3_ALLOW_FORWARDED_HEADER",
|
||||
},
|
||||
Defaults: storageApiDefaults{
|
||||
ApiPort: 5000,
|
||||
ApiPortName: "api",
|
||||
UID: 1000,
|
||||
GID: 1000,
|
||||
},
|
||||
}
|
||||
}
|
57
internal/supabase/studio.go
Normal file
57
internal/supabase/studio.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
Copyright 2025 Peter Kurfer.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package supabase
|
||||
|
||||
type studioEnvKeys struct {
|
||||
PGMetaURL stringEnv
|
||||
DBPassword secretEnv
|
||||
ApiUrl stringEnv
|
||||
APIExternalURL stringEnv
|
||||
JwtSecret secretEnv
|
||||
AnonKey secretEnv
|
||||
ServiceKey secretEnv
|
||||
Host fixedEnv
|
||||
LogsEnabled fixedEnv
|
||||
}
|
||||
|
||||
type studioDefaults struct {
|
||||
NodeUID int64
|
||||
NodeGID int64
|
||||
APIPort int32
|
||||
}
|
||||
|
||||
func studioServiceConfig() serviceConfig[studioEnvKeys, studioDefaults] {
|
||||
return serviceConfig[studioEnvKeys, studioDefaults]{
|
||||
Name: "studio",
|
||||
EnvKeys: studioEnvKeys{
|
||||
PGMetaURL: "STUDIO_PG_META_URL",
|
||||
DBPassword: "POSTGRES_PASSWORD",
|
||||
ApiUrl: "SUPABASE_URL",
|
||||
APIExternalURL: "SUPABASE_PUBLIC_URL",
|
||||
JwtSecret: "AUTH_JWT_SECRET",
|
||||
AnonKey: "SUPABASE_ANON_KEY",
|
||||
ServiceKey: "SUPABASE_SERVICE_KEY",
|
||||
Host: fixedEnvOf("HOSTNAME", "0.0.0.0"),
|
||||
LogsEnabled: fixedEnvOf("NEXT_PUBLIC_ENABLE_LOGS", "true"),
|
||||
},
|
||||
Defaults: studioDefaults{
|
||||
NodeUID: 1000,
|
||||
NodeGID: 1000,
|
||||
APIPort: 3000,
|
||||
},
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue