fine-uploader icon indicating copy to clipboard operation
fine-uploader copied to clipboard

Allow endpoints to support promises rather than urls

Open joeenzminger opened this issue 9 years ago • 13 comments

Struggling to implement CSRF on calls from the uploader in S3 mode. Even using a customHeader function for the signature and uploadSuccess endpoints, I see occasionally failures that I haven't been able to trace down that I think happen when our server rejects signature requests due to CSRF mismatches.

Add support for these endpoints to be functions that return promises (similar to how objectProperties.key currently functions). This would let us handle communication between the client and our server rather than delegating it to fineuploader.

joeenzminger avatar Nov 06 '14 22:11 joeenzminger

I'm a bit unclear on your requirements. What, specifically, are you doing now that isn't working consistently? Illustration w/ code would be best.

rnicholus avatar Nov 06 '14 22:11 rnicholus

I have figured out what was causing the CSRF mismatches - the cookie library I was using was urldecoding the CSRF cookie on the client. All appears to be working as expected in your library.

So my reason for needing this feature is kaput. However, I still think it would be a good improvement to the library. It would be nice to be in full control of all interactions with the server.

Here is a snippet of my current implementation:

$element.find('.uploader').fineUploaderS3({
    uploaderType: 'basic',
    request: {
        endpoint: $attrs.endpoint,
        accessKey: $attrs.accesskey
    },
    signature: {
        endpoint: $attrs.signature ? $attrs.signature : '/upload/signature',
        customHeaders: function () {
            return {
                RootCSRF: $.cookie('RootCSRF')
            };
        }
    },
    uploadSuccess: {
        endpoint: $attrs.success ? $attrs.success : '/upload/UploadS3',
        customHeaders: function () {
            return {
                RootCSRF: $.cookie('RootCSRF')
            };
        }
    },
    iframeSupport: {
        localBlankPagePath: $attrs.blankPagePath ? $attrs.blankPagePath : '/upload/blank'
    },
    objectProperties: {
        key: function (fileId) {
            var p = new qq.Promise();
            $scope.$apply(function () {
                adminAPI.GetKey()
                                    .then(function (result) {
                                        p.success(result.Result);
                                    }, function (err) {
                                        p.failure();
                                    });
            });
            return p;
        }
    },
    validation: {
        allowedExtensions: $attrs.extension ? $attrs.extension : [],
        acceptFiles: $attrs.accept ? $attrs.accept : [],
        itemLimit: $attrs.itemLimit ? ParseInt($attrs.itemLimit) : 1
    },
    retry: {
    },
    chucking: {
        enabled: typeof $attrs.chunk === undefined ? true : $attrs.chunk,
        concurrent: {
            enabled: true
        }
    },
    resume: {
        enabled: typeof $attrs.resume === undefined ? true : $attrs.resume
    },
    camera: {
        ios: typeof $attrs.camera === undefined ? true : $attrs.camera
    },
    //template: qq.supportedFeatures.fileDrop ? 'qq-template-with-drop' : 'qq-template',
    extraButtons: extraButtons
});

and what I would like to be able to do in the future

$element.find('.uploader').fineUploaderS3({
    uploaderType: 'basic',
    request: {
        endpoint: $attrs.endpoint,
        accessKey: $attrs.accesskey
    },
    signature: {
        endpoint: function (tosign, asheader) {
            //return thenable object
            return adminAPI.Signature(tosign, asheader || false);
        }
    },
    uploadSuccess: {
        endpoint: function (key, uuid, name, bucket) {
            //return thenable object
            return adminAPI.UploadSuccess(key, uuid, name, bucket);
        }
    },
    iframeSupport: {
        localBlankPagePath: $attrs.blankPagePath ? $attrs.blankPagePath : '/upload/blank'
    },
    objectProperties: {
        key: function (fileId) {
            var p = new qq.Promise();
            $scope.$apply(function () {
                adminAPI.GetKey()
                                    .then(function (result) {
                                        p.success(result.Result);
                                    }, function (err) {
                                        p.failure();
                                    });
            });
            return p;
        }
    },
    validation: {
        allowedExtensions: $attrs.extension ? $attrs.extension : [],
        acceptFiles: $attrs.accept ? $attrs.accept : [],
        itemLimit: $attrs.itemLimit ? ParseInt($attrs.itemLimit) : 1
    },
    retry: {
    },
    chucking: {
        enabled: typeof $attrs.chunk === undefined ? true : $attrs.chunk,
        concurrent: {
            enabled: true
        }
    },
    resume: {
        enabled: typeof $attrs.resume === undefined ? true : $attrs.resume
    },
    camera: {
        ios: typeof $attrs.camera === undefined ? true : $attrs.camera
    },
    //template: qq.supportedFeatures.fileDrop ? 'qq-template-with-drop' : 'qq-template',
    extraButtons: extraButtons
});

