From cd3d7b23be444a8c085255b557ef2594e9b3fea5 Mon Sep 17 00:00:00 2001 From: Anso Date: Sun, 12 Apr 2026 19:57:38 -0400 Subject: [PATCH] feat(app-store): add port conflict indicator to deploy sheet (#533) Show a pulsating warning dot next to host ports that are already in use by a running container. Hovering over the dot reveals which Sencho-managed stack or external app occupies the port. Adds GET /api/ports/in-use endpoint that returns a map of bound host ports with ownership info, and a getPortsInUse method on DockerController that reuses the existing container-to-stack resolution logic. --- backend/src/index.ts | 13 ++++++ backend/src/services/DockerController.ts | 41 ++++++++++++++++++ docs/features/app-store.mdx | 6 +++ .../app-store/app-store-port-conflict.png | Bin 0 -> 58201 bytes frontend/src/components/AppStoreView.tsx | 41 ++++++++++++++++-- 5 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 docs/images/app-store/app-store-port-conflict.png diff --git a/backend/src/index.ts b/backend/src/index.ts index afba83b7..f31aff73 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -3167,6 +3167,19 @@ app.get('/api/containers', async (req: Request, res: Response) => { } }); +app.get('/api/ports/in-use', async (req: Request, res: Response) => { + try { + const fsService = FileSystemService.getInstance(req.nodeId); + const stacks = await fsService.getStacks(); + const dockerController = DockerController.getInstance(req.nodeId); + const portsInUse = await dockerController.getPortsInUse(stacks); + res.json(portsInUse); + } catch (error) { + console.error('[Ports] Failed to fetch ports in use:', error); + res.status(500).json({ error: 'Failed to fetch ports in use' }); + } +}); + // --- Label Routes (Skipper+) --- app.get('/api/labels', authMiddleware, async (req: Request, res: Response): Promise => { diff --git a/backend/src/services/DockerController.ts b/backend/src/services/DockerController.ts index 676c8531..1191f482 100644 --- a/backend/src/services/DockerController.ts +++ b/backend/src/services/DockerController.ts @@ -40,6 +40,11 @@ export interface ClassifiedImage { managedStatus: 'managed' | 'unmanaged' | 'unused'; } +export interface PortInUseInfo { + stack: string | null; + container: string; +} + export interface ClassifiedVolume { Name: string; Driver: string; @@ -662,6 +667,42 @@ class DockerController { return result; } + /** + * Returns a map of host ports currently bound by running containers, + * with ownership info (Sencho-managed stack name or external). + */ + public async getPortsInUse(knownStackNames: string[]): Promise> { + const [allContainers, projectToStack] = await Promise.all([ + this.docker.listContainers({ all: false }), + DockerController.resolveProjectNameMap(knownStackNames), + ]); + + const absDirToStack = DockerController.buildAbsDirMap(knownStackNames); + const knownStackSet = new Set(knownStackNames); + const resolvedBase = path.resolve(COMPOSE_DIR); + + const result: Record = {}; + + for (const container of allContainers as Array<{ Names?: string[]; Labels?: Record; Ports?: Array<{ PublicPort?: number }> }>) { + const stackDir = DockerController.resolveContainerStack( + container.Labels, projectToStack, knownStackSet, absDirToStack, resolvedBase, + ); + + const containerName = (container.Names?.[0] || '').replace(/^\//, ''); + + if (!Array.isArray(container.Ports)) continue; + + for (const port of container.Ports) { + if (!port.PublicPort || port.PublicPort <= 0) continue; + // First container to claim a port wins (avoids overwrites) + if (result[port.PublicPort]) continue; + result[port.PublicPort] = { stack: stackDir, container: containerName }; + } + } + + return result; + } + public async getContainersByStack(stackName: string) { const stackDir = path.join(COMPOSE_DIR, stackName); diff --git a/docs/features/app-store.mdx b/docs/features/app-store.mdx index 8b3ca619..2a3b0a90 100644 --- a/docs/features/app-store.mdx +++ b/docs/features/app-store.mdx @@ -48,6 +48,12 @@ Pre-filled with the template name in lowercase. You can change it; the same [nam For each exposed port, the sheet shows an editable **host port** field (left side) and the fixed **container port** (right side). Edit the host port to avoid conflicts with other services on the same machine. Ports must be in the valid range (1 to 65535); invalid values are highlighted and block deployment. +If a host port is already in use by a running container, a pulsating warning dot appears next to the port. Hover over the dot to see which application is using the port. If the application is managed by Sencho, the stack name is shown; otherwise the indicator reads "used by an external app". + + + Port conflict indicator showing port 8080 is used by heimdall + + ### Volumes For each container mount point, enter the **host path** where that data should be stored. Suggested defaults (e.g. `./config`, `./data`) are pre-filled. diff --git a/docs/images/app-store/app-store-port-conflict.png b/docs/images/app-store/app-store-port-conflict.png new file mode 100644 index 0000000000000000000000000000000000000000..5459e8241cd74d2fd6236a189f297f19ea7a8d65 GIT binary patch literal 58201 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

q9dhh1<+ddc6VJb}#% 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 OK{(ht+dRbirv3{S_>|}X literal 0 HcmV?d00001 diff --git a/frontend/src/components/AppStoreView.tsx b/frontend/src/components/AppStoreView.tsx index 3d9a5701..b55edbdb 100644 --- a/frontend/src/components/AppStoreView.tsx +++ b/frontend/src/components/AppStoreView.tsx @@ -12,6 +12,7 @@ import { cn } from '@/lib/utils'; import { apiFetch } from '@/lib/api'; import { useNodes } from '@/context/NodeContext'; import { useAuth } from '@/context/AuthContext'; +import { CursorProvider, CursorContainer, Cursor, CursorFollow } from '@/components/animate-ui/primitives/animate/cursor'; function isValidPort(value: string): boolean { if (!value) return true; @@ -47,6 +48,11 @@ interface Template { source?: string; } +interface PortInUseInfo { + stack: string | null; + container: string; +} + interface AppStoreViewProps { onDeploySuccess: (stackName: string) => void; } @@ -70,6 +76,7 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) { const [volVars, setVolVars] = useState>({}); const [customEnvs, setCustomEnvs] = useState>([]); const [newEnvKey, setNewEnvKey] = useState(''); + const [portsInUse, setPortsInUse] = useState>({}); const [newEnvVal, setNewEnvVal] = useState(''); useEffect(() => { @@ -139,6 +146,10 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) { .replace(/^-|-$/g, ''); setStackName(defaultName); + // Fetch currently bound host ports for conflict detection + setPortsInUse({}); + apiFetch('/ports/in-use').then(r => r.ok ? r.json() : {}).then(setPortsInUse).catch(err => console.error('[AppStore] Failed to fetch ports in use:', err)); + setIsSheetOpen(true); }; @@ -424,17 +435,41 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) { {selectedTemplate.ports.map((p, idx) => { const parts = p.split(':'); if (parts.length < 2) return null; + const hostPort = portVars[p] || ''; + const conflict = hostPort ? portsInUse[hostPort] : undefined; return (

{ const val = e.target.value.replace(/[^0-9]/g, ''); setPortVars(prev => ({ ...prev, [p]: val })); }} - className={cn("w-24 text-center font-mono", portVars[p] && !isValidPort(portVars[p]) && "border-destructive")} + className={cn("w-24 text-center font-mono", hostPort && !isValidPort(hostPort) && "border-destructive")} /> - : {parts[1]} + {conflict ? ( + + + : {parts[1]} + + + +
+ + +
+ + {conflict.stack !== null + ? <>Port {hostPort} used by {conflict.stack} + : <>Port {hostPort} used by an external app + } + +
+
+ + ) : ( + : {parts[1]} + )}
); })}