|
|
|
|
@@ -36,9 +36,13 @@ extern const char* get_tag_value(cJSON* event, const char* tag_name, int value_i
|
|
|
|
|
// Forward declarations for config functions
|
|
|
|
|
extern const char* get_relay_pubkey_cached(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
|
|
|
|
|
extern int store_event(cJSON* event);
|
|
|
|
|
extern int broadcast_event_to_subscriptions(cJSON* event);
|
|
|
|
|
|
|
|
|
|
// Forward declarations for stats generation
|
|
|
|
|
extern char* generate_stats_json(void);
|
|
|
|
|
@@ -315,6 +319,224 @@ char* generate_stats_json(void) {
|
|
|
|
|
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
|
|
|
|
|
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) {
|
|
|
|
|
@@ -375,8 +597,50 @@ cJSON* process_nip17_admin_message(cJSON* gift_wrap_event, char* error_message,
|
|
|
|
|
// Step 4: Process admin command
|
|
|
|
|
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) {
|
|
|
|
|
// 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
|
|
|
|
|
cJSON* sender_pubkey_obj = cJSON_GetObjectItem(inner_dm, "pubkey");
|
|
|
|
|
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: Action: Generate and send relay statistics");
|
|
|
|
|
|
|
|
|
|
// Generate stats JSON
|
|
|
|
|
char* stats_json = generate_stats_json();
|
|
|
|
|
if (!stats_json) {
|
|
|
|
|
log_error("NIP-17: Failed to generate stats for plain text command");
|
|
|
|
|
char* stats_text = generate_stats_text();
|
|
|
|
|
if (!stats_text) {
|
|
|
|
|
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) {
|
|
|
|
|
free(stats_json);
|
|
|
|
|
log_error("NIP-17: Could not get relay keys for stats response");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
char error_msg[256];
|
|
|
|
|
int result = send_nip17_response(sender_pubkey, stats_text, error_msg, sizeof(error_msg));
|
|
|
|
|
free(stats_text);
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
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");
|
|
|
|
|
if (result != 0) {
|
|
|
|
|
log_error(error_msg);
|
|
|
|
|
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: Action: Generate and send relay configuration");
|
|
|
|
|
|
|
|
|
|
// Get relay pubkey for config response
|
|
|
|
|
const char* relay_pubkey = get_relay_pubkey_cached();
|
|
|
|
|
|
|
|
|
|
// 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");
|
|
|
|
|
char* config_text = generate_config_text();
|
|
|
|
|
if (!config_text) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get relay keys for signing
|
|
|
|
|
char* relay_privkey_hex = get_relay_private_key();
|
|
|
|
|
if (!relay_privkey_hex) {
|
|
|
|
|
free(config_json);
|
|
|
|
|
log_error("NIP-17: Could not get relay private key for config response");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
char error_msg[256];
|
|
|
|
|
int result = send_nip17_response(sender_pubkey, config_text, error_msg, sizeof(error_msg));
|
|
|
|
|
free(config_text);
|
|
|
|
|
|
|
|
|
|
// 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(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");
|
|
|
|
|
if (result != 0) {
|
|
|
|
|
log_error(error_msg);
|
|
|
|
|
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) {
|
|
|
|
|
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();
|
|
|
|
|
if (!stats_json) {
|
|
|
|
|
cJSON_Delete(command_array);
|
|
|
|
|
@@ -822,114 +894,17 @@ int process_nip17_admin_command(cJSON* dm_event, char* error_message, size_t err
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get sender pubkey for response
|
|
|
|
|
cJSON* sender_pubkey_obj = cJSON_GetObjectItem(dm_event, "pubkey");
|
|
|
|
|
if (!sender_pubkey_obj || !cJSON_IsString(sender_pubkey_obj)) {
|
|
|
|
|
char error_msg[256];
|
|
|
|
|
int result = send_nip17_response(sender_pubkey, stats_json, error_msg, sizeof(error_msg));
|
|
|
|
|
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);
|
|
|
|
|
if (result != 0) {
|
|
|
|
|
log_error(error_msg);
|
|
|
|
|
strncpy(error_message, error_msg, 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);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
log_success("NIP-17: Stats command processed successfully");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|