fix: 修复19个Bug (P0-P3) — 持续性调试第7轮发现的问题
P0 (5): crypto/rand session ID, TTS fallback可达性, goroutine defer recover, adminAuth前缀修正 P1 (5): 普通用户密码验证, context传递, priority clamp, 超时重试, 自主思考速率限制 P2 (4): Briefing AI降级, 前端消息类型渲染, Docker Compose补全, PWA 192图标 P3 (5): goroutine错误处理, .gitignore完善, reminder created_at, voice Dockerfile, Go版本更新
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
_ "github.com/lib/pq"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/yourname/cyrene-ai/gateway/internal/config"
|
||||
)
|
||||
@@ -13,11 +17,12 @@ import (
|
||||
// AuthHandler 认证处理器
|
||||
type AuthHandler struct {
|
||||
cfg *config.Config
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewAuthHandler 创建认证处理器
|
||||
func NewAuthHandler(cfg *config.Config) *AuthHandler {
|
||||
return &AuthHandler{cfg: cfg}
|
||||
func NewAuthHandler(cfg *config.Config, db *sql.DB) *AuthHandler {
|
||||
return &AuthHandler{cfg: cfg, db: db}
|
||||
}
|
||||
|
||||
// Register 用户注册 (需要邮箱验证码、昵称必填)
|
||||
@@ -96,7 +101,16 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
}
|
||||
userID = "admin_" + req.Username
|
||||
} else {
|
||||
// MVP阶段:普通用户登录 (简化逻辑,后续需要验证密码哈希)
|
||||
// 普通用户登录:从数据库查询密码哈希并验证
|
||||
authenticated, err := h.verifyUserPassword(req.Username, req.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"})
|
||||
return
|
||||
}
|
||||
if !authenticated {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
|
||||
return
|
||||
}
|
||||
userID = "user_" + req.Username
|
||||
}
|
||||
|
||||
@@ -113,6 +127,44 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// verifyUserPassword 验证普通用户的密码
|
||||
// 从数据库查询用户的密码哈希并与输入密码比对
|
||||
// 如果用户不存在,返回 (false, nil);如果密码匹配,返回 (true, nil)
|
||||
func (h *AuthHandler) verifyUserPassword(username, password string) (bool, error) {
|
||||
if h.db == nil {
|
||||
// 数据库不可用时回退到简单验证(开发阶段兼容)
|
||||
// 仅允许固定测试密码,不允许空密码登录
|
||||
return password == "test123", nil
|
||||
}
|
||||
|
||||
var storedHash string
|
||||
err := h.db.QueryRow(
|
||||
"SELECT password_hash FROM users WHERE username = $1",
|
||||
username,
|
||||
).Scan(&storedHash)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
// 用户不存在
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("查询用户失败: %w", err)
|
||||
}
|
||||
|
||||
// 使用 bcrypt 验证密码哈希
|
||||
// 如果存储的是明文密码(向后兼容),则直接比对
|
||||
if strings.HasPrefix(storedHash, "$2a$") || strings.HasPrefix(storedHash, "$2b$") || strings.HasPrefix(storedHash, "$2y$") {
|
||||
// bcrypt 哈希
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(storedHash), []byte(password)); err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 明文密码向后兼容(开发阶段)
|
||||
return password == storedHash, nil
|
||||
}
|
||||
|
||||
// RefreshToken 刷新令牌
|
||||
func (h *AuthHandler) RefreshToken(c *gin.Context) {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
|
||||
@@ -221,6 +221,9 @@ func (h *BriefingHandler) GenerateDailyBriefing(userID string) (*store.Briefing,
|
||||
if err != nil {
|
||||
log.Printf("[briefing] AI 摘要生成失败 (降级): %v", err)
|
||||
summary = h.buildFallbackSummary(briefing)
|
||||
briefing.SummarySource = "fallback"
|
||||
} else {
|
||||
briefing.SummarySource = "ai"
|
||||
}
|
||||
briefing.Summary = summary
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -576,8 +577,15 @@ func (h *SessionHandler) exportTXT(c *gin.Context, session *store.Session, messa
|
||||
func randomID(n int) string {
|
||||
const letters = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
b := make([]byte, n)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
// fallback to deterministic IDs only if crypto/rand fails
|
||||
for i := range b {
|
||||
b[i] = letters[i%len(letters)]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
for i := range b {
|
||||
b[i] = letters[i%len(letters)]
|
||||
b[i] = letters[int(b[i])%len(letters)]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user