Commit Graph

57 Commits

Author SHA1 Message Date
SaelixCode c8a54a988b fix(lint): resolve all ESLint errors to pass CI lint step
- Replace catch (error: any) with catch (error) + (error as Error).message cast
  in EditorLayout, NodeManager, HomeDashboard, AppStoreView
- Define TemplateVolume interface in AppStoreView; replace volumes any[] with typed array
- Define MetricPoint interface in HomeDashboard; replace metrics any[] with MetricPoint[]
- Define TerminalContainer type in BashExecModal; replace as any DOM property casts
- Define NodeTestInfo interface in NodeManager; replace info: any with typed shape
- Fix DockerNetworkStats cast in EditorLayout container stats WebSocket handler
- Remove unused catch variable (e) in api.ts and other components
- Cast streamFilter onValueChange val to union type in GlobalObservabilityView
- Add eslint-disable-next-line react-refresh/only-export-components to badge.tsx,
  button.tsx, AuthContext, NodeContext, use-data-state, use-is-in-view
- Add eslint-disable-next-line react-hooks/set-state-in-effect in LogViewer
- Add /* eslint-disable */ to animate-ui third-party primitive files
2026-03-22 00:27:43 -04:00
SaelixCode ce50db0fde security: pre-release hardening, automated testing, and production readiness
SECURITY (critical fixes):
- Add authMiddleware to /api/system/console-token (was publicly accessible)
- Validate api_url on node create/update to prevent SSRF (rejects localhost/loopback)
- Add rate limiting (5 req/15 min/IP) to /api/auth/login and /api/auth/setup
- Fix path traversal in env_file resolution — absolute/escaping paths rejected
- Add stack name validation to GET routes (was only on PUT/POST)
- Add helmet security headers middleware
- Restrict CORS to FRONTEND_URL in production

PRODUCTION READINESS:
- Add GET /api/health public endpoint + HEALTHCHECK in Dockerfile
- Add SIGTERM/SIGINT graceful shutdown handler (drains connections, closes DB)
- Run container as non-root sencho user in Dockerfile

QUALITY:
- Fix 4 silent empty catch{} blocks in EditorLayout (now show toast.error)
- Connect ErrorBoundary to root App in main.tsx
- Replace WebSocket.Server with named WebSocketServer import (ESM compat)

TESTING (new automated test suite):
- Install Vitest; 38 backend tests across 4 suites covering validation utilities,
  health endpoint, auth middleware, login flows, SSRF protection, and path traversal
- Extract isValidStackName/isValidRemoteUrl/isPathWithinBase to utils/validation.ts
- Playwright E2E scaffolding: auth, stacks, nodes specs + shared login helper
- CI: run Vitest + ESLint on every PR
2026-03-21 21:59:44 -04:00
SaelixCode 94d6c8fc0f fix(ts): use type-only import for Node to satisfy verbatimModuleSyntax 2026-03-21 17:48:31 -04:00
SaelixCode 1690f0d3c6 feat(notifications): aggregate alerts from all nodes in the notification panel
Remote-node alerts now appear in the local notification bell alongside
local ones, each tagged with the originating node name.

- backend: reorder WS upgrade handler so /ws/notifications?nodeId=<remote>
  falls through to the existing proxy path instead of short-circuiting
- frontend/api.ts: add fetchForNode() helper for explicit node-targeted
  requests without touching the localStorage active-node key
- frontend/EditorLayout: fetch notification history from all registered
  nodes in parallel on mount and on node-list changes; open a per-remote
  WebSocket connection for real-time push; route mark-read / delete / clear
  actions back to the originating node; show node-name badge on remote alerts
2026-03-21 17:38:55 -04:00
SaelixCode e190f3ad8a fix(alerts): overhaul alerts & notifications system for local and remote nodes
- StackAlertSheet: fetch agent status on open and show contextual banner
  (amber warning when no channels configured, green when active, blue info
  callout on remote nodes explaining remote-instance evaluation semantics)
- StackAlertSheet: surface actual server error message on addAlert failure
  instead of generic string; add console logging for all failure paths
- SettingsModal: expose Notifications tab for remote nodes so agents can be
  configured directly on any Sencho instance; section header shows node name
  and Remote badge with tooltip when proxying to a remote node
- SettingsModal: re-fetch agents/settings when active node changes
- SettingsModal: add hidden DialogTitle/DialogDescription to satisfy Radix
  UI accessibility requirements (eliminates console errors)
- backend POST /api/alerts: add Zod validation schema; rejects unknown
  metric/operator values, negative thresholds, and missing fields with 400
- EditorLayout WS: upgrade reconnect from flat 5s retry to exponential
  backoff (1s→2s→4s→8s→16s→30s max); onerror now logs the event; cleanup
  only closes OPEN sockets — CONNECTING sockets are closed in onopen after
  isMounted check, eliminating "closed before established" StrictMode error
2026-03-21 01:19:29 -04:00
SaelixCode 22e646286e fix(ui): resolve 9 animated design system bugs including Monaco tab height accumulation
- fix(editor): Monaco compose↔.env tab switching no longer accumulates height
  each switch — root cause was Monaco's position:static internal child creating
  a circular CSS dependency with the CSS grid; broken by calling layout({0,0})
  to force reflow before re-measuring. Added overflow-hidden to Monaco container.
- fix(ui): sliding tab indicator on compose/env tabs via motion.div layoutId
- fix(ui): sliding tab indicator on Notification tabs (Discord/Slack/Webhook)
- fix(ui): switch thumb now animates position (Radix data-attribute CSS translation)
- fix(ui): 'Always Local' tooltip no longer crashes — replaced animate-ui tooltip
  (getStrictContext throws outside provider) with pure Radix primitives + CSS animation
- feat(settings): Auto theme option added (light/dark/auto with matchMedia listener)
- fix(ui): NodeManager dialog buttons no longer stuck together (DialogFooter gap)
- fix(ui): AlertDialog spring animation + Quick Clean button text wraps correctly
- fix(ui): Resources/App Store/Logs buttons toggle off on second click
2026-03-20 23:51:54 -04:00
SaelixCode 0cb5fae947 feat(design): animated design system foundation with animate-ui and motion
Install motion + animate-ui, overhaul design tokens with brand cyan accent,
and replace CSS keyframe animations in Dialog, Tabs, Switch, and Tooltip
with spring-physics and blur-fade transitions via animate-ui Radix primitives.
2026-03-20 22:25:29 -04:00
SaelixCode a5ac3e4981 feat(notifications): replace polling with WebSocket push
Eliminates the 5-second setInterval polling loop for notifications in
EditorLayout. A persistent /ws/notifications WebSocket connection is
opened on mount; the backend pushes each alert the instant dispatchAlert
fires, with 5s auto-reconnect on close.

Key changes:
- NotificationService: injectable broadcaster callback (setBroadcaster)
- DatabaseService.addNotificationHistory: returns full NotificationHistory
  record (with id/is_read) instead of void
- index.ts: notificationSubscribers Set + /ws/notifications upgrade handler
  (JWT-verified, placed before remote proxy path)
- EditorLayout: polling removed, WS connect/reconnect replaces it
2026-03-20 20:47:51 -04:00
SaelixCode 74964b0e26 fix(stats): throttle container stat WebSocket updates via ref buffer
Each container's onmessage handler was calling setContainerStats()
independently, causing React to schedule up to N separate reconciliation
passes per second (one per container). With 20 containers streaming Docker
stats at ~1 update/s, EditorLayout was re-rendering up to 20 times/s.

Fix: incoming stats are written into pendingStatsRef (no re-render cost),
then flushed to React state in one batched setContainerStats call every 1.5s.

Two bugs in the original proposal are addressed:
- rawBytesRef (never cleared) owns rx/tx tracking so net I/O rate is always
  accurate; avoids the stale containerStats closure that would have shown
  0 B/s after every flush cycle
- pending snapshot is captured and cleared BEFORE calling setState so the
  functional updater stays pure (no side-effects inside it)
- pendingStatsRef is cleared in the effect cleanup so stale entries from
  the previous stack don't briefly appear on stack switch
2026-03-20 12:14:09 -04:00
SaelixCode 94018d167f fix(remote): repair stats, bash exec, and Open App for remote nodes
- Generic WebSockets (stats, exec) now connect to /ws?nodeId= instead of
  the bare root so the upgrade handler detects the remote node and proxies
  the WS connection to the correct remote Sencho instance.
- Vite dev proxy gains ws:true on /api and a new /ws entry so WebSocket
  upgrades reach localhost:3000 during npm run dev.
- Backend WS message handler falls back to the default local node when the
  nodeId in a proxied message doesn't exist in the local DB.
- Open App button now extracts the hostname from the remote node's api_url
  instead of using window.location.hostname.
2026-03-19 22:45:00 -04:00
SaelixCode d64d23fc50 feat(system): background image update checker with stack badges
Adds an ImageUpdateService that queries OCI registry manifest endpoints
every 6 hours to compare remote digests against local RepoDigests.
Results are cached in a new stack_update_status SQLite table. A pulsing
blue dot badge appears in the stack sidebar for stacks with updates
available. Manual refresh available via POST /api/image-updates/refresh
with a 10-minute rate limit.
2026-03-19 21:28:48 -04:00
SaelixCode 04c770c198 feat(ui): Phase 57 - remote context UX (Option A) + network layer fixes
Task 1 - Network layer fixes:
- AppStoreView: replace raw fetch() with apiFetch() on /templates and
  /templates/deploy so x-node-id header is injected for proxy routing
- GlobalObservabilityView: replace raw fetch() with apiFetch() on
  /stacks and /logs/global
- HostConsole: append ?nodeId= to WebSocket URL so the upgrade handler
  correctly routes the PTY session to the active remote node

Task 2 - Scoped context two-tier UX (Option A / Portainer-style):
- EditorLayout: add a context pill in the top header bar showing the
  active node name (pulsing blue for remote, green for local)
- HostConsole: header now reads "Host Console - [Node Name]"
- ResourcesView: title now reads "Resources Hub - [Node Name]" for remote
- GlobalObservabilityView: floating node badge in top-left corner for remote
- AppStoreView: deploy sheet shows "Deploying to: [Node Name]" badge
- SettingsModal: remote nodes only see System Limits + Developer tabs;
  global-only tabs (Account, Appearance, Notifications, Nodes) are hidden;
  header subtitle shows remote node name
2026-03-19 20:39:57 -04:00
SaelixCode ee9311ad03 fix: dashboard cards and stacks list do not update on remote node switch
- HomeDashboard: both polling useEffects now depend on activeNode?.id so
  intervals restart and stale local-node data is cleared immediately on
  node switch; added res.ok guard before calling res.json() to prevent
  error objects being written into stats/systemStats state
- EditorLayout: refreshStacks now guards res.ok before parsing the JSON
  body and iterates a typed fileList instead of the raw response value,
  preventing SyntaxError/TypeError when proxy returns a non-JSON response
  for an unreachable remote node
2026-03-19 16:43:07 -04:00
SaelixCode eb0c0263c7 fix: Distributed API UI & metrics polish + DEP0060 suppression
Add Node modal — type selector & state reset:
- Restored a Local/Remote <Select> dropdown in renderFormFields so users can
  explicitly choose the node type instead of it defaulting silently to 'remote'.
- Switching type clears api_url and api_token so no stale remote credentials
  carry over if a user switches from Remote to Local mid-form.
- Replaced the static "Add Remote Node" title with a dynamic one that reflects
  the currently selected type ("Add Local Node" / "Add Remote Node").
- onOpenChange now resets formData to defaultFormData whenever the dialog
  opens, preventing stale values from a previous session leaking in.

Remote connection details — real metrics:
- testRemoteConnection previously returned hard-coded '-' for containers,
  images, and cpus after a successful auth/check ping.
- Now fires three parallel requests (Promise.allSettled) after auth passes:
    /api/stats          → containers total + running count
    /api/system/stats   → cpu.cores
    /api/system/images  → image list length
