feat(cells): Phase 3 tests + Phase 4 UI for cell service-sharing

Phase 3 — tests (50 new, total now 1071):
- test_cell_link_manager: atomicity (WG fail → DNS not called, link not
  persisted), DNS warning non-fatal, inbound_services arg, unknown service
  filtered, update/get permissions, lazy migration of legacy entries
- test_wireguard_manager: subnet overlap rejection (exact, supernet, adjacent
  non-overlapping, different class-A, honours wg0.conf configured network)
- test_firewall_manager: _cell_tag sanitisation, apply_cell_rules emits correct
  ACCEPT/DROP per service + catch-all DROP, clear_cell_rules no-op and exact
  line removal, apply_all_cell_rules iterates with correct args
- test_cells_endpoints: RuntimeError→400, GET /services, GET/PUT permissions
  (200/400/404 paths, service name validation, arg forwarding)

Phase 4 — UI:
- CellNetwork.jsx: replace flat cell list with CellPanel expandable cards;
  add ServiceShareToggle (ARIA switch, saves immediately), InboundServiceBadge
  (read-only), DisconnectConfirmModal (replaces window.confirm); relative
  timestamps; paste validation on blur; WireGuard status merged by public_key
- api.js: add cellLinkAPI.getPermissions, updatePermissions, getServices

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-01 08:45:32 -04:00
parent 0b103ffafb
commit 562d866a65
6 changed files with 1023 additions and 47 deletions
+4
View File
@@ -278,6 +278,10 @@ export const cellLinkAPI = {
addConnection: (invite) => api.post('/api/cells', invite),
removeConnection: (name) => api.delete(`/api/cells/${name}`),
getStatus: (name) => api.get(`/api/cells/${name}/status`),
getPermissions: (cellName) => api.get(`/api/cells/${cellName}/permissions`),
updatePermissions: (cellName, inbound, outbound) =>
api.put(`/api/cells/${cellName}/permissions`, { inbound, outbound }),
getServices: () => api.get('/api/cells/services'),
};
// Health check