569 lines
16 KiB
Go
569 lines
16 KiB
Go
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
|
|
}
|