- Each field falls back to '-' gracefully if an endpoint is unavailable,
  so a slow or older remote instance never breaks the connection test.

DEP0060 util._extend suppression:
- http-proxy@1.18.1 calls util._extend when createProxyServer() is first
  invoked at runtime (NOT at import time). A process.emitWarning override
  placed before the proxy instantiations intercepts only DEP0060 without
  suppressing any other warnings. No package version changes needed.

Also includes linter/formatter normalisation across multiple files.
2026-03-19 16:06:47 -04:00
SaelixCode fddd855624 fix: Distributed API proxy memory leak, node switcher refresh, and copy button
Proxy memory leak (MaxListenersExceededWarning + DEP0060):
createProxyMiddleware was instantiated inside the request handler on every
single API call. Each new instance registered fresh 'close' listeners on the
HTTP server and re-ran the http-proxy util._extend deprecated path. After ~10
requests the MaxListeners threshold was breached. Fix: declare ONE global
remoteNodeProxy at startup using the router option to dynamically resolve the
target URL per request. Listeners are registered once. ECONNREFUSED errors are
caught in the on.error handler and returned as structured 502 JSON.

Node switcher "nothing happens":
EditorLayout had a single useEffect([], []) that called refreshStacks() once
on mount. Changing the active node updated NodeContext state and localStorage
but nothing re-triggered the stack list fetch. Fix: split into two effects —
notifications polling (no dependency) and a stack-refresh effect keyed on
activeNode?.id. When the node changes, stale editor/container/file state is
cleared and the stacks for the new node are fetched.

