mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-18 22:36:19 +00:00
feat: per-stack storage inventory and portability guardrails (#1399)
* feat: per-stack storage inventory and portability guardrails Add a Storage tab to the stack Anatomy panel that derives a per-stack mount inventory (bind mounts, named/anonymous volumes, tmpfs, docker socket; read-only vs read-write; host-path existence, type, and owner) from the effective Compose model, and classifies the stack as Portable, Partially portable, Node-bound, or Unknown with the reasons behind it. - New GET /api/stacks/:stackName/storage route (stack:read, Community), served by an on-demand, non-persisted service that renders the effective model, probes within-stack bind sources (symlink-escape aware), and runs the deterministic portability classifier. - Extend the effective-model parser additively with a full per-mount inventory and service-level tmpfs, leaving the rule-facing binds/namedVolumes byte-identical for the existing preflight rules. - New anonymous-volume preflight finding. - Admin-visible "no recent snapshot" warning that reuses the existing hub-local snapshot-coverage endpoint, plus a static note distinguishing config snapshots from application-data backups. - Surface storage assumptions in the Stack Dossier markdown export. - Gate the tab behind a new compose-storage capability on both sides. * docs: phrase the Storage tab availability as current behavior Replace the "older Sencho version / until it is updated" wording in the Storage feature page with present-tense, capability-based phrasing.
This commit is contained in:
@@ -12,6 +12,7 @@ import { StackActivityTimeline } from './stack/StackActivityTimeline';
|
||||
import StackDossierPanel from './stack/StackDossierPanel';
|
||||
import DriftPanel from './stack/DriftPanel';
|
||||
import PreflightPanel from './stack/PreflightPanel';
|
||||
import StoragePanel from './stack/StoragePanel';
|
||||
import EnvironmentPanel from './stack/EnvironmentPanel';
|
||||
import StackNetworkingPanel from './stack/StackNetworkingPanel';
|
||||
import { useNodes } from '@/context/NodeContext';
|
||||
@@ -93,6 +94,7 @@ export default function StackAnatomyPanel({
|
||||
const { hasCapability, activeNode } = useNodes();
|
||||
const doctorEnabled = hasCapability('compose-doctor');
|
||||
const networkingEnabled = hasCapability('compose-networking');
|
||||
const storageEnabled = hasCapability('compose-storage');
|
||||
const envInventoryEnabled = hasCapability('env-inventory');
|
||||
|
||||
const [gitSource, setGitSource] = useState<{ stack: string; info: GitSourceInfo; multiFile: boolean } | null>(null);
|
||||
@@ -370,6 +372,9 @@ export default function StackAnatomyPanel({
|
||||
</span>
|
||||
</TabsTrigger>
|
||||
)}
|
||||
{storageEnabled && (
|
||||
<TabsTrigger value="storage" data-testid="storage-tab" className="h-6 px-2.5 font-mono text-[10px] uppercase tracking-[0.18em]">Storage</TabsTrigger>
|
||||
)}
|
||||
</TabsList>
|
||||
</ScrollableTabRow>
|
||||
<div className="flex items-center gap-3 shrink-0">
|
||||
@@ -608,6 +613,11 @@ export default function StackAnatomyPanel({
|
||||
<PreflightPanel stackName={stackName} />
|
||||
</TabsContent>
|
||||
)}
|
||||
{storageEnabled && (
|
||||
<TabsContent value="storage" className="flex flex-col flex-1 min-h-0 mt-0">
|
||||
<StoragePanel stackName={stackName} />
|
||||
</TabsContent>
|
||||
)}
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user