@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
package/dist_ts_web/plugins.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// @design.estate scope
|
|
2
2
|
import * as deesElement from '@design.estate/dees-element';
|
|
3
3
|
import * as deesCatalog from '@design.estate/dees-catalog';
|
|
4
|
+
// @serve.zone scope
|
|
5
|
+
import * as szCatalog from '@serve.zone/catalog';
|
|
4
6
|
// TypedSocket for real-time push communication
|
|
5
7
|
import * as typedsocket from '@api.global/typedsocket';
|
|
6
|
-
export { deesElement, deesCatalog, typedsocket, };
|
|
8
|
+
export { deesElement, deesCatalog, szCatalog, typedsocket, };
|
|
7
9
|
// domtools gives us TypedRequest and other utilities
|
|
8
10
|
export const domtools = deesElement.domtools;
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX3dlYi9wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHVCQUF1QjtBQUN2QixPQUFPLEtBQUssV0FBVyxNQUFNLDZCQUE2QixDQUFDO0FBQzNELE9BQU8sS0FBSyxXQUFXLE1BQU0sNkJBQTZCLENBQUM7QUFFM0Qsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxTQUFTLE1BQU0scUJBQXFCLENBQUM7QUFFakQsK0NBQStDO0FBQy9DLE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFFdkQsT0FBTyxFQUNMLFdBQVcsRUFDWCxXQUFXLEVBQ1gsU0FBUyxFQUNULFdBQVcsR0FDWixDQUFBO0FBRUQscURBQXFEO0FBQ3JELE1BQU0sQ0FBQyxNQUFNLFFBQVEsR0FBRyxXQUFXLENBQUMsUUFBUSxDQUFDIn0=
|
package/dist_ts_web/router.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
export declare const validViews: readonly ["overview", "network", "emails", "logs", "configuration", "security", "certificates", "remoteingress"];
|
|
2
|
-
export declare const validEmailFolders: readonly ["queued", "sent", "failed", "security"];
|
|
1
|
+
export declare const validViews: readonly ["overview", "network", "emails", "logs", "routes", "apitokens", "configuration", "security", "certificates", "remoteingress"];
|
|
3
2
|
export type TValidView = typeof validViews[number];
|
|
4
|
-
export type TValidEmailFolder = typeof validEmailFolders[number];
|
|
5
3
|
declare class AppRouter {
|
|
6
4
|
private router;
|
|
7
5
|
private initialized;
|
|
@@ -10,15 +8,11 @@ declare class AppRouter {
|
|
|
10
8
|
init(): void;
|
|
11
9
|
private setupRoutes;
|
|
12
10
|
private setupStateSync;
|
|
13
|
-
private getExpectedPath;
|
|
14
11
|
private handleInitialRoute;
|
|
15
12
|
private updateViewState;
|
|
16
|
-
private updateEmailFolder;
|
|
17
13
|
navigateTo(path: string): void;
|
|
18
14
|
navigateToView(view: string): void;
|
|
19
|
-
navigateToEmailFolder(folder: string): void;
|
|
20
15
|
getCurrentView(): string;
|
|
21
|
-
getCurrentEmailFolder(): string;
|
|
22
16
|
destroy(): void;
|
|
23
17
|
}
|
|
24
18
|
export declare const appRouter: AppRouter;
|
package/dist_ts_web/router.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import * as appstate from './appstate.js';
|
|
3
3
|
const SmartRouter = plugins.domtools.plugins.smartrouter.SmartRouter;
|
|
4
|
-
export const validViews = ['overview', 'network', 'emails', 'logs', 'configuration', 'security', 'certificates', 'remoteingress'];
|
|
5
|
-
export const validEmailFolders = ['queued', 'sent', 'failed', 'security'];
|
|
4
|
+
export const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress'];
|
|
6
5
|
class AppRouter {
|
|
7
6
|
router;
|
|
8
7
|
initialized = false;
|
|
@@ -19,32 +18,10 @@ class AppRouter {
|
|
|
19
18
|
this.initialized = true;
|
|
20
19
|
}
|
|
21
20
|
setupRoutes() {
|
|
22
|
-
// Main views
|
|
23
21
|
for (const view of validViews) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.updateViewState('emails');
|
|
28
|
-
this.updateEmailFolder('queued');
|
|
29
|
-
});
|
|
30
|
-
// Email with folder parameter
|
|
31
|
-
this.router.on('/emails/:folder', async (routeInfo) => {
|
|
32
|
-
const folder = routeInfo.params.folder;
|
|
33
|
-
if (validEmailFolders.includes(folder)) {
|
|
34
|
-
this.updateViewState('emails');
|
|
35
|
-
this.updateEmailFolder(folder);
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
// Invalid folder, redirect to queued
|
|
39
|
-
this.navigateTo('/emails/queued');
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
this.router.on(`/${view}`, async () => {
|
|
45
|
-
this.updateViewState(view);
|
|
46
|
-
});
|
|
47
|
-
}
|
|
22
|
+
this.router.on(`/${view}`, async () => {
|
|
23
|
+
this.updateViewState(view);
|
|
24
|
+
});
|
|
48
25
|
}
|
|
49
26
|
// Root redirect
|
|
50
27
|
this.router.on('/', async () => {
|
|
@@ -52,59 +29,30 @@ class AppRouter {
|
|
|
52
29
|
});
|
|
53
30
|
}
|
|
54
31
|
setupStateSync() {
|
|
55
|
-
// Sync URL when state changes programmatically (not from router)
|
|
56
32
|
appstate.uiStatePart.state.subscribe((uiState) => {
|
|
57
33
|
if (this.suppressStateUpdate)
|
|
58
34
|
return;
|
|
59
35
|
const currentPath = window.location.pathname;
|
|
60
|
-
const expectedPath =
|
|
61
|
-
|
|
62
|
-
if (!currentPath.startsWith(expectedPath)) {
|
|
36
|
+
const expectedPath = `/${uiState.activeView}`;
|
|
37
|
+
if (currentPath !== expectedPath) {
|
|
63
38
|
this.suppressStateUpdate = true;
|
|
64
|
-
|
|
65
|
-
const emailState = appstate.emailOpsStatePart.getState();
|
|
66
|
-
this.router.pushUrl(`/emails/${emailState.currentView}`);
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
this.router.pushUrl(`/${uiState.activeView}`);
|
|
70
|
-
}
|
|
39
|
+
this.router.pushUrl(expectedPath);
|
|
71
40
|
this.suppressStateUpdate = false;
|
|
72
41
|
}
|
|
73
42
|
});
|
|
74
43
|
}
|
|
75
|
-
getExpectedPath(view) {
|
|
76
|
-
if (view === 'emails') {
|
|
77
|
-
return '/emails';
|
|
78
|
-
}
|
|
79
|
-
return `/${view}`;
|
|
80
|
-
}
|
|
81
44
|
handleInitialRoute() {
|
|
82
45
|
const path = window.location.pathname;
|
|
83
46
|
if (!path || path === '/') {
|
|
84
|
-
// Redirect root to overview
|
|
85
47
|
this.router.pushUrl('/overview');
|
|
86
48
|
}
|
|
87
49
|
else {
|
|
88
|
-
// Parse current path and update state
|
|
89
50
|
const segments = path.split('/').filter(Boolean);
|
|
90
51
|
const view = segments[0];
|
|
91
52
|
if (validViews.includes(view)) {
|
|
92
53
|
this.updateViewState(view);
|
|
93
|
-
if (view === 'emails' && segments[1]) {
|
|
94
|
-
const folder = segments[1];
|
|
95
|
-
if (validEmailFolders.includes(folder)) {
|
|
96
|
-
this.updateEmailFolder(folder);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
this.updateEmailFolder('queued');
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
else if (view === 'emails') {
|
|
103
|
-
this.updateEmailFolder('queued');
|
|
104
|
-
}
|
|
105
54
|
}
|
|
106
55
|
else {
|
|
107
|
-
// Invalid view, redirect to overview
|
|
108
56
|
this.router.pushUrl('/overview');
|
|
109
57
|
}
|
|
110
58
|
}
|
|
@@ -120,17 +68,6 @@ class AppRouter {
|
|
|
120
68
|
}
|
|
121
69
|
this.suppressStateUpdate = false;
|
|
122
70
|
}
|
|
123
|
-
updateEmailFolder(folder) {
|
|
124
|
-
this.suppressStateUpdate = true;
|
|
125
|
-
const currentState = appstate.emailOpsStatePart.getState();
|
|
126
|
-
if (currentState.currentView !== folder) {
|
|
127
|
-
appstate.emailOpsStatePart.setState({
|
|
128
|
-
...currentState,
|
|
129
|
-
currentView: folder,
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
this.suppressStateUpdate = false;
|
|
133
|
-
}
|
|
134
71
|
navigateTo(path) {
|
|
135
72
|
this.router.pushUrl(path);
|
|
136
73
|
}
|
|
@@ -142,24 +79,13 @@ class AppRouter {
|
|
|
142
79
|
this.navigateTo('/overview');
|
|
143
80
|
}
|
|
144
81
|
}
|
|
145
|
-
navigateToEmailFolder(folder) {
|
|
146
|
-
if (validEmailFolders.includes(folder)) {
|
|
147
|
-
this.navigateTo(`/emails/${folder}`);
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
this.navigateTo('/emails/queued');
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
82
|
getCurrentView() {
|
|
154
83
|
return appstate.uiStatePart.getState().activeView;
|
|
155
84
|
}
|
|
156
|
-
getCurrentEmailFolder() {
|
|
157
|
-
return appstate.emailOpsStatePart.getState().currentView;
|
|
158
|
-
}
|
|
159
85
|
destroy() {
|
|
160
86
|
this.router.destroy();
|
|
161
87
|
this.initialized = false;
|
|
162
88
|
}
|
|
163
89
|
}
|
|
164
90
|
export const appRouter = new AppRouter();
|
|
165
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
91
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfd2ViL3JvdXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssUUFBUSxNQUFNLGVBQWUsQ0FBQztBQUUxQyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDO0FBRXJFLE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRyxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLGVBQWUsRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFLGVBQWUsQ0FBVSxDQUFDO0FBSWxLLE1BQU0sU0FBUztJQUNMLE1BQU0sQ0FBbUM7SUFDekMsV0FBVyxHQUFHLEtBQUssQ0FBQztJQUNwQixtQkFBbUIsR0FBRyxLQUFLLENBQUM7SUFFcEM7UUFDRSxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksV0FBVyxDQUFDLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUVNLElBQUk7UUFDVCxJQUFJLElBQUksQ0FBQyxXQUFXO1lBQUUsT0FBTztRQUM3QixJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDbkIsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQzFCLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO0lBQzFCLENBQUM7SUFFTyxXQUFXO1FBQ2pCLEtBQUssTUFBTSxJQUFJLElBQUksVUFBVSxFQUFFLENBQUM7WUFDOUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsSUFBSSxJQUFJLEVBQUUsRUFBRSxLQUFLLElBQUksRUFBRTtnQkFDcEMsSUFBSSxDQUFDLGVBQWUsQ0FB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
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/dcrouter",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "8.1.0",
|
|
5
5
|
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"@push.rocks/smartrx": "^3.0.10",
|
|
56
56
|
"@push.rocks/smartstate": "^2.0.30",
|
|
57
57
|
"@push.rocks/smartunique": "^3.0.9",
|
|
58
|
+
"@serve.zone/catalog": "^2.3.0",
|
|
58
59
|
"@serve.zone/interfaces": "^5.3.0",
|
|
59
60
|
"@serve.zone/remoteingress": "^4.0.0",
|
|
60
61
|
"@tsclass/tsclass": "^9.3.0",
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/classes.dcrouter.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { OpsServer } from './opsserver/index.js';
|
|
|
22
22
|
import { MetricsManager } from './monitoring/index.js';
|
|
23
23
|
import { RadiusServer, type IRadiusServerConfig } from './radius/index.js';
|
|
24
24
|
import { RemoteIngressManager, TunnelManager } from './remoteingress/index.js';
|
|
25
|
+
import { RouteConfigManager, ApiTokenManager } from './config/index.js';
|
|
25
26
|
|
|
26
27
|
export interface IDcRouterOptions {
|
|
27
28
|
/** Base directory for all dcrouter data. Defaults to ~/.serve.zone/dcrouter */
|
|
@@ -212,6 +213,10 @@ export class DcRouter {
|
|
|
212
213
|
public remoteIngressManager?: RemoteIngressManager;
|
|
213
214
|
public tunnelManager?: TunnelManager;
|
|
214
215
|
|
|
216
|
+
// Programmatic config API
|
|
217
|
+
public routeConfigManager?: RouteConfigManager;
|
|
218
|
+
public apiTokenManager?: ApiTokenManager;
|
|
219
|
+
|
|
215
220
|
// DNS query logging rate limiter state
|
|
216
221
|
private dnsLogWindow: number[] = [];
|
|
217
222
|
private dnsBatchCount: number = 0;
|
|
@@ -233,6 +238,9 @@ export class DcRouter {
|
|
|
233
238
|
// TypedRouter for API endpoints
|
|
234
239
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
235
240
|
|
|
241
|
+
// Cached constructor routes (computed once during setupSmartProxy, used by RouteConfigManager)
|
|
242
|
+
private constructorRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
|
243
|
+
|
|
236
244
|
// Environment access
|
|
237
245
|
private qenv = new plugins.qenv.Qenv('./', '.nogit/');
|
|
238
246
|
|
|
@@ -275,7 +283,17 @@ export class DcRouter {
|
|
|
275
283
|
|
|
276
284
|
// Set up SmartProxy for HTTP/HTTPS and all traffic including email routes
|
|
277
285
|
await this.setupSmartProxy();
|
|
278
|
-
|
|
286
|
+
|
|
287
|
+
// Initialize programmatic config API managers
|
|
288
|
+
this.routeConfigManager = new RouteConfigManager(
|
|
289
|
+
this.storageManager,
|
|
290
|
+
() => this.getConstructorRoutes(),
|
|
291
|
+
() => this.smartProxy,
|
|
292
|
+
);
|
|
293
|
+
this.apiTokenManager = new ApiTokenManager(this.storageManager);
|
|
294
|
+
await this.apiTokenManager.initialize();
|
|
295
|
+
await this.routeConfigManager.initialize();
|
|
296
|
+
|
|
279
297
|
// Set up unified email handling if configured
|
|
280
298
|
if (this.options.emailConfig) {
|
|
281
299
|
await this.setupUnifiedEmailHandling();
|
|
@@ -443,6 +461,9 @@ export class DcRouter {
|
|
|
443
461
|
challengeHandlers.push(dns01Handler);
|
|
444
462
|
}
|
|
445
463
|
|
|
464
|
+
// Cache constructor routes for RouteConfigManager
|
|
465
|
+
this.constructorRoutes = [...routes];
|
|
466
|
+
|
|
446
467
|
// If we have routes or need a basic SmartProxy instance, create it
|
|
447
468
|
if (routes.length > 0 || this.options.smartProxyConfig) {
|
|
448
469
|
logger.log('info', 'Setting up SmartProxy with combined configuration');
|
|
@@ -857,6 +878,14 @@ export class DcRouter {
|
|
|
857
878
|
return names;
|
|
858
879
|
}
|
|
859
880
|
|
|
881
|
+
/**
|
|
882
|
+
* Get the routes derived from constructor config (smartProxy + email + DNS).
|
|
883
|
+
* Used by RouteConfigManager as the "hardcoded" base.
|
|
884
|
+
*/
|
|
885
|
+
public getConstructorRoutes(): plugins.smartproxy.IRouteConfig[] {
|
|
886
|
+
return this.constructorRoutes;
|
|
887
|
+
}
|
|
888
|
+
|
|
860
889
|
public async stop() {
|
|
861
890
|
logger.log('info', 'Stopping DcRouter services...');
|
|
862
891
|
|
|
@@ -929,6 +958,8 @@ export class DcRouter {
|
|
|
929
958
|
this.smartAcme = undefined;
|
|
930
959
|
this.certProvisionScheduler = undefined;
|
|
931
960
|
this.remoteIngressManager = undefined;
|
|
961
|
+
this.routeConfigManager = undefined;
|
|
962
|
+
this.apiTokenManager = undefined;
|
|
932
963
|
this.certificateStatusMap.clear();
|
|
933
964
|
|
|
934
965
|
logger.log('info', 'All DcRouter services stopped');
|
|
@@ -960,6 +991,11 @@ export class DcRouter {
|
|
|
960
991
|
// Start new SmartProxy with updated configuration (will include email routes if configured)
|
|
961
992
|
await this.setupSmartProxy();
|
|
962
993
|
|
|
994
|
+
// Re-apply programmatic routes and overrides after SmartProxy restart
|
|
995
|
+
if (this.routeConfigManager) {
|
|
996
|
+
await this.routeConfigManager.initialize();
|
|
997
|
+
}
|
|
998
|
+
|
|
963
999
|
logger.log('info', 'SmartProxy configuration updated');
|
|
964
1000
|
}
|
|
965
1001
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { logger } from '../logger.js';
|
|
3
|
+
import type { StorageManager } from '../storage/index.js';
|
|
4
|
+
import type {
|
|
5
|
+
IStoredApiToken,
|
|
6
|
+
IApiTokenInfo,
|
|
7
|
+
TApiTokenScope,
|
|
8
|
+
} from '../../ts_interfaces/data/route-management.js';
|
|
9
|
+
|
|
10
|
+
const TOKENS_PREFIX = '/config-api/tokens/';
|
|
11
|
+
const TOKEN_PREFIX_STR = 'dcr_';
|
|
12
|
+
|
|
13
|
+
export class ApiTokenManager {
|
|
14
|
+
private tokens = new Map<string, IStoredApiToken>();
|
|
15
|
+
|
|
16
|
+
constructor(private storageManager: StorageManager) {}
|
|
17
|
+
|
|
18
|
+
public async initialize(): Promise<void> {
|
|
19
|
+
await this.loadTokens();
|
|
20
|
+
if (this.tokens.size > 0) {
|
|
21
|
+
logger.log('info', `Loaded ${this.tokens.size} API token(s) from storage`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// =========================================================================
|
|
26
|
+
// Token lifecycle
|
|
27
|
+
// =========================================================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a new API token. Returns the raw token value (shown once).
|
|
31
|
+
*/
|
|
32
|
+
public async createToken(
|
|
33
|
+
name: string,
|
|
34
|
+
scopes: TApiTokenScope[],
|
|
35
|
+
expiresInDays: number | null,
|
|
36
|
+
createdBy: string,
|
|
37
|
+
): Promise<{ id: string; rawToken: string }> {
|
|
38
|
+
const id = plugins.uuid.v4();
|
|
39
|
+
const randomBytes = plugins.crypto.randomBytes(32);
|
|
40
|
+
const rawPayload = `${id}:${randomBytes.toString('base64url')}`;
|
|
41
|
+
const rawToken = `${TOKEN_PREFIX_STR}${rawPayload}`;
|
|
42
|
+
|
|
43
|
+
const tokenHash = plugins.crypto.createHash('sha256').update(rawToken).digest('hex');
|
|
44
|
+
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const stored: IStoredApiToken = {
|
|
47
|
+
id,
|
|
48
|
+
name,
|
|
49
|
+
tokenHash,
|
|
50
|
+
scopes,
|
|
51
|
+
createdAt: now,
|
|
52
|
+
expiresAt: expiresInDays != null ? now + expiresInDays * 86400000 : null,
|
|
53
|
+
lastUsedAt: null,
|
|
54
|
+
createdBy,
|
|
55
|
+
enabled: true,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
this.tokens.set(id, stored);
|
|
59
|
+
await this.persistToken(stored);
|
|
60
|
+
logger.log('info', `API token '${name}' created (id: ${id})`);
|
|
61
|
+
return { id, rawToken };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Validate a raw token string. Returns the stored token if valid, null otherwise.
|
|
66
|
+
* Also updates lastUsedAt.
|
|
67
|
+
*/
|
|
68
|
+
public async validateToken(rawToken: string): Promise<IStoredApiToken | null> {
|
|
69
|
+
if (!rawToken.startsWith(TOKEN_PREFIX_STR)) return null;
|
|
70
|
+
|
|
71
|
+
const hash = plugins.crypto.createHash('sha256').update(rawToken).digest('hex');
|
|
72
|
+
|
|
73
|
+
for (const stored of this.tokens.values()) {
|
|
74
|
+
if (stored.tokenHash === hash) {
|
|
75
|
+
if (!stored.enabled) return null;
|
|
76
|
+
if (stored.expiresAt !== null && stored.expiresAt < Date.now()) return null;
|
|
77
|
+
|
|
78
|
+
// Update lastUsedAt (fire and forget)
|
|
79
|
+
stored.lastUsedAt = Date.now();
|
|
80
|
+
this.persistToken(stored).catch(() => {});
|
|
81
|
+
return stored;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check if a token has a specific scope.
|
|
89
|
+
*/
|
|
90
|
+
public hasScope(token: IStoredApiToken, scope: TApiTokenScope): boolean {
|
|
91
|
+
return token.scopes.includes(scope);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* List all tokens (safe info only, no hashes).
|
|
96
|
+
*/
|
|
97
|
+
public listTokens(): IApiTokenInfo[] {
|
|
98
|
+
const result: IApiTokenInfo[] = [];
|
|
99
|
+
for (const stored of this.tokens.values()) {
|
|
100
|
+
result.push({
|
|
101
|
+
id: stored.id,
|
|
102
|
+
name: stored.name,
|
|
103
|
+
scopes: stored.scopes,
|
|
104
|
+
createdAt: stored.createdAt,
|
|
105
|
+
expiresAt: stored.expiresAt,
|
|
106
|
+
lastUsedAt: stored.lastUsedAt,
|
|
107
|
+
enabled: stored.enabled,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Revoke (delete) a token.
|
|
115
|
+
*/
|
|
116
|
+
public async revokeToken(id: string): Promise<boolean> {
|
|
117
|
+
if (!this.tokens.has(id)) return false;
|
|
118
|
+
const token = this.tokens.get(id)!;
|
|
119
|
+
this.tokens.delete(id);
|
|
120
|
+
await this.storageManager.delete(`${TOKENS_PREFIX}${id}.json`);
|
|
121
|
+
logger.log('info', `API token '${token.name}' revoked (id: ${id})`);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Enable or disable a token.
|
|
127
|
+
*/
|
|
128
|
+
public async toggleToken(id: string, enabled: boolean): Promise<boolean> {
|
|
129
|
+
const stored = this.tokens.get(id);
|
|
130
|
+
if (!stored) return false;
|
|
131
|
+
stored.enabled = enabled;
|
|
132
|
+
await this.persistToken(stored);
|
|
133
|
+
logger.log('info', `API token '${stored.name}' ${enabled ? 'enabled' : 'disabled'} (id: ${id})`);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// =========================================================================
|
|
138
|
+
// Private
|
|
139
|
+
// =========================================================================
|
|
140
|
+
|
|
141
|
+
private async loadTokens(): Promise<void> {
|
|
142
|
+
const keys = await this.storageManager.list(TOKENS_PREFIX);
|
|
143
|
+
for (const key of keys) {
|
|
144
|
+
if (!key.endsWith('.json')) continue;
|
|
145
|
+
const stored = await this.storageManager.getJSON<IStoredApiToken>(key);
|
|
146
|
+
if (stored?.id) {
|
|
147
|
+
this.tokens.set(stored.id, stored);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private async persistToken(stored: IStoredApiToken): Promise<void> {
|
|
153
|
+
await this.storageManager.setJSON(`${TOKENS_PREFIX}${stored.id}.json`, stored);
|
|
154
|
+
}
|
|
155
|
+
}
|