@serve.zone/catalog 1.0.1

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 (122) hide show
  1. package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
  2. package/dist_ts_web/00_commitinfo_data.js +9 -0
  3. package/dist_ts_web/elements/index.d.ts +33 -0
  4. package/dist_ts_web/elements/index.js +45 -0
  5. package/dist_ts_web/elements/sz-certificates-card.d.ts +14 -0
  6. package/dist_ts_web/elements/sz-certificates-card.js +210 -0
  7. package/dist_ts_web/elements/sz-dashboard-view.d.ts +33 -0
  8. package/dist_ts_web/elements/sz-dashboard-view.js +242 -0
  9. package/dist_ts_web/elements/sz-demo-view-dashboard.d.ts +18 -0
  10. package/dist_ts_web/elements/sz-demo-view-dashboard.js +184 -0
  11. package/dist_ts_web/elements/sz-demo-view-network.d.ts +32 -0
  12. package/dist_ts_web/elements/sz-demo-view-network.js +384 -0
  13. package/dist_ts_web/elements/sz-demo-view-registries.d.ts +22 -0
  14. package/dist_ts_web/elements/sz-demo-view-registries.js +240 -0
  15. package/dist_ts_web/elements/sz-demo-view-services.d.ts +32 -0
  16. package/dist_ts_web/elements/sz-demo-view-services.js +468 -0
  17. package/dist_ts_web/elements/sz-demo-view-settings.d.ts +19 -0
  18. package/dist_ts_web/elements/sz-demo-view-settings.js +151 -0
  19. package/dist_ts_web/elements/sz-demo-view-tokens.d.ts +20 -0
  20. package/dist_ts_web/elements/sz-demo-view-tokens.js +141 -0
  21. package/dist_ts_web/elements/sz-dns-ssl-card.d.ts +13 -0
  22. package/dist_ts_web/elements/sz-dns-ssl-card.js +180 -0
  23. package/dist_ts_web/elements/sz-domain-detail-view.d.ts +48 -0
  24. package/dist_ts_web/elements/sz-domain-detail-view.js +789 -0
  25. package/dist_ts_web/elements/sz-login-view.d.ts +18 -0
  26. package/dist_ts_web/elements/sz-login-view.js +384 -0
  27. package/dist_ts_web/elements/sz-network-dns-view.d.ts +20 -0
  28. package/dist_ts_web/elements/sz-network-dns-view.js +244 -0
  29. package/dist_ts_web/elements/sz-network-domains-view.d.ts +28 -0
  30. package/dist_ts_web/elements/sz-network-domains-view.js +312 -0
  31. package/dist_ts_web/elements/sz-network-proxy-view.d.ts +39 -0
  32. package/dist_ts_web/elements/sz-network-proxy-view.js +510 -0
  33. package/dist_ts_web/elements/sz-platform-service-detail-view.d.ts +49 -0
  34. package/dist_ts_web/elements/sz-platform-service-detail-view.js +733 -0
  35. package/dist_ts_web/elements/sz-platform-services-card.d.ts +19 -0
  36. package/dist_ts_web/elements/sz-platform-services-card.js +196 -0
  37. package/dist_ts_web/elements/sz-quick-actions-card.d.ts +19 -0
  38. package/dist_ts_web/elements/sz-quick-actions-card.js +194 -0
  39. package/dist_ts_web/elements/sz-registry-external-view.d.ts +22 -0
  40. package/dist_ts_web/elements/sz-registry-external-view.js +313 -0
  41. package/dist_ts_web/elements/sz-registry-onebox-view.d.ts +14 -0
  42. package/dist_ts_web/elements/sz-registry-onebox-view.js +307 -0
  43. package/dist_ts_web/elements/sz-resource-usage-card.d.ts +25 -0
  44. package/dist_ts_web/elements/sz-resource-usage-card.js +323 -0
  45. package/dist_ts_web/elements/sz-reverse-proxy-card.d.ts +16 -0
  46. package/dist_ts_web/elements/sz-reverse-proxy-card.js +216 -0
  47. package/dist_ts_web/elements/sz-service-create-view.d.ts +67 -0
  48. package/dist_ts_web/elements/sz-service-create-view.js +828 -0
  49. package/dist_ts_web/elements/sz-service-detail-view.d.ts +57 -0
  50. package/dist_ts_web/elements/sz-service-detail-view.js +728 -0
  51. package/dist_ts_web/elements/sz-services-backups-view.d.ts +37 -0
  52. package/dist_ts_web/elements/sz-services-backups-view.js +413 -0
  53. package/dist_ts_web/elements/sz-services-list-view.d.ts +20 -0
  54. package/dist_ts_web/elements/sz-services-list-view.js +272 -0
  55. package/dist_ts_web/elements/sz-settings-view.d.ts +30 -0
  56. package/dist_ts_web/elements/sz-settings-view.js +448 -0
  57. package/dist_ts_web/elements/sz-stat-card.d.ts +17 -0
  58. package/dist_ts_web/elements/sz-stat-card.js +249 -0
  59. package/dist_ts_web/elements/sz-status-grid-cluster.d.ts +19 -0
  60. package/dist_ts_web/elements/sz-status-grid-cluster.js +142 -0
  61. package/dist_ts_web/elements/sz-status-grid-infra.d.ts +17 -0
  62. package/dist_ts_web/elements/sz-status-grid-infra.js +140 -0
  63. package/dist_ts_web/elements/sz-status-grid-network.d.ts +30 -0
  64. package/dist_ts_web/elements/sz-status-grid-network.js +190 -0
  65. package/dist_ts_web/elements/sz-status-grid-services.d.ts +17 -0
  66. package/dist_ts_web/elements/sz-status-grid-services.js +145 -0
  67. package/dist_ts_web/elements/sz-tokens-view.d.ts +26 -0
  68. package/dist_ts_web/elements/sz-tokens-view.js +344 -0
  69. package/dist_ts_web/elements/sz-traffic-card.d.ts +24 -0
  70. package/dist_ts_web/elements/sz-traffic-card.js +255 -0
  71. package/dist_ts_web/index.d.ts +2 -0
  72. package/dist_ts_web/index.js +3 -0
  73. package/dist_ts_web/pages/index.d.ts +3 -0
  74. package/dist_ts_web/pages/index.js +4 -0
  75. package/dist_ts_web/pages/mainpage.d.ts +1 -0
  76. package/dist_ts_web/pages/mainpage.js +46 -0
  77. package/dist_ts_web/pages/sz-demo-app-shell.d.ts +13 -0
  78. package/dist_ts_web/pages/sz-demo-app-shell.js +212 -0
  79. package/dist_ts_web/pages/sz-demo-app.d.ts +2 -0
  80. package/dist_ts_web/pages/sz-demo-app.js +20 -0
  81. package/npmextra.json +24 -0
  82. package/package.json +45 -0
  83. package/ts_web/00_commitinfo_data.ts +8 -0
  84. package/ts_web/elements/index.ts +54 -0
  85. package/ts_web/elements/sz-certificates-card.ts +155 -0
  86. package/ts_web/elements/sz-dashboard-view.ts +217 -0
  87. package/ts_web/elements/sz-demo-view-dashboard.ts +150 -0
  88. package/ts_web/elements/sz-demo-view-network.ts +354 -0
  89. package/ts_web/elements/sz-demo-view-registries.ts +206 -0
  90. package/ts_web/elements/sz-demo-view-services.ts +434 -0
  91. package/ts_web/elements/sz-demo-view-settings.ts +118 -0
  92. package/ts_web/elements/sz-demo-view-tokens.ts +109 -0
  93. package/ts_web/elements/sz-dns-ssl-card.ts +130 -0
  94. package/ts_web/elements/sz-domain-detail-view.ts +766 -0
  95. package/ts_web/elements/sz-login-view.ts +329 -0
  96. package/ts_web/elements/sz-network-dns-view.ts +208 -0
  97. package/ts_web/elements/sz-network-domains-view.ts +273 -0
  98. package/ts_web/elements/sz-network-proxy-view.ts +456 -0
  99. package/ts_web/elements/sz-platform-service-detail-view.ts +714 -0
  100. package/ts_web/elements/sz-platform-services-card.ts +163 -0
  101. package/ts_web/elements/sz-quick-actions-card.ts +161 -0
  102. package/ts_web/elements/sz-registry-external-view.ts +279 -0
  103. package/ts_web/elements/sz-registry-onebox-view.ts +258 -0
  104. package/ts_web/elements/sz-resource-usage-card.ts +284 -0
  105. package/ts_web/elements/sz-reverse-proxy-card.ts +151 -0
  106. package/ts_web/elements/sz-service-create-view.ts +773 -0
  107. package/ts_web/elements/sz-service-detail-view.ts +710 -0
  108. package/ts_web/elements/sz-services-backups-view.ts +390 -0
  109. package/ts_web/elements/sz-services-list-view.ts +237 -0
  110. package/ts_web/elements/sz-settings-view.ts +417 -0
  111. package/ts_web/elements/sz-stat-card.ts +187 -0
  112. package/ts_web/elements/sz-status-grid-cluster.ts +105 -0
  113. package/ts_web/elements/sz-status-grid-infra.ts +88 -0
  114. package/ts_web/elements/sz-status-grid-network.ts +152 -0
  115. package/ts_web/elements/sz-status-grid-services.ts +99 -0
  116. package/ts_web/elements/sz-tokens-view.ts +308 -0
  117. package/ts_web/elements/sz-traffic-card.ts +222 -0
  118. package/ts_web/index.ts +2 -0
  119. package/ts_web/pages/index.ts +3 -0
  120. package/ts_web/pages/mainpage.ts +46 -0
  121. package/ts_web/pages/sz-demo-app-shell.ts +179 -0
  122. package/ts_web/pages/sz-demo-app.ts +20 -0
