package services import ( "errors" "fmt" "regexp" "strings" "gauth-central/internal/database" "gauth-central/internal/models" "gorm.io/gorm" ) type ServiceService struct{} func NewServiceService() *ServiceService { return &ServiceService{} } // CreateService creates a new service entry. func (s *ServiceService) CreateService(title string, content string, image string, isActive bool) (*models.Service, error) { slug := s.generateUniqueSlug(slugifyService(title), "") service := models.Service{ Title: title, Content: content, Image: image, Slug: slug, IsActive: isActive, } if err := database.DB.Create(&service).Error; err != nil { return nil, err } return s.GetServiceByID(service.ID.String()) } // GetAllServices retrieves all services. Use onlyActive to filter public data. func (s *ServiceService) GetAllServices(onlyActive bool) ([]models.Service, error) { var services []models.Service query := database.DB.Order("created_at desc") if onlyActive { query = query.Where("is_active = ?", true) } if err := query.Find(&services).Error; err != nil { return nil, err } return services, nil } // GetServiceByID retrieves a service by ID. func (s *ServiceService) GetServiceByID(id string) (*models.Service, error) { var service models.Service if err := database.DB.Where("id = ?", id).First(&service).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("service not found") } return nil, err } return &service, nil } // GetServiceBySlug retrieves a service by slug. Use onlyActive to limit public access. func (s *ServiceService) GetServiceBySlug(slug string, onlyActive bool) (*models.Service, error) { var service models.Service query := database.DB.Where("slug = ?", slug) if onlyActive { query = query.Where("is_active = ?", true) } if err := query.First(&service).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("service not found") } return nil, err } return &service, nil } // UpdateService updates an existing service entry. func (s *ServiceService) UpdateService( id string, title *string, content *string, image *string, slug *string, isActive *bool, ) (*models.Service, error) { service, err := s.GetServiceByID(id) if err != nil { return nil, err } updates := map[string]interface{}{} if title != nil { updates["title"] = *title } if content != nil { updates["content"] = *content } if image != nil { updates["image"] = *image } if slug != nil { clean := slugifyService(*slug) if clean == "" { return nil, errors.New("slug cannot be empty") } if s.slugExists(clean, id) { return nil, errors.New("slug already exists") } updates["slug"] = clean } if isActive != nil { updates["is_active"] = *isActive } if len(updates) > 0 { if err := database.DB.Model(service).Updates(updates).Error; err != nil { return nil, err } } return s.GetServiceByID(id) } // DeleteService deletes a service by ID. func (s *ServiceService) DeleteService(id string) error { result := database.DB.Delete(&models.Service{}, "id = ?", id) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.New("service not found") } return nil } func (s *ServiceService) generateUniqueSlug(baseSlug string, excludeID string) string { slug := baseSlug counter := 1 for s.slugExists(slug, excludeID) { slug = fmt.Sprintf("%s-%d", baseSlug, counter) counter++ } return slug } func (s *ServiceService) slugExists(slug string, excludeID string) bool { var count int64 query := database.DB.Model(&models.Service{}).Where("slug = ?", slug) if excludeID != "" { query = query.Where("id <> ?", excludeID) } query.Count(&count) return count > 0 } func slugifyService(input string) string { clean := strings.TrimSpace(input) if clean == "" { return "" } replacer := strings.NewReplacer( "ı", "i", "İ", "i", "ş", "s", "Ş", "s", "ğ", "g", "Ğ", "g", "ç", "c", "Ç", "c", "ö", "o", "Ö", "o", "ü", "u", "Ü", "u", ) clean = strings.ToLower(replacer.Replace(clean)) re := regexp.MustCompile(`[^a-z0-9]+`) clean = re.ReplaceAllString(clean, "-") clean = strings.Trim(clean, "-") if clean == "" { return "service" } return clean }