You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
5.3 KiB
184 lines
5.3 KiB
"use strict"; |
|
var __defProp = Object.defineProperty; |
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; |
|
var __getOwnPropNames = Object.getOwnPropertyNames; |
|
var __hasOwnProp = Object.prototype.hasOwnProperty; |
|
var __export = (target, all) => { |
|
for (var name in all) |
|
__defProp(target, name, { get: all[name], enumerable: true }); |
|
}; |
|
var __copyProps = (to, from, except, desc) => { |
|
if (from && typeof from === "object" || typeof from === "function") { |
|
for (let key of __getOwnPropNames(from)) |
|
if (!__hasOwnProp.call(to, key) && key !== except) |
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); |
|
} |
|
return to; |
|
}; |
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); |
|
|
|
// nipb7.ts |
|
var nipb7_exports = {}; |
|
__export(nipb7_exports, { |
|
BlossomClient: () => BlossomClient |
|
}); |
|
module.exports = __toCommonJS(nipb7_exports); |
|
var import_sha256 = require("@noble/hashes/sha256"); |
|
|
|
// utils.ts |
|
var import_utils = require("@noble/hashes/utils"); |
|
var utf8Decoder = new TextDecoder("utf-8"); |
|
var utf8Encoder = new TextEncoder(); |
|
|
|
// nipb7.ts |
|
var BlossomClient = class { |
|
mediaserver; |
|
signer; |
|
constructor(mediaserver, signer) { |
|
if (!mediaserver.startsWith("http")) { |
|
mediaserver = "https://" + mediaserver; |
|
} |
|
this.mediaserver = mediaserver.replace(/\/$/, "") + "/"; |
|
this.signer = signer; |
|
} |
|
async httpCall(method, url, contentType, addAuthorization, body, result) { |
|
const headers = {}; |
|
if (contentType) { |
|
headers["Content-Type"] = contentType; |
|
} |
|
if (addAuthorization) { |
|
const auth = await addAuthorization(); |
|
if (auth) { |
|
headers["Authorization"] = auth; |
|
} |
|
} |
|
const response = await fetch(this.mediaserver + url, { |
|
method, |
|
headers, |
|
body |
|
}); |
|
if (response.status >= 300) { |
|
const reason = response.headers.get("X-Reason") || response.statusText; |
|
throw new Error(`${url} returned an error (${response.status}): ${reason}`); |
|
} |
|
if (result !== null && response.headers.get("content-type")?.includes("application/json")) { |
|
return await response.json(); |
|
} |
|
return response; |
|
} |
|
async authorizationHeader(modify) { |
|
const now = Math.floor(Date.now() / 1e3); |
|
const event = { |
|
created_at: now, |
|
kind: 24242, |
|
content: "blossom stuff", |
|
tags: [["expiration", String(now + 60)]] |
|
}; |
|
if (modify) { |
|
modify(event); |
|
} |
|
try { |
|
const signedEvent = await this.signer.signEvent(event); |
|
const eventJson = JSON.stringify(signedEvent); |
|
return "Nostr " + btoa(eventJson); |
|
} catch (error) { |
|
return ""; |
|
} |
|
} |
|
isValid32ByteHex(hash) { |
|
return /^[a-f0-9]{64}$/i.test(hash); |
|
} |
|
async check(hash) { |
|
if (!this.isValid32ByteHex(hash)) { |
|
throw new Error(`${hash} is not a valid 32-byte hex string`); |
|
} |
|
try { |
|
await this.httpCall("HEAD", hash); |
|
} catch (error) { |
|
throw new Error(`failed to check for ${hash}: ${error}`); |
|
} |
|
} |
|
async uploadBlob(file, contentType) { |
|
const hash = (0, import_utils.bytesToHex)((0, import_sha256.sha256)(new Uint8Array(await file.arrayBuffer()))); |
|
const actualContentType = contentType || file.type || "application/octet-stream"; |
|
const bd = await this.httpCall( |
|
"PUT", |
|
"upload", |
|
actualContentType, |
|
() => this.authorizationHeader((evt) => { |
|
evt.tags.push(["t", "upload"]); |
|
evt.tags.push(["x", hash]); |
|
}), |
|
file, |
|
{} |
|
); |
|
return bd; |
|
} |
|
async uploadFile(file) { |
|
return this.uploadBlob(file, file.type); |
|
} |
|
async download(hash) { |
|
if (!this.isValid32ByteHex(hash)) { |
|
throw new Error(`${hash} is not a valid 32-byte hex string`); |
|
} |
|
const authHeader = await this.authorizationHeader((evt) => { |
|
evt.tags.push(["t", "get"]); |
|
evt.tags.push(["x", hash]); |
|
}); |
|
const response = await fetch(this.mediaserver + hash, { |
|
method: "GET", |
|
headers: { |
|
Authorization: authHeader |
|
} |
|
}); |
|
if (response.status >= 300) { |
|
throw new Error(`${hash} is not present in ${this.mediaserver}: ${response.status}`); |
|
} |
|
return await response.arrayBuffer(); |
|
} |
|
async downloadAsBlob(hash) { |
|
const arrayBuffer = await this.download(hash); |
|
return new Blob([arrayBuffer]); |
|
} |
|
async list() { |
|
const pubkey = await this.signer.getPublicKey(); |
|
if (!this.isValid32ByteHex(pubkey)) { |
|
throw new Error(`pubkey ${pubkey} is not valid`); |
|
} |
|
try { |
|
const bds = await this.httpCall( |
|
"GET", |
|
`list/${pubkey}`, |
|
void 0, |
|
() => this.authorizationHeader((evt) => { |
|
evt.tags.push(["t", "list"]); |
|
}), |
|
void 0, |
|
[] |
|
); |
|
return bds; |
|
} catch (error) { |
|
throw new Error(`failed to list blobs: ${error}`); |
|
} |
|
} |
|
async delete(hash) { |
|
if (!this.isValid32ByteHex(hash)) { |
|
throw new Error(`${hash} is not a valid 32-byte hex string`); |
|
} |
|
try { |
|
await this.httpCall( |
|
"DELETE", |
|
hash, |
|
void 0, |
|
() => this.authorizationHeader((evt) => { |
|
evt.tags.push(["t", "delete"]); |
|
evt.tags.push(["x", hash]); |
|
}), |
|
void 0, |
|
null |
|
); |
|
} catch (error) { |
|
throw new Error(`failed to delete ${hash}: ${error}`); |
|
} |
|
} |
|
};
|
|
|