v0.2.2 - Working on config setup
This commit is contained in:
223
src/main.c
223
src/main.c
@@ -15,26 +15,7 @@
|
||||
#include "../nostr_core_lib/cjson/cJSON.h"
|
||||
#include "../nostr_core_lib/nostr_core/nostr_core.h"
|
||||
#include "../nostr_core_lib/nostr_core/nip013.h" // NIP-13: Proof of Work
|
||||
|
||||
// Server Configuration
|
||||
#define DEFAULT_PORT 8888
|
||||
#define DEFAULT_HOST "127.0.0.1"
|
||||
#define DATABASE_PATH "db/c_nostr_relay.db"
|
||||
#define MAX_CLIENTS 100
|
||||
|
||||
// Persistent subscription system configuration
|
||||
#define MAX_SUBSCRIPTIONS_PER_CLIENT 20
|
||||
#define MAX_TOTAL_SUBSCRIPTIONS 5000
|
||||
#define MAX_FILTERS_PER_SUBSCRIPTION 10
|
||||
#define SUBSCRIPTION_ID_MAX_LENGTH 64
|
||||
#define CLIENT_IP_MAX_LENGTH 64
|
||||
|
||||
// NIP-11 relay information configuration
|
||||
#define RELAY_NAME_MAX_LENGTH 128
|
||||
#define RELAY_DESCRIPTION_MAX_LENGTH 1024
|
||||
#define RELAY_URL_MAX_LENGTH 256
|
||||
#define RELAY_CONTACT_MAX_LENGTH 128
|
||||
#define RELAY_PUBKEY_MAX_LENGTH 65 // 64 hex chars + null terminator
|
||||
#include "config.h" // Configuration management system
|
||||
|
||||
// Color constants for logging
|
||||
#define RED "\033[31m"
|
||||
@@ -45,7 +26,7 @@
|
||||
#define RESET "\033[0m"
|
||||
|
||||
// Global state
|
||||
static sqlite3* g_db = NULL;
|
||||
sqlite3* g_db = NULL; // Non-static so config.c can access it
|
||||
static int g_server_running = 1;
|
||||
static struct lws_context *ws_context = NULL;
|
||||
|
||||
@@ -188,8 +169,8 @@ static subscription_manager_t g_subscription_manager = {
|
||||
.active_subscriptions = NULL,
|
||||
.subscriptions_lock = PTHREAD_MUTEX_INITIALIZER,
|
||||
.total_subscriptions = 0,
|
||||
.max_subscriptions_per_client = MAX_SUBSCRIPTIONS_PER_CLIENT,
|
||||
.max_total_subscriptions = MAX_TOTAL_SUBSCRIPTIONS,
|
||||
.max_subscriptions_per_client = MAX_SUBSCRIPTIONS_PER_CLIENT, // Will be updated from config
|
||||
.max_total_subscriptions = MAX_TOTAL_SUBSCRIPTIONS, // Will be updated from config
|
||||
.total_created = 0,
|
||||
.total_events_broadcast = 0
|
||||
};
|
||||
@@ -200,6 +181,9 @@ void log_success(const char* message);
|
||||
void log_error(const char* message);
|
||||
void log_warning(const char* message);
|
||||
|
||||
// Forward declaration for subscription manager configuration
|
||||
void update_subscription_manager_config(void);
|
||||
|
||||
// Forward declarations for subscription database logging
|
||||
void log_subscription_created(const subscription_t* sub);
|
||||
void log_subscription_closed(const char* sub_id, const char* client_ip, const char* reason);
|
||||
@@ -955,6 +939,19 @@ void log_warning(const char* message) {
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// Update subscription manager configuration from config system
|
||||
void update_subscription_manager_config(void) {
|
||||
g_subscription_manager.max_subscriptions_per_client = get_config_int("max_subscriptions_per_client", MAX_SUBSCRIPTIONS_PER_CLIENT);
|
||||
g_subscription_manager.max_total_subscriptions = get_config_int("max_total_subscriptions", MAX_TOTAL_SUBSCRIPTIONS);
|
||||
|
||||
char config_msg[256];
|
||||
snprintf(config_msg, sizeof(config_msg),
|
||||
"Subscription limits: max_per_client=%d, max_total=%d",
|
||||
g_subscription_manager.max_subscriptions_per_client,
|
||||
g_subscription_manager.max_total_subscriptions);
|
||||
log_info(config_msg);
|
||||
}
|
||||
|
||||
// Signal handler for graceful shutdown
|
||||
void signal_handler(int sig) {
|
||||
if (sig == SIGINT || sig == SIGTERM) {
|
||||
@@ -1288,13 +1285,47 @@ int mark_event_as_deleted(const char* event_id, const char* deletion_event_id, c
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize relay information with default values
|
||||
// Initialize relay information using configuration system
|
||||
void init_relay_info() {
|
||||
// Set default relay information
|
||||
strncpy(g_relay_info.name, "C Nostr Relay", sizeof(g_relay_info.name) - 1);
|
||||
strncpy(g_relay_info.description, "A high-performance Nostr relay implemented in C with SQLite storage", sizeof(g_relay_info.description) - 1);
|
||||
strncpy(g_relay_info.software, "https://github.com/teknari/c-relay", sizeof(g_relay_info.software) - 1);
|
||||
strncpy(g_relay_info.version, "0.1.0", sizeof(g_relay_info.version) - 1);
|
||||
// Load relay information from configuration system
|
||||
const char* relay_name = get_config_value("relay_name");
|
||||
if (relay_name) {
|
||||
strncpy(g_relay_info.name, relay_name, sizeof(g_relay_info.name) - 1);
|
||||
} else {
|
||||
strncpy(g_relay_info.name, "C Nostr Relay", sizeof(g_relay_info.name) - 1);
|
||||
}
|
||||
|
||||
const char* relay_description = get_config_value("relay_description");
|
||||
if (relay_description) {
|
||||
strncpy(g_relay_info.description, relay_description, sizeof(g_relay_info.description) - 1);
|
||||
} else {
|
||||
strncpy(g_relay_info.description, "A high-performance Nostr relay implemented in C with SQLite storage", sizeof(g_relay_info.description) - 1);
|
||||
}
|
||||
|
||||
const char* relay_software = get_config_value("relay_software");
|
||||
if (relay_software) {
|
||||
strncpy(g_relay_info.software, relay_software, sizeof(g_relay_info.software) - 1);
|
||||
} else {
|
||||
strncpy(g_relay_info.software, "https://github.com/laantungir/c-relay", sizeof(g_relay_info.software) - 1);
|
||||
}
|
||||
|
||||
const char* relay_version = get_config_value("relay_version");
|
||||
if (relay_version) {
|
||||
strncpy(g_relay_info.version, relay_version, sizeof(g_relay_info.version) - 1);
|
||||
} else {
|
||||
strncpy(g_relay_info.version, "0.2.0", sizeof(g_relay_info.version) - 1);
|
||||
}
|
||||
|
||||
// Load optional fields
|
||||
const char* relay_contact = get_config_value("relay_contact");
|
||||
if (relay_contact) {
|
||||
strncpy(g_relay_info.contact, relay_contact, sizeof(g_relay_info.contact) - 1);
|
||||
}
|
||||
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
if (relay_pubkey) {
|
||||
strncpy(g_relay_info.pubkey, relay_pubkey, sizeof(g_relay_info.pubkey) - 1);
|
||||
}
|
||||
|
||||
// Initialize supported NIPs array
|
||||
g_relay_info.supported_nips = cJSON_CreateArray();
|
||||
@@ -1308,22 +1339,22 @@ void init_relay_info() {
|
||||
cJSON_AddItemToArray(g_relay_info.supported_nips, cJSON_CreateNumber(40)); // NIP-40: Expiration Timestamp
|
||||
}
|
||||
|
||||
// Initialize server limitations
|
||||
// Initialize server limitations using configuration
|
||||
g_relay_info.limitation = cJSON_CreateObject();
|
||||
if (g_relay_info.limitation) {
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_message_length", 16384);
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_subscriptions", MAX_SUBSCRIPTIONS_PER_CLIENT);
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_limit", 5000);
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_message_length", get_config_int("max_message_length", 16384));
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_subscriptions", get_config_int("max_subscriptions_per_client", 20));
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_limit", get_config_int("max_limit", 5000));
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_subid_length", SUBSCRIPTION_ID_MAX_LENGTH);
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_event_tags", 100);
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_content_length", 8196);
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_event_tags", get_config_int("max_event_tags", 100));
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "max_content_length", get_config_int("max_content_length", 8196));
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "min_pow_difficulty", g_pow_config.min_pow_difficulty);
|
||||
cJSON_AddBoolToObject(g_relay_info.limitation, "auth_required", cJSON_False);
|
||||
cJSON_AddBoolToObject(g_relay_info.limitation, "auth_required", get_config_bool("admin_enabled", 0) ? cJSON_True : cJSON_False);
|
||||
cJSON_AddBoolToObject(g_relay_info.limitation, "payment_required", cJSON_False);
|
||||
cJSON_AddBoolToObject(g_relay_info.limitation, "restricted_writes", cJSON_False);
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "created_at_lower_limit", 0);
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "created_at_upper_limit", 2147483647);
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "default_limit", 500);
|
||||
cJSON_AddNumberToObject(g_relay_info.limitation, "default_limit", get_config_int("default_limit", 500));
|
||||
}
|
||||
|
||||
// Initialize empty retention policies (can be configured later)
|
||||
@@ -1636,48 +1667,39 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize PoW configuration with environment variables and defaults
|
||||
// Initialize PoW configuration using configuration system
|
||||
void init_pow_config() {
|
||||
log_info("Initializing NIP-13 Proof of Work configuration");
|
||||
|
||||
// Initialize with defaults (already set in struct initialization)
|
||||
// Load PoW settings from configuration system
|
||||
g_pow_config.enabled = get_config_bool("pow_enabled", 1);
|
||||
g_pow_config.min_pow_difficulty = get_config_int("pow_min_difficulty", 0);
|
||||
|
||||
// Check environment variables for configuration
|
||||
const char* pow_enabled_env = getenv("RELAY_POW_ENABLED");
|
||||
if (pow_enabled_env) {
|
||||
g_pow_config.enabled = (strcmp(pow_enabled_env, "1") == 0 ||
|
||||
strcmp(pow_enabled_env, "true") == 0 ||
|
||||
strcmp(pow_enabled_env, "yes") == 0);
|
||||
}
|
||||
|
||||
const char* min_diff_env = getenv("RELAY_MIN_POW_DIFFICULTY");
|
||||
if (min_diff_env) {
|
||||
int min_diff = atoi(min_diff_env);
|
||||
if (min_diff >= 0 && min_diff <= 64) { // Reasonable bounds
|
||||
g_pow_config.min_pow_difficulty = min_diff;
|
||||
}
|
||||
}
|
||||
|
||||
const char* pow_mode_env = getenv("RELAY_POW_MODE");
|
||||
if (pow_mode_env) {
|
||||
if (strcmp(pow_mode_env, "strict") == 0) {
|
||||
// Get PoW mode from configuration
|
||||
const char* pow_mode = get_config_value("pow_mode");
|
||||
if (pow_mode) {
|
||||
if (strcmp(pow_mode, "strict") == 0) {
|
||||
g_pow_config.validation_flags = NOSTR_POW_VALIDATE_ANTI_SPAM | NOSTR_POW_STRICT_FORMAT;
|
||||
g_pow_config.require_nonce_tag = 1;
|
||||
g_pow_config.reject_lower_targets = 1;
|
||||
g_pow_config.strict_format = 1;
|
||||
g_pow_config.anti_spam_mode = 1;
|
||||
log_info("PoW configured in strict anti-spam mode");
|
||||
} else if (strcmp(pow_mode_env, "full") == 0) {
|
||||
} else if (strcmp(pow_mode, "full") == 0) {
|
||||
g_pow_config.validation_flags = NOSTR_POW_VALIDATE_FULL;
|
||||
g_pow_config.require_nonce_tag = 1;
|
||||
log_info("PoW configured in full validation mode");
|
||||
} else if (strcmp(pow_mode_env, "basic") == 0) {
|
||||
} else if (strcmp(pow_mode, "basic") == 0) {
|
||||
g_pow_config.validation_flags = NOSTR_POW_VALIDATE_BASIC;
|
||||
log_info("PoW configured in basic validation mode");
|
||||
} else if (strcmp(pow_mode_env, "disabled") == 0) {
|
||||
} else if (strcmp(pow_mode, "disabled") == 0) {
|
||||
g_pow_config.enabled = 0;
|
||||
log_info("PoW validation disabled via RELAY_POW_MODE");
|
||||
log_info("PoW validation disabled via configuration");
|
||||
}
|
||||
} else {
|
||||
// Default to basic mode
|
||||
g_pow_config.validation_flags = NOSTR_POW_VALIDATE_BASIC;
|
||||
log_info("PoW configured in basic validation mode (default)");
|
||||
}
|
||||
|
||||
// Log final configuration
|
||||
@@ -1801,45 +1823,21 @@ int validate_event_pow(cJSON* event, char* error_message, size_t error_size) {
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize expiration configuration with environment variables and defaults
|
||||
// Initialize expiration configuration using configuration system
|
||||
void init_expiration_config() {
|
||||
log_info("Initializing NIP-40 Expiration Timestamp configuration");
|
||||
|
||||
// Check environment variables for configuration
|
||||
const char* exp_enabled_env = getenv("RELAY_EXPIRATION_ENABLED");
|
||||
if (exp_enabled_env) {
|
||||
g_expiration_config.enabled = (strcmp(exp_enabled_env, "1") == 0 ||
|
||||
strcmp(exp_enabled_env, "true") == 0 ||
|
||||
strcmp(exp_enabled_env, "yes") == 0);
|
||||
}
|
||||
// Load expiration settings from configuration system
|
||||
g_expiration_config.enabled = get_config_bool("expiration_enabled", 1);
|
||||
g_expiration_config.strict_mode = get_config_bool("expiration_strict", 1);
|
||||
g_expiration_config.filter_responses = get_config_bool("expiration_filter", 1);
|
||||
g_expiration_config.delete_expired = get_config_bool("expiration_delete", 0);
|
||||
g_expiration_config.grace_period = get_config_int("expiration_grace_period", 300);
|
||||
|
||||
const char* exp_strict_env = getenv("RELAY_EXPIRATION_STRICT");
|
||||
if (exp_strict_env) {
|
||||
g_expiration_config.strict_mode = (strcmp(exp_strict_env, "1") == 0 ||
|
||||
strcmp(exp_strict_env, "true") == 0 ||
|
||||
strcmp(exp_strict_env, "yes") == 0);
|
||||
}
|
||||
|
||||
const char* exp_filter_env = getenv("RELAY_EXPIRATION_FILTER");
|
||||
if (exp_filter_env) {
|
||||
g_expiration_config.filter_responses = (strcmp(exp_filter_env, "1") == 0 ||
|
||||
strcmp(exp_filter_env, "true") == 0 ||
|
||||
strcmp(exp_filter_env, "yes") == 0);
|
||||
}
|
||||
|
||||
const char* exp_delete_env = getenv("RELAY_EXPIRATION_DELETE");
|
||||
if (exp_delete_env) {
|
||||
g_expiration_config.delete_expired = (strcmp(exp_delete_env, "1") == 0 ||
|
||||
strcmp(exp_delete_env, "true") == 0 ||
|
||||
strcmp(exp_delete_env, "yes") == 0);
|
||||
}
|
||||
|
||||
const char* exp_grace_env = getenv("RELAY_EXPIRATION_GRACE_PERIOD");
|
||||
if (exp_grace_env) {
|
||||
long grace_period = atol(exp_grace_env);
|
||||
if (grace_period >= 0 && grace_period <= 86400) { // Max 24 hours
|
||||
g_expiration_config.grace_period = grace_period;
|
||||
}
|
||||
// Validate grace period bounds
|
||||
if (g_expiration_config.grace_period < 0 || g_expiration_config.grace_period > 86400) {
|
||||
log_warning("Invalid grace period, using default of 300 seconds");
|
||||
g_expiration_config.grace_period = 300;
|
||||
}
|
||||
|
||||
// Log final configuration
|
||||
@@ -2259,7 +2257,7 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
}
|
||||
|
||||
// Check session subscription limits
|
||||
if (pss && pss->subscription_count >= MAX_SUBSCRIPTIONS_PER_CLIENT) {
|
||||
if (pss && pss->subscription_count >= g_subscription_manager.max_subscriptions_per_client) {
|
||||
log_error("Maximum subscriptions per client exceeded");
|
||||
|
||||
// Send CLOSED notice
|
||||
@@ -2883,7 +2881,7 @@ int start_websocket_relay() {
|
||||
log_info("Starting libwebsockets-based Nostr relay server...");
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.port = DEFAULT_PORT;
|
||||
info.port = get_config_int("relay_port", DEFAULT_PORT);
|
||||
info.protocols = protocols;
|
||||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
@@ -2909,7 +2907,9 @@ int start_websocket_relay() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_success("WebSocket relay started on ws://127.0.0.1:8888");
|
||||
char startup_msg[256];
|
||||
snprintf(startup_msg, sizeof(startup_msg), "WebSocket relay started on ws://127.0.0.1:%d", info.port);
|
||||
log_success(startup_msg);
|
||||
|
||||
// Main event loop with proper signal handling
|
||||
while (g_server_running) {
|
||||
@@ -2965,6 +2965,12 @@ int main(int argc, char* argv[]) {
|
||||
log_error("Invalid port number");
|
||||
return 1;
|
||||
}
|
||||
// Store port in configuration system
|
||||
char port_str[16];
|
||||
snprintf(port_str, sizeof(port_str), "%d", port);
|
||||
set_database_config("relay_port", port_str, "command_line");
|
||||
// Re-apply configuration to make sure global variables are updated
|
||||
apply_configuration_to_globals();
|
||||
} else {
|
||||
log_error("Port argument requires a value");
|
||||
return 1;
|
||||
@@ -2982,19 +2988,28 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
printf(BLUE BOLD "=== C Nostr Relay Server ===" RESET "\n");
|
||||
|
||||
// Initialize database
|
||||
// Initialize database FIRST (required for configuration system)
|
||||
if (init_database() != 0) {
|
||||
log_error("Failed to initialize database");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize nostr library
|
||||
// Initialize nostr library BEFORE configuration system
|
||||
// (required for Nostr event generation in config files)
|
||||
if (nostr_init() != 0) {
|
||||
log_error("Failed to initialize nostr library");
|
||||
close_database();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize configuration system (loads file + database + applies to globals)
|
||||
if (init_configuration_system() != 0) {
|
||||
log_error("Failed to initialize configuration system");
|
||||
nostr_cleanup();
|
||||
close_database();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize NIP-11 relay information
|
||||
init_relay_info();
|
||||
|
||||
@@ -3004,6 +3019,9 @@ int main(int argc, char* argv[]) {
|
||||
// Initialize NIP-40 expiration configuration
|
||||
init_expiration_config();
|
||||
|
||||
// Update subscription manager configuration
|
||||
update_subscription_manager_config();
|
||||
|
||||
log_info("Starting relay server...");
|
||||
|
||||
// Start WebSocket Nostr relay server
|
||||
@@ -3011,6 +3029,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
// Cleanup
|
||||
cleanup_relay_info();
|
||||
cleanup_configuration_system();
|
||||
nostr_cleanup();
|
||||
close_database();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user