v2.1.7 - Refactor SQLite connection ownership into db_ops, remove direct sqlite usage from runtime modules, and document agent-browser admin testing flow
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
# Agent Browser Testing Guide for C-Relay
|
||||
|
||||
This document explains how to use the `agent-browser` CLI tool to test the c-relay admin web UI from an AI agent context (no physical display required).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **agent-browser** installed globally: `npm install -g agent-browser`
|
||||
- Binary location: `/home/user/.nvm/versions/node/v24.14.1/bin/agent-browser`
|
||||
- The relay must be running locally (default port 8888)
|
||||
- Test keys configured in `.test_keys`
|
||||
|
||||
## Starting the Relay for Local Testing
|
||||
|
||||
```bash
|
||||
./make_and_restart_relay.sh -t
|
||||
```
|
||||
|
||||
This reads `.test_keys` and starts the relay with:
|
||||
- `ADMIN_PUBKEY` — the hex public key of the admin account
|
||||
- `ADMIN_PRIVKEY` — the hex secret key (used for browser login, not by the relay itself)
|
||||
- `SERVER_PRIVKEY` — the relay's own private key
|
||||
|
||||
## Key URLs
|
||||
|
||||
| URL | Purpose |
|
||||
|-----|---------|
|
||||
| `http://127.0.0.1:8888/api/index.html` | Admin web UI (the correct entry point) |
|
||||
| `http://127.0.0.1:8888/` | Returns 406 without NIP-11 Accept header — **do not use for browser testing** |
|
||||
| `http://127.0.0.1:8888/` with `Accept: application/nostr+json` | NIP-11 relay info JSON |
|
||||
|
||||
**Important:** The root URL `/` is a WebSocket/NIP-11 endpoint, not an HTML page. Always use `/api/index.html` for browser testing.
|
||||
|
||||
## Admin Login Credentials
|
||||
|
||||
The admin nsec for the current `.test_keys` configuration:
|
||||
|
||||
```
|
||||
nsec: nsec1zkn0hlt4jvcvn9yt5p7ea4m7f82pf3ph0gj3d4rlcz4s64x5z86satv9qm
|
||||
hex: 15a6fbfd759330c9948ba07d9ed77e49d414c4377a2516d47fc0ab0d54d411f5
|
||||
npub: npub1tlyzk6slzt8d989f4pn3synk98fea64ea3jmrdc7dtd0kw8uxlas9a9fwr
|
||||
hex pubkey: 5fc82b6a1f12ced29ca9a86718127629d39eeab9ec65b1b71e6adafb38fc37fb
|
||||
```
|
||||
|
||||
## Complete Login Flow with agent-browser
|
||||
|
||||
### Step 1: Open the admin UI
|
||||
|
||||
```bash
|
||||
agent-browser open http://127.0.0.1:8888/api/index.html
|
||||
agent-browser wait --load networkidle
|
||||
```
|
||||
|
||||
### Step 2: Take an interactive snapshot to see what is on screen
|
||||
|
||||
```bash
|
||||
agent-browser snapshot -i
|
||||
```
|
||||
|
||||
You will see a login modal with buttons like:
|
||||
- `"Browser Extension"` — skip this
|
||||
- `"Local Key"` — **use this one**
|
||||
- `"Seed Phrase"` — skip
|
||||
- `"Nostr Connect"` — skip
|
||||
- `"Read Only"` — skip
|
||||
|
||||
### Step 3: Click "Local Key"
|
||||
|
||||
```bash
|
||||
agent-browser click @e15
|
||||
```
|
||||
|
||||
(The ref number may vary — use the ref from the snapshot output for the "Local Key" button.)
|
||||
|
||||
After clicking, a text input appears asking for the secret key.
|
||||
|
||||
### Step 4: Enter the admin nsec
|
||||
|
||||
```bash
|
||||
agent-browser fill @e14 "nsec1zkn0hlt4jvcvn9yt5p7ea4m7f82pf3ph0gj3d4rlcz4s64x5z86satv9qm"
|
||||
```
|
||||
|
||||
(Use the ref from the snapshot for the textbox element.)
|
||||
|
||||
### Step 5: Click "Import Key"
|
||||
|
||||
```bash
|
||||
agent-browser snapshot -i
|
||||
```
|
||||
|
||||
Check the snapshot — the "Import Key" button should now be enabled. Click it:
|
||||
|
||||
```bash
|
||||
agent-browser click @e15
|
||||
```
|
||||
|
||||
### Step 6: Click "Continue" on the success screen
|
||||
|
||||
After import, a success screen appears with "Continue" button:
|
||||
|
||||
```bash
|
||||
agent-browser wait 800
|
||||
agent-browser snapshot -i
|
||||
agent-browser click @e19
|
||||
```
|
||||
|
||||
(Use the ref from the snapshot for the "Continue" button.)
|
||||
|
||||
### Step 7: Wait for admin UI to load
|
||||
|
||||
```bash
|
||||
agent-browser wait 5000
|
||||
agent-browser snapshot -i
|
||||
```
|
||||
|
||||
You should now see the admin dashboard with:
|
||||
- Statistics table (Database Size, Total Events, PID, etc.)
|
||||
- Navigation buttons (Statistics, Subscriptions, Configuration, Authorization, etc.)
|
||||
- Admin profile area showing "admin" label
|
||||
|
||||
## Navigating Admin Sections
|
||||
|
||||
After login, use the sidebar navigation buttons:
|
||||
|
||||
```bash
|
||||
# View configuration
|
||||
agent-browser click @e8 # Configuration button ref
|
||||
|
||||
# View authorization rules
|
||||
agent-browser click @e9 # Authorization button ref
|
||||
|
||||
# View statistics
|
||||
agent-browser click @e6 # Statistics button ref
|
||||
|
||||
# Always snapshot after navigation to see results
|
||||
agent-browser wait 2500
|
||||
agent-browser snapshot -i
|
||||
```
|
||||
|
||||
## Checking for Errors
|
||||
|
||||
```bash
|
||||
# View browser console logs
|
||||
agent-browser console
|
||||
|
||||
# View JavaScript errors
|
||||
agent-browser errors
|
||||
|
||||
# View relay server logs
|
||||
tail -n 100 relay.log
|
||||
```
|
||||
|
||||
## Chained Command Example (Full Login in One Shot)
|
||||
|
||||
```bash
|
||||
agent-browser open http://127.0.0.1:8888/api/index.html && \
|
||||
agent-browser wait --load networkidle && \
|
||||
agent-browser snapshot -i
|
||||
```
|
||||
|
||||
Then use refs from the snapshot to complete login steps.
|
||||
|
||||
## Tips for AI Agents
|
||||
|
||||
1. **Always use `/api/index.html`** — never the root URL
|
||||
2. **Use `snapshot -i`** after every action to see the current interactive elements and their refs
|
||||
3. **Refs change** between snapshots — always re-snapshot before clicking
|
||||
4. **Wait after clicks** — use `agent-browser wait 2000` (milliseconds) between actions that trigger async operations
|
||||
5. **The login flow has 3 screens**: method selection → key input → success confirmation
|
||||
6. **Console logs are cumulative** — they show all logs since page load, which is useful for debugging admin API responses
|
||||
7. **The relay log** at `relay.log` shows server-side processing of admin commands
|
||||
8. **Command chaining** with `&&` works — the browser daemon persists between commands
|
||||
|
||||
## Verifying Admin API is Working
|
||||
|
||||
After login, the statistics page should show populated data:
|
||||
- Database Size (e.g., "4 KB")
|
||||
- Process ID (the relay PID)
|
||||
- WebSocket Connections count
|
||||
- Memory Usage
|
||||
|
||||
If these show "-" or "Loading...", check:
|
||||
1. `relay.log` for errors
|
||||
2. `agent-browser console` for JavaScript errors
|
||||
3. `agent-browser errors` for page-level errors
|
||||
|
||||
## Closing the Browser
|
||||
|
||||
```bash
|
||||
agent-browser close --all
|
||||
```
|
||||
@@ -19,7 +19,6 @@ int get_active_connection_count(void);
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#include <stdbool.h>
|
||||
#include <sqlite3.h>
|
||||
#include "api.h"
|
||||
#include "embedded_web_content.h"
|
||||
#include "config.h"
|
||||
@@ -56,38 +55,12 @@ int generate_monitoring_event_for_type(const char* d_tag_value, cJSON* (*query_f
|
||||
// Forward declaration for CPU metrics query function
|
||||
cJSON* query_cpu_metrics(void);
|
||||
|
||||
static int api_open_temp_db_connection(sqlite3** out_db) {
|
||||
if (!out_db) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out_db = NULL;
|
||||
const char* db_path = db_get_database_path();
|
||||
if (!db_path || db_path[0] == '\0') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sqlite3_open_v2(db_path, out_db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
|
||||
if (*out_db) {
|
||||
sqlite3_close(*out_db);
|
||||
*out_db = NULL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
sqlite3_exec(*out_db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
|
||||
sqlite3_busy_timeout(*out_db, 5000);
|
||||
db_set_thread_connection(*out_db);
|
||||
return 0;
|
||||
static int api_open_temp_db_connection(void** out_db) {
|
||||
return db_open_worker_connection(db_get_database_path(), out_db);
|
||||
}
|
||||
|
||||
static void api_close_temp_db_connection(sqlite3* db) {
|
||||
if (!db) {
|
||||
return;
|
||||
}
|
||||
|
||||
db_clear_thread_connection();
|
||||
sqlite3_close(db);
|
||||
static void api_close_temp_db_connection(void* db) {
|
||||
db_close_worker_connection(db);
|
||||
}
|
||||
|
||||
// Monitoring system helper functions
|
||||
@@ -97,7 +70,7 @@ int get_monitoring_throttle_seconds(void) {
|
||||
|
||||
// Query event kind distribution from database
|
||||
cJSON* query_event_kind_distribution(void) {
|
||||
sqlite3* temp_db = NULL;
|
||||
void* temp_db = NULL;
|
||||
if (!db_is_available() && api_open_temp_db_connection(&temp_db) != 0) {
|
||||
DEBUG_ERROR("Database not available for monitoring query");
|
||||
return NULL;
|
||||
@@ -137,7 +110,7 @@ cJSON* query_event_kind_distribution(void) {
|
||||
|
||||
// Query time-based statistics from database
|
||||
cJSON* query_time_based_statistics(void) {
|
||||
sqlite3* temp_db = NULL;
|
||||
void* temp_db = NULL;
|
||||
if (!db_is_available() && api_open_temp_db_connection(&temp_db) != 0) {
|
||||
DEBUG_ERROR("Database not available for time stats query");
|
||||
return NULL;
|
||||
@@ -190,7 +163,7 @@ cJSON* query_time_based_statistics(void) {
|
||||
|
||||
// Query top pubkeys by event count from database
|
||||
cJSON* query_top_pubkeys(void) {
|
||||
sqlite3* temp_db = NULL;
|
||||
void* temp_db = NULL;
|
||||
if (!db_is_available() && api_open_temp_db_connection(&temp_db) != 0) {
|
||||
DEBUG_ERROR("Database not available for top pubkeys query");
|
||||
return NULL;
|
||||
@@ -237,7 +210,7 @@ cJSON* query_top_pubkeys(void) {
|
||||
// Query detailed subscription information from database log (ADMIN ONLY)
|
||||
// Uses subscriptions table instead of in-memory iteration to avoid mutex contention
|
||||
cJSON* query_subscription_details(void) {
|
||||
sqlite3* temp_db = NULL;
|
||||
void* temp_db = NULL;
|
||||
if (!db_is_available() && api_open_temp_db_connection(&temp_db) != 0) {
|
||||
DEBUG_ERROR("Database not available for subscription details query");
|
||||
return NULL;
|
||||
@@ -1210,7 +1183,7 @@ cJSON* query_cpu_metrics(void) {
|
||||
|
||||
// Generate stats JSON from database queries
|
||||
char* generate_stats_json(void) {
|
||||
sqlite3* temp_db = NULL;
|
||||
void* temp_db = NULL;
|
||||
if (!db_is_available() && api_open_temp_db_connection(&temp_db) != 0) {
|
||||
DEBUG_ERROR("Database not available for stats generation");
|
||||
return NULL;
|
||||
|
||||
+41
-4
@@ -31,7 +31,6 @@
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <libwebsockets.h>
|
||||
|
||||
// External database connection (from main.c)
|
||||
|
||||
// External shutdown flag (from main.c)
|
||||
@@ -111,6 +110,14 @@ static char g_temp_relay_privkey[65] = {0};
|
||||
// Runtime cache for relay private key to support strict mode (g_db = NULL)
|
||||
static char g_cached_relay_privkey[65] = {0};
|
||||
|
||||
static int config_open_temp_db_connection(void** out_db) {
|
||||
return db_open_worker_connection(db_get_database_path(), out_db);
|
||||
}
|
||||
|
||||
static void config_close_temp_db_connection(void* db) {
|
||||
db_close_worker_connection(db);
|
||||
}
|
||||
|
||||
// ================================
|
||||
// UTILITY FUNCTIONS
|
||||
// ================================
|
||||
@@ -2990,7 +2997,8 @@ int handle_kind_23456_unified(cJSON* event, char* error_message, size_t error_si
|
||||
int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_message, size_t error_size, struct lws* wsi) {
|
||||
// Suppress unused parameter warning
|
||||
(void)wsi;
|
||||
if (!db_is_available()) {
|
||||
void* temp_db = NULL;
|
||||
if (!db_is_available() && config_open_temp_db_connection(&temp_db) != 0) {
|
||||
snprintf(error_message, error_size, "database not available");
|
||||
return -1;
|
||||
}
|
||||
@@ -3082,6 +3090,9 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
||||
if (!admin_pubkey) {
|
||||
cJSON_Delete(response);
|
||||
cJSON_Delete(results_array);
|
||||
if (temp_db) {
|
||||
config_close_temp_db_connection(temp_db);
|
||||
}
|
||||
snprintf(error_message, error_size, "missing admin pubkey for response");
|
||||
return -1;
|
||||
}
|
||||
@@ -3090,12 +3101,18 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
||||
if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
|
||||
cJSON_Delete(response);
|
||||
cJSON_Delete(results_array);
|
||||
if (temp_db) {
|
||||
config_close_temp_db_connection(temp_db);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
cJSON_Delete(response);
|
||||
}
|
||||
|
||||
cJSON_Delete(results_array);
|
||||
if (temp_db) {
|
||||
config_close_temp_db_connection(temp_db);
|
||||
}
|
||||
snprintf(error_message, error_size, "failed to send auth query response");
|
||||
return -1;
|
||||
}
|
||||
@@ -3104,7 +3121,8 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
||||
int handle_config_query_unified(cJSON* event, const char* query_type, char* error_message, size_t error_size, struct lws* wsi) {
|
||||
// Suppress unused parameter warning
|
||||
(void)wsi;
|
||||
if (!db_is_available()) {
|
||||
void* temp_db = NULL;
|
||||
if (!db_is_available() && config_open_temp_db_connection(&temp_db) != 0) {
|
||||
snprintf(error_message, error_size, "database not available");
|
||||
return -1;
|
||||
}
|
||||
@@ -3197,6 +3215,9 @@ int handle_config_query_unified(cJSON* event, const char* query_type, char* erro
|
||||
if (!admin_pubkey) {
|
||||
cJSON_Delete(response);
|
||||
cJSON_Delete(results_array);
|
||||
if (temp_db) {
|
||||
config_close_temp_db_connection(temp_db);
|
||||
}
|
||||
snprintf(error_message, error_size, "missing admin pubkey for response");
|
||||
return -1;
|
||||
}
|
||||
@@ -3205,12 +3226,18 @@ int handle_config_query_unified(cJSON* event, const char* query_type, char* erro
|
||||
if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
|
||||
cJSON_Delete(response);
|
||||
cJSON_Delete(results_array);
|
||||
if (temp_db) {
|
||||
config_close_temp_db_connection(temp_db);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
cJSON_Delete(response);
|
||||
}
|
||||
|
||||
cJSON_Delete(results_array);
|
||||
if (temp_db) {
|
||||
config_close_temp_db_connection(temp_db);
|
||||
}
|
||||
snprintf(error_message, error_size, "failed to send config query response");
|
||||
return -1;
|
||||
}
|
||||
@@ -3713,7 +3740,8 @@ int handle_auth_rule_modification_unified(cJSON* event, char* error_message, siz
|
||||
int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_size, struct lws* wsi) {
|
||||
// Suppress unused parameter warning
|
||||
(void)wsi;
|
||||
if (!db_is_available()) {
|
||||
void* temp_db = NULL;
|
||||
if (!db_is_available() && config_open_temp_db_connection(&temp_db) != 0) {
|
||||
snprintf(error_message, error_size, "database not available");
|
||||
return -1;
|
||||
}
|
||||
@@ -3819,6 +3847,9 @@ int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_s
|
||||
|
||||
if (!admin_pubkey) {
|
||||
cJSON_Delete(response);
|
||||
if (temp_db) {
|
||||
config_close_temp_db_connection(temp_db);
|
||||
}
|
||||
snprintf(error_message, error_size, "missing admin pubkey for response");
|
||||
return -1;
|
||||
}
|
||||
@@ -3826,10 +3857,16 @@ int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_s
|
||||
// Send response as signed kind 23457 event
|
||||
if (send_admin_response_event(response, admin_pubkey, wsi) == 0) {
|
||||
cJSON_Delete(response);
|
||||
if (temp_db) {
|
||||
config_close_temp_db_connection(temp_db);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
cJSON_Delete(response);
|
||||
if (temp_db) {
|
||||
config_close_temp_db_connection(temp_db);
|
||||
}
|
||||
snprintf(error_message, error_size, "failed to send stats query response");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,33 @@ void db_clear_thread_connection(void) {
|
||||
g_thread_db = NULL;
|
||||
}
|
||||
|
||||
int db_open_worker_connection(const char* db_path, void** out_connection) {
|
||||
if (!out_connection) return -1;
|
||||
*out_connection = NULL;
|
||||
|
||||
const char* effective_path = (db_path && db_path[0] != '\0') ? db_path : g_database_path;
|
||||
if (!effective_path || effective_path[0] == '\0') return -1;
|
||||
|
||||
sqlite3* db = NULL;
|
||||
int rc = sqlite3_open_v2(effective_path, &db, SQLITE_OPEN_READWRITE, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
if (db) sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
|
||||
sqlite3_busy_timeout(db, 5000);
|
||||
db_set_thread_connection(db);
|
||||
*out_connection = (void*)db;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void db_close_worker_connection(void* connection) {
|
||||
if (!connection) return;
|
||||
db_clear_thread_connection();
|
||||
sqlite3_close((sqlite3*)connection);
|
||||
}
|
||||
|
||||
int db_is_available(void) {
|
||||
return db_active_connection() != NULL;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ const char* db_get_database_path(void);
|
||||
int db_set_thread_connection(void* connection);
|
||||
void db_clear_thread_connection(void);
|
||||
|
||||
// Worker/runtime SQLite connection lifecycle (kept internal to db_ops.c)
|
||||
int db_open_worker_connection(const char* db_path, void** out_connection);
|
||||
void db_close_worker_connection(void* connection);
|
||||
|
||||
// DB result codes (backend-agnostic)
|
||||
#define DB_OK 0
|
||||
#define DB_ERROR 1
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
// Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
||||
#define CRELAY_VERSION_MAJOR 2
|
||||
#define CRELAY_VERSION_MINOR 1
|
||||
#define CRELAY_VERSION_PATCH 6
|
||||
#define CRELAY_VERSION "v2.1.6"
|
||||
#define CRELAY_VERSION_PATCH 7
|
||||
#define CRELAY_VERSION "v2.1.7"
|
||||
|
||||
// Relay metadata (authoritative source for NIP-11 information)
|
||||
#define RELAY_NAME "C-Relay"
|
||||
|
||||
+8
-27
@@ -5,7 +5,6 @@
|
||||
#include "db_ops.h"
|
||||
#include "ip_ban.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -199,24 +198,12 @@ static void wake_event_loop(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static sqlite3* open_worker_connection(void) {
|
||||
if (g_pool.db_path[0] == '\0') {
|
||||
static void* open_worker_connection(void) {
|
||||
void* db_conn = NULL;
|
||||
if (db_open_worker_connection(g_pool.db_path, &db_conn) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sqlite3* db = NULL;
|
||||
int rc = sqlite3_open_v2(g_pool.db_path, &db, SQLITE_OPEN_READWRITE, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
if (db) {
|
||||
sqlite3_close(db);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
|
||||
sqlite3_busy_timeout(db, 5000);
|
||||
|
||||
return db;
|
||||
return db_conn;
|
||||
}
|
||||
|
||||
static void complete_job_with_result(thread_pool_job_node_t* node,
|
||||
@@ -502,14 +489,12 @@ static void* reader_worker_main(void* arg) {
|
||||
snprintf(thread_name, sizeof(thread_name), "db-read-%d", reader_index);
|
||||
pthread_setname_np(pthread_self(), thread_name);
|
||||
|
||||
sqlite3* worker_db = open_worker_connection();
|
||||
void* worker_db = open_worker_connection();
|
||||
if (!worker_db) {
|
||||
DEBUG_ERROR("Reader worker failed to open SQLite connection");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db_set_thread_connection(worker_db);
|
||||
|
||||
while (g_pool.running) {
|
||||
thread_pool_job_node_t* node = queue_pop(&g_pool.read_q);
|
||||
if (!node) break;
|
||||
@@ -517,8 +502,7 @@ static void* reader_worker_main(void* arg) {
|
||||
execute_read_job(node);
|
||||
}
|
||||
|
||||
db_clear_thread_connection();
|
||||
sqlite3_close(worker_db);
|
||||
db_close_worker_connection(worker_db);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -527,14 +511,12 @@ static void* writer_worker_main(void* arg) {
|
||||
|
||||
pthread_setname_np(pthread_self(), "db-write");
|
||||
|
||||
sqlite3* worker_db = open_worker_connection();
|
||||
void* worker_db = open_worker_connection();
|
||||
if (!worker_db) {
|
||||
DEBUG_ERROR("Writer worker failed to open SQLite connection");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db_set_thread_connection(worker_db);
|
||||
|
||||
while (g_pool.running) {
|
||||
thread_pool_job_node_t* node = queue_pop(&g_pool.write_q);
|
||||
if (!node) break;
|
||||
@@ -542,8 +524,7 @@ static void* writer_worker_main(void* arg) {
|
||||
execute_write_job(node);
|
||||
}
|
||||
|
||||
db_clear_thread_connection();
|
||||
sqlite3_close(worker_db);
|
||||
db_close_worker_connection(worker_db);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
+4
-19
@@ -18,8 +18,6 @@
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
// Include nostr_core_lib for Nostr functionality
|
||||
#include "../nostr_core_lib/cjson/cJSON.h"
|
||||
#include "../nostr_core_lib/nostr_core/nostr_core.h"
|
||||
@@ -531,22 +529,10 @@ static void* async_event_worker_main(void* arg) {
|
||||
(void)arg;
|
||||
pthread_setname_np(pthread_self(), "event-worker");
|
||||
|
||||
sqlite3* worker_db = NULL;
|
||||
void* worker_db = NULL;
|
||||
const char* db_path = db_get_database_path();
|
||||
if (db_path && db_path[0] != '\0') {
|
||||
if (sqlite3_open_v2(db_path, &worker_db, SQLITE_OPEN_READWRITE, NULL) == SQLITE_OK) {
|
||||
sqlite3_exec(worker_db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
|
||||
sqlite3_busy_timeout(worker_db, 5000);
|
||||
db_set_thread_connection(worker_db);
|
||||
} else {
|
||||
if (worker_db) {
|
||||
sqlite3_close(worker_db);
|
||||
worker_db = NULL;
|
||||
}
|
||||
DEBUG_WARN("event-worker: failed to open dedicated DB connection; using default connection context");
|
||||
}
|
||||
} else {
|
||||
DEBUG_WARN("event-worker: database path unavailable; using default connection context");
|
||||
if (db_open_worker_connection(db_path, &worker_db) != 0) {
|
||||
DEBUG_WARN("event-worker: failed to open dedicated DB connection; using default connection context");
|
||||
}
|
||||
|
||||
while (g_async_event_worker_running) {
|
||||
@@ -620,8 +606,7 @@ static void* async_event_worker_main(void* arg) {
|
||||
}
|
||||
|
||||
if (worker_db) {
|
||||
db_clear_thread_connection();
|
||||
sqlite3_close(worker_db);
|
||||
db_close_worker_connection(worker_db);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
Reference in New Issue
Block a user