Merge pull request #1195 from trheyi/main

Add register configuration support for user management
This commit is contained in:
Max 2025-10-13 16:44:56 +08:00 committed by GitHub
commit 910770263d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 133 additions and 23 deletions

View file

@ -33,6 +33,8 @@ var (
defaultConfig *Config
// Team configurations by locale
teamConfigs = make(map[string]*TeamConfig)
// Register configurations by locale
registerConfigs = make(map[string]*RegisterConfig)
// Mutex for thread safety
configMutex sync.RWMutex
)
@ -48,6 +50,7 @@ func Load(appConfig config.Config) error {
providers = make(map[string]*Provider)
defaultConfig = nil
teamConfigs = make(map[string]*TeamConfig)
registerConfigs = make(map[string]*RegisterConfig)
// Load signin configurations from openapi/user/signin directory
err := loadSigninConfigs(appConfig.Root)
@ -55,6 +58,12 @@ func Load(appConfig config.Config) error {
return fmt.Errorf("failed to load signin configs: %v", err)
}
// Load register configurations from openapi/user/register directory
err = loadRegisterConfigs(appConfig.Root)
if err != nil {
return fmt.Errorf("failed to load register configs: %v", err)
}
// Load team configurations from openapi/user/team directory
err = loadTeamConfigs(appConfig.Root)
if err != nil {
@ -357,6 +366,49 @@ func loadTeamConfigs(_ string) error {
return nil
}
// loadRegisterConfigs loads all register configurations from the openapi/user/register directory
func loadRegisterConfigs(_ string) error {
// Use Walk to find all configuration files in the register directory
err := application.App.Walk("openapi/user/register", func(root, filename string, isdir bool) error {
if isdir {
return nil
}
// Only process .yao files
if !strings.HasSuffix(filename, ".yao") {
return nil
}
// Extract locale from filename (basename without extension)
baseName := filepath.Base(filename)
locale := strings.ToLower(strings.TrimSuffix(baseName, ".yao"))
// Read configuration
configRaw, err := application.App.Read(filename)
if err != nil {
return fmt.Errorf("failed to read register config %s: %v", filename, err)
}
// Parse the configuration
var config RegisterConfig
err = application.Parse(filename, configRaw, &config)
if err != nil {
return fmt.Errorf("failed to parse register config %s: %v", filename, err)
}
// Store register configuration
registerConfigs[locale] = &config
return nil
})
if err != nil {
return fmt.Errorf("failed to walk register directory: %v", err)
}
return nil
}
// GetPublicConfig returns the public configuration for a given locale
func GetPublicConfig(locale string) *Config {
configMutex.RLock()
@ -440,6 +492,34 @@ func GetTeamConfig(locale string) *TeamConfig {
return nil
}
// GetRegisterConfig returns the register configuration for a given locale
func GetRegisterConfig(locale string) *RegisterConfig {
configMutex.RLock()
defer configMutex.RUnlock()
// Normalize language code to lowercase
if locale != "" {
locale = strings.TrimSpace(strings.ToLower(locale))
}
// Try to get the specific locale configuration
if config, exists := registerConfigs[locale]; exists {
return config
}
// If no specific locale, try to get "en" as default
if config, exists := registerConfigs["en"]; exists {
return config
}
// If "en" is not available, try to get any available configuration
for _, config := range registerConfigs {
return config
}
return nil
}
// extractEnvVarName extracts the environment variable name from a string like "$ENV.VAR_NAME"
func extractEnvVarName(value string) string {
if value == "" {

View file

@ -79,7 +79,7 @@ func getCaptcha(c *gin.Context) {
}
// LoginThirdParty is the handler for third party login
func LoginThirdParty(providerID string, userinfo *oauthtypes.OIDCUserInfo, loginCtx *LoginContext) (*LoginResponse, error) {
func LoginThirdParty(providerID string, userinfo *oauthtypes.OIDCUserInfo, loginCtx *LoginContext, locale string) (*LoginResponse, error) {
// Get provider
provider, err := GetProvider(providerID)
@ -87,6 +87,17 @@ func LoginThirdParty(providerID string, userinfo *oauthtypes.OIDCUserInfo, login
return nil, err
}
// Get register configuration for role and type
registerConfig := GetRegisterConfig(locale)
if registerConfig == nil {
// If no register config found, try to get default register config
log.Warn("Register configuration not found for locale '%s', trying default locale 'en'", locale)
registerConfig = GetRegisterConfig("en")
if registerConfig == nil {
return nil, fmt.Errorf("register configuration not found. Please create register config files in openapi/user/register/")
}
}
// Check if user exists
userProvider, err := oauth.OAuth.GetUserProvider()
if err != nil {
@ -109,8 +120,8 @@ func LoginThirdParty(providerID string, userinfo *oauthtypes.OIDCUserInfo, login
"given_name": userinfo.GivenName,
"family_name": userinfo.FamilyName,
"picture": userinfo.Picture,
"role_id": provider.Register.Role,
"type_id": provider.Register.Type,
"role_id": registerConfig.Role,
"type_id": registerConfig.Type,
"status": "active",
}

View file

@ -174,7 +174,14 @@ func authback(c *gin.Context) {
// LoginThirdParty(providerID, userInfo)
loginCtx := makeLoginContext(c)
loginResponse, err := LoginThirdParty(providerID, userInfo, loginCtx)
// Use locale from params, fallback to "en" if not provided
locale := params.Locale
if locale == "" {
locale = "en"
}
loginResponse, err := LoginThirdParty(providerID, userInfo, loginCtx, locale)
if err != nil {
errorResp := &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,

View file

@ -77,11 +77,23 @@ type ThirdParty struct {
Providers []*Provider `json:"providers,omitempty"`
}
// RegisterConfig represents the auto register configuration
// ProviderRegisterConfig represents the auto register configuration in provider
type ProviderRegisterConfig struct {
Auto bool `json:"auto,omitempty"`
}
// RegisterConfig represents the register configuration
type RegisterConfig struct {
Auto bool `json:"auto,omitempty"`
Role string `json:"role,omitempty"`
Type string `json:"type,omitempty"` // User type id
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Default bool `json:"default,omitempty"`
SuccessURL string `json:"success_url,omitempty"`
FailureURL string `json:"failure_url,omitempty"`
AutoLogin bool `json:"auto_login,omitempty"`
Role string `json:"role,omitempty"`
Type string `json:"type,omitempty"` // User type id
Form *FormConfig `json:"form,omitempty"`
ConfirmPassword *PasswordConfig `json:"confirm_password,omitempty"`
}
// YaoClientConfig represents the Yao OpenAPI Client config
@ -95,21 +107,21 @@ type YaoClientConfig struct {
// Provider represents a third party login provider
type Provider struct {
ID string `json:"id,omitempty"`
Label string `json:"label,omitempty"`
Title string `json:"title,omitempty"`
Logo string `json:"logo,omitempty"`
Color string `json:"color,omitempty"`
TextColor string `json:"text_color,omitempty"`
ClientID string `json:"client_id,omitempty"`
ClientSecret string `json:"client_secret,omitempty"`
ClientSecretGenerator *SecretGenerator `json:"client_secret_generator,omitempty"`
Scopes []string `json:"scopes,omitempty"`
ResponseMode string `json:"response_mode,omitempty"`
UserInfoSource string `json:"user_info_source,omitempty"` // "endpoint" (default) | "id_token" | "access_token"
Endpoints *Endpoints `json:"endpoints,omitempty"`
Mapping interface{} `json:"mapping,omitempty"` // string (preset) | map[string]string (custom) | nil (generic)
Register *RegisterConfig `json:"register,omitempty"`
ID string `json:"id,omitempty"`
Label string `json:"label,omitempty"`
Title string `json:"title,omitempty"`
Logo string `json:"logo,omitempty"`
Color string `json:"color,omitempty"`
TextColor string `json:"text_color,omitempty"`
ClientID string `json:"client_id,omitempty"`
ClientSecret string `json:"client_secret,omitempty"`
ClientSecretGenerator *SecretGenerator `json:"client_secret_generator,omitempty"`
Scopes []string `json:"scopes,omitempty"`
ResponseMode string `json:"response_mode,omitempty"`
UserInfoSource string `json:"user_info_source,omitempty"` // "endpoint" (default) | "id_token" | "access_token"
Endpoints *Endpoints `json:"endpoints,omitempty"`
Mapping interface{} `json:"mapping,omitempty"` // string (preset) | map[string]string (custom) | nil (generic)
Register *ProviderRegisterConfig `json:"register,omitempty"`
}
// SecretGenerator represents the client secret generator configuration