Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80b15e16e2 |
18
README.md
18
README.md
@@ -116,6 +116,24 @@ All commands are sent as NIP-44 encrypted JSON arrays in the event content. The
|
|||||||
- `pow_min_difficulty`: Minimum proof-of-work difficulty
|
- `pow_min_difficulty`: Minimum proof-of-work difficulty
|
||||||
- `nip40_expiration_enabled`: Enable event expiration (`true`/`false`)
|
- `nip40_expiration_enabled`: Enable event expiration (`true`/`false`)
|
||||||
|
|
||||||
|
### Dynamic Configuration Updates
|
||||||
|
|
||||||
|
C-Relay supports **dynamic configuration updates** without requiring a restart for most settings. Configuration parameters are categorized as either **dynamic** (can be updated immediately) or **restart-required** (require relay restart to take effect).
|
||||||
|
|
||||||
|
**Dynamic Configuration Parameters (No Restart Required):**
|
||||||
|
- All relay information (NIP-11) settings: `relay_name`, `relay_description`, `relay_contact`, `relay_software`, `relay_version`, `supported_nips`, `language_tags`, `relay_countries`, `posting_policy`, `payments_url`
|
||||||
|
- Authentication settings: `auth_enabled`, `nip42_auth_required`, `nip42_auth_required_kinds`, `nip42_challenge_timeout`
|
||||||
|
- Subscription limits: `max_subscriptions_per_client`, `max_total_subscriptions`
|
||||||
|
- Event validation limits: `max_event_tags`, `max_content_length`, `max_message_length`
|
||||||
|
- Proof of Work settings: `pow_min_difficulty`, `pow_mode`
|
||||||
|
- Event expiration settings: `nip40_expiration_enabled`, `nip40_expiration_strict`, `nip40_expiration_filter`, `nip40_expiration_grace_period`
|
||||||
|
|
||||||
|
**Restart-Required Configuration Parameters:**
|
||||||
|
- Connection settings: `max_connections`, `relay_port`
|
||||||
|
- Database and core system settings
|
||||||
|
|
||||||
|
When updating configuration, the admin API response will indicate whether a restart is required for each parameter. Dynamic updates take effect immediately and are reflected in NIP-11 relay information documents without restart.
|
||||||
|
|
||||||
### Response Format
|
### Response Format
|
||||||
|
|
||||||
All admin commands return **signed EVENT responses** via WebSocket following standard Nostr protocol. Responses use JSON content with structured data.
|
All admin commands return **signed EVENT responses** via WebSocket following standard Nostr protocol. Responses use JSON content with structured data.
|
||||||
|
|||||||
197
src/config.c
197
src/config.c
@@ -127,40 +127,119 @@ void force_config_cache_refresh(void) {
|
|||||||
log_info("Configuration cache forcibly invalidated");
|
log_info("Configuration cache forcibly invalidated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update specific cache value without full refresh
|
||||||
|
int update_cache_value(const char* key, const char* value) {
|
||||||
|
if (!key || !value) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||||
|
|
||||||
|
// Update specific cache fields
|
||||||
|
if (strcmp(key, "admin_pubkey") == 0) {
|
||||||
|
strncpy(g_unified_cache.admin_pubkey, value, sizeof(g_unified_cache.admin_pubkey) - 1);
|
||||||
|
g_unified_cache.admin_pubkey[sizeof(g_unified_cache.admin_pubkey) - 1] = '\0';
|
||||||
|
} else if (strcmp(key, "relay_pubkey") == 0) {
|
||||||
|
strncpy(g_unified_cache.relay_pubkey, value, sizeof(g_unified_cache.relay_pubkey) - 1);
|
||||||
|
g_unified_cache.relay_pubkey[sizeof(g_unified_cache.relay_pubkey) - 1] = '\0';
|
||||||
|
} else if (strcmp(key, "auth_required") == 0) {
|
||||||
|
g_unified_cache.auth_required = (strcmp(value, "true") == 0) ? 1 : 0;
|
||||||
|
} else if (strcmp(key, "admin_enabled") == 0) {
|
||||||
|
g_unified_cache.admin_enabled = (strcmp(value, "true") == 0) ? 1 : 0;
|
||||||
|
} else if (strcmp(key, "max_file_size") == 0) {
|
||||||
|
g_unified_cache.max_file_size = atol(value);
|
||||||
|
} else if (strcmp(key, "nip42_mode") == 0) {
|
||||||
|
if (strcmp(value, "disabled") == 0) {
|
||||||
|
g_unified_cache.nip42_mode = 0;
|
||||||
|
} else if (strcmp(value, "required") == 0) {
|
||||||
|
g_unified_cache.nip42_mode = 2;
|
||||||
|
} else {
|
||||||
|
g_unified_cache.nip42_mode = 1; // Optional/enabled
|
||||||
|
}
|
||||||
|
} else if (strcmp(key, "nip42_challenge_timeout") == 0) {
|
||||||
|
g_unified_cache.nip42_challenge_timeout = atoi(value);
|
||||||
|
} else if (strcmp(key, "nip42_time_tolerance") == 0) {
|
||||||
|
g_unified_cache.nip42_time_tolerance = atoi(value);
|
||||||
|
} else {
|
||||||
|
// For NIP-11 relay info fields, update the cache buffers
|
||||||
|
if (strcmp(key, "relay_name") == 0) {
|
||||||
|
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';
|
||||||
|
} else if (strcmp(key, "relay_description") == 0) {
|
||||||
|
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';
|
||||||
|
} else if (strcmp(key, "relay_contact") == 0) {
|
||||||
|
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';
|
||||||
|
} else if (strcmp(key, "relay_software") == 0) {
|
||||||
|
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';
|
||||||
|
} else if (strcmp(key, "relay_version") == 0) {
|
||||||
|
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';
|
||||||
|
} else if (strcmp(key, "supported_nips") == 0) {
|
||||||
|
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';
|
||||||
|
} else if (strcmp(key, "language_tags") == 0) {
|
||||||
|
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';
|
||||||
|
} else if (strcmp(key, "relay_countries") == 0) {
|
||||||
|
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';
|
||||||
|
} else if (strcmp(key, "posting_policy") == 0) {
|
||||||
|
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';
|
||||||
|
} else if (strcmp(key, "payments_url") == 0) {
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset cache expiration to extend validity
|
||||||
|
int cache_timeout = get_cache_timeout();
|
||||||
|
g_unified_cache.cache_expires = time(NULL) + cache_timeout;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||||
|
|
||||||
|
log_info("Updated specific cache value");
|
||||||
|
printf(" Key: %s\n", key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh unified cache from database
|
// Refresh unified cache from database
|
||||||
static int refresh_unified_cache_from_table(void) {
|
static int refresh_unified_cache_from_table(void) {
|
||||||
if (!g_db) {
|
if (!g_db) {
|
||||||
log_error("Database not available for cache refresh");
|
log_error("Database not available for cache refresh");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear cache
|
// Clear cache
|
||||||
memset(&g_unified_cache, 0, sizeof(g_unified_cache));
|
memset(&g_unified_cache, 0, sizeof(g_unified_cache));
|
||||||
g_unified_cache.cache_lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
g_unified_cache.cache_lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
// Load critical config values from table
|
// Load critical config values from table
|
||||||
const char* admin_pubkey = get_config_value_from_table("admin_pubkey");
|
const char* admin_pubkey = get_config_value_from_table("admin_pubkey");
|
||||||
if (admin_pubkey) {
|
if (admin_pubkey) {
|
||||||
strncpy(g_unified_cache.admin_pubkey, admin_pubkey, sizeof(g_unified_cache.admin_pubkey) - 1);
|
strncpy(g_unified_cache.admin_pubkey, admin_pubkey, sizeof(g_unified_cache.admin_pubkey) - 1);
|
||||||
g_unified_cache.admin_pubkey[sizeof(g_unified_cache.admin_pubkey) - 1] = '\0';
|
g_unified_cache.admin_pubkey[sizeof(g_unified_cache.admin_pubkey) - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* relay_pubkey = get_config_value_from_table("relay_pubkey");
|
const char* relay_pubkey = get_config_value_from_table("relay_pubkey");
|
||||||
if (relay_pubkey) {
|
if (relay_pubkey) {
|
||||||
strncpy(g_unified_cache.relay_pubkey, relay_pubkey, sizeof(g_unified_cache.relay_pubkey) - 1);
|
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';
|
g_unified_cache.relay_pubkey[sizeof(g_unified_cache.relay_pubkey) - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load auth-related config
|
// Load auth-related config
|
||||||
const char* auth_required = get_config_value_from_table("auth_required");
|
const char* auth_required = get_config_value_from_table("auth_required");
|
||||||
g_unified_cache.auth_required = (auth_required && strcmp(auth_required, "true") == 0) ? 1 : 0;
|
g_unified_cache.auth_required = (auth_required && strcmp(auth_required, "true") == 0) ? 1 : 0;
|
||||||
|
|
||||||
const char* admin_enabled = get_config_value_from_table("admin_enabled");
|
const char* admin_enabled = get_config_value_from_table("admin_enabled");
|
||||||
g_unified_cache.admin_enabled = (admin_enabled && strcmp(admin_enabled, "true") == 0) ? 1 : 0;
|
g_unified_cache.admin_enabled = (admin_enabled && strcmp(admin_enabled, "true") == 0) ? 1 : 0;
|
||||||
|
|
||||||
const char* max_file_size = get_config_value_from_table("max_file_size");
|
const char* max_file_size = get_config_value_from_table("max_file_size");
|
||||||
g_unified_cache.max_file_size = max_file_size ? atol(max_file_size) : 104857600; // 100MB default
|
g_unified_cache.max_file_size = max_file_size ? atol(max_file_size) : 104857600; // 100MB default
|
||||||
|
|
||||||
const char* nip42_mode = get_config_value_from_table("nip42_mode");
|
const char* nip42_mode = get_config_value_from_table("nip42_mode");
|
||||||
if (nip42_mode) {
|
if (nip42_mode) {
|
||||||
if (strcmp(nip42_mode, "disabled") == 0) {
|
if (strcmp(nip42_mode, "disabled") == 0) {
|
||||||
@@ -173,18 +252,18 @@ static int refresh_unified_cache_from_table(void) {
|
|||||||
} else {
|
} else {
|
||||||
g_unified_cache.nip42_mode = 1; // Default to optional/enabled
|
g_unified_cache.nip42_mode = 1; // Default to optional/enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* challenge_timeout = get_config_value_from_table("nip42_challenge_timeout");
|
const char* challenge_timeout = get_config_value_from_table("nip42_challenge_timeout");
|
||||||
g_unified_cache.nip42_challenge_timeout = challenge_timeout ? atoi(challenge_timeout) : 600;
|
g_unified_cache.nip42_challenge_timeout = challenge_timeout ? atoi(challenge_timeout) : 600;
|
||||||
|
|
||||||
const char* time_tolerance = get_config_value_from_table("nip42_time_tolerance");
|
const char* time_tolerance = get_config_value_from_table("nip42_time_tolerance");
|
||||||
g_unified_cache.nip42_time_tolerance = time_tolerance ? atoi(time_tolerance) : 300;
|
g_unified_cache.nip42_time_tolerance = time_tolerance ? atoi(time_tolerance) : 300;
|
||||||
|
|
||||||
// Set cache expiration
|
// Set cache expiration
|
||||||
int cache_timeout = get_cache_timeout();
|
int cache_timeout = get_cache_timeout();
|
||||||
g_unified_cache.cache_expires = time(NULL) + cache_timeout;
|
g_unified_cache.cache_expires = time(NULL) + cache_timeout;
|
||||||
g_unified_cache.cache_valid = 1;
|
g_unified_cache.cache_valid = 1;
|
||||||
|
|
||||||
log_info("Unified configuration cache refreshed from database");
|
log_info("Unified configuration cache refreshed from database");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1980,12 +2059,12 @@ int update_config_in_table(const char* key, const char* value) {
|
|||||||
// Populate default config values
|
// Populate default config values
|
||||||
int populate_default_config_values(void) {
|
int populate_default_config_values(void) {
|
||||||
log_info("Populating default configuration values in table...");
|
log_info("Populating default configuration values in table...");
|
||||||
|
|
||||||
// Add all default configuration values to the table
|
// Add all default configuration values to the table
|
||||||
for (size_t i = 0; i < DEFAULT_CONFIG_COUNT; i++) {
|
for (size_t i = 0; i < DEFAULT_CONFIG_COUNT; i++) {
|
||||||
const char* key = DEFAULT_CONFIG_VALUES[i].key;
|
const char* key = DEFAULT_CONFIG_VALUES[i].key;
|
||||||
const char* value = DEFAULT_CONFIG_VALUES[i].value;
|
const char* value = DEFAULT_CONFIG_VALUES[i].value;
|
||||||
|
|
||||||
// Determine data type
|
// Determine data type
|
||||||
const char* data_type = "string";
|
const char* data_type = "string";
|
||||||
if (strcmp(key, "relay_port") == 0 ||
|
if (strcmp(key, "relay_port") == 0 ||
|
||||||
@@ -2009,7 +2088,7 @@ int populate_default_config_values(void) {
|
|||||||
strcmp(key, "nip42_auth_required") == 0) {
|
strcmp(key, "nip42_auth_required") == 0) {
|
||||||
data_type = "boolean";
|
data_type = "boolean";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set category
|
// Set category
|
||||||
const char* category = "general";
|
const char* category = "general";
|
||||||
if (strstr(key, "relay_")) {
|
if (strstr(key, "relay_")) {
|
||||||
@@ -2023,21 +2102,29 @@ int populate_default_config_values(void) {
|
|||||||
} else if (strstr(key, "max_")) {
|
} else if (strstr(key, "max_")) {
|
||||||
category = "limits";
|
category = "limits";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if requires restart
|
// Determine if requires restart (0 = dynamic, 1 = restart required)
|
||||||
int requires_restart = 0;
|
int requires_restart = 0;
|
||||||
if (strcmp(key, "relay_port") == 0) {
|
|
||||||
|
// Restart required configs
|
||||||
|
if (strcmp(key, "relay_port") == 0 ||
|
||||||
|
strcmp(key, "max_connections") == 0 ||
|
||||||
|
strcmp(key, "auth_enabled") == 0 ||
|
||||||
|
strcmp(key, "nip42_auth_required") == 0 ||
|
||||||
|
strcmp(key, "nip42_auth_required_kinds") == 0 ||
|
||||||
|
strcmp(key, "nip42_challenge_timeout") == 0 ||
|
||||||
|
strcmp(key, "database_path") == 0) {
|
||||||
requires_restart = 1;
|
requires_restart = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_config_value_in_table(key, value, data_type, NULL, category, requires_restart) != 0) {
|
if (set_config_value_in_table(key, value, data_type, NULL, category, requires_restart) != 0) {
|
||||||
char error_msg[256];
|
char error_msg[256];
|
||||||
snprintf(error_msg, sizeof(error_msg), "Failed to set default config: %s = %s", key, value);
|
snprintf(error_msg, sizeof(error_msg), "Failed to set default config: %s = %s", key, value);
|
||||||
log_error(error_msg);
|
log_error(error_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log_success("Default configuration values populated");
|
log_success("Default configuration values populated with restart requirements");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3797,10 +3884,59 @@ int handle_config_update_unified(cJSON* event, char* error_message, size_t error
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this config requires restart
|
||||||
|
const char* requires_restart_sql = "SELECT requires_restart FROM config WHERE key = ?";
|
||||||
|
sqlite3_stmt* restart_stmt;
|
||||||
|
int requires_restart = 0;
|
||||||
|
|
||||||
|
if (sqlite3_prepare_v2(g_db, requires_restart_sql, -1, &restart_stmt, NULL) == SQLITE_OK) {
|
||||||
|
sqlite3_bind_text(restart_stmt, 1, key, -1, SQLITE_STATIC);
|
||||||
|
if (sqlite3_step(restart_stmt) == SQLITE_ROW) {
|
||||||
|
requires_restart = sqlite3_column_int(restart_stmt, 0);
|
||||||
|
}
|
||||||
|
sqlite3_finalize(restart_stmt);
|
||||||
|
}
|
||||||
|
|
||||||
// Update the configuration value in the table
|
// Update the configuration value in the table
|
||||||
if (update_config_in_table(key, value) == 0) {
|
if (update_config_in_table(key, value) == 0) {
|
||||||
updates_applied++;
|
updates_applied++;
|
||||||
|
|
||||||
|
// For dynamic configs (requires_restart = 0), refresh cache immediately
|
||||||
|
if (requires_restart == 0) {
|
||||||
|
log_info("Dynamic config updated - refreshing cache");
|
||||||
|
refresh_unified_cache_from_table();
|
||||||
|
|
||||||
|
// Apply selective re-initialization for specific dynamic configs
|
||||||
|
log_info("Applying selective re-initialization for dynamic config changes");
|
||||||
|
if (strcmp(key, "max_subscriptions_per_client") == 0 ||
|
||||||
|
strcmp(key, "max_total_subscriptions") == 0) {
|
||||||
|
log_info("Subscription limits changed - updating subscription manager");
|
||||||
|
update_subscription_manager_config();
|
||||||
|
// Also refresh NIP-11 relay info since max_subscriptions_per_client affects limitation field
|
||||||
|
log_info("Subscription limits changed - reinitializing relay info for NIP-11");
|
||||||
|
init_relay_info();
|
||||||
|
} else if (strcmp(key, "pow_min_difficulty") == 0 ||
|
||||||
|
strcmp(key, "pow_mode") == 0) {
|
||||||
|
log_info("PoW configuration changed - reinitializing PoW system");
|
||||||
|
init_pow_config();
|
||||||
|
} else if (strcmp(key, "nip40_expiration_enabled") == 0 ||
|
||||||
|
strcmp(key, "nip40_expiration_strict") == 0 ||
|
||||||
|
strcmp(key, "nip40_expiration_filter") == 0 ||
|
||||||
|
strcmp(key, "nip40_expiration_grace_period") == 0) {
|
||||||
|
log_info("Expiration configuration changed - reinitializing expiration system");
|
||||||
|
init_expiration_config();
|
||||||
|
} else if (strcmp(key, "relay_description") == 0 ||
|
||||||
|
strcmp(key, "relay_contact") == 0 ||
|
||||||
|
strcmp(key, "relay_software") == 0 ||
|
||||||
|
strcmp(key, "relay_version") == 0 ||
|
||||||
|
strcmp(key, "max_message_length") == 0 ||
|
||||||
|
strcmp(key, "max_event_tags") == 0 ||
|
||||||
|
strcmp(key, "max_content_length") == 0) {
|
||||||
|
log_info("Relay information changed - reinitializing relay info");
|
||||||
|
init_relay_info();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add successful config to response array
|
// Add successful config to response array
|
||||||
cJSON* success_config = cJSON_CreateObject();
|
cJSON* success_config = cJSON_CreateObject();
|
||||||
cJSON_AddStringToObject(success_config, "key", key);
|
cJSON_AddStringToObject(success_config, "key", key);
|
||||||
@@ -3808,15 +3944,16 @@ int handle_config_update_unified(cJSON* event, char* error_message, size_t error
|
|||||||
cJSON_AddStringToObject(success_config, "data_type", data_type);
|
cJSON_AddStringToObject(success_config, "data_type", data_type);
|
||||||
cJSON_AddStringToObject(success_config, "category", category);
|
cJSON_AddStringToObject(success_config, "category", category);
|
||||||
cJSON_AddStringToObject(success_config, "status", "updated");
|
cJSON_AddStringToObject(success_config, "status", "updated");
|
||||||
|
cJSON_AddBoolToObject(success_config, "requires_restart", requires_restart);
|
||||||
cJSON_AddItemToArray(processed_configs, success_config);
|
cJSON_AddItemToArray(processed_configs, success_config);
|
||||||
|
|
||||||
log_success("Config field updated successfully");
|
log_success("Config field updated successfully");
|
||||||
printf(" Updated: %s = %s\n", key, value);
|
printf(" Updated: %s = %s (restart: %s)\n", key, value, requires_restart ? "yes" : "no");
|
||||||
} else {
|
} else {
|
||||||
log_error("Failed to update config field in database");
|
log_error("Failed to update config field in database");
|
||||||
printf(" Failed to update: %s = %s\n", key, value);
|
printf(" Failed to update: %s = %s\n", key, value);
|
||||||
validation_errors++;
|
validation_errors++;
|
||||||
|
|
||||||
// Add failed config to response array
|
// Add failed config to response array
|
||||||
cJSON* failed_config = cJSON_CreateObject();
|
cJSON* failed_config = cJSON_CreateObject();
|
||||||
cJSON_AddStringToObject(failed_config, "key", key);
|
cJSON_AddStringToObject(failed_config, "key", key);
|
||||||
@@ -4162,9 +4299,17 @@ int populate_config_table_from_event(const cJSON* event) {
|
|||||||
category = "limits";
|
category = "limits";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if requires restart
|
// Determine if requires restart (0 = dynamic, 1 = restart required)
|
||||||
int requires_restart = 0;
|
int requires_restart = 0;
|
||||||
if (strcmp(key, "relay_port") == 0) {
|
|
||||||
|
// Restart required configs
|
||||||
|
if (strcmp(key, "relay_port") == 0 ||
|
||||||
|
strcmp(key, "max_connections") == 0 ||
|
||||||
|
strcmp(key, "auth_enabled") == 0 ||
|
||||||
|
strcmp(key, "nip42_auth_required") == 0 ||
|
||||||
|
strcmp(key, "nip42_auth_required_kinds") == 0 ||
|
||||||
|
strcmp(key, "nip42_challenge_timeout") == 0 ||
|
||||||
|
strcmp(key, "database_path") == 0) {
|
||||||
requires_restart = 1;
|
requires_restart = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,10 @@
|
|||||||
#define MAIN_H
|
#define MAIN_H
|
||||||
|
|
||||||
// Version information (auto-updated by build_and_push.sh)
|
// Version information (auto-updated by build_and_push.sh)
|
||||||
#define VERSION "v0.4.2"
|
#define VERSION "v0.4.3"
|
||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 4
|
#define VERSION_MINOR 4
|
||||||
#define VERSION_PATCH 2
|
#define VERSION_PATCH 3
|
||||||
|
|
||||||
// Relay metadata (authoritative source for NIP-11 information)
|
// Relay metadata (authoritative source for NIP-11 information)
|
||||||
#define RELAY_NAME "C-Relay"
|
#define RELAY_NAME "C-Relay"
|
||||||
|
|||||||
133
test_dynamic_config.sh
Executable file
133
test_dynamic_config.sh
Executable file
@@ -0,0 +1,133 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Test dynamic config updates without restart
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration from relay startup
|
||||||
|
ADMIN_PRIVKEY="ddea442930976541e199a05248eb6cd92f2a65ba366a883a8f6880add9bdc9c9"
|
||||||
|
RELAY_PUBKEY="1bd4a5e2e32401737f8c16cc0dfa89b93f25f395770a2896fe78c9fb61582dfc"
|
||||||
|
RELAY_URL="ws://localhost:8888"
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if nak is available
|
||||||
|
if ! command -v nak &> /dev/null; then
|
||||||
|
log_error "nak command not found. Please install nak first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Testing dynamic config updates without restart..."
|
||||||
|
|
||||||
|
# Test 1: Check current NIP-11 info
|
||||||
|
log_info "Checking current NIP-11 relay info..."
|
||||||
|
CURRENT_DESC=$(curl -s -H "Accept: application/nostr+json" http://localhost:8888 | jq -r '.description')
|
||||||
|
log_info "Current description: $CURRENT_DESC"
|
||||||
|
|
||||||
|
# Test 2: Update relay description dynamically
|
||||||
|
NEW_DESC="Dynamic Config Test - Updated at $(date)"
|
||||||
|
log_info "Updating relay description to: $NEW_DESC"
|
||||||
|
|
||||||
|
COMMAND="[\"config_update\", [{\"key\": \"relay_description\", \"value\": \"$NEW_DESC\", \"data_type\": \"string\", \"category\": \"relay\"}]]"
|
||||||
|
|
||||||
|
# Encrypt the command
|
||||||
|
ENCRYPTED_COMMAND=$(nak encrypt "$COMMAND" --sec "$ADMIN_PRIVKEY" --recipient-pubkey "$RELAY_PUBKEY")
|
||||||
|
|
||||||
|
if [ -z "$ENCRYPTED_COMMAND" ]; then
|
||||||
|
log_error "Failed to encrypt config update command"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create admin event
|
||||||
|
ADMIN_EVENT=$(nak event \
|
||||||
|
--kind 23456 \
|
||||||
|
--content "$ENCRYPTED_COMMAND" \
|
||||||
|
--sec "$ADMIN_PRIVKEY" \
|
||||||
|
--tag "p=$RELAY_PUBKEY")
|
||||||
|
|
||||||
|
# Send the admin command
|
||||||
|
log_info "Sending config update command..."
|
||||||
|
ADMIN_RESULT=$(echo "$ADMIN_EVENT" | nak event "$RELAY_URL")
|
||||||
|
|
||||||
|
if echo "$ADMIN_RESULT" | grep -q "error\|failed\|denied"; then
|
||||||
|
log_error "Failed to send config update: $ADMIN_RESULT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Config update command sent successfully"
|
||||||
|
|
||||||
|
# Wait for processing
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# Test 3: Check if NIP-11 info updated without restart
|
||||||
|
log_info "Checking if NIP-11 info was updated without restart..."
|
||||||
|
UPDATED_DESC=$(curl -s -H "Accept: application/nostr+json" http://localhost:8888 | jq -r '.description')
|
||||||
|
|
||||||
|
if [ "$UPDATED_DESC" = "$NEW_DESC" ]; then
|
||||||
|
log_success "SUCCESS: Relay description updated dynamically without restart!"
|
||||||
|
log_success "Old: $CURRENT_DESC"
|
||||||
|
log_success "New: $UPDATED_DESC"
|
||||||
|
else
|
||||||
|
log_error "FAILED: Relay description was not updated"
|
||||||
|
log_error "Expected: $NEW_DESC"
|
||||||
|
log_error "Got: $UPDATED_DESC"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4: Test another dynamic config - max_subscriptions_per_client
|
||||||
|
log_info "Testing another dynamic config: max_subscriptions_per_client"
|
||||||
|
|
||||||
|
# Get current value from database
|
||||||
|
OLD_LIMIT=$(sqlite3 build/*.db "SELECT value FROM config WHERE key = 'max_subscriptions_per_client';" 2>/dev/null || echo "25")
|
||||||
|
log_info "Current max_subscriptions_per_client: $OLD_LIMIT"
|
||||||
|
|
||||||
|
NEW_LIMIT=50
|
||||||
|
|
||||||
|
COMMAND2="[\"config_update\", [{\"key\": \"max_subscriptions_per_client\", \"value\": \"$NEW_LIMIT\", \"data_type\": \"integer\", \"category\": \"limits\"}]]"
|
||||||
|
|
||||||
|
ENCRYPTED_COMMAND2=$(nak encrypt "$COMMAND2" --sec "$ADMIN_PRIVKEY" --recipient-pubkey "$RELAY_PUBKEY")
|
||||||
|
|
||||||
|
ADMIN_EVENT2=$(nak event \
|
||||||
|
--kind 23456 \
|
||||||
|
--content "$ENCRYPTED_COMMAND2" \
|
||||||
|
--sec "$ADMIN_PRIVKEY" \
|
||||||
|
--tag "p=$RELAY_PUBKEY")
|
||||||
|
|
||||||
|
log_info "Updating max_subscriptions_per_client to $NEW_LIMIT..."
|
||||||
|
ADMIN_RESULT2=$(echo "$ADMIN_EVENT2" | nak event "$RELAY_URL")
|
||||||
|
|
||||||
|
if echo "$ADMIN_RESULT2" | grep -q "error\|failed\|denied"; then
|
||||||
|
log_error "Failed to send second config update: $ADMIN_RESULT2"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# Check updated value from database
|
||||||
|
UPDATED_LIMIT=$(sqlite3 build/*.db "SELECT value FROM config WHERE key = 'max_subscriptions_per_client';" 2>/dev/null || echo "25")
|
||||||
|
|
||||||
|
if [ "$UPDATED_LIMIT" = "$NEW_LIMIT" ]; then
|
||||||
|
log_success "SUCCESS: max_subscriptions_per_client updated dynamically!"
|
||||||
|
log_success "Old: $OLD_LIMIT, New: $UPDATED_LIMIT"
|
||||||
|
else
|
||||||
|
log_error "FAILED: max_subscriptions_per_client was not updated"
|
||||||
|
log_error "Expected: $NEW_LIMIT, Got: $UPDATED_LIMIT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Dynamic config update testing completed successfully!"
|
||||||
Reference in New Issue
Block a user