package drive import ( "bytes" "context" "encoding/json" "errors" "io" "github.com/ultisuite/ulti-backend/internal/nextcloud" ) func shouldSyncUltidocSidecar(path string) bool { return !nextcloud.IsUltidocSidecarPath(path) } func sidecarPathsForSourceMove(sourcePath, destinationPath string) (sidecarSource, sidecarDestination string, ok bool) { if !shouldSyncUltidocSidecar(sourcePath) { return "", "", false } return nextcloud.SidecarPathForSource(sourcePath), nextcloud.SidecarPathForSource(destinationPath), true } func (s *Service) sidecarExists(ctx context.Context, userID, path string) bool { _, err := s.nc.FileRevision(ctx, userID, path) return err == nil } func (s *Service) syncUltidocSidecarCopy( ctx context.Context, userID, sourcePath, destinationPath string, ) error { sidecarSource, sidecarDestination, ok := sidecarPathsForSourceMove(sourcePath, destinationPath) if !ok || !s.sidecarExists(ctx, userID, sidecarSource) { return nil } if err := mapDriveError(s.nc.Copy(ctx, userID, sidecarSource, sidecarDestination)); err != nil { return err } return s.patchUltidocSidecarSourcePath(ctx, userID, sidecarDestination, destinationPath) } func (s *Service) patchUltidocSidecarSourcePath( ctx context.Context, userID, sidecarPath, newSourcePath string, ) error { body, _, err := s.nc.Download(ctx, userID, sidecarPath) if err != nil { return mapDriveError(err) } defer body.Close() raw, err := io.ReadAll(body) if err != nil { return err } var doc map[string]any if err := json.Unmarshal(raw, &doc); err != nil { return err } source, _ := doc["source"].(map[string]any) if source == nil { source = map[string]any{} doc["source"] = source } source["path"] = newSourcePath encoded, err := json.Marshal(doc) if err != nil { return err } if err := mapDriveError(s.nc.Upload(ctx, userID, sidecarPath, bytes.NewReader(encoded), "application/json")); err != nil { return err } return nil } func (s *Service) moveWithUltidocSidecar(ctx context.Context, userID, source, destination string) error { sidecarSource, sidecarDestination, ok := sidecarPathsForSourceMove(source, destination) hasSidecar := ok && s.sidecarExists(ctx, userID, sidecarSource) if hasSidecar { if err := mapDriveError(s.nc.Move(ctx, userID, sidecarSource, sidecarDestination)); err != nil { return err } } if err := mapDriveError(s.nc.Move(ctx, userID, source, destination)); err != nil { if hasSidecar { _ = s.nc.Move(ctx, userID, sidecarDestination, sidecarSource) } return err } if hasSidecar { if err := s.patchUltidocSidecarSourcePath(ctx, userID, sidecarDestination, destination); err != nil { return errors.Join(err, mapDriveError(s.nc.Move(ctx, userID, destination, source))) } } return nil }