Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d655258311 |
@@ -3078,10 +3078,14 @@
|
|||||||
messageDiv.className = 'log-entry';
|
messageDiv.className = 'log-entry';
|
||||||
|
|
||||||
const directionColor = direction === 'sent' ? '#007bff' : '#28a745';
|
const directionColor = direction === 'sent' ? '#007bff' : '#28a745';
|
||||||
|
|
||||||
|
// Convert newlines to <br> tags for proper HTML display
|
||||||
|
const formattedMessage = message.replace(/\n/g, '<br>');
|
||||||
|
|
||||||
messageDiv.innerHTML = `
|
messageDiv.innerHTML = `
|
||||||
<span class="log-timestamp">${timestamp}</span>
|
<span class="log-timestamp">${timestamp}</span>
|
||||||
<span style="color: ${directionColor}; font-weight: bold;">[${direction.toUpperCase()}]</span>
|
<span style="color: ${directionColor}; font-weight: bold;">[${direction.toUpperCase()}]</span>
|
||||||
${message}
|
<span style="white-space: pre-wrap;">${formattedMessage}</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Remove the "No messages received yet" placeholder if it exists
|
// Remove the "No messages received yet" placeholder if it exists
|
||||||
|
|||||||
621
src/dm_admin.c
621
src/dm_admin.c
@@ -36,9 +36,13 @@ extern const char* get_tag_value(cJSON* event, const char* tag_name, int value_i
|
|||||||
// Forward declarations for config functions
|
// Forward declarations for config functions
|
||||||
extern const char* get_relay_pubkey_cached(void);
|
extern const char* get_relay_pubkey_cached(void);
|
||||||
extern char* get_relay_private_key(void);
|
extern char* get_relay_private_key(void);
|
||||||
|
extern const char* get_config_value(const char* key);
|
||||||
|
extern int get_config_bool(const char* key, int default_value);
|
||||||
|
extern const char* get_admin_pubkey_cached(void);
|
||||||
|
|
||||||
// Forward declarations for database functions
|
// Forward declarations for database functions
|
||||||
extern int store_event(cJSON* event);
|
extern int store_event(cJSON* event);
|
||||||
|
extern int broadcast_event_to_subscriptions(cJSON* event);
|
||||||
|
|
||||||
// Forward declarations for stats generation
|
// Forward declarations for stats generation
|
||||||
extern char* generate_stats_json(void);
|
extern char* generate_stats_json(void);
|
||||||
@@ -315,6 +319,224 @@ char* generate_stats_json(void) {
|
|||||||
return json_string;
|
return json_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified NIP-17 response sender - handles all common response logic
|
||||||
|
int send_nip17_response(const char* sender_pubkey, const char* response_content,
|
||||||
|
char* error_message, size_t error_size) {
|
||||||
|
if (!sender_pubkey || !response_content || !error_message) {
|
||||||
|
if (error_message) {
|
||||||
|
strncpy(error_message, "NIP-17: Invalid parameters for response", error_size - 1);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get relay keys for signing
|
||||||
|
const char* relay_pubkey = get_relay_pubkey_cached();
|
||||||
|
char* relay_privkey_hex = get_relay_private_key();
|
||||||
|
if (!relay_pubkey || !relay_privkey_hex) {
|
||||||
|
if (relay_privkey_hex) free(relay_privkey_hex);
|
||||||
|
strncpy(error_message, "NIP-17: Could not get relay keys for response", error_size - 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert relay private key to bytes
|
||||||
|
unsigned char relay_privkey[32];
|
||||||
|
if (nostr_hex_to_bytes(relay_privkey_hex, relay_privkey, sizeof(relay_privkey)) != 0) {
|
||||||
|
free(relay_privkey_hex);
|
||||||
|
strncpy(error_message, "NIP-17: Failed to convert relay private key for response", error_size - 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
free(relay_privkey_hex);
|
||||||
|
|
||||||
|
// Create DM response event using library function
|
||||||
|
cJSON* dm_response = nostr_nip17_create_chat_event(
|
||||||
|
response_content, // message content
|
||||||
|
(const char**)&sender_pubkey, // recipient pubkeys
|
||||||
|
1, // num recipients
|
||||||
|
NULL, // subject (optional)
|
||||||
|
NULL, // reply_to_event_id (optional)
|
||||||
|
NULL, // reply_relay_url (optional)
|
||||||
|
relay_pubkey // sender pubkey
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!dm_response) {
|
||||||
|
strncpy(error_message, "NIP-17: Failed to create DM response event", error_size - 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and sign gift wrap using library function
|
||||||
|
cJSON* gift_wraps[1];
|
||||||
|
int send_result = nostr_nip17_send_dm(
|
||||||
|
dm_response, // dm_event
|
||||||
|
(const char**)&sender_pubkey, // recipient_pubkeys
|
||||||
|
1, // num_recipients
|
||||||
|
relay_privkey, // sender_private_key
|
||||||
|
gift_wraps, // gift_wraps_out
|
||||||
|
1 // max_gift_wraps
|
||||||
|
);
|
||||||
|
|
||||||
|
cJSON_Delete(dm_response);
|
||||||
|
|
||||||
|
if (send_result != 1 || !gift_wraps[0]) {
|
||||||
|
strncpy(error_message, "NIP-17: Failed to create and sign response gift wrap", error_size - 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix the p tag in the gift wrap - library function may use wrong pubkey
|
||||||
|
cJSON* gift_wrap_tags = cJSON_GetObjectItem(gift_wraps[0], "tags");
|
||||||
|
if (gift_wrap_tags && cJSON_IsArray(gift_wrap_tags)) {
|
||||||
|
// Find and replace the p tag with the correct user pubkey
|
||||||
|
cJSON* tag = NULL;
|
||||||
|
cJSON_ArrayForEach(tag, gift_wrap_tags) {
|
||||||
|
if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
|
||||||
|
cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
|
||||||
|
if (tag_name && cJSON_IsString(tag_name) &&
|
||||||
|
strcmp(cJSON_GetStringValue(tag_name), "p") == 0) {
|
||||||
|
// Replace the p tag value with the correct user pubkey
|
||||||
|
cJSON_ReplaceItemInArray(tag, 1, cJSON_CreateString(sender_pubkey));
|
||||||
|
log_info("NIP-17: Fixed p tag in response gift wrap");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the gift wrap in database
|
||||||
|
int store_result = store_event(gift_wraps[0]);
|
||||||
|
|
||||||
|
if (store_result != 0) {
|
||||||
|
cJSON_Delete(gift_wraps[0]);
|
||||||
|
strncpy(error_message, "NIP-17: Failed to store response gift wrap", error_size - 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast the response event to active subscriptions
|
||||||
|
int broadcast_count = broadcast_event_to_subscriptions(gift_wraps[0]);
|
||||||
|
char debug_broadcast_msg[128];
|
||||||
|
snprintf(debug_broadcast_msg, sizeof(debug_broadcast_msg),
|
||||||
|
"NIP-17: Response broadcast to %d subscriptions", broadcast_count);
|
||||||
|
log_info(debug_broadcast_msg);
|
||||||
|
|
||||||
|
cJSON_Delete(gift_wraps[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate config text from database
|
||||||
|
char* generate_config_text(void) {
|
||||||
|
extern sqlite3* g_db;
|
||||||
|
if (!g_db) {
|
||||||
|
log_error("NIP-17: Database not available for config query");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build comprehensive config text from database
|
||||||
|
char* config_text = malloc(8192);
|
||||||
|
if (!config_text) {
|
||||||
|
log_error("NIP-17: Failed to allocate memory for config text");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
// Header
|
||||||
|
offset += snprintf(config_text + offset, 8192 - offset,
|
||||||
|
"🔧 Relay Configuration\n"
|
||||||
|
"━━━━━━━━━━━━━━━━━━━━━━━━\n");
|
||||||
|
|
||||||
|
// Query all config values from database
|
||||||
|
sqlite3_stmt* stmt;
|
||||||
|
if (sqlite3_prepare_v2(g_db, "SELECT key, value FROM config ORDER BY key", -1, &stmt, NULL) == SQLITE_OK) {
|
||||||
|
while (sqlite3_step(stmt) == SQLITE_ROW && offset < 8192 - 200) {
|
||||||
|
const char* key = (const char*)sqlite3_column_text(stmt, 0);
|
||||||
|
const char* value = (const char*)sqlite3_column_text(stmt, 1);
|
||||||
|
|
||||||
|
if (key && value) {
|
||||||
|
offset += snprintf(config_text + offset, 8192 - offset,
|
||||||
|
"%s: %s\n", key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
} else {
|
||||||
|
free(config_text);
|
||||||
|
log_error("NIP-17: Failed to query config from database");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
offset += snprintf(config_text + offset, 8192 - offset,
|
||||||
|
"\n✅ Configuration retrieved successfully");
|
||||||
|
|
||||||
|
return config_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate human-readable stats text
|
||||||
|
char* generate_stats_text(void) {
|
||||||
|
char* stats_json = generate_stats_json();
|
||||||
|
if (!stats_json) {
|
||||||
|
log_error("NIP-17: Failed to generate stats for plain text command");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the JSON to extract values for human-readable format
|
||||||
|
cJSON* stats_obj = cJSON_Parse(stats_json);
|
||||||
|
char* stats_text = malloc(4096);
|
||||||
|
if (!stats_text) {
|
||||||
|
free(stats_json);
|
||||||
|
if (stats_obj) cJSON_Delete(stats_obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats_obj) {
|
||||||
|
cJSON* total_events = cJSON_GetObjectItem(stats_obj, "total_events");
|
||||||
|
cJSON* db_size = cJSON_GetObjectItem(stats_obj, "database_size_bytes");
|
||||||
|
cJSON* time_stats = cJSON_GetObjectItem(stats_obj, "time_stats");
|
||||||
|
|
||||||
|
long long total = total_events ? (long long)cJSON_GetNumberValue(total_events) : 0;
|
||||||
|
long long db_bytes = db_size ? (long long)cJSON_GetNumberValue(db_size) : 0;
|
||||||
|
double db_mb = db_bytes / (1024.0 * 1024.0);
|
||||||
|
|
||||||
|
long long last_24h = 0, last_7d = 0, last_30d = 0;
|
||||||
|
if (time_stats) {
|
||||||
|
cJSON* h24 = cJSON_GetObjectItem(time_stats, "last_24h");
|
||||||
|
cJSON* d7 = cJSON_GetObjectItem(time_stats, "last_7d");
|
||||||
|
cJSON* d30 = cJSON_GetObjectItem(time_stats, "last_30d");
|
||||||
|
last_24h = h24 ? (long long)cJSON_GetNumberValue(h24) : 0;
|
||||||
|
last_7d = d7 ? (long long)cJSON_GetNumberValue(d7) : 0;
|
||||||
|
last_30d = d30 ? (long long)cJSON_GetNumberValue(d30) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(stats_text, 4096,
|
||||||
|
"📊 Relay Statistics\n"
|
||||||
|
"━━━━━━━━━━━━━━━━━━━━\n"
|
||||||
|
"Total Events: %lld\n"
|
||||||
|
"Database Size: %.2f MB (%lld bytes)\n"
|
||||||
|
"\n"
|
||||||
|
"📈 Recent Activity\n"
|
||||||
|
"━━━━━━━━━━━━━━━━━━━\n"
|
||||||
|
"Last 24 hours: %lld events\n"
|
||||||
|
"Last 7 days: %lld events\n"
|
||||||
|
"Last 30 days: %lld events\n"
|
||||||
|
"\n"
|
||||||
|
"✅ Statistics retrieved successfully",
|
||||||
|
total, db_mb, db_bytes, last_24h, last_7d, last_30d
|
||||||
|
);
|
||||||
|
|
||||||
|
cJSON_Delete(stats_obj);
|
||||||
|
} else {
|
||||||
|
// Fallback if JSON parsing fails
|
||||||
|
snprintf(stats_text, 4096,
|
||||||
|
"📊 Relay Statistics\n"
|
||||||
|
"━━━━━━━━━━━━━━━━━━━━\n"
|
||||||
|
"Raw data: %s\n"
|
||||||
|
"\n"
|
||||||
|
"⚠️ Could not parse statistics data",
|
||||||
|
stats_json
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(stats_json);
|
||||||
|
return stats_text;
|
||||||
|
}
|
||||||
|
|
||||||
// Main NIP-17 processing function
|
// Main NIP-17 processing function
|
||||||
cJSON* process_nip17_admin_message(cJSON* gift_wrap_event, char* error_message, size_t error_size, struct lws* wsi) {
|
cJSON* process_nip17_admin_message(cJSON* gift_wrap_event, char* error_message, size_t error_size, struct lws* wsi) {
|
||||||
if (!gift_wrap_event || !error_message) {
|
if (!gift_wrap_event || !error_message) {
|
||||||
@@ -375,8 +597,50 @@ cJSON* process_nip17_admin_message(cJSON* gift_wrap_event, char* error_message,
|
|||||||
// Step 4: Process admin command
|
// Step 4: Process admin command
|
||||||
int result = process_nip17_admin_command(inner_dm, error_message, error_size, wsi);
|
int result = process_nip17_admin_command(inner_dm, error_message, error_size, wsi);
|
||||||
|
|
||||||
// Step 5: Create response if command was processed successfully
|
// Step 5: For plain text commands (stats/config), the response is already handled
|
||||||
|
// Only create a generic response for other command types that don't handle their own responses
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
|
// Extract content to check if it's a plain text command
|
||||||
|
cJSON* content_obj = cJSON_GetObjectItem(inner_dm, "content");
|
||||||
|
if (content_obj && cJSON_IsString(content_obj)) {
|
||||||
|
const char* dm_content = cJSON_GetStringValue(content_obj);
|
||||||
|
|
||||||
|
// Check if it's a plain text command that already handled its response
|
||||||
|
char content_lower[256];
|
||||||
|
size_t content_len = strlen(dm_content);
|
||||||
|
size_t copy_len = content_len < sizeof(content_lower) - 1 ? content_len : sizeof(content_lower) - 1;
|
||||||
|
memcpy(content_lower, dm_content, copy_len);
|
||||||
|
content_lower[copy_len] = '\0';
|
||||||
|
|
||||||
|
// Convert to lowercase
|
||||||
|
for (size_t i = 0; i < copy_len; i++) {
|
||||||
|
if (content_lower[i] >= 'A' && content_lower[i] <= 'Z') {
|
||||||
|
content_lower[i] = content_lower[i] + 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a plain text stats or config command, don't create additional response
|
||||||
|
if (strstr(content_lower, "stats") != NULL || strstr(content_lower, "statistics") != NULL ||
|
||||||
|
strstr(content_lower, "config") != NULL || strstr(content_lower, "configuration") != NULL) {
|
||||||
|
log_info("NIP-17: Plain text command already handled response, skipping generic response");
|
||||||
|
cJSON_Delete(inner_dm);
|
||||||
|
return NULL; // No additional response needed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's a JSON array command that might be stats
|
||||||
|
cJSON* command_array = cJSON_Parse(dm_content);
|
||||||
|
if (command_array && cJSON_IsArray(command_array) && cJSON_GetArraySize(command_array) > 0) {
|
||||||
|
cJSON* first_item = cJSON_GetArrayItem(command_array, 0);
|
||||||
|
if (cJSON_IsString(first_item) && strcmp(cJSON_GetStringValue(first_item), "stats") == 0) {
|
||||||
|
log_info("NIP-17: JSON stats command already handled response, skipping generic response");
|
||||||
|
cJSON_Delete(command_array);
|
||||||
|
cJSON_Delete(inner_dm);
|
||||||
|
return NULL; // No additional response needed
|
||||||
|
}
|
||||||
|
cJSON_Delete(command_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get sender pubkey for response from the decrypted DM event
|
// Get sender pubkey for response from the decrypted DM event
|
||||||
cJSON* sender_pubkey_obj = cJSON_GetObjectItem(inner_dm, "pubkey");
|
cJSON* sender_pubkey_obj = cJSON_GetObjectItem(inner_dm, "pubkey");
|
||||||
if (sender_pubkey_obj && cJSON_IsString(sender_pubkey_obj)) {
|
if (sender_pubkey_obj && cJSON_IsString(sender_pubkey_obj)) {
|
||||||
@@ -557,109 +821,17 @@ int process_nip17_admin_command(cJSON* dm_event, char* error_message, size_t err
|
|||||||
log_info("NIP-17: Recognized plain text 'stats' command from admin");
|
log_info("NIP-17: Recognized plain text 'stats' command from admin");
|
||||||
log_info("NIP-17: Action: Generate and send relay statistics");
|
log_info("NIP-17: Action: Generate and send relay statistics");
|
||||||
|
|
||||||
// Generate stats JSON
|
char* stats_text = generate_stats_text();
|
||||||
char* stats_json = generate_stats_json();
|
if (!stats_text) {
|
||||||
if (!stats_json) {
|
|
||||||
log_error("NIP-17: Failed to generate stats for plain text command");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get relay keys for signing
|
char error_msg[256];
|
||||||
const char* relay_pubkey = get_relay_pubkey_cached();
|
int result = send_nip17_response(sender_pubkey, stats_text, error_msg, sizeof(error_msg));
|
||||||
char* relay_privkey_hex = get_relay_private_key();
|
free(stats_text);
|
||||||
if (!relay_pubkey || !relay_privkey_hex) {
|
|
||||||
free(stats_json);
|
|
||||||
log_error("NIP-17: Could not get relay keys for stats response");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert relay private key to bytes
|
if (result != 0) {
|
||||||
unsigned char relay_privkey[32];
|
log_error(error_msg);
|
||||||
if (nostr_hex_to_bytes(relay_privkey_hex, relay_privkey, sizeof(relay_privkey)) != 0) {
|
|
||||||
free(stats_json);
|
|
||||||
free(relay_privkey_hex);
|
|
||||||
log_error("NIP-17: Failed to convert relay private key for stats response");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
free(relay_privkey_hex);
|
|
||||||
|
|
||||||
// Create DM response event using library function
|
|
||||||
cJSON* dm_response = nostr_nip17_create_chat_event(
|
|
||||||
stats_json, // message content
|
|
||||||
(const char**)&sender_pubkey, // recipient pubkeys
|
|
||||||
1, // num recipients
|
|
||||||
NULL, // subject (optional)
|
|
||||||
NULL, // reply_to_event_id (optional)
|
|
||||||
NULL, // reply_relay_url (optional)
|
|
||||||
relay_pubkey // sender pubkey
|
|
||||||
);
|
|
||||||
|
|
||||||
free(stats_json);
|
|
||||||
|
|
||||||
if (!dm_response) {
|
|
||||||
log_error("NIP-17: Failed to create DM response event for stats");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and sign gift wrap using library function
|
|
||||||
cJSON* gift_wraps[1];
|
|
||||||
int send_result = nostr_nip17_send_dm(
|
|
||||||
dm_response, // dm_event
|
|
||||||
(const char**)&sender_pubkey, // recipient_pubkeys
|
|
||||||
1, // num_recipients
|
|
||||||
relay_privkey, // sender_private_key
|
|
||||||
gift_wraps, // gift_wraps_out
|
|
||||||
1 // max_gift_wraps
|
|
||||||
);
|
|
||||||
|
|
||||||
cJSON_Delete(dm_response);
|
|
||||||
|
|
||||||
if (send_result != 1 || !gift_wraps[0]) {
|
|
||||||
log_error("NIP-17: Failed to create and sign response gift wrap for stats");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix the p tag in the gift wrap - library function may use wrong pubkey
|
|
||||||
cJSON* gift_wrap_tags = cJSON_GetObjectItem(gift_wraps[0], "tags");
|
|
||||||
if (gift_wrap_tags && cJSON_IsArray(gift_wrap_tags)) {
|
|
||||||
// Find and replace the p tag with the correct user pubkey
|
|
||||||
cJSON* tag = NULL;
|
|
||||||
cJSON_ArrayForEach(tag, gift_wrap_tags) {
|
|
||||||
if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
|
|
||||||
cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
|
|
||||||
if (tag_name && cJSON_IsString(tag_name) &&
|
|
||||||
strcmp(cJSON_GetStringValue(tag_name), "p") == 0) {
|
|
||||||
// Replace the p tag value with the correct user pubkey
|
|
||||||
cJSON_ReplaceItemInArray(tag, 1, cJSON_CreateString(sender_pubkey));
|
|
||||||
log_info("NIP-17: Fixed p tag in stats response gift wrap");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug print to show the response event
|
|
||||||
char* response_debug = cJSON_Print(gift_wraps[0]);
|
|
||||||
if (response_debug) {
|
|
||||||
log_info("DM Admin: Response event created");
|
|
||||||
printf(" Response event: %s\n", response_debug);
|
|
||||||
free(response_debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug: Print event before storing
|
|
||||||
char* debug_before_store = cJSON_Print(gift_wraps[0]);
|
|
||||||
if (debug_before_store) {
|
|
||||||
log_info("DEBUG EVENT: Before storing in database");
|
|
||||||
printf(" Event: %s\n", debug_before_store);
|
|
||||||
free(debug_before_store);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the gift wrap in database
|
|
||||||
int store_result = store_event(gift_wraps[0]);
|
|
||||||
cJSON_Delete(gift_wraps[0]);
|
|
||||||
|
|
||||||
if (store_result != 0) {
|
|
||||||
log_error("NIP-17: Failed to store response gift wrap for stats");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,126 +843,17 @@ int process_nip17_admin_command(cJSON* dm_event, char* error_message, size_t err
|
|||||||
log_info("NIP-17: Recognized plain text 'config' command from admin");
|
log_info("NIP-17: Recognized plain text 'config' command from admin");
|
||||||
log_info("NIP-17: Action: Generate and send relay configuration");
|
log_info("NIP-17: Action: Generate and send relay configuration");
|
||||||
|
|
||||||
// Get relay pubkey for config response
|
char* config_text = generate_config_text();
|
||||||
const char* relay_pubkey = get_relay_pubkey_cached();
|
if (!config_text) {
|
||||||
|
|
||||||
// Generate config JSON - for now, use a simple config summary
|
|
||||||
cJSON* config_response = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(config_response, "command", "config");
|
|
||||||
cJSON_AddStringToObject(config_response, "relay_pubkey", relay_pubkey ? relay_pubkey : "unknown");
|
|
||||||
|
|
||||||
// Add some basic config values
|
|
||||||
const char* port = get_config_value("relay_port");
|
|
||||||
const char* nip42_auth = get_config_bool("nip42_auth_required_events", 0) ? "enabled" : "disabled";
|
|
||||||
const char* nip42_sub = get_config_bool("nip42_auth_required_subscriptions", 0) ? "enabled" : "disabled";
|
|
||||||
|
|
||||||
cJSON_AddStringToObject(config_response, "relay_port", port ? port : "8888");
|
|
||||||
cJSON_AddStringToObject(config_response, "nip42_auth_events", nip42_auth);
|
|
||||||
cJSON_AddStringToObject(config_response, "nip42_auth_subscriptions", nip42_sub);
|
|
||||||
|
|
||||||
char* config_json = cJSON_Print(config_response);
|
|
||||||
cJSON_Delete(config_response);
|
|
||||||
|
|
||||||
if (!config_json) {
|
|
||||||
log_error("NIP-17: Failed to generate config JSON for plain text command");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get relay keys for signing
|
char error_msg[256];
|
||||||
char* relay_privkey_hex = get_relay_private_key();
|
int result = send_nip17_response(sender_pubkey, config_text, error_msg, sizeof(error_msg));
|
||||||
if (!relay_privkey_hex) {
|
free(config_text);
|
||||||
free(config_json);
|
|
||||||
log_error("NIP-17: Could not get relay private key for config response");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert relay private key to bytes
|
if (result != 0) {
|
||||||
unsigned char relay_privkey[32];
|
log_error(error_msg);
|
||||||
if (nostr_hex_to_bytes(relay_privkey_hex, relay_privkey, sizeof(relay_privkey)) != 0) {
|
|
||||||
free(config_json);
|
|
||||||
free(relay_privkey_hex);
|
|
||||||
log_error("NIP-17: Failed to convert relay private key for config response");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
free(relay_privkey_hex);
|
|
||||||
|
|
||||||
// Create DM response event using library function
|
|
||||||
cJSON* dm_response = nostr_nip17_create_chat_event(
|
|
||||||
config_json, // message content
|
|
||||||
(const char**)&sender_pubkey, // recipient pubkeys
|
|
||||||
1, // num recipients
|
|
||||||
NULL, // subject (optional)
|
|
||||||
NULL, // reply_to_event_id (optional)
|
|
||||||
NULL, // reply_relay_url (optional)
|
|
||||||
relay_pubkey // sender pubkey (already declared above)
|
|
||||||
);
|
|
||||||
|
|
||||||
free(config_json);
|
|
||||||
|
|
||||||
if (!dm_response) {
|
|
||||||
log_error("NIP-17: Failed to create DM response event for config");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and sign gift wrap using library function
|
|
||||||
cJSON* gift_wraps[1];
|
|
||||||
int send_result = nostr_nip17_send_dm(
|
|
||||||
dm_response, // dm_event
|
|
||||||
(const char**)&sender_pubkey, // recipient_pubkeys
|
|
||||||
1, // num_recipients
|
|
||||||
relay_privkey, // sender_private_key
|
|
||||||
gift_wraps, // gift_wraps_out
|
|
||||||
1 // max_gift_wraps
|
|
||||||
);
|
|
||||||
|
|
||||||
cJSON_Delete(dm_response);
|
|
||||||
|
|
||||||
if (send_result != 1 || !gift_wraps[0]) {
|
|
||||||
log_error("NIP-17: Failed to create and sign response gift wrap for config");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix the p tag in the gift wrap - library function may use wrong pubkey
|
|
||||||
cJSON* gift_wrap_tags = cJSON_GetObjectItem(gift_wraps[0], "tags");
|
|
||||||
if (gift_wrap_tags && cJSON_IsArray(gift_wrap_tags)) {
|
|
||||||
// Find and replace the p tag with the correct user pubkey
|
|
||||||
cJSON* tag = NULL;
|
|
||||||
cJSON_ArrayForEach(tag, gift_wrap_tags) {
|
|
||||||
if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
|
|
||||||
cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
|
|
||||||
if (tag_name && cJSON_IsString(tag_name) &&
|
|
||||||
strcmp(cJSON_GetStringValue(tag_name), "p") == 0) {
|
|
||||||
// Replace the p tag value with the correct user pubkey
|
|
||||||
cJSON_ReplaceItemInArray(tag, 1, cJSON_CreateString(sender_pubkey));
|
|
||||||
log_info("NIP-17: Fixed p tag in config response gift wrap");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug: Print event after p tag fix
|
|
||||||
char* debug_after_fix = cJSON_Print(gift_wraps[0]);
|
|
||||||
if (debug_after_fix) {
|
|
||||||
log_info("DEBUG EVENT: After p tag fix");
|
|
||||||
printf(" Event: %s\n", debug_after_fix);
|
|
||||||
free(debug_after_fix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug print to show the response event
|
|
||||||
char* response_debug = cJSON_Print(gift_wraps[0]);
|
|
||||||
if (response_debug) {
|
|
||||||
log_info("DM Admin: Response event created");
|
|
||||||
printf(" Response event: %s\n", response_debug);
|
|
||||||
free(response_debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the gift wrap in database
|
|
||||||
int store_result = store_event(gift_wraps[0]);
|
|
||||||
cJSON_Delete(gift_wraps[0]);
|
|
||||||
|
|
||||||
if (store_result != 0) {
|
|
||||||
log_error("NIP-17: Failed to store response gift wrap for config");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,7 +877,16 @@ int process_nip17_admin_command(cJSON* dm_event, char* error_message, size_t err
|
|||||||
if (cJSON_IsString(first_item) && strcmp(cJSON_GetStringValue(first_item), "stats") == 0) {
|
if (cJSON_IsString(first_item) && strcmp(cJSON_GetStringValue(first_item), "stats") == 0) {
|
||||||
log_info("NIP-17: Processing 'stats' command directly");
|
log_info("NIP-17: Processing 'stats' command directly");
|
||||||
|
|
||||||
// Generate stats JSON
|
// Get sender pubkey for response
|
||||||
|
cJSON* sender_pubkey_obj = cJSON_GetObjectItem(dm_event, "pubkey");
|
||||||
|
if (!sender_pubkey_obj || !cJSON_IsString(sender_pubkey_obj)) {
|
||||||
|
cJSON_Delete(command_array);
|
||||||
|
strncpy(error_message, "NIP-17: DM missing sender pubkey", error_size - 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const char* sender_pubkey = cJSON_GetStringValue(sender_pubkey_obj);
|
||||||
|
|
||||||
|
// Generate stats JSON (for JSON array commands, use JSON format)
|
||||||
char* stats_json = generate_stats_json();
|
char* stats_json = generate_stats_json();
|
||||||
if (!stats_json) {
|
if (!stats_json) {
|
||||||
cJSON_Delete(command_array);
|
cJSON_Delete(command_array);
|
||||||
@@ -822,114 +894,17 @@ int process_nip17_admin_command(cJSON* dm_event, char* error_message, size_t err
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get sender pubkey for response
|
char error_msg[256];
|
||||||
cJSON* sender_pubkey_obj = cJSON_GetObjectItem(dm_event, "pubkey");
|
int result = send_nip17_response(sender_pubkey, stats_json, error_msg, sizeof(error_msg));
|
||||||
if (!sender_pubkey_obj || !cJSON_IsString(sender_pubkey_obj)) {
|
|
||||||
free(stats_json);
|
|
||||||
cJSON_Delete(command_array);
|
|
||||||
strncpy(error_message, "NIP-17: DM missing sender pubkey", error_size - 1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const char* sender_pubkey = cJSON_GetStringValue(sender_pubkey_obj);
|
|
||||||
|
|
||||||
// Get relay keys for signing
|
|
||||||
const char* relay_pubkey = get_relay_pubkey_cached();
|
|
||||||
char* relay_privkey_hex = get_relay_private_key();
|
|
||||||
if (!relay_pubkey || !relay_privkey_hex) {
|
|
||||||
free(stats_json);
|
|
||||||
cJSON_Delete(command_array);
|
|
||||||
if (relay_privkey_hex) free(relay_privkey_hex);
|
|
||||||
strncpy(error_message, "NIP-17: Could not get relay keys", error_size - 1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert relay private key to bytes
|
|
||||||
unsigned char relay_privkey[32];
|
|
||||||
if (nostr_hex_to_bytes(relay_privkey_hex, relay_privkey, sizeof(relay_privkey)) != 0) {
|
|
||||||
free(stats_json);
|
|
||||||
free(relay_privkey_hex);
|
|
||||||
cJSON_Delete(command_array);
|
|
||||||
strncpy(error_message, "NIP-17: Failed to convert relay private key", error_size - 1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
free(relay_privkey_hex);
|
|
||||||
|
|
||||||
// Create DM response event using library function
|
|
||||||
cJSON* dm_response = nostr_nip17_create_chat_event(
|
|
||||||
stats_json, // message content
|
|
||||||
(const char**)&sender_pubkey, // recipient pubkeys
|
|
||||||
1, // num recipients
|
|
||||||
NULL, // subject (optional)
|
|
||||||
NULL, // reply_to_event_id (optional)
|
|
||||||
NULL, // reply_relay_url (optional)
|
|
||||||
relay_pubkey // sender pubkey
|
|
||||||
);
|
|
||||||
|
|
||||||
free(stats_json);
|
free(stats_json);
|
||||||
|
|
||||||
if (!dm_response) {
|
|
||||||
cJSON_Delete(command_array);
|
|
||||||
strncpy(error_message, "NIP-17: Failed to create DM response event", error_size - 1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and sign gift wrap using library function
|
|
||||||
cJSON* gift_wraps[1];
|
|
||||||
int send_result = nostr_nip17_send_dm(
|
|
||||||
dm_response, // dm_event
|
|
||||||
(const char**)&sender_pubkey, // recipient_pubkeys
|
|
||||||
1, // num_recipients
|
|
||||||
relay_privkey, // sender_private_key
|
|
||||||
gift_wraps, // gift_wraps_out
|
|
||||||
1 // max_gift_wraps
|
|
||||||
);
|
|
||||||
|
|
||||||
cJSON_Delete(dm_response);
|
|
||||||
|
|
||||||
if (send_result != 1 || !gift_wraps[0]) {
|
|
||||||
cJSON_Delete(command_array);
|
|
||||||
strncpy(error_message, "NIP-17: Failed to create and sign response gift wrap", error_size - 1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix the p tag in the gift wrap - library function may use wrong pubkey
|
|
||||||
cJSON* gift_wrap_tags = cJSON_GetObjectItem(gift_wraps[0], "tags");
|
|
||||||
if (gift_wrap_tags && cJSON_IsArray(gift_wrap_tags)) {
|
|
||||||
// Find and replace the p tag with the correct user pubkey
|
|
||||||
cJSON* tag = NULL;
|
|
||||||
cJSON_ArrayForEach(tag, gift_wrap_tags) {
|
|
||||||
if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
|
|
||||||
cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
|
|
||||||
if (tag_name && cJSON_IsString(tag_name) &&
|
|
||||||
strcmp(cJSON_GetStringValue(tag_name), "p") == 0) {
|
|
||||||
// Replace the p tag value with the correct user pubkey
|
|
||||||
cJSON_ReplaceItemInArray(tag, 1, cJSON_CreateString(sender_pubkey));
|
|
||||||
log_info("NIP-17: Fixed p tag in JSON array stats response gift wrap");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug print to show the response event
|
|
||||||
char* response_debug = cJSON_Print(gift_wraps[0]);
|
|
||||||
if (response_debug) {
|
|
||||||
log_info("DM Admin: Response event created");
|
|
||||||
printf(" Response event: %s\n", response_debug);
|
|
||||||
free(response_debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the gift wrap in database
|
|
||||||
int store_result = store_event(gift_wraps[0]);
|
|
||||||
cJSON_Delete(gift_wraps[0]);
|
|
||||||
|
|
||||||
if (store_result != 0) {
|
|
||||||
cJSON_Delete(command_array);
|
|
||||||
strncpy(error_message, "NIP-17: Failed to store response gift wrap", error_size - 1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON_Delete(command_array);
|
cJSON_Delete(command_array);
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
|
log_error(error_msg);
|
||||||
|
strncpy(error_message, error_msg, error_size - 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
log_success("NIP-17: Stats command processed successfully");
|
log_success("NIP-17: Stats command processed successfully");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,4 +18,10 @@ int process_nip17_admin_command(cJSON* dm_event, char* error_message, size_t err
|
|||||||
int is_nip17_gift_wrap_for_relay(cJSON* gift_wrap_event);
|
int is_nip17_gift_wrap_for_relay(cJSON* gift_wrap_event);
|
||||||
char* generate_stats_json(void);
|
char* generate_stats_json(void);
|
||||||
|
|
||||||
|
// Unified NIP-17 response functions
|
||||||
|
int send_nip17_response(const char* sender_pubkey, const char* response_content,
|
||||||
|
char* error_message, size_t error_size);
|
||||||
|
char* generate_config_text(void);
|
||||||
|
char* generate_stats_text(void);
|
||||||
|
|
||||||
#endif // DM_ADMIN_H
|
#endif // DM_ADMIN_H
|
||||||
File diff suppressed because one or more lines are too long
@@ -644,17 +644,32 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
cJSON* response_event = process_nip17_admin_message(event, nip17_error, sizeof(nip17_error), wsi);
|
cJSON* response_event = process_nip17_admin_message(event, nip17_error, sizeof(nip17_error), wsi);
|
||||||
|
|
||||||
if (!response_event) {
|
if (!response_event) {
|
||||||
log_error("DEBUG NIP17: NIP-17 admin message processing failed");
|
// Check if this is an error or if the command was already handled
|
||||||
result = -1;
|
if (strlen(nip17_error) > 0) {
|
||||||
size_t error_len = strlen(nip17_error);
|
// There was an actual error
|
||||||
size_t copy_len = (error_len < sizeof(error_message) - 1) ? error_len : sizeof(error_message) - 1;
|
log_error("DEBUG NIP17: NIP-17 admin message processing failed");
|
||||||
memcpy(error_message, nip17_error, copy_len);
|
result = -1;
|
||||||
error_message[copy_len] = '\0';
|
size_t error_len = strlen(nip17_error);
|
||||||
|
size_t copy_len = (error_len < sizeof(error_message) - 1) ? error_len : sizeof(error_message) - 1;
|
||||||
|
memcpy(error_message, nip17_error, copy_len);
|
||||||
|
error_message[copy_len] = '\0';
|
||||||
|
|
||||||
char debug_nip17_error_msg[600];
|
char debug_nip17_error_msg[600];
|
||||||
snprintf(debug_nip17_error_msg, sizeof(debug_nip17_error_msg),
|
snprintf(debug_nip17_error_msg, sizeof(debug_nip17_error_msg),
|
||||||
"DEBUG NIP17 ERROR: %.400s", nip17_error);
|
"DEBUG NIP17 ERROR: %.400s", nip17_error);
|
||||||
log_error(debug_nip17_error_msg);
|
log_error(debug_nip17_error_msg);
|
||||||
|
} else {
|
||||||
|
// No error message means the command was already handled (plain text commands)
|
||||||
|
log_success("DEBUG NIP17: NIP-17 admin message processed successfully (already handled)");
|
||||||
|
// Store the original gift wrap event in database
|
||||||
|
if (store_event(event) != 0) {
|
||||||
|
log_error("DEBUG NIP17: Failed to store gift wrap event in database");
|
||||||
|
result = -1;
|
||||||
|
strncpy(error_message, "error: failed to store gift wrap event", sizeof(error_message) - 1);
|
||||||
|
} else {
|
||||||
|
log_info("DEBUG NIP17: Gift wrap event stored successfully in database");
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log_success("DEBUG NIP17: NIP-17 admin message processed successfully");
|
log_success("DEBUG NIP17: NIP-17 admin message processed successfully");
|
||||||
// Store the original gift wrap event in database (unlike kind 23456)
|
// Store the original gift wrap event in database (unlike kind 23456)
|
||||||
|
|||||||
Reference in New Issue
Block a user