Files
goGin/app/controllers/UserControllers.go
Beyhan Oğur 2a5b661443 first commit
2026-04-26 21:46:42 +03:00

375 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)})
}