mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-23 16:20:29 +00:00
v7
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
package database
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/cmd/configure/bla4"
|
||||
)
|
||||
|
||||
type Connector interface {
|
||||
Connect(ctx context.Context) (Pool, error)
|
||||
bla4.Configurer
|
||||
}
|
||||
|
@@ -5,23 +5,20 @@ import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/cmd/config"
|
||||
"github.com/zitadel/zitadel/backend/cmd/configure"
|
||||
"github.com/zitadel/zitadel/backend/cmd/configure/bla"
|
||||
"github.com/zitadel/zitadel/backend/cmd/configure/bla4"
|
||||
"github.com/zitadel/zitadel/backend/storage/database"
|
||||
"github.com/zitadel/zitadel/backend/storage/database/dialect/gosql"
|
||||
"github.com/zitadel/zitadel/backend/storage/database/dialect/postgres"
|
||||
)
|
||||
|
||||
type Hook struct {
|
||||
Match func(string) bool
|
||||
Decode func(name string, config any) (database.Connector, error)
|
||||
Decode func(config any) (database.Connector, error)
|
||||
Name string
|
||||
Field configure.Updater
|
||||
Constructor func() any
|
||||
Constructor func() database.Connector
|
||||
}
|
||||
|
||||
var hooks = []Hook{
|
||||
@@ -29,24 +26,65 @@ var hooks = []Hook{
|
||||
Match: postgres.NameMatcher,
|
||||
Decode: postgres.DecodeConfig,
|
||||
Name: postgres.Name,
|
||||
Field: postgres.Field,
|
||||
Constructor: func() any { return new(postgres.Config) },
|
||||
},
|
||||
{
|
||||
Match: gosql.NameMatcher,
|
||||
Decode: gosql.DecodeConfig,
|
||||
Name: gosql.Name,
|
||||
Field: gosql.Field,
|
||||
Constructor: func() any { return new(gosql.Config) },
|
||||
Constructor: func() database.Connector { return new(postgres.Config) },
|
||||
},
|
||||
// {
|
||||
// Match: gosql.NameMatcher,
|
||||
// Decode: gosql.DecodeConfig,
|
||||
// Name: gosql.Name,
|
||||
// Constructor: func() database.Connector { return new(gosql.Config) },
|
||||
// },
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Dialects dialects `mapstructure:",remain"`
|
||||
Dialects map[string]any `mapstructure:",remain" yaml:",inline"`
|
||||
|
||||
connector database.Connector
|
||||
}
|
||||
|
||||
// Configure implements [configure.Configurer].
|
||||
func (c *Config) Configure() (any, error) {
|
||||
possibilities := make([]string, len(hooks))
|
||||
var cursor int
|
||||
for i, hook := range hooks {
|
||||
if _, ok := c.Dialects[hook.Name]; ok {
|
||||
cursor = i
|
||||
}
|
||||
possibilities[i] = hook.Name
|
||||
}
|
||||
|
||||
prompt := promptui.Select{
|
||||
Label: "Select a dialect",
|
||||
Items: possibilities,
|
||||
CursorPos: cursor,
|
||||
}
|
||||
i, _, err := prompt.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var config bla4.Configurer
|
||||
|
||||
if dialect, ok := c.Dialects[hooks[i].Name]; ok {
|
||||
config, err = hooks[i].Decode(dialect)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
clear(c.Dialects)
|
||||
config = hooks[i].Constructor()
|
||||
}
|
||||
if c.Dialects == nil {
|
||||
c.Dialects = make(map[string]any)
|
||||
}
|
||||
c.Dialects[hooks[i].Name], err = config.Configure()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c Config) Connect(ctx context.Context) (database.Pool, error) {
|
||||
if len(c.Dialects) != 1 {
|
||||
return nil, errors.New("Exactly one dialect must be configured")
|
||||
@@ -62,12 +100,6 @@ func (c Config) Hooks() []viper.DecoderConfigOption {
|
||||
}
|
||||
}
|
||||
|
||||
// var _ configure.StructUpdater = (*Config)(nil)
|
||||
|
||||
func (c Config) Configure(v *viper.Viper, currentVersion config.Version) Config {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Config) decodeDialect() error {
|
||||
for _, hook := range hooks {
|
||||
for name, config := range c.Dialects {
|
||||
@@ -75,7 +107,7 @@ func (c *Config) decodeDialect() error {
|
||||
continue
|
||||
}
|
||||
|
||||
connector, err := hook.Decode(name, config)
|
||||
connector, err := hook.Decode(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -87,7 +119,7 @@ func (c *Config) decodeDialect() error {
|
||||
return errors.New("no dialect found")
|
||||
}
|
||||
|
||||
func decodeHook(from, to reflect.Value) (_ interface{}, err error) {
|
||||
func decodeHook(from, to reflect.Value) (_ any, err error) {
|
||||
if to.Type() != reflect.TypeOf(Config{}) {
|
||||
return from.Interface(), nil
|
||||
}
|
||||
@@ -103,21 +135,3 @@ func decodeHook(from, to reflect.Value) (_ interface{}, err error) {
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
type dialects map[string]any
|
||||
|
||||
// ConfigForIndex implements [bla.OneOfField].
|
||||
func (d dialects) ConfigForIndex(i int) any {
|
||||
return hooks[i].Constructor()
|
||||
}
|
||||
|
||||
// Possibilities implements [bla.OneOfField].
|
||||
func (d dialects) Possibilities() []string {
|
||||
possibilities := make([]string, len(hooks))
|
||||
for i, hook := range hooks {
|
||||
possibilities[i] = hook.Name
|
||||
}
|
||||
return possibilities
|
||||
}
|
||||
|
||||
var _ bla.OneOfField = (dialects)(nil)
|
||||
|
@@ -6,25 +6,13 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/cmd/configure"
|
||||
"github.com/zitadel/zitadel/backend/storage/database"
|
||||
)
|
||||
|
||||
var (
|
||||
_ database.Connector = (*Config)(nil)
|
||||
|
||||
Name = "gosql"
|
||||
Field = &configure.Field[string]{
|
||||
Description: "Connection string",
|
||||
Version: semver.MustParse("v3"),
|
||||
Validate: func(s string) error {
|
||||
_, err := pgxpool.ParseConfig(s)
|
||||
return err
|
||||
},
|
||||
}
|
||||
Name = "gosql"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@@ -6,117 +6,70 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/cmd/configure"
|
||||
"github.com/zitadel/zitadel/backend/cmd/configure/bla"
|
||||
"github.com/zitadel/zitadel/backend/storage/database"
|
||||
)
|
||||
|
||||
var (
|
||||
_ database.Connector = (*Config)(nil)
|
||||
Name = "postgres"
|
||||
|
||||
Field = &configure.OneOf{
|
||||
Description: "Configuring postgres using one of the following options",
|
||||
SubFields: []configure.Updater{
|
||||
&configure.Field[string]{
|
||||
Description: "Connection string",
|
||||
Version: semver.MustParse("v3"),
|
||||
Validate: func(s string) error {
|
||||
_, err := pgxpool.ParseConfig(s)
|
||||
return err
|
||||
},
|
||||
},
|
||||
&configure.Struct{
|
||||
Description: "Configuration for the connection",
|
||||
SubFields: []configure.Updater{
|
||||
&configure.Field[string]{
|
||||
FieldName: "host",
|
||||
Value: "localhost",
|
||||
Description: "The host to connect to",
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
&configure.Field[uint32]{
|
||||
FieldName: "port",
|
||||
Value: 5432,
|
||||
Description: "The port to connect to",
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
&configure.Field[string]{
|
||||
FieldName: "database",
|
||||
Value: "zitadel",
|
||||
Description: "The database to connect to",
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
&configure.Field[string]{
|
||||
FieldName: "user",
|
||||
Description: "The user to connect as",
|
||||
Value: "zitadel",
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
&configure.Field[string]{
|
||||
FieldName: "password",
|
||||
Description: "The password to connect with",
|
||||
Version: semver.MustParse("3"),
|
||||
HideInput: true,
|
||||
},
|
||||
&configure.OneOf{
|
||||
FieldName: "sslMode",
|
||||
Description: "The SSL mode to use",
|
||||
SubFields: []configure.Updater{
|
||||
&configure.Constant[string]{
|
||||
Description: "Disable",
|
||||
Constant: "disable",
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
&configure.Constant[string]{
|
||||
Description: "Require",
|
||||
Constant: "require",
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
&configure.Constant[string]{
|
||||
Description: "Verify CA",
|
||||
Constant: "verify-ca",
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
&configure.Constant[string]{
|
||||
Description: "Verify Full",
|
||||
Constant: "verify-full",
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type Config struct{ pgxpool.Config }
|
||||
type Config struct {
|
||||
config *pgxpool.Config
|
||||
|
||||
// ConfigForIndex implements bla.OneOfField.
|
||||
func (c Config) ConfigForIndex(i int) any {
|
||||
switch i {
|
||||
case 0:
|
||||
return new(string)
|
||||
case 1:
|
||||
return &c.Config
|
||||
// Host string
|
||||
// Port int32
|
||||
// Database string
|
||||
// EventPushConnRatio float64
|
||||
// MaxOpenConns uint32
|
||||
// MaxIdleConns uint32
|
||||
// MaxConnLifetime time.Duration
|
||||
// MaxConnIdleTime time.Duration
|
||||
// User User
|
||||
// Admin AdminUser
|
||||
// // Additional options to be appended as options=<Options>
|
||||
// // The value will be taken as is. Multiple options are space separated.
|
||||
// Options string
|
||||
}
|
||||
|
||||
// Configure implements bla3.Custom.
|
||||
func (c *Config) Configure() (value any, err error) {
|
||||
typeSelect := promptui.Select{
|
||||
Label: "Configure the database connection",
|
||||
Items: []string{"connection string", "fields"},
|
||||
}
|
||||
i, _, err := typeSelect.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i > 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Possibilities implements bla.OneOfField.
|
||||
func (c Config) Possibilities() []string {
|
||||
return []string{"connection string", "fields"}
|
||||
}
|
||||
if c.config == nil {
|
||||
c.config, _ = pgxpool.ParseConfig("host=localhost user=zitadel password= dbname=zitadel sslmode=disable")
|
||||
}
|
||||
|
||||
var _ bla.OneOfField = (*Config)(nil)
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Connection string",
|
||||
Default: c.config.ConnString(),
|
||||
AllowEdit: c.config.ConnString() != "",
|
||||
Validate: func(input string) error {
|
||||
_, err := pgxpool.ParseConfig(input)
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
return prompt.Run()
|
||||
}
|
||||
|
||||
// Connect implements [database.Connector].
|
||||
func (c *Config) Connect(ctx context.Context) (database.Pool, error) {
|
||||
pool, err := pgxpool.NewWithConfig(ctx, &c.Config)
|
||||
pool, err := pgxpool.NewWithConfig(ctx, c.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -130,17 +83,29 @@ func NameMatcher(name string) bool {
|
||||
return slices.Contains([]string{"postgres", "pg"}, strings.ToLower(name))
|
||||
}
|
||||
|
||||
func DecodeConfig(_ string, config any) (database.Connector, error) {
|
||||
switch c := config.(type) {
|
||||
func DecodeConfig(input any) (database.Connector, error) {
|
||||
switch c := input.(type) {
|
||||
case string:
|
||||
config, err := pgxpool.ParseConfig(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Config{Config: *config}, nil
|
||||
return &Config{config: config}, nil
|
||||
case map[string]any:
|
||||
connector := new(Config)
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
||||
WeaklyTypedInput: true,
|
||||
Result: connector,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = decoder.Decode(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Config{
|
||||
Config: pgxpool.Config{},
|
||||
config: &pgxpool.Config{},
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.New("invalid configuration")
|
||||
|
Reference in New Issue
Block a user