package config import ( "context" "fmt" "os" "github.com/go-errors/errors" v1API "github.com/supabase/cli/pkg/api" ) type ConfigUpdater struct { client v1API.ClientWithResponses } func NewConfigUpdater(client v1API.ClientWithResponses) ConfigUpdater { return ConfigUpdater{client: client} } func (u *ConfigUpdater) UpdateRemoteConfig(ctx context.Context, remote baseConfig, filter ...func(string) bool) error { if err := u.UpdateApiConfig(ctx, remote.ProjectId, remote.Api, filter...); err != nil { return err } if err := u.UpdateDbConfig(ctx, remote.ProjectId, remote.Db, filter...); err != nil { return err } if err := u.UpdateAuthConfig(ctx, remote.ProjectId, remote.Auth, filter...); err != nil { return err } if err := u.UpdateStorageConfig(ctx, remote.ProjectId, remote.Storage, filter...); err != nil { return err } if err := u.UpdateExperimentalConfig(ctx, remote.ProjectId, remote.Experimental, filter...); err != nil { return err } return nil } func (u *ConfigUpdater) UpdateApiConfig(ctx context.Context, projectRef string, c api, filter ...func(string) bool) error { apiConfig, err := u.client.V1GetPostgrestServiceConfigWithResponse(ctx, projectRef) if err != nil { return errors.Errorf("failed to read API config: %w", err) } else if apiConfig.JSON200 == nil { return errors.Errorf("unexpected status %d: %s", apiConfig.StatusCode(), string(apiConfig.Body)) } apiDiff, err := c.DiffWithRemote(*apiConfig.JSON200) if err != nil { return err } else if len(apiDiff) == 0 { fmt.Fprintln(os.Stderr, "Remote API config is up to date.") return nil } fmt.Fprintln(os.Stderr, "Updating API service with config:", string(apiDiff)) for _, keep := range filter { if !keep("api") { return nil } } if resp, err := u.client.V1UpdatePostgrestServiceConfigWithResponse(ctx, projectRef, c.ToUpdatePostgrestConfigBody()); err != nil { return errors.Errorf("failed to update API config: %w", err) } else if resp.JSON200 == nil { return errors.Errorf("unexpected status %d: %s", resp.StatusCode(), string(resp.Body)) } return nil } func (u *ConfigUpdater) UpdateDbSettingsConfig(ctx context.Context, projectRef string, s settings, filter ...func(string) bool) error { dbConfig, err := u.client.V1GetPostgresConfigWithResponse(ctx, projectRef) if err != nil { return errors.Errorf("failed to read DB config: %w", err) } else if dbConfig.JSON200 == nil { return errors.Errorf("unexpected status %d: %s", dbConfig.StatusCode(), string(dbConfig.Body)) } dbDiff, err := s.DiffWithRemote(*dbConfig.JSON200) if err != nil { return err } else if len(dbDiff) == 0 { fmt.Fprintln(os.Stderr, "Remote DB config is up to date.") return nil } fmt.Fprintln(os.Stderr, "Updating DB service with config:", string(dbDiff)) for _, keep := range filter { if !keep("db") { return nil } } updateBody := s.ToUpdatePostgresConfigBody() if resp, err := u.client.V1UpdatePostgresConfigWithResponse(ctx, projectRef, updateBody); err != nil { return errors.Errorf("failed to update DB config: %w", err) } else if resp.JSON200 == nil { return errors.Errorf("unexpected status %d: %s", resp.StatusCode(), string(resp.Body)) } return nil } func (u *ConfigUpdater) UpdateDbConfig(ctx context.Context, projectRef string, c db, filter ...func(string) bool) error { if err := u.UpdateDbSettingsConfig(ctx, projectRef, c.Settings, filter...); err != nil { return err } return nil } func (u *ConfigUpdater) UpdateAuthConfig(ctx context.Context, projectRef string, c auth, filter ...func(string) bool) error { if !c.Enabled { return nil } authConfig, err := u.client.V1GetAuthServiceConfigWithResponse(ctx, projectRef) if err != nil { return errors.Errorf("failed to read Auth config: %w", err) } else if authConfig.JSON200 == nil { return errors.Errorf("unexpected status %d: %s", authConfig.StatusCode(), string(authConfig.Body)) } authDiff, err := c.DiffWithRemote(*authConfig.JSON200) if err != nil { return err } else if len(authDiff) == 0 { fmt.Fprintln(os.Stderr, "Remote Auth config is up to date.") return nil } fmt.Fprintln(os.Stderr, "Updating Auth service with config:", string(authDiff)) for _, keep := range filter { if !keep("auth") { return nil } } if resp, err := u.client.V1UpdateAuthServiceConfigWithResponse(ctx, projectRef, c.ToUpdateAuthConfigBody()); err != nil { return errors.Errorf("failed to update Auth config: %w", err) } else if status := resp.StatusCode(); status < 200 || status >= 300 { return errors.Errorf("unexpected status %d: %s", status, string(resp.Body)) } return nil } func (u *ConfigUpdater) UpdateStorageConfig(ctx context.Context, projectRef string, c storage, filter ...func(string) bool) error { if !c.Enabled { return nil } storageConfig, err := u.client.V1GetStorageConfigWithResponse(ctx, projectRef) if err != nil { return errors.Errorf("failed to read Storage config: %w", err) } else if storageConfig.JSON200 == nil { return errors.Errorf("unexpected status %d: %s", storageConfig.StatusCode(), string(storageConfig.Body)) } storageDiff, err := c.DiffWithRemote(*storageConfig.JSON200) if err != nil { return err } else if len(storageDiff) == 0 { fmt.Fprintln(os.Stderr, "Remote Storage config is up to date.") return nil } fmt.Fprintln(os.Stderr, "Updating Storage service with config:", string(storageDiff)) for _, keep := range filter { if !keep("storage") { return nil } } if resp, err := u.client.V1UpdateStorageConfigWithResponse(ctx, projectRef, c.ToUpdateStorageConfigBody()); err != nil { return errors.Errorf("failed to update Storage config: %w", err) } else if status := resp.StatusCode(); status < 200 || status >= 300 { return errors.Errorf("unexpected status %d: %s", status, string(resp.Body)) } return nil } func (u *ConfigUpdater) UpdateExperimentalConfig(ctx context.Context, projectRef string, exp experimental, filter ...func(string) bool) error { if exp.Webhooks != nil && exp.Webhooks.Enabled { fmt.Fprintln(os.Stderr, "Enabling webhooks for project:", projectRef) for _, keep := range filter { if !keep("webhooks") { return nil } } if resp, err := u.client.V1EnableDatabaseWebhookWithResponse(ctx, projectRef); err != nil { return errors.Errorf("failed to enable webhooks: %w", err) } else if status := resp.StatusCode(); status < 200 || status >= 300 { return errors.Errorf("unexpected enable webhook status %d: %s", status, string(resp.Body)) } } return nil }