server: add ability to view inline image attachments
This commit is contained in:
@@ -16,7 +16,7 @@ use rocket::{
|
||||
use rocket_cors::{AllowedHeaders, AllowedOrigins};
|
||||
use server::{
|
||||
error::ServerError,
|
||||
graphql::{GraphqlSchema, Mutation, QueryRoot},
|
||||
graphql::{attachment_bytes, Attachment, GraphqlSchema, Mutation, QueryRoot},
|
||||
};
|
||||
|
||||
#[get("/refresh")]
|
||||
@@ -69,12 +69,34 @@ async fn show(nm: &State<Notmuch>, query: &str) -> Result<Json<ThreadSet>, Debug
|
||||
Ok(Json(res))
|
||||
}
|
||||
|
||||
struct PartResponder {
|
||||
struct InlineAttachmentResponder(Attachment);
|
||||
|
||||
impl<'r, 'o: 'r> Responder<'r, 'o> for InlineAttachmentResponder {
|
||||
fn respond_to(self, _: &'r Request<'_>) -> rocket::response::Result<'o> {
|
||||
let mut resp = Response::build();
|
||||
if let Some(filename) = self.0.filename {
|
||||
info!("filename {:?}", filename);
|
||||
resp.header(Header::new(
|
||||
"Content-Disposition",
|
||||
format!(r#"inline; filename="{}""#, filename),
|
||||
));
|
||||
}
|
||||
if let Some(content_type) = self.0.content_type {
|
||||
if let Some(ct) = ContentType::parse_flexible(&content_type) {
|
||||
resp.header(ct);
|
||||
}
|
||||
}
|
||||
resp.sized_body(self.0.bytes.len(), Cursor::new(self.0.bytes))
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
struct DownloadPartResponder {
|
||||
bytes: Vec<u8>,
|
||||
filename: Option<String>,
|
||||
}
|
||||
|
||||
impl<'r, 'o: 'r> Responder<'r, 'o> for PartResponder {
|
||||
impl<'r, 'o: 'r> Responder<'r, 'o> for DownloadPartResponder {
|
||||
fn respond_to(self, _: &'r Request<'_>) -> rocket::response::Result<'o> {
|
||||
let mut resp = Response::build();
|
||||
if let Some(filename) = self.filename {
|
||||
@@ -90,22 +112,49 @@ impl<'r, 'o: 'r> Responder<'r, 'o> for PartResponder {
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/attachment/<id>/<idx>")]
|
||||
async fn attachment(
|
||||
_nm: &State<Notmuch>,
|
||||
#[get("/view/attachment/<id>/<idx>/<_>")]
|
||||
async fn view_attachment(
|
||||
nm: &State<Notmuch>,
|
||||
id: &str,
|
||||
idx: usize,
|
||||
) -> Result<PartResponder, Debug<NotmuchError>> {
|
||||
let _idx = idx;
|
||||
let _mid = if id.starts_with("id:") {
|
||||
idx: &str,
|
||||
) -> Result<InlineAttachmentResponder, Debug<ServerError>> {
|
||||
let mid = if id.starts_with("id:") {
|
||||
id.to_string()
|
||||
} else {
|
||||
format!("id:{}", id)
|
||||
};
|
||||
let bytes = Vec::new();
|
||||
let filename = None;
|
||||
info!("view attachment {mid} {idx}");
|
||||
let idx: Vec<_> = idx
|
||||
.split('.')
|
||||
.map(|s| s.parse().expect("not a usize"))
|
||||
.collect();
|
||||
let attachment = attachment_bytes(nm, &mid, &idx)?;
|
||||
// TODO: plumb Content-Type, or just create wrappers for serving the Attachment type
|
||||
Ok(InlineAttachmentResponder(attachment))
|
||||
}
|
||||
|
||||
#[get("/download/attachment/<id>/<idx>/<_>")]
|
||||
async fn download_attachment(
|
||||
nm: &State<Notmuch>,
|
||||
id: &str,
|
||||
idx: &str,
|
||||
) -> Result<DownloadPartResponder, Debug<ServerError>> {
|
||||
let mid = if id.starts_with("id:") {
|
||||
id.to_string()
|
||||
} else {
|
||||
format!("id:{}", id)
|
||||
};
|
||||
info!("download attachment {mid} {idx}");
|
||||
let idx: Vec<_> = idx
|
||||
.split('.')
|
||||
.map(|s| s.parse().expect("not a usize"))
|
||||
.collect();
|
||||
let attachment = attachment_bytes(nm, &mid, &idx)?;
|
||||
// TODO(wathiede): use walk_attachments from graphql to fill this out
|
||||
Ok(PartResponder { bytes, filename })
|
||||
Ok(DownloadPartResponder {
|
||||
bytes: attachment.bytes,
|
||||
filename: attachment.filename,
|
||||
})
|
||||
}
|
||||
|
||||
#[get("/original/<id>/part/<part>")]
|
||||
@@ -113,7 +162,7 @@ async fn original_part(
|
||||
nm: &State<Notmuch>,
|
||||
id: &str,
|
||||
part: usize,
|
||||
) -> Result<PartResponder, Debug<NotmuchError>> {
|
||||
) -> Result<DownloadPartResponder, Debug<NotmuchError>> {
|
||||
let mid = if id.starts_with("id:") {
|
||||
id.to_string()
|
||||
} else {
|
||||
@@ -121,7 +170,7 @@ async fn original_part(
|
||||
};
|
||||
let meta = nm.show_part(&mid, part)?;
|
||||
let res = nm.show_original_part(&mid, part)?;
|
||||
Ok(PartResponder {
|
||||
Ok(DownloadPartResponder {
|
||||
bytes: res,
|
||||
filename: meta.filename,
|
||||
})
|
||||
@@ -201,7 +250,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
graphql_query,
|
||||
graphql_request,
|
||||
graphiql,
|
||||
attachment
|
||||
view_attachment,
|
||||
download_attachment,
|
||||
],
|
||||
)
|
||||
.attach(cors)
|
||||
|
||||
Reference in New Issue
Block a user