@serve.zone/dcrouter 8.0.0 → 9.1.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_serve/bundle.js +2420 -1227
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +9 -0
- package/dist_ts/classes.dcrouter.js +27 -1
- package/dist_ts/config/classes.api-token-manager.d.ts +38 -0
- package/dist_ts/config/classes.api-token-manager.js +134 -0
- package/dist_ts/config/classes.route-config-manager.d.ts +35 -0
- package/dist_ts/config/classes.route-config-manager.js +231 -0
- package/dist_ts/config/index.d.ts +2 -0
- package/dist_ts/config/index.js +3 -1
- package/dist_ts/opsserver/classes.opsserver.d.ts +2 -0
- package/dist_ts/opsserver/classes.opsserver.js +5 -1
- package/dist_ts/opsserver/handlers/{config.handler.d.ts → api-token.handler.d.ts} +5 -2
- package/dist_ts/opsserver/handlers/api-token.handler.js +66 -0
- package/dist_ts/opsserver/handlers/index.d.ts +2 -0
- package/dist_ts/opsserver/handlers/index.js +3 -1
- package/dist_ts/opsserver/handlers/route-management.handler.d.ts +13 -0
- package/dist_ts/opsserver/handlers/route-management.handler.js +117 -0
- package/dist_ts_interfaces/data/index.d.ts +1 -0
- package/dist_ts_interfaces/data/index.js +2 -1
- package/dist_ts_interfaces/data/route-management.d.ts +68 -0
- package/dist_ts_interfaces/data/route-management.js +2 -0
- package/dist_ts_interfaces/requests/api-tokens.d.ts +63 -0
- package/dist_ts_interfaces/requests/api-tokens.js +2 -0
- package/dist_ts_interfaces/requests/config.d.ts +77 -1
- package/dist_ts_interfaces/requests/index.d.ts +2 -0
- package/dist_ts_interfaces/requests/index.js +3 -1
- package/dist_ts_interfaces/requests/route-management.d.ts +114 -0
- package/dist_ts_interfaces/requests/route-management.js +2 -0
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +37 -1
- package/dist_ts_web/appstate.js +220 -2
- package/dist_ts_web/elements/index.d.ts +2 -0
- package/dist_ts_web/elements/index.js +3 -1
- package/dist_ts_web/elements/ops-dashboard.js +23 -3
- package/dist_ts_web/elements/ops-view-apitokens.d.ts +12 -0
- package/dist_ts_web/elements/ops-view-apitokens.js +310 -0
- package/dist_ts_web/elements/ops-view-config.d.ts +10 -8
- package/dist_ts_web/elements/ops-view-config.js +215 -297
- package/dist_ts_web/elements/ops-view-routes.d.ts +12 -0
- package/dist_ts_web/elements/ops-view-routes.js +404 -0
- package/dist_ts_web/router.d.ts +1 -1
- package/dist_ts_web/router.js +2 -2
- package/package.json +2 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +37 -1
- package/ts/config/classes.api-token-manager.ts +155 -0
- package/ts/config/classes.route-config-manager.ts +271 -0
- package/ts/config/index.ts +3 -1
- package/ts/opsserver/classes.opsserver.ts +4 -0
- package/ts/opsserver/handlers/api-token.handler.ts +96 -0
- package/ts/opsserver/handlers/config.handler.ts +154 -72
- package/ts/opsserver/handlers/index.ts +3 -1
- package/ts/opsserver/handlers/route-management.handler.ts +163 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +309 -2
- package/ts_web/elements/index.ts +2 -0
- package/ts_web/elements/ops-dashboard.ts +22 -2
- package/ts_web/elements/ops-view-apitokens.ts +285 -0
- package/ts_web/elements/ops-view-config.ts +237 -299
- package/ts_web/elements/ops-view-routes.ts +389 -0
- package/ts_web/router.ts +1 -1
- package/dist_ts/cache/classes.cache.cleaner.d.ts +0 -47
- package/dist_ts/cache/classes.cache.cleaner.js +0 -130
- package/dist_ts/cache/classes.cached.document.d.ts +0 -76
- package/dist_ts/cache/classes.cached.document.js +0 -100
- package/dist_ts/cache/classes.cachedb.d.ts +0 -60
- package/dist_ts/cache/classes.cachedb.js +0 -126
- package/dist_ts/cache/documents/classes.cached.email.d.ts +0 -125
- package/dist_ts/cache/documents/classes.cached.email.js +0 -337
- package/dist_ts/cache/documents/classes.cached.ip.reputation.d.ts +0 -119
- package/dist_ts/cache/documents/classes.cached.ip.reputation.js +0 -323
- package/dist_ts/cache/documents/index.d.ts +0 -2
- package/dist_ts/cache/documents/index.js +0 -3
- package/dist_ts/cache/index.d.ts +0 -4
- package/dist_ts/cache/index.js +0 -7
- package/dist_ts/monitoring/classes.metricscache.d.ts +0 -32
- package/dist_ts/monitoring/classes.metricscache.js +0 -63
- package/dist_ts/opsserver/handlers/admin.handler.d.ts +0 -31
- package/dist_ts/opsserver/handlers/admin.handler.js +0 -180
- package/dist_ts/opsserver/handlers/config.handler.js +0 -67
- package/dist_ts/opsserver/handlers/logs.handler.d.ts +0 -17
- package/dist_ts/opsserver/handlers/logs.handler.js +0 -215
- package/dist_ts/security/classes.securitylogger.js +0 -235
- package/dist_ts/storage/classes.storagemanager.d.ts +0 -82
- package/dist_ts/storage/classes.storagemanager.js +0 -344
- package/dist_ts/storage/index.d.ts +0 -1
- package/dist_ts/storage/index.js +0 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
2
|
import * as shared from './shared/index.js';
|
|
3
3
|
import * as appstate from '../appstate.js';
|
|
4
|
+
import { appRouter } from '../router.js';
|
|
4
5
|
|
|
5
6
|
import {
|
|
6
7
|
DeesElement,
|
|
@@ -12,6 +13,8 @@ import {
|
|
|
12
13
|
type TemplateResult,
|
|
13
14
|
} from '@design.estate/dees-element';
|
|
14
15
|
|
|
16
|
+
import type { IConfigField, IConfigSectionAction } from '@serve.zone/catalog';
|
|
17
|
+
|
|
15
18
|
@customElement('ops-view-config')
|
|
16
19
|
export class OpsViewConfig extends DeesElement {
|
|
17
20
|
@state()
|
|
@@ -35,165 +38,19 @@ export class OpsViewConfig extends DeesElement {
|
|
|
35
38
|
cssManager.defaultStyles,
|
|
36
39
|
shared.viewHostCss,
|
|
37
40
|
css`
|
|
38
|
-
.configSection {
|
|
39
|
-
background: ${cssManager.bdTheme('#fff', '#222')};
|
|
40
|
-
border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
|
|
41
|
-
border-radius: 8px;
|
|
42
|
-
margin-bottom: 24px;
|
|
43
|
-
overflow: hidden;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.sectionHeader {
|
|
47
|
-
background: ${cssManager.bdTheme('#f8f9fa', '#1a1a1a')};
|
|
48
|
-
padding: 16px 24px;
|
|
49
|
-
border-bottom: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
|
|
50
|
-
display: flex;
|
|
51
|
-
justify-content: space-between;
|
|
52
|
-
align-items: center;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.sectionTitle {
|
|
56
|
-
font-size: 18px;
|
|
57
|
-
font-weight: 600;
|
|
58
|
-
color: ${cssManager.bdTheme('#333', '#ccc')};
|
|
59
|
-
display: flex;
|
|
60
|
-
align-items: center;
|
|
61
|
-
gap: 12px;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.sectionTitle dees-icon {
|
|
65
|
-
font-size: 20px;
|
|
66
|
-
color: ${cssManager.bdTheme('#666', '#888')};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.sectionContent {
|
|
70
|
-
padding: 24px;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.configField {
|
|
74
|
-
margin-bottom: 20px;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.configField:last-child {
|
|
78
|
-
margin-bottom: 0;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.fieldLabel {
|
|
82
|
-
font-size: 13px;
|
|
83
|
-
font-weight: 600;
|
|
84
|
-
color: ${cssManager.bdTheme('#666', '#999')};
|
|
85
|
-
margin-bottom: 8px;
|
|
86
|
-
display: block;
|
|
87
|
-
text-transform: uppercase;
|
|
88
|
-
letter-spacing: 0.5px;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.fieldValue {
|
|
92
|
-
font-family: 'Consolas', 'Monaco', monospace;
|
|
93
|
-
font-size: 14px;
|
|
94
|
-
color: ${cssManager.bdTheme('#333', '#ccc')};
|
|
95
|
-
background: ${cssManager.bdTheme('#f8f9fa', '#1a1a1a')};
|
|
96
|
-
padding: 10px 14px;
|
|
97
|
-
border-radius: 6px;
|
|
98
|
-
border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.fieldValue.empty {
|
|
102
|
-
color: ${cssManager.bdTheme('#999', '#666')};
|
|
103
|
-
font-style: italic;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.nestedFields {
|
|
107
|
-
margin-left: 16px;
|
|
108
|
-
padding-left: 16px;
|
|
109
|
-
border-left: 2px solid ${cssManager.bdTheme('#e9ecef', '#333')};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/* Status badge styles */
|
|
113
|
-
.statusBadge {
|
|
114
|
-
display: inline-flex;
|
|
115
|
-
align-items: center;
|
|
116
|
-
gap: 6px;
|
|
117
|
-
padding: 4px 12px;
|
|
118
|
-
border-radius: 20px;
|
|
119
|
-
font-size: 13px;
|
|
120
|
-
font-weight: 600;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.statusBadge.enabled {
|
|
124
|
-
background: ${cssManager.bdTheme('#d4edda', '#1a3d1a')};
|
|
125
|
-
color: ${cssManager.bdTheme('#155724', '#66cc66')};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.statusBadge.disabled {
|
|
129
|
-
background: ${cssManager.bdTheme('#f8d7da', '#3d1a1a')};
|
|
130
|
-
color: ${cssManager.bdTheme('#721c24', '#cc6666')};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.statusBadge dees-icon {
|
|
134
|
-
font-size: 14px;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/* Array/list display */
|
|
138
|
-
.arrayItems {
|
|
139
|
-
display: flex;
|
|
140
|
-
flex-wrap: wrap;
|
|
141
|
-
gap: 8px;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.arrayItem {
|
|
145
|
-
display: inline-flex;
|
|
146
|
-
align-items: center;
|
|
147
|
-
background: ${cssManager.bdTheme('#e7f3ff', '#1a2a3d')};
|
|
148
|
-
color: ${cssManager.bdTheme('#0066cc', '#66aaff')};
|
|
149
|
-
padding: 4px 12px;
|
|
150
|
-
border-radius: 16px;
|
|
151
|
-
font-size: 13px;
|
|
152
|
-
font-family: 'Consolas', 'Monaco', monospace;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
.arrayCount {
|
|
156
|
-
font-size: 12px;
|
|
157
|
-
color: ${cssManager.bdTheme('#999', '#666')};
|
|
158
|
-
margin-bottom: 8px;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/* Numeric value formatting */
|
|
162
|
-
.numericValue {
|
|
163
|
-
font-weight: 600;
|
|
164
|
-
color: ${cssManager.bdTheme('#0066cc', '#66aaff')};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
.errorMessage {
|
|
168
|
-
background: ${cssManager.bdTheme('#fee', '#4a1f1f')};
|
|
169
|
-
border: 1px solid ${cssManager.bdTheme('#fcc', '#6a2f2f')};
|
|
170
|
-
border-radius: 4px;
|
|
171
|
-
padding: 16px;
|
|
172
|
-
color: ${cssManager.bdTheme('#c00', '#ff6666')};
|
|
173
|
-
margin: 16px 0;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
41
|
.loadingMessage {
|
|
177
42
|
text-align: center;
|
|
178
43
|
padding: 40px;
|
|
179
|
-
color: ${cssManager.bdTheme('#
|
|
44
|
+
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
|
|
180
45
|
}
|
|
181
46
|
|
|
182
|
-
.
|
|
183
|
-
background: ${cssManager.bdTheme('#
|
|
184
|
-
border: 1px solid ${cssManager.bdTheme('#
|
|
47
|
+
.errorMessage {
|
|
48
|
+
background: ${cssManager.bdTheme('#fee2e2', 'rgba(239,68,68,0.1)')};
|
|
49
|
+
border: 1px solid ${cssManager.bdTheme('#fecaca', 'rgba(239,68,68,0.3)')};
|
|
185
50
|
border-radius: 8px;
|
|
186
51
|
padding: 16px;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
display: flex;
|
|
190
|
-
align-items: center;
|
|
191
|
-
gap: 12px;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
.infoNote dees-icon {
|
|
195
|
-
font-size: 20px;
|
|
196
|
-
flex-shrink: 0;
|
|
52
|
+
color: ${cssManager.bdTheme('#dc2626', '#ef4444')};
|
|
53
|
+
margin: 16px 0;
|
|
197
54
|
}
|
|
198
55
|
`,
|
|
199
56
|
];
|
|
@@ -202,185 +59,266 @@ export class OpsViewConfig extends DeesElement {
|
|
|
202
59
|
return html`
|
|
203
60
|
<ops-sectionheading>Configuration</ops-sectionheading>
|
|
204
61
|
|
|
205
|
-
${this.configState.isLoading
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
${this.renderConfigSection('dns', 'DNS', 'lucide:globe', this.configState.config?.dns)}
|
|
222
|
-
${this.renderConfigSection('proxy', 'Proxy', 'lucide:network', this.configState.config?.proxy)}
|
|
223
|
-
${this.renderConfigSection('security', 'Security', 'lucide:shield', this.configState.config?.security)}
|
|
224
|
-
` : html`
|
|
225
|
-
<div class="errorMessage">No configuration loaded</div>
|
|
226
|
-
`}
|
|
62
|
+
${this.configState.isLoading
|
|
63
|
+
? html`
|
|
64
|
+
<div class="loadingMessage">
|
|
65
|
+
<dees-spinner></dees-spinner>
|
|
66
|
+
<p>Loading configuration...</p>
|
|
67
|
+
</div>
|
|
68
|
+
`
|
|
69
|
+
: this.configState.error
|
|
70
|
+
? html`
|
|
71
|
+
<div class="errorMessage">
|
|
72
|
+
Error loading configuration: ${this.configState.error}
|
|
73
|
+
</div>
|
|
74
|
+
`
|
|
75
|
+
: this.configState.config
|
|
76
|
+
? this.renderConfig()
|
|
77
|
+
: html`<div class="errorMessage">No configuration loaded</div>`}
|
|
227
78
|
`;
|
|
228
79
|
}
|
|
229
80
|
|
|
230
|
-
private
|
|
231
|
-
const
|
|
81
|
+
private renderConfig(): TemplateResult {
|
|
82
|
+
const cfg = this.configState.config!;
|
|
232
83
|
|
|
233
84
|
return html`
|
|
234
|
-
<
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
85
|
+
<sz-config-overview
|
|
86
|
+
infoText="This view displays the current running configuration. DcRouter is configured through code or remote management."
|
|
87
|
+
@navigate=${(e: CustomEvent) => {
|
|
88
|
+
if (e.detail?.view) {
|
|
89
|
+
appRouter.navigateToView(e.detail.view);
|
|
90
|
+
}
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
${this.renderSystemSection(cfg.system)}
|
|
94
|
+
${this.renderSmartProxySection(cfg.smartProxy)}
|
|
95
|
+
${this.renderEmailSection(cfg.email)}
|
|
96
|
+
${this.renderDnsSection(cfg.dns)}
|
|
97
|
+
${this.renderTlsSection(cfg.tls)}
|
|
98
|
+
${this.renderCacheSection(cfg.cache)}
|
|
99
|
+
${this.renderRadiusSection(cfg.radius)}
|
|
100
|
+
${this.renderRemoteIngressSection(cfg.remoteIngress)}
|
|
101
|
+
</sz-config-overview>
|
|
248
102
|
`;
|
|
249
103
|
}
|
|
250
104
|
|
|
251
|
-
private
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
:
|
|
105
|
+
private renderSystemSection(sys: appstate.IConfigState['config']['system']): TemplateResult {
|
|
106
|
+
const fields: IConfigField[] = [
|
|
107
|
+
{ key: 'Base Directory', value: sys.baseDir },
|
|
108
|
+
{ key: 'Data Directory', value: sys.dataDir },
|
|
109
|
+
{ key: 'Public IP', value: sys.publicIp },
|
|
110
|
+
{ key: 'Proxy IPs', value: sys.proxyIps.length > 0 ? sys.proxyIps : null, type: 'pills' },
|
|
111
|
+
{ key: 'Uptime', value: this.formatUptime(sys.uptime) },
|
|
112
|
+
{ key: 'Storage Backend', value: sys.storageBackend, type: 'badge' },
|
|
113
|
+
{ key: 'Storage Path', value: sys.storagePath },
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
return html`
|
|
117
|
+
<sz-config-section
|
|
118
|
+
title="System"
|
|
119
|
+
subtitle="Base paths and infrastructure"
|
|
120
|
+
icon="lucide:server"
|
|
121
|
+
status="enabled"
|
|
122
|
+
.fields=${fields}
|
|
123
|
+
></sz-config-section>
|
|
124
|
+
`;
|
|
255
125
|
}
|
|
256
126
|
|
|
257
|
-
private
|
|
258
|
-
|
|
259
|
-
|
|
127
|
+
private renderSmartProxySection(proxy: appstate.IConfigState['config']['smartProxy']): TemplateResult {
|
|
128
|
+
const fields: IConfigField[] = [
|
|
129
|
+
{ key: 'Route Count', value: proxy.routeCount },
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
if (proxy.acme) {
|
|
133
|
+
fields.push(
|
|
134
|
+
{ key: 'ACME Enabled', value: proxy.acme.enabled, type: 'boolean' },
|
|
135
|
+
{ key: 'Account Email', value: proxy.acme.accountEmail || null },
|
|
136
|
+
{ key: 'Use Production', value: proxy.acme.useProduction, type: 'boolean' },
|
|
137
|
+
{ key: 'Auto Renew', value: proxy.acme.autoRenew, type: 'boolean' },
|
|
138
|
+
{ key: 'Renew Threshold', value: `${proxy.acme.renewThresholdDays} days` },
|
|
139
|
+
);
|
|
260
140
|
}
|
|
261
141
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
// Handle boolean values with badges
|
|
267
|
-
if (typeof value === 'boolean') {
|
|
268
|
-
return html`
|
|
269
|
-
<div class="configField">
|
|
270
|
-
<label class="fieldLabel">${displayName}</label>
|
|
271
|
-
${this.renderStatusBadge(value)}
|
|
272
|
-
</div>
|
|
273
|
-
`;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Handle arrays
|
|
277
|
-
if (Array.isArray(value)) {
|
|
278
|
-
return html`
|
|
279
|
-
<div class="configField">
|
|
280
|
-
<label class="fieldLabel">${displayName}</label>
|
|
281
|
-
${this.renderArrayValue(value, key)}
|
|
282
|
-
</div>
|
|
283
|
-
`;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Handle nested objects
|
|
287
|
-
if (typeof value === 'object' && value !== null) {
|
|
288
|
-
return html`
|
|
289
|
-
<div class="configField">
|
|
290
|
-
<label class="fieldLabel">${displayName}</label>
|
|
291
|
-
<div class="nestedFields">
|
|
292
|
-
${this.renderConfigFields(value, fieldName)}
|
|
293
|
-
</div>
|
|
294
|
-
</div>
|
|
295
|
-
`;
|
|
296
|
-
}
|
|
142
|
+
const actions: IConfigSectionAction[] = [
|
|
143
|
+
{ label: 'View Routes', icon: 'lucide:arrow-right', event: 'navigate', detail: { view: 'routes' } },
|
|
144
|
+
];
|
|
297
145
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
146
|
+
return html`
|
|
147
|
+
<sz-config-section
|
|
148
|
+
title="SmartProxy"
|
|
149
|
+
subtitle="HTTP/HTTPS and TCP/SNI reverse proxy"
|
|
150
|
+
icon="lucide:network"
|
|
151
|
+
.status=${proxy.enabled ? 'enabled' : 'disabled'}
|
|
152
|
+
.fields=${fields}
|
|
153
|
+
.actions=${actions}
|
|
154
|
+
></sz-config-section>
|
|
155
|
+
`;
|
|
306
156
|
}
|
|
307
157
|
|
|
308
|
-
private
|
|
309
|
-
|
|
310
|
-
|
|
158
|
+
private renderEmailSection(email: appstate.IConfigState['config']['email']): TemplateResult {
|
|
159
|
+
const fields: IConfigField[] = [
|
|
160
|
+
{ key: 'Ports', value: email.ports.length > 0 ? email.ports.map(String) : null, type: 'pills' },
|
|
161
|
+
{ key: 'Hostname', value: email.hostname },
|
|
162
|
+
{ key: 'Domains', value: email.domains.length > 0 ? email.domains : null, type: 'pills' },
|
|
163
|
+
{ key: 'Email Routes', value: email.emailRouteCount },
|
|
164
|
+
{ key: 'Received Emails Path', value: email.receivedEmailsPath },
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
if (email.portMapping) {
|
|
168
|
+
const mappingStr = Object.entries(email.portMapping)
|
|
169
|
+
.map(([ext, int]) => `${ext} → ${int}`)
|
|
170
|
+
.join(', ');
|
|
171
|
+
fields.splice(1, 0, { key: 'Port Mapping', value: mappingStr, type: 'code' });
|
|
311
172
|
}
|
|
312
173
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (showAsPills) {
|
|
317
|
-
const itemLabel = this.getArrayItemLabel(fieldKey, arr.length);
|
|
318
|
-
return html`
|
|
319
|
-
<div class="arrayCount">${arr.length} ${itemLabel}</div>
|
|
320
|
-
<div class="arrayItems">
|
|
321
|
-
${arr.map(item => html`<span class="arrayItem">${item}</span>`)}
|
|
322
|
-
</div>
|
|
323
|
-
`;
|
|
324
|
-
}
|
|
174
|
+
const actions: IConfigSectionAction[] = [
|
|
175
|
+
{ label: 'View Emails', icon: 'lucide:arrow-right', event: 'navigate', detail: { view: 'emails' } },
|
|
176
|
+
];
|
|
325
177
|
|
|
326
|
-
// For complex arrays, show as JSON
|
|
327
178
|
return html`
|
|
328
|
-
<
|
|
329
|
-
|
|
330
|
-
|
|
179
|
+
<sz-config-section
|
|
180
|
+
title="Email Server"
|
|
181
|
+
subtitle="SMTP email handling with smartmta"
|
|
182
|
+
icon="lucide:mail"
|
|
183
|
+
.status=${email.enabled ? 'enabled' : 'disabled'}
|
|
184
|
+
.fields=${fields}
|
|
185
|
+
.actions=${actions}
|
|
186
|
+
></sz-config-section>
|
|
331
187
|
`;
|
|
332
188
|
}
|
|
333
189
|
|
|
334
|
-
private
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
190
|
+
private renderDnsSection(dns: appstate.IConfigState['config']['dns']): TemplateResult {
|
|
191
|
+
const fields: IConfigField[] = [
|
|
192
|
+
{ key: 'Port', value: dns.port },
|
|
193
|
+
{ key: 'NS Domains', value: dns.nsDomains.length > 0 ? dns.nsDomains : null, type: 'pills' },
|
|
194
|
+
{ key: 'Scopes', value: dns.scopes.length > 0 ? dns.scopes : null, type: 'pills' },
|
|
195
|
+
{ key: 'Record Count', value: dns.recordCount },
|
|
196
|
+
{ key: 'DNS Challenge', value: dns.dnsChallenge, type: 'boolean' },
|
|
197
|
+
];
|
|
341
198
|
|
|
342
|
-
|
|
343
|
-
|
|
199
|
+
return html`
|
|
200
|
+
<sz-config-section
|
|
201
|
+
title="DNS Server"
|
|
202
|
+
subtitle="Authoritative DNS with smartdns"
|
|
203
|
+
icon="lucide:globe"
|
|
204
|
+
.status=${dns.enabled ? 'enabled' : 'disabled'}
|
|
205
|
+
.fields=${fields}
|
|
206
|
+
></sz-config-section>
|
|
207
|
+
`;
|
|
344
208
|
}
|
|
345
209
|
|
|
346
|
-
private
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
.
|
|
351
|
-
.
|
|
210
|
+
private renderTlsSection(tls: appstate.IConfigState['config']['tls']): TemplateResult {
|
|
211
|
+
const fields: IConfigField[] = [
|
|
212
|
+
{ key: 'Contact Email', value: tls.contactEmail },
|
|
213
|
+
{ key: 'Domain', value: tls.domain },
|
|
214
|
+
{ key: 'Source', value: tls.source, type: 'badge' },
|
|
215
|
+
{ key: 'Certificate Path', value: tls.certPath },
|
|
216
|
+
{ key: 'Key Path', value: tls.keyPath },
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
const status = tls.source === 'none' ? 'not-configured' : 'enabled';
|
|
220
|
+
const actions: IConfigSectionAction[] = [
|
|
221
|
+
{ label: 'View Certificates', icon: 'lucide:arrow-right', event: 'navigate', detail: { view: 'certificates' } },
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
return html`
|
|
225
|
+
<sz-config-section
|
|
226
|
+
title="TLS / Certificates"
|
|
227
|
+
subtitle="Certificate management and ACME"
|
|
228
|
+
icon="lucide:shield-check"
|
|
229
|
+
.status=${status as any}
|
|
230
|
+
.fields=${fields}
|
|
231
|
+
.actions=${actions}
|
|
232
|
+
></sz-config-section>
|
|
233
|
+
`;
|
|
352
234
|
}
|
|
353
235
|
|
|
354
|
-
private
|
|
355
|
-
|
|
356
|
-
|
|
236
|
+
private renderCacheSection(cache: appstate.IConfigState['config']['cache']): TemplateResult {
|
|
237
|
+
const fields: IConfigField[] = [
|
|
238
|
+
{ key: 'Storage Path', value: cache.storagePath },
|
|
239
|
+
{ key: 'DB Name', value: cache.dbName },
|
|
240
|
+
{ key: 'Default TTL', value: `${cache.defaultTTLDays} days` },
|
|
241
|
+
{ key: 'Cleanup Interval', value: `${cache.cleanupIntervalHours} hours` },
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
if (cache.ttlConfig && Object.keys(cache.ttlConfig).length > 0) {
|
|
245
|
+
for (const [key, val] of Object.entries(cache.ttlConfig)) {
|
|
246
|
+
fields.push({ key: `TTL: ${key}`, value: `${val} days` });
|
|
247
|
+
}
|
|
357
248
|
}
|
|
358
249
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
250
|
+
return html`
|
|
251
|
+
<sz-config-section
|
|
252
|
+
title="Cache Database"
|
|
253
|
+
subtitle="Persistent caching with smartdata"
|
|
254
|
+
icon="lucide:database"
|
|
255
|
+
.status=${cache.enabled ? 'enabled' : 'disabled'}
|
|
256
|
+
.fields=${fields}
|
|
257
|
+
></sz-config-section>
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private renderRadiusSection(radius: appstate.IConfigState['config']['radius']): TemplateResult {
|
|
262
|
+
const fields: IConfigField[] = [
|
|
263
|
+
{ key: 'Auth Port', value: radius.authPort },
|
|
264
|
+
{ key: 'Accounting Port', value: radius.acctPort },
|
|
265
|
+
{ key: 'Bind Address', value: radius.bindAddress },
|
|
266
|
+
{ key: 'Client Count', value: radius.clientCount },
|
|
267
|
+
];
|
|
268
|
+
|
|
269
|
+
if (radius.vlanDefaultVlan !== null) {
|
|
270
|
+
fields.push(
|
|
271
|
+
{ key: 'Default VLAN', value: radius.vlanDefaultVlan },
|
|
272
|
+
{ key: 'Allow Unknown MACs', value: radius.vlanAllowUnknownMacs, type: 'boolean' },
|
|
273
|
+
{ key: 'VLAN Mappings', value: radius.vlanMappingCount },
|
|
274
|
+
);
|
|
374
275
|
}
|
|
375
276
|
|
|
376
|
-
|
|
277
|
+
const status = radius.enabled ? 'enabled' : 'not-configured';
|
|
278
|
+
|
|
279
|
+
return html`
|
|
280
|
+
<sz-config-section
|
|
281
|
+
title="RADIUS Server"
|
|
282
|
+
subtitle="Network authentication and VLAN assignment"
|
|
283
|
+
icon="lucide:wifi"
|
|
284
|
+
.status=${status as any}
|
|
285
|
+
.fields=${fields}
|
|
286
|
+
></sz-config-section>
|
|
287
|
+
`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private renderRemoteIngressSection(ri: appstate.IConfigState['config']['remoteIngress']): TemplateResult {
|
|
291
|
+
const fields: IConfigField[] = [
|
|
292
|
+
{ key: 'Tunnel Port', value: ri.tunnelPort },
|
|
293
|
+
{ key: 'Hub Domain', value: ri.hubDomain },
|
|
294
|
+
{ key: 'TLS Configured', value: ri.tlsConfigured, type: 'boolean' },
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
const actions: IConfigSectionAction[] = [
|
|
298
|
+
{ label: 'View Remote Ingress', icon: 'lucide:arrow-right', event: 'navigate', detail: { view: 'remoteingress' } },
|
|
299
|
+
];
|
|
300
|
+
|
|
301
|
+
return html`
|
|
302
|
+
<sz-config-section
|
|
303
|
+
title="Remote Ingress"
|
|
304
|
+
subtitle="Edge tunnel nodes"
|
|
305
|
+
icon="lucide:cloud"
|
|
306
|
+
.status=${ri.enabled ? 'enabled' : 'disabled'}
|
|
307
|
+
.fields=${fields}
|
|
308
|
+
.actions=${actions}
|
|
309
|
+
></sz-config-section>
|
|
310
|
+
`;
|
|
377
311
|
}
|
|
378
312
|
|
|
379
|
-
private
|
|
380
|
-
|
|
381
|
-
const
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
313
|
+
private formatUptime(seconds: number): string {
|
|
314
|
+
const days = Math.floor(seconds / 86400);
|
|
315
|
+
const hours = Math.floor((seconds % 86400) / 3600);
|
|
316
|
+
const mins = Math.floor((seconds % 3600) / 60);
|
|
317
|
+
|
|
318
|
+
const parts: string[] = [];
|
|
319
|
+
if (days > 0) parts.push(`${days}d`);
|
|
320
|
+
if (hours > 0) parts.push(`${hours}h`);
|
|
321
|
+
parts.push(`${mins}m`);
|
|
322
|
+
return parts.join(' ');
|
|
385
323
|
}
|
|
386
324
|
}
|