blossom-cloudron/public/upload-form.js
2024-11-04 16:09:32 +01:00

160 lines
5.3 KiB
JavaScript

import { html, LitElement } from "./lib/lit.min.js";
import { unixNow, newExpirationValue, getFileSha256, formatBytes } from "./utils.js";
import "./open-blob-form.js";
export class UploadForm extends LitElement {
static properties = {
selected: { state: true },
status: { state: true, type: String },
};
createRenderRoot() {
return this;
}
async upload(e) {
e.preventDefault();
try {
if (!this.selected) throw new Error("Select file first");
let file = this.selected;
// handle an edge case where some browsers set the mime type of .m3u8 files to audio/x-mpegurl
if (file.type === "audio/x-mpegurl" && file.name.endsWith(".m3u8")) {
file = new File([file], file.name, {
type: "application/vnd.apple.mpegurl",
});
}
this.status = "Compute SHA256 hash...";
const hash = await getFileSha256(file);
this.status = "Signing...";
// create auth event
const auth = await window.nostr.signEvent({
kind: 24242,
content: "Authorize Upload",
created_at: unixNow(),
tags: [
["t", "upload"],
["x", hash],
["expiration", newExpirationValue()],
],
});
const authorization = "Nostr " + btoa(JSON.stringify(auth));
// BUD-06 check upload
this.status = "Checking Upload...";
const check = await fetch("/upload", {
method: "HEAD",
headers: {
authorization,
"X-Content-Type": file.type,
"X-Content-Length": file.size,
"X-Sha-256": hash,
},
});
if (!check.ok) {
throw new Error(check.headers.get("x-upload-message") || "Upload Rejected");
}
// Upload blob
this.status = "Uploading...";
const res = await fetch("/upload", {
method: "PUT",
body: file,
// attach Authorization: Nostr <base64> header to request
headers: { authorization },
});
if (res.ok) {
const body = await res.json();
this.selected = undefined;
window.open(body.url, "_blank");
} else {
throw new Error(await res.text());
}
} catch (error) {
if (error instanceof Error) {
alert(error.message);
}
}
this.status = "Upload";
}
inputChange(e) {
this.selected = e.target.files[0];
}
render() {
const preview = !this.selected
? html`<div class="h-full w-full text-center flex flex-col items-center justify-center items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-10 h-10 text-blue-400 group-hover:text-blue-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
/>
</svg>
<p class="pointer-none text-gray-500">
<span class="text-sm">Drag and drop</span> files here <br />
or <a href="" id="" class="text-blue-600 hover:underline">select a file</a> from your computer
</p>
</div>`
: html`<p class="pointer-none text-gray-500 font-bold">${this.selected.name}</p>
<p class="text-gray-500 text-sm">${formatBytes(this.selected.size)}</p>`;
return html`<div class="w-full px-10 pt-10 pb-6 bg-white rounded-xl flex flex-col">
<div class="text-center">
<h1 class="mt-5 text-3xl font-bold text-gray-900">🌸 Blossom Server</h1>
<p class="mt-2 text-sm text-gray-400">Blobs stored simply on mediaservers</p>
<a class="text-sm text-blue-400" href="https://github.com/hzrd149/blossom-server">Github</a>
</div>
<form class="space-y-3" @submit="${this.upload}">
<div class="grid grid-cols-1 space-y-2">
<label class="text-sm font-bold text-gray-500 tracking-wide">Selected File</label>
<div class="flex items-center justify-center w-full">
<label
class="flex flex-col rounded-lg border-4 border-dashed w-full h-50 p-10 group text-center cursor-pointer"
>
${preview}
<input name="blob" type="file" class="hidden" @change="${this.inputChange}" />
</label>
</div>
</div>
<div>
<button
type="submit"
class="my-5 w-full flex justify-center bg-blue-500 text-gray-100 p-3 rounded-full tracking-wide font-semibold focus:outline-none focus:shadow-outline hover:bg-blue-600 shadow-lg cursor-pointer transition ease-in duration-300"
>
${this.status || "Upload"}
</button>
</div>
</form>
<get-blob-form></get-blob-form>
<div class="flex gap-4 mt-2">
<a class="text-md text-blue-500" href="#list">List Blobs</a>
${window.nostr && html`<a class="text-md text-blue-500" href="#mirror">Mirror Blobs</a>`}
<!-- <a class="text-md text-red-500" href="#delete">Request delete</a> -->
</div>
<a class="text-sm text-blue-400 ml-auto mt-4" href="https://github.com/hzrd149/blossom">🌸 Blossom Spec</a>
</div>`;
}
}
customElements.define("upload-form", UploadForm);