完善初始化更新
This commit is contained in:
173
sproutgate-backend/internal/handlers/auth_login.go
Normal file
173
sproutgate-backend/internal/handlers/auth_login.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"sproutgate-backend/internal/auth"
|
||||
"sproutgate-backend/internal/clientgeo"
|
||||
"sproutgate-backend/internal/models"
|
||||
)
|
||||
|
||||
func (h *Handler) Login(c *gin.Context) {
|
||||
var req loginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
return
|
||||
}
|
||||
req.Account = strings.TrimSpace(req.Account)
|
||||
if req.Account == "" || req.Password == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "account and password are required"})
|
||||
return
|
||||
}
|
||||
user, found, err := h.store.GetUser(req.Account)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load user"})
|
||||
return
|
||||
}
|
||||
if !found {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"})
|
||||
return
|
||||
}
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"})
|
||||
return
|
||||
}
|
||||
if user.Banned {
|
||||
writeBanJSON(c, user.BanReason)
|
||||
return
|
||||
}
|
||||
token, expiresAt, err := auth.GenerateToken(h.store.JWTSecret(), h.store.JWTIssuer(), user.Account, 7*24*time.Hour)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate token"})
|
||||
return
|
||||
}
|
||||
if cid, ok := models.NormalizeAuthClientID(req.ClientID); ok {
|
||||
name := models.ClampAuthClientName(req.ClientName)
|
||||
if rec, err := h.store.RecordAuthClient(req.Account, cid, name); err == nil {
|
||||
user = rec
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"token": token,
|
||||
"expiresAt": expiresAt.Format(time.RFC3339),
|
||||
"user": user.OwnerPublic(),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) Verify(c *gin.Context) {
|
||||
var req verifyRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil || strings.TrimSpace(req.Token) == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "token is required"})
|
||||
return
|
||||
}
|
||||
claims, err := auth.ParseToken(h.store.JWTSecret(), h.store.JWTIssuer(), req.Token)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"valid": false, "error": "invalid token"})
|
||||
return
|
||||
}
|
||||
user, found, err := h.store.GetUser(claims.Account)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load user"})
|
||||
return
|
||||
}
|
||||
if !found {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"valid": false, "error": "user not found"})
|
||||
return
|
||||
}
|
||||
if user.Banned {
|
||||
h := gin.H{"valid": false, "error": "account is banned"}
|
||||
if r := strings.TrimSpace(user.BanReason); r != "" {
|
||||
h["banReason"] = r
|
||||
}
|
||||
c.JSON(http.StatusOK, h)
|
||||
return
|
||||
}
|
||||
if cid, cname, ok := authClientFromHeaders(c); ok {
|
||||
_, _ = h.store.RecordAuthClient(claims.Account, cid, cname)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"valid": true, "user": user.Public()})
|
||||
}
|
||||
|
||||
func (h *Handler) Me(c *gin.Context) {
|
||||
token := bearerToken(c.GetHeader("Authorization"))
|
||||
if token == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "missing token"})
|
||||
return
|
||||
}
|
||||
claims, err := auth.ParseToken(h.store.JWTSecret(), h.store.JWTIssuer(), token)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
|
||||
return
|
||||
}
|
||||
user, found, err := h.store.GetUser(claims.Account)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load user"})
|
||||
return
|
||||
}
|
||||
if !found {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
if abortIfUserBanned(c, user) {
|
||||
return
|
||||
}
|
||||
if cid, cname, ok := authClientFromHeaders(c); ok {
|
||||
if rec, err := h.store.RecordAuthClient(claims.Account, cid, cname); err == nil {
|
||||
user = rec
|
||||
}
|
||||
}
|
||||
today := models.CurrentActivityDate()
|
||||
nowAt := models.CurrentActivityTime()
|
||||
user, _, err = h.store.RecordVisit(claims.Account, today, nowAt)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save visit"})
|
||||
return
|
||||
}
|
||||
visitIP := strings.TrimSpace(c.GetHeader("X-Visit-Ip"))
|
||||
visitLoc := strings.TrimSpace(c.GetHeader("X-Visit-Location"))
|
||||
if visitIP == "" {
|
||||
visitIP = strings.TrimSpace(c.ClientIP())
|
||||
}
|
||||
lookupURL := strings.TrimSpace(os.Getenv("GEO_LOOKUP_URL"))
|
||||
if lookupURL == "" {
|
||||
lookupURL = clientgeo.DefaultLookupURL
|
||||
}
|
||||
if visitLoc == "" && visitIP != "" {
|
||||
if loc, geoErr := clientgeo.FetchDisplayLocation(c.Request.Context(), lookupURL, visitIP); geoErr == nil {
|
||||
visitLoc = loc
|
||||
}
|
||||
}
|
||||
if visitIP != "" || visitLoc != "" {
|
||||
user, err = h.store.UpdateLastVisitMeta(claims.Account, visitIP, visitLoc)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save visit meta"})
|
||||
return
|
||||
}
|
||||
}
|
||||
checkInConfig := h.store.CheckInConfig()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"user": user.OwnerPublic(),
|
||||
"checkIn": gin.H{
|
||||
"rewardCoins": checkInConfig.RewardCoins,
|
||||
"checkedInToday": user.LastCheckInDate == today,
|
||||
"lastCheckInDate": user.LastCheckInDate,
|
||||
"lastCheckInAt": user.LastCheckInAt,
|
||||
"today": today,
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user