sdd
This commit is contained in:
866
pkg/api/email_api.go
Normal file
866
pkg/api/email_api.go
Normal file
@@ -0,0 +1,866 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 初始化随机数生成器
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// Client 表示Stalwart邮件API客户端
|
||||
type Client struct {
|
||||
Config *Config
|
||||
APIBaseURL string
|
||||
}
|
||||
|
||||
// NewClient 创建新的API客户端
|
||||
func NewClient(config *Config) *Client {
|
||||
client := &Client{
|
||||
Config: config,
|
||||
APIBaseURL: config.API.BaseURL,
|
||||
}
|
||||
|
||||
if config.API.DisableProxy {
|
||||
DisableProxy()
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// AuthType 认证类型
|
||||
type AuthType string
|
||||
|
||||
const (
|
||||
// BasicAuth 表示基本认证
|
||||
BasicAuth AuthType = "basic"
|
||||
// APIKeyAuth 表示API Key认证
|
||||
APIKeyAuth AuthType = "apikey"
|
||||
)
|
||||
|
||||
// User 表示用户信息
|
||||
type User struct {
|
||||
ID int `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Quota int64 `json:"quota"`
|
||||
Emails []string `json:"emails"`
|
||||
MemberOf []string `json:"memberOf"`
|
||||
Roles []string `json:"roles"`
|
||||
Secrets []string `json:"secrets"`
|
||||
}
|
||||
|
||||
// UserList 表示用户列表响应
|
||||
type UserList struct {
|
||||
Data struct {
|
||||
Items []User `json:"items"`
|
||||
Total int `json:"total"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// APIResponse 表示通用API响应
|
||||
type APIResponse struct {
|
||||
Data json.RawMessage `json:"data"`
|
||||
Error string `json:"error"`
|
||||
Message string `json:"message"`
|
||||
Field string `json:"field"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// GenerateRandomUsername 生成随机用户名
|
||||
func GenerateRandomUsername(length int) string {
|
||||
// 常见的英文名
|
||||
firstNames := []string{
|
||||
"alex", "bob", "chris", "david", "eric", "frank", "gary", "henry",
|
||||
"ian", "jack", "kevin", "leo", "mike", "nick", "oliver", "peter",
|
||||
"ryan", "sam", "tom", "victor", "william", "zack",
|
||||
"anna", "betty", "cathy", "diana", "emma", "fiona", "grace", "helen",
|
||||
"irene", "jane", "kate", "lily", "mary", "nina", "olivia", "penny",
|
||||
"queen", "rose", "sarah", "tina", "uma", "vicky", "wendy", "zoe",
|
||||
}
|
||||
|
||||
// 常见的姓氏
|
||||
lastNames := []string{
|
||||
"smith", "johnson", "williams", "jones", "brown", "davis", "miller",
|
||||
"wilson", "taylor", "clark", "hall", "lee", "allen", "young", "king",
|
||||
"wright", "hill", "scott", "green", "adams", "baker", "carter", "cook",
|
||||
}
|
||||
|
||||
// 随机选择名和姓
|
||||
firstName := firstNames[rand.Intn(len(firstNames))]
|
||||
lastName := lastNames[rand.Intn(len(lastNames))]
|
||||
|
||||
// 添加1-999的随机数字
|
||||
number := rand.Intn(999) + 1
|
||||
|
||||
// 组合成用户名 - 不使用特殊字符,直接连接
|
||||
username := fmt.Sprintf("%s%s%d", firstName, lastName, number)
|
||||
|
||||
// 如果指定了长度且用户名太长,就截断
|
||||
if length > 0 && len(username) > length {
|
||||
username = username[:length]
|
||||
}
|
||||
|
||||
return username
|
||||
}
|
||||
|
||||
// GenerateRandomPassword 生成随机密码
|
||||
func GenerateRandomPassword(length int) string {
|
||||
if length <= 0 {
|
||||
length = 10 // 默认10位密码
|
||||
}
|
||||
|
||||
// 简单词汇列表
|
||||
words := []string{
|
||||
"apple", "blue", "cat", "dog", "easy", "fish", "good", "home",
|
||||
"idea", "jump", "king", "love", "moon", "nice", "open", "park",
|
||||
"queen", "red", "star", "time", "user", "view", "work", "year",
|
||||
}
|
||||
|
||||
// 随机选择一个词
|
||||
word := words[rand.Intn(len(words))]
|
||||
|
||||
// 确保词不超过长度限制的一半
|
||||
if len(word) > length/2 {
|
||||
word = word[:length/2]
|
||||
}
|
||||
|
||||
// 为剩余长度生成数字和特殊字符
|
||||
remainingLength := length - len(word)
|
||||
numbers := "0123456789"
|
||||
specials := "@#$%&*!"
|
||||
|
||||
var password strings.Builder
|
||||
password.WriteString(word)
|
||||
|
||||
// 添加至少一个特殊字符
|
||||
password.WriteByte(specials[rand.Intn(len(specials))])
|
||||
remainingLength--
|
||||
|
||||
// 添加至少一个数字
|
||||
password.WriteByte(numbers[rand.Intn(len(numbers))])
|
||||
remainingLength--
|
||||
|
||||
// 填充剩余长度
|
||||
for i := 0; i < remainingLength; i++ {
|
||||
// 使用大小写字母、数字组合
|
||||
allChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
password.WriteByte(allChars[rand.Intn(len(allChars))])
|
||||
}
|
||||
|
||||
// 将密码字符打乱顺序
|
||||
passwordBytes := []byte(password.String())
|
||||
rand.Shuffle(len(passwordBytes), func(i, j int) {
|
||||
passwordBytes[i], passwordBytes[j] = passwordBytes[j], passwordBytes[i]
|
||||
})
|
||||
|
||||
return string(passwordBytes)
|
||||
}
|
||||
|
||||
// ParseEmail 解析邮箱地址
|
||||
func (c *Client) ParseEmail(email string) (string, string) {
|
||||
parts := strings.Split(email, "@")
|
||||
if len(parts) == 2 {
|
||||
return parts[0], parts[1]
|
||||
}
|
||||
return email, c.Config.User.DefaultDomain
|
||||
}
|
||||
|
||||
// PrepareUserData 准备用户数据
|
||||
func (c *Client) PrepareUserData(email, description string, password string, quota int64) map[string]interface{} {
|
||||
if password == "" {
|
||||
// 不再使用默认密码,而是生成随机密码
|
||||
password = GenerateRandomPassword(12)
|
||||
}
|
||||
|
||||
if quota == 0 {
|
||||
quota = c.Config.User.DefaultQuota
|
||||
}
|
||||
|
||||
if description == "" {
|
||||
description = fmt.Sprintf("%s account", email)
|
||||
}
|
||||
|
||||
// 准备用户数据
|
||||
return map[string]interface{}{
|
||||
"type": "individual",
|
||||
"name": email,
|
||||
"description": description,
|
||||
"quota": quota,
|
||||
"emails": []string{email},
|
||||
"memberOf": []string{},
|
||||
"roles": []string{"user"},
|
||||
"secrets": []string{password},
|
||||
}
|
||||
}
|
||||
|
||||
// GetAuthHeaders 获取认证头
|
||||
func (c *Client) GetAuthHeaders(authType AuthType, username, password, apiKey string) map[string]string {
|
||||
headers := map[string]string{
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
if authType == APIKeyAuth {
|
||||
// API Key认证
|
||||
if apiKey == "" {
|
||||
apiKey = c.Config.Auth.APIKey
|
||||
}
|
||||
|
||||
fmt.Printf("使用的API Key: %s\n", apiKey)
|
||||
|
||||
// 检查是否已经是完整的授权头
|
||||
if strings.HasPrefix(apiKey, "Bearer ") {
|
||||
headers["Authorization"] = apiKey
|
||||
} else if strings.HasPrefix(apiKey, "api_") {
|
||||
// 使用标准格式 api_XXX
|
||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
||||
fmt.Println("使用标准API Key格式: Bearer api_XXX")
|
||||
} else {
|
||||
// 可能是纯Token,尝试直接使用
|
||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
||||
fmt.Println("使用Token格式: Bearer XXX")
|
||||
}
|
||||
} else {
|
||||
// 基本认证
|
||||
if username == "" {
|
||||
username = c.Config.Auth.Basic.AdminUsername
|
||||
}
|
||||
if password == "" {
|
||||
password = c.Config.Auth.Basic.AdminPassword
|
||||
}
|
||||
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
|
||||
headers["Authorization"] = fmt.Sprintf("Basic %s", auth)
|
||||
}
|
||||
|
||||
fmt.Println("使用的认证头:", headers["Authorization"])
|
||||
return headers
|
||||
}
|
||||
|
||||
// MakeAPIRequest 发送API请求
|
||||
func (c *Client) MakeAPIRequest(method, endpoint string, data interface{}, headers map[string]string) (*http.Response, []byte, error) {
|
||||
url := fmt.Sprintf("%s/%s", c.APIBaseURL, strings.TrimPrefix(endpoint, "/"))
|
||||
fmt.Printf("发送请求: %s %s\n", method, url)
|
||||
|
||||
if headers["Authorization"] != "" {
|
||||
authType := strings.Split(headers["Authorization"], " ")[0]
|
||||
fmt.Printf("认证方式: %s\n", authType)
|
||||
}
|
||||
|
||||
var reqBody []byte
|
||||
var err error
|
||||
if data != nil {
|
||||
reqBody, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("序列化请求数据失败: %w", err)
|
||||
}
|
||||
fmt.Printf("请求数据: %s\n", string(reqBody))
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(reqBody))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("创建HTTP请求失败: %w", err)
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
for key, value := range headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("发送HTTP请求失败: %w", err)
|
||||
}
|
||||
|
||||
// 读取响应
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return resp, nil, fmt.Errorf("读取响应失败: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("响应状态码: %d\n", resp.StatusCode)
|
||||
fmt.Printf("响应内容: %s\n", string(respBody))
|
||||
|
||||
// 检查认证错误
|
||||
if resp.StatusCode == 401 || resp.StatusCode == 403 {
|
||||
fmt.Println("认证失败,可能需要尝试其他认证方式")
|
||||
}
|
||||
|
||||
return resp, respBody, nil
|
||||
}
|
||||
|
||||
// CreateUser 创建邮箱用户
|
||||
func (c *Client) CreateUser(email, password, description string, quota int64, authType AuthType, username, authPassword, apiKey string) (int, map[string]interface{}, error) {
|
||||
// 准备用户数据
|
||||
userData := c.PrepareUserData(email, description, password, quota)
|
||||
|
||||
fmt.Printf("创建用户 %s 并设置密码...\n", email)
|
||||
|
||||
// 获取认证头
|
||||
headers := c.GetAuthHeaders(authType, username, authPassword, apiKey)
|
||||
|
||||
// 创建用户
|
||||
resp, respBody, err := c.MakeAPIRequest("POST", "/principal", userData, headers)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("创建用户请求失败: %w", err)
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
return 0, nil, fmt.Errorf("创建用户失败:未收到响应")
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var apiResp APIResponse
|
||||
if err := json.Unmarshal(respBody, &apiResp); err != nil {
|
||||
return 0, nil, fmt.Errorf("解析响应失败: %w", err)
|
||||
}
|
||||
|
||||
// 检查是否返回错误
|
||||
if apiResp.Error != "" {
|
||||
// 特殊处理:如果用户已存在,可以尝试获取已存在用户的ID
|
||||
if apiResp.Error == "fieldAlreadyExists" && apiResp.Field == "name" {
|
||||
existingName := apiResp.Value
|
||||
fmt.Printf("用户 %s 已存在,尝试获取现有用户信息...\n", existingName)
|
||||
|
||||
// 构造一个模拟信息
|
||||
userInfo := map[string]interface{}{
|
||||
"name": email,
|
||||
"emails": []string{email},
|
||||
"description": description,
|
||||
"quota": quota,
|
||||
"_note": "This user already exists",
|
||||
}
|
||||
|
||||
// 返回一个特殊标记的ID(负数)表示这是一个已存在的用户
|
||||
return -1, userInfo, nil
|
||||
}
|
||||
|
||||
return 0, nil, fmt.Errorf("创建用户失败: %s - %s", apiResp.Error, apiResp.Message)
|
||||
}
|
||||
|
||||
// 获取用户ID
|
||||
var userID int
|
||||
if err := json.Unmarshal(apiResp.Data, &userID); err != nil {
|
||||
// 尝试解析为对象
|
||||
var objData map[string]interface{}
|
||||
if err := json.Unmarshal(apiResp.Data, &objData); err != nil {
|
||||
return 0, nil, fmt.Errorf("解析用户ID失败: %w", err)
|
||||
}
|
||||
|
||||
// 检查是否有id字段
|
||||
if id, ok := objData["id"].(float64); ok {
|
||||
userID = int(id)
|
||||
} else {
|
||||
return 0, nil, fmt.Errorf("未能从响应中获取用户ID")
|
||||
}
|
||||
}
|
||||
|
||||
if userID == 0 {
|
||||
return 0, nil, fmt.Errorf("创建用户失败:未获取到用户ID")
|
||||
}
|
||||
|
||||
fmt.Printf("用户创建成功!ID: %d\n", userID)
|
||||
|
||||
// 尝试获取用户详情进行验证
|
||||
userInfo, err := c.GetUserDetails(userID, authType, username, authPassword, apiKey)
|
||||
if err != nil || userInfo == nil {
|
||||
fmt.Println("获取用户详情失败,但用户创建成功")
|
||||
// 创建一个包含基本信息的对象
|
||||
userInfo = map[string]interface{}{
|
||||
"name": email,
|
||||
"emails": []string{email},
|
||||
"description": description,
|
||||
"quota": quota,
|
||||
"_note": "This is a synthetic record as actual details could not be retrieved",
|
||||
}
|
||||
}
|
||||
|
||||
return userID, userInfo, nil
|
||||
}
|
||||
|
||||
// GetUserDetails 获取用户详情
|
||||
func (c *Client) GetUserDetails(userID int, authType AuthType, username, authPassword, apiKey string) (map[string]interface{}, error) {
|
||||
fmt.Println("\n获取用户详情...")
|
||||
|
||||
// 获取认证头
|
||||
headers := c.GetAuthHeaders(authType, username, authPassword, apiKey)
|
||||
|
||||
// 获取用户详情
|
||||
endpoint := fmt.Sprintf("/principal/%d", userID)
|
||||
resp, respBody, err := c.MakeAPIRequest("GET", endpoint, nil, headers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取用户详情请求失败: %w", err)
|
||||
}
|
||||
|
||||
if resp == nil || resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("获取用户详情失败")
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var apiResp APIResponse
|
||||
if err := json.Unmarshal(respBody, &apiResp); err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %w", err)
|
||||
}
|
||||
|
||||
// 检查是否返回错误
|
||||
if apiResp.Error != "" {
|
||||
return nil, fmt.Errorf("获取用户详情失败: %s - %s", apiResp.Error, apiResp.Message)
|
||||
}
|
||||
|
||||
// 解析用户数据
|
||||
var userData map[string]interface{}
|
||||
if err := json.Unmarshal(apiResp.Data, &userData); err != nil {
|
||||
// 如果无法解析为对象,直接返回整个响应
|
||||
var rawResp map[string]interface{}
|
||||
if err := json.Unmarshal(respBody, &rawResp); err != nil {
|
||||
return nil, fmt.Errorf("解析用户数据失败: %w", err)
|
||||
}
|
||||
return rawResp, nil
|
||||
}
|
||||
|
||||
if userData == nil {
|
||||
return nil, fmt.Errorf("获取用户详情失败:响应中没有用户数据")
|
||||
}
|
||||
|
||||
// 打印关键信息
|
||||
fmt.Printf("用户名: %v\n", userData["name"])
|
||||
emails, _ := userData["emails"].([]interface{})
|
||||
if len(emails) > 0 {
|
||||
fmt.Printf("邮箱: %v\n", emails[0])
|
||||
}
|
||||
|
||||
return userData, nil
|
||||
}
|
||||
|
||||
// ListUsers 获取用户列表
|
||||
func (c *Client) ListUsers(authType AuthType, username, authPassword, apiKey string, limit, page int) (*UserList, error) {
|
||||
fmt.Println("获取用户列表...")
|
||||
|
||||
// 获取认证头
|
||||
headers := c.GetAuthHeaders(authType, username, authPassword, apiKey)
|
||||
|
||||
// 构建查询参数
|
||||
endpoint := fmt.Sprintf("/principal?limit=%d&page=%d", limit, page)
|
||||
|
||||
// 尝试不同的API端点路径
|
||||
endpoints := []string{
|
||||
endpoint, // 根据规范文档的主要端点
|
||||
"/principals", // 原始尝试
|
||||
"/users", // 通用users格式
|
||||
"/user", // 单数形式
|
||||
"/accounts", // 有些系统用accounts
|
||||
"/directory", // 目录形式
|
||||
}
|
||||
|
||||
var resp *http.Response
|
||||
var respBody []byte
|
||||
var err error
|
||||
var success bool
|
||||
|
||||
for _, ep := range endpoints {
|
||||
fmt.Printf("尝试端点: %s\n", ep)
|
||||
resp, respBody, err = c.MakeAPIRequest("GET", ep, nil, headers)
|
||||
if err == nil && resp != nil && resp.StatusCode == 200 {
|
||||
fmt.Printf("成功的端点: %s\n", ep)
|
||||
success = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !success {
|
||||
return nil, fmt.Errorf("获取用户列表失败,尝试了所有可能的端点")
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
fmt.Println("响应数据结构:")
|
||||
fmt.Println(string(respBody))
|
||||
|
||||
var userList UserList
|
||||
if err := json.Unmarshal(respBody, &userList); err != nil {
|
||||
// 尝试解析为不同格式
|
||||
var rawResp map[string]interface{}
|
||||
if err := json.Unmarshal(respBody, &rawResp); err != nil {
|
||||
return nil, fmt.Errorf("解析用户列表失败: %w", err)
|
||||
}
|
||||
|
||||
// 尝试不同的数据格式
|
||||
users := []User{}
|
||||
|
||||
// 检查data.items格式
|
||||
if data, ok := rawResp["data"].(map[string]interface{}); ok {
|
||||
if items, ok := data["items"].([]interface{}); ok {
|
||||
for _, item := range items {
|
||||
if user, ok := item.(map[string]interface{}); ok {
|
||||
userObj := parseUserObject(user)
|
||||
users = append(users, userObj)
|
||||
}
|
||||
}
|
||||
userList.Data.Items = users
|
||||
userList.Data.Total = len(users)
|
||||
return &userList, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 检查data数组格式
|
||||
if data, ok := rawResp["data"].([]interface{}); ok {
|
||||
for _, item := range data {
|
||||
if user, ok := item.(map[string]interface{}); ok {
|
||||
userObj := parseUserObject(user)
|
||||
users = append(users, userObj)
|
||||
}
|
||||
}
|
||||
userList.Data.Items = users
|
||||
userList.Data.Total = len(users)
|
||||
return &userList, nil
|
||||
}
|
||||
|
||||
// 检查principals数组格式
|
||||
if principals, ok := rawResp["principals"].([]interface{}); ok {
|
||||
for _, item := range principals {
|
||||
if user, ok := item.(map[string]interface{}); ok {
|
||||
userObj := parseUserObject(user)
|
||||
users = append(users, userObj)
|
||||
}
|
||||
}
|
||||
userList.Data.Items = users
|
||||
userList.Data.Total = len(users)
|
||||
return &userList, nil
|
||||
}
|
||||
|
||||
// 检查users数组格式
|
||||
if usersArray, ok := rawResp["users"].([]interface{}); ok {
|
||||
for _, item := range usersArray {
|
||||
if user, ok := item.(map[string]interface{}); ok {
|
||||
userObj := parseUserObject(user)
|
||||
users = append(users, userObj)
|
||||
}
|
||||
}
|
||||
userList.Data.Items = users
|
||||
userList.Data.Total = len(users)
|
||||
return &userList, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("未能从响应中提取用户列表")
|
||||
}
|
||||
|
||||
fmt.Printf("找到 %d 个用户\n", len(userList.Data.Items))
|
||||
return &userList, nil
|
||||
}
|
||||
|
||||
// parseUserObject 解析用户对象
|
||||
func parseUserObject(user map[string]interface{}) User {
|
||||
userObj := User{}
|
||||
|
||||
// ID
|
||||
if id, ok := user["id"].(float64); ok {
|
||||
userObj.ID = int(id)
|
||||
}
|
||||
|
||||
// Type
|
||||
if typ, ok := user["type"].(string); ok {
|
||||
userObj.Type = typ
|
||||
}
|
||||
|
||||
// Name
|
||||
if name, ok := user["name"].(string); ok {
|
||||
userObj.Name = name
|
||||
}
|
||||
|
||||
// Description
|
||||
if desc, ok := user["description"].(string); ok {
|
||||
userObj.Description = desc
|
||||
}
|
||||
|
||||
// Quota
|
||||
if quota, ok := user["quota"].(float64); ok {
|
||||
userObj.Quota = int64(quota)
|
||||
}
|
||||
|
||||
// Emails
|
||||
if emails, ok := user["emails"].([]interface{}); ok {
|
||||
for _, email := range emails {
|
||||
if e, ok := email.(string); ok {
|
||||
userObj.Emails = append(userObj.Emails, e)
|
||||
}
|
||||
}
|
||||
} else if email, ok := user["emails"].(string); ok {
|
||||
userObj.Emails = []string{email}
|
||||
}
|
||||
|
||||
// MemberOf
|
||||
if memberOf, ok := user["memberOf"].([]interface{}); ok {
|
||||
for _, member := range memberOf {
|
||||
if m, ok := member.(string); ok {
|
||||
userObj.MemberOf = append(userObj.MemberOf, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Roles
|
||||
if roles, ok := user["roles"].([]interface{}); ok {
|
||||
for _, role := range roles {
|
||||
if r, ok := role.(string); ok {
|
||||
userObj.Roles = append(userObj.Roles, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return userObj
|
||||
}
|
||||
|
||||
// GetUserByEmail 通过邮箱地址查询用户信息
|
||||
func (c *Client) GetUserByEmail(email string, authType AuthType, username, authPassword, apiKey string) (map[string]interface{}, error) {
|
||||
fmt.Printf("通过邮箱查询用户: %s\n", email)
|
||||
|
||||
// 获取所有用户,然后过滤
|
||||
userList, err := c.ListUsers(authType, username, authPassword, apiKey, 1000, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 尝试在列表中查找匹配的邮箱
|
||||
for _, user := range userList.Data.Items {
|
||||
// 检查用户邮箱是否匹配
|
||||
for _, userEmail := range user.Emails {
|
||||
if userEmail == email {
|
||||
fmt.Printf("找到匹配的用户,ID: %d\n", user.ID)
|
||||
// 获取详细信息
|
||||
return c.GetUserDetails(user.ID, authType, username, authPassword, apiKey)
|
||||
}
|
||||
}
|
||||
if user.Name == email {
|
||||
fmt.Printf("找到匹配的用户,ID: %d\n", user.ID)
|
||||
// 获取详细信息
|
||||
return c.GetUserDetails(user.ID, authType, username, authPassword, apiKey)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("未找到邮箱为 %s 的用户\n", email)
|
||||
return nil, fmt.Errorf("未找到用户")
|
||||
}
|
||||
|
||||
// FormatUserList 格式化输出用户列表
|
||||
func FormatUserList(userList *UserList) {
|
||||
if userList == nil || len(userList.Data.Items) == 0 {
|
||||
fmt.Println("没有用户数据可显示")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("\n=== 用户列表 ===")
|
||||
fmt.Printf("找到 %d 个用户\n", len(userList.Data.Items))
|
||||
fmt.Println(strings.Repeat("-", 60))
|
||||
fmt.Printf("%-10s %-30s %-15s %-30s\n", "ID", "名称", "类型", "邮箱")
|
||||
fmt.Println(strings.Repeat("-", 60))
|
||||
|
||||
for _, user := range userList.Data.Items {
|
||||
email := "N/A"
|
||||
if len(user.Emails) > 0 {
|
||||
email = user.Emails[0]
|
||||
}
|
||||
|
||||
// 截断过长的字符串
|
||||
name := user.Name
|
||||
if len(name) > 28 {
|
||||
name = name[:28]
|
||||
}
|
||||
if len(email) > 28 {
|
||||
email = email[:28]
|
||||
}
|
||||
|
||||
fmt.Printf("%-10d %-30s %-15s %-30s\n", user.ID, name, user.Type, email)
|
||||
}
|
||||
|
||||
fmt.Println(strings.Repeat("-", 60))
|
||||
}
|
||||
|
||||
// FormatUserDetails 格式化输出单个用户的详细信息
|
||||
func FormatUserDetails(userInfo map[string]interface{}) {
|
||||
if userInfo == nil {
|
||||
fmt.Println("没有用户数据可显示")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("\n=== 用户详细信息 ===")
|
||||
fmt.Println(strings.Repeat("-", 60))
|
||||
|
||||
// 获取基本信息
|
||||
id, _ := userInfo["id"]
|
||||
name, _ := userInfo["name"]
|
||||
description, _ := userInfo["description"]
|
||||
typ, _ := userInfo["type"]
|
||||
quota, _ := userInfo["quota"]
|
||||
|
||||
// 显示类型转换
|
||||
var quotaFloat float64
|
||||
switch q := quota.(type) {
|
||||
case float64:
|
||||
quotaFloat = q
|
||||
case int64:
|
||||
quotaFloat = float64(q)
|
||||
case int:
|
||||
quotaFloat = float64(q)
|
||||
}
|
||||
|
||||
quotaMB := quotaFloat / (1024 * 1024)
|
||||
|
||||
fmt.Printf("ID: %v\n", id)
|
||||
fmt.Printf("名称: %v\n", name)
|
||||
fmt.Printf("描述: %v\n", description)
|
||||
fmt.Printf("类型: %v\n", typ)
|
||||
fmt.Printf("配额: %.2f MB (%.0f 字节)\n", quotaMB, quotaFloat)
|
||||
|
||||
// 获取邮箱
|
||||
if emails, ok := userInfo["emails"].([]interface{}); ok && len(emails) > 0 {
|
||||
fmt.Println("\n邮箱地址:")
|
||||
for _, email := range emails {
|
||||
fmt.Printf(" - %v\n", email)
|
||||
}
|
||||
} else if email, ok := userInfo["emails"].(string); ok {
|
||||
fmt.Println("\n邮箱地址:")
|
||||
fmt.Printf(" - %s\n", email)
|
||||
}
|
||||
|
||||
// 获取成员组
|
||||
if memberOf, ok := userInfo["memberOf"].([]interface{}); ok && len(memberOf) > 0 {
|
||||
fmt.Println("\n所属组:")
|
||||
for _, group := range memberOf {
|
||||
fmt.Printf(" - %v\n", group)
|
||||
}
|
||||
}
|
||||
|
||||
// 其他属性
|
||||
fmt.Println("\n其他属性:")
|
||||
for key, value := range userInfo {
|
||||
if key != "id" && key != "name" && key != "description" && key != "type" && key != "quota" && key != "emails" && key != "memberOf" {
|
||||
// 跳过密码相关字段
|
||||
if strings.Contains(strings.ToLower(key), "secret") || strings.Contains(strings.ToLower(key), "password") {
|
||||
continue
|
||||
}
|
||||
fmt.Printf(" - %s: %v\n", key, value)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(strings.Repeat("-", 60))
|
||||
}
|
||||
|
||||
// CreateEmailUser 创建邮箱用户的统一入口
|
||||
func (c *Client) CreateEmailUser(emailAddress, password, description string, quota int64, authType AuthType, username, authPassword, apiKey string) (int, string, string, string, error) {
|
||||
// 如果没有提供邮箱地址,则生成随机用户名
|
||||
if emailAddress == "" {
|
||||
randomUsername := GenerateRandomUsername(8)
|
||||
// 确保用户名只包含字母和数字
|
||||
for i := 0; i < len(randomUsername); i++ {
|
||||
if !((randomUsername[i] >= 'a' && randomUsername[i] <= 'z') ||
|
||||
(randomUsername[i] >= '0' && randomUsername[i] <= '9')) {
|
||||
// 替换为字母
|
||||
randomUsername = randomUsername[:i] + "x" + randomUsername[i+1:]
|
||||
}
|
||||
}
|
||||
emailAddress = fmt.Sprintf("%s@%s", randomUsername, c.Config.User.DefaultDomain)
|
||||
}
|
||||
|
||||
// 解析邮箱地址
|
||||
usernameLocal, domain := c.ParseEmail(emailAddress)
|
||||
|
||||
// 确保用户名只包含字母和数字
|
||||
cleanUsername := ""
|
||||
for i := 0; i < len(usernameLocal); i++ {
|
||||
if (usernameLocal[i] >= 'a' && usernameLocal[i] <= 'z') ||
|
||||
(usernameLocal[i] >= '0' && usernameLocal[i] <= '9') {
|
||||
cleanUsername += string(usernameLocal[i])
|
||||
} else {
|
||||
cleanUsername += "x" // 替换非法字符为x
|
||||
}
|
||||
}
|
||||
usernameLocal = cleanUsername
|
||||
|
||||
email := fmt.Sprintf("%s@%s", usernameLocal, domain)
|
||||
|
||||
// 如果没有提供密码,生成随机密码
|
||||
if password == "" {
|
||||
password = GenerateRandomPassword(12)
|
||||
fmt.Println("已生成随机密码")
|
||||
}
|
||||
|
||||
// 设置认证类型消息
|
||||
if authType == APIKeyAuth {
|
||||
// 从API Key中提取用户名用于显示
|
||||
if apiKey != "" && strings.HasPrefix(apiKey, "api_") {
|
||||
decoded, err := base64.StdEncoding.DecodeString(apiKey[4:])
|
||||
if err == nil {
|
||||
decodedStr := string(decoded)
|
||||
if strings.Contains(decodedStr, ":") {
|
||||
parts := strings.SplitN(decodedStr, ":", 2)
|
||||
fmt.Printf("使用API Key认证 (用户: %s)\n", parts[0])
|
||||
} else {
|
||||
fmt.Println("使用API Key认证")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("使用API Key认证")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("使用API Key认证")
|
||||
}
|
||||
} else {
|
||||
actualUsername := username
|
||||
if actualUsername == "" {
|
||||
actualUsername = c.Config.Auth.Basic.AdminUsername
|
||||
}
|
||||
fmt.Printf("使用基本认证 (用户: %s)\n", actualUsername)
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
userID, _, err := c.CreateUser(email, password, description, quota, authType, username, authPassword, apiKey)
|
||||
if err != nil {
|
||||
return 0, "", "", "", err
|
||||
}
|
||||
|
||||
// 特殊处理:如果用户已存在(用户ID为负数)
|
||||
if userID < 0 {
|
||||
fmt.Printf("用户 %s 已存在,使用现有用户信息\n", email)
|
||||
userID = -userID // 转为正数便于显示
|
||||
}
|
||||
|
||||
// 返回用户信息
|
||||
return userID, usernameLocal, email, password, nil
|
||||
}
|
||||
|
||||
// PrintUserResult 打印用户创建结果
|
||||
func PrintUserResult(userID int, username, email, password string) bool {
|
||||
if userID == 0 {
|
||||
fmt.Println("\n创建用户失败")
|
||||
return false
|
||||
}
|
||||
|
||||
domainParts := strings.Split(email, "@")
|
||||
domain := ""
|
||||
if len(domainParts) > 1 {
|
||||
domain = domainParts[1]
|
||||
}
|
||||
|
||||
fmt.Println("\n=== 新创建的用户信息 ===")
|
||||
fmt.Printf("用户ID: %d\n", userID)
|
||||
fmt.Printf("登录名: %s\n", username)
|
||||
fmt.Printf("邮箱地址: %s\n", email)
|
||||
fmt.Printf("密码: %s\n", password)
|
||||
fmt.Printf("\n登录信息:\n")
|
||||
fmt.Printf(" - 登录名: %s\n", username)
|
||||
fmt.Printf(" - 密码: %s\n", password)
|
||||
fmt.Printf(" - 邮箱服务器: mail.%s\n", domain)
|
||||
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user