From 7fa2633a1ea4cdad179720e6e8ab8945be30456d Mon Sep 17 00:00:00 2001 From: Kieran Date: Fri, 30 Aug 2024 22:00:06 +0100 Subject: [PATCH] Blossom fixes --- src/routes/blossom.rs | 46 ++++++++++--------- ui/index.html | 102 +++++++++++++++++++++++++++++++----------- 2 files changed, 101 insertions(+), 47 deletions(-) diff --git a/src/routes/blossom.rs b/src/routes/blossom.rs index 754cc5a..584ca1f 100644 --- a/src/routes/blossom.rs +++ b/src/routes/blossom.rs @@ -2,7 +2,7 @@ use std::fs; use log::error; use nostr::prelude::hex; -use nostr::TagKind; +use nostr::{Alphabet, SingleLetterTag, TagKind}; use rocket::data::ByteUnit; use rocket::http::Status; use rocket::response::Responder; @@ -54,12 +54,16 @@ impl BlossomResponse { } fn check_method(event: &nostr::Event, method: &str) -> bool { - if let Some(t) = event.tags.iter().find_map(|t| if t.kind() == TagKind::Method { - t.content() - } else { - None + if let Some(t) = event.tags.iter().find_map(|t| { + if t.kind() == TagKind::Method + || t.kind() == TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::T)) + { + t.content() + } else { + None + } }) { - return t == method; + return t.eq_ignore_ascii_case(method); } false } @@ -90,24 +94,24 @@ async fn upload( return BlossomResponse::error("Invalid request method tag"); } - let name = auth - .event - .tags - .iter() - .find_map(|t| if t.kind() == TagKind::Name { t.content() } else { None }); - let size = match auth.event.tags.iter().find_map(|t| { - let values = t.as_vec(); - if values.len() == 2 && values[0] == "size" { - Some(values[1].parse::().unwrap()) + let name = auth.event.tags.iter().find_map(|t| { + if t.kind() == TagKind::Name { + t.content() } else { None } - }) { - Some(s) => s, - None => return BlossomResponse::error("Invalid request, no size tag"), - }; - if size > settings.max_upload_bytes { - return BlossomResponse::error("File too large"); + }); + let size = auth.event.tags.iter().find_map(|t| { + if t.kind() == TagKind::Size { + t.content().and_then(|v| v.parse::().ok()) + } else { + None + } + }); + if let Some(z) = size { + if z > settings.max_upload_bytes { + return BlossomResponse::error("File too large"); + } } let mime_type = auth .content_type diff --git a/ui/index.html b/ui/index.html index e9b38ae..ad8de90 100644 --- a/ui/index.html +++ b/ui/index.html @@ -66,37 +66,80 @@ const file = input.files[0]; console.debug(file); - const fd = new FormData(); - fd.append("size", file.size.toString()); - fd.append("caption", file.name); - fd.append("media_type", file.type); - fd.append("file", file); - fd.append("no_transform", document.querySelector("#no_transform").checked.toString()) - - const auth_event = await window.nostr.signEvent({ - kind: 27235, - created_at: Math.floor(new Date().getTime() / 1000), - content: "", - tags: [ - ["u", `${window.location.protocol}//${window.location.host}/n96`], - ["method", "POST"] - ] - }); - const rsp = await fetch("/n96", { - body: fd, - method: "POST", - headers: { - accept: "application/json", - authorization: `Nostr ${btoa(JSON.stringify(auth_event))}`, - }, - }); - await dumpToLog(rsp); + const r_nip96 = document.querySelector("#method-nip96").checked; + const r_blossom = document.querySelector("#method-blossom").checked; + if (r_nip96) { + await uploadFilesNip96(file) + } else if (r_blossom) { + await uploadBlossom(file); + } } catch (ex) { if (ex instanceof Error) { alert(ex.message); } } } + + function buf2hex(buffer) { // buffer is an ArrayBuffer + return [...new Uint8Array(buffer)] + .map(x => x.toString(16).padStart(2, '0')) + .join(''); + } + + async function uploadBlossom(file) { + const hash = await window.crypto.subtle.digest("SHA-256", await file.arrayBuffer()); + + const now = Math.floor(new Date().getTime() / 1000); + const auth_event = await window.nostr.signEvent({ + kind: 24242, + created_at: now, + content: `Upload ${file.name}`, + tags: [ + ["t", "upload"], + ["u", `${window.location.protocol}//${window.location.host}/upload`], + ["x", buf2hex(hash)], + ["method", "PUT"], + ["expiration", (now + 10).toString()] + ] + }); + const rsp = await fetch("/upload", { + body: file, + method: "PUT", + headers: { + accept: "application/json", + authorization: `Nostr ${btoa(JSON.stringify(auth_event))}`, + }, + }); + await dumpToLog(rsp); + } + + async function uploadFilesNip96(file) { + const fd = new FormData(); + fd.append("size", file.size.toString()); + fd.append("caption", file.name); + fd.append("media_type", file.type); + fd.append("file", file); + fd.append("no_transform", document.querySelector("#no_transform").checked.toString()) + + const auth_event = await window.nostr.signEvent({ + kind: 27235, + created_at: Math.floor(new Date().getTime() / 1000), + content: "", + tags: [ + ["u", `${window.location.protocol}//${window.location.host}/n96`], + ["method", "POST"] + ] + }); + const rsp = await fetch("/n96", { + body: fd, + method: "POST", + headers: { + accept: "application/json", + authorization: `Nostr ${btoa(JSON.stringify(auth_event))}`, + }, + }); + await dumpToLog(rsp); + } @@ -105,7 +148,14 @@
- Upload a file using NIP-96 + +
You must have a nostr extension for this to work