fix(routes): support profile and target metadata in route creation and refresh remote ingress routes after config initialization

This commit is contained in:
2026-04-02 17:27:05 +00:00
parent 0577f45ced
commit 55f5465a9a
9 changed files with 121 additions and 45 deletions

View File

@@ -24,6 +24,14 @@ export class OpsViewRoutes extends DeesElement {
lastUpdated: 0,
};
@state() accessor profilesTargetsState: appstate.IProfilesTargetsState = {
profiles: [],
targets: [],
isLoading: false,
error: null,
lastUpdated: 0,
};
constructor() {
super();
const sub = appstate.routeManagementStatePart
@@ -33,6 +41,13 @@ export class OpsViewRoutes extends DeesElement {
});
this.rxSubscriptions.push(sub);
const ptSub = appstate.profilesTargetsStatePart
.select((s) => s)
.subscribe((ptState) => {
this.profilesTargetsState = ptState;
});
this.rxSubscriptions.push(ptSub);
// Re-fetch routes when user logs in (fixes race condition where
// the view is created before authentication completes)
const loginSub = appstate.loginStatePart
@@ -40,6 +55,7 @@ export class OpsViewRoutes extends DeesElement {
.subscribe((isLoggedIn) => {
if (isLoggedIn) {
appstate.routeManagementStatePart.dispatchAction(appstate.fetchMergedRoutesAction, null);
appstate.profilesTargetsStatePart.dispatchAction(appstate.fetchProfilesAndTargetsAction, null);
}
});
this.rxSubscriptions.push(loginSub);
@@ -145,6 +161,7 @@ export class OpsViewRoutes extends DeesElement {
enabled: mr.enabled,
tags,
id: mr.storedRouteId || mr.route.name || undefined,
metadata: mr.metadata,
};
});
@@ -275,6 +292,7 @@ export class OpsViewRoutes extends DeesElement {
});
} else {
// Programmatic route
const meta = merged.metadata;
await DeesModal.createAndShow({
heading: `Route: ${merged.route.name}`,
content: html`
@@ -282,6 +300,8 @@ export class OpsViewRoutes extends DeesElement {
<p>Source: <strong style="color: #0af;">programmatic</strong></p>
<p>Status: <strong>${merged.enabled ? 'Enabled' : 'Disabled'}</strong></p>
<p>ID: <code style="color: #888;">${merged.storedRouteId}</code></p>
${meta?.securityProfileName ? html`<p>Security Profile: <strong style="color: #a78bfa;">${meta.securityProfileName}</strong></p>` : ''}
${meta?.networkTargetName ? html`<p>Network Target: <strong style="color: #a78bfa;">${meta.networkTargetName}</strong></p>` : ''}
</div>
`,
menuOptions: [
@@ -319,6 +339,24 @@ export class OpsViewRoutes extends DeesElement {
private async showCreateRouteDialog() {
const { DeesModal } = await import('@design.estate/dees-catalog');
const profiles = this.profilesTargetsState.profiles;
const targets = this.profilesTargetsState.targets;
// Build dropdown options for profiles and targets
const profileOptions = [
{ key: '', option: '(none — inline security)' },
...profiles.map((p) => ({
key: p.id,
option: `${p.name}${p.description ? ' — ' + p.description : ''}`,
})),
];
const targetOptions = [
{ key: '', option: '(none — inline target)' },
...targets.map((t) => ({
key: t.id,
option: `${t.name} (${Array.isArray(t.host) ? t.host.join(',') : t.host}:${t.port})`,
})),
];
await DeesModal.createAndShow({
heading: 'Add Programmatic Route',
@@ -327,8 +365,10 @@ export class OpsViewRoutes extends DeesElement {
<dees-input-text .key=${'name'} .label=${'Route Name'} .required=${true}></dees-input-text>
<dees-input-text .key=${'ports'} .label=${'Ports (comma-separated)'} .required=${true}></dees-input-text>
<dees-input-text .key=${'domains'} .label=${'Domains (comma-separated, optional)'}></dees-input-text>
<dees-input-text .key=${'targetHost'} .label=${'Target Host'} .value=${'localhost'} .required=${true}></dees-input-text>
<dees-input-text .key=${'targetPort'} .label=${'Target Port'} .required=${true}></dees-input-text>
<dees-input-dropdown .key=${'securityProfileRef'} .label=${'Security Profile'} .options=${profileOptions} .selectedKey=${''}></dees-input-dropdown>
<dees-input-dropdown .key=${'networkTargetRef'} .label=${'Network Target'} .options=${targetOptions} .selectedKey=${''}></dees-input-dropdown>
<dees-input-text .key=${'targetHost'} .label=${'Target Host (if no target selected)'} .value=${'localhost'}></dees-input-text>
<dees-input-text .key=${'targetPort'} .label=${'Target Port (if no target selected)'}></dees-input-text>
</dees-form>
`,
menuOptions: [
@@ -362,15 +402,27 @@ export class OpsViewRoutes extends DeesElement {
targets: [
{
host: formData.targetHost || 'localhost',
port: parseInt(formData.targetPort, 10),
port: parseInt(formData.targetPort, 10) || 443,
},
],
},
};
// Build metadata if profile/target selected
const metadata: any = {};
if (formData.securityProfileRef) {
metadata.securityProfileRef = formData.securityProfileRef;
}
if (formData.networkTargetRef) {
metadata.networkTargetRef = formData.networkTargetRef;
}
await appstate.routeManagementStatePart.dispatchAction(
appstate.createRouteAction,
{ route },
{
route,
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
},
);
await modalArg.destroy();
},