Enhance deployment script and update package dependencies
- Added detailed labels to the deployment script for IndeedHub, including title, version, description, license, icon, and repository URL. - Updated package dependencies in package.json and package-lock.json, including upgrading 'nostr-tools' to version 2.23.0 and adding 'axios' and '@tanstack/vue-query'. - Improved README with a modern description of the platform and updated project structure details. This commit enhances the clarity of the deployment process and ensures the project is using the latest dependencies for better performance and features.
This commit is contained in:
184
src/lib/nostr.ts
Normal file
184
src/lib/nostr.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
import { SimplePool, nip19, type Event as NostrEvent, type Filter } from 'nostr-tools'
|
||||
import { nostrConfig } from '../config/api.config'
|
||||
|
||||
/**
|
||||
* Nostr Client
|
||||
* Handles Nostr relay connections and event management
|
||||
*/
|
||||
class NostrClient {
|
||||
private pool: SimplePool
|
||||
private relays: string[]
|
||||
private lookupRelays: string[]
|
||||
private eventCache: Map<string, NostrEvent>
|
||||
|
||||
constructor() {
|
||||
this.pool = new SimplePool()
|
||||
this.relays = nostrConfig.relays
|
||||
this.lookupRelays = nostrConfig.lookupRelays
|
||||
this.eventCache = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events with filters
|
||||
*/
|
||||
subscribe(
|
||||
filters: Filter | Filter[],
|
||||
onEvent: (event: NostrEvent) => void,
|
||||
onEose?: () => void
|
||||
) {
|
||||
const filterArray = Array.isArray(filters) ? filters : [filters]
|
||||
const sub = this.pool.subscribeMany(
|
||||
this.relays,
|
||||
filterArray as any, // Type workaround for nostr-tools
|
||||
{
|
||||
onevent: (event) => {
|
||||
this.eventCache.set(event.id, event)
|
||||
onEvent(event)
|
||||
},
|
||||
oneose: () => {
|
||||
onEose?.()
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
return sub
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch events (one-time query)
|
||||
*/
|
||||
async fetchEvents(filters: Filter): Promise<NostrEvent[]> {
|
||||
const events = await this.pool.querySync(this.relays, filters)
|
||||
events.forEach((event) => {
|
||||
this.eventCache.set(event.id, event)
|
||||
})
|
||||
return events
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish event to relays
|
||||
*/
|
||||
async publishEvent(event: NostrEvent): Promise<void> {
|
||||
const results = this.pool.publish(this.relays, event)
|
||||
// Wait for at least one successful publish
|
||||
await Promise.race(results)
|
||||
this.eventCache.set(event.id, event)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get profile metadata (kind 0)
|
||||
*/
|
||||
async getProfile(pubkey: string): Promise<NostrEvent | null> {
|
||||
const events = await this.pool.querySync(this.lookupRelays, {
|
||||
kinds: [0],
|
||||
authors: [pubkey],
|
||||
limit: 1,
|
||||
})
|
||||
|
||||
return events[0] || null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comments for content (kind 1)
|
||||
*/
|
||||
async getComments(contentIdentifier: string): Promise<NostrEvent[]> {
|
||||
const filter: Filter = {
|
||||
kinds: [1],
|
||||
'#i': [contentIdentifier],
|
||||
}
|
||||
|
||||
return this.fetchEvents(filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reactions for content (kind 17)
|
||||
*/
|
||||
async getReactions(contentIdentifier: string): Promise<NostrEvent[]> {
|
||||
const filter: Filter = {
|
||||
kinds: [17],
|
||||
'#i': [contentIdentifier],
|
||||
}
|
||||
|
||||
return this.fetchEvents(filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to comments in real-time
|
||||
*/
|
||||
subscribeToComments(
|
||||
contentIdentifier: string,
|
||||
onComment: (event: NostrEvent) => void,
|
||||
onEose?: () => void
|
||||
) {
|
||||
return this.subscribe(
|
||||
[{
|
||||
kinds: [1],
|
||||
'#i': [contentIdentifier],
|
||||
since: Math.floor(Date.now() / 1000),
|
||||
}],
|
||||
onComment,
|
||||
onEose
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to reactions in real-time
|
||||
*/
|
||||
subscribeToReactions(
|
||||
contentIdentifier: string,
|
||||
onReaction: (event: NostrEvent) => void,
|
||||
onEose?: () => void
|
||||
) {
|
||||
return this.subscribe(
|
||||
[{
|
||||
kinds: [17],
|
||||
'#i': [contentIdentifier],
|
||||
since: Math.floor(Date.now() / 1000),
|
||||
}],
|
||||
onReaction,
|
||||
onEose
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event from cache or fetch
|
||||
*/
|
||||
async getEvent(eventId: string): Promise<NostrEvent | null> {
|
||||
// Check cache first
|
||||
if (this.eventCache.has(eventId)) {
|
||||
return this.eventCache.get(eventId)!
|
||||
}
|
||||
|
||||
// Fetch from relays
|
||||
const events = await this.fetchEvents({ ids: [eventId] })
|
||||
return events[0] || null
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all connections
|
||||
*/
|
||||
close() {
|
||||
this.pool.close(this.relays)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert npub to hex pubkey
|
||||
*/
|
||||
npubToHex(npub: string): string {
|
||||
const decoded = nip19.decode(npub)
|
||||
if (decoded.type === 'npub') {
|
||||
return decoded.data
|
||||
}
|
||||
throw new Error('Invalid npub')
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hex pubkey to npub
|
||||
*/
|
||||
hexToNpub(hex: string): string {
|
||||
return nip19.npubEncode(hex)
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const nostrClient = new NostrClient()
|
||||
Reference in New Issue
Block a user