531 lines
13 KiB
Go
531 lines
13 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"flag"
|
||
"fmt"
|
||
"log"
|
||
"math/rand"
|
||
"net/http"
|
||
"os"
|
||
"stalwart_client/pkg/api"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// 请求和响应结构
|
||
type CreateUserRequest struct {
|
||
Email string `json:"email"`
|
||
Password string `json:"password,omitempty"`
|
||
Description string `json:"description,omitempty"`
|
||
Quota int64 `json:"quota,omitempty"`
|
||
}
|
||
|
||
type UserResponse struct {
|
||
ID int `json:"id"`
|
||
Username string `json:"username"`
|
||
Email string `json:"email"`
|
||
Password string `json:"password,omitempty"`
|
||
Success bool `json:"success"`
|
||
Message string `json:"message,omitempty"`
|
||
}
|
||
|
||
type ListUsersRequest struct {
|
||
Limit int `json:"limit,omitempty"`
|
||
Page int `json:"page,omitempty"`
|
||
}
|
||
|
||
type ListUsersResponse struct {
|
||
Users []api.User `json:"users"`
|
||
Total int `json:"total"`
|
||
Success bool `json:"success"`
|
||
Message string `json:"message,omitempty"`
|
||
}
|
||
|
||
type GetUserRequest struct {
|
||
ID int `json:"id,omitempty"`
|
||
Email string `json:"email,omitempty"`
|
||
}
|
||
|
||
type GetUserResponse struct {
|
||
User map[string]interface{} `json:"user"`
|
||
Success bool `json:"success"`
|
||
Message string `json:"message,omitempty"`
|
||
}
|
||
|
||
type GetEmailRequest struct {
|
||
Email string `json:"email"`
|
||
Password string `json:"password"`
|
||
Server string `json:"server,omitempty"`
|
||
Port int `json:"port,omitempty"`
|
||
Count int `json:"count,omitempty"`
|
||
}
|
||
|
||
type EmailResponse struct {
|
||
Emails []*api.EmailMessage `json:"emails"`
|
||
Success bool `json:"success"`
|
||
Message string `json:"message,omitempty"`
|
||
}
|
||
|
||
// 全局变量
|
||
var (
|
||
client *api.Client
|
||
)
|
||
|
||
// API处理函数
|
||
|
||
// 创建用户
|
||
func createUserHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodPost {
|
||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
var req CreateUserRequest
|
||
// 尝试解析请求体,但即使失败也继续处理
|
||
_ = json.NewDecoder(r.Body).Decode(&req)
|
||
|
||
// 如果email为空,自动生成一个随机用户名和域名
|
||
if req.Email == "" {
|
||
randomUsername := api.GenerateRandomPassword(8)
|
||
req.Email = fmt.Sprintf("%s@%s", randomUsername, client.Config.User.DefaultDomain)
|
||
}
|
||
|
||
// 如果密码为空,将由系统自动生成
|
||
// 如果配额为0,使用默认配额
|
||
if req.Quota == 0 {
|
||
req.Quota = client.Config.User.DefaultQuota
|
||
}
|
||
|
||
// 如果描述为空,添加默认描述
|
||
if req.Description == "" {
|
||
req.Description = "系统自动创建的用户"
|
||
}
|
||
|
||
// 创建用户
|
||
userID, username, email, password, err := client.CreateEmailUser(
|
||
req.Email,
|
||
req.Password,
|
||
req.Description,
|
||
req.Quota,
|
||
api.APIKeyAuth, // 默认使用API Key认证
|
||
"",
|
||
"",
|
||
client.Config.Auth.APIKey,
|
||
)
|
||
|
||
if err != nil {
|
||
w.WriteHeader(http.StatusInternalServerError)
|
||
json.NewEncoder(w).Encode(map[string]string{
|
||
"success": "false",
|
||
"message": fmt.Sprintf("创建用户失败: %v", err),
|
||
})
|
||
return
|
||
}
|
||
|
||
// 返回响应
|
||
response := UserResponse{
|
||
ID: userID,
|
||
Username: username,
|
||
Email: email,
|
||
Password: password,
|
||
Success: true,
|
||
Message: "用户创建成功",
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
json.NewEncoder(w).Encode(response)
|
||
}
|
||
|
||
// 创建简单用户 - 无需任何参数,直接返回创建的用户信息
|
||
func createSimpleUserHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodGet && r.Method != http.MethodPost {
|
||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
// 生成随机用户名 - 确保不包含特殊字符
|
||
randomUsername := api.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:]
|
||
}
|
||
}
|
||
|
||
email := fmt.Sprintf("%s@%s", randomUsername, client.Config.User.DefaultDomain)
|
||
description := "自动创建的临时用户"
|
||
quota := client.Config.User.DefaultQuota
|
||
|
||
// 创建用户
|
||
userID, username, email, password, err := client.CreateEmailUser(
|
||
email,
|
||
"", // 空密码,系统会自动生成
|
||
description,
|
||
quota,
|
||
api.APIKeyAuth,
|
||
"",
|
||
"",
|
||
client.Config.Auth.APIKey,
|
||
)
|
||
|
||
if err != nil {
|
||
w.WriteHeader(http.StatusInternalServerError)
|
||
json.NewEncoder(w).Encode(map[string]string{
|
||
"success": "false",
|
||
"message": fmt.Sprintf("创建用户失败: %v", err),
|
||
})
|
||
return
|
||
}
|
||
|
||
// 返回简单格式的响应
|
||
response := map[string]interface{}{
|
||
"id": userID,
|
||
"username": username,
|
||
"email": email,
|
||
"password": password,
|
||
"server": fmt.Sprintf("mail.%s", strings.Split(email, "@")[1]),
|
||
"success": true,
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
json.NewEncoder(w).Encode(response)
|
||
}
|
||
|
||
// 创建模拟用户 - 不实际调用API,只生成模拟数据
|
||
func createMockUserHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodGet && r.Method != http.MethodPost {
|
||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
// 生成随机用户名 - 确保不包含特殊字符
|
||
randomUsername := api.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:]
|
||
}
|
||
}
|
||
|
||
domain := client.Config.User.DefaultDomain
|
||
email := fmt.Sprintf("%s@%s", randomUsername, domain)
|
||
password := api.GenerateRandomPassword(12)
|
||
|
||
// 返回模拟响应
|
||
userID := rand.Intn(1000) + 1000 // 随机生成ID
|
||
|
||
response := map[string]interface{}{
|
||
"id": userID,
|
||
"username": randomUsername,
|
||
"email": email,
|
||
"password": password,
|
||
"server": fmt.Sprintf("mail.%s", domain),
|
||
"imap_server": fmt.Sprintf("mail.%s", domain),
|
||
"imap_port": 993,
|
||
"smtp_server": fmt.Sprintf("mail.%s", domain),
|
||
"smtp_port": 465,
|
||
"description": "模拟生成的测试用户",
|
||
"quota": client.Config.User.DefaultQuota,
|
||
"created_time": time.Now().Format(time.RFC3339),
|
||
"success": true,
|
||
"message": "模拟用户创建成功(仅本地数据,未实际调用API)",
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
json.NewEncoder(w).Encode(response)
|
||
}
|
||
|
||
// 列出用户
|
||
func listUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodGet {
|
||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
// 解析查询参数
|
||
limitStr := r.URL.Query().Get("limit")
|
||
pageStr := r.URL.Query().Get("page")
|
||
|
||
limit := 100
|
||
page := 1
|
||
|
||
if limitStr != "" {
|
||
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
|
||
limit = l
|
||
}
|
||
}
|
||
|
||
if pageStr != "" {
|
||
if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
|
||
page = p
|
||
}
|
||
}
|
||
|
||
// 获取用户列表
|
||
userList, err := client.ListUsers(
|
||
api.APIKeyAuth, // 默认使用API Key认证
|
||
"",
|
||
"",
|
||
client.Config.Auth.APIKey,
|
||
limit,
|
||
page,
|
||
)
|
||
|
||
if err != nil {
|
||
w.WriteHeader(http.StatusInternalServerError)
|
||
json.NewEncoder(w).Encode(map[string]string{
|
||
"success": "false",
|
||
"message": fmt.Sprintf("获取用户列表失败: %v", err),
|
||
})
|
||
return
|
||
}
|
||
|
||
// 返回响应
|
||
response := ListUsersResponse{
|
||
Users: userList.Data.Items,
|
||
Total: userList.Data.Total,
|
||
Success: true,
|
||
Message: "获取用户列表成功",
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
json.NewEncoder(w).Encode(response)
|
||
}
|
||
|
||
// 获取单个用户信息
|
||
func getUserHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodGet {
|
||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
// 解析参数
|
||
idStr := r.URL.Query().Get("id")
|
||
email := r.URL.Query().Get("email")
|
||
|
||
// 如果两个参数都为空,尝试从路径中获取
|
||
if idStr == "" && email == "" {
|
||
pathParts := strings.Split(r.URL.Path, "/")
|
||
if len(pathParts) > 3 {
|
||
// 路径形式可能是 /api/get_user/123 或 /api/get_user/user@example.com
|
||
lastPart := pathParts[len(pathParts)-1]
|
||
if _, err := strconv.Atoi(lastPart); err == nil {
|
||
// 是数字,当作ID处理
|
||
idStr = lastPart
|
||
} else if strings.Contains(lastPart, "@") {
|
||
// 包含@,当作邮箱处理
|
||
email = lastPart
|
||
}
|
||
}
|
||
}
|
||
|
||
if idStr == "" && email == "" {
|
||
w.WriteHeader(http.StatusBadRequest)
|
||
json.NewEncoder(w).Encode(map[string]string{
|
||
"success": "false",
|
||
"message": "必须提供用户ID或邮箱地址",
|
||
})
|
||
return
|
||
}
|
||
|
||
var userInfo map[string]interface{}
|
||
var err error
|
||
|
||
if idStr != "" {
|
||
// 通过ID查询
|
||
id, err := strconv.Atoi(idStr)
|
||
if err != nil {
|
||
w.WriteHeader(http.StatusBadRequest)
|
||
json.NewEncoder(w).Encode(map[string]string{
|
||
"success": "false",
|
||
"message": "无效的用户ID",
|
||
})
|
||
return
|
||
}
|
||
|
||
userInfo, err = client.GetUserDetails(
|
||
id,
|
||
api.APIKeyAuth, // 默认使用API Key认证
|
||
"",
|
||
"",
|
||
client.Config.Auth.APIKey,
|
||
)
|
||
} else {
|
||
// 通过邮箱查询
|
||
userInfo, err = client.GetUserByEmail(
|
||
email,
|
||
api.APIKeyAuth, // 默认使用API Key认证
|
||
"",
|
||
"",
|
||
client.Config.Auth.APIKey,
|
||
)
|
||
}
|
||
|
||
if err != nil {
|
||
w.WriteHeader(http.StatusInternalServerError)
|
||
json.NewEncoder(w).Encode(map[string]string{
|
||
"success": "false",
|
||
"message": fmt.Sprintf("获取用户信息失败: %v", err),
|
||
})
|
||
return
|
||
}
|
||
|
||
// 返回响应
|
||
response := GetUserResponse{
|
||
User: userInfo,
|
||
Success: true,
|
||
Message: "获取用户信息成功",
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
json.NewEncoder(w).Encode(response)
|
||
}
|
||
|
||
// 获取邮件
|
||
func getEmailsHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodPost {
|
||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
var req GetEmailRequest
|
||
// 尝试解析请求体,但即使失败也继续处理
|
||
_ = json.NewDecoder(r.Body).Decode(&req)
|
||
|
||
// 检查是否提供了邮箱和密码参数
|
||
if req.Email == "" || req.Password == "" {
|
||
// 也可以从URL参数获取
|
||
req.Email = r.URL.Query().Get("email")
|
||
req.Password = r.URL.Query().Get("password")
|
||
|
||
// 如果仍然为空,则返回错误
|
||
if req.Email == "" || req.Password == "" {
|
||
w.WriteHeader(http.StatusBadRequest)
|
||
json.NewEncoder(w).Encode(map[string]string{
|
||
"success": "false",
|
||
"message": "邮箱地址和密码必须提供",
|
||
})
|
||
return
|
||
}
|
||
}
|
||
|
||
// 设置默认参数
|
||
server := req.Server
|
||
if server == "" {
|
||
// 尝试从邮箱地址提取域名
|
||
parts := strings.Split(req.Email, "@")
|
||
if len(parts) > 1 {
|
||
server = fmt.Sprintf("mail.%s", parts[1])
|
||
} else {
|
||
server = "mail.evnmail.com"
|
||
}
|
||
}
|
||
|
||
port := req.Port
|
||
if port == 0 {
|
||
port = 993
|
||
}
|
||
|
||
count := req.Count
|
||
if count == 0 {
|
||
count = 5 // 默认获取5封邮件
|
||
}
|
||
|
||
// 获取邮件
|
||
emails, err := api.GetLatestEmails(
|
||
req.Email,
|
||
req.Password,
|
||
server,
|
||
port,
|
||
true, // 使用SSL
|
||
count,
|
||
)
|
||
|
||
if err != nil {
|
||
w.WriteHeader(http.StatusInternalServerError)
|
||
json.NewEncoder(w).Encode(map[string]string{
|
||
"success": "false",
|
||
"message": fmt.Sprintf("获取邮件失败: %v", err),
|
||
})
|
||
return
|
||
}
|
||
|
||
// 返回响应
|
||
response := EmailResponse{
|
||
Emails: emails,
|
||
Success: true,
|
||
Message: fmt.Sprintf("成功获取 %d 封邮件", len(emails)),
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
json.NewEncoder(w).Encode(response)
|
||
}
|
||
|
||
// 启动Web服务器
|
||
func main() {
|
||
// 初始化随机数生成器
|
||
rand.Seed(time.Now().UnixNano())
|
||
|
||
// 解析命令行参数
|
||
portPtr := flag.Int("p", 8080, "服务器端口")
|
||
configPtr := flag.String("c", "", "配置文件路径")
|
||
|
||
flag.Parse()
|
||
|
||
// 加载配置
|
||
config, err := api.LoadConfig(*configPtr)
|
||
if err != nil {
|
||
fmt.Printf("加载配置失败: %v\n", err)
|
||
os.Exit(1)
|
||
}
|
||
|
||
// 创建客户端
|
||
client = api.NewClient(config)
|
||
|
||
// 设置路由 - 不再使用authMiddleware
|
||
http.HandleFunc("/api/create_user", createUserHandler)
|
||
http.HandleFunc("/api/simple_user", createSimpleUserHandler)
|
||
http.HandleFunc("/api/mock_user", createMockUserHandler)
|
||
http.HandleFunc("/api/list_users", listUsersHandler)
|
||
http.HandleFunc("/api/get_user", getUserHandler)
|
||
http.HandleFunc("/api/get_emails", getEmailsHandler)
|
||
|
||
// 添加一个简单的健康检查端点
|
||
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||
w.WriteHeader(http.StatusOK)
|
||
w.Write([]byte("OK"))
|
||
})
|
||
|
||
// 添加一个简单的根路径处理
|
||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||
if r.URL.Path != "/" {
|
||
http.NotFound(w, r)
|
||
return
|
||
}
|
||
w.WriteHeader(http.StatusOK)
|
||
w.Write([]byte("Stalwart 邮件 API 服务运行中"))
|
||
})
|
||
|
||
// 启动服务器
|
||
port := *portPtr
|
||
addr := fmt.Sprintf(":%d", port)
|
||
fmt.Printf("启动Web服务器,监听端口: %d\n", port)
|
||
fmt.Printf("API端点 (无需认证):\n")
|
||
fmt.Printf(" - POST /api/create_user (创建用户,可自定义参数)\n")
|
||
fmt.Printf(" - GET /api/simple_user (创建随机用户,无需任何参数)\n")
|
||
fmt.Printf(" - GET /api/mock_user (创建模拟用户,仅本地数据)\n")
|
||
fmt.Printf(" - GET /api/list_users (获取用户列表)\n")
|
||
fmt.Printf(" - GET /api/get_user (获取用户信息)\n")
|
||
fmt.Printf(" - POST /api/get_emails (获取用户邮件)\n")
|
||
|
||
if err := http.ListenAndServe(addr, nil); err != nil {
|
||
log.Fatalf("启动服务器失败: %v", err)
|
||
}
|
||
}
|