diff options
Diffstat (limited to 'main/local_relay.c')
| -rw-r--r-- | main/local_relay.c | 140 |
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 | |||
| 17 | static const char *TAG = "local_relay"; | ||
| 18 | |||
| 19 | #define LOCAL_RELAY_PORT 4869 | ||
| 20 | #define LOCAL_RELAY_TTL_SEC (21 * 24 * 3600) | ||
| 21 | |||
| 22 | static relay_ctx_t s_relay_ctx; | ||
| 23 | static storage_engine_t s_storage; | ||
| 24 | static sub_manager_t s_sub_mgr; | ||
| 25 | static rate_limiter_t s_rate_limiter; | ||
| 26 | static bool s_initialized = false; | ||
| 27 | |||
| 28 | relay_ctx_t g_relay_ctx; | ||
| 29 | |||
| 30 | static void on_ws_message(int fd, const char *data, size_t len) | ||
| 31 | { | ||
| 32 | router_dispatch(&g_relay_ctx, fd, data, len); | ||
| 33 | } | ||
| 34 | |||
| 35 | static 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 | |||
| 42 | esp_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 | |||
| 85 | void 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 | |||
| 104 | void 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 | |||
| 111 | esp_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 | } | ||