feat: v2.0.0 完整代码优化升级

🔴 高优先级 (6项全部完成):
- 数据库事务支持 (InsertMessageWithLog)
- SQL注入修复 (参数化查询)
- 配置验证 (Validate方法)
- 会话密钥强化 (长度验证)
- 签名验证增强 (SignVerificationResult)
- 密码哈希支持 (bcrypt)

🟡 中优先级 (15项全部完成):
- 连接池配置 (MaxOpenConns, MaxIdleConns)
- 查询优化 (范围查询, 索引)
- 健康检查增强 (/health 端点)
- API版本控制 (/api/v1/*)
- 认证中间件 (RequireAuth, RequireAPIAuth)
- 定时任务优化 (robfig/cron)
- 配置文件示例 (config.example.yaml)
- 常量定义 (config/constants.go)
- 开发文档 (DEVELOPMENT.md)

🟢 低优先级 (9项全部完成):
- Docker支持 (Dockerfile, docker-compose.yml)
- Makefile构建脚本
- 优化报告 (OPTIMIZATION_REPORT.md)
- 密码哈希工具 (tools/password_hash.go)
- 14个新文件
- 30项优化100%完成

版本: v2.0.0
This commit is contained in:
OpenClaw Agent
2026-02-08 18:59:29 +08:00
parent 06720d3438
commit 1da899a0f4
22 changed files with 1523 additions and 101 deletions

View File

@@ -93,7 +93,7 @@ func Index(w http.ResponseWriter, r *http.Request) {
if page < 1 {
page = 1
}
limit := 20
limit := config.DefaultPageSize
from := r.URL.Query().Get("from")
search := r.URL.Query().Get("search")
@@ -173,19 +173,32 @@ func Login(w http.ResponseWriter, r *http.Request) {
cfg := config.Get()
if cfg.Security.Enabled {
if username == cfg.Security.Username && password == cfg.Security.Password {
if err := auth.Login(w, r, username); err != nil {
log.Printf("创建会话失败: %v", err)
http.Error(w, "创建会话失败: "+err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/", http.StatusSeeOther)
// 验证用户名
if username != cfg.Security.Username {
templates.ExecuteTemplate(w, "login.html", map[string]string{
"error": "用户名或密码错误",
})
return
}
// 登录失败
templates.ExecuteTemplate(w, "login.html", map[string]string{
"error": "用户名或密码错误",
})
// 验证密码(支持哈希和明文)
if !auth.VerifyPassword(password, cfg.Security.PasswordHash, cfg.Security.Password) {
// 记录登录失败日志
log.Printf("登录失败: 用户=%s, IP=%s", username, getClientIP(r))
templates.ExecuteTemplate(w, "login.html", map[string]string{
"error": "用户名或密码错误",
})
return
}
// 创建会话
if err := auth.Login(w, r, username); err != nil {
log.Printf("创建会话失败: %v", err)
http.Error(w, "创建会话失败: "+err.Error(), http.StatusInternalServerError)
return
}
log.Printf("登录成功: 用户=%s, IP=%s", username, getClientIP(r))
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
@@ -247,7 +260,7 @@ func Logs(w http.ResponseWriter, r *http.Request) {
if page < 1 {
page = 1
}
limit := 50
limit := config.DefaultLogsPerPage
logs, total, err := database.GetLogs(page, limit)
if err != nil {
@@ -342,18 +355,26 @@ func ReceiveSMS(w http.ResponseWriter, r *http.Request) {
cfg := config.Get()
signValid := sql.NullBool{Bool: true, Valid: true}
if token != "" && cfg.Security.SignVerify {
valid, err := sign.VerifySign(token, timestamp, signStr, &cfg.Security)
result, err := sign.VerifySign(token, timestamp, signStr, &cfg.Security)
if err != nil {
writeJSON(w, models.APIResponse{
Success: false,
Error: "签名验证错误",
Error: "签名验证错误: " + err.Error(),
}, http.StatusInternalServerError)
return
}
signValid.Bool = valid
signValid.Bool = result.Valid
signValid.Valid = true
if !valid {
signValid.Bool = false
// 记录签名的 IP 地址
clientIP := getClientIP(r)
if result.Valid {
log.Printf("签名验证通过: token=%s, timestamp=%d, ip=%s, reason=%s",
token, timestamp, clientIP, result.Reason)
} else {
log.Printf("签名验证失败: token=%s, timestamp=%d, ip=%s, reason=%s",
token, timestamp, clientIP, result.Reason)
// 签名验证失败时仍然记录消息(标记为未验证)
}
}
@@ -368,30 +389,8 @@ func ReceiveSMS(w http.ResponseWriter, r *http.Request) {
IPAddress: getClientIP(r),
}
messageID, err := database.InsertMessage(msg)
if err != nil {
// 记录失败日志
log := &models.ReceiveLog{
FromNumber: from,
Content: content,
Timestamp: timestamp,
Sign: sql.NullString{String: signStr, Valid: signStr != ""},
SignValid: signValid,
IPAddress: getClientIP(r),
Status: "error",
ErrorMessage: sql.NullString{String: err.Error(), Valid: true},
}
database.InsertLog(log)
writeJSON(w, models.APIResponse{
Success: false,
Error: "保存消息失败",
}, http.StatusInternalServerError)
return
}
// 记录成功日志
log := &models.ReceiveLog{
receiveLog := &models.ReceiveLog{
FromNumber: from,
Content: content,
Timestamp: timestamp,
@@ -400,7 +399,22 @@ func ReceiveSMS(w http.ResponseWriter, r *http.Request) {
IPAddress: getClientIP(r),
Status: "success",
}
database.InsertLog(log)
// 使用事务同时插入消息和日志
messageID, err := database.InsertMessageWithLog(msg, receiveLog)
if err != nil {
// 记录失败日志(尝试单独插入)
receiveLog.Status = "error"
receiveLog.ErrorMessage = sql.NullString{String: err.Error(), Valid: true}
// 忽略日志插入错误,避免影响主错误返回
_, _ = database.InsertLog(receiveLog)
writeJSON(w, models.APIResponse{
Success: false,
Error: "保存消息失败",
}, http.StatusInternalServerError)
return
}
writeJSON(w, models.APIResponse{
Success: true,
@@ -422,10 +436,10 @@ func APIGetMessages(w http.ResponseWriter, r *http.Request) {
}
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
if limit <= 0 {
limit = 20
limit = config.DefaultPageSize
}
if limit > 100 {
limit = 100
if limit > config.MaxPageSize {
limit = config.MaxPageSize
}
from := r.URL.Query().Get("from")
search := r.URL.Query().Get("search")