luci-app-dockerman: network aliases feature
authorPaul Donald <redacted>
Thu, 14 May 2026 12:00:45 +0000 (15:00 +0300)
committerPaul Donald <redacted>
Thu, 14 May 2026 13:59:09 +0000 (16:59 +0300)
Add network aliases to container view/create.
Also init with ipv6 settings.

Signed-off-by: Serhii Ivanov <redacted>
applications/luci-app-dockerman/htdocs/luci-static/resources/view/dockerman/container.js
applications/luci-app-dockerman/htdocs/luci-static/resources/view/dockerman/container_new.js

index 88c14b0d5e1c46d610d0cea97139990b720f8ad8..80ce6863a4b5b68e65a5fc9b7caa21a2949416b3 100644 (file)
@@ -308,6 +308,7 @@ return dm2.dv.extend({
                                DNSNames: net?.DNSNames || '',
                                IPv4Address: net?.IPAMConfig?.IPv4Address || net?.IPAddress || '',
                                IPv6Address: net?.IPAMConfig?.IPv6Address || '',
+                               Aliases: net?.Aliases || '',
                        });
                }
 
@@ -582,6 +583,8 @@ return dm2.dv.extend({
 
                o = ss.option(form.DummyValue, 'DNSNames', _('DNS Names'));
 
+               o = ss.option(form.DummyValue, 'Aliases', _('Aliases'));
+
                ss.handleAdd = function(ev) {
                        ev.preventDefault();
                        view.executeNetworkAction('connect', null, null, this_container);
@@ -1884,7 +1887,7 @@ return dm2.dv.extend({
 
                        const ip4Input = E('input', {
                                'type': 'text',
-                               'id': 'network-ip',
+                               'id': 'network-ip4',
                                'class': 'cbi-input-text',
                                'placeholder': 'e.g., 172.18.0.5',
                                'style': 'width:100%; margin-top:5px;'
@@ -1892,18 +1895,29 @@ return dm2.dv.extend({
 
                        const ip6Input = E('input', {
                                'type': 'text',
-                               'id': 'network-ip',
+                               'id': 'network-ip6',
                                'class': 'cbi-input-text',
                                'placeholder': 'e.g., 2001:db8:1::1',
                                'style': 'width:100%; margin-top:5px;'
                        });
 
+                       const aliasesInput = E('input', {
+                               'type': 'text',
+                               'id': 'network-aliases',
+                               'class': 'cbi-input-text',
+                               'placeholder': 'e.g., database,db (comma-separated)',
+                               'style': 'width:100%; margin-top:5px;'
+                       });
+
                        const modalBody = E('div', { 'class': 'cbi-section' }, [
                                E('p', {}, _('Select network to connect:')),
                                networkSelect,
-                               E('label', { 'style': 'display:block; margin-top:10px;' }, _('IP Address (optional):')),
+                               E('label', { 'style': 'display:block; margin-top:10px;' }, _('IPv4 Address (optional):')),
                                ip4Input,
+                               E('label', { 'style': 'display:block; margin-top:10px;' }, _('IPv6 Address (optional):')),
                                ip6Input,
+                               E('label', { 'style': 'display:block; margin-top:10px;' }, _('Aliases (optional):')),
+                               aliasesInput,
                        ]);
 
                        ui.showModal(_('Connect Network'), [
@@ -1919,7 +1933,9 @@ return dm2.dv.extend({
                                                'click': () => {
                                                        const selectedNetwork = networkSelect.value;
                                                        const ip4Address = ip4Input.value || '';
-                                                       // const ip6Address = ip6Input.value || '';
+                                                       const ip6Address = ip6Input.value || '';
+                                                       const aliasesRaw = aliasesInput.value || '';
+                                                       const aliases = aliasesRaw.split(',').map(a => a.trim()).filter(Boolean);
 
                                                        if (!selectedNetwork) {
                                                                view.showNotification(_('Error'), [_('No network selected')], 5000, 'error');
@@ -1929,7 +1945,13 @@ return dm2.dv.extend({
                                                        ui.hideModal();
 
                                                        const body = { Container: this_container.Id };
-                                                       body.EndpointConfig = { IPAMConfig: { IPv4Address: ip4Address } }; //, IPv6Address: ip6Address || null
+                                                       body.EndpointConfig = { 
+                                                               IPAMConfig: { 
+                                                                       IPv4Address: ip4Address || null, 
+                                                                       IPv6Address: ip6Address || null 
+                                                               },
+                                                               Aliases: aliases.length > 0 ? aliases : null
+                                                       };
 
                                                        view.executeDockerAction(
                                                                dm2.network_connect,
index b919004766edc37b4793986b6c87113dd4bde7da..726240eac1f6420faa48551d732861b522656fd5 100644 (file)
@@ -73,7 +73,8 @@ return dm2.dv.extend({
                        const hostConfig = c.HostConfig || {};
                        const resolvedImage = resolveImageId(c.Image) || resolveImageId(c.Config?.Image) || c.Image || c.Config?.Image || '';
                        const builtInNetworks = new Set(['none', 'bridge', 'host']);
-                       const [netnames, nets] = Object.entries(c.NetworkSettings?.Networks || {});
+                       // Object.entries returns [[name, obj], ...] — pick the first network
+                       const [[firstName, firstNet] = []] = Object.entries(c.NetworkSettings?.Networks || {});
 
                        containerData.container = {
                                name: c.Name?.substring(1) || '',
@@ -82,20 +83,22 @@ return dm2.dv.extend({
                                image: resolvedImage,
                                privileged: hostConfig.Privileged ? 1 : 0,
                                restart_policy: hostConfig.RestartPolicy?.Name || 'unless-stopped',
-                               network: (() => {
-                                       return (netnames && (netnames.length > 0)) ? netnames[0] : '';
+                               network: firstName || '',
+                               network_aliases: (() => {
+                                       if (!firstName || builtInNetworks.has(firstName)) return '';
+                                       return (firstNet?.Aliases || []).join(', ');
                                })(),
                                ipv4: (() => {
-                                       if (builtInNetworks.has(netnames[0])) return '';
-                                       return (nets && (nets.length > 0)) ? nets[0]?.IPAddress || '' : '';
+                                       if (!firstName || builtInNetworks.has(firstName)) return '';
+                                       return firstNet?.IPAddress || '';
                                })(),
                                ipv6: (() => {
-                                       if (builtInNetworks.has(netnames[0])) return '';
-                                       return (nets && (nets.length > 0)) ? nets[0]?.GlobalIPv6Address || '' : '';
+                                       if (!firstName || builtInNetworks.has(firstName)) return '';
+                                       return firstNet?.GlobalIPv6Address || '';
                                })(),
                                ipv6_lla: (() => {
-                                       if (builtInNetworks.has(netnames[0])) return '';
-                                       return (nets && (nets.length > 0)) ? nets[0]?.LinkLocalIPv6Address || '' : '';
+                                       if (!firstName || builtInNetworks.has(firstName)) return '';
+                                       return firstNet?.LinkLocalIPv6Address || '';
                                })(),
                                link: hostConfig.Links || [],
                                dns: hostConfig.Dns || [],
@@ -272,6 +275,11 @@ return dm2.dv.extend({
                o.datatype = 'ip6ll';
                o.validate = not_with_a_docker_net;
 
+               o = s.option(form.Value, 'network_aliases', _('Network Aliases'));
+               o.rmempty = true;
+               o.placeholder = 'database,db (CSV)';
+               o.validate = not_with_a_docker_net;
+
                o = s.option(form.DynamicList, 'link', _('Links with other containers'));
                o.rmempty = true;
                o.placeholder='container_name:alias';
@@ -762,6 +770,7 @@ return dm2.dv.extend({
                                const name = get('name');
                                // const pull = toBool(get('pull'));
                                const network = get('network');
+                               const network_aliases = get('network_aliases');
                                const publish = get('publish');
                                const command = get('command');
                                // const publish_all = toBool(get('publish_all'));
@@ -829,7 +838,10 @@ return dm2.dv.extend({
                                                Sysctls: sysctl ? listToKv(sysctl) : undefined,
                                        },
                                        NetworkingConfig: {
-                                               EndpointsConfig: { [network]: { IPAMConfig: { IPv4Address: get('ipv4') || null, IPv6Address: get('ipv6') || null } } },
+                                               EndpointsConfig: { [network]: { 
+                                                       IPAMConfig: { IPv4Address: get('ipv4') || null, IPv6Address: get('ipv6') || null },
+                                                       Aliases: network_aliases ? network_aliases.split(',').map(a => a.trim()).filter(Boolean) : null
+                                               } },
                                        }
                                };
 
git clone https://git.99rst.org/PROJECT