@serve.zone/dcrouter 13.42.3 → 13.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/deno.json +11 -11
  2. package/dist_serve/bundle.js +722 -678
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/classes.dcrouter.js +3 -3
  5. package/dist_ts/config/classes.route-config-manager.d.ts +4 -2
  6. package/dist_ts/config/classes.route-config-manager.js +17 -9
  7. package/dist_ts/config/classes.source-policy-compiler.d.ts +1 -0
  8. package/dist_ts/config/classes.source-policy-compiler.js +24 -2
  9. package/dist_ts/config/helpers.http-redirects.d.ts +10 -0
  10. package/dist_ts/config/helpers.http-redirects.js +387 -0
  11. package/dist_ts/config/index.d.ts +1 -0
  12. package/dist_ts/config/index.js +2 -1
  13. package/dist_ts/opsserver/handlers/route-management.handler.js +10 -1
  14. package/dist_ts_interfaces/data/route-management.d.ts +20 -0
  15. package/dist_ts_interfaces/requests/route-management.d.ts +14 -1
  16. package/dist_ts_web/00_commitinfo_data.js +1 -1
  17. package/dist_ts_web/appstate.d.ts +2 -0
  18. package/dist_ts_web/appstate.js +28 -1
  19. package/dist_ts_web/elements/access/ops-view-apitokens.js +2 -1
  20. package/dist_ts_web/elements/access/ops-view-gatewayclients.js +2 -1
  21. package/dist_ts_web/elements/network/index.d.ts +1 -0
  22. package/dist_ts_web/elements/network/index.js +2 -1
  23. package/dist_ts_web/elements/network/ops-view-redirects.d.ts +18 -0
  24. package/dist_ts_web/elements/network/ops-view-redirects.js +236 -0
  25. package/dist_ts_web/elements/network/ops-view-routes.js +2 -1
  26. package/dist_ts_web/elements/ops-dashboard.js +3 -1
  27. package/dist_ts_web/router.js +2 -2
  28. package/package.json +1 -1
  29. package/ts/00_commitinfo_data.ts +1 -1
  30. package/ts/classes.dcrouter.ts +2 -2
  31. package/ts/config/classes.route-config-manager.ts +22 -10
  32. package/ts/config/classes.source-policy-compiler.ts +33 -1
  33. package/ts/config/helpers.http-redirects.ts +462 -0
  34. package/ts/config/index.ts +1 -0
  35. package/ts/opsserver/handlers/route-management.handler.ts +15 -0
  36. package/ts_web/00_commitinfo_data.ts +1 -1
  37. package/ts_web/appstate.ts +32 -0
  38. package/ts_web/elements/access/ops-view-apitokens.ts +1 -0
  39. package/ts_web/elements/access/ops-view-gatewayclients.ts +1 -0
  40. package/ts_web/elements/network/index.ts +1 -0
  41. package/ts_web/elements/network/ops-view-redirects.ts +202 -0
  42. package/ts_web/elements/network/ops-view-routes.ts +1 -0
  43. package/ts_web/elements/ops-dashboard.ts +2 -0
  44. package/ts_web/router.ts +1 -1
@@ -0,0 +1,202 @@
1
+ import {
2
+ DeesElement,
3
+ html,
4
+ customElement,
5
+ type TemplateResult,
6
+ css,
7
+ state,
8
+ cssManager,
9
+ } from '@design.estate/dees-element';
10
+ import { type IStatsTile } from '@design.estate/dees-catalog';
11
+ import * as appstate from '../../appstate.js';
12
+ import * as interfaces from '../../../dist_ts_interfaces/index.js';
13
+ import { viewHostCss } from '../shared/css.js';
14
+
15
+ declare global {
16
+ interface HTMLElementTagNameMap {
17
+ 'ops-view-redirects': OpsViewRedirects;
18
+ }
19
+ }
20
+
21
+ @customElement('ops-view-redirects')
22
+ export class OpsViewRedirects extends DeesElement {
23
+ @state()
24
+ accessor routeState: appstate.IRouteManagementState = appstate.routeManagementStatePart.getState()!;
25
+
26
+ constructor() {
27
+ super();
28
+ const routeSub = appstate.routeManagementStatePart.select().subscribe((routeState) => {
29
+ this.routeState = routeState;
30
+ });
31
+ this.rxSubscriptions.push(routeSub);
32
+
33
+ const loginSub = appstate.loginStatePart
34
+ .select((state) => state.isLoggedIn)
35
+ .subscribe((isLoggedIn) => {
36
+ if (isLoggedIn) {
37
+ void this.refreshData();
38
+ }
39
+ });
40
+ this.rxSubscriptions.push(loginSub);
41
+ }
42
+
43
+ async connectedCallback() {
44
+ await super.connectedCallback();
45
+ await this.refreshData();
46
+ }
47
+
48
+ public static styles = [
49
+ cssManager.defaultStyles,
50
+ viewHostCss,
51
+ css`
52
+ .redirectsContainer {
53
+ display: flex;
54
+ flex-direction: column;
55
+ gap: 24px;
56
+ }
57
+
58
+ .empty-state {
59
+ text-align: center;
60
+ padding: 48px 24px;
61
+ color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
62
+ }
63
+
64
+ .empty-state p {
65
+ margin: 8px 0;
66
+ }
67
+ `,
68
+ ];
69
+
70
+ public render(): TemplateResult {
71
+ const redirects = this.routeState.httpRedirects || [];
72
+ const activeCount = redirects.filter((redirect) => redirect.status === 'active').length;
73
+ const coveredCount = redirects.filter((redirect) => redirect.status === 'covered').length;
74
+ const skippedCount = redirects.filter((redirect) => redirect.status === 'skipped').length;
75
+ const remoteIngressCount = redirects.filter((redirect) => redirect.remoteIngress).length;
76
+
77
+ const statsTiles: IStatsTile[] = [
78
+ {
79
+ id: 'totalRedirects',
80
+ title: 'Total Redirects',
81
+ type: 'number',
82
+ value: redirects.length,
83
+ icon: 'lucide:CornerDownRight',
84
+ description: 'Derived HTTP to HTTPS scopes',
85
+ color: '#3b82f6',
86
+ },
87
+ {
88
+ id: 'activeRedirects',
89
+ title: 'Active',
90
+ type: 'number',
91
+ value: activeCount,
92
+ icon: 'lucide:CircleCheck',
93
+ description: 'Generated at runtime',
94
+ color: '#22c55e',
95
+ },
96
+ {
97
+ id: 'coveredRedirects',
98
+ title: 'Covered',
99
+ type: 'number',
100
+ value: coveredCount,
101
+ icon: 'lucide:ShieldCheck',
102
+ description: 'Handled by explicit HTTP routes',
103
+ color: '#8b5cf6',
104
+ },
105
+ {
106
+ id: 'skippedRedirects',
107
+ title: 'Skipped',
108
+ type: 'number',
109
+ value: skippedCount,
110
+ icon: 'lucide:AlertTriangle',
111
+ description: 'Overlaps explicit HTTP routes',
112
+ color: skippedCount > 0 ? '#f59e0b' : '#6b7280',
113
+ },
114
+ {
115
+ id: 'remoteIngressRedirects',
116
+ title: 'Remote Ingress',
117
+ type: 'number',
118
+ value: remoteIngressCount,
119
+ icon: 'lucide:Globe',
120
+ description: 'Also exposed to edge nodes',
121
+ color: '#0ea5e9',
122
+ },
123
+ ];
124
+
125
+ return html`
126
+ <dees-heading level="3">Redirects</dees-heading>
127
+ <div class="redirectsContainer">
128
+ <dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
129
+
130
+ ${redirects.length > 0
131
+ ? html`
132
+ <dees-table
133
+ .heading1=${'HTTP to HTTPS Redirects'}
134
+ .heading2=${'Runtime redirects derived from enabled HTTPS routes'}
135
+ .data=${redirects}
136
+ .showColumnFilters=${true}
137
+ .displayFunction=${(redirect: interfaces.data.IHttpRedirectInfo) => ({
138
+ Status: this.formatStatus(redirect.status),
139
+ 'Domain Pattern': redirect.domainPattern,
140
+ Path: redirect.pathPattern || '*',
141
+ From: this.formatHttpTemplate(redirect, 'http'),
142
+ To: this.formatHttpTemplate(redirect, 'https'),
143
+ Code: redirect.statusCode,
144
+ Priority: redirect.priority,
145
+ 'Source HTTPS Route': redirect.sourceRouteNames.join(', ') || '-',
146
+ 'Covered By': redirect.coveredByRouteNames.join(', ') || '-',
147
+ Notes: this.formatNotes(redirect),
148
+ })}
149
+ .dataActions=${[
150
+ {
151
+ name: 'Refresh',
152
+ iconName: 'lucide:RefreshCw',
153
+ type: ['header' as const],
154
+ actionFunc: async () => this.refreshData(),
155
+ },
156
+ ]}
157
+ ></dees-table>
158
+ `
159
+ : html`
160
+ <dees-table
161
+ .heading1=${'HTTP to HTTPS Redirects'}
162
+ .heading2=${'Runtime redirects derived from enabled HTTPS routes'}
163
+ .data=${[]}
164
+ .displayFunction=${() => ({})}
165
+ .dataActions=${[
166
+ {
167
+ name: 'Refresh',
168
+ iconName: 'lucide:RefreshCw',
169
+ type: ['header' as const],
170
+ actionFunc: async () => this.refreshData(),
171
+ },
172
+ ]}
173
+ ></dees-table>
174
+ <div class="empty-state">
175
+ <p>No derived redirects</p>
176
+ <p>Enable HTTPS routes with explicit domains to generate HTTP to HTTPS redirects.</p>
177
+ </div>
178
+ `}
179
+ </div>
180
+ `;
181
+ }
182
+
183
+ private async refreshData(): Promise<void> {
184
+ await appstate.routeManagementStatePart.dispatchAction(appstate.fetchHttpRedirectsAction, null);
185
+ }
186
+
187
+ private formatStatus(status: interfaces.data.THttpRedirectStatus): string {
188
+ return status.charAt(0).toUpperCase() + status.slice(1);
189
+ }
190
+
191
+ private formatHttpTemplate(redirect: interfaces.data.IHttpRedirectInfo, protocol: 'http' | 'https'): string {
192
+ return `${protocol}://${redirect.domainPattern}${redirect.pathPattern || '{path}'}`;
193
+ }
194
+
195
+ private formatNotes(redirect: interfaces.data.IHttpRedirectInfo): string {
196
+ const notes = redirect.notes ? [redirect.notes] : [];
197
+ if (redirect.remoteIngress) {
198
+ notes.push('Remote Ingress enabled');
199
+ }
200
+ return notes.join(' ') || 'Generated from HTTPS route';
201
+ }
202
+ }
@@ -293,6 +293,7 @@ export class OpsViewRoutes extends DeesElement {
293
293
  @state() accessor routeState: appstate.IRouteManagementState = {
294
294
  mergedRoutes: [],
295
295
  warnings: [],
296
+ httpRedirects: [],
296
297
  apiTokens: [],
297
298
  gatewayClients: [],
298
299
  isLoading: false,
@@ -23,6 +23,7 @@ import { OpsViewConfig } from './overview/ops-view-config.js';
23
23
  // Network group
24
24
  import { OpsViewNetworkActivity } from './network/ops-view-network-activity.js';
25
25
  import { OpsViewRoutes } from './network/ops-view-routes.js';
26
+ import { OpsViewRedirects } from './network/ops-view-redirects.js';
26
27
  import { OpsViewSourceProfiles } from './network/ops-view-sourceprofiles.js';
27
28
  import { OpsViewNetworkTargets } from './network/ops-view-networktargets.js';
28
29
  import { OpsViewTargetProfiles } from './network/ops-view-targetprofiles.js';
@@ -100,6 +101,7 @@ export class OpsDashboard extends DeesElement {
100
101
  subViews: [
101
102
  { slug: 'activity', name: 'Network Activity', iconName: 'lucide:activity', element: OpsViewNetworkActivity },
102
103
  { slug: 'routes', name: 'Routes', iconName: 'lucide:route', element: OpsViewRoutes },
104
+ { slug: 'redirects', name: 'Redirects', iconName: 'lucide:CornerDownRight', element: OpsViewRedirects },
103
105
  { slug: 'sourceprofiles', name: 'Source Profiles', iconName: 'lucide:shieldCheck', element: OpsViewSourceProfiles },
104
106
  { slug: 'networktargets', name: 'Network Targets', iconName: 'lucide:server', element: OpsViewNetworkTargets },
105
107
  { slug: 'targetprofiles', name: 'Target Profiles', iconName: 'lucide:target', element: OpsViewTargetProfiles },
package/ts_web/router.ts CHANGED
@@ -9,7 +9,7 @@ const flatViews = ['logs'] as const;
9
9
  // Tabbed views and their valid subviews
10
10
  const subviewMap: Record<string, readonly string[]> = {
11
11
  overview: ['stats', 'configuration'] as const,
12
- network: ['activity', 'routes', 'sourceprofiles', 'networktargets', 'targetprofiles', 'remoteingress', 'vpn'] as const,
12
+ network: ['activity', 'routes', 'redirects', 'sourceprofiles', 'networktargets', 'targetprofiles', 'remoteingress', 'vpn'] as const,
13
13
  email: ['log', 'security', 'domains'] as const,
14
14
  access: ['gatewayclients', 'apitokens', 'users'] as const,
15
15
  security: ['overview', 'blocked', 'authentication'] as const,