dev_arc_aws/src/App.tsx

126 lines
4.8 KiB
TypeScript
Raw Normal View History

2026-06-18 08:14:00 -04:00
import { useState } from 'react'
import { Routes, Route, useLocation } from 'react-router-dom'
2026-06-18 08:14:00 -04:00
import Sidebar from './components/Sidebar'
import TopBar from './components/TopBar'
import Glance from './pages/Glance'
import Infrastructure from './pages/Infrastructure'
import BookNest from './pages/BookNest'
Add Phase 1a: core SSH terminal (Termix migration) Implements the minimal-viable terminal described in TERMIX_MIGRATION.md Phase 1a: a real interactive SSH session in the browser over a WebSocket, using xterm.js on the frontend and ssh2 on the backend. Reuses ArchNest's existing SSH integrations (host/port/username/ password/privateKey/passphrase) instead of introducing a second, duplicate host-management system the way Termix has one. Backend: new /api/terminal WebSocket route (registered via @fastify/websocket) handling connect/input/resize/disconnect messages, authenticated via a JWT passed as a query param (browsers can't set custom headers on the WS handshake). Extracted the integration secret loader out of routes/integrations.ts into db/secrets.ts so the new terminal route can reuse it without duplicating the decrypt logic. Frontend: new Terminal.tsx page listing configured SSH hosts and rendering an xterm.js terminal wired to the WebSocket; wired into App.tsx at /terminal. vite.config.ts's dev proxy now forwards WebSocket upgrades (ws: true) so this works under `npm run dev`. Verified end-to-end against a real (test) ssh2-based SSH server: connect, shell banner, keystroke echo, and prompt redraw all worked correctly over the actual WebSocket protocol. Deliberately deferred to Phase 1b/1c per the migration doc: jump-host chaining, tab/split-pane UI, terminal theme/font settings, OPKSSH cert auth, tmux session monitor, session recording. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
2026-06-19 10:52:04 +00:00
import Terminal from './pages/Terminal'
import Tunnels from './pages/Tunnels'
import Files from './pages/Files'
import Containers from './pages/Containers'
import RemoteDesktop from './pages/RemoteDesktop'
import HostMetrics from './pages/HostMetrics'
import Settings from './pages/Settings'
import Help from './pages/Help'
import Login from './pages/Login'
import Enrollment from './pages/Enrollment'
Add mesh prerequisite gate (#33) * Add mesh prerequisite gate (NetBird verification before app config) Implements the design in docs/mesh-prerequisite-gate.md per the user's DECIDE A-D answers: a permanent admin override, B1 (reachable) verification with host mesh IP shown informationally, members allowed in with a notice instead of being blocked, and mesh.required defaulting off so the live production instance is unaffected. - system_config kv table + getConfig/setConfig helpers - /api/system/mesh-status, /mesh/verify, /mesh/override, /mesh/required - AuthContext gains a 'needs-mesh' status (admins only) and exposes meshStatus for a member-facing banner - MeshGate page reuses the integration create+test flow to connect NetBird * Make mesh verification universal (CIDR check, not NetBird-specific) Replace the NetBird-adapter-based "reachable" check with a vendor-agnostic one: the admin supplies the mesh's IP range (CIDR), and verification just confirms this host has an address inside it. Works identically for NetBird, WireGuard, ZeroTier, Tailscale, or any other mesh tech, with no integration record or vendor API call required. * Add reachability fallback for routed meshes (VPC peering, etc.) A host can be on the mesh's "side" of a routed network (e.g. a VPC peered into a NetBird/WireGuard mesh) without holding a local IP in the mesh's own CIDR. Local-IP-in-CIDR stays the primary check; if it fails, the admin can supply a known peer/gateway IP on the mesh and we verify by pinging it instead. Adds iputils to the backend image for the ping binary. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 17:30:46 -04:00
import MeshGate from './pages/MeshGate'
import { useAuth } from './lib/AuthContext'
2026-06-18 08:14:00 -04:00
function App() {
const { status } = useAuth()
if (status === 'loading') {
return (
<div className="flex h-screen w-screen items-center justify-center bg-page">
<p style={{ color: '#7A7D85', fontSize: '13px' }}>Loading</p>
</div>
)
}
if (status === 'needs-setup' || status === 'enrolling') return <Enrollment />
if (status === 'logged-out') return <Login />
Add mesh prerequisite gate (#33) * Add mesh prerequisite gate (NetBird verification before app config) Implements the design in docs/mesh-prerequisite-gate.md per the user's DECIDE A-D answers: a permanent admin override, B1 (reachable) verification with host mesh IP shown informationally, members allowed in with a notice instead of being blocked, and mesh.required defaulting off so the live production instance is unaffected. - system_config kv table + getConfig/setConfig helpers - /api/system/mesh-status, /mesh/verify, /mesh/override, /mesh/required - AuthContext gains a 'needs-mesh' status (admins only) and exposes meshStatus for a member-facing banner - MeshGate page reuses the integration create+test flow to connect NetBird * Make mesh verification universal (CIDR check, not NetBird-specific) Replace the NetBird-adapter-based "reachable" check with a vendor-agnostic one: the admin supplies the mesh's IP range (CIDR), and verification just confirms this host has an address inside it. Works identically for NetBird, WireGuard, ZeroTier, Tailscale, or any other mesh tech, with no integration record or vendor API call required. * Add reachability fallback for routed meshes (VPC peering, etc.) A host can be on the mesh's "side" of a routed network (e.g. a VPC peered into a NetBird/WireGuard mesh) without holding a local IP in the mesh's own CIDR. Local-IP-in-CIDR stays the primary check; if it fails, the admin can supply a known peer/gateway IP on the mesh and we verify by pinging it instead. Adds iputils to the backend image for the ping binary. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 17:30:46 -04:00
if (status === 'needs-mesh') return <MeshGate />
return <Dashboard />
}
function Dashboard() {
Add mesh prerequisite gate (#33) * Add mesh prerequisite gate (NetBird verification before app config) Implements the design in docs/mesh-prerequisite-gate.md per the user's DECIDE A-D answers: a permanent admin override, B1 (reachable) verification with host mesh IP shown informationally, members allowed in with a notice instead of being blocked, and mesh.required defaulting off so the live production instance is unaffected. - system_config kv table + getConfig/setConfig helpers - /api/system/mesh-status, /mesh/verify, /mesh/override, /mesh/required - AuthContext gains a 'needs-mesh' status (admins only) and exposes meshStatus for a member-facing banner - MeshGate page reuses the integration create+test flow to connect NetBird * Make mesh verification universal (CIDR check, not NetBird-specific) Replace the NetBird-adapter-based "reachable" check with a vendor-agnostic one: the admin supplies the mesh's IP range (CIDR), and verification just confirms this host has an address inside it. Works identically for NetBird, WireGuard, ZeroTier, Tailscale, or any other mesh tech, with no integration record or vendor API call required. * Add reachability fallback for routed meshes (VPC peering, etc.) A host can be on the mesh's "side" of a routed network (e.g. a VPC peered into a NetBird/WireGuard mesh) without holding a local IP in the mesh's own CIDR. Local-IP-in-CIDR stays the primary check; if it fails, the admin can supply a known peer/gateway IP on the mesh and we verify by pinging it instead. Adds iputils to the backend image for the ping binary. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 17:30:46 -04:00
const { user, meshStatus } = useAuth()
const showMeshNotice = !!meshStatus && meshStatus.required && !meshStatus.verified && !meshStatus.overridden && user?.role !== 'admin'
2026-06-18 08:14:00 -04:00
const [sidebarCollapsed, setSidebarCollapsed] = useState(false)
const sidebarWidth = sidebarCollapsed ? 64 : 200
const location = useLocation()
const showHero = location.pathname === '/infrastructure' || location.pathname === '/booknest'
const heroPaddingTop = location.pathname === '/booknest' ? '70px' : '72px'
const heroObjectPosition = location.pathname === '/booknest' ? '54% 8%' : 'center 5%'
const topBarHeight = location.pathname === '/booknest' ? 72 : 56
2026-06-18 08:14:00 -04:00
return (
<div className="min-h-screen w-screen overflow-hidden bg-page">
<Sidebar
collapsed={sidebarCollapsed}
onToggle={() => setSidebarCollapsed(!sidebarCollapsed)}
/>
<main
className="relative h-screen overflow-hidden"
2026-06-18 08:14:00 -04:00
style={{ marginLeft: `${sidebarWidth}px`, width: `calc(100vw - ${sidebarWidth}px)` }}
>
{showHero && (
<div className="pointer-events-none absolute left-0 right-0 top-0" style={{ height: '300px', zIndex: 0 }}>
<img
src="/archnest-hero-banner.png"
alt=""
className="absolute inset-0 h-full w-full"
style={{
objectFit: 'cover',
objectPosition: heroObjectPosition,
maskImage: 'linear-gradient(to bottom, black 0%, black 55%, transparent 100%)',
WebkitMaskImage: 'linear-gradient(to bottom, black 0%, black 55%, transparent 100%)',
}}
/>
<div
className="absolute inset-0"
style={{
background: 'radial-gradient(ellipse 70% 100% at center, transparent 40%, var(--color-page) 100%)',
}}
/>
</div>
)}
<div className="relative" style={{ zIndex: 20 }}>
<TopBar />
</div>
2026-06-18 08:14:00 -04:00
Add mesh prerequisite gate (#33) * Add mesh prerequisite gate (NetBird verification before app config) Implements the design in docs/mesh-prerequisite-gate.md per the user's DECIDE A-D answers: a permanent admin override, B1 (reachable) verification with host mesh IP shown informationally, members allowed in with a notice instead of being blocked, and mesh.required defaulting off so the live production instance is unaffected. - system_config kv table + getConfig/setConfig helpers - /api/system/mesh-status, /mesh/verify, /mesh/override, /mesh/required - AuthContext gains a 'needs-mesh' status (admins only) and exposes meshStatus for a member-facing banner - MeshGate page reuses the integration create+test flow to connect NetBird * Make mesh verification universal (CIDR check, not NetBird-specific) Replace the NetBird-adapter-based "reachable" check with a vendor-agnostic one: the admin supplies the mesh's IP range (CIDR), and verification just confirms this host has an address inside it. Works identically for NetBird, WireGuard, ZeroTier, Tailscale, or any other mesh tech, with no integration record or vendor API call required. * Add reachability fallback for routed meshes (VPC peering, etc.) A host can be on the mesh's "side" of a routed network (e.g. a VPC peered into a NetBird/WireGuard mesh) without holding a local IP in the mesh's own CIDR. Local-IP-in-CIDR stays the primary check; if it fails, the admin can supply a known peer/gateway IP on the mesh and we verify by pinging it instead. Adds iputils to the backend image for the ping binary. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 17:30:46 -04:00
{showMeshNotice && (
<div
className="relative flex items-center justify-center"
style={{
zIndex: 20,
padding: '6px 16px',
backgroundColor: 'rgba(230,126,34,0.1)',
borderBottom: '1px solid rgba(230,126,34,0.2)',
fontSize: '11px',
color: '#E67E22',
}}
>
Mesh setup is still in progress an admin needs to finish verifying the network. Some features may be limited.
</div>
)}
2026-06-18 08:14:00 -04:00
<section
className="relative flex w-full flex-col overflow-hidden"
style={{ height: `calc(100vh - ${topBarHeight}px)`, scrollbarWidth: 'none', padding: showHero ? `${heroPaddingTop} 24px 24px 24px` : '16px 24px 24px 24px', gap: '20px', zIndex: 1 }}
2026-06-18 08:14:00 -04:00
>
<Routes>
<Route path="/" element={<Glance />} />
<Route path="/infrastructure" element={<Infrastructure />} />
<Route path="/booknest" element={<BookNest />} />
Add Phase 1a: core SSH terminal (Termix migration) Implements the minimal-viable terminal described in TERMIX_MIGRATION.md Phase 1a: a real interactive SSH session in the browser over a WebSocket, using xterm.js on the frontend and ssh2 on the backend. Reuses ArchNest's existing SSH integrations (host/port/username/ password/privateKey/passphrase) instead of introducing a second, duplicate host-management system the way Termix has one. Backend: new /api/terminal WebSocket route (registered via @fastify/websocket) handling connect/input/resize/disconnect messages, authenticated via a JWT passed as a query param (browsers can't set custom headers on the WS handshake). Extracted the integration secret loader out of routes/integrations.ts into db/secrets.ts so the new terminal route can reuse it without duplicating the decrypt logic. Frontend: new Terminal.tsx page listing configured SSH hosts and rendering an xterm.js terminal wired to the WebSocket; wired into App.tsx at /terminal. vite.config.ts's dev proxy now forwards WebSocket upgrades (ws: true) so this works under `npm run dev`. Verified end-to-end against a real (test) ssh2-based SSH server: connect, shell banner, keystroke echo, and prompt redraw all worked correctly over the actual WebSocket protocol. Deliberately deferred to Phase 1b/1c per the migration doc: jump-host chaining, tab/split-pane UI, terminal theme/font settings, OPKSSH cert auth, tmux session monitor, session recording. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
2026-06-19 10:52:04 +00:00
<Route path="/terminal" element={<Terminal />} />
<Route path="/tunnels" element={<Tunnels />} />
<Route path="/files" element={<Files />} />
<Route path="/containers" element={<Containers />} />
<Route path="/remote-desktop" element={<RemoteDesktop />} />
<Route path="/host-metrics" element={<HostMetrics />} />
<Route path="/settings" element={<Settings />} />
<Route path="/help" element={<Help />} />
</Routes>
2026-06-18 08:14:00 -04:00
</section>
</main>
</div>
)
}
export default App