Clone переносил все привязки photo_objects, включая роли edit*, хотя правка принадлежит фото, чьи правки её породили. Теперь копия делит только обычные варианты и показывается в исходном виде. - EditRoles() и IsEditRole() в edit_render.go — единый список ролей; - фильтр edit-ролей в Store.Clone и SQLiteStore.Clone; - миграции 000016 (Postgres) и 000008 (SQLite) убирают привязки у копий, созданных до фикса; - контрактная проверка копирования, сервисные и e2e-тесты.
115 lines
3.4 KiB
Go
115 lines
3.4 KiB
Go
package photos
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
|
|
"galo/internal/httpx"
|
|
"galo/internal/models"
|
|
"galo/internal/roles"
|
|
)
|
|
|
|
// editablePhoto loads a photo and checks that the user may change its
|
|
// editing parameters: the uploader of the photo or an album owner/admin.
|
|
func (s *Service) editablePhoto(ctx context.Context, photoID, userID string) (*models.Photo, error) {
|
|
p, err := s.store.GetByID(ctx, photoID, userID)
|
|
if err != nil {
|
|
if errors.Is(err, ErrNotFound) {
|
|
return nil, httpx.NotFound("photo_not_found", "photo not found")
|
|
}
|
|
return nil, err
|
|
}
|
|
if p.DeletedAt != nil {
|
|
return nil, httpx.NotFound("photo_not_found", "photo not found")
|
|
}
|
|
_, role, err := s.albums.Access(ctx, p.AlbumID, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
own := p.UploaderID == userID
|
|
if !(roles.CanDeleteAnyPhoto(role) || (own && roles.CanDeleteOwnPhoto(role))) {
|
|
return nil, httpx.Forbidden("forbidden", "you cannot edit this photo")
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// EditPhoto stores new editing parameters for a photo and schedules the
|
|
// render of the edited version. The original object is never modified, so the
|
|
// edit can always be reset.
|
|
func (s *Service) EditPhoto(ctx context.Context, photoID, userID string, params EditParams) (*models.Photo, error) {
|
|
p, err := s.editablePhoto(ctx, photoID, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if p.Kind != KindImage {
|
|
return nil, httpx.BadRequest("invalid_request", "only images can be edited")
|
|
}
|
|
params = params.Normalized()
|
|
if params.IsEmpty() {
|
|
return s.ResetEdit(ctx, photoID, userID)
|
|
}
|
|
if err := s.store.SetEditParams(ctx, photoID, params.JSON()); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.store.EnqueueEditJob(ctx, photoID); err != nil {
|
|
return nil, err
|
|
}
|
|
// Рендер начинается сразу, а не в конце цикла воркера.
|
|
if s.wakeEdit != nil {
|
|
s.wakeEdit()
|
|
}
|
|
return s.publishEdit(ctx, photoID, userID)
|
|
}
|
|
|
|
// ResetEdit removes the editing parameters of a photo together with the
|
|
// edited version, returning the photo to its original look.
|
|
func (s *Service) ResetEdit(ctx context.Context, photoID, userID string) (*models.Photo, error) {
|
|
if _, err := s.editablePhoto(ctx, photoID, userID); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.store.ClearEditParams(ctx, photoID); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.store.DeleteEditJob(ctx, photoID); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.store.DeleteObjectBindings(ctx, photoID, EditRoles()...); err != nil {
|
|
return nil, err
|
|
}
|
|
return s.publishEdit(ctx, photoID, userID)
|
|
}
|
|
|
|
// publishEdit reloads a photo with its current editing state and notifies the
|
|
// album subscribers.
|
|
func (s *Service) publishEdit(ctx context.Context, photoID, userID string) (*models.Photo, error) {
|
|
p, err := s.store.GetByID(ctx, photoID, userID)
|
|
if err != nil {
|
|
if errors.Is(err, ErrNotFound) {
|
|
return nil, httpx.NotFound("photo_not_found", "photo not found")
|
|
}
|
|
return nil, err
|
|
}
|
|
if err := s.attachURLs(ctx, p); err != nil {
|
|
return nil, err
|
|
}
|
|
s.hub.Publish(p.AlbumID, "photo.updated", p)
|
|
return p, nil
|
|
}
|
|
|
|
// EditParamsOf returns the editing parameters stored on a photo.
|
|
func EditParamsOf(p *models.Photo) EditParams {
|
|
if p == nil || len(p.EditParams) == 0 {
|
|
return EditParams{}
|
|
}
|
|
data, err := json.Marshal(p.EditParams)
|
|
if err != nil {
|
|
return EditParams{}
|
|
}
|
|
params, err := ParseEditParams(data)
|
|
if err != nil {
|
|
return EditParams{}
|
|
}
|
|
return params
|
|
}
|