Public/Storage/New-VergeNASVolumeSync.ps1

function New-VergeNASVolumeSync {
    <#
    .SYNOPSIS
        Creates a new volume sync job in VergeOS.

    .DESCRIPTION
        New-VergeNASVolumeSync creates a volume synchronization job that copies data
        between NAS volumes. Syncs can run on a schedule or be triggered manually.

    .PARAMETER NASService
        The NAS service name or object to create the sync job on.

    .PARAMETER Name
        The name for the new sync job.

    .PARAMETER SourceVolume
        The source volume name or object to sync from.

    .PARAMETER DestinationVolume
        The destination volume name or object to sync to.

    .PARAMETER SourcePath
        Starting directory in the source volume. A trailing slash copies only contents.

    .PARAMETER DestinationPath
        Destination directory path to sync to.

    .PARAMETER Description
        Optional description for the sync job.

    .PARAMETER Include
        Array of file/directory patterns to include.

    .PARAMETER Exclude
        Array of file/directory patterns to exclude.

    .PARAMETER SyncMethod
        Sync method: Rsync or VergeSync. Default is VergeSync.

    .PARAMETER DestinationDelete
        How to handle deleted files: Never, Delete, DeleteBefore, DeleteDuring, DeleteDelay, DeleteAfter.

    .PARAMETER Workers
        Number of simultaneous workers (1-128). Default is 4.

    .PARAMETER PreserveACLs
        Preserve access control lists. Default is true.

    .PARAMETER PreservePermissions
        Preserve file permissions. Default is true.

    .PARAMETER PreserveOwner
        Preserve file owner. Default is true.

    .PARAMETER PreserveGroups
        Preserve file groups. Default is true.

    .PARAMETER PreserveModTime
        Preserve modification time. Default is true.

    .PARAMETER PreserveXattrs
        Preserve extended attributes. Default is true.

    .PARAMETER CopySymlinks
        Copy symbolic links. Default is true.

    .PARAMETER FreezeFilesystem
        Freeze filesystem before snapshot. Default is false.

    .PARAMETER Server
        The VergeOS connection to use. Defaults to the current default connection.

    .EXAMPLE
        New-VergeNASVolumeSync -NASService "MyNAS" -Name "DailyBackup" -SourceVolume "Data" -DestinationVolume "Backup"

        Creates a basic sync job from Data to Backup volume.

    .EXAMPLE
        New-VergeNASVolumeSync -NASService "MyNAS" -Name "SelectiveSync" -SourceVolume "Data" -DestinationVolume "Archive" -Include @("*.docx", "*.xlsx") -Exclude @("temp/*")

        Creates a sync job with include/exclude patterns.

    .OUTPUTS
        Verge.NASVolumeSync object representing the created sync job.

    .NOTES
        Use Start-VergeNASVolumeSync to manually trigger the sync.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, Position = 0)]
        [Alias('Service')]
        [object]$NASService,

        [Parameter(Mandatory, Position = 1)]
        [ValidateLength(1, 128)]
        [string]$Name,

        [Parameter(Mandatory, Position = 2)]
        [object]$SourceVolume,

        [Parameter(Mandatory, Position = 3)]
        [object]$DestinationVolume,

        [Parameter()]
        [string]$SourcePath,

        [Parameter()]
        [string]$DestinationPath,

        [Parameter()]
        [ValidateLength(0, 2048)]
        [string]$Description,

        [Parameter()]
        [string[]]$Include,

        [Parameter()]
        [string[]]$Exclude,

        [Parameter()]
        [ValidateSet('Rsync', 'VergeSync')]
        [string]$SyncMethod = 'VergeSync',

        [Parameter()]
        [ValidateSet('Never', 'Delete', 'DeleteBefore', 'DeleteDuring', 'DeleteDelay', 'DeleteAfter')]
        [string]$DestinationDelete = 'Never',

        [Parameter()]
        [ValidateRange(1, 128)]
        [int]$Workers = 4,

        [Parameter()]
        [bool]$PreserveACLs = $true,

        [Parameter()]
        [bool]$PreservePermissions = $true,

        [Parameter()]
        [bool]$PreserveOwner = $true,

        [Parameter()]
        [bool]$PreserveGroups = $true,

        [Parameter()]
        [bool]$PreserveModTime = $true,

        [Parameter()]
        [bool]$PreserveXattrs = $true,

        [Parameter()]
        [bool]$CopySymlinks = $true,

        [Parameter()]
        [switch]$FreezeFilesystem,

        [Parameter()]
        [object]$Server
    )

    begin {
        # Resolve connection
        if (-not $Server) {
            $Server = $script:DefaultConnection
        }
        if (-not $Server) {
            throw [System.InvalidOperationException]::new(
                'Not connected to VergeOS. Use Connect-VergeOS to establish a connection.'
            )
        }

        # Map friendly names to API values
        $syncMethodMap = @{
            'Rsync'     = 'rsync'
            'VergeSync' = 'ysync'
        }

        $deleteMap = @{
            'Never'        = 'never'
            'Delete'       = 'delete'
            'DeleteBefore' = 'delete-before'
            'DeleteDuring' = 'delete-during'
            'DeleteDelay'  = 'delete-delay'
            'DeleteAfter'  = 'delete-after'
        }
    }

    process {
        try {
            # Resolve NAS service
            $serviceKey = $null
            $serviceName = $null

            if ($NASService -is [string]) {
                $serviceName = $NASService
                $serviceData = Get-VergeNASService -Name $NASService -Server $Server
                if (-not $serviceData) {
                    throw "NAS service '$NASService' not found"
                }
                $serviceKey = $serviceData.Key
                $serviceName = $serviceData.Name
            }
            elseif ($NASService.Key) {
                $serviceKey = $NASService.Key
                $serviceName = $NASService.Name
            }
            elseif ($NASService -is [int]) {
                $serviceKey = $NASService
            }

            if (-not $serviceKey) {
                throw "Could not resolve NAS service key"
            }

            # Resolve source volume
            $sourceVolumeKey = $null
            $sourceVolumeName = $null

            if ($SourceVolume -is [string]) {
                $sourceVolumeName = $SourceVolume
                $volumeData = Get-VergeNASVolume -Name $SourceVolume -Server $Server
                if (-not $volumeData) {
                    throw "Source volume '$SourceVolume' not found"
                }
                $sourceVolumeKey = $volumeData.Key
                $sourceVolumeName = $volumeData.Name
            }
            elseif ($SourceVolume.Key) {
                $sourceVolumeKey = $SourceVolume.Key
                $sourceVolumeName = $SourceVolume.Name
            }

            if (-not $sourceVolumeKey) {
                throw "Could not resolve source volume key"
            }

            # Resolve destination volume
            $destVolumeKey = $null
            $destVolumeName = $null

            if ($DestinationVolume -is [string]) {
                $destVolumeName = $DestinationVolume
                $volumeData = Get-VergeNASVolume -Name $DestinationVolume -Server $Server
                if (-not $volumeData) {
                    throw "Destination volume '$DestinationVolume' not found"
                }
                $destVolumeKey = $volumeData.Key
                $destVolumeName = $volumeData.Name
            }
            elseif ($DestinationVolume.Key) {
                $destVolumeKey = $DestinationVolume.Key
                $destVolumeName = $DestinationVolume.Name
            }

            if (-not $destVolumeKey) {
                throw "Could not resolve destination volume key"
            }

            # Build request body
            $body = @{
                service              = $serviceKey
                name                 = $Name
                type                 = 'volsync'
                source_volume        = $sourceVolumeKey
                destination_volume   = $destVolumeKey
                enabled              = $true
                sync_method          = $syncMethodMap[$SyncMethod]
                destination_delete   = $deleteMap[$DestinationDelete]
                workers              = $Workers
                preserve_ACLs        = $PreserveACLs
                preserve_permissions = $PreservePermissions
                preserve_owner       = $PreserveOwner
                preserve_groups      = $PreserveGroups
                preserve_mod_time    = $PreserveModTime
                preserve_xattrs      = $PreserveXattrs
                copy_symlinks        = $CopySymlinks
                fsfreeze             = [bool]$FreezeFilesystem
            }

            if ($SourcePath) {
                $body['source_path'] = $SourcePath
            }

            if ($DestinationPath) {
                $body['destination_path'] = $DestinationPath
            }

            if ($Description) {
                $body['description'] = $Description
            }

            if ($Include -and $Include.Count -gt 0) {
                $body['include'] = $Include -join "`n"
            }

            if ($Exclude -and $Exclude.Count -gt 0) {
                $body['exclude'] = $Exclude -join "`n"
            }

            $displaySource = $sourceVolumeName ?? $sourceVolumeKey
            $displayDest = $destVolumeName ?? $destVolumeKey

            if ($PSCmdlet.ShouldProcess("NAS '$serviceName'", "Create volume sync '$Name' ($displaySource -> $displayDest)")) {
                Write-Verbose "Creating volume sync '$Name' on NAS '$serviceName'"
                $response = Invoke-VergeAPI -Method POST -Endpoint 'volume_syncs' -Body $body -Connection $Server

                # Return the created sync
                if ($response.'$key' -or $response.id) {
                    $syncKey = $response.'$key' ?? $response.id
                    Get-VergeNASVolumeSync -Key $syncKey -Server $Server
                }
                else {
                    Get-VergeNASVolumeSync -NASService $serviceKey -Name $Name -Server $Server
                }
            }
        }
        catch {
            $displayName = $serviceName ?? $serviceKey ?? 'unknown'
            Write-Error -Message "Failed to create volume sync on NAS '$displayName': $($_.Exception.Message)" -ErrorId 'NewVolumeSyncFailed'
        }
    }
}