mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 17:57:33 +00:00
works
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
package configure
|
||||
|
||||
import (
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/zitadel/zitadel/backend/cmd/config"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -70,24 +70,21 @@ func (s sub) Fields() []Updater {
|
||||
return []Updater{
|
||||
Field[string]{
|
||||
FieldName: "f1",
|
||||
Value: &s.F1,
|
||||
Default: "",
|
||||
Value: "",
|
||||
Description: "field 1",
|
||||
Version: config.V3,
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
Field[int]{
|
||||
FieldName: "f2",
|
||||
Value: &s.F2,
|
||||
Default: 0,
|
||||
Value: 0,
|
||||
Description: "field 2",
|
||||
Version: config.V3,
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
Field[*bool]{
|
||||
FieldName: "f3",
|
||||
Value: &s.F3,
|
||||
Default: nil,
|
||||
Value: nil,
|
||||
Description: "field 3",
|
||||
Version: config.V3,
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -101,10 +98,9 @@ func (t test) Fields() []Updater {
|
||||
return []Updater{
|
||||
Field[string]{
|
||||
FieldName: "f1",
|
||||
Value: &t.F1,
|
||||
Default: "",
|
||||
Value: "",
|
||||
Description: "field 1",
|
||||
Version: config.V3,
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
Struct{
|
||||
FieldName: "sub",
|
||||
|
@@ -1,246 +0,0 @@
|
||||
package configure
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/zitadel/zitadel/backend/cmd/config"
|
||||
)
|
||||
|
||||
func Update(name, description string, fields []Updater) func(cmd *cobra.Command, args []string) {
|
||||
return func(cmd *cobra.Command, args []string) {
|
||||
u := Struct{
|
||||
FieldName: name,
|
||||
Description: description,
|
||||
SubFields: fields,
|
||||
}
|
||||
fields := viper.AllSettings()
|
||||
updateStruct(u, fields, 1)
|
||||
|
||||
fmt.Println("Using config file:", viper.ConfigFileUsed(), "fields:", fields)
|
||||
// viper.MergeConfigMap(fields)
|
||||
// if err := viper.WriteConfig(); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
func updateStruct(updater StructUpdater, fields map[string]any, depth int) {
|
||||
for _, field := range updater.Fields() {
|
||||
// fmt.Printf("field: %s.%s\n %s\n", parent, field.Name(), field.Describe())
|
||||
setField(field, fields, depth)
|
||||
}
|
||||
}
|
||||
|
||||
func setField(field Updater, fields map[string]any, depth int) {
|
||||
if !field.ShouldUpdate(config.V3) {
|
||||
prompt := promptui.Prompt{
|
||||
Label: field.Name() + " did not change since last configure, skip?",
|
||||
IsConfirm: true,
|
||||
}
|
||||
if _, err := prompt.Run(); err == nil {
|
||||
fmt.Println("skip")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
fmt.Println(field.Name(), field.Describe())
|
||||
switch f := field.(type) {
|
||||
case StructUpdater:
|
||||
if fields[f.Name()] == nil {
|
||||
fields[f.Name()] = map[string]any{}
|
||||
}
|
||||
fmt.Printf("%.*s %s: %s\n", depth*2, "-", f.Name(), f.Describe())
|
||||
updateStruct(f, fields[f.Name()].(map[string]any), depth+1)
|
||||
case FieldUpdater:
|
||||
prompt := promptui.Prompt{
|
||||
Label: fmt.Sprintf("%s (%s) (%T)", f.Name(), f.Describe(), f.DefaultValue()),
|
||||
Default: fmt.Sprintf("%v", f.DefaultValue()),
|
||||
Validate: func(s string) error {
|
||||
if isConfirm(reflect.TypeOf(f.DefaultValue())) {
|
||||
return nil
|
||||
}
|
||||
return f.Set(s)
|
||||
},
|
||||
IsConfirm: isConfirm(reflect.TypeOf(f.DefaultValue())),
|
||||
}
|
||||
val, err := prompt.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fields[f.Name()] = f.Set(val)
|
||||
case OneOfUpdater:
|
||||
var possibilities []string
|
||||
for _, subField := range f.Fields() {
|
||||
possibilities = append(possibilities, subField.Name())
|
||||
fields[subField.Name()] = subField
|
||||
}
|
||||
|
||||
prompt := promptui.Select{
|
||||
Label: fmt.Sprintf("Select one of %s: (%s)", f.Name(), f.Describe()),
|
||||
Items: possibilities,
|
||||
}
|
||||
i, value, err := prompt.Run()
|
||||
if err != nil {
|
||||
fmt.Println("panic", err)
|
||||
panic(err)
|
||||
}
|
||||
fv := fields[value]
|
||||
// TODO: fv is result of decoder (postgres.config in this case)
|
||||
setField(f.Fields()[i], fv.(map[string]any), depth+1)
|
||||
}
|
||||
}
|
||||
|
||||
func isConfirm(t reflect.Type) bool {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
return isConfirm(t.Elem())
|
||||
}
|
||||
return t.Kind() == reflect.Bool
|
||||
}
|
||||
|
||||
type Updater interface {
|
||||
Name() string
|
||||
Describe() string
|
||||
ShouldUpdate(version config.Version) bool
|
||||
}
|
||||
|
||||
type FieldUpdater interface {
|
||||
Updater
|
||||
DefaultValue() any
|
||||
Set(value string) error
|
||||
_field()
|
||||
}
|
||||
|
||||
type StructUpdater interface {
|
||||
Updater
|
||||
Fields() []Updater
|
||||
_struct()
|
||||
}
|
||||
|
||||
type OneOfUpdater interface {
|
||||
Updater
|
||||
Fields() []Updater
|
||||
_oneOf()
|
||||
}
|
||||
|
||||
var _ FieldUpdater = (*Field[string])(nil)
|
||||
|
||||
type Field[T any] struct {
|
||||
FieldName string
|
||||
Default T
|
||||
Value *T
|
||||
Description string
|
||||
Version config.Version
|
||||
}
|
||||
|
||||
// DefaultValue implements [FieldUpdater].
|
||||
func (uf Field[T]) DefaultValue() any {
|
||||
return uf.Default
|
||||
}
|
||||
|
||||
// Describe implements [FieldUpdater].
|
||||
func (uf Field[T]) Describe() string {
|
||||
return uf.Description
|
||||
}
|
||||
|
||||
// Set implements [FieldUpdater].
|
||||
func (uf Field[T]) Set(value string) error {
|
||||
var v T
|
||||
if err := json.Unmarshal([]byte(value), &v); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal value: %v", err)
|
||||
}
|
||||
*uf.Value = v
|
||||
return nil
|
||||
}
|
||||
|
||||
// Field implements [FieldUpdater].
|
||||
func (uf Field[T]) Name() string {
|
||||
return uf.FieldName
|
||||
}
|
||||
|
||||
// ShouldUpdate implements [FieldUpdater].
|
||||
func (uf Field[T]) ShouldUpdate(version config.Version) bool {
|
||||
return uf.Version <= version
|
||||
}
|
||||
|
||||
func (f Field[T]) _field() {}
|
||||
|
||||
var _ StructUpdater = (*Struct)(nil)
|
||||
|
||||
type Struct struct {
|
||||
FieldName string
|
||||
Description string
|
||||
SubFields []Updater
|
||||
}
|
||||
|
||||
// Describe implements [StructUpdater].
|
||||
func (us Struct) Describe() string {
|
||||
return us.Description
|
||||
}
|
||||
|
||||
func (us Struct) Name() string {
|
||||
return us.FieldName
|
||||
}
|
||||
|
||||
func (us Struct) Fields() []Updater {
|
||||
return us.SubFields
|
||||
}
|
||||
|
||||
func (f Struct) _struct() {}
|
||||
|
||||
type OneOf struct {
|
||||
FieldName string
|
||||
Description string
|
||||
SubFields []Updater
|
||||
}
|
||||
|
||||
// Describe implements [OneOfUpdater].
|
||||
func (o OneOf) Describe() string {
|
||||
return o.Description
|
||||
}
|
||||
|
||||
// Fields implements [OneOfUpdater].
|
||||
func (o OneOf) Fields() []Updater {
|
||||
return o.SubFields
|
||||
}
|
||||
|
||||
// Name implements [FieldUpdater].
|
||||
func (o OneOf) Name() string {
|
||||
return o.FieldName
|
||||
}
|
||||
|
||||
func (f OneOf) _oneOf() {}
|
||||
|
||||
// ShouldUpdate implements [OneOfUpdater].
|
||||
func (o OneOf) ShouldUpdate(version config.Version) bool {
|
||||
for _, field := range o.SubFields {
|
||||
if !field.ShouldUpdate(version) {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var _ OneOfUpdater = (*OneOf)(nil)
|
||||
|
||||
func (us Struct) ShouldUpdate(version config.Version) bool {
|
||||
for _, field := range us.SubFields {
|
||||
if !field.ShouldUpdate(version) {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func FieldName(parent, field string) string {
|
||||
if parent == "" {
|
||||
return field
|
||||
}
|
||||
return parent + "." + field
|
||||
}
|
@@ -1,109 +0,0 @@
|
||||
package configure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
)
|
||||
|
||||
func Update2(config any) {
|
||||
// Print the intro
|
||||
printIntro()
|
||||
|
||||
// Start the interactive CLI
|
||||
interactiveCLI(reflect.ValueOf(config), 0)
|
||||
fmt.Println(config)
|
||||
}
|
||||
|
||||
const (
|
||||
ExitValue = "<Exit>"
|
||||
BackValue = "⬅ Back"
|
||||
Prefix = "📁 "
|
||||
)
|
||||
|
||||
var introTemplate = `
|
||||
+----------------------------------------+
|
||||
| 🛠 Config Interactive CLI 🛠 |
|
||||
+----------------------------------------+
|
||||
| |
|
||||
| %5s : Dive into nested config |
|
||||
| %6s : Return to previous menu |
|
||||
| %6s : Exit application |
|
||||
| |
|
||||
| Choose an option to explore! |
|
||||
| |
|
||||
+----------------------------------------+
|
||||
`
|
||||
|
||||
func printIntro() {
|
||||
fmt.Printf(introTemplate, Prefix, BackValue, ExitValue)
|
||||
}
|
||||
|
||||
// interactiveCLI handles the interactive CLI
|
||||
func interactiveCLI(v reflect.Value, depth int) {
|
||||
for {
|
||||
var items []string
|
||||
|
||||
// If depth is greater than 0, we are in a nested struct and should add a "Back" option
|
||||
if depth > 0 {
|
||||
items = append(items, BackValue)
|
||||
}
|
||||
|
||||
// Add all the field names
|
||||
items = append(items, getFieldNames(v)...)
|
||||
|
||||
// Add an "Exit" option
|
||||
items = append(items, ExitValue)
|
||||
|
||||
prompt := promptui.Select{
|
||||
Label: "Select Field",
|
||||
Items: items,
|
||||
}
|
||||
|
||||
_, result, err := prompt.Run()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Prompt failed %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case BackValue:
|
||||
return
|
||||
case ExitValue:
|
||||
// Exit the entire application
|
||||
os.Exit(0)
|
||||
default:
|
||||
fieldName := strings.TrimPrefix(result, Prefix)
|
||||
selectedField := v.FieldByName(fieldName)
|
||||
if selectedField.Kind() == reflect.Struct {
|
||||
interactiveCLI(selectedField, depth+1)
|
||||
} else {
|
||||
prompt := promptui.Prompt{
|
||||
Label: fmt.Sprintf("Field %s (%s)", result, selectedField.Kind()),
|
||||
Default: fmt.Sprintf("%v", selectedField.Interface()),
|
||||
}
|
||||
res, err := prompt.Run()
|
||||
fmt.Println(res, err)
|
||||
// fmt.Printf("%s: %v\n", result, selectedField.Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getFieldNames returns all the field names
|
||||
func getFieldNames(v reflect.Value) []string {
|
||||
t := v.Type()
|
||||
var fieldNames []string
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
fieldName := t.Field(i).Name
|
||||
if v.Field(i).Kind() == reflect.Struct {
|
||||
fieldName = Prefix + fieldName
|
||||
}
|
||||
fieldNames = append(fieldNames, fieldName)
|
||||
}
|
||||
return fieldNames
|
||||
}
|
449
backend/cmd/configure/update_config3.go
Normal file
449
backend/cmd/configure/update_config3.go
Normal file
@@ -0,0 +1,449 @@
|
||||
package configure
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/cmd/info"
|
||||
)
|
||||
|
||||
var lastConfiguredVersion *semver.Version
|
||||
|
||||
func Update(name, description string, fields []Updater) func(cmd *cobra.Command, args []string) {
|
||||
return func(cmd *cobra.Command, args []string) {
|
||||
configVersion := viper.GetString("configuredVersion")
|
||||
if configVersion != "" {
|
||||
lastConfiguredVersion, _ = semver.NewVersion(configVersion)
|
||||
}
|
||||
for _, field := range fields {
|
||||
if err := setField(field, "", 1); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Using config file:", viper.ConfigFileUsed(), "fields:", fields)
|
||||
fmt.Println(viper.AllSettings())
|
||||
viper.Set("configuredVersion", info.Version().String())
|
||||
// viper.MergeConfigMap(fields)
|
||||
// if err := viper.WriteConfig(); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setField(field Updater, parentPath string, depth int) error {
|
||||
if !field.ShouldUpdate(lastConfiguredVersion) {
|
||||
prompt := promptui.Prompt{
|
||||
Label: field.Name() + " did not change since last configure, skip?",
|
||||
IsConfirm: true,
|
||||
}
|
||||
if _, err := prompt.Run(); err == nil {
|
||||
fmt.Println("skip")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
fieldPath := path(parentPath, field.Name())
|
||||
switch f := field.(type) {
|
||||
case StructUpdater:
|
||||
fmt.Printf("\n%.*s %s: %s\n\n", depth*2, "-", f.Name(), f.Describe())
|
||||
for _, subField := range f.Fields() {
|
||||
err := setField(subField, fieldPath, depth+1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case FieldUpdater:
|
||||
value := viper.Get(fieldPath)
|
||||
if value == nil {
|
||||
value = f.ValueOrDefault()
|
||||
}
|
||||
prompt := promptui.Prompt{
|
||||
Label: fmt.Sprintf("%s (%s) (%T)", f.Name(), f.Describe(), f.ValueOrDefault()),
|
||||
Default: fmt.Sprintf("%v", value),
|
||||
Validate: func(s string) error {
|
||||
if isConfirm(reflect.TypeOf(value)) {
|
||||
return nil
|
||||
}
|
||||
return f.Set(s)
|
||||
},
|
||||
HideEntered: f.ShouldHide(),
|
||||
IsConfirm: isConfirm(reflect.TypeOf(value)),
|
||||
}
|
||||
_, err := prompt.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
viper.Set(fieldPath, f.ValueOrDefault())
|
||||
case OneOfUpdater:
|
||||
var possibilities []string
|
||||
for _, subField := range f.Fields() {
|
||||
possibility := subField.Name()
|
||||
if possibility == "" {
|
||||
possibility = subField.Describe()
|
||||
}
|
||||
possibilities = append(possibilities, possibility)
|
||||
}
|
||||
|
||||
prompt := promptui.Select{
|
||||
Label: fmt.Sprintf("Select one of %s: (%s)", f.Name(), f.Describe()),
|
||||
Items: possibilities,
|
||||
}
|
||||
i, _, err := prompt.Run()
|
||||
if err != nil {
|
||||
fmt.Println("panic", err)
|
||||
panic(err)
|
||||
}
|
||||
setField(f.Fields()[i], fieldPath, depth-1)
|
||||
// TODO: fv is result of decoder (postgres.config in this case)
|
||||
case ConstantUpdater:
|
||||
viper.Set(fieldPath, f.Value())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func path(parent, field string) string {
|
||||
if parent == "" {
|
||||
return field
|
||||
}
|
||||
if strings.HasSuffix(parent, field) {
|
||||
return parent
|
||||
}
|
||||
if field == "" {
|
||||
return parent
|
||||
}
|
||||
return parent + "." + field
|
||||
}
|
||||
|
||||
func isConfirm(t reflect.Type) bool {
|
||||
if t == nil {
|
||||
return false
|
||||
}
|
||||
if t.Kind() == reflect.Ptr {
|
||||
return isConfirm(t.Elem())
|
||||
}
|
||||
return t.Kind() == reflect.Bool
|
||||
}
|
||||
|
||||
type Updater interface {
|
||||
Name() string
|
||||
Describe() string
|
||||
ShouldUpdate(version *semver.Version) bool
|
||||
}
|
||||
|
||||
type FieldUpdater interface {
|
||||
Updater
|
||||
// DefaultValue() any
|
||||
Set(value any) error
|
||||
ValueOrDefault() any
|
||||
_field()
|
||||
ShouldHide() bool
|
||||
}
|
||||
|
||||
type StructUpdater interface {
|
||||
Updater
|
||||
Fields() []Updater
|
||||
_struct()
|
||||
}
|
||||
|
||||
type OneOfUpdater interface {
|
||||
Updater
|
||||
Fields() []Updater
|
||||
_oneOf()
|
||||
}
|
||||
|
||||
type ConstantUpdater interface {
|
||||
Updater
|
||||
Value() any
|
||||
}
|
||||
|
||||
var _ FieldUpdater = (*Field[string])(nil)
|
||||
|
||||
type Field[T any] struct {
|
||||
FieldName string
|
||||
Value T
|
||||
HideInput bool
|
||||
Description string
|
||||
Version *semver.Version
|
||||
Validate func(T) error
|
||||
}
|
||||
|
||||
// Describe implements [FieldUpdater].
|
||||
func (f Field[T]) Describe() string {
|
||||
return f.Description
|
||||
}
|
||||
|
||||
// Set implements [FieldUpdater].
|
||||
func (f *Field[T]) Set(value any) (err error) {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
f.Value, err = mapString[T](v)
|
||||
case map[string]any:
|
||||
f.Value, err = mapMap[T](v)
|
||||
case T:
|
||||
f.Value = v
|
||||
default:
|
||||
err = fmt.Errorf("unsupported type %T", v)
|
||||
}
|
||||
|
||||
if err != nil || f.Validate == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return f.Validate(f.Value)
|
||||
}
|
||||
|
||||
func (f Field[T]) ValueOrDefault() any {
|
||||
return f.Value
|
||||
}
|
||||
|
||||
func mapMap[T any](value map[string]any) (t T, err error) {
|
||||
jsonValue, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
return t, json.Unmarshal(jsonValue, &t)
|
||||
}
|
||||
|
||||
func mapString[T any](value string) (t T, err error) {
|
||||
var v any
|
||||
switch reflect.TypeFor[T]().Kind() {
|
||||
case reflect.Bool:
|
||||
v, err = strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
case reflect.Int:
|
||||
i, err := strconv.ParseInt(value, 10, 0)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = int(i)
|
||||
case reflect.Int8:
|
||||
i, err := strconv.ParseInt(value, 10, 8)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = int8(i)
|
||||
case reflect.Int16:
|
||||
i, err := strconv.ParseInt(value, 10, 16)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = int16(i)
|
||||
case reflect.Int32:
|
||||
i, err := strconv.ParseInt(value, 10, 32)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = int32(i)
|
||||
case reflect.Int64:
|
||||
i, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = int64(i)
|
||||
case reflect.Uint:
|
||||
i, err := strconv.ParseUint(value, 10, 0)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = uint(i)
|
||||
case reflect.Uint8:
|
||||
i, err := strconv.ParseUint(value, 10, 8)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = uint8(i)
|
||||
case reflect.Uint16:
|
||||
i, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = uint16(i)
|
||||
case reflect.Uint32:
|
||||
i, err := strconv.ParseUint(value, 10, 32)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = uint32(i)
|
||||
case reflect.Uint64:
|
||||
i, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = uint64(i)
|
||||
case reflect.Float32:
|
||||
i, err := strconv.ParseFloat(value, 32)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = float32(i)
|
||||
case reflect.Float64:
|
||||
i, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = float64(i)
|
||||
case reflect.Complex64:
|
||||
i, err := strconv.ParseComplex(value, 64)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
v = complex64(i)
|
||||
case reflect.Complex128:
|
||||
v, err = strconv.ParseComplex(value, 128)
|
||||
case reflect.String:
|
||||
v = value
|
||||
default:
|
||||
k := reflect.TypeFor[T]().Kind()
|
||||
_ = k
|
||||
return t, errors.New("not implemented")
|
||||
}
|
||||
return v.(T), nil
|
||||
}
|
||||
|
||||
// Field implements [FieldUpdater].
|
||||
func (f Field[T]) Name() string {
|
||||
return f.FieldName
|
||||
}
|
||||
|
||||
// ShouldUpdate implements [FieldUpdater].
|
||||
func (f Field[T]) ShouldUpdate(version *semver.Version) bool {
|
||||
if version == nil {
|
||||
return true
|
||||
}
|
||||
return f.Version.GreaterThan(version)
|
||||
}
|
||||
|
||||
// ShouldHide implements [FieldUpdater].
|
||||
func (f Field[T]) ShouldHide() bool {
|
||||
return f.HideInput
|
||||
}
|
||||
|
||||
func (f Field[T]) _field() {}
|
||||
|
||||
var _ StructUpdater = (*Struct)(nil)
|
||||
|
||||
type Struct struct {
|
||||
FieldName string
|
||||
Description string
|
||||
SubFields []Updater
|
||||
}
|
||||
|
||||
// Describe implements [StructUpdater].
|
||||
func (s Struct) Describe() string {
|
||||
return s.Description
|
||||
}
|
||||
|
||||
func (s Struct) Name() string {
|
||||
return s.FieldName
|
||||
}
|
||||
|
||||
func (s Struct) Fields() []Updater {
|
||||
return s.SubFields
|
||||
}
|
||||
|
||||
func (s Struct) ShouldUpdate(version *semver.Version) bool {
|
||||
if version == nil {
|
||||
return true
|
||||
}
|
||||
for _, field := range s.SubFields {
|
||||
if !field.ShouldUpdate(version) {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (Struct) _struct() {}
|
||||
|
||||
type OneOf struct {
|
||||
FieldName string
|
||||
Description string
|
||||
SubFields []Updater
|
||||
}
|
||||
|
||||
// Describe implements [OneOfUpdater].
|
||||
func (o OneOf) Describe() string {
|
||||
return o.Description
|
||||
}
|
||||
|
||||
// Fields implements [OneOfUpdater].
|
||||
func (o OneOf) Fields() []Updater {
|
||||
return o.SubFields
|
||||
}
|
||||
|
||||
// Name implements [FieldUpdater].
|
||||
func (o OneOf) Name() string {
|
||||
return o.FieldName
|
||||
}
|
||||
|
||||
func (OneOf) _oneOf() {}
|
||||
|
||||
// ShouldUpdate implements [OneOfUpdater].
|
||||
func (o OneOf) ShouldUpdate(version *semver.Version) bool {
|
||||
if version == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, field := range o.SubFields {
|
||||
if !field.ShouldUpdate(version) {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var _ OneOfUpdater = (*OneOf)(nil)
|
||||
|
||||
func FieldName(parent, field string) string {
|
||||
if parent == "" {
|
||||
return field
|
||||
}
|
||||
return parent + "." + field
|
||||
}
|
||||
|
||||
type Constant[T any] struct {
|
||||
Description string
|
||||
Constant T
|
||||
Version *semver.Version
|
||||
}
|
||||
|
||||
var _ ConstantUpdater = (*Constant[any])(nil)
|
||||
|
||||
// Describe implements [ConstantUpdater].
|
||||
func (c Constant[T]) Describe() string {
|
||||
return c.Description
|
||||
}
|
||||
|
||||
// Name implements [ConstantUpdater].
|
||||
func (c Constant[T]) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// ShouldUpdate implements [ConstantUpdater].
|
||||
func (c Constant[T]) ShouldUpdate(version *semver.Version) bool {
|
||||
if version == nil {
|
||||
return true
|
||||
}
|
||||
return c.Version.GreaterThan(version)
|
||||
}
|
||||
|
||||
// Value implements [ConstantUpdater].
|
||||
func (c Constant[T]) Value() any {
|
||||
return c.Constant
|
||||
}
|
36
backend/cmd/info/config.go
Normal file
36
backend/cmd/info/config.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
version string
|
||||
commit string
|
||||
date string
|
||||
)
|
||||
|
||||
func Version() *semver.Version {
|
||||
v, _ := semver.NewVersion(version)
|
||||
if v != nil {
|
||||
return v
|
||||
}
|
||||
return semver.New(uint64(Date().Year()), uint64(Date().Month()), uint64(Date().Day()), "", "")
|
||||
}
|
||||
|
||||
func Commit() string {
|
||||
return commit
|
||||
}
|
||||
|
||||
func Date() time.Time {
|
||||
if date == "" {
|
||||
return time.Now()
|
||||
}
|
||||
d, err := time.Parse(time.RFC3339, date)
|
||||
if err != nil {
|
||||
return time.Now()
|
||||
}
|
||||
return d
|
||||
}
|
@@ -5,7 +5,8 @@ import (
|
||||
"embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/cmd/config"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/cmd/configure"
|
||||
"github.com/zitadel/zitadel/backend/storage/database"
|
||||
)
|
||||
@@ -18,26 +19,24 @@ var (
|
||||
type Step001 struct {
|
||||
Database database.Pool `mapstructure:"-"`
|
||||
|
||||
DatabaseName string `configure:"added:"v3",default:"zitadel"`
|
||||
Username string `configure:"added:"v3",default:"zitadel"`
|
||||
DatabaseName string
|
||||
Username string
|
||||
}
|
||||
|
||||
// Fields implements configure.StructUpdater.
|
||||
func (v Step001) Fields() []configure.Updater {
|
||||
return []configure.Updater{
|
||||
configure.Field[string]{
|
||||
&configure.Field[string]{
|
||||
FieldName: "databaseName",
|
||||
Default: "zitadel",
|
||||
Value: &v.DatabaseName,
|
||||
Value: "zitadel",
|
||||
Description: "The name of the database Zitadel will store its data in",
|
||||
Version: config.V3,
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
configure.Field[string]{
|
||||
&configure.Field[string]{
|
||||
FieldName: "username",
|
||||
Default: "zitadel",
|
||||
Value: &v.Username,
|
||||
Value: "zitadel",
|
||||
Description: "The username Zitadel will use to connect to the database",
|
||||
Version: config.V3,
|
||||
Version: semver.MustParse("3"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package prepare
|
||||
|
||||
import (
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
@@ -65,7 +66,10 @@ func (c *Config) Name() string {
|
||||
}
|
||||
|
||||
// ShouldUpdate implements configure.StructUpdater.
|
||||
func (c *Config) ShouldUpdate(version config.Version) bool {
|
||||
func (c *Config) ShouldUpdate(version *semver.Version) bool {
|
||||
if version == nil {
|
||||
return true
|
||||
}
|
||||
for _, field := range c.Fields() {
|
||||
if field.ShouldUpdate(version) {
|
||||
return true
|
||||
@@ -77,12 +81,12 @@ func (c *Config) ShouldUpdate(version config.Version) bool {
|
||||
// Fields implements configure.UpdateConfig.
|
||||
func (c Config) Fields() []configure.Updater {
|
||||
return []configure.Updater{
|
||||
configure.Struct{
|
||||
&configure.Struct{
|
||||
FieldName: "step001",
|
||||
Description: "The configuration for the first step of the prepare command",
|
||||
SubFields: c.Step001.Fields(),
|
||||
},
|
||||
configure.Struct{
|
||||
&configure.Struct{
|
||||
FieldName: "database",
|
||||
Description: "The configuration for the database connection",
|
||||
SubFields: c.Database.Fields(),
|
||||
|
@@ -2,6 +2,7 @@ package prepare
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
step001 "github.com/zitadel/zitadel/backend/cmd/prepare/001"
|
||||
)
|
||||
|
||||
|
@@ -1,19 +1,6 @@
|
||||
configuredversion: 2025.2.23
|
||||
database:
|
||||
cockroach:
|
||||
host: localhost
|
||||
port: 26257
|
||||
gosql:
|
||||
fieldname: gosql
|
||||
default: null
|
||||
value: null
|
||||
description: Configuration for connection string for gosql
|
||||
version: 1
|
||||
postgres:
|
||||
fieldname: postgres
|
||||
default: null
|
||||
value: null
|
||||
description: Configuration for connection string for postgres
|
||||
version: 1
|
||||
postgres: host=local
|
||||
step001:
|
||||
databasename: zitadel
|
||||
username: zitadel
|
||||
databasename: zita
|
||||
username: adel
|
||||
|
@@ -20,6 +20,7 @@ type Hook struct {
|
||||
Match func(string) bool
|
||||
Decode func(name string, config any) (database.Connector, error)
|
||||
Name string
|
||||
Field configure.Updater
|
||||
}
|
||||
|
||||
var hooks = make([]Hook, 0)
|
||||
@@ -30,11 +31,13 @@ func init() {
|
||||
Match: postgres.NameMatcher,
|
||||
Decode: postgres.DecodeConfig,
|
||||
Name: postgres.Name,
|
||||
Field: postgres.Field,
|
||||
},
|
||||
Hook{
|
||||
Match: gosql.NameMatcher,
|
||||
Decode: gosql.DecodeConfig,
|
||||
Name: gosql.Name,
|
||||
Field: gosql.Field,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -48,18 +51,17 @@ type Config struct {
|
||||
// Fields implements [configure.StructUpdater].
|
||||
func (c *Config) Fields() []configure.Updater {
|
||||
dialects := configure.OneOf{
|
||||
FieldName: "dialect",
|
||||
Description: "The database dialect Zitadel connects to",
|
||||
SubFields: []configure.Updater{},
|
||||
}
|
||||
for _, hook := range hooks {
|
||||
value := c.Dialects[hook.Name]
|
||||
dialects.SubFields = append(dialects.SubFields, &configure.Field[any]{
|
||||
if hook.Field == nil {
|
||||
panic("hook must configure its config fields")
|
||||
}
|
||||
dialects.SubFields = append(dialects.SubFields, &configure.Struct{
|
||||
FieldName: hook.Name,
|
||||
Default: nil,
|
||||
Description: fmt.Sprintf("Configuration for connection string for %s", hook.Name),
|
||||
Version: config.V3,
|
||||
Value: &value,
|
||||
Description: fmt.Sprintf("Configuration for %s", hook.Name),
|
||||
SubFields: []configure.Updater{hook.Field},
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -6,12 +6,25 @@ 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"
|
||||
_ 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
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@@ -6,14 +6,91 @@ import (
|
||||
"slices"
|
||||
"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 = "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 }
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/storage/database"
|
||||
)
|
||||
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/storage/database"
|
||||
)
|
||||
|
||||
|
@@ -2,6 +2,7 @@ package postgres
|
||||
|
||||
import (
|
||||
"github.com/jackc/pgx/v5"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/storage/database"
|
||||
)
|
||||
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/storage/database"
|
||||
)
|
||||
|
||||
|
Reference in New Issue
Block a user