feat: add local relay support and centralize relay configuration

This commit is contained in:
Gigi
2025-10-05 23:38:56 +01:00
parent 7de8c49b01
commit 6e2f1102f7
6 changed files with 89 additions and 58 deletions

View File

@@ -11,6 +11,7 @@ import { createAddressLoader } from 'applesauce-loaders/loaders'
import Bookmarks from './components/Bookmarks'
import Toast from './components/Toast'
import { useToast } from './hooks/useToast'
import { ALL_RELAYS, PROFILE_RELAYS } from './config/relays'
const DEFAULT_ARTICLE = import.meta.env.VITE_DEFAULT_ARTICLE_NADDR ||
'naddr1qvzqqqr4gupzqmjxss3dld622uu8q25gywum9qtg4w4cv4064jmg20xsac2aam5nqqxnzd3cxqmrzv3exgmr2wfesgsmew'
@@ -94,33 +95,16 @@ function App() {
const pool = new RelayPool()
// Define relay URLs for bookmark fetching
const relayUrls = [
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
'wss://relay.dergigi.com',
'wss://wot.dergigi.com',
'wss://relay.snort.social',
'wss://relay.current.fyi',
'wss://nostr-pub.wellorder.net'
]
// Create a relay group for better event deduplication and management
// This follows the applesauce-relay documentation pattern
// Note: We could use pool.group(relayUrls) for direct requests in the future
pool.group(relayUrls)
console.log('Created relay group with', relayUrls.length, 'relays')
console.log('Relay URLs:', relayUrls)
pool.group(ALL_RELAYS)
console.log('Created relay group with', ALL_RELAYS.length, 'relays (including local)')
console.log('Relay URLs:', ALL_RELAYS)
// Attach address/replaceable loaders so ProfileModel can fetch profiles
const addressLoader = createAddressLoader(pool, {
eventStore: store,
lookupRelays: [
'wss://purplepag.es',
'wss://relay.primal.net',
'wss://relay.nostr.band'
]
lookupRelays: PROFILE_RELAYS
})
store.addressableLoader = addressLoader
store.replaceableLoader = addressLoader

70
src/config/relays.ts Normal file
View File

@@ -0,0 +1,70 @@
/**
* Centralized relay configuration
* All relay URLs used throughout the application
*/
// Local relay URL (hardcoded for development)
export const LOCAL_RELAY = 'ws://localhost:7777'
// Public relays for general use
export const PUBLIC_RELAYS = [
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
'wss://relay.dergigi.com',
'wss://wot.dergigi.com',
'wss://relay.snort.social',
'wss://relay.current.fyi',
'wss://nostr-pub.wellorder.net'
]
// Relays for profile lookups
export const PROFILE_RELAYS = [
'wss://purplepag.es',
'wss://relay.primal.net',
'wss://relay.nostr.band'
]
// Relays for highlights (read and write)
export const HIGHLIGHT_RELAYS = [
LOCAL_RELAY,
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
'wss://relay.snort.social',
'wss://purplepag.es'
]
// Relays for articles
export const ARTICLE_RELAYS = [
LOCAL_RELAY,
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
'wss://relay.primal.net'
]
// Relays for settings (read and write)
export const SETTINGS_RELAYS = [
LOCAL_RELAY,
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
'wss://relay.dergigi.com',
'wss://wot.dergigi.com'
]
// All relays including local (for general operations and relay pool initialization)
export const ALL_RELAYS = [LOCAL_RELAY, ...PUBLIC_RELAYS]
// All write relays (where we publish events)
export const WRITE_RELAYS = [
LOCAL_RELAY,
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
'wss://relay.snort.social',
'wss://purplepag.es',
'wss://relay.dergigi.com'
]

View File

@@ -5,11 +5,7 @@ import { EventFactory } from 'applesauce-factory'
import { AccountManager } from 'applesauce-accounts'
import { UserSettings, loadSettings, saveSettings, watchSettings } from '../services/settingsService'
import { loadFont, getFontFamily } from '../utils/fontLoader'
const RELAY_URLS = [
'wss://relay.damus.io', 'wss://nos.lol', 'wss://relay.nostr.band',
'wss://relay.dergigi.com', 'wss://wot.dergigi.com'
]
import { SETTINGS_RELAYS } from '../config/relays'
interface UseSettingsParams {
relayPool: RelayPool | null
@@ -29,7 +25,7 @@ export function useSettings({ relayPool, eventStore, pubkey, accountManager }: U
const loadAndWatch = async () => {
try {
const loadedSettings = await loadSettings(relayPool, eventStore, pubkey, RELAY_URLS)
const loadedSettings = await loadSettings(relayPool, eventStore, pubkey, SETTINGS_RELAYS)
if (loadedSettings) setSettings(loadedSettings)
} catch (err) {
console.error('Failed to load settings:', err)
@@ -65,7 +61,7 @@ export function useSettings({ relayPool, eventStore, pubkey, accountManager }: U
const fullAccount = accountManager.getActive()
if (!fullAccount) throw new Error('No active account')
const factory = new EventFactory({ signer: fullAccount })
await saveSettings(relayPool, eventStore, factory, newSettings, RELAY_URLS)
await saveSettings(relayPool, eventStore, factory, newSettings, SETTINGS_RELAYS)
setSettings(newSettings)
setToastType('success')
setToastMessage('Settings saved')

View File

@@ -9,6 +9,7 @@ import {
getArticlePublished,
getArticleSummary
} from 'applesauce-core/helpers'
import { ARTICLE_RELAYS } from '../config/relays'
export interface ArticleContent {
title: string
@@ -95,15 +96,10 @@ export async function fetchArticleByNaddr(
const pointer = decoded.data as AddressPointer
// Define relays to query
// Define relays to query - prefer relays from naddr, fallback to configured relays (including local)
const relays = pointer.relays && pointer.relays.length > 0
? pointer.relays
: [
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
'wss://relay.primal.net'
]
: ARTICLE_RELAYS
// Fetch the article event
const filter = {

View File

@@ -4,6 +4,7 @@ import { RelayPool } from 'applesauce-relay'
import { IAccount } from 'applesauce-accounts'
import { AddressPointer } from 'nostr-tools/nip19'
import { NostrEvent } from 'nostr-tools'
import { WRITE_RELAYS } from '../config/relays'
/**
* Creates and publishes a highlight event (NIP-84)
@@ -36,18 +37,10 @@ export async function createHighlight(
// Sign the event
const signedEvent = await factory.sign(highlightEvent)
// Publish to relays
const relayUrls = [
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
'wss://relay.snort.social',
'wss://purplepag.es'
]
// Publish to relays (including local relay)
await relayPool.publish(WRITE_RELAYS, signedEvent)
await relayPool.publish(relayUrls, signedEvent)
console.log('✅ Highlight published:', signedEvent)
console.log('✅ Highlight published to', WRITE_RELAYS.length, 'relays (including local):', signedEvent)
}
/**

View File

@@ -11,6 +11,7 @@ import {
getHighlightAttributions
} from 'applesauce-core/helpers'
import { Highlight } from '../types/highlights'
import { HIGHLIGHT_RELAYS } from '../config/relays'
/**
* Deduplicate highlight events by ID
@@ -42,18 +43,9 @@ export const fetchHighlightsForArticle = async (
onHighlight?: (highlight: Highlight) => void
): Promise<Highlight[]> => {
try {
// Use well-known relays for highlights even if user isn't logged in
const highlightRelays = [
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
'wss://relay.snort.social',
'wss://purplepag.es'
]
console.log('🔍 Fetching highlights (kind 9802) for article:', articleCoordinate)
console.log('🔍 Event ID:', eventId || 'none')
console.log('🔍 From relays:', highlightRelays)
console.log('🔍 From relays (including local):', HIGHLIGHT_RELAYS)
const seenIds = new Set<string>()
const processEvent = (event: NostrEvent): Highlight | null => {
@@ -89,7 +81,7 @@ export const fetchHighlightsForArticle = async (
// Query for highlights that reference this article via the 'a' tag
const aTagEvents = await lastValueFrom(
relayPool
.req(highlightRelays, { kinds: [9802], '#a': [articleCoordinate] })
.req(HIGHLIGHT_RELAYS, { kinds: [9802], '#a': [articleCoordinate] })
.pipe(
onlyEvents(),
tap((event: NostrEvent) => {
@@ -111,7 +103,7 @@ export const fetchHighlightsForArticle = async (
if (eventId) {
eTagEvents = await lastValueFrom(
relayPool
.req(highlightRelays, { kinds: [9802], '#e': [eventId] })
.req(HIGHLIGHT_RELAYS, { kinds: [9802], '#e': [eventId] })
.pipe(
onlyEvents(),
tap((event: NostrEvent) => {