11import path from "path" ;
2- import { mapValues , pick , omitBy , isObject , mergeWith , union } from "lodash-es" ;
2+ import { mapValues , pick , omitBy , isObject } from "lodash-es" ;
33import {
44 parsePortMappings ,
55 stringifyPortMappings ,
@@ -10,7 +10,15 @@ import {
1010 parseServiceNetworks
1111} from "./index.js" ;
1212import { parseEnvironment } from "@dappnode/utils" ;
13- import { Compose , ComposeServiceNetworks , PortMapping , UserSettings , VolumeMapping } from "@dappnode/types" ;
13+ import {
14+ Compose ,
15+ ComposeServiceNetwork ,
16+ ComposeServiceNetworks ,
17+ ComposeServiceNetworksObj ,
18+ PortMapping ,
19+ UserSettings ,
20+ VolumeMapping
21+ } from "@dappnode/types" ;
1422import { cleanCompose , isOmitable } from "./clean.js" ;
1523import { stringifyVolumeMappings } from "./volumes.js" ;
1624import { readContainerLabels , writeDefaultsToLabels } from "./labelsDb.js" ;
@@ -145,7 +153,7 @@ export function applyUserSettings(
145153 const userSetEnvironment = ( userSettings . environment || { } ) [ serviceName ] || { } ;
146154 const userSetPortMappings = ( userSettings . portMappings || { } ) [ serviceName ] || { } ;
147155 const userSetLegacyBindVolumes = ( userSettings . legacyBindVolumes || { } ) [ serviceName ] || { } ;
148- const userSetNetworks = ( userSettings . networks ?. serviceNetworks || { } ) [ serviceName ] || { } ;
156+ const userSetNetworks = parseServiceNetworks ( ( userSettings . networks ?. serviceNetworks || { } ) [ serviceName ] || { } ) ;
149157
150158 // New values
151159 const nextEnvironment = mapValues ( environment , ( envValue , envName ) => userSetEnvironment [ envName ] || envValue ) ;
@@ -159,13 +167,64 @@ export function applyUserSettings(
159167 } )
160168 ) ;
161169
162- // docker aliases must be unique
163- // merge base and user networks, then remove any empty/nullish props (e.g. empty ipv4_address)
164- const mergedNetworks = mergeWith ( networks , userSetNetworks , ( value1 , value2 ) =>
165- mergeWith ( value1 , value2 , ( subvalue1 , subvalue2 ) => union ( subvalue1 , subvalue2 ) )
166- ) ;
167- const nextNetworks = mapValues ( mergedNetworks , ( config ) =>
168- omitBy ( config , ( v ) => v == null || ( Array . isArray ( v ) && v . length === 0 ) )
170+ // Function to ensure types are correct and enforce Compose file compatibility
171+ const normalizeNetwork = ( network : ComposeServiceNetwork ) : ComposeServiceNetwork => {
172+ const normalized : ComposeServiceNetwork = { } ;
173+ // Only add aliases if the array is non-empty
174+ if ( Array . isArray ( network . aliases ) && network . aliases . length > 0 ) {
175+ normalized . aliases = network . aliases ;
176+ }
177+ // Only add ipv4_address if it is defined (not undefined)
178+ if ( network . ipv4_address ) {
179+ normalized . ipv4_address = network . ipv4_address ;
180+ }
181+ return normalized ;
182+ } ;
183+
184+ // Function to merge base and user networks, ensuring proper type for ipv4_address and aliases
185+ const mergeNetworks = (
186+ base : ComposeServiceNetworksObj ,
187+ user : ComposeServiceNetworksObj
188+ ) : ComposeServiceNetworksObj => {
189+ const merged : ComposeServiceNetworksObj = { } ;
190+
191+ // Iterate over the keys of the base networks
192+ for ( const networkName in base ) {
193+ merged [ networkName ] = { ...base [ networkName ] } ; // Start with a shallow copy of the base network
194+
195+ if ( user [ networkName ] ) {
196+ // If the user has provided overrides for this network, merge them
197+ for ( const subKey in user [ networkName ] ) {
198+ if ( subKey === "ipv4_address" ) {
199+ merged [ networkName ] . ipv4_address = base [ networkName ] . ipv4_address ; // Always take base ipv4_address
200+ } else if ( subKey === "aliases" ) {
201+ // Merge and deduplicate aliases
202+ const baseAliases = base [ networkName ] . aliases || [ ] ;
203+ const userAliases = user [ networkName ] . aliases || [ ] ;
204+ merged [ networkName ] . aliases = Array . from ( new Set ( [ ...baseAliases , ...userAliases ] ) ) ;
205+ }
206+ }
207+ } else {
208+ // If user doesn't have settings for the network, just copy the base network
209+ if ( ! merged [ networkName ] . ipv4_address && base [ networkName ] . ipv4_address ) {
210+ merged [ networkName ] . ipv4_address = base [ networkName ] . ipv4_address ;
211+ }
212+ }
213+ }
214+
215+ // Ensure that networks that don't have user settings but exist in base are still included
216+ for ( const key in user ) {
217+ if ( ! ( key in base ) ) {
218+ merged [ key ] = { ...user [ key ] } ; // Add user networks that don't exist in base
219+ }
220+ }
221+
222+ return merged ;
223+ } ;
224+
225+ // Normalize the merged networks to ensure types are correct and compatible with Compose
226+ const nextNetworks = Object . fromEntries (
227+ Object . entries ( mergeNetworks ( networks , userSetNetworks ) ) . map ( ( [ key , config ] ) => [ key , normalizeNetwork ( config ) ] )
169228 ) ;
170229
171230 // ##### <DEPRECATED> Kept for legacy compatibility
0 commit comments