mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
08e46d6bf3eb505d0da8bcba2ff15ae6c994d92e
35 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
08e46d6bf3 |
chore(deps): bump the all-npm-backend group in /backend with 8 updates (#1439)
Bumps the all-npm-backend group in /backend with 8 updates: | Package | From | To | | --- | --- | --- | | [axios](https://github.com/axios/axios) | `1.18.0` | `1.18.1` | | [cron-parser](https://github.com/harrisiirak/cron-parser) | `5.5.0` | `5.6.0` | | [semver](https://github.com/npm/node-semver) | `7.8.4` | `7.8.5` | | [systeminformation](https://github.com/sebhildebrandt/systeminformation) | `5.31.7` | `5.31.10` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.9.3` | `26.0.0` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.61.1` | `8.62.0` | | [@aws-sdk/client-ecr](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-ecr) | `3.1070.0` | `3.1075.0` | | [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.1070.0` | `3.1075.0` | Updates `axios` from 1.18.0 to 1.18.1 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.18.0...v1.18.1) Updates `cron-parser` from 5.5.0 to 5.6.0 - [Release notes](https://github.com/harrisiirak/cron-parser/releases) - [Commits](https://github.com/harrisiirak/cron-parser/compare/v5.5.0...v5.6.0) Updates `semver` from 7.8.4 to 7.8.5 - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v7.8.4...v7.8.5) Updates `systeminformation` from 5.31.7 to 5.31.10 - [Release notes](https://github.com/sebhildebrandt/systeminformation/releases) - [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md) - [Commits](https://github.com/sebhildebrandt/systeminformation/compare/v5.31.7...v5.31.10) Updates `@types/node` from 25.9.3 to 26.0.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `typescript-eslint` from 8.61.1 to 8.62.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.0/packages/typescript-eslint) Updates `@aws-sdk/client-ecr` from 3.1070.0 to 3.1075.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-ecr/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1075.0/clients/client-ecr) Updates `@aws-sdk/client-s3` from 3.1070.0 to 3.1075.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1075.0/clients/client-s3) --- updated-dependencies: - dependency-name: axios dependency-version: 1.18.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: cron-parser dependency-version: 5.6.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: semver dependency-version: 7.8.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: systeminformation dependency-version: 5.31.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: "@types/node" dependency-version: 26.0.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: all-npm-backend - dependency-name: typescript-eslint dependency-version: 8.62.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: "@aws-sdk/client-ecr" dependency-version: 3.1075.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: "@aws-sdk/client-s3" dependency-version: 3.1075.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
c6d1631afe |
feat(recovery): add safe-mode recovery surface and emergency CLI (#1286)
* feat(recovery): add safe-mode recovery surface and emergency CLI Add a read-only Recovery tab under Settings (admin-only) backed by a new GET /api/diagnostics endpoint reporting app version, database integrity, encryption-key status, Docker reachability, account and SSO counts, and non-secret configuration. The endpoint loads without Docker or live metrics so it stays available when the dashboard does not, requires a genuine admin session, and builds its config block from a non-secret allowlist so no credentials are ever exposed. Expand the emergency command-line toolkit beyond the two-factor reset with seven host-level commands: reset-password, create-emergency-admin, clear-sessions, disable-sso, diagnostics, validate-db, and backup-data. Each prints its result, exits with a meaningful status code, and writes an audit entry where it changes state. Document the toolkit in a new operator guide and link it from the recovery and two-factor pages. * feat(recovery): download the emergency command reference as a text file The recovery commands are needed exactly when the dashboard is unreachable, so reading them only in-app is a chicken-and-egg problem. Add a Download button to the command-line section that saves the full `docker compose exec sencho ...` reference as a text file, letting operators keep it on hand before they need it. Reuses a shared download helper with the existing diagnostics export. * fix(recovery): harden diagnostics, backup, and emergency-admin against edge cases Address findings from an independent review of the recovery toolkit: - DiagnosticsService now degrades instead of throwing when a queried table is missing or corrupt: each read falls back and is folded into database.ok, so a broken database reports "problem detected" rather than failing the whole endpoint or showing a misleading healthy state with zeroed counts. - backup-data refuses a destination that resolves to the live database, which would otherwise report success while producing no separate copy. - create-emergency-admin now applies the same username rule as the user- management route, extracted to a shared helper so both stay in sync. Adds tests for a missing read table, a malformed emergency-admin username, and the backup same-target rejection. |
||
|
|
efcc06d50b |
ci: harden CI and supply-chain pipeline (#1169)
* ci: harden CI and supply-chain pipeline * Add frontend Vitest step to ci.yml so the 241 existing frontend tests run on every PR (mirrors the backend build/test/lint/audit order). * Pin Node 26 as a single source of truth: new .node-version, node-version-file on all setup-node calls, engines.node ">=26.0.0" in all three package.json files. Matches the Dockerfile's node:26-alpine. * SHA-pin remaining mutable actions in the start-app composite (actions/setup-node v6, actions/cache v4.3.0). * Pin Dockerfile supply-chain inputs: golang:1.26.3-alpine by sha256 digest in both builder stages; replace mutable-tag git clone with commit-SHA fetch for docker/cli (v29.4.1) and docker/compose (v5.1.3). LDFLAGS version strings and otel patch preserved unchanged. * Ref-scope docker-publish concurrency so two different release tags cannot cancel each other; same-ref reruns still cancel as before. * Harden CLA workflow: drop actions:write from permissions; tighten the issue_comment trigger to PRs only (github.event.issue.pull_request != null) matching the two documented CLA phrases. No PR code is checked out. * Drop trivy-version: latest from both Trivy scans so the SHA-pinned aquasecurity/trivy-action governs the bundled binary version. The HIGH/CRITICAL gate, severity filter, and trivy.yaml (OpenVEX) are unchanged. * Restructure Dependabot: add applies-to: security-updates groups for npm (root/backend/frontend), docker, and github-actions; switch github-actions to directories so the local composite action is monitored alongside the top-level workflows. * Add a daily scheduled SARIF security scan (security-scan.yml): two parallel jobs scanning saelix/sencho:latest and a fresh main HEAD build, uploading to GitHub code scanning. Least-privilege (contents: read, security-events: write). Visibility only; existing PR-blocking and release-blocking Trivy gates are not weakened. Validation: backend tsc clean; frontend tsc clean; frontend npm test 27 files / 241 tests pass; npm audit --audit-level=high passes at root, backend, and frontend; docker buildx build --check passes with no warnings (all pinned digests resolve from the registry). * ci(frontend): set explicit jsdom URL so localStorage initializes in CI jsdom does not instantiate window.localStorage / sessionStorage when the document has the opaque about:blank origin. Five frontend test files that call localStorage.clear() in beforeEach started failing once the new frontend Vitest step in this PR began running them on Linux CI runners. Configuring environmentOptions.jsdom.url with a real same-origin URL is the documented Vitest 4.x workaround and is a config-only change. All 27 test files (241 tests) pass locally with the fix applied. |
||
|
|
8cbeb8708e |
chore(deps): bump the all-npm-backend group across 1 directory with 8 updates (#943)
Bumps the all-npm-backend group with 8 updates in the /backend directory: | Package | From | To | | --- | --- | --- | | [axios](https://github.com/axios/axios) | `1.15.2` | `1.16.0` | | [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) | `3.0.5` | `4.0.0` | | [yaml](https://github.com/eemeli/yaml) | `2.8.3` | `2.8.4` | | [zod](https://github.com/colinhacks/zod) | `4.3.6` | `4.4.3` | | [eslint](https://github.com/eslint/eslint) | `10.2.1` | `10.3.0` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.59.1` | `8.59.2` | | [@aws-sdk/client-ecr](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-ecr) | `3.1038.0` | `3.1043.0` | | [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.1038.0` | `3.1043.0` | Updates `axios` from 1.15.2 to 1.16.0 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.15.2...v1.16.0) Updates `http-proxy-middleware` from 3.0.5 to 4.0.0 - [Release notes](https://github.com/chimurai/http-proxy-middleware/releases) - [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/master/CHANGELOG.md) - [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v3.0.5...v4.0.0) Updates `yaml` from 2.8.3 to 2.8.4 - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v2.8.3...v2.8.4) Updates `zod` from 4.3.6 to 4.4.3 - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](https://github.com/colinhacks/zod/compare/v4.3.6...v4.4.3) Updates `eslint` from 10.2.1 to 10.3.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.2.1...v10.3.0) Updates `typescript-eslint` from 8.59.1 to 8.59.2 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.2/packages/typescript-eslint) Updates `@aws-sdk/client-ecr` from 3.1038.0 to 3.1043.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-ecr/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1043.0/clients/client-ecr) Updates `@aws-sdk/client-s3` from 3.1038.0 to 3.1043.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1043.0/clients/client-s3) --- updated-dependencies: - dependency-name: axios dependency-version: 1.16.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: http-proxy-middleware dependency-version: 4.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-npm-backend - dependency-name: yaml dependency-version: 2.8.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: zod dependency-version: 4.4.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: eslint dependency-version: 10.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: typescript-eslint dependency-version: 8.59.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: "@aws-sdk/client-ecr" dependency-version: 3.1043.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: "@aws-sdk/client-s3" dependency-version: 3.1043.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Anso <dev@saelix.com> |
||
|
|
04f35fdf22 |
perf(backend): mark AWS SDK clients as optional dependencies (#821)
@aws-sdk/client-ecr and @aws-sdk/client-s3 each pull in dozens of
@smithy/* and middleware-* transitive packages but only fire when an
operator configures an ECR registry or cloud backup respectively. Move
both to optionalDependencies so the package classification matches
their runtime role and operators who never use either feature can run
`npm ci --omit=optional` for a ~150 MB-slimmer image.
The default Dockerfile install (`npm ci --omit=dev`) keeps shipping
the SDKs, so default installs are unchanged. The dynamic imports in
CloudBackupService.loadS3Sdk and RegistryService.fetchEcrToken now
catch a missing-module failure and throw a wrapped Error whose
message names the recovery path (`reinstall without --omit=optional`)
and whose cause propagates the original module-not-found error for
debugging.
Bumps tsconfig.json's target and lib to ES2022 so `new Error(msg,
{ cause })` is typed; Node 25 already supports this at runtime.
|
||
|
|
502ee83438 |
chore(backend): move @types/* to devDependencies (#813)
The seven @types/* packages contain only TypeScript declaration files, which are erased at compile time and have no runtime use. Listing them under dependencies kept them installed in the production image for no benefit; they belong under devDependencies so `npm ci --omit=dev` prunes them from the runtime install. Packages moved: @types/compression, @types/cors, @types/dockerode, @types/express, @types/http-proxy, @types/semver, @types/ws. |
||
|
|
71d164cf9e |
chore(deps): bump the all-npm-backend group across 1 directory with 10 updates (#783)
* chore(deps): bump the all-npm-backend group across 1 directory with 10 updates Bumps the all-npm-backend group with 10 updates in the /backend directory: | Package | From | To | | --- | --- | --- | | [@aws-sdk/client-ecr](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-ecr) | `3.1028.0` | `3.1037.0` | | [axios](https://github.com/axios/axios) | `1.15.0` | `1.15.2` | | [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) | `12.8.0` | `12.9.0` | | [express-rate-limit](https://github.com/express-rate-limit/express-rate-limit) | `8.3.2` | `8.4.1` | | [openid-client](https://github.com/panva/openid-client) | `6.8.2` | `6.8.3` | | [otplib](https://github.com/yeojz/otplib/tree/HEAD/packages/otplib) | `12.0.1` | `13.4.0` | | [eslint](https://github.com/eslint/eslint) | `10.2.0` | `10.2.1` | | [typescript](https://github.com/microsoft/TypeScript) | `6.0.2` | `6.0.3` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.58.1` | `8.59.0` | | [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.4` | `4.1.5` | Updates `@aws-sdk/client-ecr` from 3.1028.0 to 3.1037.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-ecr/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1037.0/clients/client-ecr) Updates `axios` from 1.15.0 to 1.15.2 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.15.0...v1.15.2) Updates `better-sqlite3` from 12.8.0 to 12.9.0 - [Release notes](https://github.com/WiseLibs/better-sqlite3/releases) - [Commits](https://github.com/WiseLibs/better-sqlite3/compare/v12.8.0...v12.9.0) Updates `express-rate-limit` from 8.3.2 to 8.4.1 - [Release notes](https://github.com/express-rate-limit/express-rate-limit/releases) - [Commits](https://github.com/express-rate-limit/express-rate-limit/compare/v8.3.2...v8.4.1) Updates `openid-client` from 6.8.2 to 6.8.3 - [Release notes](https://github.com/panva/openid-client/releases) - [Changelog](https://github.com/panva/openid-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/panva/openid-client/compare/v6.8.2...v6.8.3) Updates `otplib` from 12.0.1 to 13.4.0 - [Release notes](https://github.com/yeojz/otplib/releases) - [Commits](https://github.com/yeojz/otplib/commits/v13.4.0/packages/otplib) Updates `eslint` from 10.2.0 to 10.2.1 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.2.0...v10.2.1) Updates `typescript` from 6.0.2 to 6.0.3 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/compare/v6.0.2...v6.0.3) Updates `typescript-eslint` from 8.58.1 to 8.59.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.0/packages/typescript-eslint) Updates `vitest` from 4.1.4 to 4.1.5 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.5/packages/vitest) --- updated-dependencies: - dependency-name: "@aws-sdk/client-ecr" dependency-version: 3.1037.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: axios dependency-version: 1.15.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: better-sqlite3 dependency-version: 12.9.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: express-rate-limit dependency-version: 8.4.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: openid-client dependency-version: 6.8.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: otplib dependency-version: 13.4.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-npm-backend - dependency-name: eslint dependency-version: 10.2.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: typescript dependency-version: 6.0.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-backend - dependency-name: typescript-eslint dependency-version: 8.59.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: vitest dependency-version: 4.1.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-backend ... Signed-off-by: dependabot[bot] <support@github.com> * fix(mfa): migrate otplib API to v13 The v13 release removed the singleton authenticator export and renamed HashAlgorithms to a string union type. Switch to the OTP class with generateSync/verifySync for synchronous operation, passing per-call options instead of setting global instance state. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: SaelixCode <dev@saelix.com> |
||
|
|
95cb2242ca |
chore(deps): bump uuid and dockerode in /backend (#759)
Removes [uuid](https://github.com/uuidjs/uuid). It's no longer used after updating ancestor dependency [dockerode](https://github.com/apocas/dockerode). These dependencies need to be updated together. Removes `uuid` Updates `dockerode` from 4.0.10 to 5.0.0 - [Release notes](https://github.com/apocas/dockerode/releases) - [Commits](https://github.com/apocas/dockerode/compare/v4.0.10...v5.0.0) --- updated-dependencies: - dependency-name: dockerode dependency-version: 5.0.0 dependency-type: direct:production - dependency-name: uuid dependency-version: dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
03f91cd5bb |
feat(cloud-backup): mirror fleet snapshots to S3-compatible storage (#782)
* feat(cloud-backup): mirror fleet snapshots to S3-compatible storage
Add an Admiral-tier Cloud Backup feature that replicates every fleet
snapshot to off-site storage, with two provider modes that share the
same `@aws-sdk/client-s3` code path:
- Sencho Cloud Backup: zero-config, 500 MB allowance backed by
Cloudflare R2, provisioned via the sencho.io worker against the
user's Lemon Squeezy license.
- Custom S3 (BYOB): any S3-compatible bucket (AWS, MinIO, Backblaze
B2, Wasabi, R2 with own keys), with credentials encrypted via
`CryptoService` before storage.
API-triggered snapshots upload fire-and-forget so the UI returns
immediately; scheduled snapshots block on the upload so the task's
success/failure reflects cloud durability. Object keys include the
instance_id segment to prevent collisions when the same Admiral
license is activated on multiple Sencho instances.
* fix(cloud-backup): drop ES2022-only Error cause arg breaking ES2020 build
The backend tsconfig pins lib to ES2020. The two-argument
`Error(message, { cause })` form requires ES2022, so tsc rejected it
with TS2554. Revert to single-argument throw to match the
convention used elsewhere in the backend services.
|
||
|
|
801a098a5b |
feat(files): per-stack file explorer (#780)
* feat(files): backend foundation for stack file explorer
Install multer for multipart file upload handling. Add
isValidRelativeStackPath to validation.ts to guard client-supplied
relative paths against traversal, absolute paths, NUL bytes, backslash
injection, and double-slash segments. Add isBinaryBuffer to a new
binaryDetect.ts utility for heuristic text/binary detection via
NUL-byte fast exit and non-printable byte ratio sampling.
* fix(files): reject bare dot segments in isValidRelativeStackPath
* feat(files): add safe stack-scoped file I/O methods to FileSystemService
Adds FileEntry interface and seven new public methods to FileSystemService
for stack-scoped file operations: listStackDirectory, readStackFile,
streamStackFile, writeStackFile, deleteStackPath, mkdirStackPath, and
statStackEntry.
Each method routes through a private resolveSafeStackPath helper that
enforces two-phase path containment: a pre-realpath lexical check plus a
post-realpath symlink-escape check. ENOENT targets are handled by walking
up to the deepest existing ancestor, realpaths that ancestor, and
reattaching the remaining suffix.
Binary detection delegates to isBinaryBuffer; path safety delegates to
isPathWithinBase. Protected file names and the MIME map are module-level
constants to avoid repeated allocation.
* feat(files): frontend API wrappers and Monaco language helper
* fix(files): tighten stackFilesApi error handling and localOnly support
* fix(files): FileSystemService safety and correctness fixes
* feat(files): add file explorer API endpoints to stacks router
* feat(files): FileTree and FileTreeNode components
* fix(files): route security hardening and stream cleanup
* fix(files): FileTree accessibility, icon stroke, stale fetch guard
Add strokeWidth={1.5} to all Lucide icons in FileTreeNode to match the
design system. Add aria-expanded to directory rows for accessibility.
Guard handleDirClick .then() callbacks against stale stack name
references when the component re-renders with a new stack. Add
toast.info fallbacks when compose.yaml or .env is clicked without a
navigation callback registered.
* feat(files): FileViewer, FileUploadDropzone, NewFolderDialog, DeleteFileConfirm
* fix(files): resolve code quality findings in file explorer components
- Move editorOptions useMemo above conditional returns in FileViewer (Rules of Hooks fix)
- Fix blob download: append anchor to DOM before click, defer URL revoke 100ms
- Keep protected-file confirm input visible during NOT_EMPTY recursive retry in DeleteFileConfirm
- Remove non-functional cursor-pointer/onClick from Community upgrade pill in FileUploadDropzone
- Add success toast on folder creation in NewFolderDialog
- Switch all (e as Error).message casts to instanceof Error narrowing
* test(files): unit tests for binary detection, stack path safety, and file explorer routes
- binary-detection.test.ts: covers isBinaryBuffer edge cases (empty, NUL,
PNG header, threshold boundary, sampleBytes parameter)
- filesystem-stack-paths.test.ts: covers isValidRelativeStackPath (accepts/
rejects matrix) and FileSystemService stack methods against a real temp dir
(listStackDirectory sort and protection flags, readStackFile text/binary/
oversized paths, writeStackFile/Buffer, deleteStackPath, mkdirStackPath,
traversal guard); platform-specific empty-dir/NOT_EMPTY cases skip on Windows
- stack-files-routes.test.ts: route-level integration tests for all seven
file explorer endpoints; covers auth gating, Community-tier 403 gates,
input validation, 413 TOO_LARGE upload limit, and 204/200 happy paths
* feat(files): StackFileExplorer container with lazy tree, viewer, and action bar
* fix(files): add Download button to explorer toolbar, fix Community upgrade pill, reset state on stack change
* test(files): add missing test coverage for file explorer routes and service
* feat(files): add Files tab to EditorLayout with StackFileExplorer integration
* fix(files): add defensive activeTab guard to saveFile and discardChanges
* test(files): unit tests for FileTree expand/collapse and FileViewer render modes
Covers the three FileViewer content modes (text/Monaco, binary panel,
oversized panel) and the FileTree expand/collapse/cache cycle: first
expand fetches the subdirectory, second click collapses without a fetch,
third click re-expands from the in-memory cache without a second fetch.
* test(e2e): file explorer community and skipper+ flows
Covers the full file-explorer feature surface in two describe blocks:
Community (read-only): intercepts /api/license to simulate community
tier, confirms the upgrade pill is visible in the left pane, and
asserts that the Save button is absent after opening a text file.
Skipper+ (full CRUD): uploads a text file and confirms it appears in
the tree; edits config/app.conf and saves via Monaco; deletes an
uploaded file and asserts the tree entry is gone; issues a raw HTTP
request to the download endpoint and checks for status 200 and the
content-disposition: attachment header.
Also adds data-testid="file-action-delete" to the action bar Delete
button in StackFileExplorer for stable targeting, and exports
waitForStacksLoaded from e2e/helpers.ts to eliminate the three
identical local copies in stacks, deploy-log-panel, and stack-files
spec files.
* fix(e2e): improve test isolation and selector stability in stack-files spec
Move beforeEach seed to beforeAll/afterAll so fixtures are created once per
suite, not before every test. Extract shared seedSuite/teardownSuite helpers
to eliminate the duplicate beforeAll/afterAll blocks. Wrap teardown in
try/catch so failures log a warning rather than masking test results.
Replace waitForTimeout(500) with a deterministic expect on the file tree
sentinel. Add data-testid="anatomy-files-btn" and data-testid="delete-confirm-btn"
to replace the fragile button text/positional selectors. Assert Save button
starts disabled before editing.
* docs(files): add stack file explorer documentation
Add user-facing guide for the stack file explorer feature covering
tier access (Community read-only, Skipper+ read-write), viewing
limits, upload/download caps, protected file routing, and
troubleshooting. Update the editor page to reference the new guide
and register the page in the navigation.
* fix(docs): use canonical Skipper tier name in file explorer overview card
* fix(files): resolve lint errors blocking CI
Remove unnecessary backslash escape before double-quote in the
Content-Disposition regex (no-useless-escape). Replace five synchronous
setState resets at the top of the FileTree mount effect with a React key
prop on the FileTree element in StackFileExplorer so remounting resets
state automatically, eliminating the react-hooks/set-state-in-effect
violation.
* test(files): fix e2e seeding to work on community-tier CI
Replace the browser-side paid upload/mkdir API calls in seedTestStack with
direct Node fs writes. The upload and folder endpoints require Skipper+ so
they returned 403 on CI, which runs with no license set. Stack creation
via POST /api/stacks stays as an API call since it is community-allowed and
keeps the backend registry in sync.
Add a per-test tier check in the Skipper+ beforeEach that skips gracefully
when the instance is community, matching the pattern in auto-heal-policies.
|
||
|
|
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. |
||
|
|
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.
|
||
|
|
12fe79fc85 |
fix(deps): migrate SSO OIDC code to openid-client v6 (#492)
* chore(deps): bump the all-npm-backend group across 1 directory with 4 updates Bumps the all-npm-backend group with 4 updates in the /backend directory: [@aws-sdk/client-ecr](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-ecr), [openid-client](https://github.com/panva/openid-client), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `@aws-sdk/client-ecr` from 3.1026.0 to 3.1028.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-ecr/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1028.0/clients/client-ecr) Updates `openid-client` from 5.7.1 to 6.8.2 - [Release notes](https://github.com/panva/openid-client/releases) - [Changelog](https://github.com/panva/openid-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/panva/openid-client/compare/v5.7.1...v6.8.2) Updates `@types/node` from 25.5.2 to 25.6.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `vitest` from 4.1.3 to 4.1.4 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.4/packages/vitest) --- updated-dependencies: - dependency-name: "@aws-sdk/client-ecr" dependency-version: 3.1028.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: openid-client dependency-version: 6.8.2 dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-npm-backend - dependency-name: "@types/node" dependency-version: 25.6.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-backend - dependency-name: vitest dependency-version: 4.1.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-backend ... Signed-off-by: dependabot[bot] <support@github.com> * fix(deps): migrate SSO OIDC code to openid-client v6 The v5 Issuer/Client/generators API was removed upstream. Rewrite the OIDC auth flow in SSOService to use the v6 functional API: * discovery()/Configuration replace Issuer.discover + new issuer.Client * buildAuthorizationUrl replaces client.authorizationUrl * authorizationCodeGrant replaces client.callback, and handles the state check internally so the manual CSRF guard is removed * fetchUserInfo replaces client.userinfo and now receives claims.sub so v6 can reject userinfo/id_token subject mismatches Also: * Cache the discovered Configuration per provider in CacheService (TTL 5 min) so a single login flow does not pay the HTTPS discovery round trip twice. Invalidation wired into saveProviderConfig, deleteProviderConfig, and seedOidcFromEnv via a private helper. * Fix the testOidcDiscovery hack that passed a "discovery-probe" placeholder when clientId was missing; validate clientId upfront instead and return a clear error. * Log fetchUserInfo failures at warn level before falling back to id_token claims so a subject-mismatch rejection is not silently hidden. Unblocks dependabot PR #470. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
c0c321227b |
perf: unify caching behind a single CacheService and enable HTTP compression (#468)
Replaces five ad-hoc in-process caches (project name map, templates, latest version, fleet update status, remote node meta) with a single internal CacheService that provides TTL, inflight-promise deduplication to protect against thundering herd, stale-on-error fallback, and per-namespace hit/miss/stale/size counters for observability. Wraps the hot-path dashboard endpoints in the cache with write-path invalidation: /api/stats (2s), /api/system/stats (3s), and /api/stacks/statuses (3s). Keys are namespaced by nodeId so switching nodes never serves another node's data. Every route that mutates container or stack state calls invalidateNodeCaches(nodeId), which also drops the global project-name-map, so user actions stay instantly reflected in the UI. For /api/system/stats the cheap per-request network rx/tx block is kept outside the cache so live-updating charts stay smooth while the expensive systeminformation.currentLoad() CPU sample (~200ms) is reused across the TTL. Adds admin-only GET /api/system/cache-stats returning per-namespace counters for operators who want to observe cache effectiveness. Enables the compression middleware site-wide for JSON responses. Large payloads like /api/templates shrink roughly 5x on the wire. SSE endpoints are explicitly excluded via a Content-Type filter so live log tails and metric streams are not buffered. Bumps vitest hookTimeout to match testTimeout (15s) so parallel fork workers do not hit the default 10s hook limit under CPU contention. Adds 35 new tests (26 unit for CacheService, 9 integration for cached endpoints) covering TTL expiry, inflight dedup, stale-on-error, namespace invalidation, entry-cap safety guard, and write-path invalidation end-to-end through Express routes. |
||
|
|
8e1b9826cf |
fix(api): add tiered rate limiting to prevent polling lockouts (#460)
* fix(api): add tiered rate limiting to prevent polling lockouts Replace the single global rate limiter (100 req/min/IP) with a tiered system that separates high-frequency polling endpoints from standard API traffic: - Polling tier (300/min): /stats, /system/stats, /stacks/statuses, /metrics/historical, /health, /meta, /auth/status, /auth/sso/providers, /license. Exempt from the global limiter but governed by their own safety net to prevent resource exhaustion. - Standard tier (200/min): All other endpoints, raised from 100. - Webhook tier (500/min): POST /webhooks/:id/trigger, dedicated limiter for CI/CD platforms sharing datacenter IPs. - Auth tier: Unchanged (5-10 attempts / 15 min). Enterprise adaptations: - Authenticated requests keyed by user session (JWT sub/username) instead of IP, preventing shared NAT/VPN environments from pooling budgets. - Internal node-to-node traffic (node_proxy tokens) bypasses all rate limiters entirely. Includes comprehensive stress tests (21 cases) validating tier separation, node proxy bypass, and per-user keying. * chore(deps): bump axios to 1.15.0 to fix SSRF vulnerability Addresses GHSA-3p68-rc4w-qgx5 (NO_PROXY hostname normalization bypass). |
||
|
|
670a429168 |
fix(fleet): resolve getSenchoVersion crash in Docker containers (#396)
* fix(fleet): resolve getSenchoVersion crash in Docker containers Replace the runtime __dirname walk (which fails in Docker because the root package.json is absent from the final image) with a build-time version injection. A prebuild script reads the root package.json and generates src/generated/version.ts with the version baked in as a constant. The compiled output carries the correct version regardless of the container's filesystem layout. Retains a filesystem walk fallback for dev environments where the prebuild hook may not have run. * fix(fleet): skip postinstall version generation when script is absent The Docker prod-deps stage copies only package.json (no source files) before running npm ci --omit=dev. The postinstall hook now checks for the script file before executing, so it no-ops in stages where scripts/generate-version.js has not been copied. |
||
|
|
87b5908288 |
feat(fleet): add remote node update management (#353)
Add the ability to check for outdated nodes and trigger over-the-air updates from Fleet View. Nodes self-update by pulling the latest Docker image and recreating their container via the "last breath" pattern. Backend: - SelfUpdateService: self-container identification via HOSTNAME + Docker Compose labels, triggers pull + force-recreate - CapabilityRegistry: runtime capability disabling via disableCapability() - POST /api/system/update (202 + deferred self-update) - GET /api/fleet/update-status (version comparison across fleet) - POST /api/fleet/nodes/:nodeId/update (single node) - POST /api/fleet/update-all (bulk remote update) - In-memory update tracker with 5-min timeout Frontend: - Node Updates modal with summary stats, search filter, table layout, per-node Update buttons, and bulk Update All - Version badges and update-available indicators on node cards - ReconnectingOverlay for local node updates (polls /api/health) - 5s fast-poll when any node is actively updating - UpdateStatusBadge shared component for consistent badge rendering Requires Skipper (Pro) tier. Nodes must be deployed via Docker Compose with Docker socket access. |
||
|
|
dcc2ab17f8 |
chore(deps): bump ldapts from 4.2.6 to 8.1.7 in /backend (#306)
Bumps [ldapts](https://github.com/ldapts/ldapts) from 4.2.6 to 8.1.7. - [Release notes](https://github.com/ldapts/ldapts/releases) - [Changelog](https://github.com/ldapts/ldapts/blob/main/CHANGELOG.md) - [Commits](https://github.com/ldapts/ldapts/compare/4.2.6...v8.1.7) --- updated-dependencies: - dependency-name: ldapts dependency-version: 8.1.7 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
244c83a0c3 |
feat(registries): add private registry credential management (Team Pro) (#240)
Add centralized credential storage for private Docker registries with support for Docker Hub, GHCR, AWS ECR, and self-hosted registries. - New `registries` table with AES-256-GCM encrypted secrets - RegistryService with CRUD, test connectivity, Docker config generation - 5 API endpoints gated by requireTeamPro + requireAdmin - ComposeService injects credentials via temp DOCKER_CONFIG on deploy/pull - ImageUpdateService passes stored credentials for private registry checks - AWS ECR just-in-time token refresh via @aws-sdk/client-ecr - RegistriesSection UI in Settings Hub with type-aware form - Documentation with screenshots |
||
|
|
31e1795af0 |
feat(scheduled-ops): add scheduled operations for Team Pro users (#231)
Adds the ability to schedule recurring Docker operations (stack restarts, fleet snapshots, system prunes) via cron expressions with full execution history logging. Includes Run Now for on-demand execution. |
||
|
|
bd4008f509 |
feat: SSO & LDAP authentication for Team Pro (#209)
* feat: SSO & LDAP authentication for Team Pro Add SSO integration allowing Team Pro users to authenticate via LDAP/Active Directory, Google, GitHub, and Okta identity providers. SSO works alongside password authentication with auto-provisioning and role mapping. - LDAP bind+search authentication with group-based role mapping - OIDC/OAuth2 flows with PKCE and CSRF protection for Google, GitHub, Okta - Auto-provisioning: first SSO login creates a Sencho account automatically - Role mapping via LDAP group membership or OIDC JWT claims - SSO settings UI in Settings → SSO with per-provider config and test connection - SSO login buttons on login page with LDAP toggle - Environment variable seeding for infrastructure-as-code workflows - Secrets encrypted at rest via CryptoService (AES-256-GCM) - Seat limit enforcement during auto-provisioning - Full documentation: feature docs, quickstart guides, env var reference * fix: resolve ESLint errors in SSO feature - Remove unnecessary escape characters in regex character classes - Remove unused `issuer` variable from OIDC callback handler - Fix setState-in-effect lint error in Login.tsx by using useState initializer - Suppress set-state-in-effect for SSOSection fetch pattern (matches existing codebase convention) |
||
|
|
1d482c6079 |
chore(deps-dev): bump @types/http-proxy-middleware in /backend (#165)
Bumps [@types/http-proxy-middleware](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/http-proxy-middleware) from 0.19.3 to 1.0.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/http-proxy-middleware) --- updated-dependencies: - dependency-name: "@types/http-proxy-middleware" dependency-version: 1.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
5edbf1e42c |
chore: add BSL 1.1 license (#132)
Closes #100 |
||
|
|
68a1dbe671 |
chore(deps): upgrade major dependencies (ESLint 10, recharts 3, TS 6, Vite 8) (#130)
* chore(deps): upgrade ESLint 9 → 10 with plugin compatibility fixes - eslint: ^9 → ^10.0.0 (backend + frontend) - @eslint/js: ^9 → ^10.0.0 (backend + frontend) - eslint-plugin-react-refresh: ^0.4.24 → ^0.5.2 (ESM, config factory API) - Update frontend eslint.config.js: destructured import for react-refresh, call configs.vite() as factory function (0.5 API change) - Downgrade new ESLint 10 rules (no-useless-assignment, preserve-caught-error) to warnings for existing code patterns - eslint-plugin-react-hooks stays at 7.0.1 (stable) with --legacy-peer-deps until a stable release adds ESLint 10 peer support * chore(deps): upgrade recharts 2.x to 3.8 with chart.tsx type fixes recharts 3.x moved Tooltip/Legend props to context-based API. Updated chart.tsx to use explicit prop interfaces with internal recharts type imports (LegendPayload, TooltipPayload, TooltipFormatter). * chore(deps): upgrade TypeScript 5.9 to 6.0 - Remove deprecated baseUrl from frontend tsconfig (paths works standalone in TS 6) - Add react-is dependency required by recharts 3.x at build time - Backend and frontend both compile and lint cleanly * chore(deps): upgrade Vite 7.3 to 8.0 and @vitejs/plugin-react to 6.0 Vite 8 replaces Rollup+esbuild with Rolldown, significantly improving build speed (~2s vs ~18s). No config changes required. * fix(ci): add .npmrc with legacy-peer-deps for CI and Docker builds typescript-eslint@8.x requires typescript <6.0.0 and eslint-plugin-react-hooks@7.0.1 requires eslint <=9. Until upstream packages release compatible versions, legacy-peer-deps is needed. * docs: add logo assets and re-ignore CLAUDE.md * fix(ci): copy .npmrc into prod-deps Docker stage The prod-deps stage also runs npm ci with backend/package.json but was missing the .npmrc needed to bypass peer dep conflicts. |
||
|
|
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 |
||
|
|
ce50db0fde |
security: pre-release hardening, automated testing, and production readiness
SECURITY (critical fixes):
- Add authMiddleware to /api/system/console-token (was publicly accessible)
- Validate api_url on node create/update to prevent SSRF (rejects localhost/loopback)
- Add rate limiting (5 req/15 min/IP) to /api/auth/login and /api/auth/setup
- Fix path traversal in env_file resolution — absolute/escaping paths rejected
- Add stack name validation to GET routes (was only on PUT/POST)
- Add helmet security headers middleware
- Restrict CORS to FRONTEND_URL in production
PRODUCTION READINESS:
- Add GET /api/health public endpoint + HEALTHCHECK in Dockerfile
- Add SIGTERM/SIGINT graceful shutdown handler (drains connections, closes DB)
- Run container as non-root sencho user in Dockerfile
QUALITY:
- Fix 4 silent empty catch{} blocks in EditorLayout (now show toast.error)
- Connect ErrorBoundary to root App in main.tsx
- Replace WebSocket.Server with named WebSocketServer import (ESM compat)
TESTING (new automated test suite):
- Install Vitest; 38 backend tests across 4 suites covering validation utilities,
health endpoint, auth middleware, login flows, SSRF protection, and path traversal
- Extract isValidStackName/isValidRemoteUrl/isPathWithinBase to utils/validation.ts
- Playwright E2E scaffolding: auth, stacks, nodes specs + shared login helper
- CI: run Vitest + ESLint on every PR
|
||
|
|
322e717514 |
feat(settings): harden settings API and overhaul SettingsModal
Security: - Strip auth credential keys (auth_username, auth_password_hash, auth_jwt_secret) from GET /api/settings response - Add allowlist guard to POST /api/settings — rejects unknown or auth-namespace keys with a 400 Backend: - Add PATCH /api/settings bulk endpoint with Zod schema validation (type coercion, range checks, URL format) and atomic SQLite transaction - Add system_state table — moves last_janitor_alert_timestamp out of global_settings; adds getSystemState/setSystemState on DatabaseService - Add metrics_retention_hours and log_retention_days configurable settings; MonitorService reads both dynamically each evaluation cycle - Add cleanupOldNotifications(days) to DatabaseService, called each cycle Frontend: - Replace single isLoading flag with per-operation states (isSavingSystem, isSavingDeveloper, isSavingPassword, isSavingRegistry, isSavingAgent/isTestingAgent per agent type) - Add skeleton loader that blocks interaction until fetchSettings resolves - Explicit key-picking in fetchSettings — auth keys cannot enter state - Unsaved-changes amber dot on System Limits and Developer sidebar items - Separate saveSystemSettings / saveDeveloperSettings — no cross-tab clobber - Developer tab gains Data Retention section (metrics hours, log days) - All settings saves use new PATCH /api/settings endpoint |
||
|
|
c91c9ed7fd |
refactor: pivot from ssh proxy to distributed api model
- Delete SSHFileAdapter, IFileAdapter, LocalFileAdapter (SSH/SFTP stack)
- Remove ssh2, ssh2-sftp-client dependencies; add http-proxy-middleware, http-proxy
- NodeRegistry: remote nodes no longer use Dockerode TCP; new getProxyTarget() returns {apiUrl, apiToken}
- NodeRegistry.testConnection: remote nodes use HTTP GET /api/auth/check instead of docker.info()
- DatabaseService: Node interface swaps SSH/TLS fields for api_url + api_token; legacy columns preserved for DB compat
- FileSystemService: reverted to clean local-only fs.promises; adapter pattern fully removed
- ComposeService: executeRemote() and SSH log streaming deleted; local-only execution remains
- index.ts: add /api/auth/generate-node-token endpoint (long-lived JWT, scope:node_proxy)
- index.ts: authMiddleware now accepts Bearer token in addition to cookie (Sencho-to-Sencho auth)
- index.ts: remote HTTP proxy middleware intercepts all /api/ requests for remote nodes, strips x-node-id, injects Authorization header, proxies to api_url
- index.ts: WS upgrade handler proxies WebSocket connections for remote nodes via http-proxy wsProxyServer
- NodeManager.tsx: form reduced to Name, API URL, API Token; Generate Node Token button added inline
- NodeContext.tsx: Node interface updated to api_url/api_token
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||
|
|
8c51198468 | feat: Remote Nodes Wiring & SSH Adapters | ||
|
|
1676dc22df | feat: implement app templates storefront and deployment engine | ||
|
|
9cdf273b8c | feat: add alert management functionality in NotificationSettingsModal and implement stacks API endpoint | ||
|
|
c6eb9d76e7 |
feat: add Notification Settings Modal and Stack Alert Sheet components
- Implemented NotificationSettingsModal for configuring notification agents (Discord, Slack, Webhook) and global settings. - Added StackAlertSheet for managing stack-specific alert rules with metrics, thresholds, and cooldowns. - Introduced reusable UI components: DropdownMenu, Popover, Select, Sheet, Slider, and Switch. - Enhanced Tabs component by removing unnecessary client directive. |
||
|
|
fcf0c9f983 | feat: add HostConsole component for interactive terminal access and integrate into EditorLayout | ||
|
|
e94b706a9f | refactor: enhance DockerController with smart fallback for legacy containers; add YAML parsing support and improve container retrieval logic | ||
|
|
293f9cef26 | Initial commit: Sencho V1 complete with Auth and Dockerization |