v0.8.1 - added screenshots
This commit is contained in:
165
src/api.c
165
src/api.c
@@ -271,6 +271,7 @@ cJSON* query_subscription_details(void) {
|
||||
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);
|
||||
const char* wsi_pointer = (const char*)sqlite3_column_text(stmt, 6);
|
||||
|
||||
// DEBUG: Log each subscription found
|
||||
DEBUG_LOG("Row %d: sub_id=%s, client_ip=%s, events_sent=%lld, created_at=%lld",
|
||||
@@ -284,6 +285,7 @@ cJSON* query_subscription_details(void) {
|
||||
cJSON_AddNumberToObject(sub_obj, "duration_seconds", (double)duration_seconds);
|
||||
cJSON_AddNumberToObject(sub_obj, "events_sent", events_sent);
|
||||
cJSON_AddBoolToObject(sub_obj, "active", 1); // All from this view are active
|
||||
cJSON_AddStringToObject(sub_obj, "wsi_pointer", wsi_pointer ? wsi_pointer : "N/A");
|
||||
|
||||
// Parse and add filter JSON if available
|
||||
if (filter_json) {
|
||||
@@ -499,6 +501,76 @@ void monitoring_on_subscription_change(void) {
|
||||
generate_subscription_driven_monitoring();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and post a kind 1 status event with relay statistics
|
||||
* Called periodically from main event loop based on configuration
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int generate_and_post_status_event(void) {
|
||||
// Check if feature is enabled
|
||||
int hours_interval = get_config_int("kind_1_status_posts_hours", 0);
|
||||
if (hours_interval <= 0) {
|
||||
return 0; // Feature disabled
|
||||
}
|
||||
|
||||
// Generate statistics text content using existing function
|
||||
char* stats_text = generate_stats_text();
|
||||
if (!stats_text) {
|
||||
DEBUG_ERROR("Failed to generate statistics text for status post");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get relay private key
|
||||
char* relay_privkey_hex = get_relay_private_key();
|
||||
if (!relay_privkey_hex) {
|
||||
DEBUG_ERROR("Failed to get relay private key for status post");
|
||||
free(stats_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convert hex private key to bytes
|
||||
unsigned char relay_privkey_bytes[32];
|
||||
if (nostr_hex_to_bytes(relay_privkey_hex, relay_privkey_bytes, 32) != 0) {
|
||||
DEBUG_ERROR("Failed to convert relay private key to bytes");
|
||||
free(stats_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create empty tags array (kind 1 doesn't require special tags)
|
||||
cJSON* tags = cJSON_CreateArray();
|
||||
|
||||
// Create and sign the kind 1 event
|
||||
cJSON* signed_event = nostr_create_and_sign_event(
|
||||
1, // kind 1 = text note
|
||||
stats_text, // content = statistics
|
||||
tags, // empty tags
|
||||
relay_privkey_bytes, // relay's private key
|
||||
time(NULL) // current timestamp
|
||||
);
|
||||
|
||||
free(stats_text);
|
||||
|
||||
if (!signed_event) {
|
||||
DEBUG_ERROR("Failed to create and sign status event");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Store event in database (kind 1 events MUST be stored)
|
||||
if (store_event(signed_event) != 0) {
|
||||
DEBUG_ERROR("Failed to store status event in database");
|
||||
cJSON_Delete(signed_event);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Broadcast event to subscriptions
|
||||
broadcast_event_to_subscriptions(signed_event);
|
||||
|
||||
DEBUG_LOG("Successfully posted kind 1 status event");
|
||||
cJSON_Delete(signed_event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Forward declaration for known_configs (defined in config.c)
|
||||
typedef struct {
|
||||
const char* key;
|
||||
@@ -565,6 +637,9 @@ static const config_definition_t known_configs[] = {
|
||||
{"relay_privkey", "string", 0, 65},
|
||||
{"admin_pubkey", "string", 0, 65},
|
||||
|
||||
// Kind 1 Status Posts
|
||||
{"kind_1_status_posts_hours", "int", 0, 8760},
|
||||
|
||||
// Sentinel
|
||||
{NULL, NULL, 0, 0}
|
||||
};
|
||||
@@ -1423,8 +1498,7 @@ char* generate_config_text(void) {
|
||||
}
|
||||
|
||||
// Footer
|
||||
offset += snprintf(config_text + offset, 8192 - offset,
|
||||
"\n✅ Configuration retrieved successfully");
|
||||
offset += snprintf(config_text + offset, 8192 - offset, "\n");
|
||||
|
||||
return config_text;
|
||||
}
|
||||
@@ -1454,7 +1528,7 @@ char* generate_stats_text(void) {
|
||||
cJSON* newest_event = cJSON_GetObjectItem(stats_obj, "latest_event_at");
|
||||
cJSON* time_stats = cJSON_GetObjectItem(stats_obj, "time_stats");
|
||||
cJSON* event_kinds = cJSON_GetObjectItem(stats_obj, "event_kinds");
|
||||
cJSON* top_pubkeys = cJSON_GetObjectItem(stats_obj, "top_pubkeys");
|
||||
// cJSON* top_pubkeys = cJSON_GetObjectItem(stats_obj, "top_pubkeys");
|
||||
|
||||
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;
|
||||
@@ -1495,25 +1569,23 @@ char* generate_stats_text(void) {
|
||||
|
||||
// Header
|
||||
offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
"📊 Relay Statistics\n"
|
||||
"\nRelay Statistics\n"
|
||||
"━━━━━━━━━━━━━━━━━━━━\n");
|
||||
|
||||
// Database Overview section
|
||||
offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
"Database Overview:\n"
|
||||
"Metric\tValue\tDescription\n"
|
||||
"Database Size\t%.2f MB (%lld bytes)\tCurrent database file size\n"
|
||||
"Total Events\t%lld\tTotal number of events stored\n"
|
||||
"Active Subscriptions\t%d\tCurrent active WebSocket subscriptions\n"
|
||||
"Oldest Event\t%s\tTimestamp of oldest event\n"
|
||||
"Newest Event\t%s\tTimestamp of newest event\n"
|
||||
|
||||
"Database Size %.2f MB\n"
|
||||
"Total Events %lld\n"
|
||||
"Active Subscriptions %d\n"
|
||||
"Oldest Event %s\n"
|
||||
"Newest Event %s\n"
|
||||
"\n",
|
||||
db_mb, db_bytes, total, active_subs, oldest_str, newest_str);
|
||||
|
||||
// Event Kind Distribution section
|
||||
offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
"Event Kind Distribution:\n"
|
||||
"Event Kind\tCount\tPercentage\n");
|
||||
"Event Kind Distribution:\n");
|
||||
|
||||
if (event_kinds && cJSON_IsArray(event_kinds)) {
|
||||
cJSON* kind_item = NULL;
|
||||
@@ -1540,49 +1612,48 @@ char* generate_stats_text(void) {
|
||||
// Time-based Statistics section
|
||||
offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
"Time-based Statistics:\n"
|
||||
"Period\tEvents\tDescription\n"
|
||||
"Last 24 Hours\t%lld\tEvents in the last day\n"
|
||||
"Last 7 Days\t%lld\tEvents in the last week\n"
|
||||
"Last 30 Days\t%lld\tEvents in the last month\n"
|
||||
|
||||
"%lld\tEvents in the last day\n"
|
||||
"%lld\tEvents in the last week\n"
|
||||
"%lld\tEvents in the last month\n"
|
||||
"\n",
|
||||
last_24h, last_7d, last_30d);
|
||||
|
||||
// Top Pubkeys section
|
||||
offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
"Top Pubkeys by Event Count:\n"
|
||||
"Rank\tPubkey\tEvent Count\tPercentage\n");
|
||||
// offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
// "Top Pubkeys by Event Count:\n"
|
||||
// "Rank\tPubkey\tEvent Count\tPercentage\n");
|
||||
|
||||
if (top_pubkeys && cJSON_IsArray(top_pubkeys)) {
|
||||
int rank = 1;
|
||||
cJSON* pubkey_item = NULL;
|
||||
cJSON_ArrayForEach(pubkey_item, top_pubkeys) {
|
||||
cJSON* pubkey = cJSON_GetObjectItem(pubkey_item, "pubkey");
|
||||
cJSON* event_count = cJSON_GetObjectItem(pubkey_item, "event_count");
|
||||
cJSON* percentage = cJSON_GetObjectItem(pubkey_item, "percentage");
|
||||
// if (top_pubkeys && cJSON_IsArray(top_pubkeys)) {
|
||||
// int rank = 1;
|
||||
// cJSON* pubkey_item = NULL;
|
||||
// cJSON_ArrayForEach(pubkey_item, top_pubkeys) {
|
||||
// cJSON* pubkey = cJSON_GetObjectItem(pubkey_item, "pubkey");
|
||||
// cJSON* event_count = cJSON_GetObjectItem(pubkey_item, "event_count");
|
||||
// cJSON* percentage = cJSON_GetObjectItem(pubkey_item, "percentage");
|
||||
|
||||
if (pubkey && event_count && percentage) {
|
||||
const char* pubkey_str = cJSON_GetStringValue(pubkey);
|
||||
char short_pubkey[20] = "...";
|
||||
if (pubkey_str && strlen(pubkey_str) >= 16) {
|
||||
snprintf(short_pubkey, sizeof(short_pubkey), "%.16s...", pubkey_str);
|
||||
}
|
||||
// if (pubkey && event_count && percentage) {
|
||||
// const char* pubkey_str = cJSON_GetStringValue(pubkey);
|
||||
// char short_pubkey[20] = "...";
|
||||
// if (pubkey_str && strlen(pubkey_str) >= 16) {
|
||||
// snprintf(short_pubkey, sizeof(short_pubkey), "%.16s...", pubkey_str);
|
||||
// }
|
||||
|
||||
offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
"%d\t%s\t%lld\t%.1f%%\n",
|
||||
rank++,
|
||||
short_pubkey,
|
||||
(long long)cJSON_GetNumberValue(event_count),
|
||||
cJSON_GetNumberValue(percentage));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
"No pubkey data available\n");
|
||||
}
|
||||
// offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
// "%d\t%s\t%lld\t%.1f%%\n",
|
||||
// rank++,
|
||||
// short_pubkey,
|
||||
// (long long)cJSON_GetNumberValue(event_count),
|
||||
// cJSON_GetNumberValue(percentage));
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
// "No pubkey data available\n");
|
||||
// }
|
||||
|
||||
// Footer
|
||||
offset += snprintf(stats_text + offset, 16384 - offset,
|
||||
"\n✅ Statistics retrieved successfully");
|
||||
offset += snprintf(stats_text + offset, 16384 - offset, "\n");
|
||||
|
||||
cJSON_Delete(stats_obj);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user