asd
This commit is contained in:
@@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
@@ -16,10 +17,21 @@ import (
|
||||
func NewRouter(db *gorm.DB, cfg config.Config) *gin.Engine {
|
||||
router := gin.Default()
|
||||
router.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"http://localhost:5173", "http://127.0.0.1:5173", "http://localhost:8081", "http://127.0.0.1:8081", "https://social-rating.nekiiinkognito.ru/"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowOriginFunc: func(origin string) bool {
|
||||
switch {
|
||||
case strings.HasPrefix(origin, "http://localhost:"):
|
||||
return true
|
||||
case strings.HasPrefix(origin, "http://127.0.0.1:"):
|
||||
return true
|
||||
case origin == "https://social-rating.nekiiinkognito.ru":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}))
|
||||
@@ -38,6 +50,10 @@ func NewRouter(db *gorm.DB, cfg config.Config) *gin.Engine {
|
||||
protected := api.Group("/")
|
||||
protected.Use(auth.Middleware(cfg.JWTSecret))
|
||||
protected.GET("/auth/me", authHandler.Me)
|
||||
protected.GET("/users", socialRatingHandler.ListUsers)
|
||||
protected.GET("/users/:userId", socialRatingHandler.GetUser)
|
||||
protected.GET("/users/:userId/social-rating/history", socialRatingHandler.GetUserHistory)
|
||||
protected.GET("/social-rating/operations", socialRatingHandler.GetRecentOperations)
|
||||
protected.POST("/social-rating/increase", socialRatingHandler.Increase)
|
||||
protected.POST("/social-rating/decrease", socialRatingHandler.Decrease)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package socialrating
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
@@ -26,10 +27,79 @@ type ChangeResponse struct {
|
||||
CurrentRating models.UserSocialRating `json:"currentRating"`
|
||||
}
|
||||
|
||||
type UsersResponse struct {
|
||||
Users []UserWithRating `json:"users"`
|
||||
}
|
||||
|
||||
type UserRatingResponse struct {
|
||||
User UserWithRating `json:"user"`
|
||||
}
|
||||
|
||||
type HistoryResponse struct {
|
||||
Operations []models.SocialRatingOperation `json:"operations"`
|
||||
}
|
||||
|
||||
func NewHandler(service Service) Handler {
|
||||
return Handler{Service: service}
|
||||
}
|
||||
|
||||
func (h Handler) ListUsers(ctx *gin.Context) {
|
||||
limit := parsePositiveLimit(ctx.Query("limit"), 50)
|
||||
|
||||
users, err := h.Service.ListUsersWithRatings(limit)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load users"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, UsersResponse{Users: users})
|
||||
}
|
||||
|
||||
func (h Handler) GetUser(ctx *gin.Context) {
|
||||
userID, err := parseUintParam(ctx.Param("userId"))
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.Service.GetUserRating(userID)
|
||||
if err != nil {
|
||||
handleApplyChangeError(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, UserRatingResponse{User: user})
|
||||
}
|
||||
|
||||
func (h Handler) GetUserHistory(ctx *gin.Context) {
|
||||
userID, err := parseUintParam(ctx.Param("userId"))
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
limit := parsePositiveLimit(ctx.Query("limit"), 50)
|
||||
operations, err := h.Service.GetUserHistory(userID, limit)
|
||||
if err != nil {
|
||||
handleApplyChangeError(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, HistoryResponse{Operations: operations})
|
||||
}
|
||||
|
||||
func (h Handler) GetRecentOperations(ctx *gin.Context) {
|
||||
limit := parsePositiveLimit(ctx.Query("limit"), 50)
|
||||
|
||||
operations, err := h.Service.GetRecentOperations(limit)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load operations"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, HistoryResponse{Operations: operations})
|
||||
}
|
||||
|
||||
func (h Handler) Increase(ctx *gin.Context) {
|
||||
h.applySignedChange(ctx, "increase", 1)
|
||||
}
|
||||
@@ -97,3 +167,12 @@ func handleApplyChangeError(ctx *gin.Context, err error) {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
func parseUintParam(raw string) (uint, error) {
|
||||
value, err := strconv.ParseUint(raw, 10, 64)
|
||||
if err != nil || value == 0 {
|
||||
return 0, errors.New("invalid user id")
|
||||
}
|
||||
|
||||
return uint(value), nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package socialrating
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
@@ -22,6 +24,16 @@ type ChangeInput struct {
|
||||
Source string
|
||||
}
|
||||
|
||||
type UserWithRating struct {
|
||||
ID uint `json:"id"`
|
||||
Email string `json:"email"`
|
||||
IsAdmin bool `json:"isAdmin"`
|
||||
Score int `json:"score"`
|
||||
LastOperationID *uint `json:"lastOperationId,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func NewService(db *gorm.DB) Service {
|
||||
return Service{DB: db}
|
||||
}
|
||||
@@ -95,3 +107,131 @@ func ensureUserExists(tx *gorm.DB, userID uint) error {
|
||||
var user models.User
|
||||
return tx.First(&user, "id = ?", userID).Error
|
||||
}
|
||||
|
||||
func (s Service) ListUsersWithRatings(limit int) ([]UserWithRating, error) {
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
var rows []struct {
|
||||
ID uint
|
||||
Email string
|
||||
IsAdmin bool
|
||||
Score int
|
||||
LastOperationID *uint
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
err := s.DB.
|
||||
Table("users").
|
||||
Select(
|
||||
"users.id, users.email, users.is_admin, COALESCE(user_social_ratings.score, 0) AS score, user_social_ratings.last_operation_id, users.created_at, users.updated_at",
|
||||
).
|
||||
Joins("LEFT JOIN user_social_ratings ON user_social_ratings.user_id = users.id").
|
||||
Order("score DESC, users.id ASC").
|
||||
Limit(limit).
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]UserWithRating, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
result = append(result, UserWithRating{
|
||||
ID: row.ID,
|
||||
Email: row.Email,
|
||||
IsAdmin: row.IsAdmin,
|
||||
Score: row.Score,
|
||||
LastOperationID: row.LastOperationID,
|
||||
CreatedAt: row.CreatedAt,
|
||||
UpdatedAt: row.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s Service) GetUserRating(userID uint) (UserWithRating, error) {
|
||||
var row struct {
|
||||
ID uint
|
||||
Email string
|
||||
IsAdmin bool
|
||||
Score int
|
||||
LastOperationID *uint
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
err := s.DB.
|
||||
Table("users").
|
||||
Select(
|
||||
"users.id, users.email, users.is_admin, COALESCE(user_social_ratings.score, 0) AS score, user_social_ratings.last_operation_id, users.created_at, users.updated_at",
|
||||
).
|
||||
Joins("LEFT JOIN user_social_ratings ON user_social_ratings.user_id = users.id").
|
||||
Where("users.id = ?", userID).
|
||||
Take(&row).Error
|
||||
if err != nil {
|
||||
return UserWithRating{}, err
|
||||
}
|
||||
|
||||
return UserWithRating{
|
||||
ID: row.ID,
|
||||
Email: row.Email,
|
||||
IsAdmin: row.IsAdmin,
|
||||
Score: row.Score,
|
||||
LastOperationID: row.LastOperationID,
|
||||
CreatedAt: row.CreatedAt,
|
||||
UpdatedAt: row.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s Service) GetUserHistory(userID uint, limit int) ([]models.SocialRatingOperation, error) {
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
if err := ensureUserExists(s.DB, userID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var operations []models.SocialRatingOperation
|
||||
err := s.DB.
|
||||
Where("target_user_id = ?", userID).
|
||||
Order("created_at DESC, id DESC").
|
||||
Limit(limit).
|
||||
Find(&operations).Error
|
||||
|
||||
return operations, err
|
||||
}
|
||||
|
||||
func (s Service) GetRecentOperations(limit int) ([]models.SocialRatingOperation, error) {
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
var operations []models.SocialRatingOperation
|
||||
err := s.DB.
|
||||
Order("created_at DESC, id DESC").
|
||||
Limit(limit).
|
||||
Find(&operations).Error
|
||||
|
||||
return operations, err
|
||||
}
|
||||
|
||||
func parsePositiveLimit(raw string, fallback int) int {
|
||||
if raw == "" {
|
||||
return fallback
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(raw)
|
||||
if err != nil || value <= 0 {
|
||||
return fallback
|
||||
}
|
||||
|
||||
if value > 200 {
|
||||
return 200
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user