@serve.zone/catalog 2.2.0 → 2.4.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.
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/index.d.ts +6 -0
- package/dist_ts_web/elements/index.js +9 -1
- package/dist_ts_web/elements/sz-config-overview.d.ts +14 -0
- package/dist_ts_web/elements/sz-config-overview.js +141 -0
- package/dist_ts_web/elements/sz-config-section.d.ts +31 -0
- package/dist_ts_web/elements/sz-config-section.js +591 -0
- package/dist_ts_web/elements/sz-demo-view-config.d.ts +11 -0
- package/dist_ts_web/elements/sz-demo-view-config.js +193 -0
- package/dist_ts_web/elements/sz-demo-view-routes.d.ts +22 -0
- package/dist_ts_web/elements/sz-demo-view-routes.js +388 -0
- package/dist_ts_web/elements/sz-route-card.d.ts +78 -0
- package/dist_ts_web/elements/sz-route-card.js +648 -0
- package/dist_ts_web/elements/sz-route-list-view.d.ts +21 -0
- package/dist_ts_web/elements/sz-route-list-view.js +372 -0
- package/dist_ts_web/pages/sz-demo-app-shell.js +8 -2
- package/dist_watch/bundle.js +2074 -152
- package/dist_watch/bundle.js.map +4 -4
- package/package.json +1 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/index.ts +10 -0
- package/ts_web/elements/sz-config-overview.ts +92 -0
- package/ts_web/elements/sz-config-section.ts +531 -0
- package/ts_web/elements/sz-demo-view-config.ts +164 -0
- package/ts_web/elements/sz-demo-view-routes.ts +362 -0
- package/ts_web/elements/sz-route-card.ts +667 -0
- package/ts_web/elements/sz-route-list-view.ts +326 -0
- package/ts_web/pages/sz-demo-app-shell.ts +7 -1
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DeesElement,
|
|
3
|
+
customElement,
|
|
4
|
+
html,
|
|
5
|
+
css,
|
|
6
|
+
cssManager,
|
|
7
|
+
property,
|
|
8
|
+
state,
|
|
9
|
+
type TemplateResult,
|
|
10
|
+
} from '@design.estate/dees-element';
|
|
11
|
+
|
|
12
|
+
import type { IRouteConfig, TRouteActionType } from './sz-route-card.js';
|
|
13
|
+
|
|
14
|
+
declare global {
|
|
15
|
+
interface HTMLElementTagNameMap {
|
|
16
|
+
'sz-route-list-view': SzRouteListView;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@customElement('sz-route-list-view')
|
|
21
|
+
export class SzRouteListView extends DeesElement {
|
|
22
|
+
public static demo = () => html`
|
|
23
|
+
<div style="padding: 24px; max-width: 1200px;">
|
|
24
|
+
<sz-route-list-view
|
|
25
|
+
.routes=${[
|
|
26
|
+
{
|
|
27
|
+
name: 'HTTPS Gateway',
|
|
28
|
+
description: 'Main web gateway with TLS termination',
|
|
29
|
+
enabled: true,
|
|
30
|
+
tags: ['web', 'https', 'production'],
|
|
31
|
+
match: { ports: 443, domains: ['*.example.com', 'serve.zone'], protocol: 'http' as const },
|
|
32
|
+
action: {
|
|
33
|
+
type: 'forward' as const,
|
|
34
|
+
targets: [{ host: '10.0.0.1', port: 8080 }],
|
|
35
|
+
tls: { mode: 'terminate' as const, certificate: 'auto' as const },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'SMTP Inbound',
|
|
40
|
+
description: 'Email relay for incoming mail',
|
|
41
|
+
enabled: true,
|
|
42
|
+
tags: ['email', 'smtp'],
|
|
43
|
+
match: { ports: 25, domains: 'mail.serve.zone' },
|
|
44
|
+
action: {
|
|
45
|
+
type: 'forward' as const,
|
|
46
|
+
targets: [{ host: '10.0.1.5', port: 25 }],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'WebSocket API',
|
|
51
|
+
description: 'Real-time WebSocket connections',
|
|
52
|
+
enabled: true,
|
|
53
|
+
tags: ['web', 'api'],
|
|
54
|
+
match: { ports: 443, domains: 'ws.example.com', path: '/ws/*' },
|
|
55
|
+
action: {
|
|
56
|
+
type: 'forward' as const,
|
|
57
|
+
targets: [{ host: '10.0.0.3', port: 9090 }],
|
|
58
|
+
websocket: { enabled: true },
|
|
59
|
+
tls: { mode: 'terminate' as const, certificate: 'auto' as const },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'Maintenance Page',
|
|
64
|
+
enabled: false,
|
|
65
|
+
tags: ['web'],
|
|
66
|
+
match: { ports: [80, 443], domains: 'old.example.com' },
|
|
67
|
+
action: { type: 'socket-handler' as const },
|
|
68
|
+
},
|
|
69
|
+
] satisfies IRouteConfig[]}
|
|
70
|
+
></sz-route-list-view>
|
|
71
|
+
</div>
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
public static demoGroups = ['Routes'];
|
|
75
|
+
|
|
76
|
+
@property({ type: Array })
|
|
77
|
+
public accessor routes: IRouteConfig[] = [];
|
|
78
|
+
|
|
79
|
+
@state()
|
|
80
|
+
private accessor searchQuery: string = '';
|
|
81
|
+
|
|
82
|
+
@state()
|
|
83
|
+
private accessor actionFilter: TRouteActionType | 'all' = 'all';
|
|
84
|
+
|
|
85
|
+
@state()
|
|
86
|
+
private accessor enabledFilter: 'all' | 'enabled' | 'disabled' = 'all';
|
|
87
|
+
|
|
88
|
+
private get filteredRoutes(): IRouteConfig[] {
|
|
89
|
+
return this.routes.filter((route) => {
|
|
90
|
+
// Action type filter
|
|
91
|
+
if (this.actionFilter !== 'all' && route.action.type !== this.actionFilter) return false;
|
|
92
|
+
|
|
93
|
+
// Enabled/disabled filter
|
|
94
|
+
if (this.enabledFilter === 'enabled' && route.enabled === false) return false;
|
|
95
|
+
if (this.enabledFilter === 'disabled' && route.enabled !== false) return false;
|
|
96
|
+
|
|
97
|
+
// Search query
|
|
98
|
+
if (this.searchQuery) {
|
|
99
|
+
const q = this.searchQuery.toLowerCase();
|
|
100
|
+
return this.routeMatchesSearch(route, q);
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private routeMatchesSearch(route: IRouteConfig, q: string): boolean {
|
|
107
|
+
// Name and description
|
|
108
|
+
if (route.name?.toLowerCase().includes(q)) return true;
|
|
109
|
+
if (route.description?.toLowerCase().includes(q)) return true;
|
|
110
|
+
|
|
111
|
+
// Domains
|
|
112
|
+
if (route.match.domains) {
|
|
113
|
+
const domains = Array.isArray(route.match.domains) ? route.match.domains : [route.match.domains];
|
|
114
|
+
if (domains.some((d) => d.toLowerCase().includes(q))) return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Ports
|
|
118
|
+
const portsStr = this.formatPortsForSearch(route.match.ports);
|
|
119
|
+
if (portsStr.includes(q)) return true;
|
|
120
|
+
|
|
121
|
+
// Path
|
|
122
|
+
if (route.match.path?.toLowerCase().includes(q)) return true;
|
|
123
|
+
|
|
124
|
+
// Client IPs
|
|
125
|
+
if (route.match.clientIp?.some((ip) => ip.includes(q))) return true;
|
|
126
|
+
|
|
127
|
+
// Targets
|
|
128
|
+
if (route.action.targets) {
|
|
129
|
+
for (const t of route.action.targets) {
|
|
130
|
+
const hosts = Array.isArray(t.host) ? t.host : [t.host];
|
|
131
|
+
if (hosts.some((h) => h.toLowerCase().includes(q))) return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Tags
|
|
136
|
+
if (route.tags?.some((t) => t.toLowerCase().includes(q))) return true;
|
|
137
|
+
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private formatPortsForSearch(ports: import('./sz-route-card.js').TPortRange): string {
|
|
142
|
+
if (typeof ports === 'number') return String(ports);
|
|
143
|
+
if (Array.isArray(ports)) {
|
|
144
|
+
return ports
|
|
145
|
+
.map((p) => (typeof p === 'number' ? String(p) : `${p.from}-${p.to}`))
|
|
146
|
+
.join(' ');
|
|
147
|
+
}
|
|
148
|
+
return String(ports);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
public static styles = [
|
|
152
|
+
cssManager.defaultStyles,
|
|
153
|
+
css`
|
|
154
|
+
:host {
|
|
155
|
+
display: block;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.filter-bar {
|
|
159
|
+
display: flex;
|
|
160
|
+
flex-wrap: wrap;
|
|
161
|
+
gap: 12px;
|
|
162
|
+
align-items: center;
|
|
163
|
+
margin-bottom: 12px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.search-input {
|
|
167
|
+
flex: 1;
|
|
168
|
+
min-width: 200px;
|
|
169
|
+
padding: 8px 12px;
|
|
170
|
+
background: ${cssManager.bdTheme('#ffffff', '#09090b')};
|
|
171
|
+
border: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
|
|
172
|
+
border-radius: 6px;
|
|
173
|
+
font-size: 14px;
|
|
174
|
+
color: ${cssManager.bdTheme('#18181b', '#fafafa')};
|
|
175
|
+
outline: none;
|
|
176
|
+
transition: border-color 200ms ease;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.search-input::placeholder {
|
|
180
|
+
color: ${cssManager.bdTheme('#a1a1aa', '#52525b')};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.search-input:focus {
|
|
184
|
+
border-color: ${cssManager.bdTheme('#2563eb', '#3b82f6')};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.chip-group {
|
|
188
|
+
display: flex;
|
|
189
|
+
gap: 4px;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.chip {
|
|
193
|
+
padding: 6px 12px;
|
|
194
|
+
background: transparent;
|
|
195
|
+
border: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
|
|
196
|
+
border-radius: 9999px;
|
|
197
|
+
font-size: 12px;
|
|
198
|
+
font-weight: 500;
|
|
199
|
+
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
|
|
200
|
+
cursor: pointer;
|
|
201
|
+
transition: all 200ms ease;
|
|
202
|
+
white-space: nowrap;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.chip:hover {
|
|
206
|
+
background: ${cssManager.bdTheme('#f4f4f5', '#18181b')};
|
|
207
|
+
color: ${cssManager.bdTheme('#18181b', '#fafafa')};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.chip.active {
|
|
211
|
+
background: ${cssManager.bdTheme('#18181b', '#fafafa')};
|
|
212
|
+
color: ${cssManager.bdTheme('#fafafa', '#18181b')};
|
|
213
|
+
border-color: ${cssManager.bdTheme('#18181b', '#fafafa')};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.results-count {
|
|
217
|
+
font-size: 13px;
|
|
218
|
+
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
|
|
219
|
+
margin-bottom: 16px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.grid {
|
|
223
|
+
display: grid;
|
|
224
|
+
grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
|
|
225
|
+
gap: 16px;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.grid sz-route-card {
|
|
229
|
+
cursor: pointer;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.empty-state {
|
|
233
|
+
text-align: center;
|
|
234
|
+
padding: 48px 24px;
|
|
235
|
+
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
|
|
236
|
+
font-size: 14px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.empty-state-icon {
|
|
240
|
+
font-size: 32px;
|
|
241
|
+
margin-bottom: 12px;
|
|
242
|
+
opacity: 0.5;
|
|
243
|
+
}
|
|
244
|
+
`,
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
public render(): TemplateResult {
|
|
248
|
+
const filtered = this.filteredRoutes;
|
|
249
|
+
|
|
250
|
+
return html`
|
|
251
|
+
<div class="filter-bar">
|
|
252
|
+
<input
|
|
253
|
+
class="search-input"
|
|
254
|
+
type="text"
|
|
255
|
+
placeholder="Search routes by domain, IP, port, path, or tag..."
|
|
256
|
+
.value=${this.searchQuery}
|
|
257
|
+
@input=${(e: InputEvent) => {
|
|
258
|
+
this.searchQuery = (e.target as HTMLInputElement).value;
|
|
259
|
+
}}
|
|
260
|
+
/>
|
|
261
|
+
<div class="chip-group">
|
|
262
|
+
${(['all', 'forward', 'socket-handler'] as const).map(
|
|
263
|
+
(type) => html`
|
|
264
|
+
<button
|
|
265
|
+
class="chip ${this.actionFilter === type ? 'active' : ''}"
|
|
266
|
+
@click=${() => {
|
|
267
|
+
this.actionFilter = type;
|
|
268
|
+
}}
|
|
269
|
+
>
|
|
270
|
+
${type === 'all' ? 'All' : type === 'forward' ? 'Forward' : 'Socket Handler'}
|
|
271
|
+
</button>
|
|
272
|
+
`
|
|
273
|
+
)}
|
|
274
|
+
</div>
|
|
275
|
+
<div class="chip-group">
|
|
276
|
+
${(['all', 'enabled', 'disabled'] as const).map(
|
|
277
|
+
(status) => html`
|
|
278
|
+
<button
|
|
279
|
+
class="chip ${this.enabledFilter === status ? 'active' : ''}"
|
|
280
|
+
@click=${() => {
|
|
281
|
+
this.enabledFilter = status;
|
|
282
|
+
}}
|
|
283
|
+
>
|
|
284
|
+
${status.charAt(0).toUpperCase() + status.slice(1)}
|
|
285
|
+
</button>
|
|
286
|
+
`
|
|
287
|
+
)}
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<div class="results-count">
|
|
292
|
+
Showing ${filtered.length} of ${this.routes.length} routes
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
${filtered.length > 0
|
|
296
|
+
? html`
|
|
297
|
+
<div class="grid">
|
|
298
|
+
${filtered.map(
|
|
299
|
+
(route) => html`
|
|
300
|
+
<sz-route-card
|
|
301
|
+
.route=${route}
|
|
302
|
+
@click=${() => this.handleRouteClick(route)}
|
|
303
|
+
></sz-route-card>
|
|
304
|
+
`
|
|
305
|
+
)}
|
|
306
|
+
</div>
|
|
307
|
+
`
|
|
308
|
+
: html`
|
|
309
|
+
<div class="empty-state">
|
|
310
|
+
<div class="empty-state-icon">🔍</div>
|
|
311
|
+
<div>No routes match your filters</div>
|
|
312
|
+
</div>
|
|
313
|
+
`}
|
|
314
|
+
`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private handleRouteClick(route: IRouteConfig) {
|
|
318
|
+
this.dispatchEvent(
|
|
319
|
+
new CustomEvent('route-click', {
|
|
320
|
+
detail: route,
|
|
321
|
+
bubbles: true,
|
|
322
|
+
composed: true,
|
|
323
|
+
})
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -138,6 +138,12 @@ export class SzDemoAppShell extends DeesElement {
|
|
|
138
138
|
iconName: 'lucide:Mail',
|
|
139
139
|
content: 'sz-demo-view-mta',
|
|
140
140
|
},
|
|
141
|
+
{
|
|
142
|
+
id: 'routes',
|
|
143
|
+
name: 'Routes',
|
|
144
|
+
iconName: 'lucide:Route',
|
|
145
|
+
content: 'sz-demo-view-routes',
|
|
146
|
+
},
|
|
141
147
|
{
|
|
142
148
|
id: 'settings',
|
|
143
149
|
name: 'Settings',
|
|
@@ -153,7 +159,7 @@ export class SzDemoAppShell extends DeesElement {
|
|
|
153
159
|
},
|
|
154
160
|
{
|
|
155
161
|
name: 'Infrastructure',
|
|
156
|
-
views: ['services', 'network', 'registries', 'mta'],
|
|
162
|
+
views: ['services', 'network', 'registries', 'mta', 'routes'],
|
|
157
163
|
},
|
|
158
164
|
{
|
|
159
165
|
name: 'Administration',
|