upleb.uk

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

summaryrefslogtreecommitdiff
path: root/main/tollgate_api.c
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-19 13:21:25 +0530
committerYour Name <you@example.com>2026-05-19 13:31:08 +0530
commiteeba74a4a1c011e85e33dea4252b381e35a64ea4 (patch)
tree14862e7d300511e28e214c743fd2f699bc54c5b8 /main/tollgate_api.c
parentb0d9d494f00ee77f9efc22d1ef2ea3c94b23ddbd (diff)
feat: multi-mint wallet with health tracking, WPA auto-detect, display gating
Squash merge of feature/multi-mint-support (21 commits): Multi-mint wallet: - Accept payments from 4 mints: minibits, coinos, 21mint, lnvoltz - Periodic health probing (300s interval, 3 recovery threshold) - Multi-wallet init with nucula_wallet_init_multi() - /mints and /wallet API endpoints WPA auto-detect: - wifi_auth_mode config field (default WPA2, supports WPA3) - Runtime mapping to wifi_auth_mode_t in STA config Display gating: - display_enabled config field (default true) - Guards display_init/display_update per-board Bug fixes: - 3s delay before service start prevents lwip mem_free assertion - Real npub in discovery (identity_get()->npub_hex) - Health probe interval 300s (production value) - Duplicate services_start_task call removed - UTF-8 arrow replaced with ASCII in log message Tests: 61+14 unit tests passing, firmware builds clean
Diffstat (limited to 'main/tollgate_api.c')
-rw-r--r--main/tollgate_api.c71
1 files changed, 56 insertions, 15 deletions
diff --git a/main/tollgate_api.c b/main/tollgate_api.c
index 15640c7..21bf9ef 100644
--- a/main/tollgate_api.c
+++ b/main/tollgate_api.c
@@ -1,6 +1,7 @@
1#include "tollgate_api.h" 1#include "tollgate_api.h"
2#include "cashu.h" 2#include "cashu.h"
3#include "config.h" 3#include "config.h"
4#include "identity.h"
4#include "session.h" 5#include "session.h"
5#include "firewall.h" 6#include "firewall.h"
6#include "nucula_wallet.h" 7#include "nucula_wallet.h"
@@ -17,8 +18,6 @@
17static const char *TAG = "tollgate_api"; 18static const char *TAG = "tollgate_api";
18static httpd_handle_t s_api_server = NULL; 19static httpd_handle_t s_api_server = NULL;
19 20
20static const char *TOLLGATE_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000000";
21
22static esp_err_t get_client_ip(httpd_req_t *req, uint32_t *ip_out) 21static esp_err_t get_client_ip(httpd_req_t *req, uint32_t *ip_out)
23{ 22{
24 int sockfd = httpd_req_to_sockfd(req); 23 int sockfd = httpd_req_to_sockfd(req);
@@ -35,7 +34,7 @@ static cJSON *create_notice(const char *level, const char *code, const char *con
35{ 34{
36 cJSON *root = cJSON_CreateObject(); 35 cJSON *root = cJSON_CreateObject();
37 cJSON_AddNumberToObject(root, "kind", 21023); 36 cJSON_AddNumberToObject(root, "kind", 21023);
38 cJSON_AddStringToObject(root, "pubkey", TOLLGATE_PUBKEY); 37 cJSON_AddStringToObject(root, "pubkey", identity_get()->npub_hex);
39 cJSON *tags = cJSON_CreateArray(); 38 cJSON *tags = cJSON_CreateArray();
40 cJSON *level_tag = cJSON_CreateArray(); 39 cJSON *level_tag = cJSON_CreateArray();
41 cJSON_AddItemToArray(level_tag, cJSON_CreateString("level")); 40 cJSON_AddItemToArray(level_tag, cJSON_CreateString("level"));
@@ -54,7 +53,7 @@ static cJSON *create_session_event(uint32_t client_ip, uint64_t allotment_ms)
54{ 53{
55 cJSON *root = cJSON_CreateObject(); 54 cJSON *root = cJSON_CreateObject();
56 cJSON_AddNumberToObject(root, "kind", 1022); 55 cJSON_AddNumberToObject(root, "kind", 1022);
57 cJSON_AddStringToObject(root, "pubkey", TOLLGATE_PUBKEY); 56 cJSON_AddStringToObject(root, "pubkey", identity_get()->npub_hex);
58 57
59 cJSON *tags = cJSON_CreateArray(); 58 cJSON *tags = cJSON_CreateArray();
60 59
@@ -96,7 +95,7 @@ static esp_err_t api_get_discovery(httpd_req_t *req)
96 95
97 cJSON *root = cJSON_CreateObject(); 96 cJSON *root = cJSON_CreateObject();
98 cJSON_AddNumberToObject(root, "kind", 10021); 97 cJSON_AddNumberToObject(root, "kind", 10021);
99 cJSON_AddStringToObject(root, "pubkey", TOLLGATE_PUBKEY); 98 cJSON_AddStringToObject(root, "pubkey", identity_get()->npub_hex);
100 99
101 cJSON *tags = cJSON_CreateArray(); 100 cJSON *tags = cJSON_CreateArray();
102 101
@@ -113,16 +112,36 @@ static esp_err_t api_get_discovery(httpd_req_t *req)
113 cJSON_AddItemToArray(step_tag, cJSON_CreateString(step_str)); 112 cJSON_AddItemToArray(step_tag, cJSON_CreateString(step_str));
114 cJSON_AddItemToArray(tags, step_tag); 113 cJSON_AddItemToArray(tags, step_tag);
115 114
116 cJSON *price_tag = cJSON_CreateArray();
117 cJSON_AddItemToArray(price_tag, cJSON_CreateString("price_per_step"));
118 cJSON_AddItemToArray(price_tag, cJSON_CreateString("cashu"));
119 char price_str[32]; 115 char price_str[32];
120 snprintf(price_str, sizeof(price_str), "%d", cfg->price_per_step); 116 snprintf(price_str, sizeof(price_str), "%d", cfg->price_per_step);
121 cJSON_AddItemToArray(price_tag, cJSON_CreateString(price_str)); 117
122 cJSON_AddItemToArray(price_tag, cJSON_CreateString("sat")); 118 int mint_count = 0;
123 cJSON_AddItemToArray(price_tag, cJSON_CreateString(cfg->mint_url)); 119 const mint_status_t *mints = mint_health_get_all(&mint_count);
124 cJSON_AddItemToArray(price_tag, cJSON_CreateString("1")); 120 bool any_reachable = false;
125 cJSON_AddItemToArray(tags, price_tag); 121
122 for (int i = 0; i < mint_count; i++) {
123 if (!mints[i].reachable) continue;
124 any_reachable = true;
125 cJSON *price_tag = cJSON_CreateArray();
126 cJSON_AddItemToArray(price_tag, cJSON_CreateString("price_per_step"));
127 cJSON_AddItemToArray(price_tag, cJSON_CreateString("cashu"));
128 cJSON_AddItemToArray(price_tag, cJSON_CreateString(price_str));
129 cJSON_AddItemToArray(price_tag, cJSON_CreateString("sat"));
130 cJSON_AddItemToArray(price_tag, cJSON_CreateString(mints[i].url));
131 cJSON_AddItemToArray(price_tag, cJSON_CreateString("1"));
132 cJSON_AddItemToArray(tags, price_tag);
133 }
134
135 if (!any_reachable) {
136 cJSON *price_tag = cJSON_CreateArray();
137 cJSON_AddItemToArray(price_tag, cJSON_CreateString("price_per_step"));
138 cJSON_AddItemToArray(price_tag, cJSON_CreateString("cashu"));
139 cJSON_AddItemToArray(price_tag, cJSON_CreateString(price_str));
140 cJSON_AddItemToArray(price_tag, cJSON_CreateString("sat"));
141 cJSON_AddItemToArray(price_tag, cJSON_CreateString(cfg->mint_url));
142 cJSON_AddItemToArray(price_tag, cJSON_CreateString("1"));
143 cJSON_AddItemToArray(tags, price_tag);
144 }
126 145
127 cJSON *tips_tag = cJSON_CreateArray(); 146 cJSON *tips_tag = cJSON_CreateArray();
128 cJSON_AddItemToArray(tips_tag, cJSON_CreateString("tips")); 147 cJSON_AddItemToArray(tips_tag, cJSON_CreateString("tips"));
@@ -466,8 +485,28 @@ static esp_err_t api_post_wallet_send(httpd_req_t *req)
466 return ESP_OK; 485 return ESP_OK;
467} 486}
468 487
488static esp_err_t api_get_mints(httpd_req_t *req)
489{
490 int mint_count = 0;
491 const mint_status_t *mints = mint_health_get_all(&mint_count);
492 cJSON *arr = cJSON_CreateArray();
493 for (int i = 0; i < mint_count; i++) {
494 cJSON *obj = cJSON_CreateObject();
495 cJSON_AddStringToObject(obj, "url", mints[i].url);
496 cJSON_AddBoolToObject(obj, "reachable", mints[i].reachable);
497 cJSON_AddItemToArray(arr, obj);
498 }
499 char *json = cJSON_PrintUnformatted(arr);
500 httpd_resp_set_type(req, "application/json");
501 httpd_resp_send(req, json, strlen(json));
502 cJSON_free(json);
503 cJSON_Delete(arr);
504 return ESP_OK;
505}
506
469static const httpd_uri_t uri_discovery = { .uri = "/", .method = HTTP_GET, .handler = api_get_discovery }; 507static const httpd_uri_t uri_discovery = { .uri = "/", .method = HTTP_GET, .handler = api_get_discovery };
470static const httpd_uri_t uri_payment = { .uri = "/", .method = HTTP_POST, .handler = api_post_payment }; 508static const httpd_uri_t uri_payment = { .uri = "/", .method = HTTP_POST, .handler = api_post_payment };
509static const httpd_uri_t uri_mints = { .uri = "/mints", .method = HTTP_GET, .handler = api_get_mints };
471static const httpd_uri_t uri_usage = { .uri = "/usage", .method = HTTP_GET, .handler = api_get_usage }; 510static const httpd_uri_t uri_usage = { .uri = "/usage", .method = HTTP_GET, .handler = api_get_usage };
472static const httpd_uri_t uri_whoami = { .uri = "/whoami", .method = HTTP_GET, .handler = api_get_whoami }; 511static const httpd_uri_t uri_whoami = { .uri = "/whoami", .method = HTTP_GET, .handler = api_get_whoami };
473static const httpd_uri_t uri_wallet = { .uri = "/wallet", .method = HTTP_GET, .handler = api_get_wallet }; 512static const httpd_uri_t uri_wallet = { .uri = "/wallet", .method = HTTP_GET, .handler = api_get_wallet };
@@ -520,17 +559,19 @@ esp_err_t tollgate_api_start(void)
520 httpd_config_t config = HTTPD_DEFAULT_CONFIG(); 559 httpd_config_t config = HTTPD_DEFAULT_CONFIG();
521 config.server_port = 2121; 560 config.server_port = 2121;
522 config.ctrl_port = 32769; 561 config.ctrl_port = 32769;
523 config.max_uri_handlers = 10; 562 config.max_uri_handlers = 12;
524 config.stack_size = 16384; 563 config.stack_size = 16384;
525 564
526 esp_err_t ret = httpd_start(&s_api_server, &config); 565 esp_err_t ret = httpd_start(&s_api_server, &config);
527 if (ret != ESP_OK) { 566 if (ret != ESP_OK) {
528 ESP_LOGE(TAG, "Failed to start API server: %s", esp_err_to_name(ret)); 567 ESP_LOGE(TAG, "Failed to start API server: %s (heap: %lu)", esp_err_to_name(ret), (unsigned long)esp_get_free_heap_size());
568 s_api_server = NULL;
529 return ret; 569 return ret;
530 } 570 }
531 571
532 httpd_register_uri_handler(s_api_server, &uri_discovery); 572 httpd_register_uri_handler(s_api_server, &uri_discovery);
533 httpd_register_uri_handler(s_api_server, &uri_payment); 573 httpd_register_uri_handler(s_api_server, &uri_payment);
574 httpd_register_uri_handler(s_api_server, &uri_mints);
534 httpd_register_uri_handler(s_api_server, &uri_usage); 575 httpd_register_uri_handler(s_api_server, &uri_usage);
535 httpd_register_uri_handler(s_api_server, &uri_whoami); 576 httpd_register_uri_handler(s_api_server, &uri_whoami);
536 httpd_register_uri_handler(s_api_server, &uri_wallet); 577 httpd_register_uri_handler(s_api_server, &uri_wallet);