Add docs for blossom, add nip 86 and 98 support
This commit is contained in:
@@ -535,3 +535,13 @@ export declare const hexToBytes: (hex: string) => Uint8Array;
|
|||||||
export declare const sha256: (data: ArrayBuffer | Uint8Array) => Promise<string>;
|
export declare const sha256: (data: ArrayBuffer | Uint8Array) => Promise<string>;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Text encoding
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// TextEncoder instance for encoding strings to bytes
|
||||||
|
export declare const textEncoder: TextEncoder;
|
||||||
|
|
||||||
|
// TextDecoder instance for decoding bytes to strings
|
||||||
|
export declare const textDecoder: TextDecoder;
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Blossom
|
||||||
|
|
||||||
|
Client library for interacting with Blossom media servers. Provides utilities for authentication, blob operations, and file encryption.
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export type BlossomAuthAction = "get" | "upload" | "list" | "delete"
|
||||||
|
|
||||||
|
export type BlossomAuthEventOpts = {
|
||||||
|
action: BlossomAuthAction
|
||||||
|
server: string
|
||||||
|
hashes?: string[]
|
||||||
|
expiration?: number
|
||||||
|
content?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BlossomServer = {
|
||||||
|
url: string
|
||||||
|
pubkey?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BlossomErrorResponse = {
|
||||||
|
message: string
|
||||||
|
reason?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EncryptedFile {
|
||||||
|
key: string
|
||||||
|
nonce: string
|
||||||
|
ciphertext: Uint8Array
|
||||||
|
algorithm: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Creates a Blossom auth event for server operations
|
||||||
|
export declare const makeBlossomAuthEvent: (opts: BlossomAuthEventOpts) => Event
|
||||||
|
```
|
||||||
|
|
||||||
|
## Blob Operations
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Builds URL for accessing a blob
|
||||||
|
export declare const buildBlobUrl: (server: string, sha256: string, extension?: string) => string
|
||||||
|
|
||||||
|
// Checks if a blob exists on server
|
||||||
|
export declare const checkBlobExists: (server: string, sha256: string, options?: { authEvent?: SignedEvent }) => Promise<{exists: boolean; size?: number}>
|
||||||
|
|
||||||
|
// Downloads blob from server
|
||||||
|
export declare const getBlob: (server: string, sha256: string, options?: { authEvent?: SignedEvent; range?: {start: number; end?: number} }) => Promise<Response>
|
||||||
|
|
||||||
|
// Uploads blob to server
|
||||||
|
export declare const uploadBlob: (server: string, blob: Blob | ArrayBuffer, options?: { authEvent?: SignedEvent }) => Promise<Response>
|
||||||
|
|
||||||
|
// Deletes blob from server
|
||||||
|
export declare const deleteBlob: (server: string, sha256: string, options?: { authEvent?: SignedEvent }) => Promise<Response>
|
||||||
|
|
||||||
|
// Lists blobs for a pubkey
|
||||||
|
export declare const listBlobs: (server: string, pubkey: string, options?: { authEvent?: SignedEvent; since?: number; until?: number }) => Promise<Response>
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Encryption
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Encrypts a file using AES-GCM
|
||||||
|
export declare function encryptFile(file: Blob): Promise<EncryptedFile>
|
||||||
|
|
||||||
|
// Decrypts an encrypted file
|
||||||
|
export declare function decryptFile(encryptedFile: EncryptedFile): Promise<Uint8Array>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { uploadBlob, makeBlossomAuthEvent } from '@welshman/util'
|
||||||
|
|
||||||
|
// Create auth event for upload
|
||||||
|
const authEvent = makeBlossomAuthEvent({
|
||||||
|
action: "upload",
|
||||||
|
server: "https://blossom.example.com"
|
||||||
|
})
|
||||||
|
|
||||||
|
// Sign the auth event with your signer
|
||||||
|
const signedAuthEvent = await signer.signEvent(authEvent)
|
||||||
|
|
||||||
|
// Upload a file
|
||||||
|
const file = new File(["Hello world"], "hello.txt", { type: "text/plain" })
|
||||||
|
const response = await uploadBlob("https://blossom.example.com", file, {
|
||||||
|
authEvent: signedAuthEvent
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# NIP-86 Relay Management
|
||||||
|
|
||||||
|
Implementation of NIP-86 for managing Nostr relays through authenticated RPC requests.
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export enum ManagementMethod {
|
||||||
|
SupportedMethods = "supportedmethods",
|
||||||
|
BanPubkey = "banpubkey",
|
||||||
|
AllowPubkey = "allowpubkey",
|
||||||
|
ListBannedPubkeys = "listbannedpubkeys",
|
||||||
|
ListAllowedPubkeys = "listallowedpubkeys",
|
||||||
|
ListEventsNeedingModeration = "listeventsneedingmoderation",
|
||||||
|
AllowEvent = "allowevent",
|
||||||
|
BanEvent = "banevent",
|
||||||
|
ListBannedEvents = "listbannedevents",
|
||||||
|
ChangeRelayName = "changerelayname",
|
||||||
|
ChangeRelayDescription = "changerelaydescription",
|
||||||
|
ChangeRelayIcon = "changerelayicon",
|
||||||
|
AllowKind = "allowkind",
|
||||||
|
DisallowKind = "disallowkind",
|
||||||
|
ListAllowedKinds = "listallowedkinds",
|
||||||
|
BlockIp = "blockip",
|
||||||
|
UnblockIp = "unblockip",
|
||||||
|
ListBlockedIps = "listblockedips",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ManagementRequest = {
|
||||||
|
method: ManagementMethod
|
||||||
|
params: string[]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Sends a management request to a relay
|
||||||
|
export declare const sendManagementRequest: (url: string, request: ManagementRequest, authEvent: SignedEvent) => Promise<any>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { sendManagementRequest, ManagementMethod, makeHttpAuth } from '@welshman/util'
|
||||||
|
|
||||||
|
// Set up our url and params
|
||||||
|
const url = "https://relay.example.com/"
|
||||||
|
const payload = {method: ManagementMethod.SupportedMethods, params: []}
|
||||||
|
|
||||||
|
// Create auth event for the management endpoint
|
||||||
|
const authEvent = await makeHttpAuth(url, "POST", JSON.stringify(payload))
|
||||||
|
const signedAuthEvent = await signer.signEvent(authEvent)
|
||||||
|
|
||||||
|
// Get a list of supported methods
|
||||||
|
const response = await sendManagementRequest(url, payload, signedAuthEvent)
|
||||||
|
```
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# NIP-98 HTTP Auth
|
||||||
|
|
||||||
|
Implementation of NIP-98 HTTP Authentication for authenticating HTTP requests with Nostr events.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Creates an HTTP auth event for authenticating requests
|
||||||
|
export declare const makeHttpAuth: (url: string, method?: string, body?: string) => Promise<Event>
|
||||||
|
|
||||||
|
// Creates Authorization header from signed HTTP auth event
|
||||||
|
export declare const makeHttpAuthHeader: (event: SignedEvent) => string
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { makeHttpAuth, makeHttpAuthHeader } from '@welshman/util'
|
||||||
|
|
||||||
|
const url = "https://api.example.com/upload"
|
||||||
|
const method = "POST"
|
||||||
|
const body = {data: "example"}
|
||||||
|
|
||||||
|
// Create HTTP auth event
|
||||||
|
const authEvent = await makeHttpAuth(url, method, JSON.stringify(body))
|
||||||
|
|
||||||
|
// Sign the auth event
|
||||||
|
const signedEvent = await signer.signEvent(authEvent)
|
||||||
|
|
||||||
|
// Create Authorization header
|
||||||
|
const authHeader = makeHttpAuthHeader(signedEvent)
|
||||||
|
|
||||||
|
// Use in fetch request
|
||||||
|
const response = await fetch(url, {
|
||||||
|
body,
|
||||||
|
method,
|
||||||
|
headers: {
|
||||||
|
"Authorization": authHeader,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
import {get} from "svelte/store"
|
import {get} from "svelte/store"
|
||||||
import {uniq, nthNe, removeNil, nthEq} from "@welshman/lib"
|
import {uniq, nthNe, removeNil, nthEq} from "@welshman/lib"
|
||||||
import {
|
import {
|
||||||
|
sendManagementRequest,
|
||||||
|
ManagementRequest,
|
||||||
addToListPublicly,
|
addToListPublicly,
|
||||||
EventTemplate,
|
EventTemplate,
|
||||||
removeFromList,
|
removeFromList,
|
||||||
|
makeHttpAuth,
|
||||||
getListTags,
|
getListTags,
|
||||||
getRelayTags,
|
getRelayTags,
|
||||||
makeList,
|
makeList,
|
||||||
@@ -141,3 +144,12 @@ export const sendWrapped = async ({template, pubkeys, ...options}: SendWrappedOp
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const manageRelay = async (url: string, request: ManagementRequest) => {
|
||||||
|
url = url.replace(/^ws/, "http")
|
||||||
|
|
||||||
|
const authTemplate = await makeHttpAuth(url, "POST", JSON.stringify(request))
|
||||||
|
const authEvent = await signer.get()!.sign(authTemplate)
|
||||||
|
|
||||||
|
return sendManagementRequest(url, request, authEvent)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1321,7 +1321,10 @@ export const postJson = async <T>(url: string, data: T, opts: FetchOpts = {}) =>
|
|||||||
opts.headers = {}
|
opts.headers = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.headers["Content-Type"] = "application/json"
|
if (!opts.headers["Content-Type"]) {
|
||||||
|
opts.headers["Content-Type"] = "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
opts.body = JSON.stringify(data)
|
opts.body = JSON.stringify(data)
|
||||||
|
|
||||||
return fetchJson(url, opts)
|
return fetchJson(url, opts)
|
||||||
@@ -1562,6 +1565,10 @@ export const hexToBech32 = (prefix: string, hex: string) =>
|
|||||||
export const bech32ToHex = (b32: string) =>
|
export const bech32ToHex = (b32: string) =>
|
||||||
utf8.encode(bech32.fromWords(bech32.decode(b32 as any, false).words))
|
utf8.encode(bech32.fromWords(bech32.decode(b32 as any, false).words))
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Bytes <-> hex encoding
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an array buffer or Uint8Array to hex format
|
* Converts an array buffer or Uint8Array to hex format
|
||||||
* @param buffer - ArrayBuffer or Uint8Array to convert
|
* @param buffer - ArrayBuffer or Uint8Array to convert
|
||||||
@@ -1585,6 +1592,14 @@ export const bytesToHex = (buffer: ArrayBuffer | Uint8Array) => {
|
|||||||
export const hexToBytes = (hex: string) =>
|
export const hexToBytes = (hex: string) =>
|
||||||
new Uint8Array(hex.match(/.{2}/g)!.map(hex => parseInt(hex, 16)))
|
new Uint8Array(hex.match(/.{2}/g)!.map(hex => parseInt(hex, 16)))
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Binary data
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export const textEncoder = new TextEncoder()
|
||||||
|
|
||||||
|
export const textDecoder = new TextDecoder()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes SHA-256 hash of binary data
|
* Computes SHA-256 hash of binary data
|
||||||
* @param data - Binary data to hash
|
* @param data - Binary data to hash
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {Emitter, throttle, makePromise, defer, sleep, tryCatch, randomId} from "@welshman/lib"
|
import {Emitter, throttle, makePromise, defer, sleep, tryCatch, randomId} from "@welshman/lib"
|
||||||
import {
|
import {
|
||||||
createEvent,
|
makeEvent,
|
||||||
normalizeRelayUrl,
|
normalizeRelayUrl,
|
||||||
TrustedEvent,
|
TrustedEvent,
|
||||||
StampedEvent,
|
StampedEvent,
|
||||||
@@ -163,7 +163,7 @@ export class Nip46Sender extends Emitter {
|
|||||||
|
|
||||||
const payload = JSON.stringify({id, method, params})
|
const payload = JSON.stringify({id, method, params})
|
||||||
const content = await this.signer[algorithm].encrypt(signerPubkey, payload)
|
const content = await this.signer[algorithm].encrypt(signerPubkey, payload)
|
||||||
const template = createEvent(NOSTR_CONNECT, {content, tags: [["p", signerPubkey]]})
|
const template = makeEvent(NOSTR_CONNECT, {content, tags: [["p", signerPubkey]]})
|
||||||
const event = await this.signer.sign(template)
|
const event = await this.signer.sign(template)
|
||||||
|
|
||||||
publish({relays, event, context})
|
publish({relays, event, context})
|
||||||
|
|||||||
@@ -57,9 +57,9 @@ describe("Events", () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("createEvent", () => {
|
describe("makeEvent", () => {
|
||||||
it("should create event with defaults", () => {
|
it("should create event with defaults", () => {
|
||||||
const event = Events.createEvent(1, {})
|
const event = Events.makeEvent(1, {})
|
||||||
expect(event.kind).toBe(1)
|
expect(event.kind).toBe(1)
|
||||||
expect(event.content).toBe("")
|
expect(event.content).toBe("")
|
||||||
expect(event.tags).toEqual([])
|
expect(event.tags).toEqual([])
|
||||||
@@ -67,7 +67,7 @@ describe("Events", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should create event with provided values", () => {
|
it("should create event with provided values", () => {
|
||||||
const event = Events.createEvent(1, {
|
const event = Events.makeEvent(1, {
|
||||||
content: "Hello Nostr!",
|
content: "Hello Nostr!",
|
||||||
tags: [["p", pubkey]],
|
tags: [["p", pubkey]],
|
||||||
created_at: currentTime,
|
created_at: currentTime,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {Base64} from "js-base64"
|
|
||||||
import {now, bytesToHex, hexToBytes} from "@welshman/lib"
|
import {now, bytesToHex, hexToBytes} from "@welshman/lib"
|
||||||
import {BLOSSOM_AUTH} from "./Kinds.js"
|
import {BLOSSOM_AUTH} from "./Kinds.js"
|
||||||
import {makeEvent, SignedEvent} from "./Events.js"
|
import {makeEvent, SignedEvent} from "./Events.js"
|
||||||
|
import {makeHttpAuthHeader} from "./Nip98.js"
|
||||||
|
|
||||||
export type BlossomAuthAction = "get" | "upload" | "list" | "delete"
|
export type BlossomAuthAction = "get" | "upload" | "list" | "delete"
|
||||||
|
|
||||||
@@ -48,10 +48,6 @@ export const makeBlossomAuthEvent = ({
|
|||||||
return makeEvent(BLOSSOM_AUTH, {content, tags})
|
return makeEvent(BLOSSOM_AUTH, {content, tags})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createAuthorizationHeader = (event: SignedEvent): string => {
|
|
||||||
return `Nostr ${Base64.encode(JSON.stringify(event))}`
|
|
||||||
}
|
|
||||||
|
|
||||||
export const buildBlobUrl = (server: string, sha256: string, extension?: string): string => {
|
export const buildBlobUrl = (server: string, sha256: string, extension?: string): string => {
|
||||||
const url = new URL(server)
|
const url = new URL(server)
|
||||||
const filename = extension ? `${sha256}.${extension}` : sha256
|
const filename = extension ? `${sha256}.${extension}` : sha256
|
||||||
@@ -69,7 +65,7 @@ export const checkBlobExists = async (
|
|||||||
const headers: Record<string, string> = {}
|
const headers: Record<string, string> = {}
|
||||||
|
|
||||||
if (options.authEvent) {
|
if (options.authEvent) {
|
||||||
headers.Authorization = createAuthorizationHeader(options.authEvent)
|
headers.Authorization = makeHttpAuthHeader(options.authEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -101,7 +97,7 @@ export const getBlob = async (
|
|||||||
const headers: Record<string, string> = {}
|
const headers: Record<string, string> = {}
|
||||||
|
|
||||||
if (options.authEvent) {
|
if (options.authEvent) {
|
||||||
headers.Authorization = createAuthorizationHeader(options.authEvent)
|
headers.Authorization = makeHttpAuthHeader(options.authEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.range) {
|
if (options.range) {
|
||||||
@@ -126,7 +122,7 @@ export const uploadBlob = async (
|
|||||||
const headers: Record<string, string> = {}
|
const headers: Record<string, string> = {}
|
||||||
|
|
||||||
if (options.authEvent) {
|
if (options.authEvent) {
|
||||||
headers.Authorization = createAuthorizationHeader(options.authEvent)
|
headers.Authorization = makeHttpAuthHeader(options.authEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(uploadUrl, {method: "PUT", headers, body})
|
return fetch(uploadUrl, {method: "PUT", headers, body})
|
||||||
@@ -144,7 +140,7 @@ export const deleteBlob = async (
|
|||||||
const headers: Record<string, string> = {}
|
const headers: Record<string, string> = {}
|
||||||
|
|
||||||
if (options.authEvent) {
|
if (options.authEvent) {
|
||||||
headers.Authorization = createAuthorizationHeader(options.authEvent)
|
headers.Authorization = makeHttpAuthHeader(options.authEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(url, {method: "DELETE", headers})
|
return fetch(url, {method: "DELETE", headers})
|
||||||
@@ -175,7 +171,7 @@ export const listBlobs = async (
|
|||||||
const headers: Record<string, string> = {}
|
const headers: Record<string, string> = {}
|
||||||
|
|
||||||
if (options.authEvent) {
|
if (options.authEvent) {
|
||||||
headers.Authorization = createAuthorizationHeader(options.authEvent)
|
headers.Authorization = makeHttpAuthHeader(options.authEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(fullUrl, {headers})
|
return fetch(fullUrl, {headers})
|
||||||
|
|||||||
@@ -54,12 +54,14 @@ export type MakeEventOpts = {
|
|||||||
created_at?: number
|
created_at?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event template creation
|
||||||
|
|
||||||
export const makeEvent = (
|
export const makeEvent = (
|
||||||
kind: number,
|
kind: number,
|
||||||
{content = "", tags = [], created_at = now()}: MakeEventOpts = {},
|
{content = "", tags = [], created_at = now()}: MakeEventOpts = {},
|
||||||
) => ({kind, content, tags, created_at})
|
) => ({kind, content, tags, created_at})
|
||||||
|
|
||||||
export const createEvent = makeEvent
|
// Event signature verification
|
||||||
|
|
||||||
export const verifyEvent = (() => {
|
export const verifyEvent = (() => {
|
||||||
let verify = verifyEventPure
|
let verify = verifyEventPure
|
||||||
@@ -80,6 +82,8 @@ export const verifyEvent = (() => {
|
|||||||
Boolean(event.sig && (event[verifiedSymbol] || verify(event as SignedEvent)))
|
Boolean(event.sig && (event[verifiedSymbol] || verify(event as SignedEvent)))
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
// Type guards
|
||||||
|
|
||||||
export const isEventTemplate = (e: EventTemplate): e is EventTemplate =>
|
export const isEventTemplate = (e: EventTemplate): e is EventTemplate =>
|
||||||
Boolean(typeof e.kind === "number" && Array.isArray(e.tags) && typeof e.content === "string")
|
Boolean(typeof e.kind === "number" && Array.isArray(e.tags) && typeof e.content === "string")
|
||||||
|
|
||||||
@@ -100,6 +104,8 @@ export const isUnwrappedEvent = (e: TrustedEvent): e is UnwrappedEvent =>
|
|||||||
export const isTrustedEvent = (e: TrustedEvent): e is TrustedEvent =>
|
export const isTrustedEvent = (e: TrustedEvent): e is TrustedEvent =>
|
||||||
isSignedEvent(e) || isUnwrappedEvent(e)
|
isSignedEvent(e) || isUnwrappedEvent(e)
|
||||||
|
|
||||||
|
// Type coercion and attribute stripping
|
||||||
|
|
||||||
export const asEventTemplate = (e: EventTemplate): EventTemplate =>
|
export const asEventTemplate = (e: EventTemplate): EventTemplate =>
|
||||||
pick(["kind", "tags", "content"], e)
|
pick(["kind", "tags", "content"], e)
|
||||||
|
|
||||||
@@ -121,6 +127,8 @@ export const asUnwrappedEvent = (e: UnwrappedEvent): UnwrappedEvent =>
|
|||||||
export const asTrustedEvent = (e: TrustedEvent): TrustedEvent =>
|
export const asTrustedEvent = (e: TrustedEvent): TrustedEvent =>
|
||||||
pick(["kind", "tags", "content", "created_at", "pubkey", "id", "sig", "wrap"], e)
|
pick(["kind", "tags", "content", "created_at", "pubkey", "id", "sig", "wrap"], e)
|
||||||
|
|
||||||
|
// Utilities for working with events
|
||||||
|
|
||||||
export const getIdentifier = (e: EventTemplate) => e.tags.find(t => t[0] === "d")?.[1]
|
export const getIdentifier = (e: EventTemplate) => e.tags.find(t => t[0] === "d")?.[1]
|
||||||
|
|
||||||
export const getIdOrAddress = (e: HashedEvent) => (isReplaceable(e) ? getAddress(e) : e.id)
|
export const getIdOrAddress = (e: HashedEvent) => (isReplaceable(e) ? getAddress(e) : e.id)
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import {postJson} from "@welshman/lib"
|
||||||
|
import {SignedEvent} from "./Events.js"
|
||||||
|
import {makeHttpAuthHeader} from "./Nip98.js"
|
||||||
|
|
||||||
|
export enum ManagementMethod {
|
||||||
|
SupportedMethods = "supportedmethods",
|
||||||
|
BanPubkey = "banpubkey",
|
||||||
|
AllowPubkey = "allowpubkey",
|
||||||
|
ListBannedPubkeys = "listbannedpubkeys",
|
||||||
|
ListAllowedPubkeys = "listallowedpubkeys",
|
||||||
|
ListEventsNeedingModeration = "listeventsneedingmoderation",
|
||||||
|
AllowEvent = "allowevent",
|
||||||
|
BanEvent = "banevent",
|
||||||
|
ListBannedEvents = "listbannedevents",
|
||||||
|
ChangeRelayName = "changerelayname",
|
||||||
|
ChangeRelayDescription = "changerelaydescription",
|
||||||
|
ChangeRelayIcon = "changerelayicon",
|
||||||
|
AllowKind = "allowkind",
|
||||||
|
DisallowKind = "disallowkind",
|
||||||
|
ListAllowedKinds = "listallowedkinds",
|
||||||
|
BlockIp = "blockip",
|
||||||
|
UnblockIp = "unblockip",
|
||||||
|
ListBlockedIps = "listblockedips",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ManagementRequest = {
|
||||||
|
method: ManagementMethod
|
||||||
|
params: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendManagementRequest = (
|
||||||
|
url: string,
|
||||||
|
request: ManagementRequest,
|
||||||
|
authEvent: SignedEvent,
|
||||||
|
) =>
|
||||||
|
postJson(url, request, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/nostr+json+rpc",
|
||||||
|
Authorization: makeHttpAuthHeader(authEvent),
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import {Base64} from "js-base64"
|
||||||
|
import {sha256, textEncoder} from "@welshman/lib"
|
||||||
|
import {makeEvent, SignedEvent} from "./Events.js"
|
||||||
|
import {HTTP_AUTH} from "./Kinds.js"
|
||||||
|
|
||||||
|
export const makeHttpAuth = async (url: string, method = "GET", body?: string) => {
|
||||||
|
const tags = [
|
||||||
|
["u", url],
|
||||||
|
["method", method],
|
||||||
|
]
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
tags.push(["payload", await sha256(textEncoder.encode(body))])
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeEvent(HTTP_AUTH, {tags})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeHttpAuthHeader = (event: SignedEvent) =>
|
||||||
|
`Nostr ${Base64.encode(JSON.stringify(event))}`
|
||||||
@@ -7,6 +7,8 @@ export * from "./Handler.js"
|
|||||||
export * from "./Kinds.js"
|
export * from "./Kinds.js"
|
||||||
export * from "./Links.js"
|
export * from "./Links.js"
|
||||||
export * from "./List.js"
|
export * from "./List.js"
|
||||||
|
export * from "./Nip86.js"
|
||||||
|
export * from "./Nip98.js"
|
||||||
export * from "./Profile.js"
|
export * from "./Profile.js"
|
||||||
export * from "./Relay.js"
|
export * from "./Relay.js"
|
||||||
export * from "./Tags.js"
|
export * from "./Tags.js"
|
||||||
|
|||||||
Reference in New Issue
Block a user