Skip to content

Add backend WARP actions for nodes#184

Open
RickeyRen wants to merge 8 commits into
remnawave:mainfrom
RickeyRen:codex/warp-backend-node-actions-upstream
Open

Add backend WARP actions for nodes#184
RickeyRen wants to merge 8 commits into
remnawave:mainfrom
RickeyRen:codex/warp-backend-node-actions-upstream

Conversation

@RickeyRen
Copy link
Copy Markdown

@RickeyRen RickeyRen commented Jun 3, 2026

Summary

  • add backend proxy endpoints for node WARP status and lifecycle actions
  • expose install, enable, disable, and uninstall operations through the existing node action flow
  • keep node data contracts aligned with frontend/node WARP controls

Verification

  • Deployed with the matching frontend/node branches on the production test panel
  • Browser UI shows 4 online nodes / 0 offline nodes
  • PostgreSQL nodes table shows all 4 nodes connected and enabled
  • Recent backend logs after rollout window show no continuing ECONNREFUSED/Auth/Network errors

@snyk-io
Copy link
Copy Markdown

snyk-io Bot commented Jun 3, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@RickeyRen
Copy link
Copy Markdown
Author

Production smoke test update (2026-06-03):

  • Used a temporary API token, then deleted it after the test (api_tokens cleanup verified count = 0).
  • Called the real HTTPS backend route POST /api/nodes/:uuid/actions/warp/disable and then POST /api/nodes/:uuid/actions/warp/enable for RackNerd. Both returned HTTP 200.
  • Verified on the node host that WARP was restored with a fresh WireGuard handshake and the generated config includes Table = off and PersistentKeepalive = 25.

Note: direct HTTP calls to the backend container IP are intentionally rejected by the existing proxy-check middleware; the successful test used the production HTTPS domain.

@RickeyRen RickeyRen marked this pull request as ready for review June 4, 2026 01:27
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Jun 4, 2026

Greptile Summary

This PR adds five WARP management endpoints to the nodes module (status, install, enable, disable, uninstall), proxying requests through the backend to each node's local WARP API. It also extends the contract schemas with WarpStatusSchema and HostConnectivitySchema, adds a shared requestWarp helper in AxiosService, and rebuilds @mongodb-js/zstd in the Dockerfile.

  • Five new controller/service/DTO triplets follow the established @Endpoint / errorHandler pattern and correctly gate all actions behind isDisabled and isConnected checks.
  • mergeWarpStatus in NodesSystemCacheService silently no-ops when the stats cache entry is absent; since getWarpActionNodeResponse reads from the same cache immediately after, the immediate action response may omit the WARP field even when the underlying operation succeeded.
  • Minor inconsistencies: nodes-system-cache.service.ts imports TWarpStatus via @libs/contracts/models rather than the @contract/models alias used everywhere else, and getNodeWarpStatus re-wraps getNodeForWarpAction errors with a redundant fallback pattern that the other four methods do not use.

Confidence Score: 3/5

Safe to merge for most scenarios; the main concern is that WARP action responses silently omit warp state data when the node stats cache has expired, which could leave the frontend with an incomplete view after the very operation the user just triggered.

The five new warp endpoints are structurally sound and correctly guarded. The mergeWarpStatus silent early-return means that on a cold cache the response from any of the four mutation endpoints will not contain the warp status object, even though the action ran successfully on the node. This is a real, if intermittent, data-freshness gap on the critical happy-path response. The inconsistent import alias and error-re-wrapping pattern in getNodeWarpStatus are minor but add noise to an otherwise clean implementation.

src/modules/nodes/nodes-system-cache.service.ts — the mergeWarpStatus early-exit and the inconsistent import path both live here and warrant a second look before merge.

Important Files Changed

