v0.3.19 - last save before major refactoring

This commit is contained in:
Your Name
2025-09-30 10:47:11 -04:00
parent eefb0e427e
commit c1a6e92b1d
4 changed files with 207 additions and 229 deletions

View File

@@ -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

View File

@@ -63,7 +63,7 @@ while [[ $# -gt 0 ]]; do
shift 2
fi
;;
--preserve-database)
-d|--preserve-database)
PRESERVE_DATABASE=true
shift
;;

View File

@@ -1 +1 @@
1371445
1412551

View File

@@ -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: