v0.4.8 - Implement web server functionality for embedded admin interface - serve HTML/CSS/JS from /api/ endpoint with proper MIME types, CORS headers, and performance optimizations
This commit is contained in:
186
src/nip011.c
186
src/nip011.c
@@ -352,9 +352,175 @@ cJSON* generate_relay_info_json() {
|
||||
log_error("Failed to create relay info JSON object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
|
||||
|
||||
// Defensive reinit: if relay_info appears empty (cache refresh wiped it), rebuild it directly from table
|
||||
if (strlen(g_unified_cache.relay_info.name) == 0 &&
|
||||
strlen(g_unified_cache.relay_info.description) == 0 &&
|
||||
strlen(g_unified_cache.relay_info.software) == 0) {
|
||||
log_warning("NIP-11 relay_info appears empty, rebuilding directly from config table");
|
||||
|
||||
// Rebuild relay_info directly from config table to avoid circular cache dependency
|
||||
// Get values directly from table (similar to init_relay_info but without cache calls)
|
||||
const char* relay_name = get_config_value_from_table("relay_name");
|
||||
if (relay_name) {
|
||||
strncpy(g_unified_cache.relay_info.name, relay_name, sizeof(g_unified_cache.relay_info.name) - 1);
|
||||
free((char*)relay_name);
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.name, "C Nostr Relay", sizeof(g_unified_cache.relay_info.name) - 1);
|
||||
}
|
||||
|
||||
const char* relay_description = get_config_value_from_table("relay_description");
|
||||
if (relay_description) {
|
||||
strncpy(g_unified_cache.relay_info.description, relay_description, sizeof(g_unified_cache.relay_info.description) - 1);
|
||||
free((char*)relay_description);
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.description, "A high-performance Nostr relay implemented in C with SQLite storage", sizeof(g_unified_cache.relay_info.description) - 1);
|
||||
}
|
||||
|
||||
const char* relay_software = get_config_value_from_table("relay_software");
|
||||
if (relay_software) {
|
||||
strncpy(g_unified_cache.relay_info.software, relay_software, sizeof(g_unified_cache.relay_info.software) - 1);
|
||||
free((char*)relay_software);
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.software, "https://git.laantungir.net/laantungir/c-relay.git", sizeof(g_unified_cache.relay_info.software) - 1);
|
||||
}
|
||||
|
||||
const char* relay_version = get_config_value_from_table("relay_version");
|
||||
if (relay_version) {
|
||||
strncpy(g_unified_cache.relay_info.version, relay_version, sizeof(g_unified_cache.relay_info.version) - 1);
|
||||
free((char*)relay_version);
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.version, "0.2.0", sizeof(g_unified_cache.relay_info.version) - 1);
|
||||
}
|
||||
|
||||
const char* relay_contact = get_config_value_from_table("relay_contact");
|
||||
if (relay_contact) {
|
||||
strncpy(g_unified_cache.relay_info.contact, relay_contact, sizeof(g_unified_cache.relay_info.contact) - 1);
|
||||
free((char*)relay_contact);
|
||||
}
|
||||
|
||||
const char* relay_pubkey = get_config_value_from_table("relay_pubkey");
|
||||
if (relay_pubkey) {
|
||||
strncpy(g_unified_cache.relay_info.pubkey, relay_pubkey, sizeof(g_unified_cache.relay_info.pubkey) - 1);
|
||||
free((char*)relay_pubkey);
|
||||
}
|
||||
|
||||
const char* posting_policy = get_config_value_from_table("posting_policy");
|
||||
if (posting_policy) {
|
||||
strncpy(g_unified_cache.relay_info.posting_policy, posting_policy, sizeof(g_unified_cache.relay_info.posting_policy) - 1);
|
||||
free((char*)posting_policy);
|
||||
}
|
||||
|
||||
const char* payments_url = get_config_value_from_table("payments_url");
|
||||
if (payments_url) {
|
||||
strncpy(g_unified_cache.relay_info.payments_url, payments_url, sizeof(g_unified_cache.relay_info.payments_url) - 1);
|
||||
free((char*)payments_url);
|
||||
}
|
||||
|
||||
// Rebuild supported_nips array
|
||||
const char* supported_nips_csv = get_config_value_from_table("supported_nips");
|
||||
if (supported_nips_csv) {
|
||||
g_unified_cache.relay_info.supported_nips = parse_comma_separated_array(supported_nips_csv);
|
||||
free((char*)supported_nips_csv);
|
||||
} else {
|
||||
g_unified_cache.relay_info.supported_nips = cJSON_CreateArray();
|
||||
if (g_unified_cache.relay_info.supported_nips) {
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(1));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(9));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(11));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(13));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(15));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(20));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(40));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(42));
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild limitation object
|
||||
int max_message_length = 16384;
|
||||
const char* max_msg_str = get_config_value_from_table("max_message_length");
|
||||
if (max_msg_str) {
|
||||
max_message_length = atoi(max_msg_str);
|
||||
free((char*)max_msg_str);
|
||||
}
|
||||
|
||||
int max_subscriptions_per_client = 20;
|
||||
const char* max_subs_str = get_config_value_from_table("max_subscriptions_per_client");
|
||||
if (max_subs_str) {
|
||||
max_subscriptions_per_client = atoi(max_subs_str);
|
||||
free((char*)max_subs_str);
|
||||
}
|
||||
|
||||
int max_limit = 5000;
|
||||
const char* max_limit_str = get_config_value_from_table("max_limit");
|
||||
if (max_limit_str) {
|
||||
max_limit = atoi(max_limit_str);
|
||||
free((char*)max_limit_str);
|
||||
}
|
||||
|
||||
int max_event_tags = 100;
|
||||
const char* max_tags_str = get_config_value_from_table("max_event_tags");
|
||||
if (max_tags_str) {
|
||||
max_event_tags = atoi(max_tags_str);
|
||||
free((char*)max_tags_str);
|
||||
}
|
||||
|
||||
int max_content_length = 8196;
|
||||
const char* max_content_str = get_config_value_from_table("max_content_length");
|
||||
if (max_content_str) {
|
||||
max_content_length = atoi(max_content_str);
|
||||
free((char*)max_content_str);
|
||||
}
|
||||
|
||||
int default_limit = 500;
|
||||
const char* default_limit_str = get_config_value_from_table("default_limit");
|
||||
if (default_limit_str) {
|
||||
default_limit = atoi(default_limit_str);
|
||||
free((char*)default_limit_str);
|
||||
}
|
||||
|
||||
int admin_enabled = 0;
|
||||
const char* admin_enabled_str = get_config_value_from_table("admin_enabled");
|
||||
if (admin_enabled_str) {
|
||||
admin_enabled = (strcmp(admin_enabled_str, "true") == 0) ? 1 : 0;
|
||||
free((char*)admin_enabled_str);
|
||||
}
|
||||
|
||||
g_unified_cache.relay_info.limitation = cJSON_CreateObject();
|
||||
if (g_unified_cache.relay_info.limitation) {
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_message_length", max_message_length);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subscriptions", max_subscriptions_per_client);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_limit", max_limit);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subid_length", SUBSCRIPTION_ID_MAX_LENGTH);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_event_tags", max_event_tags);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_content_length", max_content_length);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "min_pow_difficulty", g_unified_cache.pow_config.min_pow_difficulty);
|
||||
cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "auth_required", admin_enabled ? cJSON_True : cJSON_False);
|
||||
cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "payment_required", cJSON_False);
|
||||
cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "restricted_writes", cJSON_False);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_lower_limit", 0);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_upper_limit", 2147483647);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "default_limit", default_limit);
|
||||
}
|
||||
|
||||
// Rebuild other arrays (empty for now)
|
||||
g_unified_cache.relay_info.retention = cJSON_CreateArray();
|
||||
g_unified_cache.relay_info.language_tags = cJSON_CreateArray();
|
||||
if (g_unified_cache.relay_info.language_tags) {
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.language_tags, cJSON_CreateString("*"));
|
||||
}
|
||||
g_unified_cache.relay_info.relay_countries = cJSON_CreateArray();
|
||||
if (g_unified_cache.relay_info.relay_countries) {
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.relay_countries, cJSON_CreateString("*"));
|
||||
}
|
||||
g_unified_cache.relay_info.tags = cJSON_CreateArray();
|
||||
g_unified_cache.relay_info.fees = cJSON_CreateObject();
|
||||
|
||||
log_info("NIP-11 relay_info rebuilt directly from config table");
|
||||
}
|
||||
|
||||
// Add basic relay information
|
||||
if (strlen(g_unified_cache.relay_info.name) > 0) {
|
||||
cJSON_AddStringToObject(info, "name", g_unified_cache.relay_info.name);
|
||||
@@ -435,6 +601,7 @@ cJSON* generate_relay_info_json() {
|
||||
|
||||
// NIP-11 HTTP session data structure for managing buffer lifetime
|
||||
struct nip11_session_data {
|
||||
int type; // 0 for NIP-11
|
||||
char* json_buffer;
|
||||
size_t json_length;
|
||||
int headers_sent;
|
||||
@@ -504,7 +671,7 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
|
||||
|
||||
char* json_string = cJSON_Print(info_json);
|
||||
cJSON_Delete(info_json);
|
||||
|
||||
|
||||
if (!json_string) {
|
||||
log_error("Failed to serialize relay info JSON");
|
||||
unsigned char buf[LWS_PRE + 256];
|
||||
@@ -527,8 +694,11 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
|
||||
lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
size_t json_len = strlen(json_string);
|
||||
log_info("Generated NIP-11 JSON");
|
||||
printf(" JSON length: %zu bytes\n", json_len);
|
||||
printf(" JSON preview: %.100s%s\n", json_string, json_len > 100 ? "..." : "");
|
||||
|
||||
// Allocate session data to manage buffer lifetime across callbacks
|
||||
struct nip11_session_data* session_data = malloc(sizeof(struct nip11_session_data));
|
||||
@@ -539,6 +709,7 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
|
||||
}
|
||||
|
||||
// Store JSON buffer in session data for asynchronous handling
|
||||
session_data->type = 0; // NIP-11
|
||||
session_data->json_buffer = json_string;
|
||||
session_data->json_length = json_len;
|
||||
session_data->headers_sent = 0;
|
||||
@@ -594,6 +765,13 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add Connection: close to ensure connection closes after response
|
||||
if (lws_add_http_header_by_name(wsi, (unsigned char*)"connection:", (unsigned char*)"close", 5, &p, end)) {
|
||||
free(session_data->json_buffer);
|
||||
free(session_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Finalize headers
|
||||
if (lws_finalize_http_header(wsi, &p, end)) {
|
||||
|
||||
Reference in New Issue
Block a user