This commit is contained in:
n 2025-04-10 10:29:13 +01:00
parent edd5f9f254
commit 865710390a
5 changed files with 165 additions and 70 deletions

@ -180,7 +180,6 @@ export class Nostr21121Creator {
} else {
privateKeyHex = privateKey;
}
// Get the public key from the private key
const privateKeyBytes = Buffer.from(privateKeyHex, 'hex');
const pubKey = nostrTools.getPublicKey(privateKeyBytes);
@ -229,16 +228,8 @@ export class Nostr21121Creator {
pubkey: pubKey
};
// Sign the event - simplified for demo
// In a real implementation, we would use proper signing from nostr-tools
const id = nostrTools.getEventHash(eventData);
const sig = 'simulated_signature_for_demo';
const signedEvent: NostrEvent = {
...eventData,
id,
sig
};
// Use nostrTools.finalizeEvent to sign the event
const signedEvent = nostrTools.finalizeEvent(eventData, privateKeyBytes);
return {
success: true,
@ -286,19 +277,24 @@ export class Nostr21121Creator {
// Begin publishing status
ToastNotifier.show(`Publishing to ${targetRelays.length} relay(s)...`, 'info');
// For demonstration purposes - simulate relay publishing
console.log(`Would publish event to relays: ${targetRelays.join(', ')}`);
// Actually publish the event to the relays
console.log(`Publishing event to relays: ${targetRelays.join(', ')}`);
console.log('Event:', result.event);
// Simulate successful relay publishing
const successCount = targetRelays.length;
// Create a relay pool
const relayPool = new nostrTools.SimplePool();
// In a real implementation, we would use:
// const relayPool = new nostrTools.SimplePool();
// const pubPromises = targetRelays.map(url => relayPool.publish([url], result.event!));
// const publishResults = await Promise.allSettled(pubPromises);
// const successCount = publishResults.filter(res => res.status === 'fulfilled').length;
// relayPool.close(targetRelays);
// Publish to all target relays - cast to the expected type
const pubPromises = targetRelays.map(url => relayPool.publish([url], result.event as any));
// Wait for all publish operations to complete
const publishResults = await Promise.allSettled(pubPromises);
// Count successful publishes
const successCount = publishResults.filter(res => res.status === 'fulfilled').length;
// Clean up relay pool
relayPool.close(targetRelays);
// Add event to local event manager if it has an ID
if (result.event && result.event.id) {

@ -120,8 +120,55 @@ export class ServerUI {
// Load server identity if available
this.loadServerIdentity();
// Automatically connect to the default relay
this.autoConnectToRelay();
console.log('Server UI initialized');
}
/**
* Automatically connect to the default relay
*/
private autoConnectToRelay(): void {
console.log('Auto-connecting to relay...');
// Update status to show auto-connection attempt
const relayStatus = document.getElementById(this.options.relayStatusContainer);
if (relayStatus) {
relayStatus.textContent = 'Auto-connecting...';
relayStatus.className = 'relay-status connecting';
}
// Get the relay URL from the input field (using the default value)
const relayUrlInput = document.getElementById(this.options.relayUrlInput) as HTMLInputElement;
if (!relayUrlInput) {
console.error('Relay URL input element not found');
return;
}
const relayUrl = relayUrlInput.value.trim() || 'wss://relay.degmods.com';
console.log(`Using relay URL for auto-connection: ${relayUrl}`);
// Get show all events state (default to true for auto-connection)
const showAllEventsCheckbox = document.getElementById('showAllEvents') as HTMLInputElement;
const showAllEvents = showAllEventsCheckbox?.checked ?? true;
// Force the checkbox state to match what we're using for auto-connection
if (showAllEventsCheckbox) {
showAllEventsCheckbox.checked = showAllEvents;
}
// Connect to relay and subscribe
this.connectToRelayAndSubscribe(relayUrl, showAllEvents)
.then(() => {
console.log('Auto-connection successful');
ToastNotifier.show(`Auto-connected to ${relayUrl}`, 'success');
})
.catch(error => {
console.error('Auto-connection failed:', error);
// We don't show an error toast for auto-connection failures to avoid annoying users
});
}
/**
* Set up event listeners for UI interactions
@ -176,8 +223,22 @@ export class ServerUI {
// Update the relay URL input with the actual URL used
relayUrlInput.value = relayUrl;
// Update the connect button state to show connecting
const connectButton = document.getElementById(this.options.connectButton) as HTMLButtonElement;
if (connectButton) {
connectButton.textContent = 'Connecting...';
connectButton.disabled = true;
}
// Connect to relay and subscribe
this.connectToRelayAndSubscribe(relayUrl, showAllEvents);
this.connectToRelayAndSubscribe(relayUrl, showAllEvents)
.finally(() => {
// Re-enable the connect button after connection attempt
if (connectButton) {
connectButton.textContent = 'Connect';
connectButton.disabled = false;
}
});
}
/**

@ -4,7 +4,7 @@ import qrcode from 'qrcode-generator';
// Internal imports
import { defaultServerConfig, appSettings } from './config';
import { convertNpubToHex } from './relay';
import { convertNpubToHex, publishToRelay } from './relay';
import { showSuccess } from './utils';
import { encryptWithWebCrypto, encryptKeyWithNostrExtension } from './utils/crypto-utils';
@ -409,12 +409,23 @@ export async function displayConvertedEvent(): Promise<void> {
// Display the event JSON
eventOutputPre.textContent = JSON.stringify(signedEvent, null, 2);
// Add a helpful message about publishing the event
// Automatically publish the event to the relay
const publishRelayInput = document.getElementById('publishRelay') as HTMLInputElement;
if (publishRelayInput) {
const publishResult = document.getElementById('publishResult');
const relayUrl = publishRelayInput.value.trim() || defaultServerConfig.defaultRelay;
if (publishResult) {
showSuccess(publishResult, 'Event created successfully. Ready to publish!');
publishResult.innerHTML = '<span class="loading">Publishing to relay...</span>';
// Publish the event using the publishToRelay function
publishToRelay(signedEvent, relayUrl)
.then((result: string) => {
showSuccess(publishResult, result);
})
.catch((error: Error) => {
publishResult.innerHTML = `<span class="error">Error publishing: ${error.message}</span>`;
});
}
}

@ -35,6 +35,13 @@ export class NostrRelayService {
*/
public async connectToRelay(relayUrl: string): Promise<boolean> {
try {
// If already connected to the requested relay, just return true
if (this.isConnected() && this.activeRelayUrl === relayUrl) {
console.log(`Already connected to relay: ${relayUrl}`);
this.updateStatus('Connected', 'connected');
return true;
}
this.updateStatus('Connecting to relay...', 'connecting');
// Test the connection first

@ -21,56 +21,76 @@ export class WebSocketManager {
*/
public async connect(url: string, options: WebSocketOptions = {}): Promise<WebSocket> {
return new Promise<WebSocket>((resolve, reject) => {
// Safely close any existing connection first
this.close();
this.url = url;
this.ws = new WebSocket(url);
this.connected = false;
let timeoutId: number | undefined;
const timeout = setTimeout(() => {
if (!this.connected) {
this.close();
reject(new Error(`Connection timeout after ${options.timeout || 5000}ms`));
}
}, options.timeout || 5000);
this.ws.onopen = () => {
clearTimeout(timeout);
this.connected = true;
if (options.onOpen) {
options.onOpen(this.ws as WebSocket);
}
resolve(this.ws as WebSocket);
};
this.ws.onmessage = (msg) => {
if (options.onMessage && typeof msg.data === 'string') {
try {
const parsedData = JSON.parse(msg.data);
options.onMessage(parsedData);
} catch {
// Ignore parsing errors
}
}
};
this.ws.onerror = (errorEvt) => {
clearTimeout(timeout);
if (options.onError) {
options.onError(errorEvt);
}
if (!this.connected) {
reject(new Error(`WebSocket error: ${errorEvt.toString()}`));
}
};
this.ws.onclose = () => {
clearTimeout(timeout);
try {
// Create a new WebSocket connection
this.url = url;
this.ws = new WebSocket(url);
this.connected = false;
if (options.onClose) {
options.onClose();
console.log(`Attempting WebSocket connection to: ${url}`);
// Set a timeout for the connection attempt
timeoutId = window.setTimeout(() => {
if (!this.connected) {
console.warn(`WebSocket connection timed out after ${options.timeout || 5000}ms`);
this.close();
reject(new Error(`Connection timeout after ${options.timeout || 5000}ms`));
}
}, options.timeout || 5000);
// Set up event handlers
if (this.ws) {
this.ws.onopen = () => {
if (timeoutId) clearTimeout(timeoutId);
this.connected = true;
if (options.onOpen && this.ws) {
options.onOpen(this.ws);
}
resolve(this.ws as WebSocket);
};
this.ws.onmessage = (msg) => {
if (options.onMessage && typeof msg.data === 'string') {
try {
const parsedData = JSON.parse(msg.data);
options.onMessage(parsedData);
} catch {
// Ignore parsing errors
}
}
};
this.ws.onerror = (errorEvt) => {
if (timeoutId) clearTimeout(timeoutId);
if (options.onError) {
options.onError(errorEvt);
}
if (!this.connected) {
reject(new Error(`WebSocket error: ${errorEvt.toString()}`));
}
};
this.ws.onclose = () => {
if (timeoutId) clearTimeout(timeoutId);
this.connected = false;
if (options.onClose) {
options.onClose();
}
};
} else {
throw new Error('Failed to create WebSocket instance');
}
};
} catch (error) {
console.error('Error creating WebSocket connection:', error);
if (timeoutId) clearTimeout(timeoutId);
this.close();
reject(error);
}
});
}