diff --git a/caddy/Caddyfile b/caddy/Caddyfile index 0ae1a0b83a440f839def1ca8ffd1f42f1386164e..f6a17946ef269762b794205e3be6d4b14e90e9d8 100644 --- a/caddy/Caddyfile +++ b/caddy/Caddyfile @@ -169,7 +169,7 @@ Disallow: /wallet/ } @rest_apis { - path /hafah-api/* /balance-api/* /reputation-api/* /hafbe-api/* /hivesense-api/* /hivemind-api/* /nft-tracker-api/* /hived-api/* /hafsql /hafsql/* /status-api/* + path /hafah-api/* /balance-api/* /reputation-api/* /hafbe-api/* /hivesense-api/* /hivemind-api/* /nft-tracker-api/* /hived-api/* /wallet-api/* /hafsql /hafsql/* /status-api/* } handle @rest_apis { diff --git a/haf_base.yaml b/haf_base.yaml index 60c005a128adb7940fa877b0c5540a6dacc53566..768a802b76e4e549694715ea290eeae7a2e8b9bf 100644 --- a/haf_base.yaml +++ b/haf_base.yaml @@ -143,11 +143,11 @@ services: retries: 10 start_period: 1m labels: - - "io.hive.swagger.url=/hived-api/" - - "io.hive.swagger.name=Legacy Hive JSON-RPC API" - - "io.hive.swagger.interceptor=jsonrpc" - - "io.hive.swagger.order=99" - - "io.hive.healthcheck.port=7003" + - "io.hive.swagger.url=/hived-api/,/wallet-api/" + - "io.hive.swagger.name=Legacy Hive JSON-RPC API,Wallet API" + - "io.hive.swagger.interceptor=jsonrpc,jsonrpc" + - "io.hive.swagger.order=98,99" + - "io.hive.healthcheck.port=7003,7003" logrotate: image: ${LOGROTATE_IMAGE:-${HIVE_API_NODE_REGISTRY:-registry.hive.blog}/haf_api_node/logrotate}:${LOGROTATE_VERSION:-latest} profiles: diff --git a/status/app.js b/status/app.js index be40acbfb08c97552d96cd9844be2c5c036902b0..ddfc263016f9f542bb36beb5cbc4c65779321417 100644 --- a/status/app.js +++ b/status/app.js @@ -230,39 +230,78 @@ class StatusMonitor { async updateStatus() { try { - // Build filter labels - always include swagger URL - const labelFilters = ['io.hive.swagger.url']; - - // If PROJECT_NAME is set, only show containers from this stack + // Get all containers (we'll filter by project if needed) + const filterOptions = {}; if (PROJECT_NAME) { - labelFilters.push(`com.docker.compose.project=${PROJECT_NAME}`); + filterOptions.label = [`com.docker.compose.project=${PROJECT_NAME}`]; } - // Get containers with swagger labels (optionally filtered by project) const containers = await docker.listContainers({ - filters: { label: labelFilters } + filters: filterOptions }); // Extract app info and check health in parallel - const appPromises = containers.map(async (container) => { + const appPromises = []; + + for (const container of containers) { const labels = container.Labels; - const healthPort = labels['io.hive.healthcheck.port']; - const healthTimeout = parseInt(labels['io.hive.healthcheck.timeout']) || HEALTHCHECK_TIMEOUT; - const order = parseInt(labels['io.hive.swagger.order']) || 999; - - let status = 'unknown'; - if (healthPort) { - const isHealthy = await checkHealth(parseInt(healthPort), healthTimeout); - status = isHealthy ? 'healthy' : 'unhealthy'; - } - return { - name: labels['io.hive.swagger.name'] || 'Unknown', - url_path: labels['io.hive.swagger.url'] || '/', - status, - order - }; - }); + // Check if container has swagger labels + const swaggerUrl = labels['io.hive.swagger.url']; + if (!swaggerUrl) continue; + + // Check if labels contain comma-separated values (multiple specs) + if (swaggerUrl.includes(',')) { + // Split all comma-separated values + const urls = swaggerUrl.split(',').map(s => s.trim()); + const names = (labels['io.hive.swagger.name'] || '').split(',').map(s => s.trim()); + const orders = (labels['io.hive.swagger.order'] || '').split(',').map(s => s.trim()); + const healthPorts = (labels['io.hive.healthcheck.port'] || '').split(',').map(s => s.trim()); + const healthTimeouts = (labels['io.hive.healthcheck.timeout'] || '').split(',').map(s => s.trim()); + + // Create an app entry for each spec + urls.forEach((url, i) => { + const order = parseInt(orders[i]) || 999; + const healthPort = healthPorts[i] || healthPorts[0]; // Fall back to first port if not enough ports + const healthTimeout = parseInt(healthTimeouts[i] || healthTimeouts[0]) || HEALTHCHECK_TIMEOUT; + + appPromises.push((async () => { + let status = 'unknown'; + if (healthPort) { + const isHealthy = await checkHealth(parseInt(healthPort), healthTimeout); + status = isHealthy ? 'healthy' : 'unhealthy'; + } + + return { + name: names[i] || 'Unknown', + url_path: url, + status, + order + }; + })()); + }); + } else { + // Single spec (backward compatibility) + const order = parseInt(labels['io.hive.swagger.order']) || 999; + const healthPort = labels['io.hive.healthcheck.port']; + const healthTimeout = parseInt(labels['io.hive.healthcheck.timeout']) || HEALTHCHECK_TIMEOUT; + + appPromises.push((async () => { + let status = 'unknown'; + if (healthPort) { + const isHealthy = await checkHealth(parseInt(healthPort), healthTimeout); + status = isHealthy ? 'healthy' : 'unhealthy'; + } + + return { + name: labels['io.hive.swagger.name'] || 'Unknown', + url_path: swaggerUrl, + status, + order + }; + })()); + } + } const apps = await Promise.all(appPromises); diff --git a/swagger/templates/swagger.tmpl b/swagger/templates/swagger.tmpl index 693bb9cd6ba23ff29a8e453d30ac2b5e21837f5f..7a24226feedd7b47b682b632cb51bac0350d64e4 100644 --- a/swagger/templates/swagger.tmpl +++ b/swagger/templates/swagger.tmpl @@ -5,16 +5,25 @@ window.onload = function () { {{- $projectName := env "PROJECT_NAME" }} const PROJECT_NAME = "{{ $projectName }}"; const ALL_SPECS = [ - {{- $svcs := whereLabelExists . "io.hive.swagger.url" }} - {{- range $i, $c := $svcs }} - { - url: "{{ index $c.Labels "io.hive.swagger.url" }}", - name: "{{ index $c.Labels "io.hive.swagger.name" }}", - interceptor: "{{ index $c.Labels "io.hive.swagger.interceptor" }}", - order: Number("{{ index $c.Labels "io.hive.swagger.order" }}") || 0, - composeProject: "{{ index $c.Labels "com.docker.compose.project" }}" - }{{ if lt (add $i 1) (len $svcs) }},{{ end }} - {{- end }} + {{ range $c := . }} + {{ $composeProject := index $c.Labels "com.docker.compose.project" }} + {{ $urlLabel := index $c.Labels "io.hive.swagger.url" }} + {{ if $urlLabel }} + ...(function() { + const urls = "{{ $urlLabel }}".split(',').map(s => s.trim()); + const names = "{{ index $c.Labels "io.hive.swagger.name" }}".split(',').map(s => s.trim()); + const interceptors = "{{ index $c.Labels "io.hive.swagger.interceptor" }}".split(',').map(s => s.trim()); + const orders = "{{ index $c.Labels "io.hive.swagger.order" }}".split(',').map(s => s.trim()); + return urls.map((url, i) => ({ + url: url, + name: names[i] || '', + interceptor: interceptors[i] || '', + order: Number(orders[i]) || 0, + composeProject: "{{ $composeProject }}" + })); + })(), + {{ end }} + {{ end }} ]; // Filter specs to only show containers from the same stack if PROJECT_NAME is set diff --git a/varnish/default.vcl b/varnish/default.vcl index 61d7a59e4b63f9ad4c8bd2f3efeb3a3f93311143..23be140d6c5bfe17b599fcb8f4067c1987bf123b 100644 --- a/varnish/default.vcl +++ b/varnish/default.vcl @@ -77,7 +77,7 @@ backend hived_openapi_spec { # .port = "81"; #} -# backend haf_block_explorer_swagger { +# backend haf_block_explorer_swagger { # .host = "block-explorer-swagger"; # .port = "80"; # } @@ -174,6 +174,9 @@ sub vcl_recv { if (req.method == "POST") { call recv_cachable_post; } + } elseif (req.url == "/wallet-api/") { + set req.url = "/walletapi.json"; + set req.backend_hint = hived_openapi_spec; } elseif (req.url == "/hived-api/") { set req.url = "/openapi.json"; set req.backend_hint = hived_openapi_spec;