feat: add extra Docker networks to container config
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m23s
CI / docker (push) Successful in 1m7s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 38s

Apps can now join additional Docker networks (e.g., monitoring,
prometheus) configured via containerConfig.extraNetworks. Flows through
the 3-layer config merge. Networks are created if absent and containers
are connected during deployment. UI adds a pill-list field on the
Resources tab (both create and edit views).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-12 16:53:01 +02:00
parent 5b6543b167
commit be96336974
4 changed files with 68 additions and 2 deletions

View File

@@ -211,6 +211,8 @@ function CreateAppView({ environments, selectedEnv }: { environments: Environmen
const [sslOffloading, setSslOffloading] = useState(true);
const [runtimeType, setRuntimeType] = useState(String(defaults.runtimeType ?? 'auto'));
const [customArgs, setCustomArgs] = useState(String(defaults.customArgs ?? ''));
const [extraNetworks, setExtraNetworks] = useState<string[]>(Array.isArray(defaults.extraNetworks) ? defaults.extraNetworks as string[] : []);
const [newNetwork, setNewNetwork] = useState('');
const [configTab, setConfigTab] = useState<'monitoring' | 'resources' | 'variables'>('monitoring');
const [busy, setBusy] = useState(false);
@@ -226,6 +228,7 @@ function CreateAppView({ environments, selectedEnv }: { environments: Environmen
setPorts(Array.isArray(d.exposedPorts) ? d.exposedPorts as number[] : []);
setRuntimeType(String(d.runtimeType ?? 'auto'));
setCustomArgs(String(d.customArgs ?? ''));
setExtraNetworks(Array.isArray(d.extraNetworks) ? d.extraNetworks as string[] : []);
}, [envId, environments]);
useEffect(() => {
@@ -267,6 +270,7 @@ function CreateAppView({ environments, selectedEnv }: { environments: Environmen
sslOffloading: sslOffloading,
runtimeType: runtimeType,
customArgs: customArgs || null,
extraNetworks: extraNetworks,
};
await updateContainerConfig.mutateAsync({ appId: app.slug, config: containerConfig });
@@ -500,6 +504,23 @@ function CreateAppView({ environments, selectedEnv }: { environments: Environmen
<Toggle checked={sslOffloading} onChange={() => !busy && setSslOffloading(!sslOffloading)} disabled={busy} />
<span className={sslOffloading ? styles.toggleEnabled : styles.toggleDisabled}>{sslOffloading ? 'Enabled' : 'Disabled'}</span>
</div>
<span className={styles.configLabel}>Extra Networks</span>
<div>
<div className={styles.portPills}>
{extraNetworks.map((n) => (
<span key={n} className={styles.portPill}>
{n}
<button className={styles.portPillDelete} disabled={busy}
onClick={() => !busy && setExtraNetworks(extraNetworks.filter((x) => x !== n))}>&times;</button>
</span>
))}
<input className={styles.portAddInput} disabled={busy} placeholder="+ network" value={newNetwork}
onChange={(e) => setNewNetwork(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); const v = newNetwork.trim(); if (v && !extraNetworks.includes(v)) { setExtraNetworks([...extraNetworks, v]); setNewNetwork(''); } } }} />
</div>
<span className={styles.configHint}>Additional Docker networks to join (e.g., monitoring, prometheus)</span>
</div>
</div>
</div>
)}
@@ -829,6 +850,8 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
const [sslOffloading, setSslOffloading] = useState(true);
const [runtimeType, setRuntimeType] = useState(String(merged.runtimeType ?? 'auto'));
const [customArgs, setCustomArgs] = useState(String(merged.customArgs ?? ''));
const [extraNetworks, setExtraNetworks] = useState<string[]>(Array.isArray(merged.extraNetworks) ? merged.extraNetworks as string[] : []);
const [newNetwork, setNewNetwork] = useState('');
// Versions query for runtime detection hints
const { data: versions = [] } = useAppVersions(app.slug);
@@ -862,6 +885,7 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
setSslOffloading(merged.sslOffloading !== false);
setRuntimeType(String(merged.runtimeType ?? 'auto'));
setCustomArgs(String(merged.customArgs ?? ''));
setExtraNetworks(Array.isArray(merged.extraNetworks) ? merged.extraNetworks as string[] : []);
}, [agentConfig, merged]);
useEffect(() => { syncFromServer(); }, [syncFromServer]);
@@ -913,6 +937,7 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
sslOffloading: sslOffloading,
runtimeType: runtimeType,
customArgs: customArgs || null,
extraNetworks: extraNetworks,
};
try {
await updateContainerConfig.mutateAsync({ appId: app.slug, config: containerConfig });
@@ -1198,6 +1223,23 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
<Toggle checked={sslOffloading} onChange={() => editing && setSslOffloading(!sslOffloading)} disabled={!editing} />
<span className={sslOffloading ? styles.toggleEnabled : styles.toggleDisabled}>{sslOffloading ? 'Enabled' : 'Disabled'}</span>
</div>
<span className={styles.configLabel}>Extra Networks</span>
<div>
<div className={styles.portPills}>
{extraNetworks.map((n) => (
<span key={n} className={styles.portPill}>
{n}
<button className={styles.portPillDelete} disabled={!editing}
onClick={() => editing && setExtraNetworks(extraNetworks.filter((x) => x !== n))}>&times;</button>
</span>
))}
<input className={styles.portAddInput} disabled={!editing} placeholder="+ network" value={newNetwork}
onChange={(e) => setNewNetwork(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); const v = newNetwork.trim(); if (v && !extraNetworks.includes(v)) { setExtraNetworks([...extraNetworks, v]); setNewNetwork(''); } } }} />
</div>
<span className={styles.configHint}>Additional Docker networks to join (e.g., monitoring, prometheus)</span>
</div>
</div>
</div>
)}