What's the recommended interaction between uppy and tusd?

Is there a recommended way to use uppy and tusd with an app?

I’m building a crud app which has to accept uploads, to be associated with something like support tickets.

I want to use uppy on the frontend and tusd on the backend, and I’m thinking that when the upload completes, I will use tusd’s --hooks-http option to inform my app of a successful upload.

When uppy is notified of a successful upload, it will then submit the upload information to the app, and then the app will link the successful upload with the rest of the support ticket.

Is that the recommended way to use uppy+tusd with an app?

Thanks!

1 Like

There’s no one way that we recommend doing these integrations, it’s really the use case that should dictate how the components work together. I think if you’re going to make the client send information after things have already been uploaded, maybe you want to consider using the Form plugin, so that the location to the tusd files is can be part of your form, and your backend only has a single upload to process? Then you don’t even need tusd hooks, maybe?

I see, thanks!

My understanding of the Form plugin is that when the user clicks submit, Uppy will first upload the files, and when uppy.on('complete'), the frontend will then submit the form with additional information that tusd reported.

Using that would be implicitly trusting the frontend to pass on the information correctly. This should work in most cases, but not if there is malicious intent, like if someone tries to mess up the app’s view of what was uploaded to tusd.

My frontend is currently sending json to the backend, so I guess that’s another reason why using forms is hard, haha.

Thanks for your tip though!

How are you going to associate the tusd upload with the ticket though, with metadata that the client sends along with the tus payload i presume? So the trust issue wouldn’t be any different in that case. And as long as this is a system that requires a login, you could always use that session to ensure only data of the logged in user can be touched? And so worst case, they ruin their own ticket?

Regarding only accepting JSON, I guess that’s an issue with the Form Plugin yes :slight_smile: The same flow could however also be implemented without the Form Plugin?

I was thinking that since the app has a copy of the id that tusd generated, if the frontend passes on that same id, then the app can use that id to link the other information to the upload.

I would probably try not to expose this id to the frontend once the upload is complete.

Using session is a good idea too! Thanks!

One loophole I can think of if the user intentionally abuses an old id from a previous upload. In that case, I might take these ids as single use identifiers.

These ids are unique, right? How are they generated? I’ll have a look at tusd’s source too, maybe later.

Is the browser uploading to tusd directly? If so, how would the backend app have a copy of the id that tusd generated if not passed by the client? (honest question).

They are unique, yes, i would assume UUID, but we could ask @marius to be sure :slight_smile:

Exactly, tusd generates these IDs pseudo-randomly. The corresponding code can be found at https://github.com/tus/tusd/blob/master/uid/uid.go.

Thanks @marius !

The browser can upload to tusd directly, and then I’d use the post-finish hook to pass the id to the app directly, on the backend. This should be trusted since it can be set up such that there’s trust between tusd and the app, say using pre-shared keys or something.

That makes sense!, I guess the same concept could apply between the client and your backend, and security-wise there would be no difference at what place you are tagging the upload with the ticket. The origin is still going to be the client. And having all the information in a single payload is going to make it easier to catch errors, bubble them up in a sensible way, offer retries.

That being said, do feel free to go with any solution of preference. That’s the nice thing about having individual open components!