Commit Graph

241 Commits

Author SHA1 Message Date
SaelixCode 8902f6fb4c fix(ci): exclude .git from rsync --delete in sync-docs
rsync --delete removes everything in the destination that is absent from
the source. sencho/docs/ has no .git directory, so rsync was deleting
sencho-docs/.git/ on every run. The commit step then had no git repo to
work with, giving 'fatal: not in a git directory'.

Fix: add --exclude='.git' to the rsync invocation.
2026-03-23 08:02:42 -04:00
Anso 4844e51714 Merge pull request #69 from AnsoCode/chore/refresh-screenshots
docs: refresh screenshots
2026-03-23 07:55:50 -04:00
AnsoCode 70e08f2763 docs: refresh screenshots 2026-03-23 04:36:17 +00:00
Anso 392a49e0bd Merge pull request #68 from AnsoCode/fix/ci-docs-jobs-v2
fix(ci): fix update-screenshots token and sync-docs empty-repo handling
2026-03-23 00:34:12 -04:00
SaelixCode b24863db66 fix(ci): fix update-screenshots token and sync-docs empty-repo handling
update-screenshots:
- Add pull-requests: write to job permissions (required by
  peter-evans/create-pull-request to open a PR)
- Switch checkout and create-pull-request from GITHUB_TOKEN to
  DOCS_REPO_TOKEN (classic PAT with repo scope); GITHUB_TOKEN is
  blocked from creating PRs against protected branches

sync-docs:
- Replace actions/checkout + init fallback with a single bash step that
  runs git clone and falls back to git init on failure; actions/checkout
  on an empty repo creates a .git in a broken state (no HEAD/branch),
  which the previous [ ! -d .git ] guard never caught, leaving the
  commit step with no valid working tree
- Drop --global safe.directory config (no longer needed once we own
  the git setup ourselves)
2026-03-23 00:28:47 -04:00
Anso bed63f8b47 Merge pull request #67 from AnsoCode/fix/ci-docs-jobs
fix(ci): fix sync-docs empty-repo crash and update-screenshots protected-branch push
2026-03-23 00:15:11 -04:00
SaelixCode 9505132aaf fix(ci): fix sync-docs empty-repo crash and update-screenshots protected-branch push
update-screenshots: replace direct git push to develop (blocked by branch
protection) with peter-evans/create-pull-request@v6, which opens or
updates a chore/refresh-screenshots PR instead.

sync-docs: add continue-on-error on the sencho-docs checkout so an empty
repo doesn't abort the job. A new init step runs git init -b main and
re-adds the remote when .git is missing. Final push uses
git push origin HEAD:main to work on both first-run (empty repo) and
subsequent runs (existing branch).
2026-03-23 00:09:31 -04:00
Anso 3302fe75f3 Merge pull request #66 from AnsoCode/feature/bootstrap-docs
docs: bootstrap user-facing documentation from codebase audit
2026-03-22 23:56:08 -04:00
SaelixCode 932e7780ea docs: add Tier 3 reference and operations pages
- reference/settings: complete settings hub reference (all 7 tabs —
  Account, System Limits, Notifications, Appearance, Developer, Nodes,
  App Store)
- operations/troubleshooting: 1:1 path rule failures, Docker socket
  permissions, login errors, WebSocket proxy config, offline remote
  nodes, password reset, health endpoint, container logs
- operations/backup: what to back up (DATA_DIR SQLite + COMPOSE_DIR),
  hot backup via sqlite3, cron example, restore steps, host migration
  walkthrough
- mint.json: populate Reference and Operations nav groups
2026-03-22 23:25:15 -04:00
SaelixCode 7c120c671c docs: add Tier 2 feature pages
- dashboard: container stats, system stats, historical charts, docker run converter
- resources: disk footprint widget, images/volumes/networks/unmanaged tabs, prune scopes
- app-store: template grid, category filter, deploy sheet (env vars/volumes/ports), custom registry
- global-observability: unified log stream, standard vs SSE mode, filters, capacity limits
- host-console: PTY terminal, shell type, security model, common uses
- mint.json: add all 5 new pages to Features nav group
2026-03-22 22:43:53 -04:00
SaelixCode 98910c4117 docs: bootstrap user-facing documentation from codebase audit
- Add 5 new Tier 1 doc pages: configuration, stack-management, editor,
  multi-node, and alerts-notifications
