Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement image hosting directly inside Lemmy #371

Closed
Nutomic opened this issue Dec 20, 2019 · 14 comments
Closed

Implement image hosting directly inside Lemmy #371

Nutomic opened this issue Dec 20, 2019 · 14 comments
Labels
enhancement New feature or request type: discussion General talk without any clear action

Comments

@Nutomic
Copy link
Member

Nutomic commented Dec 20, 2019

Right now we are using a separate software to handle the image hosting. That has some disadvantages, for example anyone can directly open the pictshare site in their browser, and upload images without any authentication.

Implementing this in Rust instead would reduce the number of required Docker containers, and improve the performance compared to php.

It doesn't seem very complicated to implement, so I will probably give it a try.

Edit: Pictshare Features (which we use in Lemmy):

  • upload image (as http post with multipart/form-data)
  • store image to filesystem
  • have unique id for each image (like m3jbhp.jpg)
  • serve thumbnails under /192/m3jbhp.png (i see 192 and 96)
  • serve original image under /pictshare/m3jbhp.png
  • usa sha1 to detect duplicate files
  • remove exif data
  • convert images to .webp (not working in pictshare)
@dessalines
Copy link
Member

I'd prefer to use pictshare ( or any similar self-hosting service ), mainly because its a tool specifically made for image hosting, so it already does things like check for dupes, does thumbnails ( which we could do at some point if we check for pictshare in the domain), gif to mp4 conversion, etc.

I'm a big believer in using tools that are really good at what they do, and for something as general-purpose as image-hosting, its better to not duplicate that work across many projects.

If you wanted to make a general image hosting service in rust, I'd be up for using it with lemmy.

There also might be a way around the account issue, but even if there isn't, it doesn't bother me too much. We could always just hide the pictshare index.html and only have the api available too.

@dessalines dessalines added the type: discussion General talk without any clear action label Dec 22, 2019
@Nutomic
Copy link
Member Author

Nutomic commented Dec 22, 2019

Can we at least route pictshare requests through Lemmy, so that we can do proper access control? Right now everyone can upload images without any authentication.

@dessalines
Copy link
Member

Sure, first we need to make sure that pictshare has some kind of authentication / protected mode, then we can have that on the back end.

@Nutomic
Copy link
Member Author

Nutomic commented Dec 23, 2019

I meant that image requests would be routed through Lemmy, so that pictshare isn't even exposed to the internet. But I guess some auth header or parameter would also work.

@dessalines
Copy link
Member

Oh I gotcha. Yeah this is probably possible.

@Nutomic
Copy link
Member Author

Nutomic commented Feb 5, 2020

@bytesnake
Copy link
Contributor

I'm a big believer in using tools that are really good at what they do, and for something as general-purpose as image-hosting, its better to not duplicate that work across many projects.

There is already a Rust library, which supports all you usecases here https://docs.rs/image/0.23.4/image/ It supports all fileformats (except webp encoding), resizing and various filters. You could just add another HTTP endpoint for images and call in the library. And then remove the dependency to PHP completely :)

@Nutomic
Copy link
Member Author

Nutomic commented Jun 6, 2020

@bytesnake Yes thats my plan now, thanks for the link!

@Nutomic
Copy link
Member Author

Nutomic commented Jun 6, 2020

@asonix
Copy link
Collaborator

asonix commented Jun 7, 2020

related: I'm working on a standalone replacement for pictshare: https://git.asonix.dog/asonix/pict-rs

@dessalines
Copy link
Member

@asonix Didn't you have code for actix routing to pict-rs? We should add this to lemmy, and not have it handled by nginx.

@asonix
Copy link
Collaborator

asonix commented Jul 22, 2020

Yeah, here are the important bits:

        let client = Client::build()
            .header("User-Agent", "pict-rs-frontend, v0.1.0")
            .timeout(Duration::from_secs(30))
            .finish();

        App::new()
            .data(client)
            .service(web::resource("/image/{filename}").route(web::get().to(full_res)))

...

async fn full_res(
    filename: web::Path<String>,
    req: HttpRequest,
    client: web::Data<Client>,
) -> Result<HttpResponse, Error> {
    let url = CONFIG.upstream_image_url(&filename.into_inner());

    image(url, req, client).await
}

async fn image(
    url: String,
    req: HttpRequest,
    client: web::Data<Client>,
) -> Result<HttpResponse, Error> {
    let res = client
        .request_from(url, req.head())
        .if_some(req.head().peer_addr, |addr, req| {
            req.header("X-Forwarded-For", addr.to_string())
        })
        .no_decompress()
        .send()
        .await?;

    if res.status() == StatusCode::NOT_FOUND {
        return Ok(to_404());
    }

    let mut client_res = HttpResponse::build(res.status());

    for (name, value) in res.headers().iter().filter(|(h, _)| *h != "connection") {
        client_res.header(name.clone(), value.clone());
    }

    Ok(client_res.body(BodyStream::new(res)))
}

@asonix
Copy link
Collaborator

asonix commented Jul 22, 2020

oh and here's the upload code:

async fn upload(
    req: HttpRequest,
    body: web::Payload,
    client: web::Data<Client>,
) -> Result<HttpResponse, Error> {
    let mut res = client
        .request_from(CONFIG.upstream_upload_url(), req.head())
        .if_some(req.head().peer_addr, |addr, req| {
            req.header("X-Forwarded-For", addr.to_string())
        })
        .send_stream(body)
        .await?;

    let images = res.json::<Images>().await?;

    // respond to the upload
    ...
}

@Nutomic
Copy link
Member Author

Nutomic commented Aug 4, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request type: discussion General talk without any clear action
Projects
None yet
Development

No branches or pull requests

4 participants