bare repo

This commit is contained in:
padreug 2025-03-09 12:28:49 +01:00
parent d73f9bc01e
commit 3d356225cd
31 changed files with 134 additions and 3005 deletions

View file

@ -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)
}
}

View file

@ -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'
}

View file

@ -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)
}
}
}

View file

@ -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]);
}
};
}
};

View file

@ -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()
}

View file

@ -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 []
}
}
}

View file

@ -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
}
}

View file

@ -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
}
}
}