Filename Overview
src/modules/nodes/nodes.service.ts Adds five WARP service methods; getNodeWarpStatus re-wraps errors inconsistently vs. the other four methods, and all methods silently pass through an incomplete NodeResponseModel when the stats cache is cold.
src/modules/nodes/nodes-system-cache.service.ts Adds mergeWarpStatus which silently exits when cache is absent; imports TWarpStatus via @libs/contracts/models rather than the canonical @contract/models alias used elsewhere.
src/common/axios/axios.service.ts Adds a shared requestWarp helper and five public WARP methods; auth and HTTPS agent are inherited from the existing axiosInstance, error handling is consistent with existing patterns.
src/modules/nodes/nodes.controller.ts Wires five new WARP controller actions using established @Endpoint / errorHandler patterns; no issues found.
libs/contract/models/node-system.schema.ts Adds WarpStatusSchema, WarpOperationSchema, HostConnectivitySchema, and optional host/warp fields to NodeSystemStatsSchema; schema structure looks correct and properly typed.
test/warp-contract.test.mjs Tests verify file-level string presence (static assertions), not runtime behaviour; /node/warp/status path is absent from the assertions that check axios service paths.
Dockerfile Adds npm rebuild @mongodb-js/zstd before pm2 install and fixes a missing newline at end of file; straightforward and correct.

Sequence Diagram

sequenceDiagram
    participant C as Client
    participant Ctrl as NodesController
    participant Svc as NodesService
    participant Repo as NodesRepository
    participant Cache as NodesSystemCacheService
    participant Axios as AxiosService
    participant Node as Remote Node

    C->>Ctrl: POST /nodes/:uuid/actions/warp/install
    Ctrl->>Svc: installNodeWarp(uuid)
    Svc->>Repo: findByUUID(uuid)
    Repo-->>Svc: NodesEntity (or null)
    alt node missing / disabled / disconnected
        Svc-->>Ctrl: fail(error)
        Ctrl-->>C: HTTP error
    end
    Svc->>Axios: installWarp(address, port) [timeout 180s]
    Axios->>Node: POST /node/warp/install
    Node-->>Axios: TWarpStatus response
    Axios-->>Svc: ok(TNodeWarpResponse)
    Svc->>Cache: mergeWarpStatus(uuid, warpStatus)
    alt stats cache is present
        Cache->>Cache: spread stats + warp → set with TTL
    else cache is absent
        Cache-->>Svc: (silent no-op — warp not cached)
    end
    Svc->>Repo: findByUUID(uuid)
    Svc->>Cache: getOne(uuid)
    Cache-->>Svc: INodeSystem (may lack warp field)
    Svc-->>Ctrl: ok(NodeResponseModel)
    Ctrl-->>C: HTTP 200 NodeResponseModel
Loading

Comments Outside Diff (1)

  1. src/modules/nodes/nodes-system-cache.service.ts, line 564-581 (link)

    P1 Silent no-op when stats cache is cold may produce incomplete action responses

    mergeWarpStatus returns early if NODE_SYSTEM_STATS isn't currently cached. The callers in nodes.service.ts always follow this with getWarpActionNodeResponse, which reads system info from the same cold cache. When the cache is empty (e.g., it expired between the last stats push and the user triggering the action), the WARP field won't appear in the NodeResponseModel returned by any of the warp action endpoints, even though the underlying operation on the node succeeded. The frontend will receive the action's HTTP 200 but see no warp data. Consider initialising a minimal stats entry here if none exists, or at minimum populating the warp sub-key when the parent entry is missing.

Reviews (1): Last reviewed commit: "fix: rebuild zstd native binding in imag..." | Re-trigger Greptile

Comment on lines +311 to +315
if (!nodeResult.isOk) {
return fail({
code: nodeResult.code ?? ERRORS.NODE_NOT_FOUND.code,
message: nodeResult.message ?? ERRORS.NODE_NOT_FOUND.message,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Inconsistent error propagation in getNodeWarpStatus

All four other warp action methods (enable/install/disable/uninstall) forward the nodeResult failure directly (return nodeResult), but getNodeWarpStatus re-wraps it with fail({ code: nodeResult.code ?? ..., message: nodeResult.message ?? ... }). If getNodeForWarpAction returns NODE_IS_DISABLED or "Node is not connected", the code and message from those errors are still propagated correctly here (the ?? fallback is never reached in practice). However, the pattern diverges from the other four methods without any apparent reason, making the error-handling contract harder to reason about.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

import { Injectable } from '@nestjs/common';

import { RawCacheService } from '@common/raw-cache';
import { TWarpStatus } from '@libs/contracts/models';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Inconsistent import path alias

TWarpStatus is imported via @libs/contracts/models here, but both nodes.service.ts and axios.service.ts use the @contract/models alias for the same type. The two aliases likely resolve to the same location at runtime, but the inconsistency will cause confusion and may trip up tools that track import sources.

Suggested change
import { TWarpStatus } from '@libs/contracts/models';
import { TWarpStatus } from '@contract/models';

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant