mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 20:00:08 +00:00
801a098a5b
* 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.
105 lines
5.9 KiB
Plaintext
105 lines
5.9 KiB
Plaintext
---
|
|
title: Editor
|
|
description: Edit compose files and environment variables, and manage containers directly from the dashboard.
|
|
---
|
|
|
|
Selecting a stack opens the editor view, a two-column layout. The left column shows the command center (stack controls, containers, and an embedded terminal). The right column holds the Monaco code editor.
|
|
|
|
<Frame>
|
|
<img src="/images/editor/editor-overview.png" alt="Editor view showing command center on the left and Monaco editor on the right" />
|
|
</Frame>
|
|
|
|
## Files tab
|
|
|
|
The **Files** tab gives you a browseable directory tree for everything inside the stack's folder. Community users can view text files in read-only mode. Skipper and above can upload, download, edit and save, create folders, and delete files. Protected files (`compose.yaml`, `.env`, and their variants) redirect to their dedicated editor tabs when clicked.
|
|
|
|
For full details on permissions, viewing limits, upload caps, and troubleshooting, see the [Stack File Explorer](/features/stack-file-explorer) guide.
|
|
|
|
## Compose file editor
|
|
|
|
The right panel shows your `compose.yaml` with full syntax highlighting. By default the editor is **read-only** to prevent accidental changes.
|
|
|
|
Click **Edit** to enter edit mode. Your changes are unsaved until you explicitly save them.
|
|
|
|
### Save options
|
|
|
|
The primary action button is **Save & Deploy**, which writes the file to disk and immediately runs `docker compose up -d`. A dropdown arrow next to it reveals additional options:
|
|
|
|
| Action | What it does |
|
|
|--------|--------------|
|
|
| **Save & Deploy** | Writes the file, then runs `docker compose up -d` to apply changes immediately. |
|
|
| **Save Only** | Writes the file to disk without restarting any containers. |
|
|
| **Discard Changes** | Reverts the editor to the last saved version. Unsaved changes are lost. |
|
|
|
|
## Environment file editor
|
|
|
|
Click the **.env** tab to switch to the environment file editor. If your `compose.yaml` references multiple env files (via `env_file:`), a dropdown lets you select which file to edit.
|
|
|
|
The `.env` editor has the same save/discard controls as the compose editor. Changes take effect the next time the stack is deployed.
|
|
|
|
<Note>
|
|
Sencho reads the `env_file:` paths from your `compose.yaml` to discover available env files. If no `env_file:` is declared, a default `.env` in the stack directory is used.
|
|
</Note>
|
|
|
|
## Container panel
|
|
|
|
The left column lists all containers that belong to the selected stack under the **CONTAINERS** heading. Each container shows:
|
|
|
|
- **Status badge** - `running`, `exited`, `starting`, or `unhealthy` (hover for detailed status text)
|
|
- **Live stats** - CPU %, RAM usage, and network I/O displayed inline, updated every few seconds
|
|
|
|
### Container actions
|
|
|
|
Each container row has action buttons on the right side:
|
|
|
|
| Button | What it does |
|
|
|--------|--------------|
|
|
| **Open App** | Opens the container's web UI in a new tab (uses the detected web port). Only shown when a port is mapped. |
|
|
| **View Live Logs** | Opens a live log stream modal for that container. Only available when the container is running. |
|
|
| **Open Bash Terminal** | Opens an interactive bash session inside the container. Admin only, only available when running. |
|
|
|
|
<Frame>
|
|
<img src="/images/editor/container-actions.png" alt="Container row showing status badge, live stats, and action buttons" />
|
|
</Frame>
|
|
|
|
## Embedded terminal
|
|
|
|
Below the container list, an embedded terminal streams the combined output from the stack's containers. This provides a persistent view of container logs without opening a separate modal.
|
|
|
|
The terminal supports search (find text within the log output) and export (download the current log buffer).
|
|
|
|
### Container terminal (exec)
|
|
|
|
The terminal modal from the container action buttons gives you an interactive bash shell inside a running container, equivalent to `docker exec -it <id> bash`. It uses a full terminal emulator with colour support, tab completion, and automatic resizing when you resize the browser window.
|
|
|
|
#### Access requirements
|
|
|
|
- **Admin role required.** Only admin users see the terminal button in the container actions. Non-admin users cannot open a shell session.
|
|
- **Container must be running.** The button is disabled for stopped containers, and the backend will reject exec attempts against non-running containers.
|
|
- **API token restrictions.** API tokens with `read-only` or `deploy-only` scope cannot open exec sessions. Only `full-admin` API tokens are permitted.
|
|
|
|
<Frame>
|
|
<img src="/images/editor/container-exec-modal.png" alt="Container exec modal showing a connected bash session with a root shell prompt" />
|
|
</Frame>
|
|
|
|
#### Shell selection
|
|
|
|
Sencho tries `/bin/bash` first. If bash is not available inside the container, it automatically falls back to `/bin/sh`. This happens transparently; no user action is needed.
|
|
|
|
<Warning>
|
|
The container terminal requires the container to have `bash` or `sh` installed. Minimal base images (e.g. `scratch`, `distroless`) that ship no shell will not work with container exec.
|
|
</Warning>
|
|
|
|
#### Troubleshooting
|
|
|
|
| Symptom | Cause | Resolution |
|
|
|---------|-------|------------|
|
|
| "Container is not running" error | The container stopped between clicking the button and the exec starting | Start the container and try again |
|
|
| "Failed to start shell" error | The container image has no shell binary (`/bin/bash` or `/bin/sh`) | Use an image that includes a shell, or install one in your Dockerfile |
|
|
| Terminal connects but immediately shows "Session ended" | The shell process inside the container exited immediately | Check the container logs for errors; the container's entrypoint may be overriding the shell |
|
|
| Terminal is unresponsive | WebSocket connection dropped silently | Close the modal and reopen it to establish a new session |
|
|
|
|
## Log viewer
|
|
|
|
The log viewer (opened from the container action buttons) streams output from a single container in real-time using Server-Sent Events. Logs auto-scroll to the bottom as new lines arrive. Close the modal to stop the stream.
|