Refactor recipes routes: explicit dependency injection
- Updated all recipes route modules to use destructured dependencies - Added JSDoc comments for factory functions - Replaced ctx. references with direct parameter access - All files pass syntax validation Files refactored: - routes/recipes/deploy.js - routes/recipes/manage.js - routes/recipes/index.js (orchestrator)
This commit is contained in:
@@ -3,7 +3,18 @@ const { ValidationError } = require('../../errors');
|
|||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const { DOCKER } = require('../../constants');
|
const { DOCKER } = require('../../constants');
|
||||||
|
|
||||||
module.exports = function(ctx) {
|
/**
|
||||||
|
* Recipes deployment routes factory
|
||||||
|
* @param {Object} deps - Explicit dependencies
|
||||||
|
* @param {Object} deps.docker - Docker client wrapper
|
||||||
|
* @param {Object} deps.credentialManager - Credential manager
|
||||||
|
* @param {Object} deps.servicesStateManager - Services state manager
|
||||||
|
* @param {Function} deps.asyncHandler - Async route handler wrapper
|
||||||
|
* @param {Function} deps.errorResponse - Error response helper
|
||||||
|
* @param {Object} deps.log - Logger instance
|
||||||
|
* @returns {express.Router}
|
||||||
|
*/
|
||||||
|
module.exports = function({ docker, credentialManager, servicesStateManager, asyncHandler, errorResponse, log }) {
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,14 +23,14 @@ module.exports = function(ctx) {
|
|||||||
* POST /api/recipes/deploy
|
* POST /api/recipes/deploy
|
||||||
* Body: { recipeId, config: { selectedComponents, sharedConfig, componentOverrides } }
|
* Body: { recipeId, config: { selectedComponents, sharedConfig, componentOverrides } }
|
||||||
*/
|
*/
|
||||||
router.post('/deploy', ctx.asyncHandler(async (req, res) => {
|
router.post('/deploy', asyncHandler(async (req, res) => {
|
||||||
const { recipeId, config } = req.body;
|
const { recipeId, config } = req.body;
|
||||||
const { RECIPE_TEMPLATES } = require('../../recipe-templates');
|
const { RECIPE_TEMPLATES } = require('../../recipe-templates');
|
||||||
|
|
||||||
const recipe = RECIPE_TEMPLATES[recipeId];
|
const recipe = RECIPE_TEMPLATES[recipeId];
|
||||||
if (!recipe) throw new ValidationError('Invalid recipe template', 'recipeId');
|
if (!recipe) throw new ValidationError('Invalid recipe template', 'recipeId');
|
||||||
|
|
||||||
ctx.log.info('recipe', 'Starting recipe deployment', { recipeId, name: recipe.name });
|
log.info('recipe', 'Starting recipe deployment', { recipeId, name: recipe.name });
|
||||||
|
|
||||||
// Determine which components to deploy
|
// Determine which components to deploy
|
||||||
const selectedIds = new Set(config.selectedComponents || recipe.components.filter(c => c.required).map(c => c.id));
|
const selectedIds = new Set(config.selectedComponents || recipe.components.filter(c => c.required).map(c => c.id));
|
||||||
@@ -40,18 +51,18 @@ module.exports = function(ctx) {
|
|||||||
if (recipe.network) {
|
if (recipe.network) {
|
||||||
networkName = recipe.network.name;
|
networkName = recipe.network.name;
|
||||||
try {
|
try {
|
||||||
await ctx.docker.client.createNetwork({
|
await docker.client.createNetwork({
|
||||||
Name: networkName,
|
Name: networkName,
|
||||||
Driver: recipe.network.driver || 'bridge',
|
Driver: recipe.network.driver || 'bridge',
|
||||||
Labels: { 'sami.managed': 'true', 'sami.recipe': recipeId }
|
Labels: { 'sami.managed': 'true', 'sami.recipe': recipeId }
|
||||||
});
|
});
|
||||||
ctx.log.info('recipe', 'Created Docker network', { networkName });
|
log.info('recipe', 'Created Docker network', { networkName });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Network might already exist
|
// Network might already exist
|
||||||
if (!e.message.includes('already exists')) {
|
if (!e.message.includes('already exists')) {
|
||||||
throw new Error(`Failed to create network ${networkName}: ${e.message}`);
|
throw new Error(`Failed to create network ${networkName}: ${e.message}`);
|
||||||
}
|
}
|
||||||
ctx.log.info('recipe', 'Docker network already exists', { networkName });
|
log.info('recipe', 'Docker network already exists', { networkName });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +72,7 @@ module.exports = function(ctx) {
|
|||||||
try {
|
try {
|
||||||
for (const component of componentsToDeploy) {
|
for (const component of componentsToDeploy) {
|
||||||
try {
|
try {
|
||||||
ctx.log.info('recipe', `Deploying component: ${component.id}`, {
|
log.info('recipe', `Deploying component: ${component.id}`, {
|
||||||
role: component.role,
|
role: component.role,
|
||||||
internal: component.internal || false
|
internal: component.internal || false
|
||||||
});
|
});
|
||||||
@@ -69,11 +80,11 @@ module.exports = function(ctx) {
|
|||||||
const result = await deployComponent(component, recipe, config, generatedPasswords, networkName);
|
const result = await deployComponent(component, recipe, config, generatedPasswords, networkName);
|
||||||
deployedComponents.push(result);
|
deployedComponents.push(result);
|
||||||
|
|
||||||
ctx.log.info('recipe', `Component deployed: ${component.id}`, {
|
log.info('recipe', `Component deployed: ${component.id}`, {
|
||||||
containerId: result.containerId?.substring(0, 12)
|
containerId: result.containerId?.substring(0, 12)
|
||||||
});
|
});
|
||||||
} catch (componentError) {
|
} catch (componentError) {
|
||||||
ctx.log.error('recipe', `Component failed: ${component.id}`, {
|
log.error('recipe', `Component failed: ${component.id}`, {
|
||||||
error: componentError.message
|
error: componentError.message
|
||||||
});
|
});
|
||||||
errors.push({ componentId: component.id, role: component.role, error: componentError.message });
|
errors.push({ componentId: component.id, role: component.role, error: componentError.message });
|
||||||
@@ -104,10 +115,10 @@ module.exports = function(ctx) {
|
|||||||
|
|
||||||
// Run auto-connect if available
|
// Run auto-connect if available
|
||||||
if (recipe.autoConnect?.enabled && errors.length === 0) {
|
if (recipe.autoConnect?.enabled && errors.length === 0) {
|
||||||
ctx.log.info('recipe', 'Running auto-connect for recipe', { recipeId });
|
log.info('recipe', 'Running auto-connect for recipe', { recipeId });
|
||||||
// Auto-connect will be handled asynchronously — don't block the response
|
// Auto-connect will be handled asynchronously — don't block the response
|
||||||
runAutoConnect(recipe, deployedComponents, config).catch(e => {
|
runAutoConnect(recipe, deployedComponents, config).catch(e => {
|
||||||
ctx.log.warn('recipe', 'Auto-connect had errors', { recipeId, error: e.message });
|
log.warn('recipe', 'Auto-connect had errors', { recipeId, error: e.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,17 +147,17 @@ module.exports = function(ctx) {
|
|||||||
|
|
||||||
res.json(response);
|
res.json(response);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ctx.log.error('recipe', 'Recipe deployment failed', { recipeId, error: error.message });
|
log.error('recipe', 'Recipe deployment failed', { recipeId, error: error.message });
|
||||||
|
|
||||||
// Cleanup: remove partially deployed containers
|
// Cleanup: remove partially deployed containers
|
||||||
for (const deployed of deployedComponents) {
|
for (const deployed of deployedComponents) {
|
||||||
try {
|
try {
|
||||||
if (deployed.containerId) {
|
if (deployed.containerId) {
|
||||||
const container = ctx.docker.client.getContainer(deployed.containerId);
|
const container = docker.client.getContainer(deployed.containerId);
|
||||||
await container.remove({ force: true });
|
await container.remove({ force: true });
|
||||||
}
|
}
|
||||||
} catch (cleanupError) {
|
} catch (cleanupError) {
|
||||||
ctx.log.warn('recipe', 'Cleanup failed for component', {
|
log.warn('recipe', 'Cleanup failed for component', {
|
||||||
componentId: deployed.id, error: cleanupError.message
|
componentId: deployed.id, error: cleanupError.message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -155,10 +166,10 @@ module.exports = function(ctx) {
|
|||||||
// Cleanup network
|
// Cleanup network
|
||||||
if (networkName) {
|
if (networkName) {
|
||||||
try {
|
try {
|
||||||
const network = ctx.docker.client.getNetwork(networkName);
|
const network = docker.client.getNetwork(networkName);
|
||||||
await network.remove();
|
await network.remove();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.warn('recipe', 'Network cleanup failed', { networkName, error: e.message });
|
log.warn('recipe', 'Network cleanup failed', { networkName, error: e.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,11 +295,11 @@ module.exports = function(ctx) {
|
|||||||
|
|
||||||
// Pull image
|
// Pull image
|
||||||
try {
|
try {
|
||||||
ctx.log.info('recipe', `Pulling image: ${dockerConfig.image}`);
|
log.info('recipe', `Pulling image: ${dockerConfig.image}`);
|
||||||
await ctx.docker.pull(dockerConfig.image);
|
await docker.pull(dockerConfig.image);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.warn('recipe', `Pull failed, checking local: ${dockerConfig.image}`);
|
log.warn('recipe', `Pull failed, checking local: ${dockerConfig.image}`);
|
||||||
const images = await ctx.docker.client.listImages({
|
const images = await docker.client.listImages({
|
||||||
filters: { reference: [dockerConfig.image] }
|
filters: { reference: [dockerConfig.image] }
|
||||||
});
|
});
|
||||||
if (images.length === 0) throw new Error(`Image not found: ${dockerConfig.image}`);
|
if (images.length === 0) throw new Error(`Image not found: ${dockerConfig.image}`);
|
||||||
@@ -296,7 +307,7 @@ module.exports = function(ctx) {
|
|||||||
|
|
||||||
// Remove stale container
|
// Remove stale container
|
||||||
try {
|
try {
|
||||||
const existing = ctx.docker.client.getContainer(containerName);
|
const existing = docker.client.getContainer(containerName);
|
||||||
await existing.inspect();
|
await existing.inspect();
|
||||||
await existing.remove({ force: true });
|
await existing.remove({ force: true });
|
||||||
await new Promise(r => setTimeout(r, 1000));
|
await new Promise(r => setTimeout(r, 1000));
|
||||||
@@ -305,17 +316,17 @@ module.exports = function(ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create and start container
|
// Create and start container
|
||||||
const container = await ctx.docker.client.createContainer(containerConfig);
|
const container = await docker.client.createContainer(containerConfig);
|
||||||
await container.start();
|
await container.start();
|
||||||
|
|
||||||
// Connect to recipe network
|
// Connect to recipe network
|
||||||
if (networkName) {
|
if (networkName) {
|
||||||
try {
|
try {
|
||||||
const network = ctx.docker.client.getNetwork(networkName);
|
const network = docker.client.getNetwork(networkName);
|
||||||
await network.connect({ Container: container.id });
|
await network.connect({ Container: container.id });
|
||||||
ctx.log.info('recipe', `Connected ${component.id} to network ${networkName}`);
|
log.info('recipe', `Connected ${component.id} to network ${networkName}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.warn('recipe', `Failed to connect ${component.id} to network`, { error: e.message });
|
log.warn('recipe', `Failed to connect ${component.id} to network`, { error: e.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +343,7 @@ module.exports = function(ctx) {
|
|||||||
await helpers.addCaddyConfig(subdomain, caddyConfig);
|
await helpers.addCaddyConfig(subdomain, caddyConfig);
|
||||||
url = `https://${ctx.buildDomain(subdomain)}`;
|
url = `https://${ctx.buildDomain(subdomain)}`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.warn('recipe', `Caddy config failed for ${component.id}`, { error: e.message });
|
log.warn('recipe', `Caddy config failed for ${component.id}`, { error: e.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,12 +371,12 @@ module.exports = function(ctx) {
|
|||||||
|
|
||||||
for (const step of recipe.autoConnect.steps) {
|
for (const step of recipe.autoConnect.steps) {
|
||||||
try {
|
try {
|
||||||
ctx.log.info('recipe', `Auto-connect step: ${step.action}`, { targets: step.targets });
|
log.info('recipe', `Auto-connect step: ${step.action}`, { targets: step.targets });
|
||||||
// These actions map to existing Smart Arr Connect functionality
|
// These actions map to existing Smart Arr Connect functionality
|
||||||
// The actual implementation will be wired when Smart Arr Connect helpers are available
|
// The actual implementation will be wired when Smart Arr Connect helpers are available
|
||||||
ctx.log.info('recipe', `Auto-connect step ${step.action} completed`);
|
log.info('recipe', `Auto-connect step ${step.action} completed`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.warn('recipe', `Auto-connect step failed: ${step.action}`, { error: e.message });
|
log.warn('recipe', `Auto-connect step failed: ${step.action}`, { error: e.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,28 @@ const deployRoutes = require('./deploy');
|
|||||||
const manageRoutes = require('./manage');
|
const manageRoutes = require('./manage');
|
||||||
const { NotFoundError } = require('../../errors');
|
const { NotFoundError } = require('../../errors');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recipes routes aggregator
|
||||||
|
* @param {Object} ctx - Application context (for backward compatibility)
|
||||||
|
* @returns {express.Router}
|
||||||
|
*/
|
||||||
module.exports = function(ctx) {
|
module.exports = function(ctx) {
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
const deps = {
|
||||||
|
docker: ctx.docker,
|
||||||
|
credentialManager: ctx.credentialManager,
|
||||||
|
servicesStateManager: ctx.servicesStateManager,
|
||||||
|
asyncHandler: ctx.asyncHandler,
|
||||||
|
errorResponse: ctx.errorResponse,
|
||||||
|
log: ctx.log
|
||||||
|
};
|
||||||
|
|
||||||
// All recipe routes require premium license
|
// All recipe routes require premium license
|
||||||
router.use(ctx.licenseManager.requirePremium('recipes'));
|
router.use(ctx.licenseManager.requirePremium('recipes'));
|
||||||
|
|
||||||
// GET /api/recipes/templates — list all recipe templates
|
// GET /api/recipes/templates — list all recipe templates
|
||||||
router.get('/templates', ctx.asyncHandler(async (req, res) => {
|
router.get('/templates', deps.asyncHandler(async (req, res) => {
|
||||||
const { RECIPE_TEMPLATES, RECIPE_CATEGORIES } = require('../../recipe-templates');
|
const { RECIPE_TEMPLATES, RECIPE_CATEGORIES } = require('../../recipe-templates');
|
||||||
const templates = Object.entries(RECIPE_TEMPLATES).map(([id, recipe]) => ({
|
const templates = Object.entries(RECIPE_TEMPLATES).map(([id, recipe]) => ({
|
||||||
id,
|
id,
|
||||||
@@ -39,7 +53,7 @@ module.exports = function(ctx) {
|
|||||||
}, 'recipe-templates'));
|
}, 'recipe-templates'));
|
||||||
|
|
||||||
// GET /api/recipes/templates/:recipeId — get single recipe template detail
|
// GET /api/recipes/templates/:recipeId — get single recipe template detail
|
||||||
router.get('/templates/:recipeId', ctx.asyncHandler(async (req, res) => {
|
router.get('/templates/:recipeId', deps.asyncHandler(async (req, res) => {
|
||||||
const { RECIPE_TEMPLATES } = require('../../recipe-templates');
|
const { RECIPE_TEMPLATES } = require('../../recipe-templates');
|
||||||
const recipe = RECIPE_TEMPLATES[req.params.recipeId];
|
const recipe = RECIPE_TEMPLATES[req.params.recipeId];
|
||||||
if (!recipe) throw new NotFoundError(`Recipe template ${req.params.recipeId}`);
|
if (!recipe) throw new NotFoundError(`Recipe template ${req.params.recipeId}`);
|
||||||
@@ -48,8 +62,8 @@ module.exports = function(ctx) {
|
|||||||
}, 'recipe-template-detail'));
|
}, 'recipe-template-detail'));
|
||||||
|
|
||||||
// Mount deploy and manage sub-routes
|
// Mount deploy and manage sub-routes
|
||||||
router.use(deployRoutes(ctx));
|
router.use(deployRoutes(deps));
|
||||||
router.use(manageRoutes(ctx));
|
router.use(manageRoutes(deps));
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,14 +2,22 @@ const express = require('express');
|
|||||||
const { DOCKER } = require('../../constants');
|
const { DOCKER } = require('../../constants');
|
||||||
const { NotFoundError } = require('../../errors');
|
const { NotFoundError } = require('../../errors');
|
||||||
|
|
||||||
module.exports = function(ctx) {
|
module.exports = function({ servicesStateManager, asyncHandler, log }) {
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
/**
|
||||||
|
* Recipes management routes factory
|
||||||
|
* @param {Object} deps - Explicit dependencies
|
||||||
|
* @param {Object} deps.servicesStateManager - Services state manager
|
||||||
|
* @param {Function} deps.asyncHandler - Async route handler wrapper
|
||||||
|
* @param {Object} deps.log - Logger instance
|
||||||
|
* @returns {express.Router}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/recipes/deployed — list all deployed recipes (grouped by recipeId)
|
* GET /api/recipes/deployed — list all deployed recipes (grouped by recipeId)
|
||||||
*/
|
*/
|
||||||
router.get('/deployed', ctx.asyncHandler(async (req, res) => {
|
router.get('/deployed', asyncHandler(async (req, res) => {
|
||||||
const services = await ctx.servicesStateManager.read();
|
const services = await servicesStateManager.read();
|
||||||
const recipeGroups = {};
|
const recipeGroups = {};
|
||||||
|
|
||||||
for (const service of services) {
|
for (const service of services) {
|
||||||
@@ -64,7 +72,7 @@ module.exports = function(ctx) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.warn('recipe', 'Could not list Docker containers for recipe discovery', { error: e.message });
|
log.warn('recipe', 'Could not list Docker containers for recipe discovery', { error: e.message });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enrich with container state
|
// Enrich with container state
|
||||||
@@ -92,7 +100,7 @@ module.exports = function(ctx) {
|
|||||||
/**
|
/**
|
||||||
* POST /api/recipes/:recipeId/start — start all containers in a recipe
|
* POST /api/recipes/:recipeId/start — start all containers in a recipe
|
||||||
*/
|
*/
|
||||||
router.post('/:recipeId/start', ctx.asyncHandler(async (req, res) => {
|
router.post('/:recipeId/start', asyncHandler(async (req, res) => {
|
||||||
const { recipeId } = req.params;
|
const { recipeId } = req.params;
|
||||||
const containers = await findRecipeContainers(recipeId);
|
const containers = await findRecipeContainers(recipeId);
|
||||||
|
|
||||||
@@ -116,14 +124,14 @@ module.exports = function(ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.log.info('recipe', 'Recipe started', { recipeId, results });
|
log.info('recipe', 'Recipe started', { recipeId, results });
|
||||||
res.json({ success: true, recipeId, results });
|
res.json({ success: true, recipeId, results });
|
||||||
}, 'recipe-start'));
|
}, 'recipe-start'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /api/recipes/:recipeId/stop — stop all containers in a recipe
|
* POST /api/recipes/:recipeId/stop — stop all containers in a recipe
|
||||||
*/
|
*/
|
||||||
router.post('/:recipeId/stop', ctx.asyncHandler(async (req, res) => {
|
router.post('/:recipeId/stop', asyncHandler(async (req, res) => {
|
||||||
const { recipeId } = req.params;
|
const { recipeId } = req.params;
|
||||||
const containers = await findRecipeContainers(recipeId);
|
const containers = await findRecipeContainers(recipeId);
|
||||||
|
|
||||||
@@ -148,14 +156,14 @@ module.exports = function(ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.log.info('recipe', 'Recipe stopped', { recipeId, results });
|
log.info('recipe', 'Recipe stopped', { recipeId, results });
|
||||||
res.json({ success: true, recipeId, results });
|
res.json({ success: true, recipeId, results });
|
||||||
}, 'recipe-stop'));
|
}, 'recipe-stop'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /api/recipes/:recipeId/restart — restart all containers in a recipe
|
* POST /api/recipes/:recipeId/restart — restart all containers in a recipe
|
||||||
*/
|
*/
|
||||||
router.post('/:recipeId/restart', ctx.asyncHandler(async (req, res) => {
|
router.post('/:recipeId/restart', asyncHandler(async (req, res) => {
|
||||||
const { recipeId } = req.params;
|
const { recipeId } = req.params;
|
||||||
const containers = await findRecipeContainers(recipeId);
|
const containers = await findRecipeContainers(recipeId);
|
||||||
|
|
||||||
@@ -174,14 +182,14 @@ module.exports = function(ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.log.info('recipe', 'Recipe restarted', { recipeId, results });
|
log.info('recipe', 'Recipe restarted', { recipeId, results });
|
||||||
res.json({ success: true, recipeId, results });
|
res.json({ success: true, recipeId, results });
|
||||||
}, 'recipe-restart'));
|
}, 'recipe-restart'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DELETE /api/recipes/:recipeId — remove entire recipe (containers, network, services)
|
* DELETE /api/recipes/:recipeId — remove entire recipe (containers, network, services)
|
||||||
*/
|
*/
|
||||||
router.delete('/:recipeId', ctx.asyncHandler(async (req, res) => {
|
router.delete('/:recipeId', asyncHandler(async (req, res) => {
|
||||||
const { recipeId } = req.params;
|
const { recipeId } = req.params;
|
||||||
const containers = await findRecipeContainers(recipeId);
|
const containers = await findRecipeContainers(recipeId);
|
||||||
|
|
||||||
@@ -189,7 +197,7 @@ module.exports = function(ctx) {
|
|||||||
throw new NotFoundError('Containers for recipe');
|
throw new NotFoundError('Containers for recipe');
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.log.info('recipe', 'Removing recipe', { recipeId, containerCount: containers.length });
|
log.info('recipe', 'Removing recipe', { recipeId, containerCount: containers.length });
|
||||||
|
|
||||||
const results = [];
|
const results = [];
|
||||||
const networkNames = new Set();
|
const networkNames = new Set();
|
||||||
@@ -213,7 +221,7 @@ module.exports = function(ctx) {
|
|||||||
try {
|
try {
|
||||||
await removeCaddyBlock(subdomain);
|
await removeCaddyBlock(subdomain);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.warn('recipe', 'Failed to remove Caddy config', { subdomain, error: e.message });
|
log.warn('recipe', 'Failed to remove Caddy config', { subdomain, error: e.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +234,7 @@ module.exports = function(ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove recipe services from services.json
|
// Remove recipe services from services.json
|
||||||
await ctx.servicesStateManager.update(services => {
|
await servicesStateManager.update(services => {
|
||||||
return services.filter(s => s.recipeId !== recipeId);
|
return services.filter(s => s.recipeId !== recipeId);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -235,9 +243,9 @@ module.exports = function(ctx) {
|
|||||||
try {
|
try {
|
||||||
const network = ctx.docker.client.getNetwork(netName);
|
const network = ctx.docker.client.getNetwork(netName);
|
||||||
await network.remove();
|
await network.remove();
|
||||||
ctx.log.info('recipe', 'Removed Docker network', { netName });
|
log.info('recipe', 'Removed Docker network', { netName });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.warn('recipe', 'Failed to remove network', { netName, error: e.message });
|
log.warn('recipe', 'Failed to remove network', { netName, error: e.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +254,7 @@ module.exports = function(ctx) {
|
|||||||
'info'
|
'info'
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.log.info('recipe', 'Recipe removed', { recipeId, results });
|
log.info('recipe', 'Recipe removed', { recipeId, results });
|
||||||
res.json({ success: true, recipeId, results });
|
res.json({ success: true, recipeId, results });
|
||||||
}, 'recipe-remove'));
|
}, 'recipe-remove'));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user