Merge pull request #1201 from trheyi/main
Refactor user configuration to unify login and registration handling
This commit is contained in:
commit
e690ac95ca
6 changed files with 192 additions and 150 deletions
|
|
@ -33,8 +33,8 @@ var (
|
|||
defaultConfig *Config
|
||||
// Team configurations by locale
|
||||
teamConfigs = make(map[string]*TeamConfig)
|
||||
// Register configurations by locale
|
||||
registerConfigs = make(map[string]*RegisterConfig)
|
||||
// Entry configurations by locale (unified login + register)
|
||||
entryConfigs = make(map[string]*EntryConfig)
|
||||
// Mutex for thread safety
|
||||
configMutex sync.RWMutex
|
||||
)
|
||||
|
|
@ -50,7 +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)
|
||||
entryConfigs = make(map[string]*EntryConfig)
|
||||
|
||||
// Load signin configurations from openapi/user/signin directory
|
||||
err := loadSigninConfigs(appConfig.Root)
|
||||
|
|
@ -58,10 +58,10 @@ 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)
|
||||
// Load entry configurations from openapi/user/entry directory
|
||||
err = loadEntryConfigs(appConfig.Root)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load register configs: %v", err)
|
||||
return fmt.Errorf("failed to load entry configs: %v", err)
|
||||
}
|
||||
|
||||
// Load team configurations from openapi/user/team directory
|
||||
|
|
@ -366,57 +366,6 @@ 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)
|
||||
}
|
||||
|
||||
// Process ENV variables in the configuration
|
||||
processRegisterConfigENVVariables(&config)
|
||||
|
||||
// Copy third_party from signin config if available
|
||||
if signinConfig, exists := fullConfigs[locale]; exists && signinConfig.ThirdParty != nil {
|
||||
config.ThirdParty = signinConfig.ThirdParty
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
|
@ -500,34 +449,6 @@ 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 == "" {
|
||||
|
|
@ -680,13 +601,108 @@ func processConfigENVVariables(config *Config) {
|
|||
}
|
||||
}
|
||||
|
||||
// processRegisterConfigENVVariables processes environment variables in the register configuration
|
||||
func processRegisterConfigENVVariables(config *RegisterConfig) {
|
||||
// loadEntryConfigs loads all entry configurations from the openapi/user/entry directory
|
||||
// Entry config merges signin and register configurations
|
||||
func loadEntryConfigs(_ string) error {
|
||||
// Use Walk to find all configuration files in the entry directory
|
||||
err := application.App.Walk("openapi/user/entry", 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 entry config %s: %v", filename, err)
|
||||
}
|
||||
|
||||
// Parse the configuration
|
||||
var config EntryConfig
|
||||
err = application.Parse(filename, configRaw, &config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse entry config %s: %v", filename, err)
|
||||
}
|
||||
|
||||
// Process ENV variables in the configuration
|
||||
processEntryConfigENVVariables(&config)
|
||||
|
||||
// Store entry configuration
|
||||
entryConfigs[locale] = &config
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to walk entry directory: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processEntryConfigENVVariables processes environment variables in the entry configuration
|
||||
func processEntryConfigENVVariables(config *EntryConfig) {
|
||||
var missingEnvVars []string
|
||||
|
||||
// Process client_id and client_secret (from signin config)
|
||||
if strings.HasPrefix(config.ClientID, "$ENV.") {
|
||||
envVar := strings.TrimPrefix(config.ClientID, "$ENV.")
|
||||
if _, exists := os.LookupEnv(envVar); !exists {
|
||||
missingEnvVars = append(missingEnvVars, envVar)
|
||||
}
|
||||
}
|
||||
config.ClientID = replaceENVVar(config.ClientID)
|
||||
|
||||
if strings.HasPrefix(config.ClientSecret, "$ENV.") {
|
||||
envVar := strings.TrimPrefix(config.ClientSecret, "$ENV.")
|
||||
if _, exists := os.LookupEnv(envVar); !exists {
|
||||
missingEnvVars = append(missingEnvVars, envVar)
|
||||
}
|
||||
}
|
||||
config.ClientSecret = replaceENVVar(config.ClientSecret)
|
||||
|
||||
// Process form configuration
|
||||
missingEnvVars := processFormConfigENVVariables(config.Form)
|
||||
formMissingVars := processFormConfigENVVariables(config.Form)
|
||||
missingEnvVars = append(missingEnvVars, formMissingVars...)
|
||||
|
||||
// Log warning for missing environment variables
|
||||
if len(missingEnvVars) > 0 {
|
||||
fmt.Printf("Warning: The following environment variables are not set in register configuration: %v\n", missingEnvVars)
|
||||
fmt.Printf("Warning: The following environment variables are not set in entry configuration: %v\n", missingEnvVars)
|
||||
}
|
||||
}
|
||||
|
||||
// GetEntryConfig returns the entry configuration for a given locale
|
||||
func GetEntryConfig(locale string) *EntryConfig {
|
||||
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 := entryConfigs[locale]; exists {
|
||||
return config
|
||||
}
|
||||
|
||||
// If no specific locale, try to get "en" as default
|
||||
if config, exists := entryConfigs["en"]; exists {
|
||||
return config
|
||||
}
|
||||
|
||||
// If "en" is not available, try to get any available configuration
|
||||
for _, config := range entryConfigs {
|
||||
return config
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
62
openapi/user/entry.go
Normal file
62
openapi/user/entry.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yaoapp/yao/openapi/response"
|
||||
"github.com/yaoapp/yao/openapi/utils"
|
||||
)
|
||||
|
||||
// getEntryConfig is the handler for get unified auth entry configuration
|
||||
func getEntryConfig(c *gin.Context) {
|
||||
// Get locale from query parameter (optional)
|
||||
locale := c.Query("locale")
|
||||
|
||||
// Get entry configuration for the specified locale
|
||||
config := GetEntryConfig(locale)
|
||||
|
||||
// Set session id if not exists
|
||||
sid := utils.GetSessionID(c)
|
||||
if sid == "" {
|
||||
sid = generateSessionID()
|
||||
response.SendSessionCookie(c, sid)
|
||||
}
|
||||
|
||||
// If no configuration found, return error
|
||||
if config == nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "No entry configuration found for the requested locale",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusNotFound, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Create public config without sensitive data
|
||||
publicConfig := *config
|
||||
publicConfig.ClientSecret = "" // Remove sensitive data
|
||||
|
||||
// Remove captcha secret from public config
|
||||
if publicConfig.Form != nil && publicConfig.Form.Captcha != nil && publicConfig.Form.Captcha.Options != nil {
|
||||
// Create a copy of captcha options without the secret
|
||||
captchaOptions := make(map[string]interface{})
|
||||
for k, v := range publicConfig.Form.Captcha.Options {
|
||||
if k != "secret" {
|
||||
captchaOptions[k] = v
|
||||
}
|
||||
}
|
||||
publicConfig.Form.Captcha.Options = captchaOptions
|
||||
}
|
||||
|
||||
// Return the entry configuration
|
||||
response.RespondWithSuccess(c, response.StatusOK, publicConfig)
|
||||
}
|
||||
|
||||
// entry is the handler for unified auth entry (login/register)
|
||||
// The backend determines whether this is a login or registration based on email existence
|
||||
func entry(c *gin.Context) {
|
||||
// This is a placeholder - you may need to implement the actual login/register logic here
|
||||
// The logic should:
|
||||
// 1. Check if the email exists in the database
|
||||
// 2. If exists: proceed with login flow
|
||||
// 3. If not exists: proceed with registration flow
|
||||
}
|
||||
|
|
@ -87,14 +87,14 @@ 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/")
|
||||
// Get entry configuration for role and type
|
||||
entryConfig := GetEntryConfig(locale)
|
||||
if entryConfig == nil {
|
||||
// If no entry config found, try to get default entry config
|
||||
log.Warn("Entry configuration not found for locale '%s', trying default locale 'en'", locale)
|
||||
entryConfig = GetEntryConfig("en")
|
||||
if entryConfig == nil {
|
||||
return nil, fmt.Errorf("entry configuration not found. Please create entry config files in openapi/user/entry/")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,8 +120,8 @@ func LoginThirdParty(providerID string, userinfo *oauthtypes.OIDCUserInfo, login
|
|||
"given_name": userinfo.GivenName,
|
||||
"family_name": userinfo.FamilyName,
|
||||
"picture": userinfo.Picture,
|
||||
"role_id": registerConfig.Role,
|
||||
"type_id": registerConfig.Type,
|
||||
"role_id": entryConfig.Role,
|
||||
"type_id": entryConfig.Type,
|
||||
"status": "active",
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yaoapp/yao/openapi/response"
|
||||
"github.com/yaoapp/yao/openapi/utils"
|
||||
)
|
||||
|
||||
// getRegisterConfig is the handler for get register configuration
|
||||
func getRegisterConfig(c *gin.Context) {
|
||||
// Get locale from query parameter (optional)
|
||||
locale := c.Query("locale")
|
||||
|
||||
// Get register configuration for the specified locale (already includes third_party from signin config)
|
||||
config := GetRegisterConfig(locale)
|
||||
|
||||
// Set session id if not exists
|
||||
sid := utils.GetSessionID(c)
|
||||
if sid == "" {
|
||||
sid = generateSessionID()
|
||||
response.SendSessionCookie(c, sid)
|
||||
}
|
||||
|
||||
// If no configuration found, return error
|
||||
if config == nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "No register configuration found for the requested locale",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusNotFound, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Return the register configuration
|
||||
response.RespondWithSuccess(c, response.StatusOK, config)
|
||||
}
|
||||
|
||||
// register is the handler for user registration
|
||||
func register(c *gin.Context) {
|
||||
// This is a placeholder - you may need to implement the actual registration logic here
|
||||
}
|
||||
|
|
@ -84,20 +84,25 @@ type ProviderRegisterConfig struct {
|
|||
Auto bool `json:"auto,omitempty"`
|
||||
}
|
||||
|
||||
// RegisterConfig represents the register configuration
|
||||
type RegisterConfig struct {
|
||||
// EntryConfig represents the unified auth entry configuration (login + register)
|
||||
// This merges signin and register configurations into a single entry point
|
||||
type EntryConfig struct {
|
||||
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
|
||||
LogoutRedirect string `json:"logout_redirect,omitempty"` // From signin config
|
||||
ClientID string `json:"client_id,omitempty"` // From signin config
|
||||
ClientSecret string `json:"client_secret,omitempty"` // From signin config (not exposed to frontend)
|
||||
AutoLogin bool `json:"auto_login,omitempty"` // From register config
|
||||
Role string `json:"role,omitempty"` // From register config
|
||||
Type string `json:"type,omitempty"` // From register config - User type id
|
||||
Form *FormConfig `json:"form,omitempty"`
|
||||
Messenger *MessengerConfig `json:"messenger,omitempty"`
|
||||
InviteRequired bool `json:"invite_required,omitempty"`
|
||||
ThirdParty *ThirdParty `json:"third_party,omitempty"` // Third party login configuration (copied from signin config)
|
||||
Token *TokenConfig `json:"token,omitempty"` // From signin config
|
||||
Messenger *MessengerConfig `json:"messenger,omitempty"` // From register config
|
||||
InviteRequired bool `json:"invite_required,omitempty"` // From register config
|
||||
ThirdParty *ThirdParty `json:"third_party,omitempty"`
|
||||
}
|
||||
|
||||
// MessengerConfig represents the messenger configuration for user registration
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ func Attach(group *gin.RouterGroup, oauth types.OAuth) {
|
|||
group.GET("/login", getLoginConfig) // Get login page config (public) - migrated from /signin
|
||||
group.POST("/login", login) // User login (public) - migrated from /signin
|
||||
group.GET("/login/captcha", getCaptcha) // Get captcha for login (public)
|
||||
group.GET("/register", getRegisterConfig) // Get register page config (public)
|
||||
group.POST("/register", register) // User register (public)
|
||||
group.GET("/entry", getEntryConfig) // Get unified auth entry config (public)
|
||||
group.POST("/entry", entry) // Unified auth entry (login/register) (public)
|
||||
group.POST("/logout", oauth.Guard, placeholder) // User logout
|
||||
|
||||
// Logined User Settings
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue