v0.7.40 - Removed event_broadcasts table and related code to fix FOREIGN KEY constraint failures preventing event insertion
This commit is contained in:
@@ -493,6 +493,24 @@ button:disabled {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Relay Events Styles */
|
||||
.status-message {
|
||||
margin-top: 10px;
|
||||
padding: 8px;
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 14px;
|
||||
font-family: var(--font-family);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.relay-entry {
|
||||
border: var(--border-width) solid var(--border-color);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
background: var(--secondary-color);
|
||||
}
|
||||
|
||||
.config-value-input:focus {
|
||||
border: 1px solid var(--accent-color);
|
||||
background: var(--secondary-color);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<li><button class="nav-item" data-page="subscriptions">Subscriptions</button></li>
|
||||
<li><button class="nav-item" data-page="configuration">Configuration</button></li>
|
||||
<li><button class="nav-item" data-page="authorization">Authorization</button></li>
|
||||
<li><button class="nav-item" data-page="relay-events">Relay Events</button></li>
|
||||
<li><button class="nav-item" data-page="dm">DM</button></li>
|
||||
<li><button class="nav-item" data-page="database">Database Query</button></li>
|
||||
</ul>
|
||||
@@ -341,6 +342,72 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RELAY EVENTS Section -->
|
||||
<div class="section" id="relayEventsSection" style="display: none;">
|
||||
<div class="section-header">
|
||||
<h2>RELAY EVENTS MANAGEMENT</h2>
|
||||
</div>
|
||||
|
||||
<!-- Kind 0: User Metadata -->
|
||||
<div class="input-group">
|
||||
<h3>Kind 0: User Metadata</h3>
|
||||
<div class="form-group">
|
||||
<label for="kind0-name">Name:</label>
|
||||
<input type="text" id="kind0-name" placeholder="Relay Name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="kind0-about">About:</label>
|
||||
<textarea id="kind0-about" rows="3" placeholder="Relay Description"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="kind0-picture">Picture URL:</label>
|
||||
<input type="url" id="kind0-picture" placeholder="https://example.com/logo.png">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="kind0-banner">Banner URL:</label>
|
||||
<input type="url" id="kind0-banner" placeholder="https://example.com/banner.png">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="kind0-nip05">NIP-05:</label>
|
||||
<input type="text" id="kind0-nip05" placeholder="relay@example.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="kind0-website">Website:</label>
|
||||
<input type="url" id="kind0-website" placeholder="https://example.com">
|
||||
</div>
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="submit-kind0-btn">UPDATE METADATA</button>
|
||||
</div>
|
||||
<div id="kind0-status" class="status-message"></div>
|
||||
</div>
|
||||
|
||||
<!-- Kind 10050: DM Relay List -->
|
||||
<div class="input-group">
|
||||
<h3>Kind 10050: DM Relay List</h3>
|
||||
<div class="form-group">
|
||||
<label for="kind10050-relays">Relay URLs (one per line):</label>
|
||||
<textarea id="kind10050-relays" rows="4" placeholder="wss://relay1.com wss://relay2.com"></textarea>
|
||||
</div>
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="submit-kind10050-btn">UPDATE DM RELAYS</button>
|
||||
</div>
|
||||
<div id="kind10050-status" class="status-message"></div>
|
||||
</div>
|
||||
|
||||
<!-- Kind 10002: Relay List -->
|
||||
<div class="input-group">
|
||||
<h3>Kind 10002: Relay List</h3>
|
||||
<div id="kind10002-relay-entries">
|
||||
<!-- Dynamic relay entries will be added here -->
|
||||
</div>
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="add-relay-entry-btn">ADD RELAY</button>
|
||||
<button type="button" id="submit-kind10002-btn">UPDATE RELAYS</button>
|
||||
</div>
|
||||
<div id="kind10002-status" class="status-message"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SQL QUERY Section -->
|
||||
<div class="section" id="sqlQuerySection" style="display: none;">
|
||||
<div class="section-header">
|
||||
|
||||
496
api/index.js
496
api/index.js
@@ -46,6 +46,8 @@ let pendingSqlQueries = new Map();
|
||||
let eventRateChart = null;
|
||||
let previousTotalEvents = 0; // Track previous total for rate calculation
|
||||
|
||||
// Relay Events state - now handled by main subscription
|
||||
|
||||
// DOM elements
|
||||
const loginModal = document.getElementById('login-modal');
|
||||
const loginModalContainer = document.getElementById('login-modal-container');
|
||||
@@ -1048,9 +1050,9 @@ async function subscribeToConfiguration() {
|
||||
|
||||
// Mark as subscribed BEFORE calling subscribeMany to prevent race conditions
|
||||
isSubscribed = true;
|
||||
|
||||
// Subscribe to kind 23457 events (admin response events), kind 4 (NIP-04 DMs), kind 1059 (NIP-17 GiftWrap), and kind 24567 (ephemeral monitoring events)
|
||||
console.log('🔔 Calling relayPool.subscribeMany...');
|
||||
|
||||
// Subscribe to kind 23457 events (admin response events), kind 4 (NIP-04 DMs), kind 1059 (NIP-17 GiftWrap), kind 24567 (ephemeral monitoring events), and relay events (kinds 0, 10050, 10002)
|
||||
console.log('🔔 Calling relayPool.subscribeMany with all filters...');
|
||||
const subscription = relayPool.subscribeMany([url], [{
|
||||
since: Math.floor(Date.now() / 1000) - 5, // Look back 5 seconds to avoid race condition
|
||||
kinds: [23457],
|
||||
@@ -1072,8 +1074,13 @@ async function subscribeToConfiguration() {
|
||||
since: Math.floor(Date.now() / 1000), // Start from current time
|
||||
kinds: [24567], // Real-time ephemeral monitoring events
|
||||
authors: [getRelayPubkey()], // Only listen to monitoring events from the relay
|
||||
"#d": isLoggedIn ? ["event_kinds", "time_stats", "top_pubkeys", "active_subscriptions", "subscription_details", "cpu_metrics"] : ["event_kinds", "time_stats", "top_pubkeys", "active_subscriptions", "cpu_metrics"], // Include subscription_details only when authenticated, cpu_metrics available to all
|
||||
"#d": isLoggedIn ? ["event_kinds", "time_stats", "top_pubkeys", "subscription_details", "cpu_metrics"] : ["event_kinds", "time_stats", "top_pubkeys", "cpu_metrics"], // Include subscription_details only when authenticated, cpu_metrics available to all
|
||||
limit: 50
|
||||
}, {
|
||||
since: Math.floor(Date.now() / 1000) - (24 * 60 * 60), // Look back 24 hours for relay events
|
||||
kinds: [0, 10050, 10002], // Relay events: metadata, DM relays, relay list
|
||||
authors: [getRelayPubkey()], // Only listen to relay's own events
|
||||
limit: 10
|
||||
}], {
|
||||
async onevent(event) {
|
||||
// Simplified logging - one line per event
|
||||
@@ -1174,6 +1181,11 @@ async function subscribeToConfiguration() {
|
||||
// Process monitoring event (logging done above)
|
||||
processMonitoringEvent(event);
|
||||
}
|
||||
|
||||
// Handle relay events (kinds 0, 10050, 10002)
|
||||
if ([0, 10050, 10002].includes(event.kind)) {
|
||||
handleRelayEventReceived(event);
|
||||
}
|
||||
},
|
||||
oneose() {
|
||||
console.log('EOSE received - End of stored events');
|
||||
@@ -1240,9 +1252,19 @@ async function processAdminResponse(event) {
|
||||
|
||||
console.log('Decrypted admin response:', decryptedContent);
|
||||
|
||||
// Parse the decrypted JSON response
|
||||
const responseData = JSON.parse(decryptedContent);
|
||||
console.log('Parsed response data:', responseData);
|
||||
// Try to parse as JSON first, if it fails treat as plain text
|
||||
let responseData;
|
||||
try {
|
||||
responseData = JSON.parse(decryptedContent);
|
||||
console.log('Parsed response data:', responseData);
|
||||
} catch (parseError) {
|
||||
// Not JSON - treat as plain text response
|
||||
console.log('Response is plain text, not JSON');
|
||||
responseData = {
|
||||
plain_text: true,
|
||||
message: decryptedContent
|
||||
};
|
||||
}
|
||||
|
||||
// Log the response for testing
|
||||
if (typeof logTestEvent === 'function') {
|
||||
@@ -1400,14 +1422,15 @@ async function processMonitoringEvent(event) {
|
||||
updateStatsFromTopPubkeysMonitoringEvent(monitoringData);
|
||||
break;
|
||||
|
||||
case 'active_subscriptions':
|
||||
updateStatsFromActiveSubscriptionsMonitoringEvent(monitoringData);
|
||||
break;
|
||||
|
||||
case 'subscription_details':
|
||||
// Only process subscription details if user is authenticated
|
||||
if (isLoggedIn) {
|
||||
updateStatsFromSubscriptionDetailsMonitoringEvent(monitoringData);
|
||||
// Also update the active subscriptions count from this data
|
||||
if (monitoringData.data && monitoringData.data.subscriptions) {
|
||||
updateStatsCell('active-subscriptions', monitoringData.data.subscriptions.length.toString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1432,6 +1455,25 @@ function handleAdminResponseData(responseData) {
|
||||
console.log('Response data:', responseData);
|
||||
console.log('Response query_type:', responseData.query_type);
|
||||
|
||||
// Handle plain text responses (from create_relay_event and other commands)
|
||||
if (responseData.plain_text) {
|
||||
console.log('Handling plain text response');
|
||||
log(responseData.message, 'INFO');
|
||||
|
||||
// Show the message in relay events status if we're on that page
|
||||
if (currentPage === 'relay-events') {
|
||||
// Try to determine which kind based on message content
|
||||
if (responseData.message.includes('Kind: 0')) {
|
||||
showStatus('kind0-status', responseData.message, 'success');
|
||||
} else if (responseData.message.includes('Kind: 10050')) {
|
||||
showStatus('kind10050-status', responseData.message, 'success');
|
||||
} else if (responseData.message.includes('Kind: 10002')) {
|
||||
showStatus('kind10002-status', responseData.message, 'success');
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle auth query responses - updated to match backend response types
|
||||
if (responseData.query_type &&
|
||||
(responseData.query_type.includes('auth_rules') ||
|
||||
@@ -4022,27 +4064,14 @@ function updateStatsFromTopPubkeysMonitoringEvent(monitoringData) {
|
||||
}
|
||||
}
|
||||
|
||||
// Update statistics display from active_subscriptions monitoring event
|
||||
function updateStatsFromActiveSubscriptionsMonitoringEvent(monitoringData) {
|
||||
try {
|
||||
if (monitoringData.data_type !== 'active_subscriptions') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update active subscriptions cell with real-time data
|
||||
// The data is nested under monitoringData.data.total_subscriptions
|
||||
if (monitoringData.data && monitoringData.data.total_subscriptions !== undefined) {
|
||||
updateStatsCell('active-subscriptions', monitoringData.data.total_subscriptions.toString());
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
log(`Error updating active subscriptions from monitoring event: ${error.message}`, 'ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
// Update statistics display from subscription_details monitoring event
|
||||
function updateStatsFromSubscriptionDetailsMonitoringEvent(monitoringData) {
|
||||
try {
|
||||
// DEBUG: Log every subscription_details event that arrives at the webpage
|
||||
console.log('subscription_details', JSON.stringify(monitoringData, null, 2));
|
||||
console.log('subscription_details decoded:', monitoringData);
|
||||
|
||||
if (monitoringData.data_type !== 'subscription_details') {
|
||||
return;
|
||||
}
|
||||
@@ -4598,14 +4627,15 @@ function switchPage(pageName) {
|
||||
});
|
||||
|
||||
// Hide all sections
|
||||
const sections = [
|
||||
'databaseStatisticsSection',
|
||||
'subscriptionDetailsSection',
|
||||
'div_config',
|
||||
'authRulesSection',
|
||||
'nip17DMSection',
|
||||
'sqlQuerySection'
|
||||
];
|
||||
const sections = [
|
||||
'databaseStatisticsSection',
|
||||
'subscriptionDetailsSection',
|
||||
'div_config',
|
||||
'authRulesSection',
|
||||
'relayEventsSection',
|
||||
'nip17DMSection',
|
||||
'sqlQuerySection'
|
||||
];
|
||||
|
||||
sections.forEach(sectionId => {
|
||||
const section = document.getElementById(sectionId);
|
||||
@@ -4620,6 +4650,7 @@ function switchPage(pageName) {
|
||||
'subscriptions': 'subscriptionDetailsSection',
|
||||
'configuration': 'div_config',
|
||||
'authorization': 'authRulesSection',
|
||||
'relay-events': 'relayEventsSection',
|
||||
'dm': 'nip17DMSection',
|
||||
'database': 'sqlQuerySection'
|
||||
};
|
||||
@@ -5015,13 +5046,15 @@ function displaySqlQueryResults(response) {
|
||||
|
||||
// Handle SQL query response (called by event listener)
|
||||
function handleSqlQueryResponse(response) {
|
||||
// Check if this is a response to one of our queries
|
||||
if (response.request_id && pendingSqlQueries.has(response.request_id)) {
|
||||
const queryInfo = pendingSqlQueries.get(response.request_id);
|
||||
pendingSqlQueries.delete(response.request_id);
|
||||
console.log('=== HANDLING SQL QUERY RESPONSE ===');
|
||||
console.log('Response:', response);
|
||||
|
||||
// Display results
|
||||
displaySqlQueryResults(response);
|
||||
// Always display SQL query results when received
|
||||
displaySqlQueryResults(response);
|
||||
|
||||
// Clean up any pending queries
|
||||
if (response.request_id && pendingSqlQueries.has(response.request_id)) {
|
||||
pendingSqlQueries.delete(response.request_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5240,6 +5273,346 @@ function initializeToggleButtonsFromConfig(configData) {
|
||||
log('Monitoring system initialized - subscription-based activation ready', 'INFO');
|
||||
}
|
||||
|
||||
// ================================
|
||||
// RELAY EVENTS FUNCTIONS
|
||||
// ================================
|
||||
|
||||
|
||||
// Handle received relay events
|
||||
function handleRelayEventReceived(event) {
|
||||
console.log('Handling relay event:', event.kind, event);
|
||||
|
||||
switch (event.kind) {
|
||||
case 0:
|
||||
populateKind0Form(event);
|
||||
break;
|
||||
case 10050:
|
||||
populateKind10050Form(event);
|
||||
break;
|
||||
case 10002:
|
||||
populateKind10002Form(event);
|
||||
break;
|
||||
default:
|
||||
console.log('Unknown relay event kind:', event.kind);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate Kind 0 form (User Metadata)
|
||||
function populateKind0Form(event) {
|
||||
try {
|
||||
const metadata = JSON.parse(event.content);
|
||||
console.log('Populating Kind 0 form with:', metadata);
|
||||
|
||||
// Update form fields
|
||||
const nameField = document.getElementById('kind0-name');
|
||||
const aboutField = document.getElementById('kind0-about');
|
||||
const pictureField = document.getElementById('kind0-picture');
|
||||
const bannerField = document.getElementById('kind0-banner');
|
||||
const nip05Field = document.getElementById('kind0-nip05');
|
||||
const websiteField = document.getElementById('kind0-website');
|
||||
|
||||
if (nameField) nameField.value = metadata.name || '';
|
||||
if (aboutField) aboutField.value = metadata.about || '';
|
||||
if (pictureField) pictureField.value = metadata.picture || '';
|
||||
if (bannerField) bannerField.value = metadata.banner || '';
|
||||
if (nip05Field) nip05Field.value = metadata.nip05 || '';
|
||||
if (websiteField) websiteField.value = metadata.website || '';
|
||||
|
||||
showStatus('kind0-status', 'Metadata loaded from relay', 'success');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error populating Kind 0 form:', error);
|
||||
showStatus('kind0-status', 'Error loading metadata', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Populate Kind 10050 form (DM Relay List)
|
||||
function populateKind10050Form(event) {
|
||||
try {
|
||||
console.log('Populating Kind 10050 form with tags:', event.tags);
|
||||
|
||||
// Extract relay URLs from "relay" tags
|
||||
const relayUrls = event.tags
|
||||
.filter(tag => tag[0] === 'relay' && tag[1])
|
||||
.map(tag => tag[1]);
|
||||
|
||||
const relaysField = document.getElementById('kind10050-relays');
|
||||
if (relaysField) {
|
||||
relaysField.value = relayUrls.join('\n');
|
||||
}
|
||||
|
||||
showStatus('kind10050-status', 'DM relay list loaded from relay', 'success');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error populating Kind 10050 form:', error);
|
||||
showStatus('kind10050-status', 'Error loading DM relay list', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Populate Kind 10002 form (Relay List)
|
||||
function populateKind10002Form(event) {
|
||||
try {
|
||||
console.log('Populating Kind 10002 form with tags:', event.tags);
|
||||
|
||||
// Clear existing entries
|
||||
const container = document.getElementById('kind10002-relay-entries');
|
||||
if (container) {
|
||||
container.innerHTML = '';
|
||||
}
|
||||
|
||||
// Extract relay entries from "r" tags
|
||||
event.tags.forEach(tag => {
|
||||
if (tag[0] === 'r' && tag[1]) {
|
||||
const url = tag[1];
|
||||
const marker = tag[2] || 'read'; // Default to read if no marker
|
||||
const read = marker.includes('read');
|
||||
const write = marker.includes('write');
|
||||
|
||||
addRelayEntry(url, read, write);
|
||||
}
|
||||
});
|
||||
|
||||
showStatus('kind10002-status', 'Relay list loaded from relay', 'success');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error populating Kind 10002 form:', error);
|
||||
showStatus('kind10002-status', 'Error loading relay list', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Submit Kind 0 event
|
||||
async function submitKind0Event() {
|
||||
try {
|
||||
showStatus('kind0-status', 'Submitting metadata...', 'info');
|
||||
|
||||
// Collect form data
|
||||
const metadata = {
|
||||
name: document.getElementById('kind0-name').value.trim(),
|
||||
about: document.getElementById('kind0-about').value.trim(),
|
||||
picture: document.getElementById('kind0-picture').value.trim(),
|
||||
banner: document.getElementById('kind0-banner').value.trim(),
|
||||
nip05: document.getElementById('kind0-nip05').value.trim(),
|
||||
website: document.getElementById('kind0-website').value.trim()
|
||||
};
|
||||
|
||||
// Remove empty fields
|
||||
Object.keys(metadata).forEach(key => {
|
||||
if (!metadata[key]) delete metadata[key];
|
||||
});
|
||||
|
||||
// Validate required fields
|
||||
if (!metadata.name) {
|
||||
showStatus('kind0-status', 'Name is required', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
await sendCreateRelayEventCommand(0, metadata);
|
||||
showStatus('kind0-status', 'Metadata updated successfully', 'success');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error submitting Kind 0 event:', error);
|
||||
showStatus('kind0-status', 'Error updating metadata: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Submit Kind 10050 event
|
||||
async function submitKind10050Event() {
|
||||
try {
|
||||
showStatus('kind10050-status', 'Submitting DM relay list...', 'info');
|
||||
|
||||
// Parse textarea content
|
||||
const relaysText = document.getElementById('kind10050-relays').value.trim();
|
||||
const relays = relaysText.split('\n')
|
||||
.map(url => url.trim())
|
||||
.filter(url => url.length > 0)
|
||||
.filter(url => isValidRelayUrl(url));
|
||||
|
||||
if (relays.length === 0) {
|
||||
showStatus('kind10050-status', 'At least one valid relay URL is required', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
await sendCreateRelayEventCommand(10050, { relays });
|
||||
showStatus('kind10050-status', 'DM relay list updated successfully', 'success');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error submitting Kind 10050 event:', error);
|
||||
showStatus('kind10050-status', 'Error updating DM relay list: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Submit Kind 10002 event
|
||||
async function submitKind10002Event() {
|
||||
try {
|
||||
showStatus('kind10002-status', 'Submitting relay list...', 'info');
|
||||
|
||||
// Collect relay entries
|
||||
const relays = [];
|
||||
const entries = document.querySelectorAll('.relay-entry');
|
||||
|
||||
entries.forEach(entry => {
|
||||
const url = entry.querySelector('.relay-url').value.trim();
|
||||
const read = entry.querySelector('.relay-read').checked;
|
||||
const write = entry.querySelector('.relay-write').checked;
|
||||
|
||||
if (url && isValidRelayUrl(url)) {
|
||||
relays.push({
|
||||
url: url,
|
||||
read: read,
|
||||
write: write
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (relays.length === 0) {
|
||||
showStatus('kind10002-status', 'At least one valid relay entry is required', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
await sendCreateRelayEventCommand(10002, { relays });
|
||||
showStatus('kind10002-status', 'Relay list updated successfully', 'success');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error submitting Kind 10002 event:', error);
|
||||
showStatus('kind10002-status', 'Error updating relay list: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Send create_relay_event command
|
||||
async function sendCreateRelayEventCommand(kind, eventData) {
|
||||
if (!isLoggedIn || !userPubkey) {
|
||||
throw new Error('Must be logged in to create relay events');
|
||||
}
|
||||
|
||||
if (!relayPool) {
|
||||
throw new Error('SimplePool connection not available');
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`Sending create_relay_event command for kind ${kind}...`);
|
||||
|
||||
// Create command array
|
||||
const command_array = ["create_relay_event", kind, eventData];
|
||||
|
||||
// Encrypt the command array
|
||||
const encrypted_content = await encryptForRelay(JSON.stringify(command_array));
|
||||
if (!encrypted_content) {
|
||||
throw new Error('Failed to encrypt command array');
|
||||
}
|
||||
|
||||
// Create kind 23456 admin event
|
||||
const adminEvent = {
|
||||
kind: 23456,
|
||||
pubkey: userPubkey,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [["p", getRelayPubkey()]],
|
||||
content: encrypted_content
|
||||
};
|
||||
|
||||
// Sign the event
|
||||
const signedEvent = await window.nostr.signEvent(adminEvent);
|
||||
if (!signedEvent || !signedEvent.sig) {
|
||||
throw new Error('Event signing failed');
|
||||
}
|
||||
|
||||
// Publish via SimplePool
|
||||
const url = relayConnectionUrl.value.trim();
|
||||
const publishPromises = relayPool.publish([url], signedEvent);
|
||||
|
||||
// Wait for publish results
|
||||
const results = await Promise.allSettled(publishPromises);
|
||||
let successCount = 0;
|
||||
results.forEach((result, index) => {
|
||||
if (result.status === 'fulfilled') {
|
||||
successCount++;
|
||||
console.log(`✅ Relay event published successfully to relay ${index}`);
|
||||
} else {
|
||||
console.error(`❌ Relay event failed on relay ${index}:`, result.reason);
|
||||
}
|
||||
});
|
||||
|
||||
if (successCount === 0) {
|
||||
const errorDetails = results.map((r, i) => `Relay ${i}: ${r.reason?.message || r.reason}`).join('; ');
|
||||
throw new Error(`All relays rejected relay event. Details: ${errorDetails}`);
|
||||
}
|
||||
|
||||
console.log(`Relay event command sent successfully for kind ${kind}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Failed to send create_relay_event command for kind ${kind}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Validation helpers
|
||||
function isValidUrl(url) {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isValidRelayUrl(url) {
|
||||
if (!isValidUrl(url)) return false;
|
||||
return url.startsWith('ws://') || url.startsWith('wss://');
|
||||
}
|
||||
|
||||
// UI helpers
|
||||
function showStatus(elementId, message, type = 'info') {
|
||||
const element = document.getElementById(elementId);
|
||||
if (!element) return;
|
||||
|
||||
element.textContent = message;
|
||||
element.className = 'status-message';
|
||||
|
||||
// Add type-specific styling
|
||||
switch (type) {
|
||||
case 'success':
|
||||
element.style.color = 'var(--accent-color)';
|
||||
break;
|
||||
case 'error':
|
||||
element.style.color = '#ff0000';
|
||||
break;
|
||||
case 'info':
|
||||
default:
|
||||
element.style.color = 'var(--primary-color)';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function addRelayEntry(url = '', read = true, write = true) {
|
||||
const container = document.getElementById('kind10002-relay-entries');
|
||||
if (!container) return;
|
||||
|
||||
const entryDiv = document.createElement('div');
|
||||
entryDiv.className = 'relay-entry';
|
||||
entryDiv.innerHTML = `
|
||||
<div class="form-group" style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
|
||||
<input type="url" class="relay-url" placeholder="wss://relay.example.com" value="${url}" style="flex: 1; min-width: 300px; pointer-events: auto; cursor: text;">
|
||||
<label style="display: flex; align-items: center; gap: 5px; white-space: nowrap;">
|
||||
<input type="checkbox" class="relay-read" ${read ? 'checked' : ''}>
|
||||
Read
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 5px; white-space: nowrap;">
|
||||
<input type="checkbox" class="relay-write" ${write ? 'checked' : ''}>
|
||||
Write
|
||||
</label>
|
||||
<button type="button" onclick="removeRelayEntry(this)" style="padding: 4px 8px; font-size: 12px; white-space: nowrap;">Remove</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
container.appendChild(entryDiv);
|
||||
}
|
||||
|
||||
function removeRelayEntry(button) {
|
||||
const entry = button.closest('.relay-entry');
|
||||
if (entry) {
|
||||
entry.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize toggle button after DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('=== DOM CONTENT LOADED - INITIALIZING TOGGLE BUTTON ===');
|
||||
@@ -5249,4 +5622,43 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('=== SETTIMEOUT CALLBACK - CALLING initializeMonitoringToggleButton ===');
|
||||
initializeMonitoringToggleButton();
|
||||
}, 500); // Small delay to ensure DOM is fully ready
|
||||
});
|
||||
|
||||
// Initialize relay events functionality
|
||||
initializeRelayEvents();
|
||||
});
|
||||
|
||||
// Initialize relay events functionality
|
||||
function initializeRelayEvents() {
|
||||
console.log('Initializing relay events functionality...');
|
||||
|
||||
// Set up event handlers for relay events page
|
||||
const submitKind0Btn = document.getElementById('submit-kind0-btn');
|
||||
const submitKind10050Btn = document.getElementById('submit-kind10050-btn');
|
||||
const submitKind10002Btn = document.getElementById('submit-kind10002-btn');
|
||||
const addRelayEntryBtn = document.getElementById('add-relay-entry-btn');
|
||||
|
||||
if (submitKind0Btn) {
|
||||
submitKind0Btn.addEventListener('click', submitKind0Event);
|
||||
}
|
||||
|
||||
if (submitKind10050Btn) {
|
||||
submitKind10050Btn.addEventListener('click', submitKind10050Event);
|
||||
}
|
||||
|
||||
if (submitKind10002Btn) {
|
||||
submitKind10002Btn.addEventListener('click', submitKind10002Event);
|
||||
}
|
||||
|
||||
if (addRelayEntryBtn) {
|
||||
addRelayEntryBtn.addEventListener('click', () => addRelayEntry());
|
||||
}
|
||||
|
||||
// Add one empty relay entry by default for Kind 10002
|
||||
const kind10002Container = document.getElementById('kind10002-relay-entries');
|
||||
if (kind10002Container && kind10002Container.children.length === 0) {
|
||||
addRelayEntry(); // Add one empty entry to start
|
||||
console.log('Added initial empty relay entry for Kind 10002');
|
||||
}
|
||||
|
||||
console.log('Relay events functionality initialized');
|
||||
}
|
||||
Reference in New Issue
Block a user