@serve.zone/dcrouter 7.4.3 → 8.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 +11567 -3516
- 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/{email-ops.handler.d.ts → api-token.handler.d.ts} +4 -4
- 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/{radius.handler.d.ts → route-management.handler.d.ts} +6 -1
- 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/email-ops.d.ts +51 -108
- 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 +38 -16
- package/dist_ts_web/appstate.js +226 -177
- 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 +11 -1
- package/dist_ts_web/elements/ops-view-apitokens.d.ts +12 -0
- package/dist_ts_web/elements/ops-view-apitokens.js +306 -0
- package/dist_ts_web/elements/ops-view-emails.d.ts +8 -31
- package/dist_ts_web/elements/ops-view-emails.js +54 -769
- package/dist_ts_web/elements/ops-view-logs.d.ts +2 -8
- package/dist_ts_web/elements/ops-view-logs.js +4 -101
- 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/plugins.d.ts +2 -1
- package/dist_ts_web/plugins.js +4 -2
- package/dist_ts_web/router.d.ts +1 -7
- package/dist_ts_web/router.js +8 -82
- package/package.json +2 -1
- 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/email-ops.handler.ts +177 -225
- 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 +316 -222
- package/ts_web/elements/index.ts +2 -0
- package/ts_web/elements/ops-dashboard.ts +10 -0
- package/ts_web/elements/ops-view-apitokens.ts +281 -0
- package/ts_web/elements/ops-view-emails.ts +40 -749
- package/ts_web/elements/ops-view-logs.ts +2 -87
- package/ts_web/elements/ops-view-routes.ts +389 -0
- package/ts_web/plugins.ts +4 -0
- package/ts_web/router.ts +7 -82
- 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/monitoring/classes.metricsmanager.d.ts +0 -169
- package/dist_ts/monitoring/classes.metricsmanager.js +0 -591
- package/dist_ts/monitoring/index.d.ts +0 -1
- package/dist_ts/monitoring/index.js +0 -2
- 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/certificate.handler.d.ts +0 -34
- package/dist_ts/opsserver/handlers/certificate.handler.js +0 -419
- package/dist_ts/opsserver/handlers/config.handler.d.ts +0 -9
- package/dist_ts/opsserver/handlers/config.handler.js +0 -67
- package/dist_ts/opsserver/handlers/email-ops.handler.js +0 -219
- package/dist_ts/opsserver/handlers/logs.handler.d.ts +0 -17
- package/dist_ts/opsserver/handlers/logs.handler.js +0 -215
- package/dist_ts/opsserver/handlers/radius.handler.js +0 -296
- package/dist_ts/opsserver/handlers/remoteingress.handler.d.ts +0 -8
- package/dist_ts/opsserver/handlers/remoteingress.handler.js +0 -154
- package/dist_ts/opsserver/handlers/security.handler.d.ts +0 -11
- package/dist_ts/opsserver/handlers/security.handler.js +0 -232
- package/dist_ts/opsserver/handlers/stats.handler.d.ts +0 -13
- package/dist_ts/opsserver/handlers/stats.handler.js +0 -400
- package/dist_ts/security/classes.securitylogger.d.ts +0 -140
- 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,281 @@
|
|
|
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
|
+
|
|
5
|
+
import {
|
|
6
|
+
DeesElement,
|
|
7
|
+
css,
|
|
8
|
+
cssManager,
|
|
9
|
+
customElement,
|
|
10
|
+
html,
|
|
11
|
+
state,
|
|
12
|
+
type TemplateResult,
|
|
13
|
+
} from '@design.estate/dees-element';
|
|
14
|
+
|
|
15
|
+
type TApiTokenScope = interfaces.data.TApiTokenScope;
|
|
16
|
+
|
|
17
|
+
@customElement('ops-view-apitokens')
|
|
18
|
+
export class OpsViewApiTokens extends DeesElement {
|
|
19
|
+
@state() accessor routeState: appstate.IRouteManagementState = {
|
|
20
|
+
mergedRoutes: [],
|
|
21
|
+
warnings: [],
|
|
22
|
+
apiTokens: [],
|
|
23
|
+
isLoading: false,
|
|
24
|
+
error: null,
|
|
25
|
+
lastUpdated: 0,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
constructor() {
|
|
29
|
+
super();
|
|
30
|
+
const sub = appstate.routeManagementStatePart
|
|
31
|
+
.select((s) => s)
|
|
32
|
+
.subscribe((routeState) => {
|
|
33
|
+
this.routeState = routeState;
|
|
34
|
+
});
|
|
35
|
+
this.rxSubscriptions.push(sub);
|
|
36
|
+
|
|
37
|
+
// Re-fetch tokens when user logs in (fixes race condition where
|
|
38
|
+
// the view is created before authentication completes)
|
|
39
|
+
const loginSub = appstate.loginStatePart
|
|
40
|
+
.select((s) => s.isLoggedIn)
|
|
41
|
+
.subscribe((isLoggedIn) => {
|
|
42
|
+
if (isLoggedIn) {
|
|
43
|
+
appstate.routeManagementStatePart.dispatchAction(appstate.fetchApiTokensAction, null);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
this.rxSubscriptions.push(loginSub);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public static styles = [
|
|
50
|
+
cssManager.defaultStyles,
|
|
51
|
+
viewHostCss,
|
|
52
|
+
css`
|
|
53
|
+
.apiTokensContainer {
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
gap: 24px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.scopePill {
|
|
60
|
+
display: inline-flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
padding: 2px 6px;
|
|
63
|
+
border-radius: 3px;
|
|
64
|
+
font-size: 11px;
|
|
65
|
+
background: ${cssManager.bdTheme('rgba(0, 130, 200, 0.1)', 'rgba(0, 170, 255, 0.1)')};
|
|
66
|
+
color: ${cssManager.bdTheme('#0369a1', '#0af')};
|
|
67
|
+
margin-right: 4px;
|
|
68
|
+
margin-bottom: 2px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.statusBadge {
|
|
72
|
+
display: inline-flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
padding: 3px 10px;
|
|
75
|
+
border-radius: 12px;
|
|
76
|
+
font-size: 12px;
|
|
77
|
+
font-weight: 600;
|
|
78
|
+
letter-spacing: 0.02em;
|
|
79
|
+
text-transform: uppercase;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.statusBadge.active {
|
|
83
|
+
background: ${cssManager.bdTheme('#dcfce7', '#14532d')};
|
|
84
|
+
color: ${cssManager.bdTheme('#166534', '#4ade80')};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.statusBadge.disabled {
|
|
88
|
+
background: ${cssManager.bdTheme('#fef2f2', '#450a0a')};
|
|
89
|
+
color: ${cssManager.bdTheme('#991b1b', '#f87171')};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.statusBadge.expired {
|
|
93
|
+
background: ${cssManager.bdTheme('#f3f4f6', '#374151')};
|
|
94
|
+
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
95
|
+
}
|
|
96
|
+
`,
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
public render(): TemplateResult {
|
|
100
|
+
const { apiTokens } = this.routeState;
|
|
101
|
+
|
|
102
|
+
return html`
|
|
103
|
+
<ops-sectionheading>API Tokens</ops-sectionheading>
|
|
104
|
+
|
|
105
|
+
<div class="apiTokensContainer">
|
|
106
|
+
<dees-table
|
|
107
|
+
.heading1=${'API Tokens'}
|
|
108
|
+
.heading2=${'Manage programmatic access tokens'}
|
|
109
|
+
.data=${apiTokens}
|
|
110
|
+
.dataName=${'token'}
|
|
111
|
+
.searchable=${true}
|
|
112
|
+
.displayFunction=${(token: interfaces.data.IApiTokenInfo) => ({
|
|
113
|
+
name: token.name,
|
|
114
|
+
scopes: this.renderScopePills(token.scopes),
|
|
115
|
+
status: this.renderStatusBadge(token),
|
|
116
|
+
created: new Date(token.createdAt).toLocaleDateString(),
|
|
117
|
+
expires: token.expiresAt ? new Date(token.expiresAt).toLocaleDateString() : 'Never',
|
|
118
|
+
lastUsed: token.lastUsedAt ? new Date(token.lastUsedAt).toLocaleDateString() : 'Never',
|
|
119
|
+
})}
|
|
120
|
+
.dataActions=${[
|
|
121
|
+
{
|
|
122
|
+
name: 'Create Token',
|
|
123
|
+
iconName: 'lucide:plus',
|
|
124
|
+
type: ['header'],
|
|
125
|
+
actionFunc: async () => {
|
|
126
|
+
await this.showCreateTokenDialog();
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'Enable',
|
|
131
|
+
iconName: 'lucide:play',
|
|
132
|
+
type: ['inRow', 'contextmenu'] as any,
|
|
133
|
+
actionRelevancyCheckFunc: (actionData: any) => !actionData.item.enabled,
|
|
134
|
+
actionFunc: async (actionData: any) => {
|
|
135
|
+
const token = actionData.item as interfaces.data.IApiTokenInfo;
|
|
136
|
+
await appstate.routeManagementStatePart.dispatchAction(
|
|
137
|
+
appstate.toggleApiTokenAction,
|
|
138
|
+
{ id: token.id, enabled: true },
|
|
139
|
+
);
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'Disable',
|
|
144
|
+
iconName: 'lucide:pause',
|
|
145
|
+
type: ['inRow', 'contextmenu'] as any,
|
|
146
|
+
actionRelevancyCheckFunc: (actionData: any) => actionData.item.enabled,
|
|
147
|
+
actionFunc: async (actionData: any) => {
|
|
148
|
+
const token = actionData.item as interfaces.data.IApiTokenInfo;
|
|
149
|
+
await appstate.routeManagementStatePart.dispatchAction(
|
|
150
|
+
appstate.toggleApiTokenAction,
|
|
151
|
+
{ id: token.id, enabled: false },
|
|
152
|
+
);
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'Revoke',
|
|
157
|
+
iconName: 'lucide:trash2',
|
|
158
|
+
type: ['inRow', 'contextmenu'] as any,
|
|
159
|
+
actionFunc: async (actionData: any) => {
|
|
160
|
+
const token = actionData.item as interfaces.data.IApiTokenInfo;
|
|
161
|
+
await appstate.routeManagementStatePart.dispatchAction(
|
|
162
|
+
appstate.revokeApiTokenAction,
|
|
163
|
+
token.id,
|
|
164
|
+
);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
]}
|
|
168
|
+
></dees-table>
|
|
169
|
+
</div>
|
|
170
|
+
`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private renderScopePills(scopes: TApiTokenScope[]): TemplateResult {
|
|
174
|
+
return html`<div style="display: flex; flex-wrap: wrap; gap: 2px;">${scopes.map(
|
|
175
|
+
(s) => html`<span class="scopePill">${s}</span>`,
|
|
176
|
+
)}</div>`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private renderStatusBadge(token: interfaces.data.IApiTokenInfo): TemplateResult {
|
|
180
|
+
if (!token.enabled) {
|
|
181
|
+
return html`<span class="statusBadge disabled">Disabled</span>`;
|
|
182
|
+
}
|
|
183
|
+
if (token.expiresAt && token.expiresAt < Date.now()) {
|
|
184
|
+
return html`<span class="statusBadge expired">Expired</span>`;
|
|
185
|
+
}
|
|
186
|
+
return html`<span class="statusBadge active">Active</span>`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private async showCreateTokenDialog() {
|
|
190
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
191
|
+
|
|
192
|
+
const allScopes: TApiTokenScope[] = [
|
|
193
|
+
'routes:read',
|
|
194
|
+
'routes:write',
|
|
195
|
+
'config:read',
|
|
196
|
+
'tokens:read',
|
|
197
|
+
'tokens:manage',
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
await DeesModal.createAndShow({
|
|
201
|
+
heading: 'Create API Token',
|
|
202
|
+
content: html`
|
|
203
|
+
<div style="color: #888; margin-bottom: 12px; font-size: 13px;">
|
|
204
|
+
The token value will be shown once after creation. Copy it immediately.
|
|
205
|
+
</div>
|
|
206
|
+
<dees-form>
|
|
207
|
+
<dees-input-text .key=${'name'} .label=${'Token Name'} .required=${true}></dees-input-text>
|
|
208
|
+
<dees-input-tags
|
|
209
|
+
.key=${'scopes'}
|
|
210
|
+
.label=${'Token Scopes'}
|
|
211
|
+
.value=${['routes:read', 'routes:write']}
|
|
212
|
+
.suggestions=${allScopes}
|
|
213
|
+
.required=${true}
|
|
214
|
+
></dees-input-tags>
|
|
215
|
+
<dees-input-text .key=${'expiresInDays'} .label=${'Expires in (days, blank = never)'}></dees-input-text>
|
|
216
|
+
</dees-form>
|
|
217
|
+
`,
|
|
218
|
+
menuOptions: [
|
|
219
|
+
{
|
|
220
|
+
name: 'Cancel',
|
|
221
|
+
iconName: 'lucide:x',
|
|
222
|
+
action: async (modalArg: any) => await modalArg.destroy(),
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: 'Create',
|
|
226
|
+
iconName: 'lucide:key',
|
|
227
|
+
action: async (modalArg: any) => {
|
|
228
|
+
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
|
|
229
|
+
if (!form) return;
|
|
230
|
+
const formData = await form.collectFormData();
|
|
231
|
+
if (!formData.name) return;
|
|
232
|
+
|
|
233
|
+
// dees-input-tags returns string[] directly
|
|
234
|
+
const scopes = (formData.scopes || [])
|
|
235
|
+
.filter((s: string) => allScopes.includes(s as any)) as TApiTokenScope[];
|
|
236
|
+
|
|
237
|
+
const expiresInDays = formData.expiresInDays
|
|
238
|
+
? parseInt(formData.expiresInDays, 10)
|
|
239
|
+
: null;
|
|
240
|
+
|
|
241
|
+
await modalArg.destroy();
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const response = await appstate.createApiToken(formData.name, scopes, expiresInDays);
|
|
245
|
+
if (response.success && response.tokenValue) {
|
|
246
|
+
// Refresh the list first so it's ready when user dismisses the modal
|
|
247
|
+
await appstate.routeManagementStatePart.dispatchAction(appstate.fetchApiTokensAction, null);
|
|
248
|
+
|
|
249
|
+
// Show the token value in a new modal
|
|
250
|
+
await DeesModal.createAndShow({
|
|
251
|
+
heading: 'Token Created',
|
|
252
|
+
content: html`
|
|
253
|
+
<div style="color: #ccc; padding: 8px 0;">
|
|
254
|
+
<p>Copy this token now. It will not be shown again.</p>
|
|
255
|
+
<div style="background: #111; padding: 12px; border-radius: 6px; margin-top: 8px;">
|
|
256
|
+
<code style="color: #0f8; word-break: break-all; font-size: 13px;">${response.tokenValue}</code>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
`,
|
|
260
|
+
menuOptions: [
|
|
261
|
+
{
|
|
262
|
+
name: 'Done',
|
|
263
|
+
iconName: 'lucide:check',
|
|
264
|
+
action: async (m: any) => await m.destroy(),
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.error('Failed to create token:', error);
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async firstUpdated() {
|
|
279
|
+
await appstate.routeManagementStatePart.dispatchAction(appstate.fetchApiTokensAction, null);
|
|
280
|
+
}
|
|
281
|
+
}
|