'use strict'; 'require form'; 'require fs'; 'require ui'; 'require dockerman.common as dm2'; /* Copyright 2026 Docker manager JS for Luci by Paul Donald Based on Docker Lua by lisaac LICENSE: GPLv2.0 */ return dm2.dv.extend({ load() { return Promise.all([ dm2.network_list(), dm2.container_list({query: {all: true}}), ]); }, render([networks, containers]) { if (networks?.code !== 200) { return E('div', {}, [ networks?.body?.message ]); } let network_list = this.getNetworksTable(networks.body, containers.body); // let container_list = containers.body; const view = this; // Capture the view context let pollPending = null; let netSec = null; const refresh = () => { if (pollPending) return pollPending; pollPending = view.load().then(([networks2, containers2]) => { network_list = view.getNetworksTable(networks2.body, containers2.body); // container_list = containers2.body; m.data = new m.data.constructor({network: network_list, prune: {}}); if (netSec) { netSec.footer = [ `${_('Total')} ${network_list.length}`, ]; } return m.render(); }).catch((err) => { console.warn(err) }).finally(() => { pollPending = null }); return pollPending; }; let s, o; const m = new form.JSONMap({network: network_list, prune: {}}, _('Docker - Networks'), _('This page displays all docker networks that have been created on the connected docker host.')); m.submit = false; m.reset = false; s = m.section(form.TableSection, 'prune', _('Networks overview'), null); s.addremove = false; s.anonymous = true; const prune = s.option(form.Button, '_prune', null); prune.inputtitle = `${dm2.ActionTypes['prune'].i18n} ${dm2.ActionTypes['prune'].e}`; prune.inputstyle = 'negative'; prune.onclick = L.bind(function(section_id, ev) { return this.super('handleXHRTransfer', [{ q_params: { }, commandCPath: '/networks/prune', commandDPath: '/networks/prune', commandTitle: dm2.ActionTypes['prune'].i18n, onUpdate: (msg) => { try { if(msg.error) ui.addTimeLimitedNotification(dm2.ActionTypes['prune'].i18n, msg.error, 7000, 'error'); const output = JSON.stringify(msg, null, 2) + '\n'; view.insertOutput(output); } catch { } }, noFileUpload: true, }]); // return view.executeDockerAction( // dm2.network_prune, // { query: { filters: '' } }, // dm2.ActionTypes['prune'].i18n, // { // showOutput: true, // successMessage: _('started/completed'), // onSuccess: () => { // setTimeout(() => window.location.href = `${this.dockerman_url}/networks`, 1000); // } // } // ); }, this); netSec = m.section(form.TableSection, 'network'); netSec.anonymous = true; netSec.nodescriptions = true; netSec.addremove = true; netSec.sortable = true; netSec.filterrow = true; netSec.addbtntitle = `${dm2.ActionTypes['create'].i18n} ${dm2.ActionTypes['create'].e}`; netSec.footer = [ `${_('Total')} ${network_list.length}`, ]; netSec.handleAdd = function(section_id, ev) { window.location.href = `${view.dockerman_url}/network_new`; }; netSec.handleRemove = function(section_id, force, ev) { const network = network_list.find(net => net['.name'] === section_id); if (!network?.Id) return false; return view.executeDockerAction( dm2.network_remove, { id: network.Id }, dm2.ActionTypes['remove'].i18n, { showOutput: true, onSuccess: () => { return refresh(); } } ); }; netSec.handleInspect = function(section_id, ev) { const network = network_list.find(net => net['.name'] === section_id); if (!network?.Id) return false; return view.executeDockerAction( dm2.network_inspect, { id: network.Id }, dm2.ActionTypes['inspect'].i18n, { showOutput: true, showSuccess: false } ); }; netSec.renderRowActions = function (section_id) { const network = network_list.find(net => net['.name'] === section_id); const btns = [ E('button', { 'class': 'cbi-button view', 'title': dm2.ActionTypes['inspect'].i18n, 'click': ui.createHandlerFn(this, this.handleInspect, section_id), }, [dm2.ActionTypes['inspect'].e]), E('div', { 'style': 'width: 20px', // Some safety margin for mis-clicks }, [' ']), E('button', { 'class': 'cbi-button cbi-button-negative remove', 'title': dm2.ActionTypes['remove'].i18n, 'click': ui.createHandlerFn(this, this.handleRemove, section_id, false), 'disabled': network?._disable_delete, }, dm2.ActionTypes['remove'].e), E('button', { 'class': 'cbi-button cbi-button-negative important remove', 'title': dm2.ActionTypes['force_remove'].i18n, 'click': ui.createHandlerFn(this, this.handleRemove, section_id, true), 'disabled': network?._disable_delete, }, dm2.ActionTypes['force_remove'].e), ]; return E('td', { 'class': 'td middle cbi-section-actions' }, E('div', btns)); }; o = netSec.option(form.DummyValue, '_shortId', _('ID')); o = netSec.option(form.DummyValue, 'Name', _('Name')); o = netSec.option(form.DummyValue, 'Labels', _('Labels') + ' 🏷️'); o.cfgvalue = view.objectCfgValueTT; o = netSec.option(form.DummyValue, '_container', _('Containers')); o = netSec.option(form.DummyValue, 'Driver', _('Driver')); o = netSec.option(form.DummyValue, '_interface', _('Parent Interface')); o = netSec.option(form.DummyValue, '_subnet', _('Subnet')); o = netSec.option(form.DummyValue, '_gateway', _('Gateway')); this.insertOutputFrame(s, m); return m.render(); }, handleSave: null, handleSaveApply: null, handleReset: null, getNetworksTable(networks, containers) { const data = []; for (const [ , net] of (networks || []).entries()) { const n = net.Name; const _shortId = (net.Id || '').substring(0, 12); const shortLink = E('a', { 'href': `${this.dockerman_url}/network/${net.Id}`, 'style': 'font-family: monospace;', 'title': _('Click to view this network'), }, [_shortId]); // Just push plain data objects without UCI metadata const configs = Array.isArray(net?.IPAM?.Config) ? net.IPAM.Config : []; data.push({ ...net, _gateway: configs.map(o => o.Gateway).filter(o => o).join(', ') || '', _subnet: configs.map(o => o.Subnet).filter(o => o).join(', ') || '', _disable_delete: ( n === 'bridge' || n === 'none' || n === 'host' ) ? true : null, _shortId: shortLink, _container: this.parseContainerLinksForNetwork(net, containers), _interface: (net.Driver === 'bridge') ? net.Options?.['com.docker.network.bridge.name'] || '' : (net.Driver === 'macvlan') ? net?.Options?.parent : '', }); } return data; }, });