first commit
This commit is contained in:
568
api/handlers/about_handler.go
Normal file
568
api/handlers/about_handler.go
Normal file
@@ -0,0 +1,568 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gauth-central/config"
|
||||
"gauth-central/internal/services"
|
||||
"gauth-central/pkg/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AboutHandler struct {
|
||||
aboutService *services.AboutService
|
||||
}
|
||||
|
||||
func NewAboutHandler(aboutService *services.AboutService) *AboutHandler {
|
||||
return &AboutHandler{aboutService: aboutService}
|
||||
}
|
||||
|
||||
// GetAllAbout godoc
|
||||
// @Summary Get all active about entries
|
||||
// @Description Retrieve a list of active about entries
|
||||
// @Tags about
|
||||
// @Produce json
|
||||
// @Success 200 {array} models.About
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Router /about [get]
|
||||
func (h *AboutHandler) GetAllAbout(c *gin.Context) {
|
||||
abouts, err := h.aboutService.GetAllAbout(true)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, abouts)
|
||||
}
|
||||
|
||||
// GetActiveAbout godoc
|
||||
// @Summary Get active about entry
|
||||
// @Description Retrieve the newest active about entry
|
||||
// @Tags about
|
||||
// @Produce json
|
||||
// @Success 200 {object} models.About
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /about/active [get]
|
||||
func (h *AboutHandler) GetActiveAbout(c *gin.Context) {
|
||||
about, err := h.aboutService.GetFirstActiveAbout()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, about)
|
||||
}
|
||||
|
||||
// AdminGetAllAbout godoc
|
||||
// @Summary Get all about entries (Admin)
|
||||
// @Description Retrieve a list of all about entries including inactive ones
|
||||
// @Tags admin
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce json
|
||||
// @Success 200 {array} models.About
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Router /admin/about [get]
|
||||
func (h *AboutHandler) AdminGetAllAbout(c *gin.Context) {
|
||||
abouts, err := h.aboutService.GetAllAbout(false)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, abouts)
|
||||
}
|
||||
|
||||
// AdminGetAboutByID godoc
|
||||
// @Summary Get an about entry by ID (Admin)
|
||||
// @Description Retrieve details of a specific about entry
|
||||
// @Tags admin
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce json
|
||||
// @Param id path string true "About ID"
|
||||
// @Success 200 {object} models.About
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /admin/about/{id} [get]
|
||||
func (h *AboutHandler) AdminGetAboutByID(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
about, err := h.aboutService.GetAboutByID(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, about)
|
||||
}
|
||||
|
||||
// CreateAbout godoc
|
||||
// @Summary Create a new about entry (Admin)
|
||||
// @Description Create a new about entry
|
||||
// @Tags admin
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Param title formData string true "Title"
|
||||
// @Param image formData file false "Image"
|
||||
// @Param image_sub formData string false "Image subtitle"
|
||||
// @Param cv formData file false "CV file"
|
||||
// @Param birthday formData string false "Birthday"
|
||||
// @Param city formData string false "City"
|
||||
// @Param study formData string false "Study"
|
||||
// @Param website formData string false "Website"
|
||||
// @Param phone formData string false "Phone"
|
||||
// @Param age formData string false "Age"
|
||||
// @Param interests formData string false "Interests"
|
||||
// @Param degree formData string false "Degree"
|
||||
// @Param x formData string false "X"
|
||||
// @Param mail formData string false "Mail"
|
||||
// @Param done formData int false "Done"
|
||||
// @Param project_done formData string false "Project done"
|
||||
// @Param user_h formData int false "User count"
|
||||
// @Param hapy_user formData string false "Happy user"
|
||||
// @Param great formData int false "Great"
|
||||
// @Param great_reviews formData string false "Great reviews"
|
||||
// @Param team formData int false "Team"
|
||||
// @Param support_team formData string false "Support team"
|
||||
// @Param is_active formData bool false "Is active"
|
||||
// @Param counter_active formData bool false "Counter active"
|
||||
// @Success 201 {object} models.About
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Router /admin/about [post]
|
||||
func (h *AboutHandler) CreateAbout(c *gin.Context) {
|
||||
title := strings.TrimSpace(c.PostForm("title"))
|
||||
if title == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "title is required"})
|
||||
return
|
||||
}
|
||||
|
||||
imageSub := strings.TrimSpace(c.PostForm("image_sub"))
|
||||
birthday := strings.TrimSpace(c.PostForm("birthday"))
|
||||
city := strings.TrimSpace(c.PostForm("city"))
|
||||
study := strings.TrimSpace(c.PostForm("study"))
|
||||
website := strings.TrimSpace(c.PostForm("website"))
|
||||
phone := strings.TrimSpace(c.PostForm("phone"))
|
||||
age := strings.TrimSpace(c.PostForm("age"))
|
||||
interests := strings.TrimSpace(c.PostForm("interests"))
|
||||
degree := strings.TrimSpace(c.PostForm("degree"))
|
||||
xValue := strings.TrimSpace(c.PostForm("x"))
|
||||
mail := strings.TrimSpace(c.PostForm("mail"))
|
||||
projectDone := strings.TrimSpace(c.PostForm("project_done"))
|
||||
hapyUser := strings.TrimSpace(c.PostForm("hapy_user"))
|
||||
greatReviews := strings.TrimSpace(c.PostForm("great_reviews"))
|
||||
supportTeam := strings.TrimSpace(c.PostForm("support_team"))
|
||||
|
||||
done, err := parseOptionalInt(c, "done")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "done must be a number"})
|
||||
return
|
||||
}
|
||||
userH, err := parseOptionalInt(c, "user_h")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "user_h must be a number"})
|
||||
return
|
||||
}
|
||||
great, err := parseOptionalInt(c, "great")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "great must be a number"})
|
||||
return
|
||||
}
|
||||
team, err := parseOptionalInt(c, "team")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "team must be a number"})
|
||||
return
|
||||
}
|
||||
|
||||
isActivePtr, err := parseOptionalBool(c, "is_active")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "is_active must be true or false"})
|
||||
return
|
||||
}
|
||||
counterActivePtr, err := parseOptionalBool(c, "counter_active")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "counter_active must be true or false"})
|
||||
return
|
||||
}
|
||||
|
||||
isActive := false
|
||||
if isActivePtr != nil {
|
||||
isActive = *isActivePtr
|
||||
}
|
||||
counterActive := false
|
||||
if counterActivePtr != nil {
|
||||
counterActive = *counterActivePtr
|
||||
}
|
||||
|
||||
imageURL := ""
|
||||
imageFile, imageErr := c.FormFile("image")
|
||||
if imageErr == nil {
|
||||
if imageFile.Size > 5*1024*1024 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Image size exceeds 5MB limit"})
|
||||
return
|
||||
}
|
||||
imageURL, err = utils.SaveOptimizedImage(imageFile, "./uploads/about", "about", &utils.ImageOptions{
|
||||
Width: config.AppConfig.AboutImageWidth,
|
||||
Height: config.AppConfig.AboutImageHeight,
|
||||
Quality: float32(config.AppConfig.AboutImageQuality),
|
||||
Format: config.AppConfig.AboutImageFormat,
|
||||
Mode: config.AppConfig.AboutImageMode,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cvPath := ""
|
||||
cvFile, cvErr := c.FormFile("cv")
|
||||
if cvErr == nil {
|
||||
cvPath, err = saveUploadedFile(c, cvFile, "./uploads/cv", "cv")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save CV: " + err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
about, err := h.aboutService.CreateAbout(
|
||||
title,
|
||||
imageURL,
|
||||
imageSub,
|
||||
cvPath,
|
||||
birthday,
|
||||
city,
|
||||
study,
|
||||
website,
|
||||
phone,
|
||||
age,
|
||||
interests,
|
||||
degree,
|
||||
xValue,
|
||||
mail,
|
||||
done,
|
||||
projectDone,
|
||||
userH,
|
||||
hapyUser,
|
||||
great,
|
||||
greatReviews,
|
||||
team,
|
||||
supportTeam,
|
||||
isActive,
|
||||
counterActive,
|
||||
)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, about)
|
||||
}
|
||||
|
||||
// UpdateAbout godoc
|
||||
// @Summary Update an about entry (Admin)
|
||||
// @Description Update an existing about entry
|
||||
// @Tags admin
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Param id path string true "About ID"
|
||||
// @Param title formData string false "Title"
|
||||
// @Param image formData file false "Image"
|
||||
// @Param image_sub formData string false "Image subtitle"
|
||||
// @Param cv formData file false "CV file"
|
||||
// @Param birthday formData string false "Birthday"
|
||||
// @Param city formData string false "City"
|
||||
// @Param study formData string false "Study"
|
||||
// @Param website formData string false "Website"
|
||||
// @Param phone formData string false "Phone"
|
||||
// @Param age formData string false "Age"
|
||||
// @Param interests formData string false "Interests"
|
||||
// @Param degree formData string false "Degree"
|
||||
// @Param x formData string false "X"
|
||||
// @Param mail formData string false "Mail"
|
||||
// @Param done formData int false "Done"
|
||||
// @Param project_done formData string false "Project done"
|
||||
// @Param user_h formData int false "User count"
|
||||
// @Param hapy_user formData string false "Happy user"
|
||||
// @Param great formData int false "Great"
|
||||
// @Param great_reviews formData string false "Great reviews"
|
||||
// @Param team formData int false "Team"
|
||||
// @Param support_team formData string false "Support team"
|
||||
// @Param slug formData string false "Slug"
|
||||
// @Param is_active formData bool false "Is active"
|
||||
// @Param counter_active formData bool false "Counter active"
|
||||
// @Success 200 {object} models.About
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /admin/about/{id} [put]
|
||||
func (h *AboutHandler) UpdateAbout(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
title, hasTitle := getOptionalFormValue(c, "title")
|
||||
imageSub, hasImageSub := getOptionalFormValue(c, "image_sub")
|
||||
birthday, hasBirthday := getOptionalFormValue(c, "birthday")
|
||||
city, hasCity := getOptionalFormValue(c, "city")
|
||||
study, hasStudy := getOptionalFormValue(c, "study")
|
||||
website, hasWebsite := getOptionalFormValue(c, "website")
|
||||
phone, hasPhone := getOptionalFormValue(c, "phone")
|
||||
age, hasAge := getOptionalFormValue(c, "age")
|
||||
interests, hasInterests := getOptionalFormValue(c, "interests")
|
||||
degree, hasDegree := getOptionalFormValue(c, "degree")
|
||||
xValue, hasX := getOptionalFormValue(c, "x")
|
||||
mail, hasMail := getOptionalFormValue(c, "mail")
|
||||
projectDone, hasProjectDone := getOptionalFormValue(c, "project_done")
|
||||
hapyUser, hasHapyUser := getOptionalFormValue(c, "hapy_user")
|
||||
greatReviews, hasGreatReviews := getOptionalFormValue(c, "great_reviews")
|
||||
supportTeam, hasSupportTeam := getOptionalFormValue(c, "support_team")
|
||||
slug, hasSlug := getOptionalFormValue(c, "slug")
|
||||
|
||||
done, err := parseOptionalInt(c, "done")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "done must be a number"})
|
||||
return
|
||||
}
|
||||
userH, err := parseOptionalInt(c, "user_h")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "user_h must be a number"})
|
||||
return
|
||||
}
|
||||
great, err := parseOptionalInt(c, "great")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "great must be a number"})
|
||||
return
|
||||
}
|
||||
team, err := parseOptionalInt(c, "team")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "team must be a number"})
|
||||
return
|
||||
}
|
||||
|
||||
isActivePtr, err := parseOptionalBool(c, "is_active")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "is_active must be true or false"})
|
||||
return
|
||||
}
|
||||
counterActivePtr, err := parseOptionalBool(c, "counter_active")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "counter_active must be true or false"})
|
||||
return
|
||||
}
|
||||
|
||||
var titlePtr *string
|
||||
var imageSubPtr *string
|
||||
var birthdayPtr *string
|
||||
var cityPtr *string
|
||||
var studyPtr *string
|
||||
var websitePtr *string
|
||||
var phonePtr *string
|
||||
var agePtr *string
|
||||
var interestsPtr *string
|
||||
var degreePtr *string
|
||||
var xPtr *string
|
||||
var mailPtr *string
|
||||
var projectDonePtr *string
|
||||
var hapyUserPtr *string
|
||||
var greatReviewsPtr *string
|
||||
var supportTeamPtr *string
|
||||
var slugPtr *string
|
||||
|
||||
if hasTitle {
|
||||
titlePtr = &title
|
||||
}
|
||||
if hasImageSub {
|
||||
imageSubPtr = &imageSub
|
||||
}
|
||||
if hasBirthday {
|
||||
birthdayPtr = &birthday
|
||||
}
|
||||
if hasCity {
|
||||
cityPtr = &city
|
||||
}
|
||||
if hasStudy {
|
||||
studyPtr = &study
|
||||
}
|
||||
if hasWebsite {
|
||||
websitePtr = &website
|
||||
}
|
||||
if hasPhone {
|
||||
phonePtr = &phone
|
||||
}
|
||||
if hasAge {
|
||||
agePtr = &age
|
||||
}
|
||||
if hasInterests {
|
||||
interestsPtr = &interests
|
||||
}
|
||||
if hasDegree {
|
||||
degreePtr = °ree
|
||||
}
|
||||
if hasX {
|
||||
xPtr = &xValue
|
||||
}
|
||||
if hasMail {
|
||||
mailPtr = &mail
|
||||
}
|
||||
if hasProjectDone {
|
||||
projectDonePtr = &projectDone
|
||||
}
|
||||
if hasHapyUser {
|
||||
hapyUserPtr = &hapyUser
|
||||
}
|
||||
if hasGreatReviews {
|
||||
greatReviewsPtr = &greatReviews
|
||||
}
|
||||
if hasSupportTeam {
|
||||
supportTeamPtr = &supportTeam
|
||||
}
|
||||
if hasSlug {
|
||||
slugPtr = &slug
|
||||
}
|
||||
|
||||
var imagePtr *string
|
||||
imageFile, imageErr := c.FormFile("image")
|
||||
if imageErr == nil {
|
||||
if imageFile.Size > 5*1024*1024 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Image size exceeds 5MB limit"})
|
||||
return
|
||||
}
|
||||
about, fetchErr := h.aboutService.GetAboutByID(id)
|
||||
if fetchErr != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fetchErr.Error()})
|
||||
return
|
||||
}
|
||||
if about.Image != "" && strings.HasPrefix(about.Image, "/uploads/") {
|
||||
oldPath := "." + about.Image
|
||||
_ = os.Remove(oldPath)
|
||||
}
|
||||
imageURL, saveErr := utils.SaveOptimizedImage(imageFile, "./uploads/about", id, &utils.ImageOptions{
|
||||
Width: config.AppConfig.AboutImageWidth,
|
||||
Height: config.AppConfig.AboutImageHeight,
|
||||
Quality: float32(config.AppConfig.AboutImageQuality),
|
||||
Format: config.AppConfig.AboutImageFormat,
|
||||
Mode: config.AppConfig.AboutImageMode,
|
||||
})
|
||||
if saveErr != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + saveErr.Error()})
|
||||
return
|
||||
}
|
||||
imagePtr = &imageURL
|
||||
}
|
||||
|
||||
var cvPtr *string
|
||||
cvFile, cvErr := c.FormFile("cv")
|
||||
if cvErr == nil {
|
||||
about, fetchErr := h.aboutService.GetAboutByID(id)
|
||||
if fetchErr != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fetchErr.Error()})
|
||||
return
|
||||
}
|
||||
if about.CV != "" && strings.HasPrefix(about.CV, "/uploads/") {
|
||||
oldPath := "." + about.CV
|
||||
_ = os.Remove(oldPath)
|
||||
}
|
||||
cvPath, saveErr := saveUploadedFile(c, cvFile, "./uploads/cv", "cv")
|
||||
if saveErr != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save CV: " + saveErr.Error()})
|
||||
return
|
||||
}
|
||||
cvPtr = &cvPath
|
||||
}
|
||||
|
||||
about, err := h.aboutService.UpdateAbout(
|
||||
id,
|
||||
titlePtr,
|
||||
imagePtr,
|
||||
imageSubPtr,
|
||||
cvPtr,
|
||||
birthdayPtr,
|
||||
cityPtr,
|
||||
studyPtr,
|
||||
websitePtr,
|
||||
phonePtr,
|
||||
agePtr,
|
||||
interestsPtr,
|
||||
degreePtr,
|
||||
xPtr,
|
||||
mailPtr,
|
||||
done,
|
||||
projectDonePtr,
|
||||
userH,
|
||||
hapyUserPtr,
|
||||
great,
|
||||
greatReviewsPtr,
|
||||
team,
|
||||
supportTeamPtr,
|
||||
slugPtr,
|
||||
isActivePtr,
|
||||
counterActivePtr,
|
||||
)
|
||||
if err != nil {
|
||||
status := http.StatusInternalServerError
|
||||
if err.Error() == "about not found" {
|
||||
status = http.StatusNotFound
|
||||
}
|
||||
c.JSON(status, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, about)
|
||||
}
|
||||
|
||||
// DeleteAbout godoc
|
||||
// @Summary Delete an about entry (Admin)
|
||||
// @Description Delete an about entry by ID
|
||||
// @Tags admin
|
||||
// @Security ApiKeyAuth
|
||||
// @Param id path string true "About ID"
|
||||
// @Success 200 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /admin/about/{id} [delete]
|
||||
func (h *AboutHandler) DeleteAbout(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if err := h.aboutService.DeleteAbout(id); err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "About deleted successfully"})
|
||||
}
|
||||
|
||||
func parseOptionalInt(c *gin.Context, key string) (*int, error) {
|
||||
value, exists := c.GetPostForm(key)
|
||||
if !exists {
|
||||
return nil, nil
|
||||
}
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return nil, nil
|
||||
}
|
||||
parsed, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &parsed, nil
|
||||
}
|
||||
|
||||
func saveUploadedFile(c *gin.Context, file *multipart.FileHeader, uploadDir string, prefix string) (string, error) {
|
||||
if err := os.MkdirAll(uploadDir, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ext := filepath.Ext(file.Filename)
|
||||
filename := fmt.Sprintf("%s_%d%s", prefix, time.Now().UnixNano(), ext)
|
||||
fullPath := filepath.Join(uploadDir, filename)
|
||||
|
||||
if err := c.SaveUploadedFile(file, fullPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
relPath := filepath.Join(uploadDir, filename)
|
||||
if strings.HasPrefix(relPath, ".") {
|
||||
relPath = relPath[1:]
|
||||
}
|
||||
if !strings.HasPrefix(relPath, "/") {
|
||||
relPath = "/" + relPath
|
||||
}
|
||||
|
||||
return relPath, nil
|
||||
}
|
||||
Reference in New Issue
Block a user