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

759 lines
24 KiB
Go

package controllers
import (
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
database "goGin/app/database/config"
"goGin/app/database/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// Payload for creating/updating settings
type SettingPayload struct {
Title string `json:"title" binding:"required"`
MetaTitle string `json:"meta_title" binding:"required"`
MetaDescription string `json:"meta_description" binding:"required"`
Phone string `json:"phone" binding:"required"`
URL string `json:"url" binding:"required"`
Email string `json:"email" binding:"required"`
Facebook string `json:"facebook"`
X string `json:"x"`
Instagram string `json:"instagram"`
Whatsapp string `json:"whatsapp"`
Pinterest string `json:"pinterest"`
Linkedin string `json:"linkedin"`
Slogan string `json:"slogan"`
Address string `json:"address"`
Copyright string `json:"copyright"`
MapEmbed string `json:"map_embed"`
WLogo string `json:"w_logo"`
BLogo string `json:"b_logo"`
IsActive *bool `json:"is_active"`
// Optional image transformation / dimension settings
WWidth *int `json:"w_width"`
WHeight *int `json:"w_height"`
WQuality *int `json:"w_quality"`
WFormat string `json:"w_format"`
BWidth *int `json:"b_width"`
BHeight *int `json:"b_height"`
BQuality *int `json:"b_quality"`
BFormat string `json:"b_format"`
}
// AdminListSettings godoc
// @Summary Admin: List settings
// @Description Admin listing of settings. Use ?soft=only to list deleted, ?soft=with to include deleted.
// @Tags settings
// @Security BearerAuth
// @Produce json
// @Param page query int false "Page number"
// @Param per_page query int false "Items per page"
// @Param soft query string false "Soft delete filter: only|with"
// @Success 200 {object} controllers.SettingListResponse
// @Failure 500 {object} map[string]string
// @Router /api/v1/admin/settings [get]
func AdminListSettings(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 > 200 {
perPage = 200
}
offset := (page - 1) * perPage
soft := c.Query("soft")
var query *gorm.DB
if soft == "only" {
query = database.DB.Unscoped().Model(&models.Setting{}).Where("deleted_at IS NOT NULL")
} else if soft == "with" {
query = database.DB.Unscoped().Model(&models.Setting{})
} else {
query = database.DB.Model(&models.Setting{})
}
var total int64
if err := query.Count(&total).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
var items []models.Setting
if err := query.Order("created_at desc").Limit(perPage).Offset(offset).Find(&items).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"items": items, "total": total, "page": page, "per_page": perPage})
}
// AdminGetSetting godoc
// @Summary Admin: Get a setting by id
// @Description Return a single setting by id
// @Tags settings
// @Security BearerAuth
// @Produce json
// @Param id path int true "Setting ID"
// @Success 200 {object} controllers.SettingResponse
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /api/v1/admin/settings/{id} [get]
func AdminGetSetting(c *gin.Context) {
if database.DB == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database not configured"})
return
}
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 s models.Setting
if err := database.DB.Unscoped().First(&s, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "setting not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"data": s})
}
// AdminCreateSetting godoc
// @Summary Admin: Create a setting
// @Description Create a new setting
// @Tags settings
// @Security BearerAuth
// @Accept multipart/form-data
// @Produce json
// @Param title formData string true "Title"
// @Param meta_title formData string true "Meta title"
// @Param meta_description formData string true "Meta description"
// @Param phone formData string true "Phone"
// @Param url formData string true "URL"
// @Param email formData string true "Email"
// @Param facebook formData string false "Facebook"
// @Param x formData string false "X"
// @Param instagram formData string false "Instagram"
// @Param whatsapp formData string false "Whatsapp"
// @Param pinterest formData string false "Pinterest"
// @Param linkedin formData string false "Linkedin"
// @Param slogan formData string false "Slogan"
// @Param address formData string false "Address"
// @Param copyright formData string false "Copyright"
// @Param map_embed formData string false "Map embed"
// @Param w_logo formData file false "White logo file upload (or provide w_logo path as string)"
// @Param b_logo formData file false "Black logo file upload (or provide b_logo path as string)"
// @Param is_active formData boolean false "Is active"
// @Param w_width formData int false "W logo width"
// @Param w_height formData int false "W logo height"
// @Param w_quality formData int false "W logo quality"
// @Param w_format formData string false "W logo format"
// @Param b_width formData int false "B logo width"
// @Param b_height formData int false "B logo height"
// @Param b_quality formData int false "B logo quality"
// @Param b_format formData string false "B logo format"
// @Success 201 {object} controllers.SettingResponse
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/v1/admin/settings [post]
func AdminCreateSetting(c *gin.Context) {
if database.DB == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database not configured"})
return
}
// Support both JSON and multipart/form-data
var payload SettingPayload
contentType := c.GetHeader("Content-Type")
if strings.HasPrefix(contentType, "multipart/form-data") {
// read form fields
payload.Title = c.PostForm("title")
payload.MetaTitle = c.PostForm("meta_title")
payload.MetaDescription = c.PostForm("meta_description")
payload.Phone = c.PostForm("phone")
payload.URL = c.PostForm("url")
payload.Email = c.PostForm("email")
payload.Facebook = c.PostForm("facebook")
payload.X = c.PostForm("x")
payload.Instagram = c.PostForm("instagram")
payload.Whatsapp = c.PostForm("whatsapp")
payload.Pinterest = c.PostForm("pinterest")
payload.Linkedin = c.PostForm("linkedin")
payload.Slogan = c.PostForm("slogan")
payload.Address = c.PostForm("address")
payload.Copyright = c.PostForm("copyright")
payload.MapEmbed = c.PostForm("map_embed")
// keep payload.WLogo/BLogo as string if client sends path
payload.WLogo = c.PostForm("w_logo")
payload.BLogo = c.PostForm("b_logo")
if v := c.PostForm("is_active"); v != "" {
if b, err := strconv.ParseBool(v); err == nil {
payload.IsActive = &b
}
}
// numeric metadata
if v := c.PostForm("w_width"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
payload.WWidth = &n
}
}
if v := c.PostForm("w_height"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
payload.WHeight = &n
}
}
if v := c.PostForm("w_quality"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
payload.WQuality = &n
}
}
payload.WFormat = c.PostForm("w_format")
if v := c.PostForm("b_width"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
payload.BWidth = &n
}
}
if v := c.PostForm("b_height"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
payload.BHeight = &n
}
}
if v := c.PostForm("b_quality"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
payload.BQuality = &n
}
}
payload.BFormat = c.PostForm("b_format")
} else {
// JSON
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
}
// basic required validation
if payload.Title == "" || payload.MetaTitle == "" || payload.MetaDescription == "" || payload.Phone == "" || payload.URL == "" || payload.Email == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "missing required fields"})
return
}
isActive := false
if payload.IsActive != nil {
isActive = *payload.IsActive
}
setting := models.Setting{
Title: payload.Title,
MetaTitle: payload.MetaTitle,
MetaDescription: payload.MetaDescription,
Phone: payload.Phone,
URL: payload.URL,
Email: payload.Email,
Facebook: payload.Facebook,
X: payload.X,
Instagram: payload.Instagram,
Whatsapp: payload.Whatsapp,
Pinterest: payload.Pinterest,
Linkedin: payload.Linkedin,
Slogan: payload.Slogan,
Address: payload.Address,
Copyright: payload.Copyright,
MapEmbed: payload.MapEmbed,
WLogo: payload.WLogo,
BLogo: payload.BLogo,
IsActive: isActive,
}
// optional image transform params
if payload.WWidth != nil {
setting.WWidth = *payload.WWidth
}
if payload.WHeight != nil {
setting.WHeight = *payload.WHeight
}
if payload.WQuality != nil {
setting.WQuality = *payload.WQuality
}
setting.WFormat = payload.WFormat
if payload.BWidth != nil {
setting.BWidth = *payload.BWidth
}
if payload.BHeight != nil {
setting.BHeight = *payload.BHeight
}
if payload.BQuality != nil {
setting.BQuality = *payload.BQuality
}
setting.BFormat = payload.BFormat
// Handle optional logo file uploads when multipart/form-data
if strings.HasPrefix(contentType, "multipart/form-data") {
// Support file upload on field name 'w_logo' (preferred) or fallback to provided path
if file, err := c.FormFile("w_logo"); err == nil {
uploadDir := filepath.Join("uploads", "logos")
_ = os.MkdirAll(uploadDir, os.ModePerm)
ext := filepath.Ext(file.Filename)
newName := "wlogo-" + strconv.FormatInt(time.Now().UnixNano(), 10) + ext
destination := filepath.Join(uploadDir, newName)
if err := c.SaveUploadedFile(file, destination); err == nil {
setting.WLogo = "/uploads/logos/" + newName
if setting.WFormat == "" && ext != "" {
setting.WFormat = ext[1:]
}
}
}
// Support file upload on field name 'b_logo'
if file, err := c.FormFile("b_logo"); err == nil {
uploadDir := filepath.Join("uploads", "logos")
_ = os.MkdirAll(uploadDir, os.ModePerm)
ext := filepath.Ext(file.Filename)
newName := "blogo-" + strconv.FormatInt(time.Now().UnixNano(), 10) + ext
destination := filepath.Join(uploadDir, newName)
if err := c.SaveUploadedFile(file, destination); err == nil {
setting.BLogo = "/uploads/logos/" + newName
if setting.BFormat == "" && ext != "" {
setting.BFormat = ext[1:]
}
}
}
}
// Enforce single active setting rule
if setting.IsActive {
// Deactivate all other settings
if err := database.DB.Model(&models.Setting{}).Where("1 = 1").Update("is_active", false).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to deactivate other settings: " + err.Error()})
return
}
}
if err := database.DB.Create(&setting).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, gin.H{"data": setting})
}
// AdminUpdateSetting godoc
// @Summary Admin: Update a setting
// @Description Update an existing setting
// @Tags settings
// @Security BearerAuth
// @Accept multipart/form-data
// @Produce json
// @Param id path int true "Setting ID"
// @Param title formData string false "Title"
// @Param meta_title formData string false "Meta title"
// @Param meta_description formData string false "Meta description"
// @Param phone formData string false "Phone"
// @Param url formData string false "URL"
// @Param email formData string false "Email"
// @Param facebook formData string false "Facebook"
// @Param x formData string false "X"
// @Param instagram formData string false "Instagram"
// @Param whatsapp formData string false "Whatsapp"
// @Param pinterest formData string false "Pinterest"
// @Param linkedin formData string false "Linkedin"
// @Param slogan formData string false "Slogan"
// @Param address formData string false "Address"
// @Param copyright formData string false "Copyright"
// @Param map_embed formData string false "Map embed"
// @Param w_logo formData file false "White logo file upload (or provide w_logo path as string)"
// @Param b_logo formData file false "Black logo file upload (or provide b_logo path as string)"
// @Param is_active formData boolean false "Is active"
// @Param w_width formData int false "W logo width"
// @Param w_height formData int false "W logo height"
// @Param w_quality formData int false "W logo quality"
// @Param w_format formData string false "W logo format"
// @Param b_width formData int false "B logo width"
// @Param b_height formData int false "B logo height"
// @Param b_quality formData int false "B logo quality"
// @Param b_format formData string false "B logo format"
// @Success 200 {object} controllers.SettingResponse
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/v1/admin/settings/{id} [put]
func AdminUpdateSetting(c *gin.Context) {
if database.DB == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database not configured"})
return
}
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 s models.Setting
if err := database.DB.First(&s, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "setting not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
contentType := c.GetHeader("Content-Type")
if strings.HasPrefix(contentType, "multipart/form-data") {
// read form fields and update if present
if v := c.PostForm("title"); v != "" {
s.Title = v
}
if v := c.PostForm("meta_title"); v != "" {
s.MetaTitle = v
}
if v := c.PostForm("meta_description"); v != "" {
s.MetaDescription = v
}
if v := c.PostForm("phone"); v != "" {
s.Phone = v
}
if v := c.PostForm("url"); v != "" {
s.URL = v
}
if v := c.PostForm("email"); v != "" {
s.Email = v
}
if v := c.PostForm("facebook"); v != "" {
s.Facebook = v
}
if v := c.PostForm("x"); v != "" {
s.X = v
}
if v := c.PostForm("instagram"); v != "" {
s.Instagram = v
}
if v := c.PostForm("whatsapp"); v != "" {
s.Whatsapp = v
}
if v := c.PostForm("pinterest"); v != "" {
s.Pinterest = v
}
if v := c.PostForm("linkedin"); v != "" {
s.Linkedin = v
}
if v := c.PostForm("slogan"); v != "" {
s.Slogan = v
}
if v := c.PostForm("address"); v != "" {
s.Address = v
}
if v := c.PostForm("copyright"); v != "" {
s.Copyright = v
}
if v := c.PostForm("map_embed"); v != "" {
s.MapEmbed = v
}
if v := c.PostForm("w_logo"); v != "" {
s.WLogo = v
}
if v := c.PostForm("b_logo"); v != "" {
s.BLogo = v
}
if v := c.PostForm("is_active"); v != "" {
if b, err := strconv.ParseBool(v); err == nil {
s.IsActive = b
}
}
if v := c.PostForm("w_width"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
s.WWidth = n
}
}
if v := c.PostForm("w_height"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
s.WHeight = n
}
}
if v := c.PostForm("w_quality"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
s.WQuality = n
}
}
if v := c.PostForm("w_format"); v != "" {
s.WFormat = v
}
if v := c.PostForm("b_width"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
s.BWidth = n
}
}
if v := c.PostForm("b_height"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
s.BHeight = n
}
}
if v := c.PostForm("b_quality"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
s.BQuality = n
}
}
if v := c.PostForm("b_format"); v != "" {
s.BFormat = v
}
// Handle optional file uploads
if file, err := c.FormFile("w_logo"); err == nil {
uploadDir := filepath.Join("uploads", "logos")
_ = os.MkdirAll(uploadDir, os.ModePerm)
ext := filepath.Ext(file.Filename)
newName := "wlogo-" + strconv.FormatInt(time.Now().UnixNano(), 10) + ext
destination := filepath.Join(uploadDir, newName)
if err := c.SaveUploadedFile(file, destination); err == nil {
s.WLogo = "/uploads/logos/" + newName
if s.WFormat == "" && ext != "" {
s.WFormat = ext[1:]
}
}
}
if file, err := c.FormFile("b_logo"); err == nil {
uploadDir := filepath.Join("uploads", "logos")
_ = os.MkdirAll(uploadDir, os.ModePerm)
ext := filepath.Ext(file.Filename)
newName := "blogo-" + strconv.FormatInt(time.Now().UnixNano(), 10) + ext
destination := filepath.Join(uploadDir, newName)
if err := c.SaveUploadedFile(file, destination); err == nil {
s.BLogo = "/uploads/logos/" + newName
if s.BFormat == "" && ext != "" {
s.BFormat = ext[1:]
}
}
}
} else {
// JSON payload
var payload SettingPayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// update fields from payload
s.Title = payload.Title
s.MetaTitle = payload.MetaTitle
s.MetaDescription = payload.MetaDescription
s.Phone = payload.Phone
s.URL = payload.URL
s.Email = payload.Email
s.Facebook = payload.Facebook
s.X = payload.X
s.Instagram = payload.Instagram
s.Whatsapp = payload.Whatsapp
s.Pinterest = payload.Pinterest
s.Linkedin = payload.Linkedin
s.Slogan = payload.Slogan
s.Address = payload.Address
s.Copyright = payload.Copyright
s.MapEmbed = payload.MapEmbed
s.WLogo = payload.WLogo
s.BLogo = payload.BLogo
if payload.IsActive != nil {
s.IsActive = *payload.IsActive
}
if payload.WWidth != nil {
s.WWidth = *payload.WWidth
}
if payload.WHeight != nil {
s.WHeight = *payload.WHeight
}
if payload.WQuality != nil {
s.WQuality = *payload.WQuality
}
s.WFormat = payload.WFormat
if payload.BWidth != nil {
s.BWidth = *payload.BWidth
}
if payload.BHeight != nil {
s.BHeight = *payload.BHeight
}
if payload.BQuality != nil {
s.BQuality = *payload.BQuality
}
s.BFormat = payload.BFormat
}
// Enforce single active setting rule
if s.IsActive {
// Deactivate all other settings except this one
if err := database.DB.Model(&models.Setting{}).Where("id != ?", s.ID).Update("is_active", false).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to deactivate other settings: " + err.Error()})
return
}
}
if err := database.DB.Save(&s).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"data": s})
}
// AdminDeleteSetting godoc
// @Summary Admin: Delete a setting
// @Description Soft-delete a setting by ID
// @Tags settings
// @Security BearerAuth
// @Produce json
// @Param id path int true "Setting ID"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/v1/admin/settings/{id} [delete]
func AdminDeleteSetting(c *gin.Context) {
if database.DB == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database not configured"})
return
}
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 s models.Setting
if err := database.DB.First(&s, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "setting not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := database.DB.Delete(&s).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// attempt to remove logo files if present (safe: only under uploads/)
for _, p := range []string{s.WLogo, s.BLogo} {
if p == "" {
continue
}
imgPath := strings.TrimPrefix(p, "/")
clean := filepath.Clean(imgPath)
if strings.HasPrefix(clean, "uploads"+string(os.PathSeparator)) {
_ = os.Remove(clean)
}
}
c.JSON(http.StatusOK, gin.H{"message": "setting deleted successfully", "id": s.ID})
}
// AdminRestoreSetting godoc
// @Summary Admin: Restore a soft-deleted setting
// @Description Restore a soft-deleted setting by ID
// @Tags settings
// @Security BearerAuth
// @Produce json
// @Param id path int true "Setting ID"
// @Success 200 {object} controllers.SettingResponse
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/v1/admin/settings/{id}/restore [post]
func AdminRestoreSetting(c *gin.Context) {
if database.DB == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database not configured"})
return
}
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 s models.Setting
// Find soft-deleted record using Unscoped
if err := database.DB.Unscoped().Where("id = ?", id).First(&s).Error; err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "setting not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// If DeletedAt is zero, record is not soft-deleted
if s.DeletedAt.Time.IsZero() {
c.JSON(http.StatusBadRequest, gin.H{"error": "setting is not deleted"})
return
}
// Clear deleted_at (restore) using Unscoped Model to allow update on soft-deleted rows
res := database.DB.Unscoped().Model(&models.Setting{}).Where("id = ?", id).UpdateColumn("deleted_at", nil)
if res.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": res.Error.Error()})
return
}
if res.RowsAffected == 0 {
c.JSON(http.StatusInternalServerError, gin.H{"error": "restore failed (no rows affected)"})
return
}
// Reload the record in normal scope to ensure DeletedAt is nil in struct
if err := database.DB.First(&s, id).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Enforce single active setting rule if restored setting is active
if s.IsActive {
if err := database.DB.Model(&models.Setting{}).Where("id != ?", s.ID).Update("is_active", false).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to deactivate other settings: " + err.Error()})
return
}
}
c.JSON(http.StatusOK, gin.H{"data": s})
}
// GetSettings godoc
// @Summary Public: Get site settings
// @Description Return the active site setting (latest active). If none active, return latest setting.
// @Tags settings
// @Produce json
// @Success 200 {object} controllers.SettingResponse
// @Failure 500 {object} map[string]string
// @Router /api/v1/settings [get]
func GetSettings(c *gin.Context) {
if database.DB == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database not configured"})
return
}
var s models.Setting
// Try to find active setting
if err := database.DB.Where("is_active = ?", true).Order("updated_at desc").First(&s).Error; err != nil {
// if not found, fallback to latest
if err == gorm.ErrRecordNotFound {
if err2 := database.DB.Order("updated_at desc").First(&s).Error; err2 != nil {
if err2 == gorm.ErrRecordNotFound {
c.JSON(http.StatusOK, gin.H{"data": nil})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err2.Error()})
return
}
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
}
c.JSON(http.StatusOK, gin.H{"data": s})
}