bare repo
This commit is contained in:
parent
d73f9bc01e
commit
3d356225cd
31 changed files with 134 additions and 3005 deletions
|
|
@ -1,9 +0,0 @@
|
|||
export class NostrEncryption {
|
||||
static async encrypt(privkey: string, pubkey: string, content: string) {
|
||||
return await window.NostrTools.nip04.encrypt(privkey, pubkey, content)
|
||||
}
|
||||
|
||||
static async decrypt(privkey: string, pubkey: string, content: string) {
|
||||
return await window.NostrTools.nip04.decrypt(privkey, pubkey, content)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export class NostrError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public code: string,
|
||||
public context?: any
|
||||
) {
|
||||
super(message)
|
||||
this.name = 'NostrError'
|
||||
}
|
||||
}
|
||||
|
||||
export function handleNostrError(error: unknown) {
|
||||
if (error instanceof NostrError) {
|
||||
switch (error.code) {
|
||||
case 'CONNECTION_FAILED':
|
||||
return 'Failed to connect to relay. Please check your connection.'
|
||||
case 'DECRYPT_FAILED':
|
||||
return 'Failed to decrypt message.'
|
||||
default:
|
||||
return error.message
|
||||
}
|
||||
}
|
||||
return 'An unexpected error occurred'
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import type { DirectMessage } from '@/types/nostr'
|
||||
|
||||
export class MessageManager {
|
||||
private messages = new Map<string, DirectMessage[]>()
|
||||
private processedIds = new Set<string>()
|
||||
|
||||
constructor() {
|
||||
this.loadFromStorage()
|
||||
}
|
||||
|
||||
addMessage(pubkey: string, message: DirectMessage) {
|
||||
if (this.processedIds.has(message.id)) return false
|
||||
if (this.isDuplicate(pubkey, message)) return false
|
||||
|
||||
this.processedIds.add(message.id)
|
||||
const messages = [...(this.messages.get(pubkey) || []), message]
|
||||
messages.sort((a, b) => a.created_at - b.created_at)
|
||||
this.messages.set(pubkey, messages)
|
||||
this.saveToStorage()
|
||||
return true
|
||||
}
|
||||
|
||||
private isDuplicate(pubkey: string, message: DirectMessage) {
|
||||
const existing = this.messages.get(pubkey) || []
|
||||
return existing.some(msg =>
|
||||
msg.content === message.content &&
|
||||
Math.abs(msg.created_at - message.created_at) < 1
|
||||
)
|
||||
}
|
||||
|
||||
private loadFromStorage() {
|
||||
try {
|
||||
const stored = localStorage.getItem('nostr_messages')
|
||||
if (stored) {
|
||||
this.messages = new Map(JSON.parse(stored))
|
||||
this.messages.forEach(msgs =>
|
||||
msgs.forEach(msg => this.processedIds.add(msg.id))
|
||||
)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load messages:', err)
|
||||
}
|
||||
}
|
||||
|
||||
private saveToStorage() {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
'nostr_messages',
|
||||
JSON.stringify(Array.from(this.messages.entries()))
|
||||
)
|
||||
} catch (err) {
|
||||
console.error('Failed to save messages:', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
import {
|
||||
getPublicKey,
|
||||
generateSecretKey,
|
||||
nip04,
|
||||
getEventHash,
|
||||
finalizeEvent,
|
||||
validateEvent,
|
||||
nip19,
|
||||
SimplePool,
|
||||
type Event,
|
||||
type Filter
|
||||
} from 'nostr-tools';
|
||||
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
|
||||
|
||||
// Expose NostrTools to the window object
|
||||
(window as any).NostrTools = {
|
||||
getPublicKey,
|
||||
generatePrivateKey: () => bytesToHex(generateSecretKey()),
|
||||
nip04,
|
||||
getEventHash,
|
||||
getSignature: (event: Event, privateKey: string) => {
|
||||
const signedEvent = finalizeEvent(event, hexToBytes(privateKey));
|
||||
return signedEvent.sig;
|
||||
},
|
||||
signEvent: (event: Event, privateKey: string) => {
|
||||
const signedEvent = finalizeEvent(event, hexToBytes(privateKey));
|
||||
return signedEvent.sig;
|
||||
},
|
||||
verifySignature: validateEvent,
|
||||
nip19,
|
||||
relayInit: (url: string) => {
|
||||
const pool = new SimplePool();
|
||||
return {
|
||||
connect: async () => {
|
||||
await pool.ensureRelay(url);
|
||||
return true;
|
||||
},
|
||||
sub: (filters: Filter[]) => {
|
||||
return {
|
||||
on: (type: string, callback: (event: Event) => void) => {
|
||||
if (type === 'event') {
|
||||
void pool.subscribeMany(
|
||||
[url],
|
||||
filters,
|
||||
{ onevent: callback }
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
publish: (event: Event) => {
|
||||
return {
|
||||
on: (type: string, cb: (msg?: string) => void) => {
|
||||
if (type === 'ok') {
|
||||
Promise.all(pool.publish([url], event))
|
||||
.then(() => cb())
|
||||
.catch((err: Error) => cb(err.message));
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
close: () => {
|
||||
pool.close([url]);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
123
src/lib/nostr.ts
123
src/lib/nostr.ts
|
|
@ -1,123 +0,0 @@
|
|||
import type { NostrEvent, NostrRelayConfig } from '../types/nostr'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
NostrTools: {
|
||||
getPublicKey: (privkey: string) => string
|
||||
generatePrivateKey: () => string
|
||||
nip04: {
|
||||
encrypt: (privkey: string, pubkey: string, content: string) => Promise<string>
|
||||
decrypt: (privkey: string, pubkey: string, content: string) => Promise<string>
|
||||
}
|
||||
getEventHash: (event: NostrEvent) => string
|
||||
signEvent: (event: NostrEvent, privkey: string) => Promise<string>
|
||||
getSignature: (event: NostrEvent, privkey: string) => string
|
||||
verifySignature: (event: NostrEvent) => boolean
|
||||
nip19: {
|
||||
decode: (str: string) => { type: string; data: string }
|
||||
npubEncode: (hex: string) => string
|
||||
}
|
||||
relayInit: (url: string) => {
|
||||
connect: () => Promise<void>
|
||||
sub: (filters: any[]) => {
|
||||
on: (event: string, callback: (event: NostrEvent) => void) => void
|
||||
}
|
||||
publish: (event: NostrEvent) => {
|
||||
on: (type: 'ok' | 'failed', cb: (msg?: string) => void) => void
|
||||
}
|
||||
close: () => void
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function connectToRelay(url: string) {
|
||||
const relay = window.NostrTools.relayInit(url)
|
||||
try {
|
||||
await relay.connect()
|
||||
return relay
|
||||
} catch (err) {
|
||||
console.error(`Failed to connect to ${url}:`, err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function publishEvent(event: NostrEvent, relays: NostrRelayConfig[]) {
|
||||
const connectedRelays = await Promise.all(
|
||||
relays.map(relay => connectToRelay(relay.url))
|
||||
)
|
||||
|
||||
const activeRelays = connectedRelays.filter(relay => relay !== null)
|
||||
|
||||
return Promise.all(
|
||||
activeRelays.map(relay =>
|
||||
new Promise((resolve) => {
|
||||
const pub = relay.publish(event)
|
||||
pub.on('ok', () => {
|
||||
resolve(true)
|
||||
})
|
||||
pub.on('failed', () => {
|
||||
resolve(false)
|
||||
})
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export async function encryptMessage(privkey: string, pubkey: string, content: string): Promise<string> {
|
||||
return await window.NostrTools.nip04.encrypt(privkey, pubkey, content)
|
||||
}
|
||||
|
||||
export async function decryptMessage(privkey: string, pubkey: string, content: string): Promise<string> {
|
||||
return await window.NostrTools.nip04.decrypt(privkey, pubkey, content)
|
||||
}
|
||||
|
||||
export function generatePrivateKey(): string {
|
||||
return window.NostrTools.generatePrivateKey()
|
||||
}
|
||||
|
||||
export function getPublicKey(privateKey: string): string {
|
||||
return window.NostrTools.getPublicKey(privateKey)
|
||||
}
|
||||
|
||||
export function getEventHash(event: NostrEvent): string {
|
||||
return window.NostrTools.getEventHash(event)
|
||||
}
|
||||
|
||||
export async function signEvent(event: NostrEvent, privateKey: string): Promise<string> {
|
||||
return window.NostrTools.getSignature(event, privateKey)
|
||||
}
|
||||
|
||||
export function verifySignature(event: NostrEvent): boolean {
|
||||
return window.NostrTools.verifySignature(event)
|
||||
}
|
||||
|
||||
export function npubToHex(npub: string): string {
|
||||
try {
|
||||
const { type, data } = window.NostrTools.nip19.decode(npub)
|
||||
if (type !== 'npub') throw new Error('Invalid npub')
|
||||
return data
|
||||
} catch (err) {
|
||||
console.error('Failed to decode npub:', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
export function hexToNpub(hex: string): string {
|
||||
return window.NostrTools.nip19.npubEncode(hex)
|
||||
}
|
||||
|
||||
export function isValidPrivateKey(key: string): boolean {
|
||||
try {
|
||||
if (!/^[0-9a-fA-F]{64}$/.test(key)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function formatPrivateKey(key: string): string {
|
||||
return key.trim().toLowerCase()
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import type { DirectMessage } from '@/types/nostr'
|
||||
|
||||
export class MessageStorage {
|
||||
static saveMessages(pubkey: string, messages: DirectMessage[]) {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
`messages_${pubkey}`,
|
||||
JSON.stringify(messages)
|
||||
)
|
||||
} catch (err) {
|
||||
console.error('Failed to save messages:', err)
|
||||
}
|
||||
}
|
||||
|
||||
static loadMessages(pubkey: string): DirectMessage[] {
|
||||
try {
|
||||
const stored = localStorage.getItem(`messages_${pubkey}`)
|
||||
return stored ? JSON.parse(stored) : []
|
||||
} catch (err) {
|
||||
console.error('Failed to load messages:', err)
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import type { NostrEvent } from '@/types/nostr'
|
||||
|
||||
export class SubscriptionManager {
|
||||
private currentSubs: any[] = []
|
||||
private isActive = false
|
||||
|
||||
async subscribe(relay: any, filters: any[], handlers: {
|
||||
onEvent: (event: NostrEvent) => void,
|
||||
onEose?: () => void
|
||||
}) {
|
||||
if (this.isActive) return
|
||||
|
||||
this.isActive = true
|
||||
const sub = relay.sub(filters)
|
||||
|
||||
sub.on('event', handlers.onEvent)
|
||||
if (handlers.onEose) {
|
||||
sub.on('eose', handlers.onEose)
|
||||
}
|
||||
|
||||
this.currentSubs.push(sub)
|
||||
return sub
|
||||
}
|
||||
|
||||
unsubscribe() {
|
||||
this.currentSubs.forEach(sub => {
|
||||
try {
|
||||
if (sub?.unsub) sub.unsub()
|
||||
} catch (err) {
|
||||
console.error('Failed to unsubscribe:', err)
|
||||
}
|
||||
})
|
||||
this.currentSubs = []
|
||||
this.isActive = false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
import { withTimeout } from '@/lib/utils'
|
||||
import type { NostrEvent } from '@/types/nostr'
|
||||
|
||||
// Create a new WebSocket manager class
|
||||
export class NostrWebSocketManager {
|
||||
private relayPool: any[] = []
|
||||
private subscriptions = new Map<string, any>()
|
||||
|
||||
async connect(relays: { url: string }[]) {
|
||||
// Close existing connections
|
||||
await this.disconnect()
|
||||
|
||||
// Connect to all relays
|
||||
this.relayPool = (await Promise.all(
|
||||
relays.map(relay => this.connectToRelay(relay.url))
|
||||
)).filter((relay): relay is any => relay !== null)
|
||||
|
||||
return this.relayPool.length > 0
|
||||
}
|
||||
|
||||
private async connectToRelay(url: string) {
|
||||
const relay = window.NostrTools.relayInit(url)
|
||||
try {
|
||||
await withTimeout(relay.connect())
|
||||
return relay
|
||||
} catch (err) {
|
||||
console.error(`Failed to connect to ${url}:`, err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async publish(event: NostrEvent, relays: { url: string }[]) {
|
||||
return Promise.all(
|
||||
relays.map(({ url }) => this.publishToRelay(event, url))
|
||||
)
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.relayPool.forEach(relay => relay.close())
|
||||
this.relayPool = []
|
||||
this.subscriptions.clear()
|
||||
}
|
||||
|
||||
get isConnected() {
|
||||
return this.relayPool.length > 0
|
||||
}
|
||||
|
||||
private async publishToRelay(event: NostrEvent, url: string) {
|
||||
const relay = window.NostrTools.relayInit(url)
|
||||
try {
|
||||
await relay.connect()
|
||||
return new Promise((resolve, reject) => {
|
||||
const pub = relay.publish(event)
|
||||
pub.on('ok', () => resolve(true))
|
||||
pub.on('failed', reject)
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(`Failed to publish to ${url}:`, err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue