Compare commits

...

6 Commits

Author SHA1 Message Date
59e0d41bdb v12.9.1
Some checks failed
Docker (tags) / security (push) Failing after 2s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2026-04-04 17:08:05 +00:00
9509d87b1e fix(monitoring): update SmartProxy and use direct connection protocol metrics access 2026-04-04 17:08:05 +00:00
b835e2d0eb v12.9.0
Some checks failed
Docker (tags) / security (push) Failing after 3s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2026-04-04 16:45:02 +00:00
6c3d8714a2 feat(monitoring): add frontend and backend protocol distribution metrics to network stats 2026-04-04 16:45:02 +00:00
94f53f0259 v12.8.1
Some checks failed
Docker (tags) / security (push) Failing after 3s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2026-04-04 11:00:03 +00:00
1004f8579f fix(ops-view-routes): correct route form dropdown selection handling for security profiles and network targets 2026-04-04 11:00:03 +00:00
15 changed files with 181 additions and 39 deletions

View File

@@ -1,5 +1,26 @@
# Changelog
## 2026-04-04 - 12.9.1 - fix(monitoring)
update SmartProxy and use direct connection protocol metrics access
- bump @push.rocks/smartproxy from ^27.1.0 to ^27.2.0
- replace fallback any-based access with direct frontend and backend protocol metric calls in MetricsManager
## 2026-04-04 - 12.9.0 - feat(monitoring)
add frontend and backend protocol distribution metrics to network stats
- Expose frontend and backend protocol distribution data in monitoring metrics, stats responses, and shared interfaces.
- Render protocol distribution donut charts in the ops network view using the new stats fields.
- Preserve existing stored certificate IDs when updating certificate records by domain.
- Bump @design.estate/dees-catalog to ^3.55.5 for the new chart component support.
## 2026-04-04 - 12.8.1 - fix(ops-view-routes)
correct route form dropdown selection handling for security profiles and network targets
- Update route edit and create forms to use selectedOption for dropdowns backed by the newer dees-catalog version
- Normalize submitted dropdown values to extract option keys before storing securityProfileRef and networkTargetRef
- Refresh documentation to reflect expanded stats coverage for network, RADIUS, and VPN metrics
## 2026-04-03 - 12.8.0 - feat(certificates)
add force renew option for domain certificate reprovisioning

View File

@@ -1,7 +1,7 @@
{
"name": "@serve.zone/dcrouter",
"private": false,
"version": "12.8.0",
"version": "12.9.1",
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
"type": "module",
"exports": {
@@ -35,7 +35,7 @@
"@api.global/typedserver": "^8.4.6",
"@api.global/typedsocket": "^4.1.2",
"@apiclient.xyz/cloudflare": "^7.1.0",
"@design.estate/dees-catalog": "^3.52.3",
"@design.estate/dees-catalog": "^3.55.5",
"@design.estate/dees-element": "^2.2.4",
"@push.rocks/lik": "^6.4.0",
"@push.rocks/projectinfo": "^5.1.0",
@@ -53,7 +53,7 @@
"@push.rocks/smartnetwork": "^4.5.2",
"@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartproxy": "^27.1.0",
"@push.rocks/smartproxy": "^27.2.0",
"@push.rocks/smartradius": "^1.1.1",
"@push.rocks/smartrequest": "^5.0.1",
"@push.rocks/smartrx": "^3.0.10",

55
pnpm-lock.yaml generated
View File

@@ -24,8 +24,8 @@ importers:
specifier: ^7.1.0
version: 7.1.0
'@design.estate/dees-catalog':
specifier: ^3.52.3
version: 3.52.3(@tiptap/pm@2.27.2)
specifier: ^3.55.5
version: 3.55.5(@tiptap/pm@2.27.2)
'@design.estate/dees-element':
specifier: ^2.2.4
version: 2.2.4
@@ -78,8 +78,8 @@ importers:
specifier: ^4.2.3
version: 4.2.3
'@push.rocks/smartproxy':
specifier: ^27.1.0
version: 27.1.0
specifier: ^27.2.0
version: 27.2.0
'@push.rocks/smartradius':
specifier: ^1.1.1
version: 1.1.1
@@ -350,8 +350,8 @@ packages:
'@configvault.io/interfaces@1.0.17':
resolution: {integrity: sha512-bEcCUR2VBDJsTin8HQh8Uw/mlYl2v8A3jMIaQ+MTB9Hrqd6CZL2dL7iJdWyFl/3EIX+LDxWFR+Oq7liIq7w+1Q==}
'@design.estate/dees-catalog@3.52.3':
resolution: {integrity: sha512-4/vybRZQtdkpa3IOZQ/EbL6gGjIMO+430db43RRfdw+HPXird7Jl+oIXz6pDh+rGah2H2+Srb/c+eve46xAhtQ==}
'@design.estate/dees-catalog@3.55.5':
resolution: {integrity: sha512-NAMUkTVqdZZmwI/g1xKOxOYM9QUd9FHODh6MYkP6LhLjD0NOGh3bITCnNN9Z3x8/mI7vQQOlSe9tyTtxCP1itQ==}
'@design.estate/dees-comms@1.0.30':
resolution: {integrity: sha512-KchMlklJfKAjQiJiR0xmofXtQ27VgZtBIxcMwPE9d+h3jJRv+lPZxzBQVOM0eyM0uS44S5vJMZ11IeV4uDXSHg==}
@@ -1279,8 +1279,8 @@ packages:
'@push.rocks/smartpromise@4.2.3':
resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==}
'@push.rocks/smartproxy@27.1.0':
resolution: {integrity: sha512-uMtmbT6/9Y+lOnSi4w6SRICWJr9q9bHsYAq6xMLmym3zvnEzEwJWF6sw4Jb/uEFEjI2/e4irNSQ9Ba74DhFRlg==}
'@push.rocks/smartproxy@27.2.0':
resolution: {integrity: sha512-F7stwFDv2BLyDK5WY7WocNbsxrjfuT+X1R0O3Ii4fw1weRDekOyxUPmeK/QcWxBdp+LgsSuVvBEv3sFpQHipvw==}
'@push.rocks/smartpuppeteer@2.0.5':
resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==}
@@ -2516,6 +2516,9 @@ packages:
ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
echarts@5.6.0:
resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -2866,8 +2869,8 @@ packages:
humanize-ms@1.2.1:
resolution: {integrity: sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=}
ibantools@4.5.1:
resolution: {integrity: sha512-DfKQpLlFq9yEUIEnFuCJzss3XavD7iHZTU5PyqXiAJ+rmaMp+NFP3hboumHKuK8nZjuOJg93WemTzcQ5b9jOZA==}
ibantools@4.5.2:
resolution: {integrity: sha512-is+8TgZcKS/AMv/z9nW1zz0bhjhoyjpA1p0nc3A6GkW/InOdcQiUZpkufADzh/aO/LY/TOD/P3oPWncNRn5QMA==}
iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
@@ -4068,6 +4071,9 @@ packages:
tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
tslib@2.3.0:
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@@ -4315,6 +4321,9 @@ packages:
zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
zrender@5.6.1:
resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
@@ -4342,7 +4351,7 @@ snapshots:
'@api.global/typedrequest-interfaces': 3.0.19
'@api.global/typedsocket': 4.1.2(@push.rocks/smartserve@2.0.3)
'@cloudflare/workers-types': 4.20260317.1
'@design.estate/dees-catalog': 3.52.3(@tiptap/pm@2.27.2)
'@design.estate/dees-catalog': 3.55.5(@tiptap/pm@2.27.2)
'@design.estate/dees-comms': 1.0.30
'@push.rocks/lik': 6.4.0
'@push.rocks/smartdelay': 3.0.5
@@ -4871,7 +4880,7 @@ snapshots:
dependencies:
'@api.global/typedrequest-interfaces': 3.0.19
'@design.estate/dees-catalog@3.52.3(@tiptap/pm@2.27.2)':
'@design.estate/dees-catalog@3.55.5(@tiptap/pm@2.27.2)':
dependencies:
'@design.estate/dees-domtools': 2.5.4
'@design.estate/dees-element': 2.2.4
@@ -4891,8 +4900,9 @@ snapshots:
'@tiptap/extension-underline': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))
'@tiptap/starter-kit': 2.27.2
'@tsclass/tsclass': 9.5.0
echarts: 5.6.0
highlight.js: 11.11.1
ibantools: 4.5.1
ibantools: 4.5.2
lightweight-charts: 5.1.0
lucide: 0.577.0
monaco-editor: 0.55.1
@@ -6508,11 +6518,11 @@ snapshots:
'@push.rocks/smartpromise@4.2.3': {}
'@push.rocks/smartproxy@27.1.0':
'@push.rocks/smartproxy@27.2.0':
dependencies:
'@push.rocks/smartcrypto': 2.0.4
'@push.rocks/smartlog': 3.2.1
'@push.rocks/smartnftables': 1.0.1
'@push.rocks/smartnftables': 1.1.0
'@push.rocks/smartrust': 1.3.2
'@tsclass/tsclass': 9.5.0
minimatch: 10.2.4
@@ -6909,7 +6919,7 @@ snapshots:
'@serve.zone/catalog@2.11.0(@tiptap/pm@2.27.2)':
dependencies:
'@design.estate/dees-catalog': 3.52.3(@tiptap/pm@2.27.2)
'@design.estate/dees-catalog': 3.55.5(@tiptap/pm@2.27.2)
'@design.estate/dees-domtools': 2.5.4
'@design.estate/dees-element': 2.2.4
'@design.estate/dees-wcctools': 3.8.0
@@ -7978,6 +7988,11 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
echarts@5.6.0:
dependencies:
tslib: 2.3.0
zrender: 5.6.1
emoji-regex@8.0.0: {}
encoding-japanese@2.2.0: {}
@@ -8410,7 +8425,7 @@ snapshots:
dependencies:
ms: 2.1.3
ibantools@4.5.1: {}
ibantools@4.5.2: {}
iconv-lite@0.4.24:
dependencies:
@@ -9942,6 +9957,8 @@ snapshots:
tslib@1.14.1: {}
tslib@2.3.0: {}
tslib@2.8.1: {}
tsx@4.21.0:
@@ -10169,4 +10186,8 @@ snapshots:
zod@3.25.76: {}
zrender@5.6.1:
dependencies:
tslib: 2.3.0
zwitch@2.0.4: {}

View File

@@ -25,7 +25,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
- [Remote Ingress](#remote-ingress)
- [VPN Access Control](#vpn-access-control)
- [Certificate Management](#certificate-management)
- [Storage & Caching](#storage--caching)
- [Storage & Database](#storage--database)
- [Security Features](#security-features)
- [OpsServer Dashboard](#opsserver-dashboard)
- [API Client](#api-client)

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/dcrouter',
version: '12.8.0',
version: '12.9.1',
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
}

View File

@@ -29,9 +29,9 @@ export class StorageBackedCertManager implements plugins.smartacme.ICertManager
let doc = await AcmeCertDoc.findByDomain(cert.domainName);
if (!doc) {
doc = new AcmeCertDoc();
doc.id = cert.id;
doc.domainName = cert.domainName;
}
doc.id = cert.id;
doc.created = cert.created;
doc.privateKey = cert.privateKey;
doc.publicKey = cert.publicKey;

View File

@@ -591,6 +591,10 @@ export class MetricsManager {
const requestsPerSecond = proxyMetrics.requests.perSecond();
const requestsTotal = proxyMetrics.requests.total();
// Get frontend/backend protocol distribution
const frontendProtocols = proxyMetrics.connections.frontendProtocols() ?? null;
const backendProtocols = proxyMetrics.connections.backendProtocols() ?? null;
// Collect backend protocol data
const backendMetrics = proxyMetrics.backends.byBackend();
const protocolCache = proxyMetrics.backends.detectedProtocols();
@@ -705,6 +709,8 @@ export class MetricsManager {
requestsPerSecond,
requestsTotal,
backends,
frontendProtocols,
backendProtocols,
};
}, 1000); // 1s cache — matches typical dashboard poll interval
}

View File

@@ -311,6 +311,8 @@ export class StatsHandler {
requestsPerSecond: stats.requestsPerSecond || 0,
requestsTotal: stats.requestsTotal || 0,
backends: stats.backends || [],
frontendProtocols: stats.frontendProtocols || null,
backendProtocols: stats.backendProtocols || null,
};
})()
);

View File

@@ -166,6 +166,21 @@ export interface INetworkMetrics {
requestsPerSecond?: number;
requestsTotal?: number;
backends?: IBackendInfo[];
frontendProtocols?: IProtocolDistribution | null;
backendProtocols?: IProtocolDistribution | null;
}
export interface IProtocolDistribution {
h1Active: number;
h1Total: number;
h2Active: number;
h2Total: number;
h3Active: number;
h3Total: number;
wsActive: number;
wsTotal: number;
otherActive: number;
otherTotal: number;
}
export interface IConnectionDetails {

View File

@@ -80,6 +80,8 @@ interface IIdentity {
| `IQueueStatus` | Queue name, size, processing/failed/retrying counts |
| `IHealthStatus` | Healthy flag, uptime, per-service status map |
| `INetworkMetrics` | Bandwidth, connection counts, top endpoints |
| `IRadiusStats` | Running, uptime, auth requests/accepts/rejects, sessions, data transfer |
| `IVpnStats` | Running, subnet, registered/connected clients, WireGuard port |
| `ILogEntry` | Timestamp, level, category, message, metadata |
#### Route Management Interfaces
@@ -135,7 +137,8 @@ TypedRequest interfaces for the OpsServer API, organized by domain:
| `IReq_GetActiveConnections` | `getActiveConnections` | Active connection list |
| `IReq_GetQueueStatus` | `getQueueStatus` | Email queue status |
| `IReq_GetHealthStatus` | `getHealthStatus` | System health check |
| `IReq_GetCombinedMetrics` | `getCombinedMetrics` | All metrics in one request |
| `IReq_GetNetworkStats` | `getNetworkStats` | Network throughput and connection analytics |
| `IReq_GetCombinedMetrics` | `getCombinedMetrics` | All metrics in one request (server, email, DNS, security, network, RADIUS, VPN) |
#### ⚙️ Configuration
| Interface | Method | Description |

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/dcrouter',
version: '12.8.0',
version: '12.9.1',
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
}

View File

@@ -56,6 +56,8 @@ export interface INetworkState {
requestsPerSecond: number;
requestsTotal: number;
backends: interfaces.data.IBackendInfo[];
frontendProtocols: interfaces.data.IProtocolDistribution | null;
backendProtocols: interfaces.data.IProtocolDistribution | null;
lastUpdated: number;
isLoading: boolean;
error: string | null;
@@ -154,6 +156,8 @@ export const networkStatePart = await appState.getStatePart<INetworkState>(
requestsPerSecond: 0,
requestsTotal: 0,
backends: [],
frontendProtocols: null,
backendProtocols: null,
lastUpdated: 0,
isLoading: false,
error: null,
@@ -523,6 +527,8 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
requestsPerSecond: networkStatsResponse.requestsPerSecond || 0,
requestsTotal: networkStatsResponse.requestsTotal || 0,
backends: networkStatsResponse.backends || [],
frontendProtocols: networkStatsResponse.frontendProtocols || null,
backendProtocols: networkStatsResponse.backendProtocols || null,
lastUpdated: Date.now(),
isLoading: false,
error: null,
@@ -1845,6 +1851,8 @@ async function dispatchCombinedRefreshActionInner() {
requestsPerSecond: network.requestsPerSecond || 0,
requestsTotal: network.requestsTotal || 0,
backends: network.backends || [],
frontendProtocols: network.frontendProtocols || null,
backendProtocols: network.backendProtocols || null,
lastUpdated: Date.now(),
isLoading: false,
error: null,
@@ -1866,6 +1874,8 @@ async function dispatchCombinedRefreshActionInner() {
requestsPerSecond: network.requestsPerSecond || 0,
requestsTotal: network.requestsTotal || 0,
backends: network.backends || [],
frontendProtocols: network.frontendProtocols || null,
backendProtocols: network.backendProtocols || null,
lastUpdated: Date.now(),
isLoading: false,
error: null,

View File

@@ -274,6 +274,12 @@ export class OpsViewNetwork extends DeesElement {
background: ${cssManager.bdTheme('#fff3e0', '#3a2a1a')};
color: ${cssManager.bdTheme('#f57c00', '#ff9933')};
}
.protocolChartGrid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
`,
];
@@ -305,6 +311,9 @@ export class OpsViewNetwork extends DeesElement {
.yAxisFormatter=${(val: number) => `${val} Mbit/s`}
></dees-chart-area>
<!-- Protocol Distribution Charts -->
${this.renderProtocolCharts()}
<!-- Top IPs Section -->
${this.renderTopIPs()}
@@ -527,7 +536,54 @@ export class OpsViewNetwork extends DeesElement {
`;
}
private renderProtocolCharts(): TemplateResult {
const fp = this.networkState.frontendProtocols;
const bp = this.networkState.backendProtocols;
const protoColors: Record<string, string> = {
'HTTP/1.1': '#1976d2',
'HTTP/2': '#388e3c',
'HTTP/3': '#7b1fa2',
'WebSocket': '#f57c00',
'Other': '#757575',
};
const buildDonutData = (dist: interfaces.data.IProtocolDistribution | null) => {
if (!dist) return [];
const items: Array<{ name: string; value: number; color: string }> = [];
if (dist.h1Active > 0) items.push({ name: 'HTTP/1.1', value: dist.h1Active, color: protoColors['HTTP/1.1'] });
if (dist.h2Active > 0) items.push({ name: 'HTTP/2', value: dist.h2Active, color: protoColors['HTTP/2'] });
if (dist.h3Active > 0) items.push({ name: 'HTTP/3', value: dist.h3Active, color: protoColors['HTTP/3'] });
if (dist.wsActive > 0) items.push({ name: 'WebSocket', value: dist.wsActive, color: protoColors['WebSocket'] });
if (dist.otherActive > 0) items.push({ name: 'Other', value: dist.otherActive, color: protoColors['Other'] });
return items;
};
const frontendData = buildDonutData(fp);
const backendData = buildDonutData(bp);
return html`
<div class="protocolChartGrid">
<dees-chart-donut
.label=${'Frontend Protocols'}
.data=${frontendData.length > 0 ? frontendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
.showLegend=${true}
.showLabels=${true}
.innerRadiusPercent=${'55%'}
.valueFormatter=${(val: number) => `${val} active`}
></dees-chart-donut>
<dees-chart-donut
.label=${'Backend Protocols'}
.data=${backendData.length > 0 ? backendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
.showLegend=${true}
.showLabels=${true}
.innerRadiusPercent=${'55%'}
.valueFormatter=${(val: number) => `${val} active`}
></dees-chart-donut>
</div>
`;
}
private renderTopIPs(): TemplateResult {
if (this.networkState.topIPs.length === 0) {
return html``;

View File

@@ -431,8 +431,8 @@ export class OpsViewRoutes extends DeesElement {
<dees-input-text .key=${'ports'} .label=${'Ports (comma-separated)'} .value=${currentPorts} .required=${true}></dees-input-text>
<dees-input-list .key=${'domains'} .label=${'Domains'} .placeholder=${'Add domain...'} .value=${currentDomains}></dees-input-list>
<dees-input-text .key=${'priority'} .label=${'Priority (higher = matched first)'} .value=${route.priority != null ? String(route.priority) : ''}></dees-input-text>
<dees-input-dropdown .key=${'securityProfileRef'} .label=${'Security Profile'} .options=${profileOptions} .selectedKey=${merged.metadata?.securityProfileRef || ''}></dees-input-dropdown>
<dees-input-dropdown .key=${'networkTargetRef'} .label=${'Network Target'} .options=${targetOptions} .selectedKey=${merged.metadata?.networkTargetRef || ''}></dees-input-dropdown>
<dees-input-dropdown .key=${'securityProfileRef'} .label=${'Security Profile'} .options=${profileOptions} .selectedOption=${profileOptions.find((o) => o.key === (merged.metadata?.securityProfileRef || '')) || null}></dees-input-dropdown>
<dees-input-dropdown .key=${'networkTargetRef'} .label=${'Network Target'} .options=${targetOptions} .selectedOption=${targetOptions.find((o) => o.key === (merged.metadata?.networkTargetRef || '')) || null}></dees-input-dropdown>
<dees-input-text .key=${'targetHost'} .label=${'Target Host (if no target selected)'} .value=${currentTargetHost}></dees-input-text>
<dees-input-text .key=${'targetPort'} .label=${'Target Port (if no target selected)'} .value=${currentTargetPort}></dees-input-text>
</dees-form>
@@ -477,11 +477,15 @@ export class OpsViewRoutes extends DeesElement {
};
const metadata: any = {};
if (formData.securityProfileRef) {
metadata.securityProfileRef = formData.securityProfileRef;
const profileRefValue = formData.securityProfileRef as any;
const profileKey = typeof profileRefValue === 'string' ? profileRefValue : profileRefValue?.key;
if (profileKey) {
metadata.securityProfileRef = profileKey;
}
if (formData.networkTargetRef) {
metadata.networkTargetRef = formData.networkTargetRef;
const targetRefValue = formData.networkTargetRef as any;
const targetKey = typeof targetRefValue === 'string' ? targetRefValue : targetRefValue?.key;
if (targetKey) {
metadata.networkTargetRef = targetKey;
}
await appstate.routeManagementStatePart.dispatchAction(
@@ -528,8 +532,8 @@ export class OpsViewRoutes extends DeesElement {
<dees-input-text .key=${'ports'} .label=${'Ports (comma-separated)'} .required=${true}></dees-input-text>
<dees-input-list .key=${'domains'} .label=${'Domains'} .placeholder=${'Add domain...'}></dees-input-list>
<dees-input-text .key=${'priority'} .label=${'Priority (higher = matched first)'}></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-dropdown .key=${'securityProfileRef'} .label=${'Security Profile'} .options=${profileOptions}></dees-input-dropdown>
<dees-input-dropdown .key=${'networkTargetRef'} .label=${'Network Target'} .options=${targetOptions}></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>
@@ -575,11 +579,15 @@ export class OpsViewRoutes extends DeesElement {
// Build metadata if profile/target selected
const metadata: any = {};
if (formData.securityProfileRef) {
metadata.securityProfileRef = formData.securityProfileRef;
const profileRefValue = formData.securityProfileRef as any;
const profileKey = typeof profileRefValue === 'string' ? profileRefValue : profileRefValue?.key;
if (profileKey) {
metadata.securityProfileRef = profileKey;
}
if (formData.networkTargetRef) {
metadata.networkTargetRef = formData.networkTargetRef;
const targetRefValue = formData.networkTargetRef as any;
const targetKey = typeof targetRefValue === 'string' ? targetRefValue : targetRefValue?.key;
if (targetKey) {
metadata.networkTargetRef = targetKey;
}
await appstate.routeManagementStatePart.dispatchAction(

View File

@@ -131,7 +131,7 @@ The app uses `@push.rocks/smartstate` v2.3+ with multiple state parts, scheduled
| State Part | Mode | Description |
|-----------|------|-------------|
| `loginStatePart` | Persistent (IndexedDB) | JWT identity and login status |
| `statsStatePart` | Soft (memory) | Server, email, DNS, security metrics |
| `statsStatePart` | Soft (memory) | Server, email, DNS, security, RADIUS, VPN metrics |
| `configStatePart` | Soft | Current system configuration |
| `uiStatePart` | Soft | Active view, sidebar, auto-refresh, theme |
| `logStatePart` | Soft | Recent logs, streaming status, filters |