@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
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import * as appstate from '../appstate.js';
|
|
2
|
+
import * as interfaces from '../../dist_ts_interfaces/index.js';
|
|
3
|
+
import { viewHostCss } from './shared/css.js';
|
|
4
|
+
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
DeesElement,
|
|
8
|
+
css,
|
|
9
|
+
cssManager,
|
|
10
|
+
customElement,
|
|
11
|
+
html,
|
|
12
|
+
state,
|
|
13
|
+
type TemplateResult,
|
|
14
|
+
} from '@design.estate/dees-element';
|
|
15
|
+
|
|
16
|
+
@customElement('ops-view-routes')
|
|
17
|
+
export class OpsViewRoutes extends DeesElement {
|
|
18
|
+
@state() accessor routeState: appstate.IRouteManagementState = {
|
|
19
|
+
mergedRoutes: [],
|
|
20
|
+
warnings: [],
|
|
21
|
+
apiTokens: [],
|
|
22
|
+
isLoading: false,
|
|
23
|
+
error: null,
|
|
24
|
+
lastUpdated: 0,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
constructor() {
|
|
28
|
+
super();
|
|
29
|
+
const sub = appstate.routeManagementStatePart
|
|
30
|
+
.select((s) => s)
|
|
31
|
+
.subscribe((routeState) => {
|
|
32
|
+
this.routeState = routeState;
|
|
33
|
+
});
|
|
34
|
+
this.rxSubscriptions.push(sub);
|
|
35
|
+
|
|
36
|
+
// Re-fetch routes when user logs in (fixes race condition where
|
|
37
|
+
// the view is created before authentication completes)
|
|
38
|
+
const loginSub = appstate.loginStatePart
|
|
39
|
+
.select((s) => s.isLoggedIn)
|
|
40
|
+
.subscribe((isLoggedIn) => {
|
|
41
|
+
if (isLoggedIn) {
|
|
42
|
+
appstate.routeManagementStatePart.dispatchAction(appstate.fetchMergedRoutesAction, null);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
this.rxSubscriptions.push(loginSub);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public static styles = [
|
|
49
|
+
cssManager.defaultStyles,
|
|
50
|
+
viewHostCss,
|
|
51
|
+
css`
|
|
52
|
+
.routesContainer {
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
gap: 24px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.warnings-bar {
|
|
59
|
+
background: ${cssManager.bdTheme('rgba(255, 170, 0, 0.08)', 'rgba(255, 170, 0, 0.1)')};
|
|
60
|
+
border: 1px solid ${cssManager.bdTheme('rgba(255, 170, 0, 0.25)', 'rgba(255, 170, 0, 0.3)')};
|
|
61
|
+
border-radius: 8px;
|
|
62
|
+
padding: 12px 16px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.warning-item {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
gap: 8px;
|
|
69
|
+
padding: 4px 0;
|
|
70
|
+
font-size: 13px;
|
|
71
|
+
color: ${cssManager.bdTheme('#b45309', '#fa0')};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.warning-icon {
|
|
75
|
+
flex-shrink: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.empty-state {
|
|
79
|
+
text-align: center;
|
|
80
|
+
padding: 48px 24px;
|
|
81
|
+
color: ${cssManager.bdTheme('#6b7280', '#666')};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.empty-state p {
|
|
85
|
+
margin: 8px 0;
|
|
86
|
+
}
|
|
87
|
+
`,
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
public render(): TemplateResult {
|
|
91
|
+
const { mergedRoutes, warnings } = this.routeState;
|
|
92
|
+
|
|
93
|
+
const hardcodedCount = mergedRoutes.filter((mr) => mr.source === 'hardcoded').length;
|
|
94
|
+
const programmaticCount = mergedRoutes.filter((mr) => mr.source === 'programmatic').length;
|
|
95
|
+
const disabledCount = mergedRoutes.filter((mr) => !mr.enabled).length;
|
|
96
|
+
|
|
97
|
+
const statsTiles: IStatsTile[] = [
|
|
98
|
+
{
|
|
99
|
+
id: 'totalRoutes',
|
|
100
|
+
title: 'Total Routes',
|
|
101
|
+
type: 'number',
|
|
102
|
+
value: mergedRoutes.length,
|
|
103
|
+
icon: 'lucide:route',
|
|
104
|
+
description: 'All configured routes',
|
|
105
|
+
color: '#3b82f6',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'hardcoded',
|
|
109
|
+
title: 'Hardcoded',
|
|
110
|
+
type: 'number',
|
|
111
|
+
value: hardcodedCount,
|
|
112
|
+
icon: 'lucide:lock',
|
|
113
|
+
description: 'Routes from constructor config',
|
|
114
|
+
color: '#8b5cf6',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'programmatic',
|
|
118
|
+
title: 'Programmatic',
|
|
119
|
+
type: 'number',
|
|
120
|
+
value: programmaticCount,
|
|
121
|
+
icon: 'lucide:code',
|
|
122
|
+
description: 'Routes added via API',
|
|
123
|
+
color: '#0ea5e9',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'disabled',
|
|
127
|
+
title: 'Disabled',
|
|
128
|
+
type: 'number',
|
|
129
|
+
value: disabledCount,
|
|
130
|
+
icon: 'lucide:pauseCircle',
|
|
131
|
+
description: 'Currently disabled routes',
|
|
132
|
+
color: disabledCount > 0 ? '#ef4444' : '#6b7280',
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
// Map merged routes to sz-route-list-view format
|
|
137
|
+
const szRoutes = mergedRoutes.map((mr) => {
|
|
138
|
+
const tags = [...(mr.route.tags || [])];
|
|
139
|
+
tags.push(mr.source);
|
|
140
|
+
if (!mr.enabled) tags.push('disabled');
|
|
141
|
+
if (mr.overridden) tags.push('overridden');
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
...mr.route,
|
|
145
|
+
enabled: mr.enabled,
|
|
146
|
+
tags,
|
|
147
|
+
id: mr.storedRouteId || mr.route.name || undefined,
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return html`
|
|
152
|
+
<ops-sectionheading>Route Management</ops-sectionheading>
|
|
153
|
+
|
|
154
|
+
<div class="routesContainer">
|
|
155
|
+
<dees-statsgrid
|
|
156
|
+
.tiles=${statsTiles}
|
|
157
|
+
.gridActions=${[
|
|
158
|
+
{
|
|
159
|
+
name: 'Add Route',
|
|
160
|
+
iconName: 'lucide:plus',
|
|
161
|
+
action: () => this.showCreateRouteDialog(),
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'Refresh',
|
|
165
|
+
iconName: 'lucide:refreshCw',
|
|
166
|
+
action: () => this.refreshData(),
|
|
167
|
+
},
|
|
168
|
+
]}
|
|
169
|
+
></dees-statsgrid>
|
|
170
|
+
|
|
171
|
+
${warnings.length > 0
|
|
172
|
+
? html`
|
|
173
|
+
<div class="warnings-bar">
|
|
174
|
+
${warnings.map(
|
|
175
|
+
(w) => html`
|
|
176
|
+
<div class="warning-item">
|
|
177
|
+
<span class="warning-icon">⚠</span>
|
|
178
|
+
<span>${w.message}</span>
|
|
179
|
+
</div>
|
|
180
|
+
`,
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
183
|
+
`
|
|
184
|
+
: ''}
|
|
185
|
+
|
|
186
|
+
${szRoutes.length > 0
|
|
187
|
+
? html`
|
|
188
|
+
<sz-route-list-view
|
|
189
|
+
.routes=${szRoutes}
|
|
190
|
+
@route-click=${(e: CustomEvent) => this.handleRouteClick(e)}
|
|
191
|
+
></sz-route-list-view>
|
|
192
|
+
`
|
|
193
|
+
: html`
|
|
194
|
+
<div class="empty-state">
|
|
195
|
+
<p>No routes configured</p>
|
|
196
|
+
<p>Add a programmatic route or check your constructor configuration.</p>
|
|
197
|
+
</div>
|
|
198
|
+
`}
|
|
199
|
+
</div>
|
|
200
|
+
`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private async handleRouteClick(e: CustomEvent) {
|
|
204
|
+
const clickedRoute = e.detail;
|
|
205
|
+
if (!clickedRoute) return;
|
|
206
|
+
|
|
207
|
+
// Find the corresponding merged route
|
|
208
|
+
const merged = this.routeState.mergedRoutes.find(
|
|
209
|
+
(mr) => mr.route.name === clickedRoute.name,
|
|
210
|
+
);
|
|
211
|
+
if (!merged) return;
|
|
212
|
+
|
|
213
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
214
|
+
|
|
215
|
+
if (merged.source === 'hardcoded') {
|
|
216
|
+
const menuOptions = merged.enabled
|
|
217
|
+
? [
|
|
218
|
+
{
|
|
219
|
+
name: 'Disable Route',
|
|
220
|
+
iconName: 'lucide:pause',
|
|
221
|
+
action: async (modalArg: any) => {
|
|
222
|
+
await appstate.routeManagementStatePart.dispatchAction(
|
|
223
|
+
appstate.setRouteOverrideAction,
|
|
224
|
+
{ routeName: merged.route.name!, enabled: false },
|
|
225
|
+
);
|
|
226
|
+
await modalArg.destroy();
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: 'Close',
|
|
231
|
+
iconName: 'lucide:x',
|
|
232
|
+
action: async (modalArg: any) => await modalArg.destroy(),
|
|
233
|
+
},
|
|
234
|
+
]
|
|
235
|
+
: [
|
|
236
|
+
{
|
|
237
|
+
name: 'Enable Route',
|
|
238
|
+
iconName: 'lucide:play',
|
|
239
|
+
action: async (modalArg: any) => {
|
|
240
|
+
await appstate.routeManagementStatePart.dispatchAction(
|
|
241
|
+
appstate.setRouteOverrideAction,
|
|
242
|
+
{ routeName: merged.route.name!, enabled: true },
|
|
243
|
+
);
|
|
244
|
+
await modalArg.destroy();
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'Remove Override',
|
|
249
|
+
iconName: 'lucide:undo',
|
|
250
|
+
action: async (modalArg: any) => {
|
|
251
|
+
await appstate.routeManagementStatePart.dispatchAction(
|
|
252
|
+
appstate.removeRouteOverrideAction,
|
|
253
|
+
merged.route.name!,
|
|
254
|
+
);
|
|
255
|
+
await modalArg.destroy();
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: 'Close',
|
|
260
|
+
iconName: 'lucide:x',
|
|
261
|
+
action: async (modalArg: any) => await modalArg.destroy(),
|
|
262
|
+
},
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
await DeesModal.createAndShow({
|
|
266
|
+
heading: `Route: ${merged.route.name}`,
|
|
267
|
+
content: html`
|
|
268
|
+
<div style="color: #ccc; padding: 8px 0;">
|
|
269
|
+
<p>Source: <strong style="color: #88f;">hardcoded</strong></p>
|
|
270
|
+
<p>Status: <strong>${merged.enabled ? 'Enabled' : 'Disabled (overridden)'}</strong></p>
|
|
271
|
+
<p style="color: #888; font-size: 13px;">Hardcoded routes cannot be edited or deleted, but they can be disabled via an override.</p>
|
|
272
|
+
</div>
|
|
273
|
+
`,
|
|
274
|
+
menuOptions,
|
|
275
|
+
});
|
|
276
|
+
} else {
|
|
277
|
+
// Programmatic route
|
|
278
|
+
await DeesModal.createAndShow({
|
|
279
|
+
heading: `Route: ${merged.route.name}`,
|
|
280
|
+
content: html`
|
|
281
|
+
<div style="color: #ccc; padding: 8px 0;">
|
|
282
|
+
<p>Source: <strong style="color: #0af;">programmatic</strong></p>
|
|
283
|
+
<p>Status: <strong>${merged.enabled ? 'Enabled' : 'Disabled'}</strong></p>
|
|
284
|
+
<p>ID: <code style="color: #888;">${merged.storedRouteId}</code></p>
|
|
285
|
+
</div>
|
|
286
|
+
`,
|
|
287
|
+
menuOptions: [
|
|
288
|
+
{
|
|
289
|
+
name: merged.enabled ? 'Disable' : 'Enable',
|
|
290
|
+
iconName: merged.enabled ? 'lucide:pause' : 'lucide:play',
|
|
291
|
+
action: async (modalArg: any) => {
|
|
292
|
+
await appstate.routeManagementStatePart.dispatchAction(
|
|
293
|
+
appstate.toggleRouteAction,
|
|
294
|
+
{ id: merged.storedRouteId!, enabled: !merged.enabled },
|
|
295
|
+
);
|
|
296
|
+
await modalArg.destroy();
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: 'Delete',
|
|
301
|
+
iconName: 'lucide:trash-2',
|
|
302
|
+
action: async (modalArg: any) => {
|
|
303
|
+
await appstate.routeManagementStatePart.dispatchAction(
|
|
304
|
+
appstate.deleteRouteAction,
|
|
305
|
+
merged.storedRouteId!,
|
|
306
|
+
);
|
|
307
|
+
await modalArg.destroy();
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: 'Close',
|
|
312
|
+
iconName: 'lucide:x',
|
|
313
|
+
action: async (modalArg: any) => await modalArg.destroy(),
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private async showCreateRouteDialog() {
|
|
321
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
322
|
+
|
|
323
|
+
await DeesModal.createAndShow({
|
|
324
|
+
heading: 'Add Programmatic Route',
|
|
325
|
+
content: html`
|
|
326
|
+
<dees-form>
|
|
327
|
+
<dees-input-text .key=${'name'} .label=${'Route Name'} .required=${true}></dees-input-text>
|
|
328
|
+
<dees-input-text .key=${'ports'} .label=${'Ports (comma-separated)'} .required=${true}></dees-input-text>
|
|
329
|
+
<dees-input-text .key=${'domains'} .label=${'Domains (comma-separated, optional)'}></dees-input-text>
|
|
330
|
+
<dees-input-text .key=${'targetHost'} .label=${'Target Host'} .value=${'localhost'} .required=${true}></dees-input-text>
|
|
331
|
+
<dees-input-text .key=${'targetPort'} .label=${'Target Port'} .required=${true}></dees-input-text>
|
|
332
|
+
</dees-form>
|
|
333
|
+
`,
|
|
334
|
+
menuOptions: [
|
|
335
|
+
{
|
|
336
|
+
name: 'Cancel',
|
|
337
|
+
iconName: 'lucide:x',
|
|
338
|
+
action: async (modalArg: any) => await modalArg.destroy(),
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: 'Create',
|
|
342
|
+
iconName: 'lucide:plus',
|
|
343
|
+
action: async (modalArg: any) => {
|
|
344
|
+
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
|
|
345
|
+
if (!form) return;
|
|
346
|
+
const formData = await form.collectFormData();
|
|
347
|
+
if (!formData.name || !formData.ports) return;
|
|
348
|
+
|
|
349
|
+
const ports = formData.ports.split(',').map((p: string) => parseInt(p.trim(), 10)).filter((p: number) => !isNaN(p));
|
|
350
|
+
const domains = formData.domains
|
|
351
|
+
? formData.domains.split(',').map((d: string) => d.trim()).filter(Boolean)
|
|
352
|
+
: undefined;
|
|
353
|
+
|
|
354
|
+
const route: any = {
|
|
355
|
+
name: formData.name,
|
|
356
|
+
match: {
|
|
357
|
+
ports,
|
|
358
|
+
...(domains && domains.length > 0 ? { domains } : {}),
|
|
359
|
+
},
|
|
360
|
+
action: {
|
|
361
|
+
type: 'forward',
|
|
362
|
+
targets: [
|
|
363
|
+
{
|
|
364
|
+
host: formData.targetHost || 'localhost',
|
|
365
|
+
port: parseInt(formData.targetPort, 10),
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
await appstate.routeManagementStatePart.dispatchAction(
|
|
372
|
+
appstate.createRouteAction,
|
|
373
|
+
{ route },
|
|
374
|
+
);
|
|
375
|
+
await modalArg.destroy();
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private refreshData() {
|
|
383
|
+
appstate.routeManagementStatePart.dispatchAction(appstate.fetchMergedRoutesAction, null);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async firstUpdated() {
|
|
387
|
+
await appstate.routeManagementStatePart.dispatchAction(appstate.fetchMergedRoutesAction, null);
|
|
388
|
+
}
|
|
389
|
+
}
|
package/ts_web/router.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as appstate from './appstate.js';
|
|
|
3
3
|
|
|
4
4
|
const SmartRouter = plugins.domtools.plugins.smartrouter.SmartRouter;
|
|
5
5
|
|
|
6
|
-
export const validViews = ['overview', 'network', 'emails', 'logs', 'configuration', 'security', 'certificates', 'remoteingress'] as const;
|
|
6
|
+
export const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress'] as const;
|
|
7
7
|
|
|
8
8
|
export type TValidView = typeof validViews[number];
|
|
9
9
|
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { CacheDb } from './classes.cachedb.js';
|
|
2
|
-
/**
|
|
3
|
-
* Configuration for the cache cleaner
|
|
4
|
-
*/
|
|
5
|
-
export interface ICacheCleanerOptions {
|
|
6
|
-
/** Cleanup interval in milliseconds (default: 1 hour) */
|
|
7
|
-
intervalMs?: number;
|
|
8
|
-
/** Enable verbose logging */
|
|
9
|
-
verbose?: boolean;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* CacheCleaner - Periodically removes expired documents from the cache
|
|
13
|
-
*
|
|
14
|
-
* Runs on a configurable interval (default: hourly) and queries each
|
|
15
|
-
* collection for documents where expiresAt < now(), then deletes them.
|
|
16
|
-
*/
|
|
17
|
-
export declare class CacheCleaner {
|
|
18
|
-
private cleanupInterval;
|
|
19
|
-
private isRunning;
|
|
20
|
-
private options;
|
|
21
|
-
private cacheDb;
|
|
22
|
-
constructor(cacheDb: CacheDb, options?: ICacheCleanerOptions);
|
|
23
|
-
/**
|
|
24
|
-
* Start the periodic cleanup process
|
|
25
|
-
*/
|
|
26
|
-
start(): void;
|
|
27
|
-
/**
|
|
28
|
-
* Stop the periodic cleanup process
|
|
29
|
-
*/
|
|
30
|
-
stop(): void;
|
|
31
|
-
/**
|
|
32
|
-
* Run a single cleanup cycle
|
|
33
|
-
*/
|
|
34
|
-
runCleanup(): Promise<void>;
|
|
35
|
-
/**
|
|
36
|
-
* Clean expired documents from a specific collection using smartdata API
|
|
37
|
-
*/
|
|
38
|
-
private cleanExpiredDocuments;
|
|
39
|
-
/**
|
|
40
|
-
* Check if the cleaner is running
|
|
41
|
-
*/
|
|
42
|
-
isActive(): boolean;
|
|
43
|
-
/**
|
|
44
|
-
* Get the cleanup interval in milliseconds
|
|
45
|
-
*/
|
|
46
|
-
getIntervalMs(): number;
|
|
47
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../plugins.js';
|
|
2
|
-
import { logger } from '../logger.js';
|
|
3
|
-
import { CacheDb } from './classes.cachedb.js';
|
|
4
|
-
// Import document classes for cleanup
|
|
5
|
-
import { CachedEmail } from './documents/classes.cached.email.js';
|
|
6
|
-
import { CachedIPReputation } from './documents/classes.cached.ip.reputation.js';
|
|
7
|
-
/**
|
|
8
|
-
* CacheCleaner - Periodically removes expired documents from the cache
|
|
9
|
-
*
|
|
10
|
-
* Runs on a configurable interval (default: hourly) and queries each
|
|
11
|
-
* collection for documents where expiresAt < now(), then deletes them.
|
|
12
|
-
*/
|
|
13
|
-
export class CacheCleaner {
|
|
14
|
-
cleanupInterval = null;
|
|
15
|
-
isRunning = false;
|
|
16
|
-
options;
|
|
17
|
-
cacheDb;
|
|
18
|
-
constructor(cacheDb, options = {}) {
|
|
19
|
-
this.cacheDb = cacheDb;
|
|
20
|
-
this.options = {
|
|
21
|
-
intervalMs: options.intervalMs || 60 * 60 * 1000, // 1 hour default
|
|
22
|
-
verbose: options.verbose || false,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Start the periodic cleanup process
|
|
27
|
-
*/
|
|
28
|
-
start() {
|
|
29
|
-
if (this.isRunning) {
|
|
30
|
-
logger.log('warn', 'CacheCleaner already running');
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
this.isRunning = true;
|
|
34
|
-
// Run cleanup immediately on start
|
|
35
|
-
this.runCleanup().catch((error) => {
|
|
36
|
-
logger.log('error', `Initial cache cleanup failed: ${error.message}`);
|
|
37
|
-
});
|
|
38
|
-
// Schedule periodic cleanup
|
|
39
|
-
this.cleanupInterval = setInterval(() => {
|
|
40
|
-
this.runCleanup().catch((error) => {
|
|
41
|
-
logger.log('error', `Cache cleanup failed: ${error.message}`);
|
|
42
|
-
});
|
|
43
|
-
}, this.options.intervalMs);
|
|
44
|
-
logger.log('info', `CacheCleaner started with interval: ${this.options.intervalMs / 1000 / 60} minutes`);
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Stop the periodic cleanup process
|
|
48
|
-
*/
|
|
49
|
-
stop() {
|
|
50
|
-
if (!this.isRunning) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
if (this.cleanupInterval) {
|
|
54
|
-
clearInterval(this.cleanupInterval);
|
|
55
|
-
this.cleanupInterval = null;
|
|
56
|
-
}
|
|
57
|
-
this.isRunning = false;
|
|
58
|
-
logger.log('info', 'CacheCleaner stopped');
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Run a single cleanup cycle
|
|
62
|
-
*/
|
|
63
|
-
async runCleanup() {
|
|
64
|
-
if (!this.cacheDb.isReady()) {
|
|
65
|
-
logger.log('warn', 'CacheDb not ready, skipping cleanup');
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const now = new Date();
|
|
69
|
-
const results = [];
|
|
70
|
-
try {
|
|
71
|
-
const emailsDeleted = await this.cleanExpiredDocuments(CachedEmail, now);
|
|
72
|
-
results.push({ collection: 'CachedEmail', deleted: emailsDeleted });
|
|
73
|
-
const ipReputationDeleted = await this.cleanExpiredDocuments(CachedIPReputation, now);
|
|
74
|
-
results.push({ collection: 'CachedIPReputation', deleted: ipReputationDeleted });
|
|
75
|
-
// Log results
|
|
76
|
-
const totalDeleted = results.reduce((sum, r) => sum + r.deleted, 0);
|
|
77
|
-
if (totalDeleted > 0 || this.options.verbose) {
|
|
78
|
-
const summary = results
|
|
79
|
-
.filter((r) => r.deleted > 0)
|
|
80
|
-
.map((r) => `${r.collection}: ${r.deleted}`)
|
|
81
|
-
.join(', ');
|
|
82
|
-
logger.log('info', `Cache cleanup completed. Deleted ${totalDeleted} expired documents. ${summary || 'No deletions.'}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
logger.log('error', `Cache cleanup error: ${error.message}`);
|
|
87
|
-
throw error;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Clean expired documents from a specific collection using smartdata API
|
|
92
|
-
*/
|
|
93
|
-
async cleanExpiredDocuments(documentClass, now) {
|
|
94
|
-
try {
|
|
95
|
-
// Find all expired documents
|
|
96
|
-
const expiredDocs = await documentClass.getInstances({
|
|
97
|
-
expiresAt: { $lt: now },
|
|
98
|
-
});
|
|
99
|
-
// Delete each expired document
|
|
100
|
-
let deletedCount = 0;
|
|
101
|
-
for (const doc of expiredDocs) {
|
|
102
|
-
try {
|
|
103
|
-
await doc.delete();
|
|
104
|
-
deletedCount++;
|
|
105
|
-
}
|
|
106
|
-
catch (deleteError) {
|
|
107
|
-
logger.log('warn', `Failed to delete expired document: ${deleteError.message}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return deletedCount;
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
logger.log('error', `Error cleaning collection: ${error.message}`);
|
|
114
|
-
return 0;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Check if the cleaner is running
|
|
119
|
-
*/
|
|
120
|
-
isActive() {
|
|
121
|
-
return this.isRunning;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Get the cleanup interval in milliseconds
|
|
125
|
-
*/
|
|
126
|
-
getIntervalMs() {
|
|
127
|
-
return this.options.intervalMs;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5jYWNoZS5jbGVhbmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvY2FjaGUvY2xhc3Nlcy5jYWNoZS5jbGVhbmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDdEMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRS9DLHNDQUFzQztBQUN0QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDbEUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNkNBQTZDLENBQUM7QUFZakY7Ozs7O0dBS0c7QUFDSCxNQUFNLE9BQU8sWUFBWTtJQUNmLGVBQWUsR0FBMEMsSUFBSSxDQUFDO0lBQzlELFNBQVMsR0FBWSxLQUFLLENBQUM7SUFDM0IsT0FBTyxDQUFpQztJQUN4QyxPQUFPLENBQVU7SUFFekIsWUFBWSxPQUFnQixFQUFFLFVBQWdDLEVBQUU7UUFDOUQsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLE9BQU8sR0FBRztZQUNiLFVBQVUsRUFBRSxPQUFPLENBQUMsVUFBVSxJQUFJLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLGlCQUFpQjtZQUNuRSxPQUFPLEVBQUUsT0FBTyxDQUFDLE9BQU8sSUFBSSxLQUFLO1NBQ2xDLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLO1FBQ1YsSUFBSSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDbkIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsOEJBQThCLENBQUMsQ0FBQztZQUNuRCxPQUFPO1FBQ1QsQ0FBQztRQUVELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO1FBRXRCLG1DQUFtQztRQUNuQyxJQUFJ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
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../plugins.js';
|
|
2
|
-
/**
|
|
3
|
-
* Base class for all cached documents with TTL support
|
|
4
|
-
*
|
|
5
|
-
* Extends smartdata's SmartDataDbDoc to add:
|
|
6
|
-
* - Automatic timestamps (createdAt, lastAccessedAt)
|
|
7
|
-
* - TTL/expiration support (expiresAt)
|
|
8
|
-
* - Helper methods for TTL management
|
|
9
|
-
*
|
|
10
|
-
* NOTE: Subclasses MUST add @svDb() decorators to createdAt, expiresAt, and lastAccessedAt
|
|
11
|
-
* since decorators on abstract classes don't propagate correctly.
|
|
12
|
-
*/
|
|
13
|
-
export declare abstract class CachedDocument<T extends CachedDocument<T>> extends plugins.smartdata.SmartDataDbDoc<T, T> {
|
|
14
|
-
/**
|
|
15
|
-
* Timestamp when the document was created
|
|
16
|
-
* NOTE: Subclasses must add @svDb() decorator
|
|
17
|
-
*/
|
|
18
|
-
createdAt: Date;
|
|
19
|
-
/**
|
|
20
|
-
* Timestamp when the document expires and should be cleaned up
|
|
21
|
-
* NOTE: Subclasses must add @svDb() decorator
|
|
22
|
-
*/
|
|
23
|
-
expiresAt: Date;
|
|
24
|
-
/**
|
|
25
|
-
* Timestamp of last access (for LRU-style eviction if needed)
|
|
26
|
-
* NOTE: Subclasses must add @svDb() decorator
|
|
27
|
-
*/
|
|
28
|
-
lastAccessedAt: Date;
|
|
29
|
-
/**
|
|
30
|
-
* Set the TTL (time to live) for this document
|
|
31
|
-
* @param ttlMs Time to live in milliseconds
|
|
32
|
-
*/
|
|
33
|
-
setTTL(ttlMs: number): void;
|
|
34
|
-
/**
|
|
35
|
-
* Set TTL using days
|
|
36
|
-
* @param days Number of days until expiration
|
|
37
|
-
*/
|
|
38
|
-
setTTLDays(days: number): void;
|
|
39
|
-
/**
|
|
40
|
-
* Set TTL using hours
|
|
41
|
-
* @param hours Number of hours until expiration
|
|
42
|
-
*/
|
|
43
|
-
setTTLHours(hours: number): void;
|
|
44
|
-
/**
|
|
45
|
-
* Check if this document has expired
|
|
46
|
-
*/
|
|
47
|
-
isExpired(): boolean;
|
|
48
|
-
/**
|
|
49
|
-
* Update the lastAccessedAt timestamp
|
|
50
|
-
*/
|
|
51
|
-
touch(): void;
|
|
52
|
-
/**
|
|
53
|
-
* Get remaining TTL in milliseconds
|
|
54
|
-
* Returns 0 if expired, -1 if no expiration set
|
|
55
|
-
*/
|
|
56
|
-
getRemainingTTL(): number;
|
|
57
|
-
/**
|
|
58
|
-
* Extend the TTL by the specified milliseconds from now
|
|
59
|
-
* @param ttlMs Additional time to live in milliseconds
|
|
60
|
-
*/
|
|
61
|
-
extendTTL(ttlMs: number): void;
|
|
62
|
-
/**
|
|
63
|
-
* Set the document to never expire (100 years in the future)
|
|
64
|
-
*/
|
|
65
|
-
setNeverExpires(): void;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* TTL constants in milliseconds
|
|
69
|
-
*/
|
|
70
|
-
export declare const TTL: {
|
|
71
|
-
readonly HOURS_1: number;
|
|
72
|
-
readonly HOURS_24: number;
|
|
73
|
-
readonly DAYS_7: number;
|
|
74
|
-
readonly DAYS_30: number;
|
|
75
|
-
readonly DAYS_90: number;
|
|
76
|
-
};
|