v0.8.1 - added screenshots

This commit is contained in:
Your Name
2025-10-29 07:39:08 -04:00
parent f49aae8ab0
commit b2ad70b028
18 changed files with 180 additions and 538 deletions

165
src/api.c
View File

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