first commit
This commit is contained in:
374
app/controllers/UserControllers.go
Normal file
374
app/controllers/UserControllers.go
Normal file
@@ -0,0 +1,374 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
database "goGin/app/database/config"
|
||||
"goGin/app/database/models"
|
||||
"goGin/app/middlewares"
|
||||
utils "goGin/pkg/utis"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserResponse, kullanıcı verilerini güvenli bir şekilde döndürmek için
|
||||
type UserResponse struct {
|
||||
ID uint `json:"id"`
|
||||
UserName string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
}
|
||||
|
||||
// AdminUserListItem, admin listesinde deleted_at ile ayırt etmek için
|
||||
type AdminUserListItem struct {
|
||||
UserResponse
|
||||
DeletedAt *time.Time `json:"deleted_at,omitempty"`
|
||||
}
|
||||
|
||||
// UserPayload, kullanıcı güncelleme payload'u
|
||||
type UserPayload struct {
|
||||
UserName string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password,omitempty"` // Opsiyonel şifre güncellemesi
|
||||
}
|
||||
|
||||
// AdminUserUpdatePayload, admin tarafından kullanıcı güncelleme
|
||||
type AdminUserUpdatePayload struct {
|
||||
UserName string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
IsAdmin *bool `json:"is_admin"` // Pointer allows checking if field is present
|
||||
}
|
||||
|
||||
// Helper to convert model to response
|
||||
func toUserResponse(u models.User) UserResponse {
|
||||
isAdmin := false
|
||||
if u.IsAdmin != nil {
|
||||
isAdmin = *u.IsAdmin
|
||||
}
|
||||
isVerified := false
|
||||
if u.EmailVerified != nil {
|
||||
isVerified = *u.EmailVerified
|
||||
}
|
||||
|
||||
return UserResponse{
|
||||
ID: u.ID,
|
||||
UserName: u.UserName,
|
||||
Email: u.Email,
|
||||
EmailVerified: isVerified,
|
||||
IsAdmin: isAdmin,
|
||||
}
|
||||
}
|
||||
|
||||
// toAdminUserListItem, admin listesinde deleted_at döndürmek için
|
||||
func toAdminUserListItem(u models.User) AdminUserListItem {
|
||||
item := AdminUserListItem{UserResponse: toUserResponse(u)}
|
||||
if u.DeletedAt.Valid {
|
||||
item.DeletedAt = &u.DeletedAt.Time
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
// GetProfile godoc
|
||||
// @Summary Get current user profile
|
||||
// @Description Get profile of the logged-in user
|
||||
// @Tags users
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Success 200 {object} controllers.UserResponse
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Router /api/v1/users/profile [get]
|
||||
func GetProfile(c *gin.Context) {
|
||||
claims, ok := middlewares.GetAuthClaims(c)
|
||||
if !ok {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := database.DB.Preload("SocialAccounts").Preload("Profile").First(&user, claims.UserID).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": toUserResponse(user)})
|
||||
}
|
||||
|
||||
// UpdateProfile godoc
|
||||
// @Summary Update current user profile
|
||||
// @Description Update profile of the logged-in user
|
||||
// @Tags users
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param user body UserPayload true "User update payload"
|
||||
// @Success 200 {object} controllers.UserResponse
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Router /api/v1/users/profile [put]
|
||||
func UpdateProfile(c *gin.Context) {
|
||||
claims, ok := middlewares.GetAuthClaims(c)
|
||||
if !ok {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
var payload UserPayload
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := database.DB.First(&user, claims.UserID).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
|
||||
if payload.UserName != "" {
|
||||
user.UserName = payload.UserName
|
||||
}
|
||||
if payload.Email != "" {
|
||||
user.Email = payload.Email
|
||||
// Email değişirse doğrulama sıfırlanabilir
|
||||
f := false
|
||||
user.EmailVerified = &f
|
||||
}
|
||||
if payload.Password != "" {
|
||||
hashed, err := utils.HashPassword(payload.Password)
|
||||
if err == nil {
|
||||
user.Password = hashed
|
||||
}
|
||||
}
|
||||
|
||||
if err := database.DB.Save(&user).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": toUserResponse(user)})
|
||||
}
|
||||
|
||||
// AdminListUsers godoc
|
||||
// @Summary Admin: List users
|
||||
// @Description Admin listing of users with pagination and search
|
||||
// @Tags users_admin
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param page query int false "Page number"
|
||||
// @Param per_page query int false "Items per page"
|
||||
// @Param q query string false "Search query (username or email)"
|
||||
// @Param soft query string false "Soft delete filter: only|with"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Router /api/v1/admin/users [get]
|
||||
func AdminListUsers(c *gin.Context) {
|
||||
if database.DB == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "database not configured"})
|
||||
return
|
||||
}
|
||||
pageStr := c.DefaultQuery("page", "1")
|
||||
perPageStr := c.DefaultQuery("per_page", "20")
|
||||
page, _ := strconv.Atoi(pageStr)
|
||||
perPage, _ := strconv.Atoi(perPageStr)
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if perPage < 1 {
|
||||
perPage = 20
|
||||
}
|
||||
if perPage > 100 {
|
||||
perPage = 100
|
||||
}
|
||||
offset := (page - 1) * perPage
|
||||
|
||||
soft := c.Query("soft")
|
||||
var query *gorm.DB
|
||||
if soft == "only" {
|
||||
query = database.DB.Unscoped().Model(&models.User{}).Where("deleted_at IS NOT NULL")
|
||||
} else if soft == "with" {
|
||||
query = database.DB.Unscoped().Model(&models.User{})
|
||||
} else {
|
||||
query = database.DB.Model(&models.User{})
|
||||
}
|
||||
|
||||
if q := c.Query("q"); q != "" {
|
||||
like := "%" + q + "%"
|
||||
query = query.Where("user_name LIKE ? OR email LIKE ?", like, like)
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var users []models.User
|
||||
if err := query.Order("created_at desc").Limit(perPage).Offset(offset).Find(&users).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var data []AdminUserListItem
|
||||
for _, u := range users {
|
||||
data = append(data, toAdminUserListItem(u))
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"items": data, "total": total, "page": page, "per_page": perPage})
|
||||
}
|
||||
|
||||
// AdminGetUser godoc
|
||||
// @Summary Admin: Get user
|
||||
// @Description Get user details by ID
|
||||
// @Tags users_admin
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param id path int true "User ID"
|
||||
// @Success 200 {object} controllers.UserResponse
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /api/v1/admin/users/{id} [get]
|
||||
func AdminGetUser(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil || id < 1 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
|
||||
var user models.User
|
||||
// Admin deleted kullanıcıyı da görebilmeli mi? Genelde evet, soft=with ile listede görüyorsa detayda da görmeli.
|
||||
// Varsayılan olarak normal get soft-deleted getirmez. Unscoped kullanalım veya id ile direk bakalım.
|
||||
if err := database.DB.Unscoped().First(&user, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": toUserResponse(user)})
|
||||
}
|
||||
|
||||
// AdminUpdateUser godoc
|
||||
// @Summary Admin: Update user
|
||||
// @Description Update user details (admin)
|
||||
// @Tags users_admin
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "User ID"
|
||||
// @Param user body AdminUserUpdatePayload true "User update payload"
|
||||
// @Success 200 {object} controllers.UserResponse
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Router /api/v1/admin/users/{id} [put]
|
||||
func AdminUpdateUser(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil || id < 1 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
|
||||
var payload AdminUserUpdatePayload
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := database.DB.Unscoped().First(&user, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
|
||||
if payload.UserName != "" {
|
||||
user.UserName = payload.UserName
|
||||
}
|
||||
if payload.Email != "" {
|
||||
user.Email = payload.Email
|
||||
}
|
||||
if payload.IsAdmin != nil {
|
||||
user.IsAdmin = payload.IsAdmin
|
||||
}
|
||||
|
||||
if err := database.DB.Save(&user).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": toUserResponse(user)})
|
||||
}
|
||||
|
||||
// AdminDeleteUser godoc
|
||||
// @Summary Admin: Delete user
|
||||
// @Description Soft delete user
|
||||
// @Tags users_admin
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param id path int true "User ID"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Router /api/v1/admin/users/{id} [delete]
|
||||
func AdminDeleteUser(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil || id < 1 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := database.DB.First(&user, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.DB.Delete(&user).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "user deleted successfully",
|
||||
"id": user.ID,
|
||||
})
|
||||
}
|
||||
|
||||
// AdminRestoreUser godoc
|
||||
// @Summary Admin: Restore user
|
||||
// @Description Restore soft-deleted user
|
||||
// @Tags users_admin
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param id path int true "User ID"
|
||||
// @Success 200 {object} controllers.UserResponse
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Router /api/v1/admin/users/{id}/restore [post]
|
||||
func AdminRestoreUser(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil || id < 1 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := database.DB.Unscoped().First(&user, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
|
||||
if user.DeletedAt.Valid {
|
||||
// Restore
|
||||
if err := database.DB.Unscoped().Model(&user).Update("deleted_at", nil).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": toUserResponse(user)})
|
||||
}
|
||||
Reference in New Issue
Block a user