mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-31 06:05:43 +00:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
			060e20e578
			...
			7795a989cd
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 7795a989cd | ||
|   | cebce1a92c | ||
|   | 120ae2c79d | 
| @@ -11,13 +11,15 @@ export function TailscaleIcon(props: SVGProps<SVGSVGElement>) { | |||||||
|       {...props} |       {...props} | ||||||
|     > |     > | ||||||
|       <path |       <path | ||||||
|         className="opacity-80 fill-white" |         className="opacity-80" | ||||||
|  |         fill="currentColor" | ||||||
|         d="M65.6 318.1c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9S1.8 219 1.8 254.2s28.6 63.9 63.8 63.9m191.6 0c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m0 193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m189.2-193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9" |         d="M65.6 318.1c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9S1.8 219 1.8 254.2s28.6 63.9 63.8 63.9m191.6 0c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m0 193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m189.2-193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9" | ||||||
|       /> |       /> | ||||||
|  |  | ||||||
|       <path |       <path | ||||||
|         d="M65.6 127.7c35.3 0 63.9-28.6 63.9-63.9S100.9 0 65.6 0 1.8 28.6 1.8 63.9s28.6 63.8 63.8 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.8 28.7-63.8 63.9S30.4 512 65.6 512m191.6-384.3c35.3 0 63.9-28.6 63.9-63.9S292.5 0 257.2 0s-63.9 28.6-63.9 63.9 28.6 63.8 63.9 63.8m189.2 0c35.3 0 63.9-28.6 63.9-63.9S481.6 0 446.4 0c-35.3 0-63.9 28.6-63.9 63.9s28.6 63.8 63.9 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9" |         d="M65.6 127.7c35.3 0 63.9-28.6 63.9-63.9S100.9 0 65.6 0 1.8 28.6 1.8 63.9s28.6 63.8 63.8 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.8 28.7-63.8 63.9S30.4 512 65.6 512m191.6-384.3c35.3 0 63.9-28.6 63.9-63.9S292.5 0 257.2 0s-63.9 28.6-63.9 63.9 28.6 63.8 63.9 63.8m189.2 0c35.3 0 63.9-28.6 63.9-63.9S481.6 0 446.4 0c-35.3 0-63.9 28.6-63.9 63.9s28.6 63.8 63.9 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9" | ||||||
|         className="opacity-20 fill-white" |         className="opacity-20" | ||||||
|  |         fill="currentColor" | ||||||
|       /> |       /> | ||||||
|     </svg> |     </svg> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -1,8 +1,10 @@ | |||||||
| ALTER TABLE "sessions" ADD COLUMN "oauth_name" TEXT; | ALTER TABLE "sessions" ADD COLUMN "oauth_name" TEXT; | ||||||
|  |  | ||||||
| UPDATE | UPDATE "sessions" | ||||||
|     "sessions" | SET "oauth_name" = CASE | ||||||
| SET |   WHEN LOWER("provider") = 'github' THEN 'GitHub' | ||||||
|     "oauth_name" = "Generic" |   WHEN LOWER("provider") = 'google' THEN 'Google' | ||||||
| WHERE |   ELSE UPPER(SUBSTR("provider", 1, 1)) || SUBSTR("provider", 2) | ||||||
|     "oauth_name" IS NULL AND "provider" IS NOT NULL; | END | ||||||
|  | WHERE "oauth_name" IS NULL AND "provider" IS NOT NULL; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -51,16 +51,16 @@ type Claims struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type OAuthServiceConfig struct { | type OAuthServiceConfig struct { | ||||||
| 	ClientID           string | 	ClientID           string   `key:"client-id"` | ||||||
| 	ClientSecret       string | 	ClientSecret       string   `key:"client-secret"` | ||||||
| 	ClientSecretFile   string | 	ClientSecretFile   string   `key:"client-secret-file"` | ||||||
| 	Scopes             []string | 	Scopes             []string `key:"scopes"` | ||||||
| 	RedirectURL        string | 	RedirectURL        string   `key:"redirect-url"` | ||||||
| 	AuthURL            string | 	AuthURL            string   `key:"auth-url"` | ||||||
| 	TokenURL           string | 	TokenURL           string   `key:"token-url"` | ||||||
| 	UserinfoURL        string | 	UserinfoURL        string   `key:"user-info-url"` | ||||||
| 	InsecureSkipVerify bool | 	InsecureSkipVerify bool     `key:"insecure-skip-verify"` | ||||||
| 	Name               string | 	Name               string   `key:"name"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // User/session related stuff | // User/session related stuff | ||||||
|   | |||||||
| @@ -142,7 +142,9 @@ func GetOAuthProvidersConfig(env []string, args []string, appUrl string) (map[st | |||||||
|  |  | ||||||
| 	for _, e := range env { | 	for _, e := range env { | ||||||
| 		pair := strings.SplitN(e, "=", 2) | 		pair := strings.SplitN(e, "=", 2) | ||||||
| 		envMap[pair[0]] = pair[1] | 		if len(pair) == 2 { | ||||||
|  | 			envMap[pair[0]] = pair[1] | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	envProviders, err := decoders.DecodeEnv(envMap) | 	envProviders, err := decoders.DecodeEnv(envMap) | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								internal/utils/decoders/decoders.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								internal/utils/decoders/decoders.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | package decoders | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"strings" | ||||||
|  | 	"tinyauth/internal/config" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func NormalizeKeys(keys map[string]string, rootName string, sep string) map[string]string { | ||||||
|  | 	normalized := make(map[string]string) | ||||||
|  | 	knownKeys := getKnownKeys() | ||||||
|  |  | ||||||
|  | 	for k, v := range keys { | ||||||
|  | 		var finalKey []string | ||||||
|  | 		var suffix string | ||||||
|  | 		var camelClientName string | ||||||
|  | 		var camelField string | ||||||
|  |  | ||||||
|  | 		finalKey = append(finalKey, rootName) | ||||||
|  | 		finalKey = append(finalKey, "providers") | ||||||
|  | 		cebabKey := strings.ToLower(k) | ||||||
|  |  | ||||||
|  | 		for _, known := range knownKeys { | ||||||
|  | 			if strings.HasSuffix(cebabKey, strings.ReplaceAll(known, "-", sep)) { | ||||||
|  | 				suffix = known | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if suffix == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		clientNameParts := strings.Split(strings.TrimPrefix(strings.TrimSuffix(cebabKey, sep+strings.ReplaceAll(suffix, "-", sep)), "providers"+sep), sep) | ||||||
|  |  | ||||||
|  | 		for i, p := range clientNameParts { | ||||||
|  | 			if i == 0 { | ||||||
|  | 				camelClientName += p | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			if p == "" { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			camelClientName += strings.ToUpper(string([]rune(p)[0])) + string([]rune(p)[1:]) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		finalKey = append(finalKey, camelClientName) | ||||||
|  |  | ||||||
|  | 		filedParts := strings.Split(suffix, "-") | ||||||
|  |  | ||||||
|  | 		for i, p := range filedParts { | ||||||
|  | 			if i == 0 { | ||||||
|  | 				camelField += p | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			if p == "" { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			camelField += strings.ToUpper(string([]rune(p)[0])) + string([]rune(p)[1:]) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		finalKey = append(finalKey, camelField) | ||||||
|  | 		normalized[strings.Join(finalKey, ".")] = v | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return normalized | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getKnownKeys() []string { | ||||||
|  | 	var known []string | ||||||
|  |  | ||||||
|  | 	p := config.OAuthServiceConfig{} | ||||||
|  | 	v := reflect.ValueOf(p) | ||||||
|  | 	typeOfP := v.Type() | ||||||
|  |  | ||||||
|  | 	for field := range typeOfP.NumField() { | ||||||
|  | 		known = append(known, typeOfP.Field(field).Tag.Get("key")) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return known | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								internal/utils/decoders/decoders_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								internal/utils/decoders/decoders_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | package decoders_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 	"tinyauth/internal/utils/decoders" | ||||||
|  |  | ||||||
|  | 	"gotest.tools/v3/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestNormalizeKeys(t *testing.T) { | ||||||
|  | 	// Test with env | ||||||
|  | 	test := map[string]string{ | ||||||
|  | 		"PROVIDERS_CLIENT1_CLIENT_ID":                    "my-client-id", | ||||||
|  | 		"PROVIDERS_CLIENT1_CLIENT_SECRET":                "my-client-secret", | ||||||
|  | 		"PROVIDERS_MY_AWESOME_CLIENT_CLIENT_ID":          "my-awesome-client-id", | ||||||
|  | 		"PROVIDERS_MY_AWESOME_CLIENT_CLIENT_SECRET_FILE": "/path/to/secret", | ||||||
|  | 	} | ||||||
|  | 	expected := map[string]string{ | ||||||
|  | 		"tinyauth.providers.client1.clientId":                 "my-client-id", | ||||||
|  | 		"tinyauth.providers.client1.clientSecret":             "my-client-secret", | ||||||
|  | 		"tinyauth.providers.myAwesomeClient.clientId":         "my-awesome-client-id", | ||||||
|  | 		"tinyauth.providers.myAwesomeClient.clientSecretFile": "/path/to/secret", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	normalized := decoders.NormalizeKeys(test, "tinyauth", "_") | ||||||
|  | 	assert.DeepEqual(t, normalized, expected) | ||||||
|  |  | ||||||
|  | 	// Test with flags (assume -- is already stripped) | ||||||
|  | 	test = map[string]string{ | ||||||
|  | 		"providers-client1-client-id":                    "my-client-id", | ||||||
|  | 		"providers-client1-client-secret":                "my-client-secret", | ||||||
|  | 		"providers-my-awesome-client-client-id":          "my-awesome-client-id", | ||||||
|  | 		"providers-my-awesome-client-client-secret-file": "/path/to/secret", | ||||||
|  | 	} | ||||||
|  | 	expected = map[string]string{ | ||||||
|  | 		"tinyauth.providers.client1.clientId":                 "my-client-id", | ||||||
|  | 		"tinyauth.providers.client1.clientSecret":             "my-client-secret", | ||||||
|  | 		"tinyauth.providers.myAwesomeClient.clientId":         "my-awesome-client-id", | ||||||
|  | 		"tinyauth.providers.myAwesomeClient.clientSecretFile": "/path/to/secret", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	normalized = decoders.NormalizeKeys(test, "tinyauth", "-") | ||||||
|  | 	assert.DeepEqual(t, normalized, expected) | ||||||
|  | } | ||||||
| @@ -1,37 +1,16 @@ | |||||||
| package decoders | package decoders | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"slices" |  | ||||||
| 	"sort" |  | ||||||
| 	"strings" |  | ||||||
| 	"tinyauth/internal/config" | 	"tinyauth/internal/config" | ||||||
|  |  | ||||||
| 	"github.com/traefik/paerser/parser" | 	"github.com/traefik/paerser/parser" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Based on https://github.com/traefik/paerser/blob/master/parser/labels_decode.go |  | ||||||
|  |  | ||||||
| func DecodeEnv(env map[string]string) (config.Providers, error) { | func DecodeEnv(env map[string]string) (config.Providers, error) { | ||||||
| 	normalized := normalizeEnv(env, "tinyauth") | 	normalized := NormalizeKeys(env, "tinyauth", "_") | ||||||
|  |  | ||||||
| 	node, err := decodeEnvsToNode(normalized, "tinyauth", "tinyauth_providers") |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		return config.Providers{}, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var providers config.Providers | 	var providers config.Providers | ||||||
|  |  | ||||||
| 	metaOpts := parser.MetadataOpts{TagName: "env", AllowSliceAsStruct: true} | 	err := parser.Decode(normalized, &providers, "tinyauth", "tinyauth.providers") | ||||||
|  |  | ||||||
| 	err = parser.AddMetadata(&providers, node, metaOpts) |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		return config.Providers{}, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = parser.Fill(&providers, node, parser.FillerOpts{AllowSliceAsStruct: true}) |  | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return config.Providers{}, err | 		return config.Providers{}, err | ||||||
| @@ -39,99 +18,3 @@ func DecodeEnv(env map[string]string) (config.Providers, error) { | |||||||
|  |  | ||||||
| 	return providers, nil | 	return providers, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func decodeEnvsToNode(env map[string]string, rootName string, filters ...string) (*parser.Node, error) { |  | ||||||
| 	sorted := sortEnvKeys(env, filters) |  | ||||||
|  |  | ||||||
| 	var node *parser.Node |  | ||||||
|  |  | ||||||
| 	for i, k := range sorted { |  | ||||||
| 		split := strings.SplitN(k, "_", 4) // Something like PROVIDERS_MY_AWESOME_CLIENT is not supported because it will confuse the parser |  | ||||||
|  |  | ||||||
| 		if split[0] != rootName { |  | ||||||
| 			return nil, fmt.Errorf("invalid env root %s", split[0]) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if slices.Contains(split, "") { |  | ||||||
| 			return nil, fmt.Errorf("invalid element: %s", k) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if i == 0 { |  | ||||||
| 			node = &parser.Node{} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		decodeEnvToNode(node, split, env[k]) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return node, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func decodeEnvToNode(root *parser.Node, path []string, value string) { |  | ||||||
| 	if len(root.Name) == 0 { |  | ||||||
| 		root.Name = path[0] |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(path) <= 1 { |  | ||||||
| 		root.Value = value |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if n := containsEnvNode(root.Children, path[1]); n != nil { |  | ||||||
| 		decodeEnvToNode(n, path[1:], value) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	child := &parser.Node{Name: path[1]} |  | ||||||
| 	decodeEnvToNode(child, path[1:], value) |  | ||||||
| 	root.Children = append(root.Children, child) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func containsEnvNode(node []*parser.Node, name string) *parser.Node { |  | ||||||
| 	for _, n := range node { |  | ||||||
| 		if strings.EqualFold(n.Name, name) { |  | ||||||
| 			return n |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func sortEnvKeys(env map[string]string, filters []string) []string { |  | ||||||
| 	var sorted []string |  | ||||||
|  |  | ||||||
| 	for k := range env { |  | ||||||
| 		if len(filters) == 0 { |  | ||||||
| 			sorted = append(sorted, k) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for _, f := range filters { |  | ||||||
| 			if strings.HasPrefix(k, f) { |  | ||||||
| 				sorted = append(sorted, k) |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	sort.Strings(sorted) |  | ||||||
| 	return sorted |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // normalizeEnv converts env vars from PROVIDERS_CLIENT1_CLIENT_ID to tinyauth_providers_client_clientId |  | ||||||
| func normalizeEnv(env map[string]string, rootName string) map[string]string { |  | ||||||
| 	n := make(map[string]string) |  | ||||||
| 	for k, v := range env { |  | ||||||
| 		fk := strings.ToLower(k) |  | ||||||
| 		fks := strings.SplitN(fk, "_", 3) |  | ||||||
| 		fkb := "" |  | ||||||
| 		for i, s := range strings.Split(fks[len(fks)-1], "_") { |  | ||||||
| 			if i == 0 { |  | ||||||
| 				fkb += s |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			fkb += strings.ToUpper(string([]rune(s)[0])) + string([]rune(s)[1:]) |  | ||||||
| 		} |  | ||||||
| 		fk = rootName + "_" + strings.Join(fks[:len(fks)-1], "_") + "_" + fkb |  | ||||||
| 		n[fk] = v |  | ||||||
| 	} |  | ||||||
| 	return n |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -1,37 +1,18 @@ | |||||||
| package decoders | package decoders | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"slices" |  | ||||||
| 	"sort" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"tinyauth/internal/config" | 	"tinyauth/internal/config" | ||||||
|  |  | ||||||
| 	"github.com/traefik/paerser/parser" | 	"github.com/traefik/paerser/parser" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Based on https://github.com/traefik/paerser/blob/master/parser/labels_decode.go |  | ||||||
|  |  | ||||||
| func DecodeFlags(flags map[string]string) (config.Providers, error) { | func DecodeFlags(flags map[string]string) (config.Providers, error) { | ||||||
| 	normalized := normalizeFlags(flags, "tinyauth") | 	filtered := filterFlags(flags) | ||||||
|  | 	normalized := NormalizeKeys(filtered, "tinyauth", "-") | ||||||
| 	node, err := decodeFlagsToNode(normalized, "tinyauth", "tinyauth_providers") |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		return config.Providers{}, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var providers config.Providers | 	var providers config.Providers | ||||||
|  |  | ||||||
| 	metaOpts := parser.MetadataOpts{TagName: "flag", AllowSliceAsStruct: true} | 	err := parser.Decode(normalized, &providers, "tinyauth", "tinyauth.providers") | ||||||
|  |  | ||||||
| 	err = parser.AddMetadata(&providers, node, metaOpts) |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		return config.Providers{}, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = parser.Fill(&providers, node, parser.FillerOpts{AllowSliceAsStruct: true}) |  | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return config.Providers{}, err | 		return config.Providers{}, err | ||||||
| @@ -40,98 +21,10 @@ func DecodeFlags(flags map[string]string) (config.Providers, error) { | |||||||
| 	return providers, nil | 	return providers, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func decodeFlagsToNode(flags map[string]string, rootName string, filters ...string) (*parser.Node, error) { | func filterFlags(flags map[string]string) map[string]string { | ||||||
| 	sorted := sortFlagKeys(flags, filters) | 	filtered := make(map[string]string) | ||||||
|  |  | ||||||
| 	var node *parser.Node |  | ||||||
|  |  | ||||||
| 	for i, k := range sorted { |  | ||||||
| 		split := strings.SplitN(k, "_", 4) // Something like --providers-my-awesome-client is not supported because it will confuse the parser |  | ||||||
|  |  | ||||||
| 		if split[0] != rootName { |  | ||||||
| 			return nil, fmt.Errorf("invalid flag root %s", split[0]) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if slices.Contains(split, "") { |  | ||||||
| 			return nil, fmt.Errorf("invalid element: %s", k) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if i == 0 { |  | ||||||
| 			node = &parser.Node{} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		decodeFlagToNode(node, split, flags[k]) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return node, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func decodeFlagToNode(root *parser.Node, path []string, value string) { |  | ||||||
| 	if len(root.Name) == 0 { |  | ||||||
| 		root.Name = path[0] |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(path) <= 1 { |  | ||||||
| 		root.Value = value |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if n := containsFlagNode(root.Children, path[1]); n != nil { |  | ||||||
| 		decodeFlagToNode(n, path[1:], value) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	child := &parser.Node{Name: path[1]} |  | ||||||
| 	decodeFlagToNode(child, path[1:], value) |  | ||||||
| 	root.Children = append(root.Children, child) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func containsFlagNode(node []*parser.Node, name string) *parser.Node { |  | ||||||
| 	for _, n := range node { |  | ||||||
| 		if strings.EqualFold(n.Name, name) { |  | ||||||
| 			return n |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func sortFlagKeys(flags map[string]string, filters []string) []string { |  | ||||||
| 	var sorted []string |  | ||||||
|  |  | ||||||
| 	for k := range flags { |  | ||||||
| 		if len(filters) == 0 { |  | ||||||
| 			sorted = append(sorted, k) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for _, f := range filters { |  | ||||||
| 			if strings.HasPrefix(k, f) { |  | ||||||
| 				sorted = append(sorted, k) |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	sort.Strings(sorted) |  | ||||||
| 	return sorted |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // normalizeFlags converts flags from --providers-client-client-id to tinyauth_providers_client_clientId |  | ||||||
| func normalizeFlags(flags map[string]string, rootName string) map[string]string { |  | ||||||
| 	n := make(map[string]string) |  | ||||||
| 	for k, v := range flags { | 	for k, v := range flags { | ||||||
| 		fk := strings.TrimPrefix(k, "--") | 		filtered[strings.TrimPrefix(k, "--")] = v | ||||||
| 		fks := strings.SplitN(fk, "-", 3) |  | ||||||
| 		fkb := "" |  | ||||||
| 		for i, s := range strings.Split(fks[len(fks)-1], "-") { |  | ||||||
| 			if i == 0 { |  | ||||||
| 				fkb += s |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			fkb += strings.ToUpper(string([]rune(s)[0])) + string([]rune(s)[1:]) |  | ||||||
| 		} |  | ||||||
| 		fk = rootName + "_" + strings.Join(fks[:len(fks)-1], "_") + "_" + fkb |  | ||||||
| 		n[fk] = v |  | ||||||
| 	} | 	} | ||||||
| 	return n | 	return filtered | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user