- Update introduction, quickstart, and features/overview to reflect
  current feature set and link to new pages
- Restructure mint.json with Getting Started / Features / Reference /
  Operations navigation groups
- Add Playwright-captured screenshots for all major UI screens
2026-03-22 22:39:06 -04:00
Anso dfa93c0bba Merge pull request #65 from AnsoCode/fix/ci-yaml-and-sync-docs
fix(ci): YAML syntax error and sync-docs git directory failure
2026-03-22 22:15:13 -04:00
SaelixCode b0e2b2d025 fix(e2e): use button role for Resources nav item in screenshots spec 2026-03-22 22:12:13 -04:00
SaelixCode 353fd253e9 fix(ci): add load: true to buildx so Trivy can find the built image 2026-03-22 22:07:32 -04:00
SaelixCode f5c5eda30a fix(ci): use double-quoted string for if condition to fix YAML parse error 2026-03-22 22:04:49 -04:00
SaelixCode dc79683b01 fix(ci): YAML syntax error in if condition and safe.directory for sync-docs 2026-03-22 22:00:57 -04:00
Anso eaf217720c Merge pull request #64 from AnsoCode/feature/ci-screenshot-refresh
feat: auto-refresh doc screenshots on develop push
2026-03-22 21:52:17 -04:00
SaelixCode ed8b8e33b6 feat: add update-screenshots CI job and screenshot capture spec 2026-03-22 21:28:20 -04:00
Anso 05a6b93af0 Merge pull request #63 from AnsoCode/feature/docs-pipeline
feat: automated docs pipeline
2026-03-22 21:20:42 -04:00
SaelixCode 7d1b996bb7 fix: trigger docs sync on develop instead of main 2026-03-22 21:17:45 -04:00
SaelixCode 9496b14f72 feat: add automated docs pipeline and scaffold /docs folder
- Add sync-docs CI job: runs on push to main, copies /docs into sencho-docs repo via DOCS_REPO_TOKEN
- Scaffold /docs with mint.json, getting-started/introduction.mdx, getting-started/quickstart.mdx, features/overview.mdx
- Update CHANGELOG
2026-03-22 21:08:16 -04:00
Anso c36ee93416 Merge pull request #62 from AnsoCode/fix/csp-coop-and-vite-modulepreload
fix(security): disable COOP header and Vite module-preload polyfill
2026-03-22 19:51:25 -04:00
SaelixCode 9f0257e94f fix(merge): resolve CHANGELOG conflict with develop 2026-03-22 19:48:37 -04:00
Anso 16e978bf4e Merge pull request #61 from AnsoCode/fix/managed-container-classification-by-working-dir
fix(stats): classify managed containers by working_dir instead of project name
2026-03-22 19:42:05 -04:00
SaelixCode 35a57e5fa7 fix(security): disable COOP header and Vite module-preload polyfill
Two console errors on HTTP deployments with no functional impact:

1. Helmet's default Cross-Origin-Opener-Policy: same-origin is ignored
   by browsers over HTTP but logged as a console error. Disabled via
   crossOriginOpenerPolicy: false (same rationale as HSTS/COEP).

2. Vite's production build injects an inline module-preload polyfill
   script blocked by script-src 'self'. Disabled via
   build.modulePreload.polyfill: false — all modern browsers support
   link rel="modulepreload" natively.
2026-03-22 19:41:13 -04:00
SaelixCode d62ac09503 fix(stats): classify managed containers by working_dir instead of project name
Containers launched from the COMPOSE_DIR root (not individual subdirs)
all share the root folder's name as their com.docker.compose.project
label, making them appear as "external" despite being in COMPOSE_DIR.

