v0.4.4 - Just waking up
This commit is contained in:
259
src/websockets.c
259
src/websockets.c
@@ -74,6 +74,7 @@ int is_event_expired(cJSON* event, time_t current_time);
|
||||
|
||||
// Forward declarations for subscription handling
|
||||
int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, struct per_session_data *pss);
|
||||
int handle_count_message(const char* sub_id, cJSON* filters, struct lws *wsi, struct per_session_data *pss);
|
||||
|
||||
// Forward declarations for NOTICE message support
|
||||
void send_notice_message(struct lws* wsi, const char* message);
|
||||
@@ -619,6 +620,41 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
||||
}
|
||||
cJSON_Delete(eose_response);
|
||||
}
|
||||
} else if (strcmp(msg_type, "COUNT") == 0) {
|
||||
// Check NIP-42 authentication for COUNT requests if required
|
||||
if (pss && pss->nip42_auth_required_subscriptions && !pss->authenticated) {
|
||||
if (!pss->auth_challenge_sent) {
|
||||
send_nip42_auth_challenge(wsi, pss);
|
||||
} else {
|
||||
send_notice_message(wsi, "NIP-42 authentication required for count requests");
|
||||
log_warning("COUNT rejected: NIP-42 authentication required");
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
free(message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle COUNT message
|
||||
cJSON* sub_id = cJSON_GetArrayItem(json, 1);
|
||||
|
||||
if (sub_id && cJSON_IsString(sub_id)) {
|
||||
const char* subscription_id = cJSON_GetStringValue(sub_id);
|
||||
|
||||
// Create array of filter objects from position 2 onwards
|
||||
cJSON* filters = cJSON_CreateArray();
|
||||
int json_size = cJSON_GetArraySize(json);
|
||||
for (int i = 2; i < json_size; i++) {
|
||||
cJSON* filter = cJSON_GetArrayItem(json, i);
|
||||
if (filter) {
|
||||
cJSON_AddItemToArray(filters, cJSON_Duplicate(filter, 1));
|
||||
}
|
||||
}
|
||||
|
||||
handle_count_message(subscription_id, filters, wsi, pss);
|
||||
|
||||
// Clean up the filters array we created
|
||||
cJSON_Delete(filters);
|
||||
}
|
||||
} else if (strcmp(msg_type, "CLOSE") == 0) {
|
||||
// Handle CLOSE message
|
||||
cJSON* sub_id = cJSON_GetArrayItem(json, 1);
|
||||
@@ -899,3 +935,226 @@ int start_websocket_relay(int port_override, int strict_port) {
|
||||
log_success("WebSocket relay shut down cleanly");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle NIP-45 COUNT message
|
||||
int handle_count_message(const char* sub_id, cJSON* filters, struct lws *wsi, struct per_session_data *pss) {
|
||||
(void)pss; // Suppress unused parameter warning
|
||||
log_info("Handling COUNT message for subscription");
|
||||
|
||||
if (!cJSON_IsArray(filters)) {
|
||||
log_error("COUNT filters is not an array");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int total_count = 0;
|
||||
|
||||
// Process each filter in the array
|
||||
for (int i = 0; i < cJSON_GetArraySize(filters); i++) {
|
||||
cJSON* filter = cJSON_GetArrayItem(filters, i);
|
||||
if (!filter || !cJSON_IsObject(filter)) {
|
||||
log_warning("Invalid filter object in COUNT");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build SQL COUNT query based on filter - exclude ephemeral events (kinds 20000-29999) from historical queries
|
||||
char sql[1024] = "SELECT COUNT(*) FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000)";
|
||||
char* sql_ptr = sql + strlen(sql);
|
||||
int remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
// Note: Expiration filtering will be done at application level
|
||||
// after retrieving events to ensure compatibility with all SQLite versions
|
||||
|
||||
// Handle kinds filter
|
||||
cJSON* kinds = cJSON_GetObjectItem(filter, "kinds");
|
||||
if (kinds && cJSON_IsArray(kinds)) {
|
||||
int kind_count = cJSON_GetArraySize(kinds);
|
||||
if (kind_count > 0) {
|
||||
snprintf(sql_ptr, remaining, " AND kind IN (");
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
for (int k = 0; k < kind_count; k++) {
|
||||
cJSON* kind = cJSON_GetArrayItem(kinds, k);
|
||||
if (cJSON_IsNumber(kind)) {
|
||||
if (k > 0) {
|
||||
snprintf(sql_ptr, remaining, ",");
|
||||
sql_ptr++;
|
||||
remaining--;
|
||||
}
|
||||
snprintf(sql_ptr, remaining, "%d", (int)cJSON_GetNumberValue(kind));
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
}
|
||||
snprintf(sql_ptr, remaining, ")");
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle authors filter
|
||||
cJSON* authors = cJSON_GetObjectItem(filter, "authors");
|
||||
if (authors && cJSON_IsArray(authors)) {
|
||||
int author_count = cJSON_GetArraySize(authors);
|
||||
if (author_count > 0) {
|
||||
snprintf(sql_ptr, remaining, " AND pubkey IN (");
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
for (int a = 0; a < author_count; a++) {
|
||||
cJSON* author = cJSON_GetArrayItem(authors, a);
|
||||
if (cJSON_IsString(author)) {
|
||||
if (a > 0) {
|
||||
snprintf(sql_ptr, remaining, ",");
|
||||
sql_ptr++;
|
||||
remaining--;
|
||||
}
|
||||
snprintf(sql_ptr, remaining, "'%s'", cJSON_GetStringValue(author));
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
}
|
||||
snprintf(sql_ptr, remaining, ")");
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle ids filter
|
||||
cJSON* ids = cJSON_GetObjectItem(filter, "ids");
|
||||
if (ids && cJSON_IsArray(ids)) {
|
||||
int id_count = cJSON_GetArraySize(ids);
|
||||
if (id_count > 0) {
|
||||
snprintf(sql_ptr, remaining, " AND id IN (");
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
for (int i = 0; i < id_count; i++) {
|
||||
cJSON* id = cJSON_GetArrayItem(ids, i);
|
||||
if (cJSON_IsString(id)) {
|
||||
if (i > 0) {
|
||||
snprintf(sql_ptr, remaining, ",");
|
||||
sql_ptr++;
|
||||
remaining--;
|
||||
}
|
||||
snprintf(sql_ptr, remaining, "'%s'", cJSON_GetStringValue(id));
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
}
|
||||
snprintf(sql_ptr, remaining, ")");
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle tag filters (#e, #p, #t, etc.)
|
||||
cJSON* filter_item = NULL;
|
||||
cJSON_ArrayForEach(filter_item, filter) {
|
||||
const char* filter_key = filter_item->string;
|
||||
if (filter_key && filter_key[0] == '#' && strlen(filter_key) > 1) {
|
||||
// This is a tag filter like "#e", "#p", etc.
|
||||
const char* tag_name = filter_key + 1; // Get the tag name (e, p, t, type, etc.)
|
||||
|
||||
if (cJSON_IsArray(filter_item)) {
|
||||
int tag_value_count = cJSON_GetArraySize(filter_item);
|
||||
if (tag_value_count > 0) {
|
||||
// Use EXISTS with JSON extraction to check for matching tags
|
||||
snprintf(sql_ptr, remaining, " AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = '%s' AND json_extract(value, '$[1]') IN (", tag_name);
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
for (int i = 0; i < tag_value_count; i++) {
|
||||
cJSON* tag_value = cJSON_GetArrayItem(filter_item, i);
|
||||
if (cJSON_IsString(tag_value)) {
|
||||
if (i > 0) {
|
||||
snprintf(sql_ptr, remaining, ",");
|
||||
sql_ptr++;
|
||||
remaining--;
|
||||
}
|
||||
snprintf(sql_ptr, remaining, "'%s'", cJSON_GetStringValue(tag_value));
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
}
|
||||
snprintf(sql_ptr, remaining, "))");
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle since filter
|
||||
cJSON* since = cJSON_GetObjectItem(filter, "since");
|
||||
if (since && cJSON_IsNumber(since)) {
|
||||
snprintf(sql_ptr, remaining, " AND created_at >= %ld", (long)cJSON_GetNumberValue(since));
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
|
||||
// Handle until filter
|
||||
cJSON* until = cJSON_GetObjectItem(filter, "until");
|
||||
if (until && cJSON_IsNumber(until)) {
|
||||
snprintf(sql_ptr, remaining, " AND created_at <= %ld", (long)cJSON_GetNumberValue(until));
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
|
||||
// Debug: Log the SQL query being executed
|
||||
char debug_msg[1280];
|
||||
snprintf(debug_msg, sizeof(debug_msg), "Executing COUNT SQL: %s", sql);
|
||||
log_info(debug_msg);
|
||||
|
||||
// Execute count query
|
||||
sqlite3_stmt* stmt;
|
||||
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
char error_msg[256];
|
||||
snprintf(error_msg, sizeof(error_msg), "Failed to prepare COUNT query: %s", sqlite3_errmsg(g_db));
|
||||
log_error(error_msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
int filter_count = 0;
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
filter_count = sqlite3_column_int(stmt, 0);
|
||||
}
|
||||
|
||||
char count_debug[128];
|
||||
snprintf(count_debug, sizeof(count_debug), "Filter %d returned count: %d", i + 1, filter_count);
|
||||
log_info(count_debug);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
total_count += filter_count;
|
||||
}
|
||||
|
||||
char total_debug[128];
|
||||
snprintf(total_debug, sizeof(total_debug), "Total COUNT result: %d", total_count);
|
||||
log_info(total_debug);
|
||||
|
||||
// Send COUNT response - NIP-45 format: ["COUNT", <subscription_id>, {"count": <count>}]
|
||||
cJSON* count_response = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(count_response, cJSON_CreateString("COUNT"));
|
||||
cJSON_AddItemToArray(count_response, cJSON_CreateString(sub_id));
|
||||
|
||||
// Create count object as per NIP-45 specification
|
||||
cJSON* count_obj = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(count_obj, "count", total_count);
|
||||
cJSON_AddItemToArray(count_response, count_obj);
|
||||
|
||||
char *count_str = cJSON_Print(count_response);
|
||||
if (count_str) {
|
||||
size_t count_len = strlen(count_str);
|
||||
unsigned char *buf = malloc(LWS_PRE + count_len);
|
||||
if (buf) {
|
||||
memcpy(buf + LWS_PRE, count_str, count_len);
|
||||
lws_write(wsi, buf + LWS_PRE, count_len, LWS_WRITE_TEXT);
|
||||
free(buf);
|
||||
}
|
||||
free(count_str);
|
||||
}
|
||||
cJSON_Delete(count_response);
|
||||
|
||||
return total_count;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user