fix(vpn,target-profiles): normalize target profile route references and stabilize VPN host-IP client routing behavior
This commit is contained in:
@@ -28,7 +28,7 @@ function setupFormVisibility(formEl: any) {
|
||||
const staticIpGroup = contentEl.querySelector('.staticIpGroup') as HTMLElement;
|
||||
const vlanIdGroup = contentEl.querySelector('.vlanIdGroup') as HTMLElement;
|
||||
const aclGroup = contentEl.querySelector('.aclGroup') as HTMLElement;
|
||||
if (hostIpGroup) hostIpGroup.style.display = show; // always show (forceTarget is always on)
|
||||
if (hostIpGroup) hostIpGroup.style.display = show;
|
||||
if (hostIpDetails) hostIpDetails.style.display = data.useHostIp ? show : 'none';
|
||||
if (staticIpGroup) staticIpGroup.style.display = data.useDhcp ? 'none' : show;
|
||||
if (vlanIdGroup) vlanIdGroup.style.display = data.forceVlan ? show : 'none';
|
||||
@@ -390,7 +390,7 @@ export class OpsViewVpn extends DeesElement {
|
||||
if (!form) return;
|
||||
const data = await form.collectFormData();
|
||||
if (!data.clientId) return;
|
||||
const targetProfileIds = this.resolveProfileNamesToIds(
|
||||
const targetProfileIds = this.resolveProfileLabelsToIds(
|
||||
Array.isArray(data.targetProfileNames) ? data.targetProfileNames : [],
|
||||
);
|
||||
|
||||
@@ -414,10 +414,10 @@ export class OpsViewVpn extends DeesElement {
|
||||
description: data.description || undefined,
|
||||
targetProfileIds,
|
||||
|
||||
useHostIp: useHostIp || undefined,
|
||||
useDhcp: useDhcp || undefined,
|
||||
useHostIp,
|
||||
useDhcp,
|
||||
staticIp,
|
||||
forceVlan: forceVlan || undefined,
|
||||
forceVlan,
|
||||
vlanId,
|
||||
destinationAllowList,
|
||||
destinationBlockList,
|
||||
@@ -485,7 +485,7 @@ export class OpsViewVpn extends DeesElement {
|
||||
<div class="infoItem"><span class="infoLabel">Transport</span><span class="infoValue">${conn.transport}</span></div>
|
||||
` : ''}
|
||||
<div class="infoItem"><span class="infoLabel">Description</span><span class="infoValue">${client.description || '-'}</span></div>
|
||||
<div class="infoItem"><span class="infoLabel">Target Profiles</span><span class="infoValue">${this.resolveProfileIdsToNames(client.targetProfileIds)?.join(', ') || '-'}</span></div>
|
||||
<div class="infoItem"><span class="infoLabel">Target Profiles</span><span class="infoValue">${this.resolveProfileIdsToLabels(client.targetProfileIds)?.join(', ') || '-'}</span></div>
|
||||
<div class="infoItem"><span class="infoLabel">Routing</span><span class="infoValue">${client.useHostIp ? 'Host IP' : 'SmartProxy'}</span></div>
|
||||
${client.useHostIp ? html`
|
||||
<div class="infoItem"><span class="infoLabel">Host IP</span><span class="infoValue">${client.useDhcp ? 'DHCP' : client.staticIp ? `Static: ${client.staticIp}` : 'Not configured'}</span></div>
|
||||
@@ -649,7 +649,7 @@ export class OpsViewVpn extends DeesElement {
|
||||
const client = actionData.item as interfaces.data.IVpnClient;
|
||||
const { DeesModal } = await import('@design.estate/dees-catalog');
|
||||
const currentDescription = client.description ?? '';
|
||||
const currentTargetProfileNames = this.resolveProfileIdsToNames(client.targetProfileIds) || [];
|
||||
const currentTargetProfileNames = this.resolveProfileIdsToLabels(client.targetProfileIds) || [];
|
||||
const profileCandidates = this.getTargetProfileCandidates();
|
||||
const currentUseHostIp = client.useHostIp ?? false;
|
||||
const currentUseDhcp = client.useDhcp ?? false;
|
||||
@@ -695,7 +695,7 @@ export class OpsViewVpn extends DeesElement {
|
||||
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
|
||||
if (!form) return;
|
||||
const data = await form.collectFormData();
|
||||
const targetProfileIds = this.resolveProfileNamesToIds(
|
||||
const targetProfileIds = this.resolveProfileLabelsToIds(
|
||||
Array.isArray(data.targetProfileNames) ? data.targetProfileNames : [],
|
||||
);
|
||||
|
||||
@@ -719,10 +719,10 @@ export class OpsViewVpn extends DeesElement {
|
||||
description: data.description || undefined,
|
||||
targetProfileIds,
|
||||
|
||||
useHostIp: useHostIp || undefined,
|
||||
useDhcp: useDhcp || undefined,
|
||||
useHostIp,
|
||||
useDhcp,
|
||||
staticIp,
|
||||
forceVlan: forceVlan || undefined,
|
||||
forceVlan,
|
||||
vlanId,
|
||||
destinationAllowList,
|
||||
destinationBlockList,
|
||||
@@ -811,41 +811,52 @@ export class OpsViewVpn extends DeesElement {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build autocomplete candidates from loaded target profiles.
|
||||
* viewKey = profile name (displayed), payload = { id } (carried for resolution).
|
||||
* Build stable profile labels for list inputs.
|
||||
*/
|
||||
private getTargetProfileCandidates() {
|
||||
private getTargetProfileChoices() {
|
||||
const profileState = appstate.targetProfilesStatePart.getState();
|
||||
const profiles = profileState?.profiles || [];
|
||||
return profiles.map((p) => ({ viewKey: p.name, payload: { id: p.id } }));
|
||||
const nameCounts = new Map<string, number>();
|
||||
|
||||
for (const profile of profiles) {
|
||||
nameCounts.set(profile.name, (nameCounts.get(profile.name) || 0) + 1);
|
||||
}
|
||||
|
||||
return profiles.map((profile) => ({
|
||||
id: profile.id,
|
||||
label: (nameCounts.get(profile.name) || 0) > 1
|
||||
? `${profile.name} (${profile.id})`
|
||||
: profile.name,
|
||||
}));
|
||||
}
|
||||
|
||||
private getTargetProfileCandidates() {
|
||||
return this.getTargetProfileChoices().map((profile) => ({ viewKey: profile.label }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert profile IDs to profile names (for populating edit form values).
|
||||
* Convert profile IDs to form labels (for populating edit form values).
|
||||
*/
|
||||
private resolveProfileIdsToNames(ids?: string[]): string[] | undefined {
|
||||
private resolveProfileIdsToLabels(ids?: string[]): string[] | undefined {
|
||||
if (!ids?.length) return undefined;
|
||||
const profileState = appstate.targetProfilesStatePart.getState();
|
||||
const profiles = profileState?.profiles || [];
|
||||
const choices = this.getTargetProfileChoices();
|
||||
const labelsById = new Map(choices.map((profile) => [profile.id, profile.label]));
|
||||
return ids.map((id) => {
|
||||
const profile = profiles.find((p) => p.id === id);
|
||||
return profile?.name || id;
|
||||
return labelsById.get(id) || id;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert profile names back to IDs (for saving form data).
|
||||
* Uses the dees-input-list candidates' payload when available.
|
||||
* Convert profile form labels back to IDs.
|
||||
*/
|
||||
private resolveProfileNamesToIds(names: string[]): string[] | undefined {
|
||||
if (!names.length) return undefined;
|
||||
const profileState = appstate.targetProfilesStatePart.getState();
|
||||
const profiles = profileState?.profiles || [];
|
||||
return names
|
||||
.map((name) => {
|
||||
const profile = profiles.find((p) => p.name === name);
|
||||
return profile?.id;
|
||||
})
|
||||
private resolveProfileLabelsToIds(labels: string[]): string[] {
|
||||
if (!labels.length) return [];
|
||||
|
||||
const labelsToIds = new Map(
|
||||
this.getTargetProfileChoices().map((profile) => [profile.label, profile.id]),
|
||||
);
|
||||
return labels
|
||||
.map((label) => labelsToIds.get(label))
|
||||
.filter((id): id is string => !!id);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user