- bulk create/delete/move, reorder, rename-category endpoints - /bookmarks/meta with SSRF-safe fetcher (blocks private/loopback IPs, 8s timeout, 1 MiB body cap) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
148 lines
3.5 KiB
Go
148 lines
3.5 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"evanpage-backend/internal/domain"
|
|
"evanpage-backend/internal/repository"
|
|
)
|
|
|
|
type BookmarkService struct {
|
|
repo *repository.BookmarkRepository
|
|
}
|
|
|
|
type BookmarkInput struct {
|
|
Title string
|
|
URL string
|
|
Description string
|
|
Icon string
|
|
Category string
|
|
SortOrder int
|
|
}
|
|
|
|
func NewBookmarkService(repo *repository.BookmarkRepository) *BookmarkService {
|
|
return &BookmarkService{repo: repo}
|
|
}
|
|
|
|
func (s *BookmarkService) Create(userID uint, in BookmarkInput) (*domain.Bookmark, error) {
|
|
if in.Title == "" || in.URL == "" {
|
|
return nil, errors.New("title and url are required")
|
|
}
|
|
if in.Category == "" {
|
|
in.Category = "默认"
|
|
}
|
|
bm := &domain.Bookmark{
|
|
UserID: userID,
|
|
Title: in.Title,
|
|
URL: in.URL,
|
|
Description: in.Description,
|
|
Icon: in.Icon,
|
|
Category: in.Category,
|
|
SortOrder: in.SortOrder,
|
|
}
|
|
if err := s.repo.Create(bm); err != nil {
|
|
return nil, err
|
|
}
|
|
return bm, nil
|
|
}
|
|
|
|
func (s *BookmarkService) BulkCreate(userID uint, items []BookmarkInput) (int, error) {
|
|
bms := make([]*domain.Bookmark, 0, len(items))
|
|
for _, in := range items {
|
|
if in.Title == "" || in.URL == "" {
|
|
continue
|
|
}
|
|
cat := in.Category
|
|
if cat == "" {
|
|
cat = "默认"
|
|
}
|
|
bms = append(bms, &domain.Bookmark{
|
|
UserID: userID,
|
|
Title: in.Title,
|
|
URL: in.URL,
|
|
Description: in.Description,
|
|
Icon: in.Icon,
|
|
Category: cat,
|
|
SortOrder: in.SortOrder,
|
|
})
|
|
}
|
|
if err := s.repo.BulkCreate(bms); err != nil {
|
|
return 0, err
|
|
}
|
|
return len(bms), nil
|
|
}
|
|
|
|
func (s *BookmarkService) ListByUser(userID uint) ([]domain.Bookmark, error) {
|
|
return s.repo.ListByUser(userID)
|
|
}
|
|
|
|
func (s *BookmarkService) ListByUserAndCategory(userID uint, category string) ([]domain.Bookmark, error) {
|
|
return s.repo.ListByUserAndCategory(userID, category)
|
|
}
|
|
|
|
func (s *BookmarkService) ListCategoriesByUser(userID uint) ([]string, error) {
|
|
return s.repo.ListCategoriesByUser(userID)
|
|
}
|
|
|
|
func (s *BookmarkService) Update(userID, bookmarkID uint, in BookmarkInput) (*domain.Bookmark, error) {
|
|
bm, err := s.repo.FindByID(bookmarkID)
|
|
if err != nil {
|
|
return nil, errors.New("bookmark not found")
|
|
}
|
|
if bm.UserID != userID {
|
|
return nil, errors.New("forbidden")
|
|
}
|
|
if in.Title != "" {
|
|
bm.Title = in.Title
|
|
}
|
|
if in.URL != "" {
|
|
bm.URL = in.URL
|
|
}
|
|
bm.Description = in.Description
|
|
bm.Icon = in.Icon
|
|
if in.Category != "" {
|
|
bm.Category = in.Category
|
|
}
|
|
bm.SortOrder = in.SortOrder
|
|
if err := s.repo.Update(bm); err != nil {
|
|
return nil, err
|
|
}
|
|
return bm, nil
|
|
}
|
|
|
|
func (s *BookmarkService) Delete(userID, bookmarkID uint) error {
|
|
bm, err := s.repo.FindByID(bookmarkID)
|
|
if err != nil {
|
|
return errors.New("bookmark not found")
|
|
}
|
|
if bm.UserID != userID {
|
|
return errors.New("forbidden")
|
|
}
|
|
return s.repo.Delete(bookmarkID)
|
|
}
|
|
|
|
func (s *BookmarkService) BulkDelete(userID uint, ids []uint) (int64, error) {
|
|
return s.repo.BulkDeleteByUser(userID, ids)
|
|
}
|
|
|
|
func (s *BookmarkService) BulkUpdateCategory(userID uint, ids []uint, category string) (int64, error) {
|
|
if category == "" {
|
|
category = "默认"
|
|
}
|
|
return s.repo.BulkUpdateCategoryByUser(userID, ids, category)
|
|
}
|
|
|
|
func (s *BookmarkService) RenameCategory(userID uint, from, to string) (int64, error) {
|
|
if from == "" || to == "" {
|
|
return 0, errors.New("from and to are required")
|
|
}
|
|
if from == to {
|
|
return 0, nil
|
|
}
|
|
return s.repo.RenameCategoryByUser(userID, from, to)
|
|
}
|
|
|
|
func (s *BookmarkService) Reorder(userID uint, orderedIDs []uint) error {
|
|
return s.repo.ReorderByUser(userID, orderedIDs)
|
|
}
|