v0.7.40 - Removed event_broadcasts table and related code to fix FOREIGN KEY constraint failures preventing event insertion
This commit is contained in:
423
src/api.c
423
src/api.c
@@ -13,6 +13,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#include <stdbool.h>
|
||||
#include "api.h"
|
||||
#include "embedded_web_content.h"
|
||||
#include "config.h"
|
||||
@@ -222,79 +223,6 @@ cJSON* query_top_pubkeys(void) {
|
||||
return top_pubkeys;
|
||||
}
|
||||
|
||||
// Query active subscriptions summary from database
|
||||
cJSON* query_active_subscriptions(void) {
|
||||
extern sqlite3* g_db;
|
||||
if (!g_db) {
|
||||
DEBUG_ERROR("Database not available for active subscriptions query");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get configuration limits
|
||||
int max_subs = g_subscription_manager.max_total_subscriptions;
|
||||
int max_per_client = g_subscription_manager.max_subscriptions_per_client;
|
||||
|
||||
// Query total active subscriptions from database
|
||||
sqlite3_stmt* stmt;
|
||||
const char* sql =
|
||||
"SELECT COUNT(*) as total_subs, "
|
||||
"COUNT(DISTINCT client_ip) as client_count "
|
||||
"FROM subscriptions "
|
||||
"WHERE event_type = 'created' AND ended_at IS NULL";
|
||||
|
||||
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to prepare active subscriptions query");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int total_subs = 0;
|
||||
int client_count = 0;
|
||||
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
total_subs = sqlite3_column_int(stmt, 0);
|
||||
client_count = sqlite3_column_int(stmt, 1);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
// Query max subscriptions per client
|
||||
int most_subs_per_client = 0;
|
||||
const char* max_sql =
|
||||
"SELECT MAX(sub_count) FROM ("
|
||||
" SELECT COUNT(*) as sub_count "
|
||||
" FROM subscriptions "
|
||||
" WHERE event_type = 'created' AND ended_at IS NULL "
|
||||
" GROUP BY client_ip"
|
||||
")";
|
||||
|
||||
if (sqlite3_prepare_v2(g_db, max_sql, -1, &stmt, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
most_subs_per_client = sqlite3_column_int(stmt, 0);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
// Calculate statistics
|
||||
double utilization_percentage = max_subs > 0 ? (total_subs * 100.0 / max_subs) : 0.0;
|
||||
double avg_subs_per_client = client_count > 0 ? (total_subs * 1.0 / client_count) : 0.0;
|
||||
|
||||
// Build JSON response matching the design spec
|
||||
cJSON* subscriptions = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(subscriptions, "data_type", "active_subscriptions");
|
||||
cJSON_AddNumberToObject(subscriptions, "timestamp", (double)time(NULL));
|
||||
|
||||
cJSON* data = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(data, "total_subscriptions", total_subs);
|
||||
cJSON_AddNumberToObject(data, "max_subscriptions", max_subs);
|
||||
cJSON_AddNumberToObject(data, "utilization_percentage", utilization_percentage);
|
||||
cJSON_AddNumberToObject(data, "subscriptions_per_client_avg", avg_subs_per_client);
|
||||
cJSON_AddNumberToObject(data, "most_subscriptions_per_client", most_subs_per_client);
|
||||
cJSON_AddNumberToObject(data, "max_subscriptions_per_client", max_per_client);
|
||||
cJSON_AddNumberToObject(data, "active_clients", client_count);
|
||||
|
||||
cJSON_AddItemToObject(subscriptions, "data", data);
|
||||
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
// Query detailed subscription information from database log (ADMIN ONLY)
|
||||
// Uses subscriptions table instead of in-memory iteration to avoid mutex contention
|
||||
@@ -305,16 +233,18 @@ cJSON* query_subscription_details(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Query active subscriptions directly from subscriptions table
|
||||
// Get subscriptions that were created but not yet closed/expired/disconnected
|
||||
// Query active subscriptions from the active_subscriptions_log view
|
||||
// This view properly handles deduplication of closed/expired subscriptions
|
||||
sqlite3_stmt* stmt;
|
||||
const char* sql =
|
||||
"SELECT subscription_id, client_ip, wsi_pointer, filter_json, events_sent, "
|
||||
"created_at, (strftime('%s', 'now') - created_at) as duration_seconds "
|
||||
"FROM subscriptions "
|
||||
"WHERE event_type = 'created' AND ended_at IS NULL "
|
||||
"SELECT * "
|
||||
"FROM active_subscriptions_log "
|
||||
"ORDER BY created_at DESC LIMIT 100";
|
||||
|
||||
// DEBUG: Log the query results for debugging subscription_details
|
||||
DEBUG_LOG("=== SUBSCRIPTION_DETAILS QUERY DEBUG ===");
|
||||
DEBUG_LOG("Query: %s", sql);
|
||||
|
||||
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to prepare subscription details query");
|
||||
return NULL;
|
||||
@@ -329,22 +259,27 @@ cJSON* query_subscription_details(void) {
|
||||
cJSON* subscriptions_array = cJSON_CreateArray();
|
||||
|
||||
// Iterate through query results
|
||||
int row_count = 0;
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
row_count++;
|
||||
cJSON* sub_obj = cJSON_CreateObject();
|
||||
|
||||
// Extract subscription data from database
|
||||
const char* sub_id = (const char*)sqlite3_column_text(stmt, 0);
|
||||
const char* client_ip = (const char*)sqlite3_column_text(stmt, 1);
|
||||
const char* wsi_pointer = (const char*)sqlite3_column_text(stmt, 2);
|
||||
const char* filter_json = (const char*)sqlite3_column_text(stmt, 3);
|
||||
long long events_sent = sqlite3_column_int64(stmt, 4);
|
||||
long long created_at = sqlite3_column_int64(stmt, 5);
|
||||
long long duration_seconds = sqlite3_column_int64(stmt, 6);
|
||||
const char* filter_json = (const char*)sqlite3_column_text(stmt, 2);
|
||||
long long events_sent = sqlite3_column_int64(stmt, 3);
|
||||
long long created_at = sqlite3_column_int64(stmt, 4);
|
||||
long long duration_seconds = sqlite3_column_int64(stmt, 5);
|
||||
|
||||
// DEBUG: Log each subscription found
|
||||
DEBUG_LOG("Row %d: sub_id=%s, client_ip=%s, events_sent=%lld, created_at=%lld",
|
||||
row_count, sub_id ? sub_id : "NULL", client_ip ? client_ip : "NULL",
|
||||
events_sent, created_at);
|
||||
|
||||
// Add basic subscription info
|
||||
cJSON_AddStringToObject(sub_obj, "id", sub_id ? sub_id : "");
|
||||
cJSON_AddStringToObject(sub_obj, "client_ip", client_ip ? client_ip : "");
|
||||
cJSON_AddStringToObject(sub_obj, "wsi_pointer", wsi_pointer ? wsi_pointer : "");
|
||||
cJSON_AddNumberToObject(sub_obj, "created_at", (double)created_at);
|
||||
cJSON_AddNumberToObject(sub_obj, "duration_seconds", (double)duration_seconds);
|
||||
cJSON_AddNumberToObject(sub_obj, "events_sent", events_sent);
|
||||
@@ -374,6 +309,10 @@ cJSON* query_subscription_details(void) {
|
||||
|
||||
cJSON_AddItemToObject(subscriptions_data, "data", data);
|
||||
|
||||
// DEBUG: Log final summary
|
||||
DEBUG_LOG("Total subscriptions found: %d", cJSON_GetArraySize(subscriptions_array));
|
||||
DEBUG_LOG("=== END SUBSCRIPTION_DETAILS QUERY DEBUG ===");
|
||||
|
||||
return subscriptions_data;
|
||||
}
|
||||
|
||||
@@ -397,11 +336,6 @@ int generate_event_driven_monitoring(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Generate active_subscriptions monitoring event
|
||||
if (generate_monitoring_event_for_type("active_subscriptions", query_active_subscriptions) != 0) {
|
||||
DEBUG_ERROR("Failed to generate active_subscriptions monitoring event");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Generate CPU metrics monitoring event (also triggered by event storage)
|
||||
if (generate_monitoring_event_for_type("cpu_metrics", query_cpu_metrics) != 0) {
|
||||
@@ -409,17 +343,11 @@ int generate_event_driven_monitoring(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Generated and broadcast event-driven monitoring events");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Generate subscription-driven monitoring events (triggered by subscription changes)
|
||||
int generate_subscription_driven_monitoring(void) {
|
||||
// Generate active_subscriptions monitoring event (subscription changes affect this)
|
||||
if (generate_monitoring_event_for_type("active_subscriptions", query_active_subscriptions) != 0) {
|
||||
DEBUG_ERROR("Failed to generate active_subscriptions monitoring event");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Generate subscription_details monitoring event (admin-only)
|
||||
if (generate_monitoring_event_for_type("subscription_details", query_subscription_details) != 0) {
|
||||
@@ -433,7 +361,6 @@ int generate_subscription_driven_monitoring(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Generated and broadcast subscription-driven monitoring events");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -799,7 +726,7 @@ int send_admin_response(const char* sender_pubkey, const char* response_content,
|
||||
}
|
||||
|
||||
// Encrypt response content using NIP-44
|
||||
char encrypted_content[16384]; // Buffer for encrypted content (increased size)
|
||||
char encrypted_content[131072]; // Buffer for encrypted content (128KB to handle large SQL responses)
|
||||
int encrypt_result = nostr_nip44_encrypt(
|
||||
relay_privkey, // sender private key (bytes)
|
||||
sender_pubkey_bytes, // recipient public key (bytes)
|
||||
@@ -2304,6 +2231,306 @@ int process_config_change_request(const char* admin_pubkey, const char* message)
|
||||
return 1; // Confirmation sent
|
||||
}
|
||||
|
||||
// Forward declarations for relay event creation functions
|
||||
cJSON* create_relay_metadata_event(cJSON* metadata);
|
||||
cJSON* create_relay_dm_list_event(cJSON* dm_relays);
|
||||
cJSON* create_relay_list_event(cJSON* relays);
|
||||
|
||||
// Handle create_relay_event admin commands
|
||||
int handle_create_relay_event_command(cJSON* event, int kind, cJSON* event_data, char* error_message, size_t error_size, struct lws* wsi) {
|
||||
if (!event || !event_data || !error_message) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get request event ID for response correlation
|
||||
cJSON* request_id_obj = cJSON_GetObjectItem(event, "id");
|
||||
if (!request_id_obj || !cJSON_IsString(request_id_obj)) {
|
||||
snprintf(error_message, error_size, "Missing request event ID");
|
||||
return -1;
|
||||
}
|
||||
const char* request_id = cJSON_GetStringValue(request_id_obj);
|
||||
|
||||
// Get sender pubkey for response
|
||||
cJSON* sender_pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
|
||||
if (!sender_pubkey_obj || !cJSON_IsString(sender_pubkey_obj)) {
|
||||
snprintf(error_message, error_size, "Missing sender pubkey");
|
||||
return -1;
|
||||
}
|
||||
const char* sender_pubkey = cJSON_GetStringValue(sender_pubkey_obj);
|
||||
|
||||
// Create the relay event based on kind
|
||||
cJSON* relay_event = NULL;
|
||||
switch (kind) {
|
||||
case 0: // User metadata
|
||||
relay_event = create_relay_metadata_event(event_data);
|
||||
break;
|
||||
case 10050: // DM relay list
|
||||
relay_event = create_relay_dm_list_event(event_data);
|
||||
break;
|
||||
case 10002: // Relay list
|
||||
relay_event = create_relay_list_event(event_data);
|
||||
break;
|
||||
default: {
|
||||
char response_content[256];
|
||||
snprintf(response_content, sizeof(response_content),
|
||||
"❌ Unsupported event kind: %d\n\nSupported kinds: 0 (metadata), 10050 (DM relays), 10002 (relays)",
|
||||
kind);
|
||||
return send_admin_response(sender_pubkey, response_content, request_id, error_message, error_size, wsi);
|
||||
}
|
||||
}
|
||||
|
||||
if (!relay_event) {
|
||||
char response_content[128];
|
||||
snprintf(response_content, sizeof(response_content),
|
||||
"❌ Failed to create relay event (kind %d)\n\nCheck relay logs for details.", kind);
|
||||
return send_admin_response(sender_pubkey, response_content, request_id, error_message, error_size, wsi);
|
||||
}
|
||||
|
||||
// Store the event in database
|
||||
int store_result = store_event(relay_event);
|
||||
if (store_result != 0) {
|
||||
cJSON_Delete(relay_event);
|
||||
char response_content[128];
|
||||
snprintf(response_content, sizeof(response_content),
|
||||
"❌ Failed to store relay event (kind %d) in database", kind);
|
||||
return send_admin_response(sender_pubkey, response_content, request_id, error_message, error_size, wsi);
|
||||
}
|
||||
|
||||
// Broadcast the event to connected clients
|
||||
broadcast_event_to_subscriptions(relay_event);
|
||||
|
||||
// Clean up
|
||||
cJSON_Delete(relay_event);
|
||||
|
||||
// Send success response (plain text like other admin commands)
|
||||
char response_content[256];
|
||||
const char* kind_name = (kind == 0) ? "metadata" : (kind == 10050) ? "DM relay list" : "relay list";
|
||||
snprintf(response_content, sizeof(response_content),
|
||||
"✅ Relay event created successfully\n\nKind: %d (%s)\n\nEvent has been stored and broadcast to subscribers.",
|
||||
kind, kind_name);
|
||||
|
||||
return send_admin_response(sender_pubkey, response_content, request_id, error_message, error_size, wsi);
|
||||
}
|
||||
|
||||
// Create a relay metadata event (kind 0)
|
||||
cJSON* create_relay_metadata_event(cJSON* metadata) {
|
||||
if (!metadata || !cJSON_IsObject(metadata)) {
|
||||
DEBUG_ERROR("Invalid metadata object for kind 0 event");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get relay keys
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
char* relay_privkey_hex = get_relay_private_key();
|
||||
if (!relay_pubkey || !relay_privkey_hex) {
|
||||
DEBUG_ERROR("Could not get relay keys for metadata event");
|
||||
if (relay_privkey_hex) free(relay_privkey_hex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 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);
|
||||
DEBUG_ERROR("Failed to convert relay private key for metadata event");
|
||||
return NULL;
|
||||
}
|
||||
free(relay_privkey_hex);
|
||||
|
||||
// Create metadata content
|
||||
char* content = cJSON_Print(metadata);
|
||||
if (!content) {
|
||||
DEBUG_ERROR("Failed to serialize metadata for kind 0 event");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create and sign the event
|
||||
cJSON* signed_event = nostr_create_and_sign_event(
|
||||
0, // kind (metadata)
|
||||
content, // content
|
||||
NULL, // tags (none for kind 0)
|
||||
relay_privkey, // private key
|
||||
(time_t)time(NULL) // timestamp
|
||||
);
|
||||
|
||||
free(content);
|
||||
|
||||
if (!signed_event) {
|
||||
DEBUG_ERROR("Failed to create and sign metadata event");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Created relay metadata event (kind 0)");
|
||||
return signed_event;
|
||||
}
|
||||
|
||||
// Create a relay DM list event (kind 10050)
|
||||
cJSON* create_relay_dm_list_event(cJSON* dm_relays) {
|
||||
if (!dm_relays || !cJSON_IsObject(dm_relays)) {
|
||||
DEBUG_ERROR("Invalid DM relays object for kind 10050 event");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get relay keys
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
char* relay_privkey_hex = get_relay_private_key();
|
||||
if (!relay_pubkey || !relay_privkey_hex) {
|
||||
DEBUG_ERROR("Could not get relay keys for DM list event");
|
||||
if (relay_privkey_hex) free(relay_privkey_hex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 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);
|
||||
DEBUG_ERROR("Failed to convert relay private key for DM list event");
|
||||
return NULL;
|
||||
}
|
||||
free(relay_privkey_hex);
|
||||
|
||||
// Create empty content for kind 10050
|
||||
const char* content = "";
|
||||
|
||||
// Create tags from relay list
|
||||
cJSON* tags = cJSON_CreateArray();
|
||||
if (!tags) {
|
||||
DEBUG_ERROR("Failed to create tags array for DM list event");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Extract relays array
|
||||
cJSON* relays_array = cJSON_GetObjectItem(dm_relays, "relays");
|
||||
if (relays_array && cJSON_IsArray(relays_array)) {
|
||||
cJSON* relay_item = NULL;
|
||||
cJSON_ArrayForEach(relay_item, relays_array) {
|
||||
if (cJSON_IsString(relay_item)) {
|
||||
const char* relay_url = cJSON_GetStringValue(relay_item);
|
||||
if (relay_url && strlen(relay_url) > 0) {
|
||||
cJSON* tag = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(tag, cJSON_CreateString("relay"));
|
||||
cJSON_AddItemToArray(tag, cJSON_CreateString(relay_url));
|
||||
cJSON_AddItemToArray(tags, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create and sign the event
|
||||
cJSON* signed_event = nostr_create_and_sign_event(
|
||||
10050, // kind (DM relay list)
|
||||
content, // content (empty)
|
||||
tags, // tags
|
||||
relay_privkey, // private key
|
||||
(time_t)time(NULL) // timestamp
|
||||
);
|
||||
|
||||
cJSON_Delete(tags);
|
||||
|
||||
if (!signed_event) {
|
||||
DEBUG_ERROR("Failed to create and sign DM list event");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Created relay DM list event (kind 10050)");
|
||||
return signed_event;
|
||||
}
|
||||
|
||||
// Create a relay list event (kind 10002)
|
||||
cJSON* create_relay_list_event(cJSON* relays) {
|
||||
if (!relays || !cJSON_IsObject(relays)) {
|
||||
DEBUG_ERROR("Invalid relays object for kind 10002 event");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get relay keys
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
char* relay_privkey_hex = get_relay_private_key();
|
||||
if (!relay_pubkey || !relay_privkey_hex) {
|
||||
DEBUG_ERROR("Could not get relay keys for relay list event");
|
||||
if (relay_privkey_hex) free(relay_privkey_hex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 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);
|
||||
DEBUG_ERROR("Failed to convert relay private key for relay list event");
|
||||
return NULL;
|
||||
}
|
||||
free(relay_privkey_hex);
|
||||
|
||||
// Create empty content for kind 10002
|
||||
const char* content = "";
|
||||
|
||||
// Create tags from relay list
|
||||
cJSON* tags = cJSON_CreateArray();
|
||||
if (!tags) {
|
||||
DEBUG_ERROR("Failed to create tags array for relay list event");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Extract relays array
|
||||
cJSON* relays_array = cJSON_GetObjectItem(relays, "relays");
|
||||
if (relays_array && cJSON_IsArray(relays_array)) {
|
||||
cJSON* relay_item = NULL;
|
||||
cJSON_ArrayForEach(relay_item, relays_array) {
|
||||
if (cJSON_IsObject(relay_item)) {
|
||||
cJSON* url = cJSON_GetObjectItem(relay_item, "url");
|
||||
cJSON* read = cJSON_GetObjectItem(relay_item, "read");
|
||||
cJSON* write = cJSON_GetObjectItem(relay_item, "write");
|
||||
|
||||
if (url && cJSON_IsString(url)) {
|
||||
const char* relay_url = cJSON_GetStringValue(url);
|
||||
int read_flag = read && cJSON_IsBool(read) ? cJSON_IsTrue(read) : true;
|
||||
int write_flag = write && cJSON_IsBool(write) ? cJSON_IsTrue(write) : true;
|
||||
|
||||
// Create marker string
|
||||
const char* marker = NULL;
|
||||
if (read_flag && write_flag) {
|
||||
marker = ""; // No marker means both read and write
|
||||
} else if (read_flag) {
|
||||
marker = "read";
|
||||
} else if (write_flag) {
|
||||
marker = "write";
|
||||
} else {
|
||||
// Skip invalid entries
|
||||
continue;
|
||||
}
|
||||
|
||||
cJSON* tag = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(tag, cJSON_CreateString("r"));
|
||||
cJSON_AddItemToArray(tag, cJSON_CreateString(relay_url));
|
||||
if (marker[0] != '\0') {
|
||||
cJSON_AddItemToArray(tag, cJSON_CreateString(marker));
|
||||
}
|
||||
cJSON_AddItemToArray(tags, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create and sign the event
|
||||
cJSON* signed_event = nostr_create_and_sign_event(
|
||||
10002, // kind (relay list)
|
||||
content, // content (empty)
|
||||
tags, // tags
|
||||
relay_privkey, // private key
|
||||
(time_t)time(NULL) // timestamp
|
||||
);
|
||||
|
||||
cJSON_Delete(tags);
|
||||
|
||||
if (!signed_event) {
|
||||
DEBUG_ERROR("Failed to create and sign relay list event");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Created relay list event (kind 10002)");
|
||||
return signed_event;
|
||||
}
|
||||
|
||||
// Handle monitoring system admin commands
|
||||
int handle_monitoring_command(cJSON* event, const char* command, char* error_message, size_t error_size, struct lws* wsi) {
|
||||
if (!event || !command || !error_message) {
|
||||
|
||||
Reference in New Issue
Block a user