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;
|
padding: 8px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-family: var(--font-family);
|
font-family: var(--font-family);
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-table-container {
|
.config-table-container {
|
||||||
@@ -509,7 +509,7 @@
|
|||||||
<label for="relay-pubkey-manual">Relay Pubkey (if not available via NIP-11):</label>
|
<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"
|
<input type="text" id="relay-pubkey-manual" placeholder="64-character hex pubkey"
|
||||||
pattern="[0-9a-fA-F]{64}" title="64-character hexadecimal public key">
|
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>
|
||||||
|
|
||||||
<div class="inline-buttons">
|
<div class="inline-buttons">
|
||||||
@@ -546,116 +546,101 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Testing Section -->
|
<!-- Testing Section -->
|
||||||
<div id="div_config" class="section flex-section">
|
<div id="div_config" class="section flex-section">
|
||||||
|
<h2>RELAY CONFIGURATION</h2>
|
||||||
<div id="config-display" class="hidden">
|
<div id="config-display" class="hidden">
|
||||||
<div id="config-view-mode">
|
<div id="config-view-mode">
|
||||||
<div class="config-table-container">
|
<div class="config-table-container">
|
||||||
<table class="config-table" id="config-table">
|
<table class="config-table" id="config-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Parameter</th>
|
<th>Parameter</th>
|
||||||
<th>Value</th>
|
<th>Value</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="config-table-body">
|
<tbody id="config-table-body">
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</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>
|
|
||||||
</div>
|
</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">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Auth Rules Table -->
|
<div id="config-edit-mode" class="hidden">
|
||||||
<div id="authRulesTableContainer" style="display: none;">
|
<h3>Edit Configuration</h3>
|
||||||
<table class="config-table" id="authRulesTable">
|
<div id="config-form" class="section">
|
||||||
<thead>
|
<!-- Dynamic form will be generated here -->
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
</div>
|
<div class="inline-buttons">
|
||||||
|
<button type="button" id="save-config-btn">SAVE & PUBLISH</button>
|
||||||
<!-- Auth Rules Status Display -->
|
<button type="button" id="cancel-edit-btn">CANCEL</button>
|
||||||
<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>
|
</div>
|
||||||
</div>
|
</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 persistentUserPubkey = document.getElementById('persistent-user-pubkey');
|
||||||
const persistentUserAbout = document.getElementById('persistent-user-about');
|
const persistentUserAbout = document.getElementById('persistent-user-about');
|
||||||
const persistentUserDetails = document.getElementById('persistent-user-details');
|
const persistentUserDetails = document.getElementById('persistent-user-details');
|
||||||
const relayUrl = document.getElementById('relay-url');
|
|
||||||
const fetchConfigBtn = document.getElementById('fetch-config-btn');
|
const fetchConfigBtn = document.getElementById('fetch-config-btn');
|
||||||
// Relay connection elements
|
// Relay connection elements
|
||||||
const relayConnectionUrl = document.getElementById('relay-connection-url');
|
const relayConnectionUrl = document.getElementById('relay-connection-url');
|
||||||
@@ -992,10 +976,7 @@
|
|||||||
// Step 4: Update UI
|
// Step 4: Update UI
|
||||||
updateRelayConnectionStatus('connected');
|
updateRelayConnectionStatus('connected');
|
||||||
|
|
||||||
// Step 5: Update the old relay URL field for backward compatibility
|
// Step 5: Relay URL updated
|
||||||
if (relayUrl) {
|
|
||||||
relayUrl.value = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 6: Automatically load configuration and auth rules
|
// Step 6: Automatically load configuration and auth rules
|
||||||
log('Relay connected successfully. Auto-loading configuration and auth rules...', 'INFO');
|
log('Relay connected successfully. Auto-loading configuration and auth rules...', 'INFO');
|
||||||
@@ -1308,7 +1289,7 @@
|
|||||||
function disconnectFromRelay() {
|
function disconnectFromRelay() {
|
||||||
if (relayPool) {
|
if (relayPool) {
|
||||||
console.log('Cleaning up relay pool connection...');
|
console.log('Cleaning up relay pool connection...');
|
||||||
const url = relayUrl.value.trim();
|
const url = relayConnectionUrl.value.trim();
|
||||||
if (url) {
|
if (url) {
|
||||||
relayPool.close([url]);
|
relayPool.close([url]);
|
||||||
}
|
}
|
||||||
@@ -1392,7 +1373,7 @@
|
|||||||
// Clean up configuration pool
|
// Clean up configuration pool
|
||||||
if (relayPool) {
|
if (relayPool) {
|
||||||
console.log('Closing configuration pool...');
|
console.log('Closing configuration pool...');
|
||||||
const url = relayUrl.value.trim();
|
const url = relayConnectionUrl.value.trim();
|
||||||
if (url) {
|
if (url) {
|
||||||
relayPool.close([url]);
|
relayPool.close([url]);
|
||||||
}
|
}
|
||||||
@@ -1760,7 +1741,7 @@
|
|||||||
currentAuthRules = responseData.data;
|
currentAuthRules = responseData.data;
|
||||||
console.log('Updated currentAuthRules with', currentAuthRules.length, 'rules');
|
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...');
|
console.log('Auto-showing auth rules table since we received data...');
|
||||||
showAuthRulesTable();
|
showAuthRulesTable();
|
||||||
|
|
||||||
@@ -1770,7 +1751,7 @@
|
|||||||
currentAuthRules = [];
|
currentAuthRules = [];
|
||||||
console.log('No auth rules data received, cleared 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...');
|
console.log('Auto-showing auth rules table with empty data...');
|
||||||
showAuthRulesTable();
|
showAuthRulesTable();
|
||||||
|
|
||||||
@@ -2275,7 +2256,7 @@
|
|||||||
console.log(`Config update event signed with ${configObjects.length} objects`);
|
console.log(`Config update event signed with ${configObjects.length} objects`);
|
||||||
|
|
||||||
// Publish via SimplePool with detailed error diagnostics
|
// Publish via SimplePool with detailed error diagnostics
|
||||||
const url = relayUrl.value.trim();
|
const url = relayConnectionUrl.value.trim();
|
||||||
const publishPromises = relayPool.publish([url], signedEvent);
|
const publishPromises = relayPool.publish([url], signedEvent);
|
||||||
|
|
||||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||||
@@ -2437,9 +2418,6 @@
|
|||||||
|
|
||||||
// DOM elements for auth rules
|
// DOM elements for auth rules
|
||||||
const authRulesSection = document.getElementById('authRulesSection');
|
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 refreshAuthRulesBtn = document.getElementById('refreshAuthRulesBtn');
|
||||||
const authRulesTableContainer = document.getElementById('authRulesTableContainer');
|
const authRulesTableContainer = document.getElementById('authRulesTableContainer');
|
||||||
const authRulesTableBody = document.getElementById('authRulesTableBody');
|
const authRulesTableBody = document.getElementById('authRulesTableBody');
|
||||||
@@ -2448,9 +2426,6 @@
|
|||||||
const authRuleFormTitle = document.getElementById('authRuleFormTitle');
|
const authRuleFormTitle = document.getElementById('authRuleFormTitle');
|
||||||
const saveAuthRuleBtn = document.getElementById('saveAuthRuleBtn');
|
const saveAuthRuleBtn = document.getElementById('saveAuthRuleBtn');
|
||||||
const cancelAuthRuleBtn = document.getElementById('cancelAuthRuleBtn');
|
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
|
// Show auth rules section after login
|
||||||
function showAuthRulesSection() {
|
function showAuthRulesSection() {
|
||||||
@@ -2473,9 +2448,6 @@
|
|||||||
if (authRuleFormContainer) {
|
if (authRuleFormContainer) {
|
||||||
authRuleFormContainer.style.display = 'none';
|
authRuleFormContainer.style.display = 'none';
|
||||||
}
|
}
|
||||||
if (authRulesStatusDisplay) {
|
|
||||||
authRulesStatusDisplay.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
currentAuthRules = [];
|
currentAuthRules = [];
|
||||||
editingAuthRule = null;
|
editingAuthRule = null;
|
||||||
@@ -2483,28 +2455,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update auth rules status indicator
|
// Update auth rules status indicator (removed - no status element)
|
||||||
function updateAuthRulesStatus(status) {
|
function updateAuthRulesStatus(status) {
|
||||||
if (!authRulesStatus) return;
|
// Status element removed - no-op
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load auth rules from relay using admin API
|
// Load auth rules from relay using admin API
|
||||||
@@ -2550,7 +2503,7 @@
|
|||||||
log('Sending auth rules query to relay...', 'INFO');
|
log('Sending auth rules query to relay...', 'INFO');
|
||||||
|
|
||||||
// Publish via SimplePool with detailed error diagnostics
|
// Publish via SimplePool with detailed error diagnostics
|
||||||
const url = relayUrl.value.trim();
|
const url = relayConnectionUrl.value.trim();
|
||||||
const publishPromises = relayPool.publish([url], signedEvent);
|
const publishPromises = relayPool.publish([url], signedEvent);
|
||||||
|
|
||||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||||
@@ -2636,31 +2589,21 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Update status display
|
// Update status display
|
||||||
if (authRulesCount) {
|
console.log(`Total Rules: ${rules.length}, Active Rules: ${rules.filter(r => r.enabled !== false).length}`);
|
||||||
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('=== END DISPLAY AUTH RULES DEBUG ===');
|
console.log('=== END DISPLAY AUTH RULES DEBUG ===');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show auth rules table
|
// Show auth rules table (automatically called when auth rules are loaded)
|
||||||
function showAuthRulesTable() {
|
function showAuthRulesTable() {
|
||||||
console.log('=== SHOW AUTH RULES TABLE DEBUG ===');
|
console.log('=== SHOW AUTH RULES TABLE DEBUG ===');
|
||||||
console.log('authRulesTableContainer element:', authRulesTableContainer);
|
console.log('authRulesTableContainer element:', authRulesTableContainer);
|
||||||
console.log('authRulesStatusDisplay element:', authRulesStatusDisplay);
|
|
||||||
console.log('Current display style:', authRulesTableContainer ? authRulesTableContainer.style.display : 'element not found');
|
console.log('Current display style:', authRulesTableContainer ? authRulesTableContainer.style.display : 'element not found');
|
||||||
|
|
||||||
if (authRulesTableContainer) {
|
if (authRulesTableContainer) {
|
||||||
authRulesTableContainer.style.display = 'block';
|
authRulesTableContainer.style.display = 'block';
|
||||||
console.log('Set authRulesTableContainer display to 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 we already have cached auth rules, display them immediately
|
||||||
if (currentAuthRules && currentAuthRules.length >= 0) {
|
if (currentAuthRules && currentAuthRules.length >= 0) {
|
||||||
console.log('Displaying cached auth rules:', currentAuthRules.length, 'rules');
|
console.log('Displaying cached auth rules:', currentAuthRules.length, 'rules');
|
||||||
@@ -2766,7 +2709,7 @@
|
|||||||
log('Sending delete auth rule command to relay...', 'INFO');
|
log('Sending delete auth rule command to relay...', 'INFO');
|
||||||
|
|
||||||
// Publish via SimplePool with detailed error diagnostics
|
// Publish via SimplePool with detailed error diagnostics
|
||||||
const url = relayUrl.value.trim();
|
const url = relayConnectionUrl.value.trim();
|
||||||
const publishPromises = relayPool.publish([url], signedEvent);
|
const publishPromises = relayPool.publish([url], signedEvent);
|
||||||
|
|
||||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||||
@@ -2917,20 +2860,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Auth rules event handlers
|
// 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) {
|
if (refreshAuthRulesBtn) {
|
||||||
refreshAuthRulesBtn.addEventListener('click', function (e) {
|
refreshAuthRulesBtn.addEventListener('click', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -2991,34 +2920,29 @@
|
|||||||
// Add blacklist rule (updated to use combined input)
|
// Add blacklist rule (updated to use combined input)
|
||||||
function addBlacklistRule() {
|
function addBlacklistRule() {
|
||||||
const input = document.getElementById('authRulePubkey');
|
const input = document.getElementById('authRulePubkey');
|
||||||
const statusDiv = document.getElementById('authRuleStatus');
|
|
||||||
|
|
||||||
if (!input || !statusDiv) return;
|
if (!input) return;
|
||||||
|
|
||||||
const inputValue = input.value.trim();
|
const inputValue = input.value.trim();
|
||||||
if (!inputValue) {
|
if (!inputValue) {
|
||||||
statusDiv.className = 'rule-status error';
|
log('Please enter a pubkey or nsec', 'ERROR');
|
||||||
statusDiv.textContent = 'Please enter a pubkey or nsec';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert nsec to hex if needed
|
// Convert nsec to hex if needed
|
||||||
const hexPubkey = nsecToHex(inputValue);
|
const hexPubkey = nsecToHex(inputValue);
|
||||||
if (!hexPubkey) {
|
if (!hexPubkey) {
|
||||||
statusDiv.className = 'rule-status error';
|
log('Invalid pubkey format. Please enter nsec1... or 64-character hex', 'ERROR');
|
||||||
statusDiv.textContent = 'Invalid pubkey format. Please enter nsec1... or 64-character hex';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate hex length
|
// Validate hex length
|
||||||
if (hexPubkey.length !== 64) {
|
if (hexPubkey.length !== 64) {
|
||||||
statusDiv.className = 'rule-status error';
|
log('Invalid pubkey length. Must be exactly 64 characters', 'ERROR');
|
||||||
statusDiv.textContent = 'Invalid pubkey length. Must be exactly 64 characters';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
statusDiv.className = 'rule-status';
|
log('Adding to blacklist...', 'INFO');
|
||||||
statusDiv.textContent = 'Adding to blacklist...';
|
|
||||||
|
|
||||||
// Create auth rule data
|
// Create auth rule data
|
||||||
const ruleData = {
|
const ruleData = {
|
||||||
@@ -3031,8 +2955,7 @@
|
|||||||
// Add to WebSocket queue for processing
|
// Add to WebSocket queue for processing
|
||||||
addAuthRuleViaWebSocket(ruleData)
|
addAuthRuleViaWebSocket(ruleData)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
statusDiv.className = 'rule-status success';
|
log(`Pubkey ${hexPubkey.substring(0, 16)}... added to blacklist`, 'INFO');
|
||||||
statusDiv.textContent = `Pubkey ${hexPubkey.substring(0, 16)}... added to blacklist`;
|
|
||||||
input.value = '';
|
input.value = '';
|
||||||
|
|
||||||
// Refresh auth rules display if visible
|
// Refresh auth rules display if visible
|
||||||
@@ -3041,38 +2964,33 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
statusDiv.className = 'rule-status error';
|
log(`Failed to add rule: ${error.message}`, 'ERROR');
|
||||||
statusDiv.textContent = `Failed to add rule: ${error.message}`;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add whitelist rule (updated to use combined input)
|
// Add whitelist rule (updated to use combined input)
|
||||||
function addWhitelistRule() {
|
function addWhitelistRule() {
|
||||||
const input = document.getElementById('authRulePubkey');
|
const input = document.getElementById('authRulePubkey');
|
||||||
const statusDiv = document.getElementById('authRuleStatus');
|
|
||||||
const warningDiv = document.getElementById('whitelistWarning');
|
const warningDiv = document.getElementById('whitelistWarning');
|
||||||
|
|
||||||
if (!input || !statusDiv) return;
|
if (!input) return;
|
||||||
|
|
||||||
const inputValue = input.value.trim();
|
const inputValue = input.value.trim();
|
||||||
if (!inputValue) {
|
if (!inputValue) {
|
||||||
statusDiv.className = 'rule-status error';
|
log('Please enter a pubkey or nsec', 'ERROR');
|
||||||
statusDiv.textContent = 'Please enter a pubkey or nsec';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert nsec to hex if needed
|
// Convert nsec to hex if needed
|
||||||
const hexPubkey = nsecToHex(inputValue);
|
const hexPubkey = nsecToHex(inputValue);
|
||||||
if (!hexPubkey) {
|
if (!hexPubkey) {
|
||||||
statusDiv.className = 'rule-status error';
|
log('Invalid pubkey format. Please enter nsec1... or 64-character hex', 'ERROR');
|
||||||
statusDiv.textContent = 'Invalid pubkey format. Please enter nsec1... or 64-character hex';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate hex length
|
// Validate hex length
|
||||||
if (hexPubkey.length !== 64) {
|
if (hexPubkey.length !== 64) {
|
||||||
statusDiv.className = 'rule-status error';
|
log('Invalid pubkey length. Must be exactly 64 characters', 'ERROR');
|
||||||
statusDiv.textContent = 'Invalid pubkey length. Must be exactly 64 characters';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3081,8 +2999,7 @@
|
|||||||
warningDiv.style.display = 'block';
|
warningDiv.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
statusDiv.className = 'rule-status';
|
log('Adding to whitelist...', 'INFO');
|
||||||
statusDiv.textContent = 'Adding to whitelist...';
|
|
||||||
|
|
||||||
// Create auth rule data
|
// Create auth rule data
|
||||||
const ruleData = {
|
const ruleData = {
|
||||||
@@ -3095,8 +3012,7 @@
|
|||||||
// Add to WebSocket queue for processing
|
// Add to WebSocket queue for processing
|
||||||
addAuthRuleViaWebSocket(ruleData)
|
addAuthRuleViaWebSocket(ruleData)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
statusDiv.className = 'rule-status success';
|
log(`Pubkey ${hexPubkey.substring(0, 16)}... added to whitelist`, 'INFO');
|
||||||
statusDiv.textContent = `Pubkey ${hexPubkey.substring(0, 16)}... added to whitelist`;
|
|
||||||
input.value = '';
|
input.value = '';
|
||||||
|
|
||||||
// Refresh auth rules display if visible
|
// Refresh auth rules display if visible
|
||||||
@@ -3105,8 +3021,7 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
statusDiv.className = 'rule-status error';
|
log(`Failed to add rule: ${error.message}`, 'ERROR');
|
||||||
statusDiv.textContent = `Failed to add rule: ${error.message}`;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3179,7 +3094,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Publish via SimplePool with detailed error diagnostics
|
// Publish via SimplePool with detailed error diagnostics
|
||||||
const url = relayUrl.value.trim();
|
const url = relayConnectionUrl.value.trim();
|
||||||
const publishPromises = relayPool.publish([url], signedEvent);
|
const publishPromises = relayPool.publish([url], signedEvent);
|
||||||
|
|
||||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
// 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');
|
logTestEvent('SENT', `Get Auth Rules event: ${JSON.stringify(signedEvent)}`, 'EVENT');
|
||||||
|
|
||||||
// Publish via SimplePool with detailed error diagnostics
|
// Publish via SimplePool with detailed error diagnostics
|
||||||
const url = relayUrl.value.trim();
|
const url = relayConnectionUrl.value.trim();
|
||||||
const publishPromises = relayPool.publish([url], signedEvent);
|
const publishPromises = relayPool.publish([url], signedEvent);
|
||||||
|
|
||||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
// 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');
|
logTestEvent('SENT', `Clear Auth Rules event: ${JSON.stringify(signedEvent)}`, 'EVENT');
|
||||||
|
|
||||||
// Publish via SimplePool with detailed error diagnostics
|
// Publish via SimplePool with detailed error diagnostics
|
||||||
const url = relayUrl.value.trim();
|
const url = relayConnectionUrl.value.trim();
|
||||||
const publishPromises = relayPool.publish([url], signedEvent);
|
const publishPromises = relayPool.publish([url], signedEvent);
|
||||||
|
|
||||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
// 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');
|
logTestEvent('SENT', `Add Blacklist event: ${JSON.stringify(signedEvent)}`, 'EVENT');
|
||||||
|
|
||||||
// Publish via SimplePool with detailed error diagnostics
|
// Publish via SimplePool with detailed error diagnostics
|
||||||
const url = relayUrl.value.trim();
|
const url = relayConnectionUrl.value.trim();
|
||||||
const publishPromises = relayPool.publish([url], signedEvent);
|
const publishPromises = relayPool.publish([url], signedEvent);
|
||||||
|
|
||||||
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
// Use Promise.allSettled to capture per-relay outcomes instead of Promise.any
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ while [[ $# -gt 0 ]]; do
|
|||||||
shift 2
|
shift 2
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
--preserve-database)
|
-d|--preserve-database)
|
||||||
PRESERVE_DATABASE=true
|
PRESERVE_DATABASE=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
|||||||
115
src/main.c
115
src/main.c
@@ -1716,7 +1716,15 @@ cJSON* generate_relay_info_json() {
|
|||||||
return info;
|
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) {
|
int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
|
||||||
log_info("Handling NIP-11 relay information request");
|
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);
|
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
|
// Prepare HTTP response with CORS headers
|
||||||
unsigned char buf[LWS_PRE + 1024];
|
unsigned char buf[LWS_PRE + 1024];
|
||||||
unsigned char *p = &buf[LWS_PRE];
|
unsigned char *p = &buf[LWS_PRE];
|
||||||
@@ -1813,70 +1838,66 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
|
|||||||
|
|
||||||
// Add status
|
// Add status
|
||||||
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add content type
|
// Add content type
|
||||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
||||||
(unsigned char*)"application/nostr+json", 22, &p, end)) {
|
(unsigned char*)"application/nostr+json", 22, &p, end)) {
|
||||||
free(json_string);
|
free(session_data->json_buffer);
|
||||||
|
free(session_data);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add content length
|
// Add content length
|
||||||
if (lws_add_http_header_content_length(wsi, json_len, &p, end)) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add CORS headers as required by NIP-11
|
// Add CORS headers as required by NIP-11
|
||||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-origin:",
|
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-origin:",
|
||||||
(unsigned char*)"*", 1, &p, end)) {
|
(unsigned char*)"*", 1, &p, end)) {
|
||||||
free(json_string);
|
free(session_data->json_buffer);
|
||||||
|
free(session_data);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-headers:",
|
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-headers:",
|
||||||
(unsigned char*)"content-type, accept", 20, &p, end)) {
|
(unsigned char*)"content-type, accept", 20, &p, end)) {
|
||||||
free(json_string);
|
free(session_data->json_buffer);
|
||||||
|
free(session_data);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-methods:",
|
if (lws_add_http_header_by_name(wsi, (unsigned char*)"access-control-allow-methods:",
|
||||||
(unsigned char*)"GET, OPTIONS", 12, &p, end)) {
|
(unsigned char*)"GET, OPTIONS", 12, &p, end)) {
|
||||||
free(json_string);
|
free(session_data->json_buffer);
|
||||||
|
free(session_data);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize headers
|
// Finalize headers
|
||||||
if (lws_finalize_http_header(wsi, &p, end)) {
|
if (lws_finalize_http_header(wsi, &p, end)) {
|
||||||
free(json_string);
|
free(session_data->json_buffer);
|
||||||
|
free(session_data);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write headers
|
// Write headers
|
||||||
if (lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS) < 0) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write JSON body
|
session_data->headers_sent = 1;
|
||||||
unsigned char *json_buf = malloc(LWS_PRE + json_len);
|
|
||||||
if (!json_buf) {
|
|
||||||
free(json_string);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(json_buf + LWS_PRE, json_string, json_len);
|
// Request callback for body transmission
|
||||||
if (lws_write(wsi, json_buf + LWS_PRE, json_len, LWS_WRITE_HTTP) < 0) {
|
lws_callback_on_writable(wsi);
|
||||||
free(json_string);
|
|
||||||
free(json_buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(json_buf);
|
log_success("NIP-11 headers sent, body transmission scheduled");
|
||||||
|
|
||||||
free(json_string);
|
|
||||||
log_success("NIP-11 relay information served successfully");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3163,7 +3184,49 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
}
|
}
|
||||||
|
|
||||||
case LWS_CALLBACK_HTTP_WRITEABLE:
|
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;
|
break;
|
||||||
|
|
||||||
case LWS_CALLBACK_ESTABLISHED:
|
case LWS_CALLBACK_ESTABLISHED:
|
||||||
|
|||||||
Reference in New Issue
Block a user