mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-25 09:46:47 +00:00
7663f4cd8bb3f0cba89dd994098ce0431a89b0e4
293 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7d78c9fe22 |
feat(auth): add TOTP two-factor authentication with backup codes (#615)
* feat(auth): add TOTP two-factor authentication with backup codes Adds RFC 6238 time-based one-time password support to every tier, integrated with the existing password and SSO login paths. Backend: - New MfaService wrapping otplib with a plus or minus 1 step tolerance, base32 secret generation, and hashed single-use backup codes (bcrypt). - user_mfa and mfa_used_tokens tables in DatabaseService. The second table is a DB-backed replay blacklist, purged on a 60s interval. - authMiddleware now recognizes an mfa_pending scope. A token carrying that scope is rejected on every route except the MFA challenge and logout, so no API surface is reachable before the second factor clears. - /api/auth/login issues only a short-lived mfa_pending cookie when the user has MFA enrolled. /api/auth/login/mfa consumes that cookie, verifies the code (or backup code), and swaps in a real session. - /api/auth/mfa/* routes for status, enrol/start, enrol/confirm, disable, backup-code regenerate, and SSO-bypass opt-in. - Admin recovery path: POST /api/users/:id/mfa/reset clears the target's MFA state, bumps token_version, and writes an audit log entry. - CLI emergency fallback: backend/src/cli/resetMfa.ts is wired via `npm run reset-mfa <username>` and also exported for tests. - SSO flows (LDAP and OIDC) gate on user_mfa.sso_enforce_mfa before issuing a session; default behaviour keeps the SSO path frictionless. - Per-user lockout after 5 consecutive failed codes (15 min). Frontend: - AppStatus gains an mfa-challenge branch driven by /api/auth/status. - New MfaChallenge screen, MfaEnrollDialog (QR plus manual secret plus backup codes), MfaDisableDialog, MfaBackupCodesDialog. - Account section shows a Two-factor authentication card with enrol, regenerate, disable, and the SSO-enforce toggle (shown only when SSO providers are configured). - Users section gains a Reset 2FA action for admins. Docs: - New user guide at features/two-factor-authentication.mdx. - New admin guide at operations/two-factor-admin.mdx. - SSO page cross-links to the 2FA doc. * fix(mfa): drop unused TEST_PASSWORD import and stale eslint disable * fix(mfa): simplify e2e openAccountSettings helper to match working pattern * fix(mfa): make e2e suite self-contained and always clean up Test #2 called loginAs() before the MFA challenge step, which waited for the dashboard indicator that never appears once the previous test enrolled the user. That timeout skipped the rest of the serial block, including the disable step, leaving MFA enabled and breaking every later spec. Two fixes: - Tests #2 and #3 now navigate directly to the login page instead of piggybacking on loginAs, which only handles the password-only path. - A new afterAll hook unconditionally disables MFA via the API using two unused backup codes, so the DB is reset even if a test fails midway. * fix(e2e): use backup code for mfa recovery to avoid totp replay race The final recovery step in the backup-code replay test previously generated a fresh TOTP to sign back in. When the timing landed inside the same 30-second window that test #2 consumed, the server's replay blacklist correctly rejected it, producing a ~50% flake rate. Backup codes are single-use and sidestep the replay window, so the recovery becomes deterministic. * fix(e2e): drive mfa disable test through the challenge screen Test #4 called loginAs after test #3 left MFA enabled, but loginAs waits for the dashboard indicator and does not handle the challenge screen, so it timed out. Drive the login manually, satisfy the challenge with a backup code, and use a backup code for the disable step too to avoid any TOTP replay-window race against earlier tests in the serial block. |
||
|
|
6529a24530 |
feat(git-sources): harden create-from-git with LFS + submodule warnings (#609)
* feat(git-sources): surface LFS and submodule warnings on create
Creating a stack from a Git repo now detects two common anomalies and
tells the user about them rather than silently producing broken stacks.
- LFS-pointer compose/env files fail early with a clear error instead
of writing a 130-byte pointer stub to disk as real content.
- Repositories containing .gitmodules produce a non-fatal warning so
the user knows build contexts or volumes inside submodules will be
empty at deploy time.
Also refines the create dialog: sr-only DialogDescription for a11y,
short commit SHA suffix on the success toast, env-path hint under the
"Sync .env" checkbox showing which path will be read, and a route-level
diagnostic log line gated on developer mode for support debugging.
* test(git-sources): cover LFS, submodule, and nested env_path paths
Adds unit coverage for the new LFS-pointer rejection and submodule
warning plumbing, plus a nested compose_path case that exercises the
default env_path resolution ("apps/web/compose.yaml" with sync_env on
and env_path unset writes "apps/web/.env" both to disk and to the DB).
Extends the E2E suite with a happy-path assertion that the full-length
commit SHA is returned in the create response, and a UI flow that
verifies the short-SHA suffix appears in the success toast.
* docs(git-sources): add troubleshooting for LFS, submodules, HTTPS-only
Adds troubleshooting entries for the newly surfaced LFS and submodule
anomalies, expands the clone-timeout entry with the bounded-fetch
explanation, and adds a dedicated HTTPS-only entry. Also consolidates
the known limitations into a single list covering LFS, submodules,
branch-tracking, and HTTPS-only.
* fix(settings): use Route icon for notification routing
The routing section in Settings previously used GitBranch, which now
clashes with the Git Source feature's icon across the editor. Switch
to Route (a branching-flow glyph) so routing rules have a distinct
visual identity and aren't visually conflated with Git-backed stacks.
* fix(git-sources): return 400 for upstream auth failures and disambiguate 404s
Upstream git-host auth failures were mapping to HTTP 401, which the frontend
apiFetch treats as a Sencho session expiry and fires the global logout event.
They now return 400 with code=AUTH_FAILED in the body so the UI can branch on
the discriminator without logging the user out. The status mapping moved into
utils/gitSourceHttp so it can be unit-tested without booting the app.
mapGitError also relied on the HttpError class alone, so any non-2xx response
(including 404) was classified as auth failure. It now inspects the numeric
status on err.data and considers whether a token was supplied, producing more
actionable messages for missing repos, private repos, and wrong-scope tokens.
|
||
|
|
3955267bbe |
feat(git-sources): create a stack from a Git repository (#606)
* refactor(git-sources): extract GitSourceFields from GitSourcePanel Pure extraction of the repo/branch/path/auth/apply-mode form fields into a reusable controlled component so the upcoming Create Stack from Git flow can render the same form in the Create Stack dialog. No behavior change. * feat(git-sources): create a stack from a Git repository Add a From Git tab to the Create Stack dialog so users can name a new stack, point it at a repo + branch + compose path, and have the compose fetched, validated, written to disk, and linked in one shot. Optional deploy-after-create runs the initial bring-up when requested. Backend: new POST /api/stacks/from-git route gated by stack:create. GitSourceService.createStackFromGit() fetches and validates before touching disk, then creates the stack, writes the compose (and .env if sync is enabled), and seeds the git source row with the fetched commit so future pulls produce a clean diff. Runs under the per-stack lock so a concurrent webhook cannot race the create. Deploy failure is non-fatal and surfaced to the caller. Frontend: the existing Create Stack dialog is now tabbed, with Empty keeping the original single-field flow unchanged. * test(git-sources): cover create-from-git endpoint and e2e flow Service tests verify createStackFromGit seeds the last_applied columns on success, writes the env file when sync is enabled, refuses an invalid apply-matrix without fetching, rejects invalid compose without leaving orphan state, and rolls back the on-disk stack dir when a post-create step fails. Route tests cover auth, missing stack_name, invalid stack name, http:// rejection, oversized repo_url, and the 409 collision guard. E2E adds a Create-stack-from-Git block covering tab visibility, client-side HTTPS check, backend .git/config rejection, and a happy-path fetch against a public demo repo (skipped on network failure). * docs(git-sources): document create-stack-from-git tab Add a new section near the top describing the From Git tab in the Create Stack dialog: what it does, the Deploy after create checkbox, and the four failure modes (name collision, unreachable repo, invalid compose, deploy-after-create failure). |
||
|
|
00901cf5bf |
fix(git-sources): harden validation, RBAC, concurrency, and deploy recovery (#603)
* fix(git-sources): harden validation, RBAC, concurrency, and deploy recovery
Tightens the surface area around the Git source feature:
- Enforce HTTPS-only repo URLs server-side (regex was permissive).
- Add stack:read permission check on git-source reads and filter the
list endpoint by callable permission.
- Validate stack names before permission checks on mutation routes so
scoped lookups never see unvalidated input.
- Cap repo_url / branch / compose_path / env_path / token lengths and
require the stack directory to exist before upsert.
- Wrap pull() in the per-stack mutex to eliminate the pull/delete race
that could orphan pending data.
- Block .git/ path components in compose_path / env_path so a
misconfigured clone cannot leak repo metadata.
- Return {applied, deployed, deployError?} on deploy failure instead of
throwing, and surface deployError as a warning toast so the user can
retry deploy without re-pulling.
- Always clean the stack_git_sources row on stack delete even when the
file deletion step fails.
- Add shadow-card-bevel to the pending alert and metadata card per the
design system.
- Handle the new 403 response on the panel fetch gracefully.
- Add diagnostic logging gated on developer_mode (isDebugEnabled) across
fetch / pull / apply / webhook paths with credential scrubbing.
* test(git-sources): expand coverage for hardening and route validation
- New route-level suite covers HTTPS enforcement, required fields,
max-length caps on repo_url / branch / compose_path / env_path /
token, the stack-existence 404 guard, and GET authz.
- Service tests cover the .git metadata guard on compose and env
paths (including nested and substring-containing "git"), pull and
apply rejections when no source is configured or pending is
cleared, the sha-mismatch branch, and the deploy-failure return
shape that now carries deployError.
- E2E adds three server-side contract assertions: PUT against a
missing stack returns 404, http:// is rejected with 400, and
.git/config is rejected as compose_path.
* docs(git-sources): document deploy-failure recovery path
Adds a Troubleshooting entry explaining that when apply succeeds but
the subsequent deploy fails, the compose content is already on disk
and the user can retry deploy from the stack editor without
re-pulling.
* docs(git-sources): add configuration, diff, pending, and webhook screenshots
|
||
|
|
377df7e546 |
feat(git-sources): link stacks to Git repositories with diff-and-apply workflow (#600)
* feat(git-sources): link stacks to Git repositories with diff-and-apply workflow
Add Git Sources so any stack can point at an HTTPS Git repository, branch, and
compose file path. Pulls fetch + validate the incoming commit, store a
diffable pending snapshot, and apply writes only after explicit confirmation
(or automatically, per the configured apply mode). Sibling .env sync is
optional. Works on the Community tier.
Apply modes:
- Review only: mark pending, wait for manual apply in the diff dialog
- Auto-write: write compose + env to disk, do not redeploy
- Auto-deploy: write files and run docker compose up -d
Webhook integration: webhooks can target the new "git-pull" action to trigger
a sync from CI. Per-source debounce prevents runaway pipelines from hammering
the repository host. Tokens are encrypted at rest and never returned to the
frontend.
Docs and tests included. Screenshots and Playwright E2E flows to follow.
* fix(git-sources): drop unnecessary useMemo on commit sha slice
React Compiler's lint rule rejected the manual dependency list because the
inferred dep ('pull') was less specific than the written one ('pull?.commitSha').
The computation is a cheap 7-char slice, so drop the useMemo entirely rather
than fight the rule.
* test(git-sources): add Playwright E2E flows and drop orphan source rows on stack delete
- E2E coverage: non-HTTPS URL rejected client-side, unreachable repo surfaces
a toast error on save, and configure+remove walks the AlertDialog confirm path.
- Deleting a stack now also drops its linked Git source row so a future stack
with the same name starts clean rather than inheriting a stale config.
|
||
|
|
6275adc6b3 |
feat(registries): harden Private Registry Credentials feature (#597)
* feat(registries): add stateless test endpoint, ECR caching, URL and host hardening Adds a POST /api/registries/test endpoint so credentials can be verified before being persisted. Caches ECR authorization tokens in memory until their AWS-reported expiry (minus a safety margin) instead of fetching on every compose invocation. Normalizes registry URLs on save so the stored values match the keys Docker expects in ~/.docker/config.json, fixes a bidirectional host-match bug in getAuthForRegistry that could cross-match overlapping hostnames, and surfaces per-registry decryption failures as warnings in the deploy log stream instead of swallowing them. Also strips the Authorization header on cross-host redirects in the test probe, rejects non-http(s) schemes on save, and validates the shape of returned ECR authorization tokens before use. * refactor(registries): align UI with design system and add in-form test button Swaps the registry type dropdown from shadcn Select to the project's Combobox, applies the canonical card bevel and top-border hover styling to the form container and each registry row, restyles the delete button to the ghost + muted destructive pattern, uses strokeWidth 1.5 on every Lucide icon, and routes all toast errors through the standard defensive chain. Adds a Test connection button inside the form so credentials can be verified before saving. * test(registries): cover RegistryService and deploy warnings surface Adds unit coverage for URL normalization, the encrypt/decrypt round trip through create and resolveDockerConfig, exact-host matching in getAuthForRegistry, resolveDockerConfig warnings on decryption failure, ECR token cache hit/miss and invalidation on update, the stateless testWithCredentials path for 200, 401 with and without a challenge, network errors, and ECR success and failure including malformed tokens. Extends the ComposeService tests to verify that warnings from resolveDockerConfig reach the deploy log stream. * docs(registries): document test-before-save flow and troubleshooting Describes the in-form Test connection button, the two-point testing flow from the registries list, the cached ECR token behavior during deploys, the per-registry warning Sencho emits when a stored secret cannot be decrypted, and adds a Troubleshooting section covering common 401 causes, ECR token handling, warning interpretation, and per-node credential scoping. |
||
|
|
23fc702296 |
fix(network-topology): harden with edge-case fixes, logging, and test coverage (#583)
* fix(network-topology): harden with edge-case fixes, logging, and test coverage Fixes stale data on node switch (topology now refreshes when active node changes), adds manual refresh button to toolbar, and guards against containers with empty Names arrays by falling back to a 12-char short ID. Adds operational and diagnostic logging around topology fetches, and introduces 25 unit tests covering happy paths, system network filtering, stack resolution, container deduplication, edge cases, and error handling. * docs: refresh screenshots |
||
|
|
718c1eb1ea |
fix(host-console): harden with security fixes, validation, and test coverage (#580)
Security: - Enforce RBAC (system:console permission) on WebSocket upgrade - Validate token_version for user sessions on WS connections - Expand env var sanitization to cover additional secret patterns (PRIVATE, AUTH, PASSPHRASE, ENCRYPT, SIGNING) and connection strings (REDIS_URL, MONGO_URI, AMQP_URL, DSN) Reliability: - Add session tracking with max 5 concurrent console sessions - Add WebSocket heartbeat (30s ping, 60s pong timeout) to detect and clean up dead connections and orphaned PTY processes - Differentiate PTY spawn error messages (shell not found, permission denied, generic failure) - Guard against duplicate cleanup when both WS close and PTY exit fire Observability: - Add structured logging with [HostConsole] prefix for session lifecycle (open, close, duration, user, pid) - Add diagnostic logging behind developer_mode for terminal resize events and message parse errors Frontend: - Replace hardcoded hex colors with oklch CSS custom properties (--terminal-bg, --terminal-fg, --terminal-cursor, etc.) - Apply design system material tokens (shadow-card-bevel, recessed well shadow, card border hierarchy, strokeWidth 1.5) Tests: - Add 20 tests covering env sanitization patterns (10 keyword categories + safe vars + case insensitivity), session tracking, and console-token RBAC (admin, viewer, deployer, API tokens) Docs: - Document admin role requirement and session limits - Add troubleshooting section (session limits, shell not found, proxy timeouts, missing console tab) - Update security section with expanded env var coverage |
||
|
|
c4ff58347e |
fix(container-exec): harden with security fixes, validation, and test coverage (#577)
* fix(container-exec): harden with security fixes, validation, and test coverage - Enforce admin role at WebSocket upgrade for container exec sessions - Validate container is running before creating exec - Fix bash-to-sh fallback (move .start() inside try/catch) - Register container-exec as a capability for fleet visibility - Add standard and diagnostic logging for exec lifecycle - Fix false Admiral license claim in API docs - Fix design system violations in BashExecModal (hardcoded colors) - Remove duplicate legacy xterm dependencies - Add 18-test suite covering auth, validation, fallback, and cleanup * fix(container-exec): use correct Duplex type for exec stream The stream variable was typed as NodeJS.ReadWriteStream, which lacks .destroy(). Dockerode's Exec.start() returns stream.Duplex per its type definitions. This caused tsc to fail while Vitest (which skips full type checking) passed. |
||
|
|
0a94df318a |
fix(alerts): harden with security fixes, design compliance, and test coverage (#570)
* fix(alerts): harden with security fixes, design compliance, and test coverage Add authMiddleware to all alert endpoints, validate notification test dispatch inputs, fix restart_count metric via Docker inspect, correct network metric units, replace any types with DockerContainerStats interface, add webhook timeouts and dispatch error tracking. Frontend: migrate Select to Combobox, add ScrollArea and delete confirmation AlertDialog, fix icon strokeWidth to 1.5. Add update availability notifications for both Sencho version updates (6-hour check in MonitorService) and stack image updates (state transition detection in ImageUpdateService). Extract shared version fetch logic into utils/version-check.ts. Add diagnostic logging gated behind developer_mode for MonitorService breach state machine and NotificationService dispatch routing. Tests: 24 new alert API integration tests, restart_count and version check unit tests (688 total passing). Docs updated with HTTPS requirement, update notifications section, and troubleshooting guide. * fix(alerts): remove unused TEST_USERNAME import in alerts-api tests |
||
|
|
e0d1ca9dc0 |
fix(api-tokens): harden with security fixes, design compliance, and test coverage (#567)
Security: add JWT-level expiry ceiling (400d), per-user token count limit (25), and token name uniqueness enforcement. Fix async clipboard copy. Design: migrate Select to Combobox, apply card bevel styling, fix icon strokeWidth, add tabular-nums to timestamps, fix destructive button pattern. Tests: expand from ~20 to 47 test cases covering creation validation, token limits, name uniqueness, last_used_at tracking, ownership constraints, delete edge cases, and registry blocked endpoints. Docs: update API Tokens docs with token limits, name uniqueness, registry restrictions, and JWT expiry ceiling. Update OpenAPI spec with 409 response. |
||
|
|
1d89e8ce59 |
fix(sso): harden SSO with role sync, security fixes, design compliance, and test coverage (#564)
Rate-limit OIDC callback route and clear state cookie on all paths. Validate GitHub API responses and add server-side config validation. Sync SSO user roles on every login respecting seat limits. Add LDAP connection timeout, bind DN warning, and multiple entry logging. Replace Select with Combobox and apply card design tokens in SSOSection. Expose OIDC scopes field in settings UI. Fix hardcoded colors to use design system tokens. Add standard and diagnostic logging throughout SSO flow. Add tests for role sync, seat limits, LDAP escaping, and config validation. Remove unused login-form.tsx template. Update docs and screenshots for SSO feature. |
||
|
|
6daa7a135d |
fix(audit): harden audit log with summary fixes, design compliance, and test coverage (#561)
* fix(audit): harden audit log with summary fixes, design compliance, and test coverage Replace stale /compose/* route summaries with current /stacks/* patterns, add ~30 missing route summaries for container ops, labels, fleet updates, SSO, templates, and auto-update. Fix X-Forwarded-For extracting full proxy chain instead of first client IP. Pre-sort pattern table at module load for O(1) lookup instead of sorting per request. Add Calendar and DatePicker UI components (react-day-picker v9 + date-fns) replacing browser-native date inputs. Align AuditLogView with design system: Combobox instead of Select, design token colors, strokeWidth 1.5, card bevel, tabular-nums on all numeric cells. Add diagnostic logging gated behind developer mode for audit middleware, query, and export endpoints. Extract getAuditSummary to testable utility. Add 32 tests covering summary resolution, DB round-trips, API permissions, export formats, and middleware integration. * fix(audit): use correct Chevron prop types for react-day-picker compatibility |
||
|
|
c261fbc327 |
fix(rbac): harden user management with token versioning, session invalidation, and test coverage (#558)
Security fixes:
- Map deploy-only API tokens to deployer role (not admin)
- Add token versioning to invalidate sessions on password/role changes
- Reject deleted users immediately in auth middleware (no 24h JWT grace)
- Use DB role instead of JWT role so changes take effect instantly
- Block password setting on SSO-provisioned users
- Check proxy variant in scoped permission resolver
Cleanup and logging:
- Remove orphaned role assignments when stacks/nodes are deleted
- Add standard logging for login, user CRUD, role assignments, password changes
- Add diagnostic logging gated behind developer_mode setting
- Extract issueSessionCookie helper (DRY across 5 JWT signing sites)
Frontend:
- Replace Select with Combobox in UsersSection (design system compliance)
- Add strokeWidth={1.5} to Lucide icons
- Hide password fields for SSO-provisioned users
Testing:
- Add 42-test RBAC suite covering user CRUD, token versioning, scoped
assignments, permissions endpoint, password management, seat limits,
last-admin protection, and orphan cleanup
|
||
|
|
809bf76c20 |
fix(fleet): harden fleet snapshots with DRY capture, audit fixes, and design compliance (#555)
Extract duplicated snapshot capture functions from index.ts and SchedulerService.ts into a shared module (snapshot-capture.ts). Fix audit log route patterns that used singular 'snapshot' instead of plural 'snapshots'. Apply design system to FleetSnapshots component (card styling, strokeWidth, font-mono, tabular-nums, ScrollArea). Add safePage pagination, loading toast for creation, and fix the restore dialog race condition with a controlled AlertDialog. Add diagnostic logging gated behind Developer Mode. Add tests for restore endpoint, admin role enforcement, and edge cases. Update docs with troubleshooting section and refresh screenshots. |
||
|
|
a695251f38 |
fix(labels): harden stack labels with nodeId filtering, concurrency guard, and test coverage (#552)
* fix(labels): add nodeId filter, existence check, stale cleanup, concurrency guard, and body validation - Fix getStacksForLabel to filter by node_id (prevents cross-node data leak) - Add getLabel(id, nodeId) for single-label existence check - Add getLabelCount(nodeId) for enforcing per-node label limit (50) - Add cleanupStaleAssignments to remove orphaned assignments for deleted stacks - Add label/assignment cleanup to deleteNode transaction - Add label existence check on bulk action endpoint (returns 404 for missing labels) - Add concurrency guard on bulk actions (returns 429 if already in-flight) - Add requireBody guard on all mutation endpoints - Extract isSqliteUniqueViolation helper to deduplicate constraint checks - Add MAX_LABELS_PER_NODE constant (50) with limit enforcement on create - Add diagnostic logging on all label endpoints (gated behind developer_mode) - Add operational log line for bulk action results * fix(labels): use ScrollArea, show failure details, add loading feedback, deduplicate constants - Replace overflow-y-auto div with ScrollArea in LabelAssignPopover (design system) - Show failed stack names in bulk action error toast - Add loading toast for context menu label toggle - Disable bulk action menu items while a bulk action is running - Disable "New Label" button at 50-label limit with "Limit reached" text - Export LABEL_COLORS and MAX_LABELS_PER_NODE from LabelPill (single source of truth) - Import shared constants in LabelAssignPopover and LabelsSection (remove duplicates) - Add BulkActionResult interface to replace inline type assertion * test(labels): add comprehensive coverage for label CRUD, assignments, and bulk edge cases 42 tests covering: - getLabels: empty, ordered, node isolation - getLabel: found, wrong node, nonexistent - createLabel: returns with ID, duplicate name constraint - getLabelCount: correct count, zero for empty node - updateLabel: name, color, both, not found, wrong node - deleteLabel: removes label, cascades assignments, wrong node no-op - setStackLabels: assign, replace, clear, invalid ID throws - getLabelsForStacks: correct mapping, empty result - getStacksForLabel: correct results, node filter, empty for nonexistent - cleanupStaleAssignments: removes stale, preserves valid, handles empty - deleteNode: cascades labels and assignments - Edge cases: atomicity, cascade across stacks, multi-label assignment * docs(labels): document 50-label limit and bulk action failure details * fix(labels): add missing LabelColor type imports and explicit parameter types * refactor(labels): extract label types and constants to label-types.ts Moves LabelColor, Label, LABEL_COLORS, and MAX_LABELS_PER_NODE out of LabelPill.tsx into a dedicated non-component file. This fixes the react-refresh/only-export-components lint error caused by mixing constant exports with component exports. |
||
|
|
44e8fdfba9 |
fix(scheduler): harden scheduled operations with stale cleanup, cron validation, and design fixes (#549)
* fix(scheduler): clean up stale runs on startup and auto-disable invalid cron tasks
- Add markStaleRunsAsFailed() bulk DB method with status index
- Clean up orphaned 'running' records on scheduler startup
- Auto-disable tasks when cron expression becomes invalid at execution time
- Promote CRUD debug logs to standard logs for scheduled task admin actions
- Add diagnostic logging for task pre-checks, action timing, and prune fallback
* refactor(scheduling): extract shared types and fix design system violations
- Extract ScheduledTask, TaskRun, NodeOption to shared types file
- Extract getCronDescription and formatTimestamp to shared utilities
- Fix formatTimestamp falsy-zero null check
- Tighten last_status type to 'success' | 'failure' | null
- Add strokeWidth={1.5} to all action icons per design system
- Add sr-only DialogDescription for accessibility
- Wrap Sheet run history in ScrollArea
- Fix delete button styling to match design system pattern
- Change manual trigger toast from "executed" to "triggered"
* test(scheduler): add tests for snapshot, remote update, stale cleanup, and cron invalidation
- Add stale run cleanup tests (bulk markStaleRunsAsFailed, logging)
- Add cron invalidation test (auto-disable, error message)
- Add executeSnapshot tests (fleet capture, empty stacks)
- Add executeUpdateRemote tests (proxy success, remote error)
- Document stale run cleanup and cron auto-disable in troubleshooting docs
|
||
|
|
a17b16b258 |
fix(scheduler): harden auto-update policies with cascade deletes, error reporting, and UI fixes (#545)
* fix(scheduler): harden auto-update policies with cascade deletes, error reporting, and UI fixes
- Fix orphaned task runs on policy/node deletion with transaction-wrapped cascade deletes
- Make manual trigger non-blocking (202 Accepted) to prevent proxy timeouts
- Distinguish registry check failures from clean "no update" results via structured ImageCheckResult
- Trim whitespace-only policy names in both frontend and backend validation
- Add strokeWidth={1.5} to action icons per design system
- Add sr-only DialogDescription for Radix accessibility
- Replace Select with Combobox for frequency picker
- Wrap run history sheet content in ScrollArea
- Support concurrent Run Now indicators via Set-based state
- Abort stale stack fetches on node switch with AbortController
- Add standard and diagnostic logging to SchedulerService and ImageUpdateService
- Add tests for cascade deletes, image checking, and scheduler edge cases
- Add troubleshooting section to auto-update docs
* fix(tests): resolve lint errors in image-update-service tests
Remove unused mock variables (mockGetImage, mockGetDocker) and unused
ImageCheckResult type import. Replace CommonJS require('yaml') with
ESM import to satisfy no-require-imports rule.
* chore(deps): bump Docker CLI to 29.4.0 and Compose to v5.1.2
Resolves Trivy CVE-2026-32282 (Go stdlib symlink follow in Root.Chmod)
by upgrading to releases that ship Go 1.25.9. Compose v5.1.2 also bumps
grpc to 1.80.0, resolving CVE-2026-33186.
* chore(security): accept CVE-2026-32282 in .trivyignore, update stale refs
Go stdlib symlink-following in Root.Chmod (CVE-2026-32282) affects both
Docker CLI 29.4.0 (Go 1.26.1) and Compose v5.1.2 (Go 1.25.8). Fix
requires Go 1.25.9 or 1.26.2; no upstream static binary ships a patched
runtime yet. The vulnerable code path requires a chroot context with
attacker-controlled filesystem, which does not apply to our usage.
Also updates version references from v5.1.1/v29.3.1 to v5.1.2/v29.4.0
for existing CVE entries, and notes that Compose v5.1.2 resolved
CVE-2026-33186 (grpc bumped to 1.80.0) for the compose binary.
|
||
|
|
d23c6779af |
fix(fleet): harden remote node updates with admin enforcement, expiry fix, and diagnostics (#542)
- Fix completed-entry auto-expiry using resolvedAt instead of startedAt - Add admin role enforcement to update trigger and update-all endpoints - Add missing error field in rejected-promise fallback for update-status - Fix rejected promises in update-all losing node names - Harden frontend recheck button with try/catch/finally error handling - Align frontend isValidVersion with stricter regex validation - Add diagnostic logging gated behind developer_mode - Extract resolveTracker helper to centralize terminal state transitions - Add 15 new fleet test cases covering auth, tier gating, input validation, and admin roles - Document admin requirement and troubleshooting in fleet-view docs |
||
|
|
a74a516850 |
fix(logs): harden global logs with shared parsing, SSE fixes, and level filter (#539)
- Extract log parsing utilities (normalizeContainerName, parseLogTimestamp,
detectLogLevel, stripControlChars, demuxDockerLog) to shared module,
eliminating duplication between polling and SSE endpoints
- Fix timestamp regex to accept timezone offsets (+HH:MM/-HH:MM), not just Z
- Replace any[] with typed GlobalLogEntry interface
- Add SSE heartbeat (30s) to prevent reverse proxy timeouts
- Add X-Accel-Buffering: no header for nginx SSE compatibility
- Replace swallowed catch blocks with console.warn diagnostics
- Fix SSE not reconnecting on node switch (missing dep in effect array)
- Add settings change event so devMode/pollRate updates apply without remount
- Add log level filter (ALL/ERROR/WARN/INFO) to toolbar
- Replace overflow-auto div with ScrollArea for design system compliance
- Add strokeWidth={1.5} to all toolbar icons
- Include stack name in download format
- Surface fetch errors with inline banner
- Add 39 unit tests for all log parsing utilities
- Update docs with level filter documentation and refreshed screenshot
|
||
|
|
1702dabb7a |
fix(fleet): add auth middleware, input validation, and design system compliance (#536)
Add authMiddleware to all 13 fleet endpoints that were previously accessible without authentication. Add NaN validation for parseInt params, stackName validation on snapshot restore, and description length cap on snapshot creation. Clean up updateTracker entries on node deletion to prevent memory leaks. Replace hardcoded colors with design system tokens, swap Select for Combobox, replace overflow-y-auto with ScrollArea, fix card styling (shadow-card-bevel, border tokens). Fix stale container data by always refetching on stack expand with a loading guard against concurrent requests. Add operational logging for state-changing fleet operations and diagnostic logging gated behind Developer Mode. Add 20 fleet tests covering auth enforcement, input validation, tier gating, and snapshot CRUD lifecycle. |
||
|
|
cd3d7b23be |
feat(app-store): add port conflict indicator to deploy sheet (#533)
Show a pulsating warning dot next to host ports that are already in use by a running container. Hovering over the dot reveals which Sencho-managed stack or external app occupies the port. Adds GET /api/ports/in-use endpoint that returns a map of bound host ports with ownership info, and a getPortsInUse method on DockerController that reuses the existing container-to-stack resolution logic. |
||
|
|
4909c35e50 |
fix(resources): harden Resource Explorer with auth, validation, design, and UX fixes (#527)
- Sanitize error messages in all delete/prune/create/inspect endpoints to prevent Docker internals from leaking to the frontend - Add CIDR, IPv4, and Docker resource ID input validation - Add requirePaid gate to network topology endpoint - Add invalidateNodeCaches after image/volume/network mutations - Fix design system violations: card borders, destructive button variant, visible DialogDescription, overflow-auto replaced with ScrollArea, hardcoded Tailwind colors replaced with tokens - Gate purge button behind isAdmin to prevent silent 403s - Fix shared inspect loading state to be per-network-row - Parse error response bodies for meaningful toast messages - Add clipboard API fallback for non-HTTPS contexts - Render Options section in network inspect sheet - Add operational and diagnostic logging for resource operations - Extend validation and DockerController test suites - Update docs with Options field in network inspect |
||
|
|
d4882d32d9 |
fix(app-store): harden App Store with auth, validation, bug fixes, and design compliance (#523)
* fix(app-store): harden App Store with auth, validation, bug fixes, and design compliance Add authMiddleware to GET /api/templates and POST /api/templates/deploy endpoints. Add isValidStackName and isPathWithinBase checks to the deploy endpoint. Replace fs.existsSync with async fsPromises.access. Extract FileSystemService to local variable to avoid repeated getInstance calls. Fix template mutation bug where PUID/PGID/TZ duplicated on re-open by working on a copy instead of mutating state. Make env_file conditional in generated compose YAML (only when env vars exist). Add port validation (range 1-65535) with visual feedback and deploy blocking. Add env key collision warning toast for custom variables. Replace any types with proper LSIO API interfaces. Change catch types from any to unknown with getErrorMessage. Add structured logging with [Templates] prefix and diagnostic logging gated behind Developer Mode. Align with design system: remove hardcoded bg-white and text-red-500, use ScrollArea, fix destructive button variant, add tabular-nums and strokeWidth 1.5, use cn() for conditional classes. Add empty-registry state distinct from no-search-results. Add 16 unit tests for TemplateService covering compose generation, conditional env_file, env string generation, and cache clearing. Update App Store docs with port validation and env collision details. * refactor(app-store): remove unused interface exports Remove export keyword from interfaces that are only used within their own file: TemplateEnv, TemplateVolume, and TemplatesResponse in TemplateService.ts; TemplateEnv and Template in AppStoreView.tsx. No external consumers import these types. * fix(app-store): remove unused fs default import The fs.existsSync call was replaced with fsPromises.access in the deploy endpoint, leaving the fs default import unused. Remove it to fix the ESLint no-unused-vars error in CI. |
||
|
|
2465f7607e |
fix(stacks): harden stack management with security, validation, and logging (#520)
* fix(stacks): harden stack management with security fixes, validation alignment, and logging Validate WebSocket stack names with isValidStackName() to close a path-traversal gap on the /api/stacks/:stackName/logs WS endpoint. Align POST /api/stacks to use the canonical validator (allows underscores). Replace error: any catch blocks with error: unknown + type narrowing. Add cache invalidation to PUT /api/stacks/:stackName/env. Rename DELETE param from :name to :stackName for consistency. Add standard [Stacks] lifecycle logs and diagnostic [Stacks:debug] logs gated behind the Developer Mode toggle (with 5s TTL cache). Extract shared isDebugEnabled() and getErrorMessage() utilities. Frontend: roll back optimistic status on API failure, guard unsaved changes when switching stacks, pre-check duplicate names in App Store. * docs(settings): update Developer Mode description to mention debug diagnostics |
||
|
|
9db97107aa |
fix(dashboard): harden real-time dashboard with bug fixes and design compliance (#517)
* fix(dashboard): harden real-time dashboard with bug fixes and design compliance - Fix host alert spam: add 5-minute cooldown for CPU/RAM/disk threshold alerts, preventing duplicate notifications every 30s during sustained breaches. Extract shared dispatchWithCooldown helper (also used by Docker janitor alerts). - Fix memory metric inflation: subtract filesystem cache from stored memory_mb values, matching the existing calculateMemoryPercent logic. - Fix crash detection reliability: replace fragile 'seconds ago' string matching with a tracked Set of alerted container IDs. Containers are only alerted once per crash event, with automatic cleanup when they start running again or after a 1-hour TTL. - Fix health status bar: exited containers now trigger 'degraded' state independently of unread error notifications. - Fix CPU chart Y-axis: auto-scale when aggregate container CPU exceeds 100% instead of silently clipping at the hardcoded domain ceiling. - Fix grammar: 'actives' to 'active' in container count label. - Add shadow-card-bevel to all dashboard cards per design system. - Update dashboard docs to reflect revised health status thresholds. * test(dashboard): update monitor service tests for new alert signatures - Add container Id fields to crash detection test fixtures - Update host alert assertions to match dispatchWithCooldown 3-arg call - Fix unhealthy container test to use State: 'unhealthy' instead of State: 'running' (running containers are now skipped in crash detect) |
||
|
|
4950cd0bd0 |
fix(updates): scan all filesystem stacks for image updates (#514)
ImageUpdateService previously discovered stacks by iterating Docker containers, which meant stacks without containers (e.g. after docker compose down) were silently excluded from update checks. Switch to a hybrid discovery approach: enumerate stacks from the filesystem via FileSystemService.getStacks(), parse compose files for image refs with .env variable resolution, then augment with container-based image discovery for running stacks. Also cleans up stale stack_update_status entries when stacks are deleted or no longer exist on disk, and replaces the plain update-available tooltip with an animated cursor follow pattern. |
||
|
|
622c1f9262 |
feat: home dashboard and Settings Hub polish (#506)
* feat(dashboard): drop CPU column and relative timestamp from Stack Health and status bar The Stack Health table's CPU column duplicated data already surfaced in the top ResourceGauges and the CPU Usage historical chart. The health status bar's 'just now' timestamp was cosmetic: no consumer relied on lastUpdated state for polling, staleness detection, or conditional rendering. Removing both tightens the dashboard and eliminates a dead prop chain through useDashboardData. * refactor: remove dead admin_email field from setup flow The Setup form captured an admin email under 'Used for license recovery. Never shared with third parties.' but the value was written to global_settings and read nowhere: no license recovery, SMTP, or support contact flow consumed it. Rather than building UI on top of the dead field, delete the input, the payload key, and the backend persistence. Any orphaned row from prior setups is harmless and the frontend ignores unknown settings keys. * feat(settings): use Radix ScrollArea with per-section scroll memory Settings Hub used a native-scroll div that snapped to the top every time the user switched subsections and exposed the default browser scrollbar. Wrap the nav and content panes with the shadcn ScrollArea (Radix under the hood, type='hover') and expose a viewportRef so the modal can stash each section's scrollTop in a ref and restore it via useLayoutEffect on switch. Style the thumb with translucent foreground tokens so it reads as glass against popovers and dialogs. Replaces a hand-rolled scroll hook and ad-hoc CSS utility. |
||
|
|
3d69746eee |
fix(fleet): make local self-update flow reliable end-to-end (#472)
The "Updating Sencho..." overlay used to dismiss prematurely while the image pull was still running, after which the local node card would get stuck in "updating" and eventually surface a generic "Timed Out" error while the container remained on the old version. Three root causes are addressed: 1. The image pull was synchronous (`execFileSync`), which blocked the Node event loop. The overlay's health probe saw the server come back the moment the pull finished and reloaded the page, even though the container had not restarted yet. The pull is now async via `promisify(execFile)`, so /api/health and /api/fleet/update-status keep serving throughout. 2. The overlay reloaded on the first 200 from /api/health regardless of whether the underlying process had actually restarted. /api/health now exposes the gateway boot timestamp, and the overlay captures it pre-update and only reloads when it observes a different value. A wasOffline-then-online fallback handles the case where the pre-update fetch failed. 3. Helper container spawn errors from `docker run` were silently discarded, so a failed compose recreate never surfaced anywhere. Errors are now captured into `lastUpdateError` via the execFile callback and surfaced through the existing /api/fleet/update-status error path. A 3-minute early-fail heuristic on the local node block surfaces a clear failure message when the helper fails silently, instead of waiting the full 5-minute timeout for an unknown failure. |
||
|
|
8de82ed81f |
fix(fleet): prevent modal flash when clicking Recheck button (#457)
Use a separate recheckingUpdates state for the Recheck button so the modal content stays visible while refreshing. The full-screen loading spinner (checkingUpdates) is now only used for the initial "Check Updates" open from the header. |
||
|
|
368bef20d3 |
fix(fleet): detect updates via GitHub Releases instead of gateway self-comparison (#454)
* fix(fleet): detect updates via GitHub Releases instead of gateway self-comparison The fleet update check compared each node's version against the gateway's own version, so the local node could never appear outdated. Now fetches the actual latest release from GitHub Releases API with a 30-minute in-memory cache and thundering-herd protection. The Recheck button invalidates this cache via ?recheck=true to force a fresh lookup. * docs(fleet): update docs to reflect GitHub Releases version detection Replace "Gateway version" references with "Latest version" to match the new label. Document that version comparison uses the latest GitHub release rather than the gateway's own version, and that Recheck refreshes the cached latest version. |
||
|
|
089d43b855 |
fix(ui): standardize toast background to match floating overlay glass style (#451)
Replace bg-card/80 with bg-popover/95 and match the backdrop-blur/saturate values used by dialogs, dropdowns, and popovers. Remove per-type colored gradient overlays so color differentiation comes from icons only. Unify the determinate progress bar gradient with the loading bar's brand gradient. |
||
|
|
3ee4fe6e44 |
feat(topology): overhaul network topology with dagre layout, enriched nodes, and click-to-logs (#447)
Replace N+1 Docker API calls (inspectNetwork per network) with a container-centric approach that fetches all networks and containers in 2 parallel calls, then maps relationships in memory. Add dagre auto-layout algorithm for hierarchical DAG visualization, replacing the static two-row layout that caused edge spaghetti at scale. Add "Show system networks" toggle, enrich container nodes with running state indicators, stack badges, and base image names. Clicking a running container opens its log viewer directly from the topology graph. |
||
|
|
8e0857e7f6 |
feat(contact): add official contact emails throughout app and docs (#428)
Surface six dedicated email channels (support, contact, licensing, security, privacy, conduct) across contextually appropriate locations: - security.txt: create .well-known/security.txt for app (RFC 9116) - SECURITY.md: add security@sencho.io as alternative to GitHub reporting - PaidGate/AdmiralGate: add licensing@sencho.io to upgrade prompts - docs: new Contact & Support reference page listing all channels - CHANGELOG: document additions under [Unreleased] Website changes (separate repo): privacy@sencho.io in Privacy/Terms pages, licensing@sencho.io in Refund page and pricing footer, contact@sencho.io in website footer. |
||
|
|
be7eda85f1 |
fix(billing): hide billing portal for lifetime licenses (#427)
Lifetime licenses have no recurring subscription, so the Lemon Squeezy
customer portal cannot generate a URL. The Manage Subscription button in
Settings already had the isLifetime guard, but the Billing button in the
profile dropdown did not, causing a confusing "No billing portal
available" error.
- Add !license.isLifetime guard to UserProfileDropdown (matches
LicenseSection pattern)
- Move lifetime detection into getBillingPortalUrl() so the service owns
all billing eligibility logic
- Change return type to { url } | { error } discriminated union for
clear error propagation
|
||
|
|
f6d2199978 |
feat(resources): add loading toast for prune, delete, and purge operations (#426)
Show a loading notification with spinner and indeterminate progress bar while Resource Hub operations are in progress, replacing the dead moment between confirmation and result. |
||
|
|
662bc1a210 |
fix(resources): unify container/resource classification with multi-fallback resolution (#425)
* fix(sidebar): add service name and config_files fallbacks for container-to-stack matching Containers that predate Sencho's reorganization of compose files into subdirectories carry stale Docker labels where the project name is set to the COMPOSE_DIR basename (e.g. "compose") rather than the stack directory name. The existing project name map and working_dir fallbacks from PR #416 did not cover this case. Added two new fallback strategies to getBulkStackStatuses: - Match by com.docker.compose.service label against known stack names - Extract stack name from com.docker.compose.project.config_files path Also reused the existing isPathWithinBase utility for path containment checks and hoisted path.resolve(COMPOSE_DIR) out of the per-container loop. * fix(resources): unify container/resource classification with multi-fallback resolution Extract shared helpers (resolveContainerStack, resolveProjectLabel, buildAbsDirMap) and apply them consistently across getClassifiedResources, pruneManagedOnly, getDiskUsageClassified, and getBulkStackStatuses. Fixes incorrect "External" tagging in Resources Hub for images, volumes, and networks belonging to stacks that predate Sencho's compose file reorganization. Also fixes the "active" plural on the dashboard Containers card. |
||
|
|
a6849aedab |
feat(dashboard): add node badge to Recent Alerts for remote node alerts (#423)
Alerts from remote nodes now display a node name badge in the Recent Alerts card, matching the existing badge style in the notification panel. This helps users identify which node generated each alert. |
||
|
|
455bfa8734 |
fix(schedules): filter auto-update policies from Scheduled Operations view (#420)
* fix(schedules): filter auto-update policies from Scheduled Operations view Auto-update policies (action=update) were appearing in both the Auto-Update tab and the Scheduled Operations tab. Added server-side action/exclude_action query params to GET /api/scheduled-tasks. ScheduledOperationsView now requests ?exclude_action=update and AutoUpdatePoliciesView requests ?action=update, so each view only shows its relevant tasks. * fix(schedules): use typeof guard for query param type safety Express can parse ?action[]=x as an array. Use typeof === 'string' guard instead of unsafe type assertion. |
||
|
|
8920d8c55a |
feat(nav): add pulsing animation to local node status dot (#418)
* feat(nav): add pulsing animation to local node status dot Apply the same animate-ping pattern used by the notification bell badge to the local node context pill in the top header bar. The green dot now pulses to give a stronger "live connection" signal. * fix(nav): use animate-pulse instead of animate-ping for node status dot Switch the local node pill dot from the ping pattern (expanding ring) to animate-pulse for consistency with the remote node pill. Only the notification bell badge should use animate-ping. |
||
|
|
5b06992e05 |
fix(nav): remove toggle behavior on navigation tabs (#417)
Clicking an already-active navigation tab previously toggled back to the dashboard/editor view. This was a holdover from the old UI where the tab buttons were different. Under the current design it causes confusion, so clicking the active tab now does nothing. |
||
|
|
cc2da99d6f |
fix(fleet): resolve stuck update states and improve detection (#405)
* fix(fleet): resolve stuck update states and improve update UX The fleet node update flow had several bugs: the in-memory update tracker never cleared terminal states (timeout, failed, completed), leaving nodes permanently stuck with no way to retry or dismiss. The Recheck button only re-fetched stale state without clearing it, and the POST trigger rejected retries with 409 even after timeout. Backend fixes: - Add DELETE endpoints (single node + batch) to clear tracker entries - Fix 409 race: detect expired timeouts and clear terminal states before re-triggering - Populate error messages in the tracker for timeouts and failures - Include error field in the update-status API response - Auto-expire completed entries after 60 seconds Frontend fixes: - Add retry (RotateCcw) and dismiss (X) buttons on failed/timed-out badges - Show error details via animated cursor hover (CursorFollow pattern) - Recheck button now batch-clears all terminal states before fetching - Recheck shows loading spinner and disables while checking - Extract NodeCardProps interface for readability * fix(fleet): detect update completion via process start time Remote nodes that cannot report their version (e.g. older builds) caused updates to always time out because completion detection relied solely on version comparison. The gateway now tracks the remote node's process start time from /api/meta and detects container restarts by comparing it across polls. Also extracts a createTracker() factory to eliminate repeated object construction across 5 call sites. * docs: add troubleshooting for first-update timeout on old nodes Adds a new troubleshooting entry explaining why the first remote update on nodes running pre-v0.40.0 always times out (neither version nor process start time can be detected). Documents the fix: dismiss, recheck, and confirm the node updated. Also adds a screenshot of the timed-out state with retry/dismiss buttons to the remote updates feature page. * fix(fleet): detect update completion via offline detection and error reporting The update completion detection relied on version change and process start time, both of which fail on nodes running older Sencho versions that report "unknown" and lack the startedAt field. This caused every update to time out after 5 minutes. Add three-signal detection: version change, process restart (startedAt), and offline/online detection (node went unreachable during update and came back). Also add a 90-second early failure heuristic for when the remote image pull fails silently, and surface pull errors from SelfUpdateService via /api/meta so the gateway can report them immediately. * fix(deps): bump vite to 8.0.5 to resolve high severity vulnerabilities Fixes GHSA-4w7w-66w2-5vf9, GHSA-v2wj-q39q-566r, GHSA-p9ff-h696-f583. * fix(deps): bump vite in backend lockfile to resolve audit failures Vitest pulls in vite as a transitive dependency. Bumps to 8.0.5. |
||
|
|
a55d1245f8 |
fix(fleet): resolve version detection pipeline for Docker builds (#402)
* fix(fleet): resolve version detection pipeline for Docker builds The Dockerfile backend-builder stage was missing a COPY of the root package.json, causing generate-version.js to fall back to "0.0.0-dev" at build time. At runtime, the filesystem walk also failed (root package.json not in the final image), producing the string "unknown" which the frontend rendered as "vunknown". Changes: - Dockerfile: copy root package.json into backend-builder stage - CapabilityRegistry: return null (not "unknown") for unresolvable versions; add isValidVersion() type guard; normalize remote meta responses to strip "unknown"/"0.0.0-dev" sentinel values - Fleet endpoints: hoist gateway version validation outside per-node loops; treat unresolvable remote versions as "potentially outdated" instead of silently marking them up to date - FleetView: guard all version display points (card badge, update button, gateway label, modal columns) via shared formatVersion() - EditorLayout, CapabilityGate: use shared isValidVersion utility - New frontend/src/lib/version.ts shared utility - Docs: add troubleshooting section for version display edge cases - Screenshots: updated Fleet Overview and Node Updates modal * docs: update fleet node updates screenshot with live remote node |
||
|
|
2089e75ef1 |
fix(fleet): filter invalid version strings from UI display (#399)
Treat 'unknown' and '0.0.0-dev' as absent versions in the node switcher, fleet overview, and capability gate. Remote nodes running older Docker images that return these fallback values now show a clean label instead of "vunknown". |
||
|
|
9e0c9d3f2d |
fix(licensing): backward-compatible tier/variant enforcement and self-healing variant detection (#385)
Accept legacy tier ('pro') and variant ('personal', 'team') names from older
remote nodes, normalizing them to current values ('paid', 'skipper', 'admiral')
in authMiddleware. This fixes distributed license enforcement failing between
v0.38.3 and v0.38.0 nodes due to the tier rename in v0.38.1.
Also fixes:
- Self-healing getVariant() that cross-checks stored variant_type against
product/variant name metadata on every call, correcting stale cached values
from previous buggy resolution logic
- Unguarded API responses in ResourcesView causing potential t.map crashes
- Fleet update status now polls on a 120s interval (was only fetched on mount)
- fetchRemoteMeta failures now logged for diagnosability
|
||
|
|
797623e56f |
fix(licensing): rename variant values to skipper/admiral and store resolved type (#379)
Rename internal variant values from 'personal'/'team' to 'skipper'/'admiral', aligning code with user-facing tier names. Variant type is now resolved once at activation/validation and stored in DB via license_variant_type, instead of string-matching the Lemon Squeezy variant_name on every read. Also captures variant_id for future lookups. Pre-existing installs auto-migrate on first getVariant() call. |
||
|
|
f841c402b2 |
fix(licensing): resolve Admiral variant detection and lifetime license handling (#376)
* fix(licensing): resolve Admiral variant detection and lifetime license handling The Lemon Squeezy variant name for Admiral licenses contains "Admiral" (not "Team"), but getVariant() only checked for "team" and "personal". This caused Admiral licenses to be misidentified as Skipper, locking all Admiral-exclusive features. - Map "admiral" variant names to internal "team" value, "skipper" to "personal" - Add isLifetime field to LicenseInfo API response - Hide "Manage Subscription" button for lifetime licenses (no billing portal) - Show "Duration: Lifetime" instead of empty renewal date - Hide upgrade cards for active Admiral users - Add 23 unit tests covering variant resolution, tier computation, and lifetime detection - Add troubleshooting entries for wrong tier label, locked features, and billing portal errors * fix(licensing): address code review findings - Fix nested ternary in LicenseSection JSX; restore conditional rendering to avoid showing an empty "N/A" row for non-subscription states - Clean up test file: use shared svc variable, remove redundant comments, add trialDaysRemaining assertions, rename describe block |
||
|
|
f516275834 |
refactor(licensing): replace Pro branding with Community/Skipper/Admiral tiers (#375)
Eliminate all references to "Pro" across backend, frontend, and docs. Internal tier value renamed from 'pro' to 'paid'; user-facing text now uses the thematic tier names (Community, Skipper, Admiral). - Rename LicenseTier 'pro' to 'paid' in backend and frontend types - Rename requirePro guard to requirePaid, error code PRO_REQUIRED to PAID_REQUIRED - Rename ProGate.tsx to PaidGate.tsx with updated copy - Fix: trial users can now see upgrade/purchase cards in Settings - Update all docs and openapi.yaml to use correct tier names |
||
|
|
a1804c8fbe |
docs: comprehensive review and refresh of all documentation (#374)
* docs: comprehensive review and refresh of all documentation pages Reviewed every doc page against the current app state after the v0.38 dashboard redesign. Updated content, fixed inaccuracies, and refreshed all screenshots at 1920x1080. Pages updated: - introduction: expanded feature list to 25 items across 6 subsections - quickstart: fixed docker run command (Docker Hub, auto JWT, COMPOSE_DIR) - configuration: replaced personal paths with generic /home/user/docker - sso-quickstart: fixed Settings navigation reference - sso: added SSO_LDAP_DISPLAY_NAME env var - overview: added 8 missing feature sections (labels, API tokens, schedules, etc.) - dashboard: complete rewrite for new health bar, gauges, stack health table - stack-management: updated for UP/DN indicators, rollback button, split actions - editor: rewritten for two-column layout, inline stats, embedded terminal - resources: updated Quick Clean docs, added network topology and inspect - app-store: updated categories, deploy sheet details, permission gate, settings - openapi.yaml: fixed YAML parsing error on line 1831 Screenshots refreshed: 14 images across 6 feature areas. * docs: review and update observability, console, multi-node, and compatibility pages - Global Observability: fix log format fields, add download button docs, split display limits into memory buffer vs rendered rows, correct settings labels - Host Console: remove internal implementation details per security docs policy, add stack directory behavior, expand header bar docs, remove unverified scrollback claim - Multi-Node: add Compose Directory field, document connection test details panel, fix edit/delete node behavior, simplify token security section, remove internal details - Node Compatibility: add missing self-update capability, remove internal endpoint paths and cache TTL, move from Features to Reference group in navigation - Refresh all screenshots for the redesigned UI (7 images) * docs: review and refresh fleet, remote updates, labels, alerts, routing, and webhooks pages - Fleet View: added node updates modal, container detail, version/update/critical badges, Tags filter - Remote Updates: removed internal details, added capability cross-link, fast polling - Stack Labels: three creation methods, two assignment methods, 10 colors, bulk actions screenshot - Alerts & Notifications: fixed metric labels, added notification popover detail, status banner - Notification Routing: HTTPS requirement, rule card layout, channel terminology fix - Webhooks: corrected license tier to Admiral, matched action labels to UI, removed internal security details, added local-only note - Troubleshooting: centralized entries from remote-updates, stack-labels, notification-routing - Refreshed all screenshots at 1920x1080, removed 11 orphaned images * docs: review and refresh RBAC, user management, and atomic deployments pages - RBAC: added missing Auditor role (5th role), updated permission matrix, fixed license tier references, documented username/password validation rules, self-deletion protection - Atomic Deployments: added "Which operations are protected" section covering webhooks/schedules/app store, removed internal backup path, fixed license tier to Skipper/Admiral - Screenshots: cropped to dialog element per updated strategic cropping guideline, removed 2 orphaned images * docs: review and refresh fleet-wide backups and audit log pages Update fleet-backups page to reflect current inline create form, add scheduled snapshots section, document the detail view and restore dialog, expand RBAC table to all five roles. Update audit log page to document expanded row detail fields, pagination, refresh button, and data retention screenshot. Replace all screenshots with fresh captures at 1920x720. * docs: review and refresh API tokens and private registries pages - API Tokens: clarify Full Admin scope, add Managing tokens section with card details, document revocation confirmation dialog, add usage tracking to security model, refresh screenshot - Private Registries: add Managing registries section with card details and action buttons, document edit behavior, fix URL auto-fill description, remove encryption algorithm name per security policy, fix grammar, refresh both screenshots * docs: review and refresh auto-update policies, scheduled operations, and SSO pages - Auto-Update Policies: document all 8 table columns, expand action buttons, add "All Stacks" wildcard option, fix field labels, add CSV export and pagination details, refresh screenshots - Scheduled Operations: fix System Prune target description, add Task List table columns, restructure create dialog fields with action-specific annotations, rewrite execution history with column table, refresh screenshots - SSO: remove encryption algorithm name per security policy, add LDAP and OIDC configuration field tables, document provider card controls (Save, Test Connection, Remove, Active badge), refresh screenshots - Move SSO troubleshooting entries to centralized troubleshooting page * docs: review and refresh licensing & billing page Update upgrade card feature lists to match actual tier gating (Skipper: fleet view, webhooks, labels, atomic deployments, backups, auto-update policies; Admiral: scoped RBAC, SSO, audit log, host console, API tokens, private registries, scheduled operations). Add flex layout to align upgrade card buttons at the bottom. Replace stale screenshot with fresh community and active license captures. Add feature breakdown subsection and profile menu billing shortcut to docs. * docs: review and refresh settings reference and security advisories pages Settings Reference: add 5 missing sections (SSO, API Tokens, Registries, Labels, Routing), expand Users from 2 to 5 roles, fix System Limits and Developer field labels to match UI, restructure Developer into Streaming and Data Retention sub-tables, update App Store and Support sections, refresh overview screenshot. Security Advisories: restructure into versioned sections (v0.25.x hardening and v0.19-v0.24 CVE remediation), expand from 3 bullet points to 10 specific improvements, fix GitHub URL from SaelixCode to AnsoCode, redact internal details per security docs policy. Remove "Sencho Pro" product name from all three pages, replaced with tier names (Community, Skipper, Admiral). * docs: review and refresh troubleshooting page, remove architecture and development guides - Rewrote forgotten password section to remove exposed SQL and table names - Updated all Settings navigation paths to Profile > Settings > X - Fixed network topology from "tab" to "view mode", added Pro license note - Updated Prune Networks to current "Prune Dead Networks" label - Corrected update check cooldown from vague to 2 minutes - Consolidated two network creation error sections into one - Removed hardcoded version reference (v0.34.0) - Replaced em dashes throughout - Deleted architecture.mdx (exposes internal implementation details) - Deleted development.mdx (contributor guide belongs in repo, not public docs) - Removed both pages from docs.json navigation * docs: review and refresh operations pages (backup, upgrade, self-hosting, troubleshooting) Backup & Restore: - Added missing encryption.key to all backup/restore procedures - Added Warning about restoring db without matching encryption key - Added cross-reference to Fleet-Wide Backups for paid tiers - Removed false claim about no built-in backup scheduler - Updated cron example to include encryption key copy Upgrading Sencho: - Removed internal migration details (table names, column specs, encryption algorithm) - Replaced with high-level migration summary per security docs policy - Added encryption.key to pre-upgrade backup command - Updated version pinning example from 0.25.3 to 0.38.0 - Added Remote Updates cross-reference for Skipper/Admiral users Self-Hosting Best Practices: - Removed JWT_SECRET from env var table (auto-generated, not an env var) - Removed PORT from env var table (hardcoded to 3000, not configurable) - Added API_RATE_LIMIT to env var table (actually exists in code) - Fixed listen port description from "configurable" to "fixed" - Updated resource recommendations based on measured footprint audit - Removed su-exec reference (internal implementation detail) - Upgraded data directory Note to Warning with file names Troubleshooting: - Fixed "Pro features" heading to "Paid features" with correct tier names |
||
|
|
2ee959ec3b |
feat(dashboard): redesign as DevOps command center (#371)
* feat(dashboard): redesign as DevOps command center Transform the dashboard from a basic stats viewer into a high-signal operational command center with 5 composable sections: - Health status bar with system health derivation (Healthy/Degraded/Critical) - Resource gauges with visual progress bars and threshold coloring - Paginated stack health table with per-stack UP/DN, CPU, memory, and click-to-navigate (8 per page) - Enhanced historical CPU/RAM charts with skeleton empty states - Recent alerts feed with severity-coded notifications Extract monolithic HomeDashboard.tsx (447 lines) into composable sub-components under dashboard/ directory. Remove Docker Run to Compose converter from the landing surface. Add defensive .ok check on container status fallback in EditorLayout. * feat(dashboard): add Clear All Notifications button to Recent Alerts Add a destructive ghost button below the alerts feed that calls DELETE /api/notifications to clear all notifications, then refreshes the list. Button only appears when there are alerts to clear. * feat(dashboard): add pagination to Recent Alerts section Same pattern as Stack Health table: 8 items per page with prev/next chevron controls and page indicator in the card header. Pagination auto-hides when there are 8 or fewer alerts. Page resets on clear all. * fix(dashboard): resolve container count oscillation and add cursor hover detail Fix container stats flickering between 0 and correct values by moving state resets to the top of each useEffect body (runs once per node switch, not on every poll tick). Add animate-ui cursor primitive and wire it to the active containers number in ResourceGauges to show managed/external breakdown on hover. Silence noisy Docker socket errors when engine is unreachable. * feat(dashboard): add cursor hover to health status with reason breakdown Wrap the health badge (pulsing dot + label) in a CursorFollow tooltip that explains why the node is Critical, Degraded, or Healthy. Shows specific metrics (e.g. "RAM at 97.9%", "Disk at 96.4%") when hovered. Displays "All systems nominal" for healthy nodes. * fix(dashboard): resolve OOM from unbounded Docker stats polling Three root causes addressed: 1. updateGlobalDockerNetwork had no overlap guard. When Docker was slow, 3-second interval ticks stacked up, creating dozens of concurrent container.stats() calls that exhausted the heap. Added isUpdatingNetwork flag and increased interval from 3s to 5s. 2. Historical metrics query returned ~20K rows (1-minute buckets x 14 containers x 24h). Downsampled to 5-minute buckets, reducing response size by ~5x. 3. Dashboard polling continued when the browser tab was hidden, creating phantom load. Replaced setInterval with visibilityInterval helper that pauses polling on tab hide and resumes with an immediate fetch on focus. * fix(dashboard): use loadFile for stack navigation from Stack Health table The onNavigateToStack callback was only calling setSelectedFile and setActiveView, skipping the full load flow (YAML content, env files, containers, backup info). This caused the editor to show stale state with a "Start" button for running stacks and empty YAML. Now calls loadFile() which is the same path the sidebar uses. * refactor(dashboard): simplify Containers card layout Replace 2-column grid with vertical layout matching other gauge cards. Active count uses text-2xl hero number, exited count sits in subtitle position. Removed redundant total count row. * fix(dashboard): unify notification types and fix multi-node clear - Replace duplicate Notification interface in EditorLayout with shared NotificationItem from dashboard/types.ts - Tighten is_read type from number | boolean to number (matches SQLite) - Pass notifications from EditorLayout (which aggregates all nodes) to HomeDashboard as props, removing duplicate local-only polling from useDashboardData - Fix Clear All to use clearAllNotifications (deletes from all nodes) instead of fetchNotifications (which was just a re-fetch, causing remote notifications to reappear immediately after clearing) - Delegate DELETE responsibility from RecentAlerts to parent handler * fix(dashboard): handle optional nodeId in notification operations Guard against undefined nodeId when calling fetchForNode for mark-read, delete, and clear-all notification operations. The shared NotificationItem type has nodeId as optional since the API response doesn't include it; EditorLayout enriches it but TypeScript correctly flags the possibility. |