feat(routes): add route UI components and demo view with list/card and app-shell integration
This commit is contained in:
362
ts_web/elements/sz-demo-view-routes.ts
Normal file
362
ts_web/elements/sz-demo-view-routes.ts
Normal file
@@ -0,0 +1,362 @@
|
||||
import {
|
||||
DeesElement,
|
||||
customElement,
|
||||
html,
|
||||
css,
|
||||
cssManager,
|
||||
state,
|
||||
type TemplateResult,
|
||||
} from '@design.estate/dees-element';
|
||||
import type { DeesAppui } from '@design.estate/dees-catalog';
|
||||
import type { IRouteConfig } from './sz-route-card.js';
|
||||
import './index.js';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'sz-demo-view-routes': SzDemoViewRoutes;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('sz-demo-view-routes')
|
||||
export class SzDemoViewRoutes extends DeesElement {
|
||||
private appui: DeesAppui | null = null;
|
||||
|
||||
@state()
|
||||
private accessor currentTab: 'all' | 'https' | 'email' | 'dns' = 'all';
|
||||
|
||||
private demoRoutes: IRouteConfig[] = [
|
||||
// 1. HTTPS with TLS termination + auto cert
|
||||
{
|
||||
id: 'route-1',
|
||||
name: 'Web Frontend',
|
||||
description: 'Main website with TLS termination and automatic certificates',
|
||||
enabled: true,
|
||||
priority: 10,
|
||||
tags: ['web', 'https', 'production'],
|
||||
match: {
|
||||
ports: 443,
|
||||
domains: ['serve.zone', 'www.serve.zone'],
|
||||
protocol: 'http',
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{ host: '10.0.0.10', port: 3000 }],
|
||||
tls: { mode: 'terminate', certificate: 'auto' },
|
||||
},
|
||||
},
|
||||
// 2. HTTP to HTTPS redirect
|
||||
{
|
||||
id: 'route-2',
|
||||
name: 'HTTP Redirect',
|
||||
description: 'Redirects all HTTP traffic to HTTPS',
|
||||
enabled: true,
|
||||
priority: 100,
|
||||
tags: ['web', 'http', 'redirect'],
|
||||
match: {
|
||||
ports: 80,
|
||||
domains: ['serve.zone', 'www.serve.zone'],
|
||||
protocol: 'http',
|
||||
},
|
||||
action: {
|
||||
type: 'socket-handler',
|
||||
},
|
||||
},
|
||||
// 3. Email SMTP route
|
||||
{
|
||||
id: 'route-3',
|
||||
name: 'SMTP Inbound',
|
||||
description: 'Inbound email relay for serve.zone domain',
|
||||
enabled: true,
|
||||
tags: ['email', 'smtp', 'production'],
|
||||
match: {
|
||||
ports: 25,
|
||||
domains: 'mail.serve.zone',
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{ host: '10.0.1.5', port: 25 }],
|
||||
tls: { mode: 'terminate', certificate: 'auto' },
|
||||
},
|
||||
},
|
||||
// 4. API gateway with path matching, rate limiting, CORS
|
||||
{
|
||||
id: 'route-4',
|
||||
name: 'API Gateway',
|
||||
description: 'API gateway with rate limiting, CORS headers, and load balancing',
|
||||
enabled: true,
|
||||
priority: 20,
|
||||
tags: ['web', 'api', 'https', 'production'],
|
||||
match: {
|
||||
ports: 443,
|
||||
domains: 'api.serve.zone',
|
||||
path: '/v2/*',
|
||||
protocol: 'http',
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [
|
||||
{ host: ['10.0.0.20', '10.0.0.21', '10.0.0.22'], port: 8080 },
|
||||
],
|
||||
tls: { mode: 'terminate', certificate: 'auto' },
|
||||
loadBalancing: { algorithm: 'round-robin' },
|
||||
},
|
||||
security: {
|
||||
rateLimit: { enabled: true, maxRequests: 200, window: 60 },
|
||||
maxConnections: 5000,
|
||||
},
|
||||
headers: {
|
||||
response: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'X-Request-Id': '{{requestId}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
// 5. WebSocket route
|
||||
{
|
||||
id: 'route-5',
|
||||
name: 'WebSocket Realtime',
|
||||
description: 'Real-time WebSocket connections for live updates',
|
||||
enabled: true,
|
||||
tags: ['web', 'https', 'websocket'],
|
||||
match: {
|
||||
ports: 443,
|
||||
domains: 'ws.serve.zone',
|
||||
path: '/ws/*',
|
||||
protocol: 'http',
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{ host: '10.0.0.30', port: 9090 }],
|
||||
tls: { mode: 'terminate', certificate: 'auto' },
|
||||
websocket: { enabled: true },
|
||||
},
|
||||
},
|
||||
// 6. Wildcard domain route
|
||||
{
|
||||
id: 'route-6',
|
||||
name: 'Tenant Wildcard',
|
||||
description: 'Multi-tenant wildcard routing for customer subdomains',
|
||||
enabled: true,
|
||||
priority: 50,
|
||||
tags: ['web', 'https', 'multi-tenant'],
|
||||
match: {
|
||||
ports: 443,
|
||||
domains: '*.customers.serve.zone',
|
||||
protocol: 'http',
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{ host: '10.0.0.40', port: 8080 }],
|
||||
tls: { mode: 'terminate', certificate: 'auto' },
|
||||
},
|
||||
security: {
|
||||
ipAllowList: ['10.0.0.0/8', '172.16.0.0/12'],
|
||||
},
|
||||
},
|
||||
// 7. Load-balanced route with health check
|
||||
{
|
||||
id: 'route-7',
|
||||
name: 'Microservices LB',
|
||||
description: 'Load-balanced microservices backend with IP-hash affinity',
|
||||
enabled: true,
|
||||
tags: ['web', 'https', 'production'],
|
||||
match: {
|
||||
ports: [443, 8443],
|
||||
domains: 'services.serve.zone',
|
||||
protocol: 'http',
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [
|
||||
{ host: ['10.0.2.1', '10.0.2.2', '10.0.2.3', '10.0.2.4'], port: 3000 },
|
||||
],
|
||||
tls: { mode: 'terminate-and-reencrypt', certificate: 'auto' },
|
||||
loadBalancing: { algorithm: 'ip-hash' },
|
||||
},
|
||||
},
|
||||
// 8. DNS-over-HTTPS route
|
||||
{
|
||||
id: 'route-8',
|
||||
name: 'DNS over HTTPS',
|
||||
description: 'DNS-over-HTTPS resolver endpoint',
|
||||
enabled: true,
|
||||
tags: ['dns', 'https'],
|
||||
match: {
|
||||
ports: 443,
|
||||
domains: 'dns.serve.zone',
|
||||
path: '/dns-query',
|
||||
protocol: 'http',
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{ host: '10.0.3.1', port: 8053 }],
|
||||
tls: { mode: 'terminate', certificate: 'auto' },
|
||||
},
|
||||
},
|
||||
// 9. NFTables high-performance route
|
||||
{
|
||||
id: 'route-9',
|
||||
name: 'High-Perf TCP Proxy',
|
||||
description: 'NFTables-accelerated TCP proxy for game servers',
|
||||
enabled: true,
|
||||
tags: ['tcp', 'nftables', 'production'],
|
||||
match: {
|
||||
ports: [{ from: 27000, to: 27050 }],
|
||||
protocol: 'tcp',
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{ host: '10.0.4.1', port: 'preserve' }],
|
||||
forwardingEngine: 'nftables',
|
||||
},
|
||||
},
|
||||
// 10. Disabled maintenance route
|
||||
{
|
||||
id: 'route-10',
|
||||
name: 'Legacy Admin Panel',
|
||||
description: 'Deprecated admin panel — disabled for maintenance',
|
||||
enabled: false,
|
||||
tags: ['web', 'https', 'deprecated'],
|
||||
match: {
|
||||
ports: 443,
|
||||
domains: 'admin-old.serve.zone',
|
||||
protocol: 'http',
|
||||
},
|
||||
action: {
|
||||
type: 'socket-handler',
|
||||
},
|
||||
security: {
|
||||
ipBlockList: ['0.0.0.0/0'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
private get filteredRoutes(): IRouteConfig[] {
|
||||
if (this.currentTab === 'all') return this.demoRoutes;
|
||||
if (this.currentTab === 'https') {
|
||||
return this.demoRoutes.filter((r) =>
|
||||
r.tags?.some((t) => ['web', 'https', 'http'].includes(t))
|
||||
);
|
||||
}
|
||||
if (this.currentTab === 'email') {
|
||||
return this.demoRoutes.filter((r) =>
|
||||
r.tags?.some((t) => ['email', 'smtp'].includes(t))
|
||||
);
|
||||
}
|
||||
if (this.currentTab === 'dns') {
|
||||
return this.demoRoutes.filter((r) =>
|
||||
r.tags?.some((t) => ['dns'].includes(t))
|
||||
);
|
||||
}
|
||||
return this.demoRoutes;
|
||||
}
|
||||
|
||||
async onActivate(context: { appui: DeesAppui; viewId: string }) {
|
||||
this.appui = context.appui;
|
||||
|
||||
this.appui.setContentTabs([
|
||||
{
|
||||
key: 'All Routes',
|
||||
action: () => {
|
||||
this.currentTab = 'all';
|
||||
this.updateSecondaryMenu();
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'HTTP/S',
|
||||
action: () => {
|
||||
this.currentTab = 'https';
|
||||
this.updateSecondaryMenu();
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'Email',
|
||||
action: () => {
|
||||
this.currentTab = 'email';
|
||||
this.updateSecondaryMenu();
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'DNS',
|
||||
action: () => {
|
||||
this.currentTab = 'dns';
|
||||
this.updateSecondaryMenu();
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
this.updateSecondaryMenu();
|
||||
}
|
||||
|
||||
private updateSecondaryMenu() {
|
||||
if (!this.appui) return;
|
||||
|
||||
const total = this.demoRoutes.length;
|
||||
const active = this.demoRoutes.filter((r) => r.enabled !== false).length;
|
||||
const forwardCount = this.demoRoutes.filter((r) => r.action.type === 'forward').length;
|
||||
|
||||
this.appui.setSecondaryMenu({
|
||||
heading: 'Routes',
|
||||
groups: [
|
||||
{
|
||||
name: 'Actions',
|
||||
items: [
|
||||
{
|
||||
type: 'action',
|
||||
key: 'Refresh',
|
||||
iconName: 'lucide:RefreshCw',
|
||||
action: () => {
|
||||
console.log('Refresh routes');
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'action',
|
||||
key: 'Add Route',
|
||||
iconName: 'lucide:Plus',
|
||||
action: () => {
|
||||
console.log('Add route');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Statistics',
|
||||
items: [
|
||||
{ type: 'header' as const, label: `${total} Total Routes` },
|
||||
{ type: 'header' as const, label: `${active} Active` },
|
||||
{ type: 'header' as const, label: `${forwardCount} Forward` },
|
||||
{ type: 'header' as const, label: `${total - forwardCount} Socket Handler` },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
onDeactivate() {
|
||||
// Cleanup if needed
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
cssManager.defaultStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
padding: 24px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<sz-route-list-view
|
||||
.routes=${this.filteredRoutes}
|
||||
@route-click=${(e: CustomEvent<IRouteConfig>) => {
|
||||
console.log('Route clicked:', e.detail.name);
|
||||
}}
|
||||
></sz-route-list-view>
|
||||
`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user