Switch to com.docker.compose.project.working_dir: a container is
managed if its working directory is within COMPOSE_DIR, regardless
of what project name Docker Compose assigned to it.
2026-03-22 18:36:17 -04:00
SaelixCode 50df5b3c02 fix(security): explicitly disable upgrade-insecure-requests via Helmet 8 API
Helmet 8 merges custom directives with its built-in defaults, which
include upgrade-insecure-requests. Simply omitting the directive from
the custom object (PR #59) was insufficient — Helmet silently re-adds
it from defaults. Setting upgradeInsecureRequests: null is the correct
Helmet 8 API to remove a default directive.

This was the root cause of the persistent blank page on plain-HTTP
self-hosted deployments: the directive tells browsers to upgrade all
HTTP sub-resource fetches to HTTPS, producing ERR_SSL_PROTOCOL_ERROR
on every JS/CSS asset.
2026-03-22 17:26:08 -04:00
Anso 278aa2298f Merge pull request #60 from AnsoCode/fix/proxy-401-logout-and-csp-hardening
fix(proxy): prevent remote 401 from triggering local session logout
2026-03-22 16:59:12 -04:00
SaelixCode aeefd79b50 fix(proxy): prevent remote 401 from triggering local session logout
- Add x-sencho-proxy sentinel header to all proxied responses so
  the frontend can distinguish remote auth failures from local session
  expiry, breaking the logout loop when a node's api_token expires
- Add authMiddleware to all 5 /api/notifications endpoints that were
  missing protection (default-deny policy enforcement)
- Expand CSP to include connectSrc ws:/wss: and workerSrc blob:
  for WebSocket and Monaco editor worker support
2026-03-22 16:43:06 -04:00
SaelixCode 987fc3d339 fix(docker): repair broken entrypoint from bad merge conflict resolution
The merge of PR58 into PR59's branch produced a corrupt docker-entrypoint.sh
(97 lines) where PR59's if block was cut off mid-way (missing exec su-exec
and closing fi) and then PR58's entire if block was appended. This caused:

  /usr/local/bin/docker-entrypoint.sh: line 98: syntax error: unexpected
  end of file (expecting "fi")

Fix: overwrite with the clean 71-line version and add a defensive
`sed -i 's/\r//'` step in the Dockerfile to strip Windows CRLF line
endings at build time, preventing this class of error even if CRLF
slips past .gitattributes in future.
2026-03-22 16:08:50 -04:00
Anso 25012a07ca Merge pull request #59 from AnsoCode/fix/helmet-csp-and-docker-socket-diagnosis
fix(security): remove CSP upgrade-insecure-requests and HSTS for HTTP deployments
2026-03-22 15:55:42 -04:00
Anso 5ddf85d364 Merge branch 'develop' into fix/helmet-csp-and-docker-socket-diagnosis 2026-03-22 15:51:41 -04:00
SaelixCode cf2946cfa6 fix(security): remove CSP upgrade-insecure-requests and HSTS over HTTP
Helmet's defaults are designed for HTTPS-only deployments. Two directives
were actively breaking plain-HTTP self-hosted instances:

- upgrade-insecure-requests: causes browsers to upgrade all JS/CSS/asset
  fetches to HTTPS → ERR_SSL_PROTOCOL_ERROR → completely blank page
- Strict-Transport-Security: permanently instructs browsers to refuse HTTP
  for 1 year, even after the header is removed from the server

Also handle docker socket GID=0 (root:root) edge case in entrypoint and
add [entrypoint] diagnostic log lines for easier debugging.
2026-03-22 15:42:48 -04:00
Anso c743f6cd45 Merge pull request #58 from AnsoCode/fix/docker-entrypoint-volume-permissions
fix(docker): entrypoint for volume permission handling
2026-03-22 15:22:43 -04:00
SaelixCode 593a709197 fix(docker): add entrypoint for volume permission handling
Add docker-entrypoint.sh that runs as root at startup, fixes ownership
of the DATA_DIR volume (only files with wrong user or group), then drops
to the non-root sencho user via su-exec before starting Node.

