v0.4.2 - Implement NIP-11 Relay Information Document with event-based configuration - make relay info dynamically configurable via admin API

This commit is contained in:
Your Name
2025-10-02 11:38:28 -04:00
parent c3bab033ed
commit cfacedbb1a
13 changed files with 497 additions and 230 deletions

View File

@@ -99,19 +99,25 @@ static char g_temp_relay_privkey[65] = {0};
static int get_cache_timeout(void) {
char *no_cache = getenv("GINX_NO_CACHE");
char *cache_timeout = getenv("GINX_CACHE_TIMEOUT");
if (no_cache && strcmp(no_cache, "1") == 0) {
return 0; // No caching
}
if (cache_timeout) {
int timeout = atoi(cache_timeout);
return (timeout >= 0) ? timeout : 300; // Use provided value or default
}
return 300; // Default 5 minutes
}
// Helper function to safely return dynamically allocated string from static buffer
static char* safe_strdup_from_static(const char* static_str) {
if (!static_str) return NULL;
return strdup(static_str);
}
// Force cache refresh - invalidates current cache
void force_config_cache_refresh(void) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
@@ -396,58 +402,61 @@ const char* get_config_value(const char* key) {
if (!key) {
return NULL;
}
// Special fast path for frequently accessed keys via unified cache
if (strcmp(key, "admin_pubkey") == 0) {
return get_admin_pubkey_cached();
const char* cached_value = get_admin_pubkey_cached();
return safe_strdup_from_static(cached_value);
}
if (strcmp(key, "relay_pubkey") == 0) {
return get_relay_pubkey_cached();
const char* cached_value = get_relay_pubkey_cached();
return safe_strdup_from_static(cached_value);
}
// For other keys, try config table first
const char* table_value = get_config_value_from_table(key);
if (table_value) {
return table_value;
}
// Fallback to legacy event-based config for backward compatibility
// Use unified cache buffer instead of static buffer
if (!g_current_config) {
return NULL;
}
// Look for key in current configuration tags
cJSON* tags = cJSON_GetObjectItem(g_current_config, "tags");
if (!tags || !cJSON_IsArray(tags)) {
return NULL;
}
pthread_mutex_lock(&g_unified_cache.cache_lock);
cJSON* tag = NULL;
cJSON_ArrayForEach(tag, tags) {
if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
cJSON* tag_key = cJSON_GetArrayItem(tag, 0);
cJSON* tag_value = cJSON_GetArrayItem(tag, 1);
if (tag_key && tag_value &&
cJSON_IsString(tag_key) && cJSON_IsString(tag_value)) {
if (strcmp(cJSON_GetStringValue(tag_key), key) == 0) {
strncpy(g_unified_cache.temp_buffer, cJSON_GetStringValue(tag_value),
sizeof(g_unified_cache.temp_buffer) - 1);
g_unified_cache.temp_buffer[sizeof(g_unified_cache.temp_buffer) - 1] = '\0';
const char* result = safe_strdup_from_static(g_unified_cache.temp_buffer);
pthread_mutex_unlock(&g_unified_cache.cache_lock);
return g_unified_cache.temp_buffer;
return result;
}
}
}
}
pthread_mutex_unlock(&g_unified_cache.cache_lock);
return NULL;
}
@@ -456,14 +465,18 @@ int get_config_int(const char* key, int default_value) {
if (!str_value) {
return default_value;
}
char* endptr;
long val = strtol(str_value, &endptr, 10);
if (endptr == str_value || *endptr != '\0') {
// Free the dynamically allocated string
free((char*)str_value);
return default_value;
}
// Free the dynamically allocated string
free((char*)str_value);
return (int)val;
}
@@ -472,18 +485,23 @@ int get_config_bool(const char* key, int default_value) {
if (!str_value) {
return default_value;
}
if (strcasecmp(str_value, "true") == 0 ||
strcasecmp(str_value, "yes") == 0 ||
int result;
if (strcasecmp(str_value, "true") == 0 ||
strcasecmp(str_value, "yes") == 0 ||
strcasecmp(str_value, "1") == 0) {
return 1;
} else if (strcasecmp(str_value, "false") == 0 ||
strcasecmp(str_value, "no") == 0 ||
result = 1;
} else if (strcasecmp(str_value, "false") == 0 ||
strcasecmp(str_value, "no") == 0 ||
strcasecmp(str_value, "0") == 0) {
return 0;
result = 0;
} else {
result = default_value;
}
return default_value;
// Free the dynamically allocated string
free((char*)str_value);
return result;
}
// ================================
@@ -1063,33 +1081,38 @@ int startup_existing_relay(const char* relay_pubkey) {
log_error("Invalid relay pubkey for existing relay startup");
return -1;
}
log_info("Starting existing relay...");
printf(" Relay pubkey: %s\n", relay_pubkey);
// Store relay pubkey in unified cache
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_pubkey, relay_pubkey, sizeof(g_unified_cache.relay_pubkey) - 1);
g_unified_cache.relay_pubkey[sizeof(g_unified_cache.relay_pubkey) - 1] = '\0';
pthread_mutex_unlock(&g_unified_cache.cache_lock);
// Set database path
char* db_name = get_database_name_from_relay_pubkey(relay_pubkey);
if (!db_name) {
log_error("Failed to generate database name");
return -1;
}
strncpy(g_database_path, db_name, sizeof(g_database_path) - 1);
g_database_path[sizeof(g_database_path) - 1] = '\0';
free(db_name);
// Ensure default configuration values are populated (for any missing keys)
if (populate_default_config_values() != 0) {
log_warning("Failed to populate default config values for existing relay - continuing");
}
// Configuration will be migrated from events to table after database initialization
log_info("Configuration migration will be performed after database is available");
// Load configuration event from database (after database is initialized)
// This will be done in apply_configuration_from_database()
log_success("Existing relay startup prepared");
return 0;
}
@@ -1814,31 +1837,90 @@ int handle_configuration_event(cJSON* event, char* error_message, size_t error_s
// Get value from config table
const char* get_config_value_from_table(const char* key) {
if (!g_db || !key) return NULL;
const char* sql = "SELECT value FROM config WHERE key = ?";
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
return NULL;
}
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
const char* result = NULL;
if (sqlite3_step(stmt) == SQLITE_ROW) {
const char* value = (char*)sqlite3_column_text(stmt, 0);
if (value) {
// Use unified cache buffer with thread safety
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.temp_buffer, value, sizeof(g_unified_cache.temp_buffer) - 1);
g_unified_cache.temp_buffer[sizeof(g_unified_cache.temp_buffer) - 1] = '\0';
result = g_unified_cache.temp_buffer;
pthread_mutex_unlock(&g_unified_cache.cache_lock);
// For NIP-11 fields, store in cache buffers but return dynamically allocated strings for consistency
if (strcmp(key, "relay_name") == 0) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_info.name, value, sizeof(g_unified_cache.relay_info.name) - 1);
g_unified_cache.relay_info.name[sizeof(g_unified_cache.relay_info.name) - 1] = '\0';
result = strdup(value); // Return dynamically allocated copy
pthread_mutex_unlock(&g_unified_cache.cache_lock);
} else if (strcmp(key, "relay_description") == 0) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_info.description, value, sizeof(g_unified_cache.relay_info.description) - 1);
g_unified_cache.relay_info.description[sizeof(g_unified_cache.relay_info.description) - 1] = '\0';
result = strdup(value); // Return dynamically allocated copy
pthread_mutex_unlock(&g_unified_cache.cache_lock);
} else if (strcmp(key, "relay_contact") == 0) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_info.contact, value, sizeof(g_unified_cache.relay_info.contact) - 1);
g_unified_cache.relay_info.contact[sizeof(g_unified_cache.relay_info.contact) - 1] = '\0';
result = strdup(value); // Return dynamically allocated copy
pthread_mutex_unlock(&g_unified_cache.cache_lock);
} else if (strcmp(key, "relay_software") == 0) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_info.software, value, sizeof(g_unified_cache.relay_info.software) - 1);
g_unified_cache.relay_info.software[sizeof(g_unified_cache.relay_info.software) - 1] = '\0';
result = strdup(value); // Return dynamically allocated copy
pthread_mutex_unlock(&g_unified_cache.cache_lock);
} else if (strcmp(key, "relay_version") == 0) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_info.version, value, sizeof(g_unified_cache.relay_info.version) - 1);
g_unified_cache.relay_info.version[sizeof(g_unified_cache.relay_info.version) - 1] = '\0';
result = strdup(value); // Return dynamically allocated copy
pthread_mutex_unlock(&g_unified_cache.cache_lock);
} else if (strcmp(key, "supported_nips") == 0) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_info.supported_nips_str, value, sizeof(g_unified_cache.relay_info.supported_nips_str) - 1);
g_unified_cache.relay_info.supported_nips_str[sizeof(g_unified_cache.relay_info.supported_nips_str) - 1] = '\0';
result = strdup(value); // Return dynamically allocated copy
pthread_mutex_unlock(&g_unified_cache.cache_lock);
} else if (strcmp(key, "language_tags") == 0) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_info.language_tags_str, value, sizeof(g_unified_cache.relay_info.language_tags_str) - 1);
g_unified_cache.relay_info.language_tags_str[sizeof(g_unified_cache.relay_info.language_tags_str) - 1] = '\0';
result = strdup(value); // Return dynamically allocated copy
pthread_mutex_unlock(&g_unified_cache.cache_lock);
} else if (strcmp(key, "relay_countries") == 0) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_info.relay_countries_str, value, sizeof(g_unified_cache.relay_info.relay_countries_str) - 1);
g_unified_cache.relay_info.relay_countries_str[sizeof(g_unified_cache.relay_info.relay_countries_str) - 1] = '\0';
result = strdup(value); // Return dynamically allocated copy
pthread_mutex_unlock(&g_unified_cache.cache_lock);
} else if (strcmp(key, "posting_policy") == 0) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_info.posting_policy, value, sizeof(g_unified_cache.relay_info.posting_policy) - 1);
g_unified_cache.relay_info.posting_policy[sizeof(g_unified_cache.relay_info.posting_policy) - 1] = '\0';
result = strdup(value); // Return dynamically allocated copy
pthread_mutex_unlock(&g_unified_cache.cache_lock);
} else if (strcmp(key, "payments_url") == 0) {
pthread_mutex_lock(&g_unified_cache.cache_lock);
strncpy(g_unified_cache.relay_info.payments_url, value, sizeof(g_unified_cache.relay_info.payments_url) - 1);
g_unified_cache.relay_info.payments_url[sizeof(g_unified_cache.relay_info.payments_url) - 1] = '\0';
result = strdup(value); // Return dynamically allocated copy
pthread_mutex_unlock(&g_unified_cache.cache_lock);
} else {
// For other keys, return a dynamically allocated string to prevent buffer reuse
result = strdup(value);
}
}
}
sqlite3_finalize(stmt);
return result;
}
@@ -3906,12 +3988,17 @@ const char* get_config_value_hybrid(const char* key) {
if (is_config_table_ready()) {
const char* table_value = get_config_value_from_table(key);
if (table_value) {
return table_value;
return table_value; // Already dynamically allocated
}
}
// Fall back to event-based config
return get_config_value(key);
// Fall back to event-based config, but ensure it's dynamically allocated
const char* fallback_value = get_config_value(key);
if (fallback_value) {
return strdup(fallback_value); // Make a copy since fallback might be static
}
return NULL;
}
// Check if config table is ready