diff --git a/backend/src/index.ts b/backend/src/index.ts index 01cc4f44..afba83b7 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -5687,10 +5687,17 @@ app.post('/api/templates/deploy', authMiddleware, async (req: Request, res: Resp try { await fsPromises.access(stackPath); - return res.status(409).json({ - error: `A stack directory named '${stackName}' already exists. Please choose a different Stack Name.`, - rolledBack: false - }); + + if (await fsService.hasComposeFile(stackPath)) { + return res.status(409).json({ + error: `A stack directory named '${stackName}' already exists. Please choose a different Stack Name.`, + rolledBack: false + }); + } + + // Orphaned directory left by external deletion (e.g. Docker Desktop). + console.log(`[Templates] Cleaned up orphaned stack directory: ${stackName}`); + await fsService.deleteStack(stackName); } catch { // Directory does not exist; proceed with deploy } diff --git a/backend/src/services/FileSystemService.ts b/backend/src/services/FileSystemService.ts index 25a5d243..f721dcc6 100644 --- a/backend/src/services/FileSystemService.ts +++ b/backend/src/services/FileSystemService.ts @@ -34,7 +34,7 @@ export class FileSystemService { return new FileSystemService(nodeId); } - private async hasComposeFile(dir: string): Promise { + async hasComposeFile(dir: string): Promise { const composeFiles = ['compose.yaml', 'compose.yml', 'docker-compose.yaml', 'docker-compose.yml']; for (const file of composeFiles) { try { diff --git a/docs/features/app-store.mdx b/docs/features/app-store.mdx index af2fcbd2..8b3ca619 100644 --- a/docs/features/app-store.mdx +++ b/docs/features/app-store.mdx @@ -74,6 +74,8 @@ Below the template variables, an **Add Custom Variable** section lets you define A toast notification confirms success or shows the error message. Large images may take a few minutes to pull. +If a previous stack with the same name was removed outside of Sencho (for example, through the Docker CLI or Docker Desktop), the leftover directory is cleaned up automatically and the deploy proceeds as normal. + Deploying a template requires the `stack:create` permission. Users without this permission will see the Deploy button disabled.