feat: successful confirmation from relay

This commit is contained in:
complex 2025-04-09 16:35:54 +02:00
parent aad74cf1a7
commit 58d16dbe85

@ -194,16 +194,29 @@ export class NostrHttpServer {
/**
* Publish an event to all configured relays
* @param event The event to publish
* @returns A map of relay URLs to their publish results
*/
private async publishToRelays(event: any): Promise<void> {
for (const relayUrl of this.config.relayUrls) {
private async publishToRelays(event: any): Promise<Map<string, { success: boolean; message: string }>> {
const results = new Map<string, { success: boolean; message: string }>();
const publishPromises = this.config.relayUrls.map(async (relayUrl) => {
try {
await this.publishToRelay(event, relayUrl);
const result = await this.publishToRelay(event, relayUrl);
results.set(relayUrl, result);
return result;
} catch (error) {
console.error(`Error publishing to relay ${relayUrl}:`, error);
// Continue to the next relay
const errorResult = {
success: false,
message: `Error publishing to relay ${relayUrl}: ${String(error)}`
};
results.set(relayUrl, errorResult);
console.error(errorResult.message);
return errorResult;
}
}
});
// Wait for all publish attempts to complete
await Promise.all(publishPromises);
return results;
}
/**
@ -211,57 +224,114 @@ export class NostrHttpServer {
* @param event The event to publish
* @param relayUrl The relay URL
*/
private async publishToRelay(event: any, relayUrl: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
private async publishToRelay(event: any, relayUrl: string): Promise<{ success: boolean; message: string }> {
return new Promise<{ success: boolean; message: string }>((resolve, reject) => {
try {
console.log(`[${relayUrl}] Initiating WebSocket connection...`);
// Ensure the event has all required fields in the correct format
const formattedEvent = {
id: event.id || '',
pubkey: event.pubkey || '',
created_at: typeof event.created_at === 'number' ? event.created_at : Math.floor(Date.now() / 1000),
kind: typeof event.kind === 'number' ? event.kind : 21121,
tags: Array.isArray(event.tags) ? event.tags : [],
content: typeof event.content === 'string' ? event.content : '',
sig: event.sig || ''
};
console.log(`[${relayUrl}] Formatted event:`, JSON.stringify(formattedEvent, null, 2));
// Create a WebSocket connection
const ws = new WebSocket(relayUrl);
let connected = false;
let ackReceived = false;
// Set a timeout for the connection
const connectionTimeout = setTimeout(() => {
if (!connected) {
console.log(`[${relayUrl}] Connection timeout - closing WebSocket`);
ws.close();
reject(new Error(`Connection timeout to relay: ${relayUrl}`));
}
}, 5000);
// Set a timeout for the publish operation
const timeout = setTimeout(() => {
ws.close();
reject(new Error(`Timed out connecting to relay: ${relayUrl}`));
const publishTimeout = setTimeout(() => {
if (!ackReceived) {
console.log(`[${relayUrl}] Publish timeout - closing WebSocket`);
ws.close();
reject(new Error(`Publish timeout waiting for acknowledgment from relay: ${relayUrl}`));
}
}, 10000);
ws.onopen = () => {
clearTimeout(timeout);
console.log(`[${relayUrl}] WebSocket connection established`);
clearTimeout(connectionTimeout);
connected = true;
// Send the event
const reqId = `pub-${Date.now()}`;
const reqMsg = JSON.stringify(["EVENT", reqId, event]);
// Format the message according to NIP-01 specification
const reqMsg = JSON.stringify(["EVENT", formattedEvent]);
console.log(`[${relayUrl}] Event content length: ${formattedEvent.content?.length || 0} bytes`);
console.log(`[${relayUrl}] Event tags: ${JSON.stringify(formattedEvent.tags)}`);
console.log(`[${relayUrl}] Sending message:`, reqMsg);
ws.send(reqMsg);
};
// Wait for OK response
const okTimeout = setTimeout(() => {
ws.close();
resolve(); // Resolve anyway, we don't want to block on OK response
}, 5000);
ws.onmessage = (msg) => {
try {
const data = JSON.parse(msg.data as string);
if (Array.isArray(data)) {
console.log(`[${relayUrl}] Received message: ${JSON.stringify(data).substring(0, 200)}...`);
const messageHandler = (msg: any) => {
try {
const data = JSON.parse(msg.data as string);
if (Array.isArray(data) && data[0] === "OK" && data[1] === reqId) {
clearTimeout(okTimeout);
ws.removeListener('message', messageHandler);
if (data[0] === "OK") {
ackReceived = true;
clearTimeout(publishTimeout);
const success = data[2] === true;
const message = success ?
`Event acknowledged by ${relayUrl}` :
`Event rejected by ${relayUrl}: ${data[3] || 'Unknown reason'}`;
console.log(`[${relayUrl}] ${message}`);
ws.close();
resolve();
resolve({ success, message });
} else if (data[0] === "EVENT") {
console.log(`[${relayUrl}] Received EVENT message (ignoring, waiting for OK)`);
} else if (data[0] === "NOTICE") {
console.error(`[${relayUrl}] Received NOTICE: ${data[1]}`);
// Don't reject here, wait for OK or timeout
}
} catch (e) {
// Ignore parsing errors
}
};
ws.on('message', messageHandler);
} catch (e) {
console.error(`[${relayUrl}] Error parsing message:`, e);
}
};
ws.onerror = (err) => {
clearTimeout(timeout);
reject(new Error(`WebSocket error: ${err.toString()}`));
if (!ackReceived) {
console.error(`[${relayUrl}] WebSocket error:`, err);
clearTimeout(connectionTimeout);
clearTimeout(publishTimeout);
const errorMsg = `WebSocket error with relay ${relayUrl}: ${err.toString()}`;
console.error(`[${relayUrl}] ${errorMsg}`);
ws.close();
reject(new Error(errorMsg));
}
};
ws.onclose = (event) => {
console.log(`[${relayUrl}] WebSocket closed with code ${event.code}${event.reason ? `: ${event.reason}` : ''}`);
clearTimeout(connectionTimeout);
clearTimeout(publishTimeout);
if (!ackReceived) {
const errorMsg = `WebSocket connection closed with relay ${relayUrl} before acknowledgment`;
console.error(`[${relayUrl}] ${errorMsg}`);
reject(new Error(errorMsg));
}
};
} catch (error) {
reject(error);
console.error(`[${relayUrl}] Error in publishToRelay:`, error);
reject(new Error(`Error setting up WebSocket connection to ${relayUrl}: ${String(error)}`));
}
});
}
@ -859,10 +929,34 @@ ${responseBody}`;
throw new Error('Failed to sign response event');
}
console.log('signedEvent', signedEvent);
console.log('Sending response event:', signedEvent.id);
// Publish to all relays
await this.publishToRelays(signedEvent);
// Publish to all relays and wait for acknowledgments
const results = await this.publishToRelays(signedEvent);
// Log results for each relay
let successCount = 0;
let failureCount = 0;
results.forEach((result, relayUrl) => {
if (result.success) {
successCount++;
console.log(`${result.message}`);
} else {
failureCount++;
console.error(`${result.message}`);
}
});
// Log summary
console.log(`Response event ${signedEvent.id} published to ${this.config.relayUrls.length} relays:`);
console.log(` Success: ${successCount}`);
console.log(` Failed: ${failureCount}`);
// If all relays failed, throw an error
if (failureCount === this.config.relayUrls.length) {
throw new Error('Failed to publish response event to any relay');
}
} catch (error) {
console.error('Error sending response event:', error);
throw error;