CI / test (push) Successful in 9m53s
Add host snapshot and network IPAM suggestions, image-driven form hydration, command palette, Host/Events/Settings views, offline banner, job drawer for deploy steps, and typed RPC errors. Mark roadmap complete.
113 lines
3.7 KiB
JavaScript
113 lines
3.7 KiB
JavaScript
/**
|
|
* Network RPC handlers.
|
|
*/
|
|
import { docker } from '../services/docker.js'
|
|
import * as validation from '../utils/validation.js'
|
|
|
|
export function registerNetworkHandlers(session) {
|
|
session.respond('listNetworks', async () => {
|
|
const networks = await docker.listNetworks()
|
|
const containers = await docker.listContainers({ all: true })
|
|
const networkUsage = {}
|
|
for (const container of containers) {
|
|
const nets = container.NetworkSettings?.Networks
|
|
if (!nets) continue
|
|
for (const networkName of Object.keys(nets)) {
|
|
if (!networkUsage[networkName]) networkUsage[networkName] = []
|
|
networkUsage[networkName].push({
|
|
id: container.Id,
|
|
name: container.Names[0]?.replace(/^\//, '') || container.Id.substring(0, 12),
|
|
state: container.State,
|
|
})
|
|
}
|
|
}
|
|
return {
|
|
type: 'networks',
|
|
data: networks.map((network) => ({
|
|
...network,
|
|
usage: networkUsage[network.Name] || [],
|
|
})),
|
|
}
|
|
})
|
|
|
|
session.respond('createNetwork', async (args) => {
|
|
const name = validation.sanitizeString(args.name, 128)
|
|
if (!name) throw new Error('Network name required')
|
|
const networkConfig = {
|
|
Name: name,
|
|
Driver: args.driver || 'bridge',
|
|
CheckDuplicate: true,
|
|
Internal: Boolean(args.internal),
|
|
Attachable: args.attachable !== false,
|
|
EnableIPv6: Boolean(args.enableIPv6 || args.ipv6),
|
|
Labels: args.labels && typeof args.labels === 'object' ? args.labels : undefined,
|
|
}
|
|
// Auto-fill IPAM when client asks (or subnet provided)
|
|
let subnet = args.subnet
|
|
let gateway = args.gateway
|
|
if ((!subnet || args.autoIPAM) && networkConfig.Driver === 'bridge') {
|
|
const { suggestNetworkIPAM } = await import('../utils/suggestions.js')
|
|
const sug = await suggestNetworkIPAM()
|
|
if (!subnet && sug.subnet) {
|
|
subnet = sug.subnet
|
|
gateway = gateway || sug.gateway
|
|
}
|
|
}
|
|
if (subnet || gateway || args.ipRange) {
|
|
networkConfig.IPAM = {
|
|
Config: [
|
|
{
|
|
...(subnet ? { Subnet: subnet } : {}),
|
|
...(gateway ? { Gateway: gateway } : {}),
|
|
...(args.ipRange ? { IPRange: args.ipRange } : {}),
|
|
},
|
|
],
|
|
}
|
|
}
|
|
if (args.options && typeof args.options === 'object') {
|
|
networkConfig.Options = args.options
|
|
}
|
|
const network = await docker.createNetwork(networkConfig)
|
|
return {
|
|
success: true,
|
|
message: `Network "${name}" created successfully`,
|
|
data: network.id,
|
|
applied: { name, driver: networkConfig.Driver, subnet, gateway },
|
|
}
|
|
})
|
|
|
|
session.respond('removeNetwork', async (args) => {
|
|
await docker.getNetwork(args.id).remove()
|
|
return { success: true, message: `Network ${args.id} removed` }
|
|
})
|
|
|
|
session.respond('inspectNetwork', async (args) => {
|
|
const networkData = await docker.getNetwork(args.id).inspect()
|
|
return { type: 'networkConfig', data: networkData }
|
|
})
|
|
|
|
session.respond('connectNetwork', async (args) => {
|
|
await docker.getNetwork(args.networkId).connect({ Container: args.containerId })
|
|
return { success: true, message: 'Container connected to network' }
|
|
})
|
|
|
|
session.respond('disconnectNetwork', async (args) => {
|
|
await docker
|
|
.getNetwork(args.networkId)
|
|
.disconnect({ Container: args.containerId, Force: args.force || false })
|
|
return { success: true, message: 'Container disconnected from network' }
|
|
})
|
|
|
|
session.respond('pruneNetworks', async (args) => {
|
|
const opts = {}
|
|
if (args.filters) opts.filters = args.filters
|
|
const result = await docker.pruneNetworks(opts)
|
|
return {
|
|
success: true,
|
|
type: 'pruneNetworks',
|
|
message: 'Unused networks pruned',
|
|
data: result,
|
|
}
|
|
})
|
|
}
|