s3rius / rustus

TUS protocol implementation in Rust.
https://s3rius.github.io/rustus/
MIT License
150 stars 13 forks source link

Application examples? #139

Open leplatrem opened 1 year ago

leplatrem commented 1 year ago

Is rustus supposed to be exposed to the world or proxied by a "domain-specific" web service?

Did I understand well from this example:

https://github.com/s3rius/rustus/blob/320625059dd8ac362f29471952b596ad8348f911/docs/hooks.md?plain=1#L852-L855

That hooks could be used to authenticate file uploads?

I think it would be interesting as a narrative example to unroll a whole demo app that leverages rustus. Or give more context on why it was built? Or for example, answer questions like "if I want to built a WeTransfer clone, what is the intended way to integrate rustus?"

If you're willing to give me a few pointers, I'd be glad to contribute something :)

s3rius commented 1 year ago

HI! And thanks for you question. Rustus is supposed to be opened to the world. To make it more secure, you can use CORS.
If you have no hooks configured, then everyone can upload anything.

But if you have file hooks or http hooks, then on pre-create hook you can easily abort uploads by either exiting with non-zero status code (if you use file hooks) or return not 200 status code from your application (if you use http hooks) .

When upload is ready you can add post-finish hook to handle events when upload is complete.

I use rustus to upload renedered videos to one of my side-projects.

@router.post("/rustus_hooks")
async def rustus_hooks(
    hook: RustusHook,
    auth: AuthJWT = Depends(),
    hook_name: Optional[HookName] = Header(None),
) -> None:
    """
    Upload stream in database.

    :param hook: hook information.

    :param auth: service to validate authorization header.

    :param hook_name: hook name

    :raises HTTPException: if you dumb enough to simulate rustus.
    """
    auth.jwt_required()
    upload_id = hook.upload.id
    if hook_name == HookName.PRE_CREATE:
        logger.info("Stream upload started.")
        return
    elif hook_name == HookName.POST_TERMINATE:
        logger.info(f"Removing stream for upload_id={upload_id}")
        await Stream.filter(upload_id=hook.upload.id).delete()
    elif hook_name == HookName.POST_FINISH:
        logger.info(f"Stream upload_id={upload_id} successfully uploaded")
        user = await AdminTable.get(username=auth.get_jwt_subject())
        await Stream.create(
            upload_id=hook.upload.id,
            order=await Stream.all().count(),
            path=hook.upload.path,
            created_by=user,
        )
    else:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Are you drunk?",
        )

That's the whole code to handle uploads. I use rustus with these parameters:

    RUSTUS_MAX_BODY_SIZE: "1000000"
    RUSTUS_DIR_STRUCTURE: "{year}/{month}/{day}"
    RUSTUS_TUS_EXTENSIONS: "getting,creation,termination"
    RUSTUS_HOOKS_HTTP_PROXY_HEADERS: "Authorization"
    RUSTUS_HOOKS_HTTP_URLS: "http://{my_service}/rustus_hooks"
    RUSTUS_HOOKS: "pre-create,post-terminate,post-finish"

I hope it helped you.

leplatrem commented 1 year ago

That definitely helps! Thank you :)

s3rius commented 1 year ago

I'm really glad, you find this project useful.

If it works for you, please consider closing the issue.

s3rius commented 1 year ago

Or maybe before closing it would be nice to add examples section in docs.

leplatrem commented 1 year ago

Yes I think it would be useful to show a few recipes/patterns in the docs.

I'm thinking one of:

s3rius commented 1 year ago

Good set of cases. I will try to implement suggested examples and will put them in folder inside this repo.