v0.3.19 - last save before major refactoring
This commit is contained in:
317
api/index.html
317
api/index.html
@@ -184,7 +184,7 @@
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
font-family: var(--font-family);
|
||||
font-size: 10px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.config-table-container {
|
||||
@@ -509,7 +509,7 @@
|
||||
<label for="relay-pubkey-manual">Relay Pubkey (if not available via NIP-11):</label>
|
||||
<input type="text" id="relay-pubkey-manual" placeholder="64-character hex pubkey"
|
||||
pattern="[0-9a-fA-F]{64}" title="64-character hexadecimal public key">
|
||||
<small>If the relay hasn't been configured yet, enter the relay pubkey shown during startup</small>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="inline-buttons">
|
||||
@@ -546,116 +546,101 @@
|
||||
|
||||
|
||||
|
||||
<!-- Testing Section -->
|
||||
<div id="div_config" class="section flex-section">
|
||||
|
||||
<div id="config-display" class="hidden">
|
||||
<div id="config-view-mode">
|
||||
<div class="config-table-container">
|
||||
<table class="config-table" id="config-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Value</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="config-table-body">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="fetch-config-btn">REFRESH</button>
|
||||
<button type="button" id="edit-config-btn">EDIT CONFIGURATION</button>
|
||||
<button type="button" id="copy-config-btn">COPY CONFIGURATION</button>
|
||||
</div>
|
||||
<!-- Testing Section -->
|
||||
<div id="div_config" class="section flex-section">
|
||||
<h2>RELAY CONFIGURATION</h2>
|
||||
<div id="config-display" class="hidden">
|
||||
<div id="config-view-mode">
|
||||
<div class="config-table-container">
|
||||
<table class="config-table" id="config-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Value</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="config-table-body">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="config-edit-mode" class="hidden">
|
||||
<h3>Edit Configuration</h3>
|
||||
<div id="config-form" class="section">
|
||||
<!-- Dynamic form will be generated here -->
|
||||
</div>
|
||||
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="save-config-btn">SAVE & PUBLISH</button>
|
||||
<button type="button" id="cancel-edit-btn">CANCEL</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auth Rules Management - Moved after configuration -->
|
||||
<div class="section flex-section" id="authRulesSection" style="display: none;">
|
||||
<div class="section-header">
|
||||
<h2>AUTH RULES MANAGEMENT</h2>
|
||||
<div class="status" id="authRulesStatus">●</div>
|
||||
</div>
|
||||
|
||||
<div class="auth-rules-controls">
|
||||
<div class="inline-buttons">
|
||||
<button id="viewAuthRulesBtn" class="btn">VIEW RULES</button>
|
||||
<button id="refreshAuthRulesBtn" class="btn">REFRESH</button>
|
||||
|
||||
<button type="button" id="edit-config-btn">EDIT CONFIGURATION</button>
|
||||
<button type="button" id="copy-config-btn">COPY CONFIGURATION</button>
|
||||
<button type="button" id="fetch-config-btn">REFRESH</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auth Rules Table -->
|
||||
<div id="authRulesTableContainer" style="display: none;">
|
||||
<table class="config-table" id="authRulesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rule Type</th>
|
||||
<th>Pattern Type</th>
|
||||
<th>Pattern Value</th>
|
||||
<th>Action</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="authRulesTableBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Simplified Auth Rule Input Section -->
|
||||
<div id="authRuleInputSections" style="display: block;">
|
||||
|
||||
<!-- Combined Pubkey Auth Rule Section -->
|
||||
<div class="auth-rule-section">
|
||||
<h3>MANAGE PUBKEY ACCESS</h3>
|
||||
<p>Add pubkeys to whitelist (allow) or blacklist (deny) access</p>
|
||||
<div class="input-group">
|
||||
<label for="authRulePubkey">Pubkey (nsec or hex):</label>
|
||||
<input type="text" id="authRulePubkey" placeholder="nsec1... or 64-character hex pubkey">
|
||||
<small id="authRuleHelp">Enter nsec (will auto-convert) or 64-character hex pubkey</small>
|
||||
</div>
|
||||
<div id="whitelistWarning" class="warning-box" style="display: none;">
|
||||
<strong>⚠️ WARNING:</strong> Adding whitelist rules changes relay behavior to whitelist-only
|
||||
mode.
|
||||
Only whitelisted users will be able to interact with the relay.
|
||||
</div>
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="addWhitelistBtn" onclick="addWhitelistRule()">ADD TO
|
||||
WHITELIST</button>
|
||||
<button type="button" id="addBlacklistBtn" onclick="addBlacklistRule()">ADD TO
|
||||
BLACKLIST</button>
|
||||
</div>
|
||||
<div id="authRuleStatus" class="rule-status"></div>
|
||||
<div id="config-edit-mode" class="hidden">
|
||||
<h3>Edit Configuration</h3>
|
||||
<div id="config-form" class="section">
|
||||
<!-- Dynamic form will be generated here -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Auth Rules Status Display -->
|
||||
<div id="authRulesStatusDisplay" style="display: none;">
|
||||
<h3>Auth System Status</h3>
|
||||
<div class="status" id="authSystemStatus">CHECKING...</div>
|
||||
<div class="json-display" id="authRulesCount">
|
||||
Rules: Loading...
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="save-config-btn">SAVE & PUBLISH</button>
|
||||
<button type="button" id="cancel-edit-btn">CANCEL</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auth Rules Management - Moved after configuration -->
|
||||
<div class="section flex-section" id="authRulesSection" style="display: none;">
|
||||
<div class="section-header">
|
||||
<h2>AUTH RULES MANAGEMENT</h2>
|
||||
</div>
|
||||
|
||||
<!-- Auth Rules Table -->
|
||||
<div id="authRulesTableContainer" style="display: none;">
|
||||
<table class="config-table" id="authRulesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rule Type</th>
|
||||
<th>Pattern Type</th>
|
||||
<th>Pattern Value</th>
|
||||
<th>Action</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="authRulesTableBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Simplified Auth Rule Input Section -->
|
||||
<div id="authRuleInputSections" style="display: block;">
|
||||
|
||||
<!-- Combined Pubkey Auth Rule Section -->
|
||||
<div class="auth-rule-section">
|
||||
<h3>MANAGE PUBKEY ACCESS</h3>
|
||||
<p>Add pubkeys to whitelist (allow) or blacklist (deny) access</p>
|
||||
<div class="input-group">
|
||||
<label for="authRulePubkey">Pubkey (nsec or hex):</label>
|
||||
<input type="text" id="authRulePubkey" placeholder="nsec1... or 64-character hex pubkey">
|
||||
<small id="authRuleHelp">Enter nsec (will auto-convert) or 64-character hex pubkey</small>
|
||||
</div>
|
||||
<div id="whitelistWarning" class="warning-box" style="display: none;">
|
||||
<strong>⚠️ WARNING:</strong> Adding whitelist rules changes relay behavior to whitelist-only
|
||||
mode.
|
||||
Only whitelisted users will be able to interact with the relay.
|
||||
</div>
|
||||
<div class="inline-buttons">
|
||||
<button type="button" id="addWhitelistBtn" onclick="addWhitelistRule()">ADD TO
|
||||
WHITELIST</button>
|
||||
<button type="button" id="addBlacklistBtn" onclick="addBlacklistRule()">ADD TO
|
||||
BLACKLIST</button>
|
||||
<button type="button" id="refreshAuthRulesBtn">REFRESH</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -766,7 +751,6 @@
|
||||
const persistentUserPubkey = document.getElementById('persistent-user-pubkey');
|
||||
const persistentUserAbout = document.getElementById('persistent-user-about');
|
||||
const persistentUserDetails = document.getElementById('persistent-user-details');
|
||||
const relayUrl = document.getElementById('relay-url');
|
||||
const fetchConfigBtn = document.getElementById('fetch-config-btn');
|
||||
// Relay connection elements
|
||||
const relayConnectionUrl = document.getElementById('relay-connection-url');
|
||||
@@ -992,10 +976,7 @@
|
||||
// Step 4: Update UI
|
||||
updateRelayConnectionStatus('connected');
|
||||
|
||||
// Step 5: Update the old relay URL field for backward compatibility
|
||||
if (relayUrl) {
|
||||
relayUrl.value = url;
|
||||
}
|
||||
// Step 5: Relay URL updated
|
||||
|
||||
// Step 6: Automatically load configuration and auth rules
|
||||
log('Relay connected successfully. Auto-loading configuration and auth rules...', 'INFO');
|
||||
@@ -1308,7 +1289,7 @@
|
||||
function disconnectFromRelay() {
|
||||
if (relayPool) {
|
||||
console.log('Cleaning up relay pool connection...');
|
||||
const url = relayUrl.value.trim();
|
||||
const url = relayConnectionUrl.value.trim();
|
||||
if (url) {
|
||||
relayPool.close([url]);
|
||||
}
|
||||
@@ -1392,7 +1373,7 @@
|
||||
// Clean up configuration pool
|
||||
if (relayPool) {
|
||||
console.log('Closing configuration pool...');
|
||||
const url = relayUrl.value.trim();
|
||||
const url = relayConnectionUrl.value.trim();
|
||||
if (url) {
|
||||
relayPool.close([url]);
|
||||
}
|
||||
@@ -1760,7 +1741,7 @@
|
||||
currentAuthRules = responseData.data;
|
||||
console.log('Updated currentAuthRules with', currentAuthRules.length, 'rules');
|
||||
|
||||
// Always show the auth rules table when we receive data
|
||||
// Always show the auth rules table when we receive data (no VIEW RULES button anymore)
|
||||
console.log('Auto-showing auth rules table since we received data...');
|
||||
showAuthRulesTable();
|
||||
|
||||
@@ -1770,7 +1751,7 @@
|
||||
currentAuthRules = [];
|
||||
console.log('No auth rules data received, cleared currentAuthRules');
|
||||
|
||||
// Show empty table
|
||||
// Show empty table (no VIEW RULES button anymore)
|
||||
console.log('Auto-showing auth rules table with empty data...');
|
||||
showAuthRulesTable();
|
||||
|
||||
@@ -2275,7 +2256,7 @@
|
||||
console.log(`Config update event signed with ${configObjects.length} objects`);
|
||||
|
||||
// Publish via SimplePool with detailed error diagnostics
|
||||
const url = relayUrl.value.trim();
|
||||
const url = relayConnectionUrl.value.trim();
|
||||
const publishPromises = relayPool.publish([url], signedEvent);
|
||||
|
||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||
@@ -2437,9 +2418,6 @@
|
||||
|
||||
// DOM elements for auth rules
|
||||
const authRulesSection = document.getElementById('authRulesSection');
|
||||
const authRulesStatus = document.getElementById('authRulesStatus');
|
||||
const viewAuthRulesBtn = document.getElementById('viewAuthRulesBtn');
|
||||
const addAuthRuleBtn = document.getElementById('addAuthRuleBtn');
|
||||
const refreshAuthRulesBtn = document.getElementById('refreshAuthRulesBtn');
|
||||
const authRulesTableContainer = document.getElementById('authRulesTableContainer');
|
||||
const authRulesTableBody = document.getElementById('authRulesTableBody');
|
||||
@@ -2448,9 +2426,6 @@
|
||||
const authRuleFormTitle = document.getElementById('authRuleFormTitle');
|
||||
const saveAuthRuleBtn = document.getElementById('saveAuthRuleBtn');
|
||||
const cancelAuthRuleBtn = document.getElementById('cancelAuthRuleBtn');
|
||||
const authRulesStatusDisplay = document.getElementById('authRulesStatusDisplay');
|
||||
const authSystemStatus = document.getElementById('authSystemStatus');
|
||||
const authRulesCount = document.getElementById('authRulesCount');
|
||||
|
||||
// Show auth rules section after login
|
||||
function showAuthRulesSection() {
|
||||
@@ -2473,9 +2448,6 @@
|
||||
if (authRuleFormContainer) {
|
||||
authRuleFormContainer.style.display = 'none';
|
||||
}
|
||||
if (authRulesStatusDisplay) {
|
||||
authRulesStatusDisplay.style.display = 'none';
|
||||
}
|
||||
|
||||
currentAuthRules = [];
|
||||
editingAuthRule = null;
|
||||
@@ -2483,28 +2455,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Update auth rules status indicator
|
||||
// Update auth rules status indicator (removed - no status element)
|
||||
function updateAuthRulesStatus(status) {
|
||||
if (!authRulesStatus) return;
|
||||
|
||||
switch (status) {
|
||||
case 'ready':
|
||||
authRulesStatus.textContent = 'READY';
|
||||
authRulesStatus.className = 'status disconnected';
|
||||
break;
|
||||
case 'loading':
|
||||
authRulesStatus.textContent = 'LOADING';
|
||||
authRulesStatus.className = 'status connected';
|
||||
break;
|
||||
case 'loaded':
|
||||
authRulesStatus.textContent = 'LOADED';
|
||||
authRulesStatus.className = 'status connected';
|
||||
break;
|
||||
case 'error':
|
||||
authRulesStatus.textContent = 'ERROR';
|
||||
authRulesStatus.className = 'status error';
|
||||
break;
|
||||
}
|
||||
// Status element removed - no-op
|
||||
}
|
||||
|
||||
// Load auth rules from relay using admin API
|
||||
@@ -2550,7 +2503,7 @@
|
||||
log('Sending auth rules query to relay...', 'INFO');
|
||||
|
||||
// Publish via SimplePool with detailed error diagnostics
|
||||
const url = relayUrl.value.trim();
|
||||
const url = relayConnectionUrl.value.trim();
|
||||
const publishPromises = relayPool.publish([url], signedEvent);
|
||||
|
||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||
@@ -2636,31 +2589,21 @@
|
||||
});
|
||||
|
||||
// Update status display
|
||||
if (authRulesCount) {
|
||||
const activeRules = rules.filter(r => r.enabled !== false).length;
|
||||
authRulesCount.textContent = `Total Rules: ${rules.length} (${activeRules} active)`;
|
||||
console.log(`Updated status display: ${rules.length} total, ${activeRules} active`);
|
||||
}
|
||||
console.log(`Total Rules: ${rules.length}, Active Rules: ${rules.filter(r => r.enabled !== false).length}`);
|
||||
|
||||
console.log('=== END DISPLAY AUTH RULES DEBUG ===');
|
||||
}
|
||||
|
||||
// Show auth rules table
|
||||
// Show auth rules table (automatically called when auth rules are loaded)
|
||||
function showAuthRulesTable() {
|
||||
console.log('=== SHOW AUTH RULES TABLE DEBUG ===');
|
||||
console.log('authRulesTableContainer element:', authRulesTableContainer);
|
||||
console.log('authRulesStatusDisplay element:', authRulesStatusDisplay);
|
||||
console.log('Current display style:', authRulesTableContainer ? authRulesTableContainer.style.display : 'element not found');
|
||||
|
||||
if (authRulesTableContainer) {
|
||||
authRulesTableContainer.style.display = 'block';
|
||||
console.log('Set authRulesTableContainer display to block');
|
||||
|
||||
if (authRulesStatusDisplay) {
|
||||
authRulesStatusDisplay.style.display = 'block';
|
||||
console.log('Set authRulesStatusDisplay display to block');
|
||||
}
|
||||
|
||||
// If we already have cached auth rules, display them immediately
|
||||
if (currentAuthRules && currentAuthRules.length >= 0) {
|
||||
console.log('Displaying cached auth rules:', currentAuthRules.length, 'rules');
|
||||
@@ -2766,7 +2709,7 @@
|
||||
log('Sending delete auth rule command to relay...', 'INFO');
|
||||
|
||||
// Publish via SimplePool with detailed error diagnostics
|
||||
const url = relayUrl.value.trim();
|
||||
const url = relayConnectionUrl.value.trim();
|
||||
const publishPromises = relayPool.publish([url], signedEvent);
|
||||
|
||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||
@@ -2917,20 +2860,6 @@
|
||||
};
|
||||
|
||||
// Auth rules event handlers
|
||||
if (viewAuthRulesBtn) {
|
||||
viewAuthRulesBtn.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
showAuthRulesTable();
|
||||
});
|
||||
}
|
||||
|
||||
if (addAuthRuleBtn) {
|
||||
addAuthRuleBtn.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
showAddAuthRuleForm();
|
||||
});
|
||||
}
|
||||
|
||||
if (refreshAuthRulesBtn) {
|
||||
refreshAuthRulesBtn.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
@@ -2991,34 +2920,29 @@
|
||||
// Add blacklist rule (updated to use combined input)
|
||||
function addBlacklistRule() {
|
||||
const input = document.getElementById('authRulePubkey');
|
||||
const statusDiv = document.getElementById('authRuleStatus');
|
||||
|
||||
if (!input || !statusDiv) return;
|
||||
if (!input) return;
|
||||
|
||||
const inputValue = input.value.trim();
|
||||
if (!inputValue) {
|
||||
statusDiv.className = 'rule-status error';
|
||||
statusDiv.textContent = 'Please enter a pubkey or nsec';
|
||||
log('Please enter a pubkey or nsec', 'ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert nsec to hex if needed
|
||||
const hexPubkey = nsecToHex(inputValue);
|
||||
if (!hexPubkey) {
|
||||
statusDiv.className = 'rule-status error';
|
||||
statusDiv.textContent = 'Invalid pubkey format. Please enter nsec1... or 64-character hex';
|
||||
log('Invalid pubkey format. Please enter nsec1... or 64-character hex', 'ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate hex length
|
||||
if (hexPubkey.length !== 64) {
|
||||
statusDiv.className = 'rule-status error';
|
||||
statusDiv.textContent = 'Invalid pubkey length. Must be exactly 64 characters';
|
||||
log('Invalid pubkey length. Must be exactly 64 characters', 'ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
statusDiv.className = 'rule-status';
|
||||
statusDiv.textContent = 'Adding to blacklist...';
|
||||
log('Adding to blacklist...', 'INFO');
|
||||
|
||||
// Create auth rule data
|
||||
const ruleData = {
|
||||
@@ -3031,8 +2955,7 @@
|
||||
// Add to WebSocket queue for processing
|
||||
addAuthRuleViaWebSocket(ruleData)
|
||||
.then(() => {
|
||||
statusDiv.className = 'rule-status success';
|
||||
statusDiv.textContent = `Pubkey ${hexPubkey.substring(0, 16)}... added to blacklist`;
|
||||
log(`Pubkey ${hexPubkey.substring(0, 16)}... added to blacklist`, 'INFO');
|
||||
input.value = '';
|
||||
|
||||
// Refresh auth rules display if visible
|
||||
@@ -3041,38 +2964,33 @@
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
statusDiv.className = 'rule-status error';
|
||||
statusDiv.textContent = `Failed to add rule: ${error.message}`;
|
||||
log(`Failed to add rule: ${error.message}`, 'ERROR');
|
||||
});
|
||||
}
|
||||
|
||||
// Add whitelist rule (updated to use combined input)
|
||||
function addWhitelistRule() {
|
||||
const input = document.getElementById('authRulePubkey');
|
||||
const statusDiv = document.getElementById('authRuleStatus');
|
||||
const warningDiv = document.getElementById('whitelistWarning');
|
||||
|
||||
if (!input || !statusDiv) return;
|
||||
if (!input) return;
|
||||
|
||||
const inputValue = input.value.trim();
|
||||
if (!inputValue) {
|
||||
statusDiv.className = 'rule-status error';
|
||||
statusDiv.textContent = 'Please enter a pubkey or nsec';
|
||||
log('Please enter a pubkey or nsec', 'ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert nsec to hex if needed
|
||||
const hexPubkey = nsecToHex(inputValue);
|
||||
if (!hexPubkey) {
|
||||
statusDiv.className = 'rule-status error';
|
||||
statusDiv.textContent = 'Invalid pubkey format. Please enter nsec1... or 64-character hex';
|
||||
log('Invalid pubkey format. Please enter nsec1... or 64-character hex', 'ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate hex length
|
||||
if (hexPubkey.length !== 64) {
|
||||
statusDiv.className = 'rule-status error';
|
||||
statusDiv.textContent = 'Invalid pubkey length. Must be exactly 64 characters';
|
||||
log('Invalid pubkey length. Must be exactly 64 characters', 'ERROR');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3081,8 +2999,7 @@
|
||||
warningDiv.style.display = 'block';
|
||||
}
|
||||
|
||||
statusDiv.className = 'rule-status';
|
||||
statusDiv.textContent = 'Adding to whitelist...';
|
||||
log('Adding to whitelist...', 'INFO');
|
||||
|
||||
// Create auth rule data
|
||||
const ruleData = {
|
||||
@@ -3095,8 +3012,7 @@
|
||||
// Add to WebSocket queue for processing
|
||||
addAuthRuleViaWebSocket(ruleData)
|
||||
.then(() => {
|
||||
statusDiv.className = 'rule-status success';
|
||||
statusDiv.textContent = `Pubkey ${hexPubkey.substring(0, 16)}... added to whitelist`;
|
||||
log(`Pubkey ${hexPubkey.substring(0, 16)}... added to whitelist`, 'INFO');
|
||||
input.value = '';
|
||||
|
||||
// Refresh auth rules display if visible
|
||||
@@ -3105,8 +3021,7 @@
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
statusDiv.className = 'rule-status error';
|
||||
statusDiv.textContent = `Failed to add rule: ${error.message}`;
|
||||
log(`Failed to add rule: ${error.message}`, 'ERROR');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3179,7 +3094,7 @@
|
||||
}
|
||||
|
||||
// Publish via SimplePool with detailed error diagnostics
|
||||
const url = relayUrl.value.trim();
|
||||
const url = relayConnectionUrl.value.trim();
|
||||
const publishPromises = relayPool.publish([url], signedEvent);
|
||||
|
||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||
@@ -3285,7 +3200,7 @@
|
||||
logTestEvent('SENT', `Get Auth Rules event: ${JSON.stringify(signedEvent)}`, 'EVENT');
|
||||
|
||||
// Publish via SimplePool with detailed error diagnostics
|
||||
const url = relayUrl.value.trim();
|
||||
const url = relayConnectionUrl.value.trim();
|
||||
const publishPromises = relayPool.publish([url], signedEvent);
|
||||
|
||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||
@@ -3359,7 +3274,7 @@
|
||||
logTestEvent('SENT', `Clear Auth Rules event: ${JSON.stringify(signedEvent)}`, 'EVENT');
|
||||
|
||||
// Publish via SimplePool with detailed error diagnostics
|
||||
const url = relayUrl.value.trim();
|
||||
const url = relayConnectionUrl.value.trim();
|
||||
const publishPromises = relayPool.publish([url], signedEvent);
|
||||
|
||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||
@@ -3442,7 +3357,7 @@
|
||||
logTestEvent('SENT', `Add Blacklist event: ${JSON.stringify(signedEvent)}`, 'EVENT');
|
||||
|
||||
// Publish via SimplePool with detailed error diagnostics
|
||||
const url = relayUrl.value.trim();
|
||||
const url = relayConnectionUrl.value.trim();
|
||||
const publishPromises = relayPool.publish([url], signedEvent);
|
||||
|
||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||
|
||||
@@ -63,7 +63,7 @@ while [[ $# -gt 0 ]]; do
|
||||
shift 2
|
||||
fi
|
||||
;;
|
||||
--preserve-database)
|
||||
-d|--preserve-database)
|
||||
PRESERVE_DATABASE=true
|
||||
shift
|
||||
;;
|
||||
|
||||
115
src/main.c
115
src/main.c
@@ -1716,7 +1716,15 @@ cJSON* generate_relay_info_json() {
|
||||
return info;
|
||||
}
|
||||
|
||||
// Handle NIP-11 HTTP request
|
||||
// NIP-11 HTTP session data structure for managing buffer lifetime
|
||||
struct nip11_session_data {
|
||||
char* json_buffer;
|
||||
size_t json_length;
|
||||
int headers_sent;
|
||||
int body_sent;
|
||||
};
|
||||
|
||||
// Handle NIP-11 HTTP request with proper asynchronous buffer management
|
||||
int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
|
||||
log_info("Handling NIP-11 relay information request");
|
||||
|
||||
@@ -1805,6 +1813,23 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
|
||||
|
||||
size_t json_len = strlen(json_string);
|
||||
|
||||
// Allocate session data to manage buffer lifetime across callbacks
|
||||
struct nip11_session_data* session_data = malloc(sizeof(struct nip11_session_data));
|
||||
if (!session_data) {
|
||||
log_error("Failed to allocate NIP-11 session data");
|
||||
free(json_string);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Store JSON buffer in session data for asynchronous handling
|
||||
session_data->json_buffer = json_string;
|
||||
session_data->json_length = json_len;
|
||||
session_data->headers_sent = 0;
|
||||
session_data->body_sent = 0;
|
||||
|
||||
// Store session data in WSI user data for callback access
|
||||
lws_set_wsi_user(wsi, session_data);
|
||||
|
||||
// Prepare HTTP response with CORS headers
|
||||
unsigned char buf[LWS_PRE + 1024];
|
||||
unsigned char *p = &buf[LWS_PRE];
|
||||
@@ -1813,70 +1838,66 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
|
||||
|
||||
// Add status
|
||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) {
|
||||
free(json_string);
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add content type
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
||||
(unsigned char*)"application/nostr+json", 22, &p, end)) {
|
||||
free(json_string);
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add content length
|
||||
if (lws_add_http_header_content_length(wsi, json_len, &p, end)) {
|
||||
free(json_string);
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add CORS headers as required by NIP-11
|
||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-origin:",
|
||||
(unsigned char*)"*", 1, &p, end)) {
|
||||
free(json_string);
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-headers:",
|
||||
(unsigned char*)"content-type, accept", 20, &p, end)) {
|
||||
free(json_string);
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-methods:",
|
||||
(unsigned char*)"GET, OPTIONS", 12, &p, end)) {
|
||||
free(json_string);
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Finalize headers
|
||||
if (lws_finalize_http_header(wsi, &p, end)) {
|
||||
free(json_string);
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Write headers
|
||||
if (lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS) < 0) {
|
||||
free(json_string);
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Write JSON body
|
||||
unsigned char *json_buf = malloc(LWS_PRE + json_len);
|
||||
if (!json_buf) {
|
||||
free(json_string);
|
||||
return -1;
|
||||
}
|
||||
session_data->headers_sent = 1;
|
||||
|
||||
memcpy(json_buf + LWS_PRE, json_string, json_len);
|
||||
if (lws_write(wsi, json_buf + LWS_PRE, json_len, LWS_WRITE_HTTP) < 0) {
|
||||
free(json_string);
|
||||
free(json_buf);
|
||||
return -1;
|
||||
}
|
||||
// Request callback for body transmission
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
free(json_buf);
|
||||
|
||||
free(json_string);
|
||||
log_success("NIP-11 relay information served successfully");
|
||||
log_success("NIP-11 headers sent, body transmission scheduled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3163,7 +3184,49 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
||||
}
|
||||
|
||||
case LWS_CALLBACK_HTTP_WRITEABLE:
|
||||
// HTTP response continuation if needed
|
||||
// Handle NIP-11 HTTP body transmission with proper buffer management
|
||||
{
|
||||
struct nip11_session_data* session_data = (struct nip11_session_data*)lws_wsi_user(wsi);
|
||||
if (session_data && session_data->headers_sent && !session_data->body_sent) {
|
||||
// Allocate buffer for JSON body transmission
|
||||
unsigned char *json_buf = malloc(LWS_PRE + session_data->json_length);
|
||||
if (!json_buf) {
|
||||
log_error("Failed to allocate buffer for NIP-11 body transmission");
|
||||
// Clean up session data
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
lws_set_wsi_user(wsi, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy JSON data to buffer
|
||||
memcpy(json_buf + LWS_PRE, session_data->json_buffer, session_data->json_length);
|
||||
|
||||
// Write JSON body
|
||||
int write_result = lws_write(wsi, json_buf + LWS_PRE, session_data->json_length, LWS_WRITE_HTTP);
|
||||
|
||||
// Free the transmission buffer immediately (it's been copied by libwebsockets)
|
||||
free(json_buf);
|
||||
|
||||
if (write_result < 0) {
|
||||
log_error("Failed to write NIP-11 JSON body");
|
||||
// Clean up session data
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
lws_set_wsi_user(wsi, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Mark body as sent and clean up session data
|
||||
session_data->body_sent = 1;
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
lws_set_wsi_user(wsi, NULL);
|
||||
|
||||
log_success("NIP-11 relay information served successfully");
|
||||
return 0; // Close connection after successful transmission
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
|
||||
Reference in New Issue
Block a user