Blossom fixes
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Kieran 2024-08-30 22:00:06 +01:00
parent f9cdc6d85e
commit 7fa2633a1e
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
2 changed files with 101 additions and 47 deletions

View File

@ -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::<usize>().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::<usize>().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

View File

@ -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);
}
</script>
</head>
<body>
@ -105,7 +148,14 @@
</h1>
<div class="flex flex-col gap-2">
<div>
Upload a file using NIP-96
<label>
NIP-96
<input type="radio" name="method" id="method-nip96"/>
</label>
<label>
Blossom
<input type="radio" name="method" id="method-blossom"/>
</label>
</div>
<div style="color: #ff8383;">
You must have a nostr extension for this to work