Skip to main content

stacks-dashboard

Use when building or customizing the Stacks admin dashboard, including dashboard pages, model management views, analytics widgets, commerce dashboards, content management, settings panels, deployment monitoring, job/queue management, or the 409 built-in dashboard components. Covers the dashboard system at storage/framework/defaults/.

소스 정보

저장소
stacksjs/stacks
최근 소스 활동
2026년 10월 7일 18:12
감지된 SKILL.md 언어
영어
스타
631
포크
20

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
2 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
stacks-dashboard
description
Use when building or customizing the Stacks admin dashboard, including dashboard pages, model management views, analytics widgets, commerce dashboards, content management, settings panels, deployment monitoring, job/queue management, or the 409 built-in dashboard components. Covers the dashboard system at storage/framework/defaults/.
license
MIT
compatibility
Bun >= 1.3.0, TypeScript
allowed-tools
Read Edit Write Bash Grep Glob
# Stacks Dashboard The Stacks admin dashboard provides a full-featured admin panel with 100+ route views, 409 components, and a multi-section layout. ## Key Paths - Dashboard components: `storage/framework/defaults/resources/components/Dashboard/` - Dashboard route views: `storage/framework/defaults/views/dashboard/` - Dashboard layouts: `storage/framework/defaults/views/dashboard/layouts/` - Dashboard actions: `storage/framework/defaults/app/Actions/Dashboard/` - Dashboard page endpoints: `storage/framework/defaults/routes/dashboard-api.ts` - Dashboard navigation registry: `storage/framework/defaults/resources/functions/dashboard/sidebar.ts` - Configuration: `config/ui.ts` ## Dashboard Sections Dashboard route views are mounted at the dashboard server root. Do not prefix page links with `/dashboard`. The `/api/dashboard/*` prefix is reserved for dashboard data Actions. ### Analytics and Monitoring - `/` - main dashboard overview - `/analytics` - analytics hub and chart navigation - `/analytics/web`, `/analytics/pages`, `/analytics/referrers` - HTTP analytics - `/analytics/countries`, `/analytics/devices`, `/analytics/browsers` - audience breakdowns - `/analytics/events`, `/analytics/blog`, `/analytics/marketing` - domain analytics - `/requests` - captured HTTP request metrics - `/errors`, `/monitoring/errors` - error tracking and analysis - `/jobs`, `/jobs/history` - background job monitoring - `/queue` - queue management and metrics - `/queries`, `/queries/slow`, `/queries/history` - query analysis ### Commerce - `/commerce/dashboard` - commerce overview - `/commerce/pos` - point of sale - `/commerce/products` - product management - `/commerce/orders` - order management and processing - `/commerce/customers` - customer profiles and history - `/commerce/payments` - payment tracking - `/commerce/coupons` - coupon and promotion management - `/commerce/gift-cards` - gift card management - `/commerce/categories`, `/commerce/manufacturers`, `/commerce/units` - catalog metadata - `/commerce/variants`, `/commerce/reviews`, `/commerce/taxes` - catalog operations - `/commerce/waitlist/products`, `/commerce/waitlist/restaurant` - waitlists - `/commerce/delivery` and `/commerce/delivery/*` - delivery, shipping, driver, and license management ### Content Management - `/content/dashboard` - content overview - `/content/posts` - blog post CRUD - `/content/pages` - page management - `/content/authors` - author profiles - `/content/categories` - content categorization - `/content/tags` - tag management - `/content/comments` - comment moderation - `/content/files`, `/content/blog`, `/content/seo` - files, blog operations, and SEO ### The file manager's two layers (stacksjs/stacks#2577) Worth knowing before adding anything to it, because the split is not obvious from the endpoints: - **Storage operations** map to a `StorageAdapter` method and go straight to the disk: list, upload, create folder, rename, visibility, duplicate, delete. - **Metadata** - favourites and tags - has nowhere to live on a disk (extended attributes do not survive a copy; S3 object metadata is set at write time, so starring a 2 GB video would rewrite 2 GB). It lives in `storage_items`, keyed by `(disk, path)`, written by `PUT /files/favorite` and `PUT /files/tags`. **The disk is authoritative and the table is advisory.** The listing comes from the disk and rows are joined onto it, so a path with no row is a file with nothing recorded - which is most files. Renames and deletes made THROUGH the dashboard reconcile eagerly (a folder is a prefix update, because moving a folder moves everything under it); a completed listing sweeps rows for paths it did not see, which is free because the walk already enumerated them. A TRUNCATED listing sweeps nothing - it has not proved a path is absent. A file renamed outside the dashboard loses its metadata, and that is by design: a rename and a copy-then-delete are the same two events to a bucket listing, so reconciling would be guessing. ### The media pipeline (stacksjs/stacks#2578) None of the three things an upload might need can happen inside the request: a transcode is minutes, a vision call is a round trip to a third party. So an upload dispatches and the dashboard shows state. - `storage_item_tasks`, one row per `(disk, path, kind)`, kind being `optimize` (images, via `ts-images`), `transcode` (video, via `ts-videos`), `tag` (a vision model) or `preview` (the file manager thumbnail, #308). They succeed and fail independently, which is why this is not a column on `storage_items` - a video whose transcode finished and whose tagging failed is a normal state. - `dispatchDashboardFileTasks` decides from the CONTENT TYPE what a file needs. Most uploads are documents and get nothing. A transcode waits for a video profile, because the ladder is derived from the source dimensions. - A dispatch failure is RECORDED, not thrown: a queue that is down leaves a visible failure rather than an upload that fails or a file that is silently never processed. - `runTask` owns the queued -> running -> done/failed transitions so the three jobs cannot disagree about them. It rethrows after recording, because the row and the queue answer different questions - the queue decides whether to retry, the row is what somebody looking at the file sees. - `POST /files/reprocess` re-runs everything, or the kinds you name. - `preview` is decided by `file-preview.ts`, from the type the file CLAIMS (the disk's answer, or the extension when that is `octet-stream`), and the job checks the bytes agree before drawing. Images, WAV waveforms and TrueType specimens become `.variants/<path>/preview.png`; text becomes a `preview.txt` snippet. SVG, video, compressed audio, PDF, CFF/WOFF fonts, HEIC and TIFF are recorded `skipped` with the reason in `error` - no homegrown renderer draws them, and SVG is never rasterized. Never add a renderer that shells out: that is the rule the skips exist to keep. - `skipped` is terminal: `runTask` records a thrown `StorageTaskSkipped` without rethrowing, so the queue does not retry it, and `aggregateTaskState` ignores it unless nothing else applied. Derivatives are written back to the same disk under `.variants/<path>/`. The leading dot keeps them out of the listing, which skips hidden components - a folder of thirty derivatives beside every photo makes the browser useless. A rename or delete through the dashboard moves or removes that folder with the file, after the storage operation succeeds and best effort, since the task rows already followed and a missing derivative is regenerated by reprocessing. ### Remote commands (stacksjs/stacks#960) Running a configured operation on a configured host over SSH: the request names a host KEY and a command KEY, both from `config/remote.ts`, so there is nothing to escape and no shell to reach. The interactive terminal beside it is covered below. Four things make it safe to expose, and each is a rule to keep: - **Host keys are verified.** `StrictHostKeyChecking=yes` against the host's declared `knownHosts`. Do NOT reuse `sshExec` from `@stacksjs/ts-cloud` for anything long-lived: it disables host key checking on purpose, for boxes a minute old whose keys cannot be known. - **Hosts and commands come from config, never the request.** A `RemoteCommand` carries an `argv` ARRAY that is never interpolated. - **The routes do NOT use `guard()`.** That helper drops auth entirely under `APP_ENV=local|development|test`, which here would be an unauthenticated command runner on any dev machine on the network. They use `authenticatedGuard`, and `remote-routes.test.ts` asserts it. - **Authorization fails CLOSED.** The `run-remote-command` gate receives the host and command keys; with no gate defined, every run is refused. This is the opposite of the websocket authenticator in `@stacksjs/realtime`, which proceeds when none is installed. Runs are recorded before AND after - a run recorded only on completion loses the command that hung and the one whose process died with the box. The audit sink writes to the application log rather than the dashboard's own database, which is the thing an operator with dashboard access could edit. ### Remote terminals (stacksjs/stacks#960) `/remote` in the dashboard also opens an interactive shell on a host that sets `terminal: true`. Every command-runner rule above holds, plus: - **A host opts in, and a gate decides.** `terminal: true` on the host, and the `open-remote-terminal` gate (receives the host key). No gate, no session. - **A session belongs to the user who opened it.** 128-bit id; another user naming it gets the same 404 as a missing id. - **It does not outlive its purpose.** 15 minutes without input, 2 hours in all, 30 seconds after the last viewer leaves: the remote shell is killed, never left running. At most 4 per user. - **What is typed is audited line by line** in the application log, so hosts should take keys and passwordless sudo, not typed passwords. Transport: a local PTY (`Bun.spawn({ terminal })`, which Bun now has) running `ssh -tt` with the pinned host key, so a browser resize reaches the remote side. Output reaches the browser as server-sent events, base64 and sequence-numbered for replay on reconnect; input and resize are small POSTs that each pass auth, CSRF and the session-owner check. Sessions live in the server process. In the browser it is `<stacks-remote-terminal host="...">` (xterm.js), from `functions/remote-terminal-element.ts`. Two rules that module follows: - **Nothing browser-only at module top level.** `defaults/functions` is also auto-imported on the SERVER; a top-level `class extends HTMLElement` threw there and took every function in the barrel down. xterm and the class load inside `registerRemoteTerminal()`. - **Survive being moved.** stx's `:if` detaches and re-inserts the SAME nodes, so the element acts on a removal only if it is still detached a tick later, and starts a fresh session whenever it is attached again. `remote-terminal.test.ts` runs a real session through a throwaway user-level `sshd` on a loopback port when one is installed (macOS and the CI image both have one), including a host whose key does not match the pinned one. **There is no ffmpeg.** #2578 asked whether video was in scope given the external binary, its licensing and its provisioning; `@stacksjs/video` is built on `ts-videos`, which encodes itself, so that question was already answered. Tags go through `taggables` + `taggable_models` with `taggable_type = 'storage_items'` - the trait the CMS already uses. Do NOT declare a `belongsToMany` to the `Tag` model for this: `taggable_models.tag_id` resolves against `taggables`, which is a different table from `tags`. ### Data Management - `/data/dashboard` - data overview - `/data/users` - user management - `/data/subscribers` - subscriber management - `/data/teams` - team management - `/data/activity` - persisted activity - `/models`, `/models/{model}` - generic model registry and explorer - `/notifications/dashboard`, `/notifications/history` - notification operations ### Mail - `/inbox` - inbound messages from the configured mailbox provider - `/inbox/activity` - inbound and outbound delivery activity - `/inbox/captured` - outbound messages captured by the local log mail driver - `/inbox/settings` - mailbox display and behavior preferences Captured mail uses `GET /api/dashboard/email/captured` and `GET /api/dashboard/email/captured/{id}`. Read captures through the shared parser in `Actions/Dashboard/Email/captured-mail.ts`; do not scrape files in an STX component or duplicate the log-driver format. Render all inbound and captured HTML through `Email/EmailBodyPreview.stx`. It owns the sandboxed `srcdoc` iframe, restrictive content policy, and no-referrer boundary. Never inject message HTML into the dashboard document. ### Marketing - `/marketing/campaigns` - campaign management - `/marketing/lists` - email list management - `/marketing/social-posts` - social post management - `/marketing/reviews` - marketing review workflows ### Library - `/library/components` - component browser - `/functions` - function registry and scaffold - `/releases` - release management - `/packages`, `/dependencies` - package and dependency inspection ### Settings - `/settings` - typed `config/*.ts` browser and editor - `/settings/appearance` - dashboard appearance - `/settings/billing` - account billing - `/settings/mail` - mail configuration - `/environment` - environment summary - `/access-tokens` - access-token management - `/cloud`, `/dns`, `/mailboxes` - infrastructure-specific settings ### Deployments - `/deployments` - deployment history, deployment controls, the custom TypeScript deploy-script editor, and visibility-aware live terminal output - `/deployments/{id}` - one persisted Deployment model record The deployment page composes `DeploymentList`, `DeploymentTable`, `DeploymentPreviewDialog`, `DeployScript`, and `LiveTerminalOutput`. The Preview action first collects the environment and optional domain, then calls the guarded `POST /api/dashboard/deployments/preview` Action. That Action runs the native `buddy deploy --dry-run --json` planner and returns its versioned, non-mutating plan. The dialog renders the ordered operations and resolved sites before the user may continue to the separate real deployment confirmation. Script reads and atomic writes use `GET|PUT /api/dashboard/deployments/script`. The terminal uses `GET /api/dashboard/deployments/terminal` and pauses polling while the document is hidden. Do not create separate `/deployments/scripts` or `/deployments/live-terminal` pages. Deployment recovery uses `POST /api/dashboard/deployments/rollback/preview` followed by `POST /api/dashboard/deployments/rollback`. The preview must run the native `buddy deploy:rollback --dry-run` path successfully, and execution must re-run that preview, compare its revision, and require the typed target environment confirmation. Never implement rollback by editing release links, restarting services directly, or guessing a prior release from Deployment model rows. Deployment rows are application history. Preserved releases and activation are owned by ts-cloud. ### Operations control plane The Operations sidebar entry deliberately remains one item. Section navigation lives in `Dashboard/Operations/OperationsNavigation.stx`: - `/operations/changes` - unified change review, active work, release approvals - `/operations/scheduler` - registered task runs and persisted pause state - `/operations/recovery` - destinations, policies, recovery points, restore drills - `/operations/migrations` - model diff, schema effects, ledger reconciliation - `/operations/incidents` - native alerts, health rules, ownership, silence state - `/operations/audit` - append-only operator events and correlations Operational state belongs in the ts-cloud control plane initialized by `Operations/control-plane.ts`. Use its stores for durable operations, events, releases, approvals, alerts, backups, actors, and environments. Do not create a parallel dashboard-only JSON file or duplicate those entities in application models. Application domain records still follow the normal `app/Models` and `useApi` convention. Use dashboard Actions for aggregate operational views and guard every route in `dashboard-api.ts`. Every mutating operator action must resolve the authenticated actor and append or correlate a control-plane event. Use `trackOperatorOperation()` for bounded synchronous work and the native durable queue for long-running backup, restore, or provider work. Empty states must reflect real absence of configuration or events. Never seed operational pages with sample incidents, releases, backups, or health data. Migration execution must start from `previewPendingMigrations()`, audit the ledger against live schema effects, hash the reviewed plan, and recheck it immediately before `buddy migrate`. Only `reconcileMigrationLedger()` may repair provable ledger drift. Partial and unverifiable migrations require human review and must not be silently recorded. ### Utilities - `/health`, `/insights`, `/logs` - operational health and logs - `/servers`, `/serverless`, `/realtime` - runtime infrastructure - `/management/permissions` - RBAC management - `/kanban` - model-backed board management - `/ci`, `/buddy` - CI and Buddy workflows ## Dashboard Components (250+) ### Layout Components - `Navbar` - top navigation bar - `Sidebar` - fixed desktop side navigation provided by the STX runtime - `MobileSidebar` - responsive drawer around the same sidebar content - `DashboardLayout` - reusable layout wrapper ### UI Components - Buttons, Modals, Toasts, Alerts, Dropdowns - Tables with sorting, filtering, pagination - Forms with validation - Charts and analytics widgets - File upload components - Rich text editors ### Action controls - Use `Dashboard/UI/Button.stx` for every dashboard action. Do not add page-local primary, secondary, success, warning, or danger button styles. - The `primary` variant is the canonical Deployment `Deploy` treatment: `bg-gradient-to-b from-blue-500 to-blue-600`. - Keep native buttons only for controls whose visual state is their meaning, such as tabs, sort headers, color choices, and full-surface modal backdrops. - Use `variant="secondary"` for supporting actions and `variant="danger"` for destructive confirmation actions. - Use `tag="a"` whenever `href` is reactive, for example `<Button tag="a" :href="detailsPath()">Open details</Button>`. Server rendering cannot infer an anchor from a client-only reactive URL. - When a submit action lives in a shared Modal footer, give the form a stable `id` and associate the action with `<Button type="submit" form="form-id">`. Do not duplicate the footer inside the form or use script-driven submission. - Prefer component events and named slots over string callback props or `data-action` markers. A `data-action` attribute is only valid when an active host integration consumes that exact action. ### Feature Components - `ProductForm`, `ProductList`, `ProductVariants` - `OrderTable`, `OrderDetail`, `OrderStatusUpdate` - `UserTable`, `UserForm`, `UserProfile` - `PostEditor`, `PostList`, `PostPublish` - `CouponForm`, `CouponList` - `EmailCompose`, `EmailList`, `EmailDetail` - `DeploymentList`, `DeploymentTable`, `DeploymentDetail`, `DeployScript`,
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기