v0.3.15 - How can administration take so long
This commit is contained in:
160
src/config.c
160
src/config.c
@@ -342,7 +342,7 @@ int store_config_event_in_database(const cJSON* event) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Insert or replace the configuration event (kind 33334 is replaceable)
|
||||
// Insert or replace the configuration event
|
||||
const char* sql = "INSERT OR REPLACE INTO events (id, pubkey, created_at, kind, event_type, content, sig, tags) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
@@ -357,7 +357,7 @@ int store_config_event_in_database(const cJSON* event) {
|
||||
sqlite3_bind_text(stmt, 2, cJSON_GetStringValue(pubkey_obj), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(stmt, 3, (sqlite3_int64)cJSON_GetNumberValue(created_at_obj));
|
||||
sqlite3_bind_int(stmt, 4, (int)cJSON_GetNumberValue(kind_obj));
|
||||
sqlite3_bind_text(stmt, 5, "addressable", -1, SQLITE_STATIC); // kind 33334 is addressable
|
||||
sqlite3_bind_text(stmt, 5, "regular", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 6, cJSON_GetStringValue(content_obj), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 7, cJSON_GetStringValue(sig_obj), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 8, tags_str, -1, SQLITE_TRANSIENT);
|
||||
@@ -384,26 +384,9 @@ cJSON* load_config_event_from_database(const char* relay_pubkey) {
|
||||
sqlite3_stmt* stmt;
|
||||
int rc;
|
||||
|
||||
// Try to get admin pubkey from cache, otherwise find the most recent kind 33334 event
|
||||
const char* admin_pubkey = get_admin_pubkey_cached();
|
||||
if (admin_pubkey && strlen(admin_pubkey) > 0) {
|
||||
sql = "SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE kind = 33334 AND pubkey = ? ORDER BY created_at DESC LIMIT 1";
|
||||
rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
log_error("Failed to prepare configuration event query");
|
||||
return NULL;
|
||||
}
|
||||
sqlite3_bind_text(stmt, 1, admin_pubkey, -1, SQLITE_STATIC);
|
||||
} else {
|
||||
// During existing relay startup, we don't know the admin pubkey yet
|
||||
// Look for any kind 33334 configuration event (should only be one per relay)
|
||||
sql = "SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE kind = 33334 ORDER BY created_at DESC LIMIT 1";
|
||||
rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
log_error("Failed to prepare configuration event query");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
// Configuration is now managed through config table, not events
|
||||
log_info("Configuration events are no longer stored in events table");
|
||||
return NULL;
|
||||
|
||||
cJSON* event = NULL;
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
@@ -937,7 +920,7 @@ cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
|
||||
|
||||
// Create and sign event using nostr_core_lib
|
||||
cJSON* event = nostr_create_and_sign_event(
|
||||
33334, // kind
|
||||
23455, // kind
|
||||
"C Nostr Relay Configuration", // content
|
||||
tags, // tags
|
||||
admin_privkey_bytes, // private key bytes for signing
|
||||
@@ -1614,7 +1597,7 @@ int process_configuration_event(const cJSON* event) {
|
||||
cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
|
||||
cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
|
||||
|
||||
if (!kind_obj || cJSON_GetNumberValue(kind_obj) != 33334) {
|
||||
if (!kind_obj || (cJSON_GetNumberValue(kind_obj) != 23455 && cJSON_GetNumberValue(kind_obj) != 23456)) {
|
||||
log_error("Invalid event kind for configuration");
|
||||
return -1;
|
||||
}
|
||||
@@ -1775,7 +1758,7 @@ int apply_runtime_config_handlers(const cJSON* old_config, const cJSON* new_conf
|
||||
if (handlers_applied > 0) {
|
||||
char audit_msg[512];
|
||||
snprintf(audit_msg, sizeof(audit_msg),
|
||||
"Configuration updated via kind 33334 event - %d system components reinitialized",
|
||||
"Configuration updated via admin event - %d system components reinitialized",
|
||||
handlers_applied);
|
||||
log_success(audit_msg);
|
||||
} else {
|
||||
@@ -1832,7 +1815,7 @@ int apply_configuration_from_event(const cJSON* event) {
|
||||
// REAL-TIME EVENT HANDLER (called from main.c)
|
||||
// ================================
|
||||
|
||||
// Handle kind 33334 configuration events received via WebSocket
|
||||
// Handle configuration events received via WebSocket
|
||||
int handle_configuration_event(cJSON* event, char* error_message, size_t error_size) {
|
||||
if (!event) {
|
||||
snprintf(error_message, error_size, "invalid: null configuration event");
|
||||
@@ -2121,12 +2104,6 @@ int process_admin_event_in_config(cJSON* event, char* error_message, size_t erro
|
||||
case 23456: // New ephemeral auth rules management
|
||||
log_info("DEBUG: Routing to process_admin_auth_event (kind 23456)");
|
||||
return process_admin_auth_event(event, error_message, error_size, wsi);
|
||||
case 33334: // Legacy addressable config events (backward compatibility)
|
||||
log_info("DEBUG: Routing to process_admin_config_event (legacy kind 33334)");
|
||||
return process_admin_config_event(event, error_message, error_size);
|
||||
case 33335: // Legacy addressable auth events (backward compatibility)
|
||||
log_info("DEBUG: Routing to process_admin_auth_event (legacy kind 33335)");
|
||||
return process_admin_auth_event(event, error_message, error_size, wsi);
|
||||
default:
|
||||
log_error("DEBUG: Unsupported admin event kind");
|
||||
printf(" Unsupported kind: %d\n", kind);
|
||||
@@ -2135,7 +2112,7 @@ int process_admin_event_in_config(cJSON* event, char* error_message, size_t erro
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Kind 23455 configuration management events and legacy Kind 33334
|
||||
// Handle Kind 23455 configuration management events
|
||||
int process_admin_config_event(cJSON* event, char* error_message, size_t error_size) {
|
||||
cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
|
||||
int kind = kind_obj ? (int)cJSON_GetNumberValue(kind_obj) : 0;
|
||||
@@ -2211,10 +2188,6 @@ int process_admin_config_event(cJSON* event, char* error_message, size_t error_s
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip relay identifier tag (only for legacy addressable events)
|
||||
if (kind == 33334 && strcmp(key, "d") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update configuration in table
|
||||
if (update_config_in_table(key, value) == 0) {
|
||||
@@ -2238,7 +2211,7 @@ int process_admin_config_event(cJSON* event, char* error_message, size_t error_s
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle Kind 23456 auth rules management and legacy Kind 33335
|
||||
// Handle Kind 23456 auth rules management
|
||||
int process_admin_auth_event(cJSON* event, char* error_message, size_t error_size, struct lws* wsi) {
|
||||
log_info("DEBUG: Entering process_admin_auth_event()");
|
||||
|
||||
@@ -2267,13 +2240,6 @@ int process_admin_auth_event(cJSON* event, char* error_message, size_t error_siz
|
||||
return handle_kind_23456_unified(event, error_message, error_size, wsi);
|
||||
}
|
||||
|
||||
// Legacy Kind 33335 events use the unified handler as well
|
||||
if (kind == 33335) {
|
||||
log_info("DEBUG: Routing legacy Kind 33335 to unified handler");
|
||||
// For legacy events, we still use the unified handler but may need special processing
|
||||
// The unified handler already supports all the functionality
|
||||
return handle_kind_23456_unified(event, error_message, error_size, wsi);
|
||||
}
|
||||
|
||||
log_error("DEBUG: Unsupported auth event kind in process_admin_auth_event");
|
||||
printf(" Unsupported kind: %d\n", kind);
|
||||
@@ -3072,6 +3038,85 @@ int handle_system_command_unified(cJSON* event, const char* command, char* error
|
||||
snprintf(error_message, error_size, "failed to send clear auth rules response");
|
||||
return -1;
|
||||
}
|
||||
else if (strcmp(command, "delete_auth_rule") == 0) {
|
||||
// Get rule parameters from tags
|
||||
const char* rule_type = get_tag_value(event, "system_command", 2);
|
||||
const char* pattern_type = get_tag_value(event, "system_command", 3);
|
||||
const char* pattern_value = get_tag_value(event, "system_command", 4);
|
||||
|
||||
if (!rule_type || !pattern_type || !pattern_value) {
|
||||
snprintf(error_message, error_size, "invalid: delete_auth_rule requires rule_type, pattern_type, and pattern_value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_info("Processing delete auth rule command");
|
||||
printf(" Rule type: %s\n", rule_type);
|
||||
printf(" Pattern type: %s\n", pattern_type);
|
||||
printf(" Pattern value: %s\n", pattern_value);
|
||||
|
||||
// Check if rule exists before deletion
|
||||
const char* check_sql = "SELECT COUNT(*) FROM auth_rules WHERE rule_type = ? AND pattern_type = ? AND pattern_value = ?";
|
||||
sqlite3_stmt* check_stmt;
|
||||
|
||||
int check_rc = sqlite3_prepare_v2(g_db, check_sql, -1, &check_stmt, NULL);
|
||||
if (check_rc != SQLITE_OK) {
|
||||
snprintf(error_message, error_size, "failed to prepare rule existence check");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sqlite3_bind_text(check_stmt, 1, rule_type, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(check_stmt, 2, pattern_type, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(check_stmt, 3, pattern_value, -1, SQLITE_STATIC);
|
||||
|
||||
int rule_exists = 0;
|
||||
if (sqlite3_step(check_stmt) == SQLITE_ROW) {
|
||||
rule_exists = sqlite3_column_int(check_stmt, 0) > 0;
|
||||
}
|
||||
sqlite3_finalize(check_stmt);
|
||||
|
||||
if (!rule_exists) {
|
||||
snprintf(error_message, error_size, "error: auth rule not found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Delete the specific auth rule
|
||||
if (remove_auth_rule_from_config(rule_type, pattern_type, pattern_value) != 0) {
|
||||
snprintf(error_message, error_size, "failed to delete auth rule from database");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Build response
|
||||
cJSON* response = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(response, "command", "delete_auth_rule");
|
||||
cJSON_AddStringToObject(response, "rule_type", rule_type);
|
||||
cJSON_AddStringToObject(response, "pattern_type", pattern_type);
|
||||
cJSON_AddStringToObject(response, "pattern_value", pattern_value);
|
||||
cJSON_AddStringToObject(response, "status", "success");
|
||||
cJSON_AddNumberToObject(response, "timestamp", (double)time(NULL));
|
||||
|
||||
printf("Deleted auth rule: %s %s:%s\n", rule_type, pattern_type, pattern_value);
|
||||
|
||||
// Get admin pubkey from event for response
|
||||
cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
|
||||
const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
|
||||
|
||||
if (!admin_pubkey) {
|
||||
cJSON_Delete(response);
|
||||
snprintf(error_message, error_size, "missing admin pubkey for response");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Send response as signed kind 23457 event
|
||||
if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
|
||||
log_success("Delete auth rule command completed successfully with signed response");
|
||||
cJSON_Delete(response);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cJSON_Delete(response);
|
||||
snprintf(error_message, error_size, "failed to send delete auth rule response");
|
||||
return -1;
|
||||
}
|
||||
else if (strcmp(command, "system_status") == 0) {
|
||||
// Build system status response
|
||||
cJSON* response = cJSON_CreateObject();
|
||||
@@ -3535,7 +3580,7 @@ int process_startup_config_event(const cJSON* event) {
|
||||
|
||||
// Validate event structure first
|
||||
cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
|
||||
if (!kind_obj || cJSON_GetNumberValue(kind_obj) != 33334) {
|
||||
if (!kind_obj || cJSON_GetNumberValue(kind_obj) != 23455) {
|
||||
log_error("Invalid event kind for startup configuration");
|
||||
return -1;
|
||||
}
|
||||
@@ -3633,14 +3678,14 @@ int process_startup_config_event_with_fallback(const cJSON* event) {
|
||||
// DYNAMIC EVENT GENERATION FROM CONFIG TABLE
|
||||
// ================================
|
||||
|
||||
// Generate synthetic kind 33334 configuration event from current config table data
|
||||
// Generate synthetic configuration event from current config table data
|
||||
cJSON* generate_config_event_from_table(void) {
|
||||
if (!g_db) {
|
||||
log_error("Database not available for config event generation");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log_info("Generating synthetic kind 33334 event from config table...");
|
||||
log_info("Generating synthetic configuration event from config table...");
|
||||
|
||||
// Get relay pubkey for event generation
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
@@ -3664,7 +3709,7 @@ cJSON* generate_config_event_from_table(void) {
|
||||
cJSON_AddStringToObject(event, "id", "synthetic_config_event_id");
|
||||
cJSON_AddStringToObject(event, "pubkey", relay_pubkey); // Use relay pubkey as event author
|
||||
cJSON_AddNumberToObject(event, "created_at", (double)time(NULL));
|
||||
cJSON_AddNumberToObject(event, "kind", 33334);
|
||||
cJSON_AddNumberToObject(event, "kind", 23455);
|
||||
cJSON_AddStringToObject(event, "content", "C Nostr Relay Configuration");
|
||||
cJSON_AddStringToObject(event, "sig", "synthetic_signature");
|
||||
|
||||
@@ -3724,13 +3769,13 @@ cJSON* generate_config_event_from_table(void) {
|
||||
|
||||
char success_msg[256];
|
||||
snprintf(success_msg, sizeof(success_msg),
|
||||
"Generated synthetic kind 33334 event with %d configuration items", config_items_added);
|
||||
"Generated synthetic configuration event with %d configuration items", config_items_added);
|
||||
log_success(success_msg);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
// Check if a REQ filter requests kind 33334 events
|
||||
// Check if a REQ filter requests configuration events
|
||||
int req_filter_requests_config_events(const cJSON* filter) {
|
||||
if (!filter || !cJSON_IsObject(filter)) {
|
||||
return 0;
|
||||
@@ -3741,10 +3786,11 @@ int req_filter_requests_config_events(const cJSON* filter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if kinds array contains 33334
|
||||
// Check if kinds array contains configuration event kinds
|
||||
cJSON* kind_item = NULL;
|
||||
cJSON_ArrayForEach(kind_item, kinds) {
|
||||
if (cJSON_IsNumber(kind_item) && (int)cJSON_GetNumberValue(kind_item) == 33334) {
|
||||
int kind_val = (int)cJSON_GetNumberValue(kind_item);
|
||||
if (cJSON_IsNumber(kind_item) && (kind_val == 23455 || kind_val == 23456)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -3758,7 +3804,7 @@ cJSON* generate_synthetic_config_event_for_subscription(const char* sub_id, cons
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check if any filter requests kind 33334
|
||||
// Check if any filter requests configuration events
|
||||
int requests_config = 0;
|
||||
|
||||
if (cJSON_IsArray(filters)) {
|
||||
@@ -3778,7 +3824,7 @@ cJSON* generate_synthetic_config_event_for_subscription(const char* sub_id, cons
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log_info("Generating synthetic kind 33334 event for subscription");
|
||||
log_info("Generating synthetic configuration event for subscription");
|
||||
|
||||
// Generate synthetic config event from table
|
||||
cJSON* config_event = generate_config_event_from_table();
|
||||
@@ -3793,12 +3839,12 @@ cJSON* generate_synthetic_config_event_for_subscription(const char* sub_id, cons
|
||||
cJSON_AddItemToArray(event_msg, cJSON_CreateString(sub_id));
|
||||
cJSON_AddItemToArray(event_msg, config_event);
|
||||
|
||||
log_success("Generated synthetic kind 33334 configuration event message");
|
||||
log_success("Generated synthetic configuration event message");
|
||||
return event_msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a synthetic kind 33334 configuration event from config table data
|
||||
* Generate a synthetic configuration event from config table data
|
||||
* This allows WebSocket clients to fetch configuration via REQ messages
|
||||
* Returns JSON string that must be freed by caller
|
||||
*/
|
||||
|
||||
@@ -98,6 +98,7 @@ typedef struct {
|
||||
int port_override; // -1 = not set, >0 = port value
|
||||
char admin_privkey_override[65]; // Empty string = not set, 64-char hex = override
|
||||
char relay_privkey_override[65]; // Empty string = not set, 64-char hex = override
|
||||
int strict_port; // 0 = allow port increment, 1 = fail if exact port unavailable
|
||||
} cli_options_t;
|
||||
|
||||
// Global unified configuration cache
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
* Default Configuration Event Template
|
||||
*
|
||||
* This header contains the default configuration values for the C Nostr Relay.
|
||||
* These values are used to create the initial kind 33334 configuration event
|
||||
* during first-time startup.
|
||||
* These values are used to populate the config table during first-time startup.
|
||||
*
|
||||
* IMPORTANT: These values should never be accessed directly by other parts
|
||||
* of the program. They are only used during initial configuration event creation.
|
||||
|
||||
51
src/main.c
51
src/main.c
@@ -224,10 +224,7 @@ int handle_event_message(cJSON* event, char* error_message, size_t error_size);
|
||||
// Forward declaration for unified validation
|
||||
int nostr_validate_unified_request(const char* json_string, size_t json_length);
|
||||
|
||||
// Forward declaration for configuration event handling (kind 33334)
|
||||
int handle_configuration_event(cJSON* event, char* error_message, size_t error_size);
|
||||
|
||||
// Forward declaration for admin event processing (kinds 33334 and 33335)
|
||||
// Forward declaration for admin event processing (kinds 23455 and 23456)
|
||||
int process_admin_event_in_config(cJSON* event, char* error_message, size_t error_size, struct lws* wsi);
|
||||
|
||||
// Forward declaration for enhanced admin event authorization
|
||||
@@ -3035,7 +3032,7 @@ int is_authorized_admin_event(cJSON* event, char* error_buffer, size_t error_buf
|
||||
}
|
||||
|
||||
int event_kind = kind_json->valueint;
|
||||
if (event_kind != 33334 && event_kind != 33335 && event_kind != 23455 && event_kind != 23456) {
|
||||
if (event_kind != 23455 && event_kind != 23456) {
|
||||
snprintf(error_buffer, error_buffer_size, "Event kind %d is not an admin event type", event_kind);
|
||||
return -1;
|
||||
}
|
||||
@@ -3356,7 +3353,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
||||
// Cleanup event JSON string
|
||||
free(event_json_str);
|
||||
|
||||
// Check for admin events (kinds 33334, 33335, 23455, and 23456) and intercept them
|
||||
// Check for admin events (kinds 23455 and 23456) and intercept them
|
||||
if (result == 0) {
|
||||
cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
|
||||
if (kind_obj && cJSON_IsNumber(kind_obj)) {
|
||||
@@ -3378,7 +3375,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
||||
}
|
||||
}
|
||||
|
||||
if (event_kind == 33334 || event_kind == 33335 || event_kind == 23455 || event_kind == 23456) {
|
||||
if (event_kind == 23455 || event_kind == 23456) {
|
||||
// Enhanced admin event security - check authorization first
|
||||
log_info("DEBUG ADMIN: Admin event detected, checking authorization");
|
||||
|
||||
@@ -3697,7 +3694,7 @@ int check_port_available(int port) {
|
||||
}
|
||||
|
||||
// Start libwebsockets-based WebSocket Nostr relay server
|
||||
int start_websocket_relay(int port_override) {
|
||||
int start_websocket_relay(int port_override, int strict_port) {
|
||||
struct lws_context_creation_info info;
|
||||
|
||||
log_info("Starting libwebsockets-based Nostr relay server...");
|
||||
@@ -3707,7 +3704,7 @@ int start_websocket_relay(int port_override) {
|
||||
int configured_port = (port_override > 0) ? port_override : get_config_int("relay_port", DEFAULT_PORT);
|
||||
int actual_port = configured_port;
|
||||
int port_attempts = 0;
|
||||
const int max_port_attempts = 5;
|
||||
const int max_port_attempts = 10; // Increased from 5 to 10
|
||||
|
||||
// Minimal libwebsockets configuration
|
||||
info.protocols = protocols;
|
||||
@@ -3726,8 +3723,8 @@ int start_websocket_relay(int port_override) {
|
||||
// Max payload size for Nostr events
|
||||
info.max_http_header_data = 4096;
|
||||
|
||||
// Find an available port with pre-checking
|
||||
while (port_attempts < max_port_attempts) {
|
||||
// Find an available port with pre-checking (or fail immediately in strict mode)
|
||||
while (port_attempts < (strict_port ? 1 : max_port_attempts)) {
|
||||
char attempt_msg[256];
|
||||
snprintf(attempt_msg, sizeof(attempt_msg), "Checking port availability: %d", actual_port);
|
||||
log_info(attempt_msg);
|
||||
@@ -3735,7 +3732,13 @@ int start_websocket_relay(int port_override) {
|
||||
// Pre-check if port is available
|
||||
if (!check_port_available(actual_port)) {
|
||||
port_attempts++;
|
||||
if (port_attempts < max_port_attempts) {
|
||||
if (strict_port) {
|
||||
char error_msg[256];
|
||||
snprintf(error_msg, sizeof(error_msg),
|
||||
"Strict port mode: port %d is not available", actual_port);
|
||||
log_error(error_msg);
|
||||
return -1;
|
||||
} else if (port_attempts < max_port_attempts) {
|
||||
char retry_msg[256];
|
||||
snprintf(retry_msg, sizeof(retry_msg), "Port %d is in use, trying port %d (attempt %d/%d)",
|
||||
actual_port, actual_port + 1, port_attempts + 1, max_port_attempts);
|
||||
@@ -3774,7 +3777,13 @@ int start_websocket_relay(int port_override) {
|
||||
log_warning(lws_error_msg);
|
||||
|
||||
port_attempts++;
|
||||
if (port_attempts < max_port_attempts) {
|
||||
if (strict_port) {
|
||||
char error_msg[256];
|
||||
snprintf(error_msg, sizeof(error_msg),
|
||||
"Strict port mode: failed to bind to port %d", actual_port);
|
||||
log_error(error_msg);
|
||||
break;
|
||||
} else if (port_attempts < max_port_attempts) {
|
||||
actual_port++;
|
||||
continue;
|
||||
}
|
||||
@@ -3840,6 +3849,7 @@ void print_usage(const char* program_name) {
|
||||
printf(" -p, --port PORT Override relay port (first-time startup only)\n");
|
||||
printf(" -a, --admin-privkey HEX Override admin private key (64-char hex)\n");
|
||||
printf(" -r, --relay-privkey HEX Override relay private key (64-char hex)\n");
|
||||
printf(" --strict-port Fail if exact port is unavailable (no port increment)\n");
|
||||
printf("\n");
|
||||
printf("Configuration:\n");
|
||||
printf(" This relay uses event-based configuration stored in the database.\n");
|
||||
@@ -3848,10 +3858,16 @@ void print_usage(const char* program_name) {
|
||||
printf(" After initial setup, all configuration is managed via database events.\n");
|
||||
printf(" Database file: <relay_pubkey>.db (created automatically)\n");
|
||||
printf("\n");
|
||||
printf("Port Binding:\n");
|
||||
printf(" Default: Try up to 10 consecutive ports if requested port is busy\n");
|
||||
printf(" --strict-port: Fail immediately if exact requested port is unavailable\n");
|
||||
printf("\n");
|
||||
printf("Examples:\n");
|
||||
printf(" %s # Start relay (auto-configure on first run)\n", program_name);
|
||||
printf(" %s -p 8080 # First-time setup with port 8080\n", program_name);
|
||||
printf(" %s --port 9000 # First-time setup with port 9000\n", program_name);
|
||||
printf(" %s --strict-port # Fail if default port 8888 is unavailable\n", program_name);
|
||||
printf(" %s -p 8080 --strict-port # Fail if port 8080 is unavailable\n", program_name);
|
||||
printf(" %s --help # Show this help\n", program_name);
|
||||
printf(" %s --version # Show version info\n", program_name);
|
||||
printf("\n");
|
||||
@@ -3870,7 +3886,8 @@ int main(int argc, char* argv[]) {
|
||||
cli_options_t cli_options = {
|
||||
.port_override = -1, // -1 = not set
|
||||
.admin_privkey_override = {0}, // Empty string = not set
|
||||
.relay_privkey_override = {0} // Empty string = not set
|
||||
.relay_privkey_override = {0}, // Empty string = not set
|
||||
.strict_port = 0 // 0 = allow port increment (default)
|
||||
};
|
||||
|
||||
// Parse command line arguments
|
||||
@@ -3965,6 +3982,10 @@ int main(int argc, char* argv[]) {
|
||||
i++; // Skip the key argument
|
||||
|
||||
log_info("Relay private key override specified");
|
||||
} else if (strcmp(argv[i], "--strict-port") == 0) {
|
||||
// Strict port mode option
|
||||
cli_options.strict_port = 1;
|
||||
log_info("Strict port mode enabled - will fail if exact port is unavailable");
|
||||
} else {
|
||||
log_error("Unknown argument. Use --help for usage information.");
|
||||
print_usage(argv[0]);
|
||||
@@ -4186,7 +4207,7 @@ int main(int argc, char* argv[]) {
|
||||
log_info("Starting relay server...");
|
||||
|
||||
// Start WebSocket Nostr relay server (port from configuration)
|
||||
int result = start_websocket_relay(-1); // Let config system determine port
|
||||
int result = start_websocket_relay(-1, cli_options.strict_port); // Let config system determine port, pass strict_port flag
|
||||
|
||||
// Cleanup
|
||||
cleanup_relay_info();
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
static const char* const EMBEDDED_SCHEMA_SQL =
|
||||
"-- C Nostr Relay Database Schema\n\
|
||||
-- SQLite schema for storing Nostr events with JSON tags support\n\
|
||||
-- Event-based configuration system using kind 33334 Nostr events\n\
|
||||
-- Configuration system using config table\n\
|
||||
\n\
|
||||
-- Schema version tracking\n\
|
||||
PRAGMA user_version = 7;\n\
|
||||
|
||||
Reference in New Issue
Block a user