ftp

package
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 28, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var CpCmd = &cobra.Command{
	Use:   "cp [SOURCE...] [DESTINATION]",
	Short: "Copy files between local and remote locations",
	Long: `Copy files between your local machine and a remote server.
Supports SSH-like user@host:path syntax for specifying the username inline with the remote path.
Remote paths use the format [USER@]SERVER:/path.`,
	Example: `  # Upload files to a remote server
  alpacon cp /local/file1.txt /local/file2.txt my-server:/remote/path/

  # Upload or download a directory
  alpacon cp -r /local/directory my-server:/remote/path/
  alpacon cp -r my-server:/remote/directory /local/path/

  # Download a file from a remote server
  alpacon cp my-server:/remote/file.txt /local/path/

  # Specify username with SSH-like syntax
  alpacon cp /local/file.txt admin@my-server:/remote/path/
  alpacon cp -r admin@my-server:/var/log/ /local/logs/

  # Specify username with flag
  alpacon cp -u admin /local/file.txt my-server:/remote/path/

  # Specify groupname
  alpacon cp -g developers /local/file.txt my-server:/remote/path/`,
	Run: func(cmd *cobra.Command, args []string) {
		username, _ := cmd.Flags().GetString("username")
		groupname, _ := cmd.Flags().GetString("groupname")
		recursive, _ := cmd.Flags().GetBool("recursive")

		if len(args) < 2 {
			utils.CliErrorWithExit("You must specify at least two arguments.\n\n" +
				"Usage examples:\n" +
				"  • Upload file to server:\n" +
				"    alpacon cp /local/file.txt server:/remote/path/\n" +
				"  • Download file from server:\n" +
				"    alpacon cp server:/remote/file.txt /local/path/\n" +
				"  • Upload folder (recursive):\n" +
				"    alpacon cp -r /local/folder server:/remote/path/\n\n" +
				"Note: Remote paths must include server name (e.g., myserver:/path/)")
			return
		}

		for i, arg := range args {
			if strings.Contains(arg, "@") && strings.Contains(arg, ":") {

				sshTarget := utils.ParseSSHTarget(arg)
				if username == "" && sshTarget.User != "" {
					username = sshTarget.User
				}

				if sshTarget.Path != "" {
					args[i] = sshTarget.Host + ":" + sshTarget.Path
				} else {
					args[i] = sshTarget.Host
				}
			}
		}

		sources := args[:len(args)-1]
		dest := args[len(args)-1]

		if err := validatePaths(sources, dest); err != nil {
			utils.CliErrorWithExit("%s", err.Error())
			return
		}

		alpaconClient, err := client.NewAlpaconAPIClient()
		if err != nil {
			utils.CliErrorWithExit("Connection to Alpacon API failed: %s.\n\n"+
				"Try these solutions:\n"+
				"  • Re-login with 'alpacon login'\n"+
				"  • Check your internet connection\n"+
				"  • Verify the API endpoint is accessible", err)
			return
		}

		if isLocalPaths(sources) && isRemotePath(dest) {
			serverName, _ := utils.SplitPath(dest)
			err := uploadObject(alpaconClient, sources, dest, username, groupname, recursive)
			if err != nil {
				err = utils.HandleCommonErrors(err, serverName, utils.ErrorHandlerCallbacks{
					OnMFARequired: func(srv string) error {
						return mfa.HandleMFAError(alpaconClient, srv)
					},
					OnUsernameRequired: func() error {
						_, err := iam.HandleUsernameRequired()
						return err
					},
					CheckMFACompleted: func() (bool, error) {
						return mfa.CheckMFACompletion(alpaconClient)
					},
					RefreshToken: alpaconClient.RefreshToken,
					RetryOperation: func() error {
						return uploadObject(alpaconClient, sources, dest, username, groupname, recursive)
					},
				})

				if err != nil {
					utils.CliErrorWithExit("Failed to upload to '%s': %s", dest, err)
					return
				}
			}
			wrappedSrc := fmt.Sprintf("[%s]", strings.Join(sources, ", "))
			utils.CliSuccess("Uploaded %s to %s", wrappedSrc, dest)
		} else if isRemotePath(sources[0]) && isLocalPath(dest) {
			serverName, _ := utils.SplitPath(sources[0])
			err := downloadObject(alpaconClient, sources[0], dest, username, groupname, recursive)
			if err != nil {
				err = utils.HandleCommonErrors(err, serverName, utils.ErrorHandlerCallbacks{
					OnMFARequired: func(srv string) error {
						return mfa.HandleMFAError(alpaconClient, srv)
					},
					OnUsernameRequired: func() error {
						_, err := iam.HandleUsernameRequired()
						return err
					},
					CheckMFACompleted: func() (bool, error) {
						return mfa.CheckMFACompletion(alpaconClient)
					},
					RefreshToken: alpaconClient.RefreshToken,
					RetryOperation: func() error {
						return downloadObject(alpaconClient, sources[0], dest, username, groupname, recursive)
					},
				})

				if err != nil {
					utils.CliErrorWithExit("Failed to download from '%s': %s", sources[0], err)
					return
				}
			}
			utils.CliSuccess("Downloaded %s to %s", sources[0], dest)
		} else {
			utils.CliErrorWithExit("Invalid combination of source and destination paths.\n\n" +
				"Valid operations:\n" +
				"  • Upload (local → remote): alpacon cp /local/file server:/remote/path/\n" +
				"  • Download (remote → local): alpacon cp server:/remote/file /local/path/\n\n" +
				"Note: Remote paths must be in format 'servername:/path' (e.g., myserver:/tmp/file.txt)")
		}
	},
}

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL