Phase 1: Add ESLint/Prettier config + baseline auto-fixes
This commit is contained in:
@@ -46,90 +46,90 @@ module.exports = function(ctx) {
|
||||
const containerId = req.params.id;
|
||||
const container = await getVerifiedContainer(containerId);
|
||||
|
||||
// Get container info
|
||||
const containerInfo = await container.inspect();
|
||||
const imageName = containerInfo.Config.Image;
|
||||
const containerName = containerInfo.Name.replace(/^\//, '');
|
||||
// Get container info
|
||||
const containerInfo = await container.inspect();
|
||||
const imageName = containerInfo.Config.Image;
|
||||
const containerName = containerInfo.Name.replace(/^\//, '');
|
||||
|
||||
ctx.log.info('docker', 'Updating container', { containerName, imageName });
|
||||
ctx.log.info('docker', 'Updating container', { containerName, imageName });
|
||||
|
||||
// Pull the latest image
|
||||
ctx.log.info('docker', `Pulling latest image: ${imageName}`);
|
||||
await ctx.docker.pull(imageName);
|
||||
// Pull the latest image
|
||||
ctx.log.info('docker', `Pulling latest image: ${imageName}`);
|
||||
await ctx.docker.pull(imageName);
|
||||
|
||||
// Get current container config for recreation
|
||||
const hostConfig = containerInfo.HostConfig;
|
||||
const config = {
|
||||
Image: imageName,
|
||||
name: containerName,
|
||||
Env: containerInfo.Config.Env,
|
||||
ExposedPorts: containerInfo.Config.ExposedPorts,
|
||||
Labels: containerInfo.Config.Labels,
|
||||
HostConfig: {
|
||||
Binds: hostConfig.Binds,
|
||||
PortBindings: hostConfig.PortBindings,
|
||||
RestartPolicy: hostConfig.RestartPolicy,
|
||||
NetworkMode: hostConfig.NetworkMode,
|
||||
ExtraHosts: hostConfig.ExtraHosts,
|
||||
Privileged: hostConfig.Privileged,
|
||||
CapAdd: hostConfig.CapAdd,
|
||||
CapDrop: hostConfig.CapDrop,
|
||||
Devices: hostConfig.Devices,
|
||||
LogConfig: DOCKER.LOG_CONFIG // Ensure log rotation on updated containers
|
||||
},
|
||||
NetworkingConfig: {}
|
||||
// Get current container config for recreation
|
||||
const hostConfig = containerInfo.HostConfig;
|
||||
const config = {
|
||||
Image: imageName,
|
||||
name: containerName,
|
||||
Env: containerInfo.Config.Env,
|
||||
ExposedPorts: containerInfo.Config.ExposedPorts,
|
||||
Labels: containerInfo.Config.Labels,
|
||||
HostConfig: {
|
||||
Binds: hostConfig.Binds,
|
||||
PortBindings: hostConfig.PortBindings,
|
||||
RestartPolicy: hostConfig.RestartPolicy,
|
||||
NetworkMode: hostConfig.NetworkMode,
|
||||
ExtraHosts: hostConfig.ExtraHosts,
|
||||
Privileged: hostConfig.Privileged,
|
||||
CapAdd: hostConfig.CapAdd,
|
||||
CapDrop: hostConfig.CapDrop,
|
||||
Devices: hostConfig.Devices,
|
||||
LogConfig: DOCKER.LOG_CONFIG, // Ensure log rotation on updated containers
|
||||
},
|
||||
NetworkingConfig: {},
|
||||
};
|
||||
|
||||
// Get network settings if using a custom network
|
||||
if (hostConfig.NetworkMode && !['bridge', 'host', 'none'].includes(hostConfig.NetworkMode)) {
|
||||
const networkName = hostConfig.NetworkMode;
|
||||
config.NetworkingConfig.EndpointsConfig = {
|
||||
[networkName]: containerInfo.NetworkSettings.Networks[networkName],
|
||||
};
|
||||
}
|
||||
|
||||
// Get network settings if using a custom network
|
||||
if (hostConfig.NetworkMode && !['bridge', 'host', 'none'].includes(hostConfig.NetworkMode)) {
|
||||
const networkName = hostConfig.NetworkMode;
|
||||
config.NetworkingConfig.EndpointsConfig = {
|
||||
[networkName]: containerInfo.NetworkSettings.Networks[networkName]
|
||||
};
|
||||
// Stop and remove old container
|
||||
ctx.log.info('docker', 'Stopping container', { containerName });
|
||||
await container.stop().catch(() => {}); // Ignore if already stopped
|
||||
ctx.log.info('docker', 'Removing container', { containerName });
|
||||
await container.remove();
|
||||
|
||||
// Wait for port release (Windows/Docker Desktop can be slow to free ports)
|
||||
await new Promise(r => setTimeout(r, 3000));
|
||||
|
||||
// Create and start new container
|
||||
ctx.log.info('docker', 'Creating new container', { containerName });
|
||||
let newContainer;
|
||||
try {
|
||||
newContainer = await ctx.docker.client.createContainer(config);
|
||||
ctx.log.info('docker', 'Starting container', { containerName });
|
||||
await newContainer.start();
|
||||
} catch (startError) {
|
||||
// Clean up the failed container so it doesn't block future attempts
|
||||
ctx.log.error('docker', 'Failed to start new container', { containerName, error: startError.message });
|
||||
if (newContainer) {
|
||||
try { await newContainer.remove({ force: true }); } catch (e) { /* already gone */ }
|
||||
}
|
||||
throw startError;
|
||||
}
|
||||
|
||||
// Stop and remove old container
|
||||
ctx.log.info('docker', 'Stopping container', { containerName });
|
||||
await container.stop().catch(() => {}); // Ignore if already stopped
|
||||
ctx.log.info('docker', 'Removing container', { containerName });
|
||||
await container.remove();
|
||||
const newContainerInfo = await newContainer.inspect();
|
||||
|
||||
// Wait for port release (Windows/Docker Desktop can be slow to free ports)
|
||||
await new Promise(r => setTimeout(r, 3000));
|
||||
|
||||
// Create and start new container
|
||||
ctx.log.info('docker', 'Creating new container', { containerName });
|
||||
let newContainer;
|
||||
try {
|
||||
newContainer = await ctx.docker.client.createContainer(config);
|
||||
ctx.log.info('docker', 'Starting container', { containerName });
|
||||
await newContainer.start();
|
||||
} catch (startError) {
|
||||
// Clean up the failed container so it doesn't block future attempts
|
||||
ctx.log.error('docker', 'Failed to start new container', { containerName, error: startError.message });
|
||||
if (newContainer) {
|
||||
try { await newContainer.remove({ force: true }); } catch (e) { /* already gone */ }
|
||||
}
|
||||
throw startError;
|
||||
// Prune dangling images after update
|
||||
try {
|
||||
const pruneResult = await ctx.docker.client.pruneImages({ filters: { dangling: { true: true } } });
|
||||
if (pruneResult.SpaceReclaimed > 0) {
|
||||
ctx.log.info('docker', 'Pruned dangling images after update', { spaceReclaimed: `${Math.round(pruneResult.SpaceReclaimed / 1024 / 1024) }MB` });
|
||||
}
|
||||
} catch (pruneErr) {
|
||||
ctx.log.debug('docker', 'Image prune after update failed', { error: pruneErr.message });
|
||||
}
|
||||
|
||||
const newContainerInfo = await newContainer.inspect();
|
||||
|
||||
// Prune dangling images after update
|
||||
try {
|
||||
const pruneResult = await ctx.docker.client.pruneImages({ filters: { dangling: { true: true } } });
|
||||
if (pruneResult.SpaceReclaimed > 0) {
|
||||
ctx.log.info('docker', 'Pruned dangling images after update', { spaceReclaimed: Math.round(pruneResult.SpaceReclaimed / 1024 / 1024) + 'MB' });
|
||||
}
|
||||
} catch (pruneErr) {
|
||||
ctx.log.debug('docker', 'Image prune after update failed', { error: pruneErr.message });
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `Container ${containerName} updated successfully`,
|
||||
newContainerId: newContainerInfo.Id
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
message: `Container ${containerName} updated successfully`,
|
||||
newContainerId: newContainerInfo.Id,
|
||||
});
|
||||
}, 'container-update'));
|
||||
|
||||
// Check for available updates (compares local and remote image digests)
|
||||
@@ -148,7 +148,7 @@ module.exports = function(ctx) {
|
||||
const pullStream = await ctx.docker.pull(imageName);
|
||||
|
||||
const downloadedLayers = pullStream.filter(e =>
|
||||
e.status === 'Downloading' || e.status === 'Download complete'
|
||||
e.status === 'Downloading' || e.status === 'Download complete',
|
||||
);
|
||||
updateAvailable = downloadedLayers.length > 0;
|
||||
|
||||
@@ -167,7 +167,7 @@ module.exports = function(ctx) {
|
||||
success: true,
|
||||
imageName,
|
||||
updateAvailable,
|
||||
currentDigest: localDigest
|
||||
currentDigest: localDigest,
|
||||
});
|
||||
}, 'container-check-update'));
|
||||
|
||||
@@ -178,7 +178,7 @@ module.exports = function(ctx) {
|
||||
stdout: true,
|
||||
stderr: true,
|
||||
tail: 100,
|
||||
timestamps: true
|
||||
timestamps: true,
|
||||
});
|
||||
res.json({ success: true, logs: logs.toString() });
|
||||
}, 'container-logs'));
|
||||
@@ -194,7 +194,7 @@ module.exports = function(ctx) {
|
||||
router.get('/discover', ctx.asyncHandler(async (req, res) => {
|
||||
const containers = await ctx.docker.client.listContainers({ all: true });
|
||||
const samiContainers = containers.filter(container =>
|
||||
container.Labels && container.Labels['sami.managed'] === 'true'
|
||||
container.Labels && container.Labels['sami.managed'] === 'true',
|
||||
);
|
||||
|
||||
const discoveredContainers = samiContainers.map(container => ({
|
||||
@@ -205,7 +205,7 @@ module.exports = function(ctx) {
|
||||
status: container.Status,
|
||||
appTemplate: container.Labels['sami.app'],
|
||||
subdomain: container.Labels['sami.subdomain'],
|
||||
ports: container.Ports
|
||||
ports: container.Ports,
|
||||
}));
|
||||
|
||||
const paginationParams = parsePaginationParams(req.query);
|
||||
|
||||
Reference in New Issue
Block a user