This eliminates the SQLITE_READONLY crash that occurs when a host-mounted
data volume was created by root or chowned to the wrong UID. The pattern
mirrors the official PostgreSQL, Redis, and MariaDB Docker images.

- Install su-exec in Stage 3 (10KB Alpine tool, idiomatic alternative to gosu)
- Remove USER directive; entrypoint handles the privilege drop instead
- Use ENTRYPOINT + CMD so Node becomes PID 1 (correct SIGTERM handling)
- Add .gitattributes to enforce LF line endings for *.sh files
2026-03-22 15:16:34 -04:00
Anso 2a444bde99 Merge pull request #57 from AnsoCode/feat/resources-managed-unmanaged-separation
feat(resources): managed/unmanaged resource separation across Resources Hub
2026-03-22 03:24:35 -04:00
SaelixCode 0d5dc574a4 fix(dashboard): correct stale Stats reset with inactive field 2026-03-22 03:21:36 -04:00
SaelixCode 5191737d53 feat(resources): managed/unmanaged resource separation across Resources Hub
- Classify all Docker images, volumes, and networks as managed (Sencho
  stack), external (other Compose project), or unused/system via a new
  getClassifiedResources() method and GET /api/system/resources endpoint
- Add pruneManagedOnly() + getDiskUsageClassified() to DockerController
- Prune buttons now default to Sencho-managed scope; "All Docker" is
  hidden in a ⋮ dropdown with a distinct destructive confirm dialog
- Replace Reclaimable Space donut with interactive Docker Disk Footprint
  widget (stacked bar with clickable segments that filter resource tabs)
- Add managed/external filter toggles and classification badges per tab
- GET /api/stats now returns managed + unmanaged container counts; Home
  Dashboard Active Containers card subtitle shows "N managed · N external"
- Rename "Ghost Containers" tab/copy to "Unmanaged Containers" throughout
2026-03-22 03:17:54 -04:00
Anso 740796b869 Merge pull request #56 from AnsoCode/ci/improve-pipeline
ci: restructure pipeline with parallel jobs, E2E, Docker validation, and auditing
2026-03-22 02:02:16 -04:00
SaelixCode 707a5e81c1 fix(e2e): fill api_token in nodes tests so submit button is enabled
The Add Node button requires both api_url AND api_token to be non-empty
before it enables. Both validation tests were only filling api_url,
leaving the button permanently disabled and timing out after 30s.
Add a dummy api_token fill in each test so the button enables and the
form submits — the backend then correctly rejects the invalid URL.
2026-03-22 01:58:47 -04:00
SaelixCode 12bbe51a3a fix(e2e): fully rewrite nodes tests to handle Radix UI Select and remote type flow
- openAddNodeAsRemote helper: clicks #node-type (Radix combobox, not native select),
  picks the 'Remote' option, then waits for #node-api-url to appear — the API URL
  field is conditionally rendered only when type === 'remote'
- Fix getByLabel(/node name/i) → #node-name in both tests (missed in second test)
- Use .last() for submit button to avoid matching the Add Node trigger behind the dialog
- Remove typeSelect.selectOption() which throws 'not a select element' on Radix UI
2026-03-22 01:34:43 -04:00
SaelixCode e01c0d6b48 fix(e2e): use #node-name locator instead of getByLabel in nodes tests
The NodeManager form labels the field 'Name' (not 'Node Name'), so
getByLabel(/node name/i) never matched and timed out after 30s.
Switch to the stable #node-name id which is bound via htmlFor.
2026-03-22 01:27:33 -04:00
SaelixCode 14c24c8245 fix(e2e): fix stacks timeout and nodes skip in CI
stacks.spec.ts:
- waitForStacksLoaded: wait for 'Create Stack' button instead of [cmdk-item] > 0
  CI starts with empty COMPOSE_DIR so there are no stacks; the button is always
  rendered in the sidebar regardless of whether any stacks exist

