hunk.code_window:                 noValidate
                 >
                   <FormControl
                     inputId="edge_enable"
                     label="Enable Edge Compute features"
                     size="medium"
                     errors={errors.EnableEdgeComputeFeatures}
                   >
                     <Switch
  
hunk.labels: keep keep keep keep keep replace keep keep keep
hunk.after_edit:                   size="small"
  
hunk.file_path: app/portainer/settings/edge-compute/EdgeComputeSettings/EdgeComputeSettings.tsx
hunk.type: replace
file: package stacks
  
  import (
  	"errors"
  	"fmt"
  	"net/http"
  
  	httperror "github.com/portainer/libhttp/error"
  	"github.com/portainer/libhttp/request"
  	"github.com/portainer/libhttp/response"
  	portainer "github.com/portainer/portainer/api"
  	httperrors "github.com/portainer/portainer/api/http/errors"
  	"github.com/portainer/portainer/api/http/security"
  	"github.com/portainer/portainer/api/internal/stackutils"
  )
  
  type stackMigratePayload struct {
  	// Environment(Endpoint) identifier of the target environment(endpoint) where the stack will be relocated
  	EndpointID int `example:"2" validate:"required"`
  	// Swarm cluster identifier, must match the identifier of the cluster where the stack will be relocated
  	SwarmID string `example:"jpofkc0i9uo9wtx1zesuk649w"`
  	// If provided will rename the migrated stack
  	Name string `example:"new-stack"`
  }
  
  func (payload *stackMigratePayload) Validate(r *http.Request) error {
  	if payload.EndpointID == 0 {
  		return errors.New("Invalid environment identifier. Must be a positive number")
  	}
  	return nil
  }
  
  // @id StackMigrate
  // @summary Migrate a stack to another environment(endpoint)
  // @description  Migrate a stack from an environment(endpoint) to another environment(endpoint). It will re-create the stack inside the target environment(endpoint) before removing the original stack.
  // @description **Access policy**: authenticated
  // @tags stacks
  // @security ApiKeyAuth
  // @security jwt
  // @produce json
  // @param id path int true "Stack identifier"
  // @param endpointId query int false "Stacks created before version 1.18.0 might not have an associated environment(endpoint) identifier. Use this optional parameter to set the environment(endpoint) identifier used by the stack."
  // @param body body stackMigratePayload true "Stack migration details"
  // @success 200 {object} portainer.Stack "Success"
  // @failure 400 "Invalid request"
  // @failure 403 "Permission denied"
  // @failure 404 "Stack not found"
  // @failure 500 "Server error"
  // @router /stacks/{id}/migrate [post]
  func (handler *Handler) stackMigrate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
  	stackID, err := request.RetrieveNumericRouteVariableValue(r, "id")
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Invalid stack identifier route variable", Err: err}
  	}
  
  	var payload stackMigratePayload
  	err = request.DecodeAndValidateJSONPayload(r, &payload)
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Invalid request payload", Err: err}
  	}
  
  	stack, err := handler.DataStore.Stack().Stack(portainer.StackID(stackID))
  	if handler.DataStore.IsErrObjectNotFound(err) {
  		return &httperror.HandlerError{StatusCode: http.StatusNotFound, Message: "Unable to find a stack with the specified identifier inside the database", Err: err}
  	} else if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to find a stack with the specified identifier inside the database", Err: err}
  	}
  
  	if stack.Type == portainer.KubernetesStack {
  		return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Migrating a kubernetes stack is not supported", Err: err}
  	}
  
  	endpoint, err := handler.DataStore.Endpoint().Endpoint(stack.EndpointID)
  	if handler.DataStore.IsErrObjectNotFound(err) {
  		return &httperror.HandlerError{StatusCode: http.StatusNotFound, Message: "Unable to find an endpoint with the specified identifier inside the database", Err: err}
  	} else if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to find an endpoint with the specified identifier inside the database", Err: err}
  	}
  
  	err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint)
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusForbidden, Message: "Permission denied to access endpoint", Err: err}
  	}
  
  	securityContext, err := security.RetrieveRestrictedRequestContext(r)
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to retrieve info from request context", Err: err}
  	}
  
  	resourceControl, err := handler.DataStore.ResourceControl().ResourceControlByResourceIDAndType(stackutils.ResourceControlID(stack.EndpointID, stack.Name), portainer.StackResourceControl)
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to retrieve a resource control associated to the stack", Err: err}
  	}
  
  	access, err := handler.userCanAccessStack(securityContext, endpoint.ID, resourceControl)
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to verify user authorizations to validate stack access", Err: err}
  	}
  	if !access {
  		return &httperror.HandlerError{StatusCode: http.StatusForbidden, Message: "Access denied to resource", Err: httperrors.ErrResourceAccessDenied}
  	}
  
  	// TODO: this is a work-around for stacks created with Portainer version >= 1.17.1
  	// The EndpointID property is not available for these stacks, this API environment(endpoint)
  	// can use the optional EndpointID query parameter to associate a valid environment(endpoint) identifier to the stack.
  	endpointID, err := request.RetrieveNumericQueryParameter(r, "endpointId", true)
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Invalid query parameter: endpointId", Err: err}
  	}
  	if endpointID != int(stack.EndpointID) {
  		stack.EndpointID = portainer.EndpointID(endpointID)
  	}
  
  	targetEndpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(payload.EndpointID))
  	if handler.DataStore.IsErrObjectNotFound(err) {
  		return &httperror.HandlerError{StatusCode: http.StatusNotFound, Message: "Unable to find an endpoint with the specified identifier inside the database", Err: err}
  	} else if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to find an endpoint with the specified identifier inside the database", Err: err}
  	}
  
  	stack.EndpointID = portainer.EndpointID(payload.EndpointID)
  	if payload.SwarmID != "" {
  		stack.SwarmID = payload.SwarmID
  	}
  
  	oldName := stack.Name
  	if payload.Name != "" {
  		stack.Name = payload.Name
  	}
  
  	isUnique, err := handler.checkUniqueStackNameInDocker(targetEndpoint, stack.Name, stack.ID, stack.SwarmID != "")
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to check for name collision", Err: err}
  	}
  
  	if !isUnique {
  		errorMessage := fmt.Sprintf("A stack with the name '%s' is already running on endpoint '%s'", stack.Name, targetEndpoint.Name)
  		return &httperror.HandlerError{StatusCode: http.StatusConflict, Message: errorMessage, Err: errors.New(errorMessage)}
  	}
  
  	migrationError := handler.migrateStack(r, stack, targetEndpoint)
  	if migrationError != nil {
  		return migrationError
  	}
  
  	newName := stack.Name
  	stack.Name = oldName
  	err = handler.deleteStack(securityContext.UserID, stack, endpoint)
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: err.Error(), Err: err}
  	}
  
  	stack.Name = newName
  	err = handler.DataStore.Stack().UpdateStack(stack.ID, stack)
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to persist the stack changes inside the database", Err: err}
  	}
  
  	if resourceControl != nil {
  		resourceControl.ResourceID = stackutils.ResourceControlID(stack.EndpointID, stack.Name)
  		err := handler.DataStore.ResourceControl().UpdateResourceControl(resourceControl.ID, resourceControl)
  		if err != nil {
  			return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the resource control changes", err}
  		}
  	}
  
  	if stack.GitConfig != nil && stack.GitConfig.Authentication != nil && stack.GitConfig.Authentication.Password != "" {
  		// sanitize password in the http response to minimise possible security leaks
  		stack.GitConfig.Authentication.Password = ""
  	}
  
  	return response.JSON(w, stack)
  }
  
  func (handler *Handler) migrateStack(r *http.Request, stack *portainer.Stack, next *portainer.Endpoint) *httperror.HandlerError {
  	if stack.Type == portainer.DockerSwarmStack {
  		return handler.migrateSwarmStack(r, stack, next)
  	}
  	return handler.migrateComposeStack(r, stack, next)
  }
  
  func (handler *Handler) migrateComposeStack(r *http.Request, stack *portainer.Stack, next *portainer.Endpoint) *httperror.HandlerError {
  	config, configErr := handler.createComposeDeployConfig(r, stack, next)
  	if configErr != nil {
  		return configErr
  	}
  
  	err := handler.deployComposeStack(config, false)
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: err.Error(), Err: err}
  	}
  
  	return nil
  }
  
  func (handler *Handler) migrateSwarmStack(r *http.Request, stack *portainer.Stack, next *portainer.Endpoint) *httperror.HandlerError {
  	config, configErr := handler.createSwarmDeployConfig(r, stack, next, true)
  	if configErr != nil {
  		return configErr
  	}
  
  	err := handler.deploySwarmStack(config)
  	if err != nil {
  		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: err.Error(), Err: err}
  	}
  
  	return nil
  }
  