Files
SingBox-Gopanel/internal/handler/user_api.go
CN-JS-HuiBai 97f0672729
Some checks failed
build / build (api, amd64, linux) (push) Failing after -50s
build / build (api, arm64, linux) (push) Failing after -51s
build / build (api.exe, amd64, windows) (push) Failing after -51s
修复订阅无法正常获取的错误
2026-04-17 23:07:47 +08:00

244 lines
5.8 KiB
Go

package handler
import (
"crypto/md5"
"fmt"
"net/http"
"strings"
"time"
"xboard-go/internal/database"
"xboard-go/internal/model"
"xboard-go/internal/service"
"xboard-go/pkg/utils"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
func UserInfo(c *gin.Context) {
user, ok := currentUser(c)
if !ok {
Fail(c, http.StatusUnauthorized, "user not found")
return
}
Success(c, gin.H{
"email": user.Email,
"transfer_enable": user.TransferEnable,
"last_login_at": user.LastLoginAt,
"created_at": user.CreatedAt,
"banned": user.Banned,
"remind_expire": user.RemindExpire,
"remind_traffic": user.RemindTraffic,
"expired_at": user.ExpiredAt,
"balance": user.Balance,
"commission_balance": user.CommissionBalance,
"plan_id": user.PlanID,
"discount": user.Discount,
"commission_rate": user.CommissionRate,
"telegram_id": user.TelegramID,
"uuid": user.UUID,
"avatar_url": "https://cdn.v2ex.com/gravatar/" + fmt.Sprintf("%x", md5.Sum([]byte(strings.ToLower(user.Email)))) + "?s=64&d=identicon",
})
}
func UserGetStat(c *gin.Context) {
user, ok := currentUser(c)
if !ok {
Fail(c, http.StatusUnauthorized, "user not found")
return
}
var pendingOrders int64
var openTickets int64
var invitedUsers int64
database.DB.Model(&model.Order{}).Where("status = ? AND user_id = ?", 0, user.ID).Count(&pendingOrders)
database.DB.Model(&model.Ticket{}).Where("status = ? AND user_id = ?", 0, user.ID).Count(&openTickets)
database.DB.Model(&model.User{}).Where("invite_user_id = ?", user.ID).Count(&invitedUsers)
Success(c, []int64{pendingOrders, openTickets, invitedUsers})
}
func UserGetSubscribe(c *gin.Context) {
user, ok := currentUser(c)
if !ok {
Fail(c, http.StatusUnauthorized, "user not found")
return
}
if user.PlanID != nil {
var plan model.Plan
if err := database.DB.First(&plan, *user.PlanID).Error; err == nil {
user.Plan = &plan
}
}
baseURL := requestBaseURL(c)
subscribePath := service.GetSubscribePath()
Success(c, gin.H{
"plan_id": user.PlanID,
"token": user.Token,
"expired_at": user.ExpiredAt,
"u": user.U,
"d": user.D,
"transfer_enable": user.TransferEnable,
"email": user.Email,
"uuid": user.UUID,
"device_limit": user.DeviceLimit,
"speed_limit": user.SpeedLimit,
"next_reset_at": user.NextResetAt,
"plan": user.Plan,
"subscribe_url": strings.TrimRight(baseURL, "/") + "/" + subscribePath + "/" + user.Token,
"reset_day": nil,
})
}
func UserCheckLogin(c *gin.Context) {
user, ok := currentUser(c)
if !ok {
Success(c, gin.H{"is_login": false})
return
}
Success(c, gin.H{
"is_login": true,
"is_admin": user.IsAdmin,
})
}
func UserResetSecurity(c *gin.Context) {
user, ok := currentUser(c)
if !ok {
Fail(c, http.StatusUnauthorized, "user not found")
return
}
newUUID := uuid.New().String()
newToken := fmt.Sprintf("%x", md5.Sum([]byte(time.Now().String()+user.Email)))[:16]
if err := database.DB.Model(&model.User{}).
Where("id = ?", user.ID).
Updates(map[string]any{"uuid": newUUID, "token": newToken, "updated_at": time.Now().Unix()}).Error; err != nil {
Fail(c, 500, "reset failed")
return
}
baseURL := requestBaseURL(c)
Success(c, strings.TrimRight(baseURL, "/")+"/"+service.GetSubscribePath()+"/"+newToken)
}
func UserUpdate(c *gin.Context) {
user, ok := currentUser(c)
if !ok {
Fail(c, http.StatusUnauthorized, "user not found")
return
}
var req struct {
RemindExpire *int `json:"remind_expire"`
RemindTraffic *int `json:"remind_traffic"`
}
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, 400, "invalid request body")
return
}
updates := map[string]any{"updated_at": time.Now().Unix()}
if req.RemindExpire != nil {
updates["remind_expire"] = *req.RemindExpire
}
if req.RemindTraffic != nil {
updates["remind_traffic"] = *req.RemindTraffic
}
if err := database.DB.Model(&model.User{}).Where("id = ?", user.ID).Updates(updates).Error; err != nil {
Fail(c, 500, "save failed")
return
}
Success(c, true)
}
func UserChangePassword(c *gin.Context) {
user, ok := currentUser(c)
if !ok {
Fail(c, http.StatusUnauthorized, "user not found")
return
}
var req struct {
OldPassword string `json:"old_password" binding:"required"`
NewPassword string `json:"new_password" binding:"required,min=8"`
}
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, 400, "invalid request body")
return
}
if !utils.CheckPassword(req.OldPassword, user.Password, user.PasswordAlgo, user.PasswordSalt) {
Fail(c, 400, "the old password is wrong")
return
}
hashed, err := utils.HashPassword(req.NewPassword)
if err != nil {
Fail(c, 500, "failed to hash password")
return
}
if err := database.DB.Model(&model.User{}).
Where("id = ?", user.ID).
Updates(map[string]any{
"password": hashed,
"password_algo": nil,
"password_salt": nil,
"updated_at": time.Now().Unix(),
}).Error; err != nil {
Fail(c, 500, "save failed")
return
}
Success(c, true)
}
func UserServerFetch(c *gin.Context) {
user, ok := currentUser(c)
if !ok {
Fail(c, http.StatusUnauthorized, "user not found")
return
}
servers, err := service.AvailableServersForUser(user)
if err != nil {
Fail(c, 500, "failed to fetch servers")
return
}
Success(c, servers)
}
func currentUser(c *gin.Context) (*model.User, bool) {
if value, exists := c.Get("user"); exists {
if user, ok := value.(*model.User); ok {
return user, true
}
}
userIDValue, exists := c.Get("user_id")
if !exists {
return nil, false
}
userID, ok := userIDValue.(int)
if !ok {
return nil, false
}
var user model.User
if err := database.DB.First(&user, userID).Error; err != nil {
return nil, false
}
c.Set("user", &user)
return &user, true
}