v0.4.3 - feat: Implement dynamic configuration updates without restart
- Add cache refresh mechanism for config updates - Implement selective re-initialization for NIP-11 relay info changes - Categorize configs as dynamic vs restart-required using requires_restart field - Enhance admin API responses with restart requirement information - Add comprehensive test for dynamic config updates - Update documentation for dynamic configuration capabilities Most relay settings can now be updated via admin API without requiring restart, improving operational flexibility while maintaining stability for critical changes.
This commit is contained in:
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
|
||||
- `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
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
// 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
|
||||
static int refresh_unified_cache_from_table(void) {
|
||||
if (!g_db) {
|
||||
log_error("Database not available for cache refresh");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Clear cache
|
||||
memset(&g_unified_cache, 0, sizeof(g_unified_cache));
|
||||
g_unified_cache.cache_lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
|
||||
// Load critical config values from table
|
||||
const char* admin_pubkey = get_config_value_from_table("admin_pubkey");
|
||||
if (admin_pubkey) {
|
||||
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';
|
||||
}
|
||||
|
||||
|
||||
const char* relay_pubkey = get_config_value_from_table("relay_pubkey");
|
||||
if (relay_pubkey) {
|
||||
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';
|
||||
}
|
||||
|
||||
|
||||
// Load auth-related config
|
||||
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;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
const char* nip42_mode = get_config_value_from_table("nip42_mode");
|
||||
if (nip42_mode) {
|
||||
if (strcmp(nip42_mode, "disabled") == 0) {
|
||||
@@ -173,18 +252,18 @@ static int refresh_unified_cache_from_table(void) {
|
||||
} else {
|
||||
g_unified_cache.nip42_mode = 1; // Default to optional/enabled
|
||||
}
|
||||
|
||||
|
||||
const char* challenge_timeout = get_config_value_from_table("nip42_challenge_timeout");
|
||||
g_unified_cache.nip42_challenge_timeout = challenge_timeout ? atoi(challenge_timeout) : 600;
|
||||
|
||||
|
||||
const char* time_tolerance = get_config_value_from_table("nip42_time_tolerance");
|
||||
g_unified_cache.nip42_time_tolerance = time_tolerance ? atoi(time_tolerance) : 300;
|
||||
|
||||
|
||||
// Set cache expiration
|
||||
int cache_timeout = get_cache_timeout();
|
||||
g_unified_cache.cache_expires = time(NULL) + cache_timeout;
|
||||
g_unified_cache.cache_valid = 1;
|
||||
|
||||
|
||||
log_info("Unified configuration cache refreshed from database");
|
||||
return 0;
|
||||
}
|
||||
@@ -1980,12 +2059,12 @@ int update_config_in_table(const char* key, const char* value) {
|
||||
// Populate default config values
|
||||
int populate_default_config_values(void) {
|
||||
log_info("Populating default configuration values in table...");
|
||||
|
||||
|
||||
// Add all default configuration values to the table
|
||||
for (size_t i = 0; i < DEFAULT_CONFIG_COUNT; i++) {
|
||||
const char* key = DEFAULT_CONFIG_VALUES[i].key;
|
||||
const char* value = DEFAULT_CONFIG_VALUES[i].value;
|
||||
|
||||
|
||||
// Determine data type
|
||||
const char* data_type = "string";
|
||||
if (strcmp(key, "relay_port") == 0 ||
|
||||
@@ -2009,7 +2088,7 @@ int populate_default_config_values(void) {
|
||||
strcmp(key, "nip42_auth_required") == 0) {
|
||||
data_type = "boolean";
|
||||
}
|
||||
|
||||
|
||||
// Set category
|
||||
const char* category = "general";
|
||||
if (strstr(key, "relay_")) {
|
||||
@@ -2023,21 +2102,29 @@ int populate_default_config_values(void) {
|
||||
} else if (strstr(key, "max_")) {
|
||||
category = "limits";
|
||||
}
|
||||
|
||||
// Determine if requires restart
|
||||
|
||||
// Determine if requires restart (0 = dynamic, 1 = restart required)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
if (set_config_value_in_table(key, value, data_type, NULL, category, requires_restart) != 0) {
|
||||
char error_msg[256];
|
||||
snprintf(error_msg, sizeof(error_msg), "Failed to set default config: %s = %s", key, value);
|
||||
log_error(error_msg);
|
||||
}
|
||||
}
|
||||
|
||||
log_success("Default configuration values populated");
|
||||
|
||||
log_success("Default configuration values populated with restart requirements");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3797,10 +3884,59 @@ int handle_config_update_unified(cJSON* event, char* error_message, size_t error
|
||||
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
|
||||
if (update_config_in_table(key, value) == 0) {
|
||||
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
|
||||
cJSON* success_config = cJSON_CreateObject();
|
||||
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, "category", category);
|
||||
cJSON_AddStringToObject(success_config, "status", "updated");
|
||||
cJSON_AddBoolToObject(success_config, "requires_restart", requires_restart);
|
||||
cJSON_AddItemToArray(processed_configs, success_config);
|
||||
|
||||
|
||||
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 {
|
||||
log_error("Failed to update config field in database");
|
||||
printf(" Failed to update: %s = %s\n", key, value);
|
||||
validation_errors++;
|
||||
|
||||
|
||||
// Add failed config to response array
|
||||
cJSON* failed_config = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(failed_config, "key", key);
|
||||
@@ -4162,9 +4299,17 @@ int populate_config_table_from_event(const cJSON* event) {
|
||||
category = "limits";
|
||||
}
|
||||
|
||||
// Determine if requires restart
|
||||
// Determine if requires restart (0 = dynamic, 1 = restart required)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
#define MAIN_H
|
||||
|
||||
// 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_MINOR 4
|
||||
#define VERSION_PATCH 2
|
||||
#define VERSION_PATCH 3
|
||||
|
||||
// Relay metadata (authoritative source for NIP-11 information)
|
||||
#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