joeenzminger avatar Nov 07 '14 00:11 joeenzminger

Looks like you've declared a chucking configuration option. If this is an exact copy of your production setup, you'll want to correct that to be chunking instead.

I can see other benefits to allowing for an endpoint to be determined on-demand asynchronously as you have illustrated. So, I'll keep this open for future consideration.

rnicholus avatar Nov 07 '14 03:11 rnicholus

Hah. Thanks for catching that. Pre production :)

joeenzminger avatar Nov 07 '14 03:11 joeenzminger

Ray - just bumping this to see if you guys are still considering making the endpoint properties accept functions that return promises rather than just strings (so we can implement our own xhr, etc rather than having fineuploader do it internally).

joeenzminger avatar Mar 27 '15 18:03 joeenzminger

I would expect the promise to return a URL to the endpoint. Is this what you are suggesting?

rnicholus avatar Mar 27 '15 18:03 rnicholus

No. The promise would resolve to the data that you'd expect to currently be returned from the endpoint. This is a somewhat contrived example, but something along these lines should work:

uploadSuccess: {
        endpoint: function (key, uuid, name, bucket) {
           var p = new qq.Promise();
           setTimeout(function() {
              p.success({});
           }, 1000);
           return p;
        }
    }

This has the advantage of allowing us to have a bit more flexibility into how our API is structured - for instance (and again contrived), if we wanted to use a Websocket in the above example rather than xhr we could do it.

I'm not talking about endpoints that talk to services outside our control (like the S3 endpoints - obviously you have to manage that internal to the uploader). I'm talking about the endpoints you specify that are integration points with our application (uploadSuccess, getKey, signature, etc.). Promises would give us more flexibility in implementation.

joeenzminger avatar Mar 27 '15 19:03 joeenzminger

Ah I see. I think I misunderstood the intent of this case, looking back at my earlier comments.

I can see implementing this for everything other than upload requests (for all endpoint types, not just S3). There would need to be some agreement as to what the resolved promise would "return". Should it return an XMLHttpRequest object, or some other object with conventional properties? The former seems to be the simplest choice, but I suppose this makes the solution less flexible for integrators.

This seems interesting, but it probably isn't going to happen any time soon. There are several non-trivial higher-priority enhancements ahead of this in the queue, such as support for S3 v4 signatures (#1336), and substantial updates to the documentation. I'd also want to hear a few other users express support for this.

rnicholus avatar Mar 27 '15 19:03 rnicholus

I'll start the lobbying process :). Not urgent for us, either - just something I thought might be a good idea.

joeenzminger avatar Mar 27 '15 19:03 joeenzminger

+1

ajeeshpu avatar Apr 28 '15 01:04 ajeeshpu

+1

quisse avatar Dec 01 '15 17:12 quisse

I don't see this making it into 5.5, as that is currently intended to be a small, focused release. 6.0 is also quite full at the moment (overfull even). Still I can see that it is a desired feature, and I'll keep it in my periphery when planning releases.

rnicholus avatar Dec 14 '15 00:12 rnicholus

+1

In my scenario I'd like to control how the success request is made, rather than conforming to what fileUploader wants to do.

sedouard avatar Mar 03 '16 08:03 sedouard