package user import ( "fmt" "strconv" "strings" "time" "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/session" "github.com/yaoapp/kun/exception" ) // Session Utilities // GetUserIDFromSession gets the current user ID from session // Returns the user ID string or throws an exception if not authenticated func GetUserIDFromSession(process *process.Process) string { sessionData, err := session.Global().ID(process.Sid).Get("__user_id") if err != nil || sessionData == nil { exception.New("user not authenticated", 401).Throw() } userIDStr, ok := sessionData.(string) if !ok { exception.New("invalid user_id in session", 401).Throw() } return userIDStr } // Type Conversion Utilities // toBool converts various types to boolean // Supports: bool, int, int64, float64, string // Returns false for nil or unsupported types func toBool(v interface{}) bool { if v == nil { return false } switch val := v.(type) { case bool: return val case int: return val != 0 case int64: return val != 0 case float64: return val != 0 case string: return val == "true" || val == "1" default: return false } } // toString converts various types to string // Supports: string, int, int64, float64, bool // Returns empty string for nil or unsupported types func toString(v interface{}) string { if v == nil { return "" } switch val := v.(type) { case string: return val case int: return fmt.Sprintf("%d", val) case int64: return fmt.Sprintf("%d", val) case float64: return fmt.Sprintf("%.0f", val) case bool: if val { return "true" } return "false" default: return "" } } // toInt64 converts various types to int64 // Supports: int, int64, float64, string // Returns 0 for nil or unsupported types func toInt64(v interface{}) int64 { if v == nil { return 0 } switch val := v.(type) { case int64: return val case int: return int64(val) case float64: return int64(val) case string: if parsed, err := strconv.ParseInt(val, 10, 64); err == nil { return parsed } return 0 default: return 0 } } // toTimeString converts various time types to RFC3339 string // Supports: time.Time, string, int64 (unix timestamp) // Returns empty string for nil or unsupported types func toTimeString(v interface{}) string { if v == nil { return "" } switch val := v.(type) { case time.Time: if val.IsZero() { return "" } return val.Format(time.RFC3339) case string: // Try to parse as RFC3339 first if t, err := time.Parse(time.RFC3339, val); err == nil { return t.Format(time.RFC3339) } // Try to parse as other common formats formats := []string{ "2006-01-02 15:04:05", "2006-01-02T15:04:05Z", "2006-01-02T15:04:05.000Z", } for _, format := range formats { if t, err := time.Parse(format, val); err == nil { return t.Format(time.RFC3339) } } return val // Return as-is if can't parse case int64: // Assume unix timestamp if val > 0 { return time.Unix(val, 0).Format(time.RFC3339) } return "" default: return "" } } // Security Utilities // maskEmail masks an email address for privacy protection // Keeps the first and last character of the local part, masks the middle with *** // Examples: // - "john.doe@example.com" -> "j***e@example.com" // - "a@example.com" -> "a***@example.com" // - "ab@example.com" -> "a***b@example.com" // // Returns empty string for invalid email or empty input func maskEmail(email string) string { if email == "" { return "" } // Split email into local and domain parts parts := strings.Split(email, "@") if len(parts) != 2 || parts[0] == "" || parts[1] == "" { return "" // Invalid email format } local := parts[0] domain := parts[1] // Mask the local part var masked string localLen := len(local) switch localLen { case 1: // Single character: show it with *** masked = local + "***" case 2: // Two characters: show first + *** + last masked = string(local[0]) + "***" + string(local[1]) default: // Three or more characters: show first + *** + last masked = string(local[0]) + "***" + string(local[localLen-1]) } return masked + "@" + domain }