@@ -0,0 +1,456 @@
1
+ import {
2
+ DeesElement,
3
+ customElement,
4
+ html,
5
+ css,
6
+ cssManager,
7
+ property,
8
+ type TemplateResult,
9
+ } from '@design.estate/dees-element';
10
+
11
+ import './sz-stat-card.js';
12
+
13
+ declare global {
14
+ interface HTMLElementTagNameMap {
15
+ 'sz-network-proxy-view': SzNetworkProxyView;
16
+ }
17
+ }
18
+
19
+ export interface ITrafficTarget {
20
+ type: 'service' | 'registry' | 'platform';
21
+ name: string;
22
+ domain: string | null;
23
+ target: string;
24
+ status: 'running' | 'stopped';
25
+ }
26
+
27
+ export interface IAccessLogEntry {
28
+ timestamp: string;
29
+ method: string;
30
+ path: string;
31
+ status: number;
32
+ duration: number;
33
+ ip: string;
34
+ }
35
+
36
+ @customElement('sz-network-proxy-view')
37
+ export class SzNetworkProxyView extends DeesElement {
38
+ public static demo = () => html`
39
+ <div style="padding: 24px; max-width: 1400px;">
40
+ <sz-network-proxy-view
41
+ proxyStatus="running"
42
+ routeCount="3"
43
+ certificateCount="2"
44
+ targetCount="11"
45
+ .targets=${[
46
+ { type: 'service', name: 'test-nginx', domain: 'app.bleu.de', target: 'localhost:8080', status: 'running' },
47
+ { type: 'service', name: 'hello-world', domain: 'hello.task.vc', target: 'localhost:8081', status: 'running' },
48
+ { type: 'registry', name: 'onebox-registry', domain: null, target: 'localhost:4000', status: 'running' },
49
+ { type: 'platform', name: 'MongoDB', domain: null, target: 'localhost:27017', status: 'running' },
50
+ { type: 'platform', name: 'ClickHouse', domain: null, target: 'localhost:8123', status: 'running' },
51
+ ]}
52
+ .logs=${[
53
+ { timestamp: '2024-01-02 10:15:32', method: 'GET', path: '/api/services', status: 200, duration: 45, ip: '192.168.1.100' },
54
+ { timestamp: '2024-01-02 10:15:30', method: 'POST', path: '/api/auth/login', status: 200, duration: 120, ip: '192.168.1.101' },
55
+ { timestamp: '2024-01-02 10:15:28', method: 'GET', path: '/static/bundle.js', status: 304, duration: 5, ip: '192.168.1.100' },
56
+ ]}
57
+ ></sz-network-proxy-view>
58
+ </div>
59
+ `;
60
+
61
+ @property({ type: String })
62
+ public accessor proxyStatus: 'running' | 'stopped' = 'stopped';
63
+
64
+ @property({ type: String })
65
+ public accessor routeCount: string = '0';
66
+
67
+ @property({ type: String })
68
+ public accessor certificateCount: string = '0';
69
+
70
+ @property({ type: String })
71
+ public accessor targetCount: string = '0';
72
+
73
+ @property({ type: Array })
74
+ public accessor targets: ITrafficTarget[] = [];
75
+
76
+ @property({ type: Array })
77
+ public accessor logs: IAccessLogEntry[] = [];
78
+
79
+ @property({ type: Boolean })
80
+ public accessor streaming: boolean = false;
81
+
82
+ public static styles = [
83
+ cssManager.defaultStyles,
84
+ css`
85
+ :host {
86
+ display: block;
87
+ }
88
+
89
+ .actions {
90
+ display: flex;
91
+ justify-content: flex-end;
92
+ margin-bottom: 16px;
93
+ }
94
+
95
+ .refresh-button {
96
+ display: inline-flex;
97
+ align-items: center;
98
+ gap: 8px;
99
+ padding: 8px 16px;
100
+ background: ${cssManager.bdTheme('#ffffff', '#09090b')};
101
+ border: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
102
+ border-radius: 6px;
103
+ font-size: 14px;
104
+ font-weight: 500;
105
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
106
+ cursor: pointer;
107
+ transition: all 200ms ease;
108
+ }
109
+
110
+ .refresh-button:hover {
111
+ background: ${cssManager.bdTheme('#f4f4f5', '#18181b')};
112
+ }
113
+
114
+ .stats-grid {
115
+ display: grid;
116
+ grid-template-columns: repeat(2, 1fr);
117
+ gap: 16px;
118
+ margin-bottom: 24px;
119
+ }
120
+
121
+ @media (min-width: 768px) {
122
+ .stats-grid {
123
+ grid-template-columns: repeat(4, 1fr);
124
+ }
125
+ }
126
+
127
+ .section {
128
+ background: ${cssManager.bdTheme('#ffffff', '#09090b')};
129
+ border: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
130
+ border-radius: 8px;
131
+ margin-bottom: 24px;
132
+ overflow: hidden;
133
+ }
134
+
135
+ .section-header {
136
+ padding: 16px;
137
+ border-bottom: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
138
+ }
139
+
140
+ .section-title {
141
+ font-size: 16px;
142
+ font-weight: 600;
143
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
144
+ }
145
+
146
+ .section-subtitle {
147
+ font-size: 13px;
148
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
149
+ margin-top: 2px;
150
+ }
151
+
152
+ .table-header {
153
+ display: grid;
154
+ grid-template-columns: 80px 1.5fr 1.5fr 1.5fr 80px;
155
+ gap: 16px;
156
+ padding: 12px 16px;
157
+ background: ${cssManager.bdTheme('#f4f4f5', '#18181b')};
158
+ border-bottom: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
159
+ font-size: 12px;
160
+ font-weight: 600;
161
+ text-transform: uppercase;
162
+ letter-spacing: 0.05em;
163
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
164
+ }
165
+
166
+ .table-row {
167
+ display: grid;
168
+ grid-template-columns: 80px 1.5fr 1.5fr 1.5fr 80px;
169
+ gap: 16px;
170
+ padding: 12px 16px;
171
+ border-bottom: 1px solid ${cssManager.bdTheme('#f4f4f5', '#27272a')};
172
+ font-size: 14px;
173
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
174
+ cursor: pointer;
175
+ transition: background 200ms ease;
176
+ }
177
+
178
+ .table-row:last-child {
179
+ border-bottom: none;
180
+ }
181
+
182
+ .table-row:hover {
183
+ background: ${cssManager.bdTheme('#f4f4f5', '#18181b')};
184
+ }
185
+
186
+ .type-badge {
187
+ display: inline-flex;
188
+ align-items: center;
189
+ padding: 2px 8px;
190
+ border-radius: 4px;
191
+ font-size: 11px;
192
+ font-weight: 600;
193
+ text-transform: uppercase;
194
+ }
195
+
196
+ .type-badge.service {
197
+ background: ${cssManager.bdTheme('#dbeafe', 'rgba(59, 130, 246, 0.2)')};
198
+ color: ${cssManager.bdTheme('#2563eb', '#60a5fa')};
199
+ }
200
+
201
+ .type-badge.registry {
202
+ background: ${cssManager.bdTheme('#f3e8ff', 'rgba(168, 85, 247, 0.2)')};
203
+ color: ${cssManager.bdTheme('#9333ea', '#a855f7')};
204
+ }
205
+
206
+ .type-badge.platform {
207
+ background: ${cssManager.bdTheme('#fef3c7', 'rgba(245, 158, 11, 0.2)')};
208
+ color: ${cssManager.bdTheme('#d97706', '#f59e0b')};
209
+ }
210
+
211
+ .target-value {
212
+ font-family: monospace;
213
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
214
+ }
215
+
216
+ .status-badge {
217
+ display: inline-flex;
218
+ align-items: center;
219
+ padding: 2px 8px;
220
+ border-radius: 9999px;
221
+ font-size: 12px;
222
+ font-weight: 500;
223
+ }
224
+
225
+ .status-badge.running {
226
+ background: ${cssManager.bdTheme('#dcfce7', 'rgba(34, 197, 94, 0.2)')};
227
+ color: ${cssManager.bdTheme('#16a34a', '#22c55e')};
228
+ }
229
+
230
+ .status-badge.stopped {
231
+ background: ${cssManager.bdTheme('#fee2e2', 'rgba(239, 68, 68, 0.2)')};
232
+ color: ${cssManager.bdTheme('#dc2626', '#ef4444')};
233
+ }
234
+
235
+ .logs-header {
236
+ display: flex;
237
+ justify-content: space-between;
238
+ align-items: center;
239
+ padding: 16px;
240
+ border-bottom: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
241
+ }
242
+
243
+ .logs-actions {
244
+ display: flex;
245
+ gap: 8px;
246
+ }
247
+
248
+ .stream-button {
249
+ display: inline-flex;
250
+ align-items: center;
251
+ gap: 6px;
252
+ padding: 6px 12px;
253
+ background: ${cssManager.bdTheme('#2563eb', '#3b82f6')};
254
+ border: none;
255
+ border-radius: 4px;
256
+ font-size: 13px;
257
+ font-weight: 500;
258
+ color: white;
259
+ cursor: pointer;
260
+ transition: all 200ms ease;
261
+ }
262
+
263
+ .stream-button:hover {
264
+ background: ${cssManager.bdTheme('#1d4ed8', '#2563eb')};
265
+ }
266
+
267
+ .stream-button.streaming {
268
+ background: ${cssManager.bdTheme('#dc2626', '#ef4444')};
269
+ }
270
+
271
+ .stream-button.streaming:hover {
272
+ background: ${cssManager.bdTheme('#b91c1c', '#dc2626')};
273
+ }
274
+
275
+ .clear-button {
276
+ padding: 6px 12px;
277
+ background: transparent;
278
+ border: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
279
+ border-radius: 4px;
280
+ font-size: 13px;
281
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
282
+ cursor: pointer;
283
+ transition: all 200ms ease;
284
+ }
285
+
286
+ .clear-button:hover {
287
+ background: ${cssManager.bdTheme('#f4f4f5', '#18181b')};
288
+ }
289
+
290
+ .logs-container {
291
+ padding: 16px;
292
+ font-family: monospace;
293
+ font-size: 13px;
294
+ max-height: 300px;
295
+ overflow-y: auto;
296
+ background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
297
+ }
298
+
299
+ .log-entry {
300
+ padding: 4px 0;
301
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
302
+ }
303
+
304
+ .log-timestamp {
305
+ color: ${cssManager.bdTheme('#a1a1aa', '#52525b')};
306
+ }
307
+
308
+ .log-method {
309
+ font-weight: 600;
310
+ color: ${cssManager.bdTheme('#2563eb', '#60a5fa')};
311
+ }
312
+
313
+ .log-status-2xx {
314
+ color: ${cssManager.bdTheme('#16a34a', '#22c55e')};
315
+ }
316
+
317
+ .log-status-3xx {
318
+ color: ${cssManager.bdTheme('#2563eb', '#60a5fa')};
319
+ }
320
+
321
+ .log-status-4xx {
322
+ color: ${cssManager.bdTheme('#ca8a04', '#facc15')};
323
+ }
324
+
325
+ .log-status-5xx {
326
+ color: ${cssManager.bdTheme('#dc2626', '#ef4444')};
327
+ }
328
+
329
+ .empty-logs {
330
+ padding: 24px;
331
+ text-align: center;
332
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
333
+ }
334
+ `,
335
+ ];
336
+
337
+ public render(): TemplateResult {
338
+ return html`
339
+ <div class="actions">
340
+ <button class="refresh-button" @click=${() => this.handleRefresh()}>Refresh</button>
341
+ </div>
342
+
343
+ <div class="stats-grid">
344
+ <sz-stat-card
345
+ label="Proxy Status"
346
+ value="${this.proxyStatus === 'running' ? 'Running' : 'Stopped'}"
347
+ icon="server"
348
+ variant="${this.proxyStatus === 'running' ? 'success' : 'error'}"
349
+ valueBadge
350
+ ></sz-stat-card>
351
+ <sz-stat-card
352
+ label="Routes"
353
+ value="${this.routeCount}"
354
+ icon="server"
355
+ ></sz-stat-card>
356
+ <sz-stat-card
357
+ label="Certificates"
358
+ value="${this.certificateCount}"
359
+ icon="check"
360
+ ></sz-stat-card>
361
+ <sz-stat-card
362
+ label="Targets"
363
+ value="${this.targetCount}"
364
+ icon="server"
365
+ ></sz-stat-card>
366
+ </div>
367
+
368
+ <div class="section">
369
+ <div class="section-header">
370
+ <div class="section-title">Traffic Targets</div>
371
+ <div class="section-subtitle">Services, registry, and platform services with their routing info</div>
372
+ </div>
373
+ <div class="table-header">
374
+ <span>Type</span>
375
+ <span>Name</span>
376
+ <span>Domain</span>
377
+ <span>Target</span>
378
+ <span>Status</span>
379
+ </div>
380
+ ${this.targets.map(target => html`
381
+ <div class="table-row" @click=${() => this.handleTargetClick(target)}>
382
+ <span><span class="type-badge ${target.type}">${target.type}</span></span>
383
+ <span>${target.name}</span>
384
+ <span>${target.domain || '-'}</span>
385
+ <span class="target-value">${target.target}</span>
386
+ <span><span class="status-badge ${target.status}">${target.status}</span></span>
387
+ </div>
388
+ `)}
389
+ </div>
390
+
391
+ <div class="section">
392
+ <div class="logs-header">
393
+ <div>
394
+ <div class="section-title">Access Logs</div>
395
+ <div class="section-subtitle">Real-time Caddy access logs</div>
396
+ </div>
397
+ <div class="logs-actions">
398
+ <button class="stream-button ${this.streaming ? 'streaming' : ''}" @click=${() => this.toggleStreaming()}>
399
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
400
+ ${this.streaming
401
+ ? html`<rect x="6" y="6" width="12" height="12" rx="1"/>`
402
+ : html`<polygon points="5,3 19,12 5,21"/>`
403
+ }
404
+ </svg>
405
+ ${this.streaming ? 'Stop' : 'Stream'}
406
+ </button>
407
+ <button class="clear-button" @click=${() => this.handleClearLogs()}>
408
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
409
+ <polyline points="3,6 5,6 21,6"/><path d="M19,6v14a2,2,0,0,1-2,2H7a2,2,0,0,1-2-2V6m3,0V4a2,2,0,0,1,2-2h4a2,2,0,0,1,2,2v2"/>
410
+ </svg>
411
+ Clear logs
412
+ </button>
413
+ </div>
414
+ </div>
415
+ <div class="logs-container">
416
+ ${this.logs.length > 0 ? this.logs.map(log => html`
417
+ <div class="log-entry">
418
+ <span class="log-timestamp">${log.timestamp}</span>
419
+ <span class="log-method">${log.method}</span>
420
+ ${log.path}
421
+ <span class="${this.getStatusClass(log.status)}">${log.status}</span>
422
+ ${log.duration}ms
423
+ ${log.ip}
424
+ </div>
425
+ `) : html`
426
+ <div class="empty-logs">Click "Stream" to start live access log streaming</div>
427
+ `}
428
+ </div>
429
+ </div>
430
+ `;
431
+ }
432
+
433
+ private getStatusClass(status: number): string {
434
+ if (status >= 500) return 'log-status-5xx';
435
+ if (status >= 400) return 'log-status-4xx';
436
+ if (status >= 300) return 'log-status-3xx';
437
+ return 'log-status-2xx';
438
+ }
439
+
440
+ private handleRefresh() {
441
+ this.dispatchEvent(new CustomEvent('refresh', { bubbles: true, composed: true }));
442
+ }
443
+
444
+ private handleTargetClick(target: ITrafficTarget) {
445
+ this.dispatchEvent(new CustomEvent('target-click', { detail: target, bubbles: true, composed: true }));
446
+ }
447
+
448
+ private toggleStreaming() {
449
+ this.streaming = !this.streaming;
450
+ this.dispatchEvent(new CustomEvent('stream-toggle', { detail: { streaming: this.streaming }, bubbles: true, composed: true }));
451
+ }
452
+
453
+ private handleClearLogs() {
454
+ this.dispatchEvent(new CustomEvent('clear-logs', { bubbles: true, composed: true }));
455
+ }
456
+ }