upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/main/local_relay.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/local_relay.c')
-rw-r--r--main/local_relay.c140
1 files changed, 140 insertions, 0 deletions
diff --git a/main/local_relay.c b/main/local_relay.c
new file mode 100644
index 0000000..d7b1ff8
--- /dev/null
+++ b/main/local_relay.c
@@ -0,0 +1,140 @@
1#include "local_relay.h"
2#include "storage_engine.h"
3#include "sub_manager.h"
4#include "rate_limiter.h"
5#include "ws_server.h"
6#include "relay_core.h"
7#include "router.h"
8#include "handlers.h"
9#include "broadcaster.h"
10#include "flash_monitor.h"
11#include "cJSON.h"
12#include "esp_log.h"
13#include "freertos/FreeRTOS.h"
14#include "freertos/task.h"
15#include <string.h>
16
17static const char *TAG = "local_relay";
18
19#define LOCAL_RELAY_PORT 4869
20#define LOCAL_RELAY_TTL_SEC (21 * 24 * 3600)
21
22static relay_ctx_t s_relay_ctx;
23static storage_engine_t s_storage;
24static sub_manager_t s_sub_mgr;
25static rate_limiter_t s_rate_limiter;
26static bool s_initialized = false;
27
28relay_ctx_t g_relay_ctx;
29
30static void on_ws_message(int fd, const char *data, size_t len)
31{
32 router_dispatch(&g_relay_ctx, fd, data, len);
33}
34
35static void on_ws_disconnect(int fd)
36{
37 if (g_relay_ctx.sub_manager) {
38 sub_manager_remove_all(g_relay_ctx.sub_manager, fd);
39 }
40}
41
42esp_err_t local_relay_init(void)
43{
44 memset(&s_relay_ctx, 0, sizeof(s_relay_ctx));
45 memset(&s_storage, 0, sizeof(s_storage));
46 memset(&s_sub_mgr, 0, sizeof(s_sub_mgr));
47 memset(&s_rate_limiter, 0, sizeof(s_rate_limiter));
48
49 esp_err_t ret = storage_init(&s_storage, LOCAL_RELAY_TTL_SEC);
50 if (ret != ESP_OK) {
51 ESP_LOGE(TAG, "Failed to init storage: %s", esp_err_to_name(ret));
52 return ret;
53 }
54
55 ret = sub_manager_init(&s_sub_mgr);
56 if (ret != ESP_OK) {
57 storage_destroy(&s_storage);
58 return ret;
59 }
60
61 rate_config_t rl_cfg = {
62 .events_per_minute = 60,
63 .reqs_per_minute = 30,
64 };
65 rate_limiter_init(&s_rate_limiter, &rl_cfg);
66
67 s_relay_ctx.storage = &s_storage;
68 s_relay_ctx.sub_manager = &s_sub_mgr;
69 s_relay_ctx.rate_limiter = &s_rate_limiter;
70 s_relay_ctx.config.port = LOCAL_RELAY_PORT;
71 s_relay_ctx.config.max_event_age_sec = LOCAL_RELAY_TTL_SEC;
72 s_relay_ctx.config.max_subs_per_conn = 8;
73 s_relay_ctx.config.max_filters_per_sub = 4;
74 s_relay_ctx.config.max_future_sec = 600;
75
76 memcpy(&g_relay_ctx, &s_relay_ctx, sizeof(relay_ctx_t));
77
78 storage_start_cleanup_task(&s_storage);
79
80 s_initialized = true;
81 ESP_LOGI(TAG, "Local relay initialized (port=%d, TTL=%ds)", LOCAL_RELAY_PORT, LOCAL_RELAY_TTL_SEC);
82 return ESP_OK;
83}
84
85void local_relay_start(void)
86{
87 if (!s_initialized) {
88 ESP_LOGE(TAG, "Not initialized");
89 return;
90 }
91
92 esp_err_t ret = ws_server_init(&s_relay_ctx.ws_server, LOCAL_RELAY_PORT, on_ws_message);
93 if (ret != ESP_OK) {
94 ESP_LOGE(TAG, "Failed to start WS server: %s", esp_err_to_name(ret));
95 return;
96 }
97
98 ws_server_set_disconnect_cb(on_ws_disconnect);
99 memcpy(&g_relay_ctx, &s_relay_ctx, sizeof(relay_ctx_t));
100
101 ESP_LOGI(TAG, "Local relay listening on port %d", LOCAL_RELAY_PORT);
102}
103
104void local_relay_stop(void)
105{
106 if (!s_initialized) return;
107 ws_server_stop(&s_relay_ctx.ws_server);
108 ESP_LOGI(TAG, "Local relay stopped");
109}
110
111esp_err_t local_relay_publish(const char *event_json, size_t event_len)
112{
113 if (!s_initialized || !event_json) return ESP_ERR_INVALID_STATE;
114
115 storage_error_t err = storage_save_event_json(s_relay_ctx.storage, event_json, event_len);
116 if (err == STORAGE_ERR_DUPLICATE) {
117 ESP_LOGD(TAG, "Duplicate event, skipping broadcast");
118 return ESP_OK;
119 }
120 if (err != STORAGE_OK) {
121 ESP_LOGW(TAG, "Failed to save event: %d", err);
122 return ESP_FAIL;
123 }
124
125 cJSON *obj = cJSON_ParseWithLength(event_json, event_len);
126 if (!obj) return ESP_OK;
127
128 cJSON *pk = cJSON_GetObjectItem(obj, "pubkey");
129 cJSON *kind = cJSON_GetObjectItem(obj, "kind");
130 cJSON *ca = cJSON_GetObjectItem(obj, "created_at");
131
132 if (pk && kind && ca) {
133 broadcaster_fanout_json(&s_relay_ctx, event_json, event_len,
134 kind->valueint, pk->valuestring,
135 (uint64_t)ca->valuedouble);
136 }
137 cJSON_Delete(obj);
138
139 return ESP_OK;
140}