Copy button silently failing:
navigator.clipboard.writeText() throws DOMException in non-HTTPS / non-localhost
contexts (e.g. http://192.168.x.x). The uncaught async exception silently
swallowed the success toast and state update. Fix: wrapped in try/catch with
an execCommand('copy') textarea fallback and a final error toast if both fail.
2026-03-19 15:34:04 -04:00
SaelixCode 8c51198468 feat: Remote Nodes Wiring & SSH Adapters 2026-03-18 12:55:30 -04:00
SaelixCode 02e1ebe1b6 feat: Remote Nodes Foundation (Strategy B) - Add nodes table with auto-seeded default local node in DatabaseService - Create NodeRegistry service for multi-instance Docker daemon connections - Add 6 Node management API endpoints (CRUD + test connection) - Create NodeManager component with table UI and connection testing - Add NodeContext for frontend-wide active node state management - Add node switcher dropdown to sidebar (visible when >1 node) - Add Nodes tab to Settings Hub 2026-03-18 11:45:04 -04:00
SaelixCode 9af0f85749 fix: refine log level parsing and implement bottom auto-scroll 2026-03-06 23:07:17 -05:00
SaelixCode a4a5365da1 feat: implement centralized logging and historical metrics dashboard 2026-03-06 21:05:23 -05:00
SaelixCode b765403dcf feat: implement real-time container log streaming via SSE 2026-03-06 15:41:06 -05:00
SaelixCode 44acfd7d90 feat: polish app store ui and add advanced deployment configuration 2026-03-05 14:06:31 -05:00
SaelixCode 1676dc22df feat: implement app templates storefront and deployment engine 2026-03-04 14:26:07 -05:00
SaelixCode b3521a078b feat: add Docker API endpoints for managing images, volumes, and networks; implement Resources view in editor 2026-03-04 11:17:16 -05:00
SaelixCode b45553c927 feat: refactor authentication handling and migrate config to database 2026-03-04 09:05:03 -05:00
SaelixCode 386de2c76f refactor: remove redundant pro tip from environment tab in editor 2026-03-03 19:33:04 -05:00
SaelixCode 594bec6d91 feat: update environment file selection logic and enhance editor UI with helpful tips 2026-03-03 19:18:32 -05:00
SaelixCode 13ef241d02 feat: enhance environment file handling by resolving multiple env files and adding selection functionality in the editor 2026-03-03 19:13:28 -05:00
SaelixCode ed1a60bee0 feat: implement dynamic env file resolution and enhance error handling in stack operations 2026-03-03 15:25:05 -05:00
SaelixCode ccf6ed4bc8 refactor: rename isActionLoading to loadingAction for clarity and consistency 2026-03-03 12:47:41 -05:00
SaelixCode 10a483a0a6 feat: await command execution in stack management endpoints and update container stats display based on state 2026-03-03 12:06:19 -05:00
SaelixCode 3161d59abf feat: add save and deploy functionality to EditorLayout component 2026-03-03 11:51:48 -05:00
SaelixCode 2b96b08c04 feat: update stack deployment endpoint and enhance deployment functionality 2026-03-03 11:41:44 -05:00
SaelixCode 07f4d18a22 Add logo to branding header in EditorLayout component 2026-02-27 14:29:09 -05:00
SaelixCode 6bbd9b6484 feat: add notification deletion functionality in API and EditorLayout 2026-02-27 12:47:16 -05:00
SaelixCode f4700ebfc9 feat: enhance API error handling and improve alert message formatting 2026-02-27 12:22:10 -05:00
SaelixCode 91cafb49f9 refactor: update journal mode comment for DatabaseService and optimize CPU state check in EditorLayout 2026-02-27 11:10:35 -05:00
SaelixCode b95838ae9c feat: remove unused alert management code and optimize notification fetching interval 2026-02-27 10:17:33 -05:00
SaelixCode 0899fc0d64 feat: update alert configuration button text from "Configure Alerts" to "Create Alert" 2026-02-27 00:14:36 -05:00
SaelixCode c6eb9d76e7 feat: add Notification Settings Modal and Stack Alert Sheet components
- Implemented NotificationSettingsModal for configuring notification agents (Discord, Slack, Webhook) and global settings.
- Added StackAlertSheet for managing stack-specific alert rules with metrics, thresholds, and cooldowns.
- Introduced reusable UI components: DropdownMenu, Popover, Select, Sheet, Slider, and Switch.
- Enhanced Tabs component by removing unnecessary client directive.
2026-02-26 23:09:47 -05:00
SaelixCode ba7853b2b3 feat: enhance item hover effects in Command component for improved user interaction 2026-02-26 20:07:09 -05:00
SaelixCode fcf0c9f983 feat: add HostConsole component for interactive terminal access and integrate into EditorLayout 2026-02-26 19:52:43 -05:00
SaelixCode 4932d90e4a feat: add HoverCard component and integrate it into EditorLayout for enhanced container status display 2026-02-26 18:41:10 -05:00
SaelixCode 5fe416f941 feat: add frontend index.html and Vite logo; enhance system stats API with network metrics
- Created a new index.html file for the frontend with theme handling and linked assets.
- Added a Vite logo SVG to the public directory.
- Updated the system stats API to include network statistics (rxBytes, txBytes, rxSec, txSec).
- Enhanced the EditorLayout component to display network I/O rates for containers.
- Modified HomeDashboard to show host network statistics with appropriate formatting.
2026-02-26 14:43:45 -05:00
SaelixCode 9b9d4de620 feat: enhance container badge display with tooltip for status details 2026-02-26 11:07:03 -05:00
SaelixCode ba986021cd feat: enhance container status display with tooltip for detailed information 2026-02-26 10:50:13 -05:00
SaelixCode 7ce0409ee3 feat: add xterm search and serialize addons, enhance terminal component with search functionality and download logs feature 2026-02-25 23:38:11 -05:00
SaelixCode 36335ee5ab feat: improved EditorLayout component for managing Docker Compose stacks, editing files, and monitoring containers. 2026-02-25 23:30:24 -05:00
SaelixCode 46e6aedfd0 feat: Improved frontend UI components with shadcn 2026-02-25 22:33:51 -05:00
SaelixCode aeb3d6e812 feat: implement initial frontend application with Monaco editor, Docker Compose stack management, and container monitoring. 2026-02-25 14:18:10 -05:00
SaelixCode ac6b8e2849 feat: Initialize core frontend application, editor layout, and integrate UI components including alert dialogs and toasts. 2026-02-25 10:18:59 -05:00