From 2a86bec93273e2f4ceeab60683058c65dbb1da3d Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 18 May 2026 14:50:41 +0530 Subject: feat: multi-mint health tracker, discovery, portal, multi-wallet (Phase 3-8) - mint_health.h/c: FreeRTOS probing task, GET /v1/info every 5min, recovery threshold 3, immediate failure, mutex-protected state - cashu.c: health-gated acceptance (config match AND reachable) - tollgate_api.c: one price_per_step tag per reachable mint in discovery - captive_portal.c: mint list with green/grey indicators, /mints API, auto-refresh every 30s via JS - nucula_wallet.h/cpp: multi-wallet (up to 4), route receive to correct wallet by mint URL, balance sums across all wallets - tollgate_main.c: init health tracker + multi-wallet on service start - CMakeLists.txt: add mint_health.c --- main/captive_portal.c | 86 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 11 deletions(-) (limited to 'main/captive_portal.c') diff --git a/main/captive_portal.c b/main/captive_portal.c index 1a3d5ce..57ef65a 100644 --- a/main/captive_portal.c +++ b/main/captive_portal.c @@ -2,6 +2,7 @@ #include "firewall.h" #include "session.h" #include "config.h" +#include "mint_health.h" #include "esp_log.h" #include "esp_wifi.h" #include "cJSON.h" @@ -42,9 +43,14 @@ static const char PORTAL_HTML_TEMPLATE[] = \ ".btn:disabled{background:#333;color:#666;cursor:not-allowed}" ".mints{background:#252525;border-radius:12px;padding:12px;margin-top:16px;text-align:left}" ".mints-title{color:#888;font-size:12px;margin-bottom:8px}" -".mint-url{font-family:monospace;font-size:11px;color:#f7931a;word-break:break-all;" -"background:#1a1a1a;padding:8px;border-radius:6px;cursor:pointer}" -".mint-url:active{opacity:0.7}" +".mint-item{display:flex;align-items:center;padding:6px 8px;margin-bottom:4px;" +"background:#1a1a1a;border-radius:6px;cursor:pointer}" +".mint-item:active{opacity:0.7}" +".mint-dot{width:8px;height:8px;border-radius:50%;margin-right:8px;flex-shrink:0}" +".mint-dot.green{background:#4caf50}" +".mint-dot.grey{background:#666}" +".mint-url{font-family:monospace;font-size:11px;color:#f7931a;word-break:break-all}" +".mint-url.dim{color:#666}" ".mint-hint{color:#666;font-size:10px;margin-top:4px}" "#status{margin-top:16px;padding:12px;border-radius:8px;display:none;font-size:14px}" "#status.success{display:block;background:#1a472a;color:#4caf50}" @@ -63,20 +69,21 @@ static const char PORTAL_HTML_TEMPLATE[] = \ "" "
" "
SUPPORTED MINTS
" -"
__MINT_URL__
" -"
Tap to copy • Mint tokens at this URL before paying
" +"
__MINT_LIST__
" +"
Tap to copy • Green = reachable
" "
" "
" "" "" ""; @@ -122,10 +143,32 @@ static esp_err_t portal_handler(httpd_req_t *req) const char *tpl = PORTAL_HTML_TEMPLATE; size_t tpl_len = strlen(tpl); + char mint_list_html[4096]; + mint_list_html[0] = '\0'; + int mint_count = 0; + const mint_status_t *mints = mint_health_get_all(&mint_count); + for (int i = 0; i < mint_count; i++) { + const char *cls = mints[i].reachable ? "green" : "grey"; + const char *url_cls = mints[i].reachable ? "mint-url" : "mint-url dim"; + char item[512]; + snprintf(item, sizeof(item), + "
" + "" + "%s
", + mints[i].url, cls, url_cls, mints[i].url); + strncat(mint_list_html, item, sizeof(mint_list_html) - strlen(mint_list_html) - 1); + } + if (mint_count == 0) { + const tollgate_config_t *cfg = tollgate_config_get(); + snprintf(mint_list_html, sizeof(mint_list_html), + "
" + "%s
", cfg->mint_url); + } + struct { const char *key; const char *val; } subs[] = { { "__AP_IP__", s_ap_ip_str }, { "__PRICE__", price_str }, - { "__MINT_URL__", cfg->mint_url }, + { "__MINT_LIST__", mint_list_html }, }; int nsubs = sizeof(subs) / sizeof(subs[0]); @@ -190,6 +233,25 @@ static esp_err_t grant_access_handler(httpd_req_t *req) return ESP_OK; } +static esp_err_t mints_handler(httpd_req_t *req) +{ + int mint_count = 0; + const mint_status_t *mints = mint_health_get_all(&mint_count); + cJSON *arr = cJSON_CreateArray(); + for (int i = 0; i < mint_count; i++) { + cJSON *obj = cJSON_CreateObject(); + cJSON_AddStringToObject(obj, "url", mints[i].url); + cJSON_AddBoolToObject(obj, "reachable", mints[i].reachable); + cJSON_AddItemToArray(arr, obj); + } + char *json = cJSON_PrintUnformatted(arr); + httpd_resp_set_type(req, "application/json"); + httpd_resp_send(req, json, strlen(json)); + cJSON_free(json); + cJSON_Delete(arr); + return ESP_OK; +} + static esp_err_t status_handler(httpd_req_t *req) { const tollgate_config_t *cfg = tollgate_config_get(); @@ -290,6 +352,7 @@ static esp_err_t catchall_handler(httpd_req_t *req) static const httpd_uri_t uri_portal = { .uri = "/", .method = HTTP_GET, .handler = portal_handler }; static const httpd_uri_t uri_grant = { .uri = "/grant_access", .method = HTTP_GET, .handler = grant_access_handler }; +static const httpd_uri_t uri_mints = { .uri = "/mints", .method = HTTP_GET, .handler = mints_handler }; static const httpd_uri_t uri_status = { .uri = "/api/status", .method = HTTP_GET, .handler = status_handler }; static const httpd_uri_t uri_whoami = { .uri = "/whoami", .method = HTTP_GET, .handler = whoami_handler }; static const httpd_uri_t uri_usage = { .uri = "/usage", .method = HTTP_GET, .handler = usage_handler }; @@ -320,6 +383,7 @@ esp_err_t captive_portal_start(const char *ap_ip_str) httpd_register_uri_handler(s_server, &uri_portal); httpd_register_uri_handler(s_server, &uri_grant); + httpd_register_uri_handler(s_server, &uri_mints); httpd_register_uri_handler(s_server, &uri_status); httpd_register_uri_handler(s_server, &uri_whoami); httpd_register_uri_handler(s_server, &uri_usage); -- cgit v1.2.3