nodes.spec.ts:
- beforeEach: click Settings then 'Nodes' nav item to reach NodeManager
  The Add Node button lives inside Settings → Nodes; the previous beforeEach
  only clicked Settings but never navigated to the Nodes section, so the
  add-node button was never found and tests silently skipped
2026-03-22 01:20:54 -04:00
SaelixCode e876a91a2e fix(lint): resolve all backend ESLint errors to pass CI lint step
Config changes (eslint.config.mjs):
- no-unused-vars: caughtErrors=none (catch clause vars are intentionally ignored throughout)
- varsIgnorePattern/argsIgnorePattern: ^_ (allow _-prefixed intentional ignores)
- ban-ts-comment, no-namespace, no-empty: downgraded to warn (pre-existing patterns)
- no-control-regex: off (terminal output processing intentionally uses control chars)

Dead code removed:
- index.ts: remove unused spawn/exec/promisify imports and dead execAsync variable
- FileSystemService.ts: remove duplicate fs default import (fsPromises already imported)
- LogFormatter.ts: remove unused parsed variable (JSON.parse used only for validation)

Auto-fixed via eslint --fix:
- prefer-const: ComposeService, DockerController, MonitorService, index.ts
2026-03-22 01:09:18 -04:00
SaelixCode 3cf9f023d3 fix(ci): update lock files after adding ESLint deps and fixing high CVEs
- backend/package-lock.json: regenerated after adding eslint, typescript-eslint, @eslint/js devDeps
- frontend/package-lock.json: ran npm audit fix to resolve 3 high-severity CVEs
  (flatted DoS, minimatch ReDoS, rollup path traversal)
- backend/package-lock.json: resolved minimatch high ReDoS CVE
2026-03-22 01:02:17 -04:00
SaelixCode fbd6b8933d ci: restructure pipeline into parallel jobs with E2E, Docker validation, and auditing
- Split serial build-and-test job into parallel backend, frontend, docker-validate, and e2e jobs
- backend job: build → test → lint (new ESLint) → npm audit --audit-level=high
- frontend job: build → lint → npm audit --audit-level=high
- docker-validate job: builds image on every PR without pushing; Trivy scans for CRITICAL/HIGH CVEs (informational)
- e2e job: runs full Playwright suite against live dev servers after backend+frontend pass
- Add backend/eslint.config.mjs and lint script to backend/package.json
2026-03-22 00:48:49 -04:00
Anso 431acc3179 Merge pull request #55 from AnsoCode/fix/alerts-notifications-overhaul
security: pre-release hardening, automated testing & production readiness
2026-03-22 00:29:09 -04:00
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 0dd72b3eb4 fix(ts): remove unused motion import from alert-dialog 2026-03-22 00:01:08 -04:00
SaelixCode f7471a1a18 fix(e2e): get all E2E tests passing and fix AlertDialog crash on delete
- Fix rate limiter to allow 100 attempts in dev mode so E2E tests are
  not blocked by failed-login attempts during test development
- Simplify logout button selector to use Lucide icon class (lucide-log-out)
  instead of the fragile Tooltip-content locator chain that broke on navigation
- Rewrite stacks E2E spec: use waitForFunction to wait for sidebar to load,
  delete leftover stacks via browser-context fetch before creating, and use
  page.reload() to get a clean sidebar state
- Fix AlertDialogContent: remove asChild+motion.div pattern that triggered a
  React.Children.only crash — Radix AlertDialog.Content injects a second
  DescriptionWarning child internally, breaking Slot when asChild=true; replace
  with CSS keyframe animations (data-[state=open]:animate-in)
- Fix final assertion in delete test to use exact text + listbox scope to avoid
  false positives from similarly-named stacks like e2e-test-stack-*
- All 6 E2E tests pass (4 auth + 2 stacks); node tests skip gracefully
2026-03-21 23:58:46 -04:00