feat(FN-2920): merge fusion/fn-2920

- Add cloudflared binary detection to the remote-access routes, surfaced in the Settings modal with install/uninstall UX states
- Update `SettingsModal.tsx` with new cloudflared status UI and install prompt flows; add co-located CSS in `SettingsModal.css`
- Add `SettingsModal.test.tsx` covering the new cloudflared states and API interactions
- Extend `remote-access-routes.test.ts` with detection and install/uninstall coverage
- Document the new cloudflared detection UX in `docs/remote-access.md` and `docs/settings-reference.md`
- Minor `server.test.ts` fixture update to align with the new server contract

Commits merged:
- docs(FN-2920): complete Step 7 — document cloudflared detection UX
- test(FN-2920): complete Step 6 — update server contract expectations
- feat(FN-2920): complete Step 5 — add cloudflared detection and install coverage

Files changed:
docs/remote-access.md                              |   2 +
 docs/settings-reference.md                         |   2 +
 packages/dashboard/app/api/legacy.ts               |   7 ++
 .../dashboard/app/components/SettingsModal.css     |  42 ++++++++
 .../dashboard/app/components/SettingsModal.tsx     | 112 ++++++++++++++++-----
 .../components/__tests__/SettingsModal.test.tsx    |  66 ++++++++++++
 .../src/__tests__/remote-access-routes.test.ts     | 107 +++++++++++++++++++-
 packages/dashboard/src/__tests__/server.test.ts    |   4 +-
 .../src/routes/register-settings-memory-routes.ts  |  59 ++++++++++-
 9 files changed, 372 insertions(+), 29 deletions(-)

Fusion-Task-Id: FN-2920
This commit is contained in:
Fusion
2026-04-29 13:55:42 -07:00
committed by gsxdsm
parent 9d9f351749
commit 6a5e240e9a

View File

@@ -333,5 +333,15 @@ describe("remote access API route contracts", () => {
data: expect.stringContaining("<svg"),
tokenType: "short-lived",
});
// The .url field is what the QR matrix encodes — assert it carries the
// tailnet origin AND the rt= access token so a single scan authenticates.
expect(qrSvg.body.url).toMatch(/\/remote-login\?rt=[A-Za-z0-9_-]+/);
const qrTerminal = await REQUEST(app, "GET", "/api/remote/qr?format=terminal&tokenType=persistent");
expect(qrTerminal.status).toBe(200);
expect(qrTerminal.body.format).toBe("terminal");
expect(typeof qrTerminal.body.data).toBe("string");
expect(qrTerminal.body.data.length).toBeGreaterThan(0);
expect(qrTerminal.body.url).toMatch(/\/remote-login\?rt=[A-Za-z0-9_-]+/);
});
});