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 }