v0.3.0 - Complete deployment documentation and examples - Added comprehensive deployment guide, automated deployment scripts, nginx SSL proxy setup, backup automation, and monitoring tools. Includes VPS deployment, cloud platform guides, and practical examples for production deployment of event-based configuration system.
This commit is contained in:
337
src/main.c
337
src/main.c
@@ -198,6 +198,9 @@ int check_and_handle_replaceable_event(int kind, const char* pubkey, long create
|
||||
int check_and_handle_addressable_event(int kind, const char* pubkey, const char* d_tag_value, long created_at);
|
||||
int handle_event_message(cJSON* event, char* error_message, size_t error_size);
|
||||
|
||||
// Forward declaration for configuration event handling (kind 33334)
|
||||
int handle_configuration_event(cJSON* event, char* error_message, size_t error_size);
|
||||
|
||||
// Forward declaration for NOTICE message support
|
||||
void send_notice_message(struct lws* wsi, const char* message);
|
||||
|
||||
@@ -1940,9 +1943,9 @@ int validate_event_expiration(cJSON* event, char* error_message, size_t error_si
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize database connection and schema
|
||||
int init_database() {
|
||||
int init_database(const char* database_path_override) {
|
||||
// Priority 1: Command line database path override
|
||||
const char* db_path = getenv("C_RELAY_DATABASE_PATH_OVERRIDE");
|
||||
const char* db_path = database_path_override;
|
||||
|
||||
// Priority 2: Configuration system (if available)
|
||||
if (!db_path) {
|
||||
@@ -2678,6 +2681,11 @@ int handle_event_message(cJSON* event, char* error_message, size_t error_size) {
|
||||
return handle_deletion_request(event, error_message, error_size);
|
||||
}
|
||||
|
||||
// Kind 33334: Handle configuration events
|
||||
if (kind == 33334) {
|
||||
return handle_configuration_event(event, error_message, error_size);
|
||||
}
|
||||
|
||||
// Handle replaceable events (NIP-01)
|
||||
event_type_t event_type = classify_event_kind(kind);
|
||||
if (event_type == EVENT_TYPE_REPLACEABLE) {
|
||||
@@ -2949,13 +2957,14 @@ static struct lws_protocols protocols[] = {
|
||||
};
|
||||
|
||||
// Start libwebsockets-based WebSocket Nostr relay server
|
||||
int start_websocket_relay() {
|
||||
int start_websocket_relay(int port_override) {
|
||||
struct lws_context_creation_info info;
|
||||
|
||||
log_info("Starting libwebsockets-based Nostr relay server...");
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.port = get_config_int("relay_port", DEFAULT_PORT);
|
||||
// Use port override if provided, otherwise use configuration
|
||||
info.port = (port_override > 0) ? port_override : get_config_int("relay_port", DEFAULT_PORT);
|
||||
info.protocols = protocols;
|
||||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
@@ -3016,211 +3025,185 @@ int start_websocket_relay() {
|
||||
void print_usage(const char* program_name) {
|
||||
printf("Usage: %s [OPTIONS]\n", program_name);
|
||||
printf("\n");
|
||||
printf("C Nostr Relay Server\n");
|
||||
printf("C Nostr Relay Server - Event-Based Configuration\n");
|
||||
printf("\n");
|
||||
printf("Options:\n");
|
||||
printf(" -p, --port PORT Listen port (default: %d)\n", DEFAULT_PORT);
|
||||
printf(" -c, --config FILE Configuration file path\n");
|
||||
printf(" -d, --config-dir DIR Configuration directory path\n");
|
||||
printf(" -D, --database-path PATH Database file path (default: %s)\n", DEFAULT_DATABASE_PATH);
|
||||
printf(" -h, --help Show this help message\n");
|
||||
printf(" -v, --version Show version information\n");
|
||||
printf("\n");
|
||||
printf("Configuration:\n");
|
||||
printf(" This relay uses event-based configuration stored in the database.\n");
|
||||
printf(" On first startup, keys are automatically generated and printed once.\n");
|
||||
printf(" Database file: <relay_pubkey>.nrdb (created automatically)\n");
|
||||
printf("\n");
|
||||
printf("Examples:\n");
|
||||
printf(" %s --config /path/to/config.json\n", program_name);
|
||||
printf(" %s --config-dir ~/.config/c-relay-dev\n", program_name);
|
||||
printf(" %s --port 9999 --config-dir /etc/c-relay\n", program_name);
|
||||
printf(" %s --database-path /var/lib/c-relay/relay.db\n", program_name);
|
||||
printf(" %s --database-path ./test.db --port 7777\n", program_name);
|
||||
printf(" %s # Start relay (auto-configure on first run)\n", program_name);
|
||||
printf(" %s --help # Show this help\n", program_name);
|
||||
printf(" %s --version # Show version info\n", program_name);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// Print version information
|
||||
void print_version() {
|
||||
printf("C Nostr Relay Server v1.0.0\n");
|
||||
printf("Event-based configuration system\n");
|
||||
printf("Built with nostr_core_lib integration\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int port = DEFAULT_PORT;
|
||||
char* config_dir_override = NULL;
|
||||
char* config_file_override = NULL;
|
||||
char* database_path_override = NULL;
|
||||
|
||||
// Parse command line arguments
|
||||
// Parse minimal command line arguments (no configuration overrides)
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
port = atoi(argv[++i]);
|
||||
if (port <= 0 || port > 65535) {
|
||||
log_error("Invalid port number");
|
||||
return 1;
|
||||
}
|
||||
// Port will be stored in configuration system after it's initialized
|
||||
} else {
|
||||
log_error("Port argument requires a value");
|
||||
return 1;
|
||||
}
|
||||
} else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--config") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
config_file_override = argv[++i];
|
||||
} else {
|
||||
log_error("Config file argument requires a value");
|
||||
return 1;
|
||||
}
|
||||
} else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--config-dir") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
config_dir_override = argv[++i];
|
||||
} else {
|
||||
log_error("Config directory argument requires a value");
|
||||
return 1;
|
||||
}
|
||||
} else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--database-path") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
database_path_override = argv[++i];
|
||||
} else {
|
||||
log_error("Database path argument requires a value");
|
||||
return 1;
|
||||
}
|
||||
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
|
||||
print_version();
|
||||
return 0;
|
||||
} else {
|
||||
log_error("Unknown argument");
|
||||
log_error("Unknown argument. Use --help for usage information.");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Store config overrides in global variables for configuration system access
|
||||
if (config_dir_override) {
|
||||
setenv("C_RELAY_CONFIG_DIR_OVERRIDE", config_dir_override, 1);
|
||||
}
|
||||
if (config_file_override) {
|
||||
setenv("C_RELAY_CONFIG_FILE_OVERRIDE", config_file_override, 1);
|
||||
}
|
||||
|
||||
// Set up signal handlers
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
printf(BLUE BOLD "=== C Nostr Relay Server ===" RESET "\n");
|
||||
printf("Event-based configuration system\n\n");
|
||||
|
||||
// Apply database path override BEFORE any database operations
|
||||
if (database_path_override) {
|
||||
log_info("Database path override specified from command line");
|
||||
printf(" Override path: %s\n", database_path_override);
|
||||
// Set environment variable so init_database can use the correct path
|
||||
setenv("C_RELAY_DATABASE_PATH_OVERRIDE", database_path_override, 1);
|
||||
}
|
||||
|
||||
// Initialize database FIRST (required for configuration system)
|
||||
if (init_database() != 0) {
|
||||
log_error("Failed to initialize database");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Database path override is applied via environment variable - no need to store in config
|
||||
// (storing database path in database creates circular dependency)
|
||||
|
||||
// Initialize nostr library BEFORE configuration system
|
||||
// (required for Nostr event generation in config files)
|
||||
// Initialize nostr library FIRST (required for key generation and event creation)
|
||||
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");
|
||||
// Check if this is first-time startup or existing relay
|
||||
if (is_first_time_startup()) {
|
||||
log_info("First-time startup detected");
|
||||
|
||||
// Initialize event-based configuration system
|
||||
if (init_configuration_system(NULL, NULL) != 0) {
|
||||
log_error("Failed to initialize event-based configuration system");
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Run first-time startup sequence (generates keys, creates database, etc.)
|
||||
if (first_time_startup_sequence() != 0) {
|
||||
log_error("Failed to complete first-time startup sequence");
|
||||
cleanup_configuration_system();
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize database with the generated relay pubkey
|
||||
if (init_database(g_database_path) != 0) {
|
||||
log_error("Failed to initialize database after first-time setup");
|
||||
cleanup_configuration_system();
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Retry storing the configuration event now that database is initialized
|
||||
if (retry_store_initial_config_event() != 0) {
|
||||
log_warning("Failed to store initial configuration event after database init");
|
||||
}
|
||||
} else {
|
||||
log_info("Existing relay detected");
|
||||
|
||||
// Find existing database file
|
||||
char** existing_files = find_existing_nrdb_files();
|
||||
if (!existing_files || !existing_files[0]) {
|
||||
log_error("No existing relay database found");
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Extract relay pubkey from filename
|
||||
char* relay_pubkey = extract_pubkey_from_filename(existing_files[0]);
|
||||
if (!relay_pubkey) {
|
||||
log_error("Failed to extract relay pubkey from database filename");
|
||||
// Free the files array
|
||||
for (int i = 0; existing_files[i]; i++) {
|
||||
free(existing_files[i]);
|
||||
}
|
||||
free(existing_files);
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize event-based configuration system
|
||||
if (init_configuration_system(NULL, NULL) != 0) {
|
||||
log_error("Failed to initialize event-based configuration system");
|
||||
free(relay_pubkey);
|
||||
for (int i = 0; existing_files[i]; i++) {
|
||||
free(existing_files[i]);
|
||||
}
|
||||
free(existing_files);
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Setup existing relay (sets database path and loads config)
|
||||
if (startup_existing_relay(relay_pubkey) != 0) {
|
||||
log_error("Failed to setup existing relay");
|
||||
cleanup_configuration_system();
|
||||
free(relay_pubkey);
|
||||
for (int i = 0; existing_files[i]; i++) {
|
||||
free(existing_files[i]);
|
||||
}
|
||||
free(existing_files);
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize database with existing database path
|
||||
if (init_database(g_database_path) != 0) {
|
||||
log_error("Failed to initialize existing database");
|
||||
cleanup_configuration_system();
|
||||
free(relay_pubkey);
|
||||
for (int i = 0; existing_files[i]; i++) {
|
||||
free(existing_files[i]);
|
||||
}
|
||||
free(existing_files);
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load configuration from database
|
||||
cJSON* config_event = load_config_event_from_database(relay_pubkey);
|
||||
if (config_event) {
|
||||
if (apply_configuration_from_event(config_event) != 0) {
|
||||
log_warning("Failed to apply configuration from database");
|
||||
} else {
|
||||
log_success("Configuration loaded from database");
|
||||
}
|
||||
cJSON_Delete(config_event);
|
||||
} else {
|
||||
log_warning("No configuration event found in existing database");
|
||||
}
|
||||
|
||||
// Free memory
|
||||
free(relay_pubkey);
|
||||
for (int i = 0; existing_files[i]; i++) {
|
||||
free(existing_files[i]);
|
||||
}
|
||||
free(existing_files);
|
||||
}
|
||||
|
||||
// Verify database is now available
|
||||
if (!g_db) {
|
||||
log_error("Database not available after initialization");
|
||||
cleanup_configuration_system();
|
||||
nostr_cleanup();
|
||||
close_database();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Update database_path field to reflect actual database path used
|
||||
if (database_path_override) {
|
||||
// Convert to absolute path and normalize
|
||||
char actual_db_path[1024];
|
||||
if (database_path_override[0] == '/') {
|
||||
// Already absolute
|
||||
strncpy(actual_db_path, database_path_override, sizeof(actual_db_path) - 1);
|
||||
} else {
|
||||
// Make absolute by prepending current working directory
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof(cwd))) {
|
||||
// Handle the case where path starts with ./
|
||||
const char* clean_path = database_path_override;
|
||||
if (strncmp(database_path_override, "./", 2) == 0) {
|
||||
clean_path = database_path_override + 2;
|
||||
}
|
||||
|
||||
// Ensure we don't exceed buffer size
|
||||
int written = snprintf(actual_db_path, sizeof(actual_db_path), "%s/%s", cwd, clean_path);
|
||||
if (written >= (int)sizeof(actual_db_path)) {
|
||||
log_warning("Database path too long, using original path");
|
||||
strncpy(actual_db_path, database_path_override, sizeof(actual_db_path) - 1);
|
||||
actual_db_path[sizeof(actual_db_path) - 1] = '\0';
|
||||
}
|
||||
} else {
|
||||
strncpy(actual_db_path, database_path_override, sizeof(actual_db_path) - 1);
|
||||
}
|
||||
}
|
||||
actual_db_path[sizeof(actual_db_path) - 1] = '\0';
|
||||
|
||||
// Update the database_path configuration to reflect actual path used
|
||||
if (set_database_config("database_path", actual_db_path, "system") == 0) {
|
||||
log_info("Updated database_path configuration with actual path used");
|
||||
} else {
|
||||
log_warning("Failed to update database_path configuration");
|
||||
}
|
||||
}
|
||||
|
||||
// Store metadata about configuration file path used
|
||||
if (strlen(g_config_manager.config_file_path) > 0) {
|
||||
// Convert to absolute path and normalize (fix double slash issue)
|
||||
char actual_config_path[1024];
|
||||
if (g_config_manager.config_file_path[0] == '/') {
|
||||
// Already absolute - use as-is
|
||||
strncpy(actual_config_path, g_config_manager.config_file_path, sizeof(actual_config_path) - 1);
|
||||
} else {
|
||||
// Make absolute by prepending current working directory
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof(cwd))) {
|
||||
// Handle the case where path starts with ./
|
||||
const char* clean_path = g_config_manager.config_file_path;
|
||||
if (strncmp(g_config_manager.config_file_path, "./", 2) == 0) {
|
||||
clean_path = g_config_manager.config_file_path + 2;
|
||||
}
|
||||
|
||||
// Remove any trailing slash from cwd to avoid double slash
|
||||
size_t cwd_len = strlen(cwd);
|
||||
if (cwd_len > 0 && cwd[cwd_len - 1] == '/') {
|
||||
cwd[cwd_len - 1] = '\0';
|
||||
}
|
||||
|
||||
// Remove any leading slash from clean_path to avoid double slash
|
||||
if (clean_path[0] == '/') {
|
||||
clean_path++;
|
||||
}
|
||||
|
||||
snprintf(actual_config_path, sizeof(actual_config_path), "%s/%s", cwd, clean_path);
|
||||
} else {
|
||||
strncpy(actual_config_path, g_config_manager.config_file_path, sizeof(actual_config_path) - 1);
|
||||
}
|
||||
}
|
||||
actual_config_path[sizeof(actual_config_path) - 1] = '\0';
|
||||
|
||||
if (set_database_config("config_location", actual_config_path, "system") == 0) {
|
||||
log_info("Stored configuration location metadata");
|
||||
} else {
|
||||
log_warning("Failed to store configuration location metadata");
|
||||
}
|
||||
}
|
||||
|
||||
// Apply command line overrides AFTER configuration system is initialized
|
||||
if (port != DEFAULT_PORT) {
|
||||
log_info("Applying port override from command line");
|
||||
printf(" Port: %d\n", port);
|
||||
// Set environment variable for port override (runtime only, not persisted)
|
||||
char port_str[16];
|
||||
snprintf(port_str, sizeof(port_str), "%d", port);
|
||||
setenv("C_RELAY_PORT_OVERRIDE", port_str, 1);
|
||||
}
|
||||
// Configuration system is now fully initialized with event-based approach
|
||||
// All configuration is loaded from database events
|
||||
|
||||
// Initialize NIP-11 relay information
|
||||
init_relay_info();
|
||||
@@ -3236,8 +3219,8 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
log_info("Starting relay server...");
|
||||
|
||||
// Start WebSocket Nostr relay server
|
||||
int result = start_websocket_relay();
|
||||
// Start WebSocket Nostr relay server (port from configuration)
|
||||
int result = start_websocket_relay(-1); // Let config system determine port
|
||||
|
||||
// Cleanup
|
||||
cleanup_relay_info();
|
||||
|
||||
Reference in New Issue
Block a user