@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,180 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
3
|
-
export class AdminHandler {
|
|
4
|
-
opsServerRef;
|
|
5
|
-
typedrouter = new plugins.typedrequest.TypedRouter();
|
|
6
|
-
// JWT instance
|
|
7
|
-
smartjwtInstance;
|
|
8
|
-
// Simple in-memory user storage (in production, use proper database)
|
|
9
|
-
users = new Map();
|
|
10
|
-
constructor(opsServerRef) {
|
|
11
|
-
this.opsServerRef = opsServerRef;
|
|
12
|
-
// Add this handler's router to the parent
|
|
13
|
-
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
|
|
14
|
-
}
|
|
15
|
-
async initialize() {
|
|
16
|
-
await this.initializeJwt();
|
|
17
|
-
this.initializeDefaultUsers();
|
|
18
|
-
this.registerHandlers();
|
|
19
|
-
}
|
|
20
|
-
async initializeJwt() {
|
|
21
|
-
this.smartjwtInstance = new plugins.smartjwt.SmartJwt();
|
|
22
|
-
await this.smartjwtInstance.init();
|
|
23
|
-
// For development, create new keypair each time
|
|
24
|
-
// In production, load from storage like cloudly does
|
|
25
|
-
await this.smartjwtInstance.createNewKeyPair();
|
|
26
|
-
}
|
|
27
|
-
initializeDefaultUsers() {
|
|
28
|
-
// Add default admin user
|
|
29
|
-
const adminId = plugins.uuid.v4();
|
|
30
|
-
this.users.set(adminId, {
|
|
31
|
-
id: adminId,
|
|
32
|
-
username: 'admin',
|
|
33
|
-
password: 'admin',
|
|
34
|
-
role: 'admin',
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
registerHandlers() {
|
|
38
|
-
// Admin Login Handler
|
|
39
|
-
this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('adminLoginWithUsernameAndPassword', async (dataArg) => {
|
|
40
|
-
try {
|
|
41
|
-
// Find user by username and password
|
|
42
|
-
let user = null;
|
|
43
|
-
for (const [_, userData] of this.users) {
|
|
44
|
-
if (userData.username === dataArg.username && userData.password === dataArg.password) {
|
|
45
|
-
user = userData;
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (!user) {
|
|
50
|
-
throw new plugins.typedrequest.TypedResponseError('login failed');
|
|
51
|
-
}
|
|
52
|
-
const expiresAtTimestamp = Date.now() + 3600 * 1000 * 24; // 24 hours
|
|
53
|
-
const jwt = await this.smartjwtInstance.createJWT({
|
|
54
|
-
userId: user.id,
|
|
55
|
-
status: 'loggedIn',
|
|
56
|
-
expiresAt: expiresAtTimestamp,
|
|
57
|
-
});
|
|
58
|
-
return {
|
|
59
|
-
identity: {
|
|
60
|
-
jwt,
|
|
61
|
-
userId: user.id,
|
|
62
|
-
name: user.username,
|
|
63
|
-
expiresAt: expiresAtTimestamp,
|
|
64
|
-
role: user.role,
|
|
65
|
-
type: 'user',
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
catch (error) {
|
|
70
|
-
if (error instanceof plugins.typedrequest.TypedResponseError) {
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
73
|
-
throw new plugins.typedrequest.TypedResponseError('login failed');
|
|
74
|
-
}
|
|
75
|
-
}));
|
|
76
|
-
// Admin Logout Handler
|
|
77
|
-
this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('adminLogout', async (dataArg) => {
|
|
78
|
-
// In a real implementation, you might want to blacklist the JWT
|
|
79
|
-
// For now, just return success
|
|
80
|
-
return {
|
|
81
|
-
success: true,
|
|
82
|
-
};
|
|
83
|
-
}));
|
|
84
|
-
// Verify Identity Handler
|
|
85
|
-
this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('verifyIdentity', async (dataArg) => {
|
|
86
|
-
if (!dataArg.identity?.jwt) {
|
|
87
|
-
return {
|
|
88
|
-
valid: false,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
try {
|
|
92
|
-
const jwtData = await this.smartjwtInstance.verifyJWTAndGetData(dataArg.identity.jwt);
|
|
93
|
-
// Check if expired
|
|
94
|
-
if (jwtData.expiresAt < Date.now()) {
|
|
95
|
-
return {
|
|
96
|
-
valid: false,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
// Check if logged in
|
|
100
|
-
if (jwtData.status !== 'loggedIn') {
|
|
101
|
-
return {
|
|
102
|
-
valid: false,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
// Find user
|
|
106
|
-
const user = this.users.get(jwtData.userId);
|
|
107
|
-
if (!user) {
|
|
108
|
-
return {
|
|
109
|
-
valid: false,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
valid: true,
|
|
114
|
-
identity: {
|
|
115
|
-
jwt: dataArg.identity.jwt,
|
|
116
|
-
userId: user.id,
|
|
117
|
-
name: user.username,
|
|
118
|
-
expiresAt: jwtData.expiresAt,
|
|
119
|
-
role: user.role,
|
|
120
|
-
type: 'user',
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
125
|
-
return {
|
|
126
|
-
valid: false,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
}));
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Create a guard for valid identity (matching cloudly pattern)
|
|
133
|
-
*/
|
|
134
|
-
validIdentityGuard = new plugins.smartguard.Guard(async (dataArg) => {
|
|
135
|
-
if (!dataArg.identity?.jwt) {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
try {
|
|
139
|
-
const jwtData = await this.smartjwtInstance.verifyJWTAndGetData(dataArg.identity.jwt);
|
|
140
|
-
// Check expiration
|
|
141
|
-
if (jwtData.expiresAt < Date.now()) {
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
// Check status
|
|
145
|
-
if (jwtData.status !== 'loggedIn') {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
// Verify data hasn't been tampered with
|
|
149
|
-
if (dataArg.identity.expiresAt !== jwtData.expiresAt) {
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
if (dataArg.identity.userId !== jwtData.userId) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
catch (error) {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
}, {
|
|
161
|
-
failedHint: 'identity is not valid',
|
|
162
|
-
name: 'validIdentityGuard',
|
|
163
|
-
});
|
|
164
|
-
/**
|
|
165
|
-
* Create a guard for admin identity (matching cloudly pattern)
|
|
166
|
-
*/
|
|
167
|
-
adminIdentityGuard = new plugins.smartguard.Guard(async (dataArg) => {
|
|
168
|
-
// First check if identity is valid
|
|
169
|
-
const isValid = await this.validIdentityGuard.exec(dataArg);
|
|
170
|
-
if (!isValid) {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
// Check if user has admin role
|
|
174
|
-
return dataArg.identity.role === 'admin';
|
|
175
|
-
}, {
|
|
176
|
-
failedHint: 'user is not admin',
|
|
177
|
-
name: 'adminIdentityGuard',
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRtaW4uaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL29wc3NlcnZlci9oYW5kbGVycy9hZG1pbi5oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFFNUMsT0FBTyxLQUFLLFVBQVUsTUFBTSxpQ0FBaUMsQ0FBQztBQVE5RCxNQUFNLE9BQU8sWUFBWTtJQWNIO0lBYmIsV0FBVyxHQUFHLElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUU1RCxlQUFlO0lBQ1IsZ0JBQWdCLENBQXNDO0lBRTdELHFFQUFxRTtJQUM3RCxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBS25CLENBQUM7SUFFTCxZQUFvQixZQUF1QjtRQUF2QixpQkFBWSxHQUFaLFlBQVksQ0FBVztRQUN6QywwQ0FBMEM7UUFDMUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUNqRSxDQUFDO0lBRU0sS0FBSyxDQUFDLFVBQVU7UUFDckIsTUFBTSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDM0IsSUFBSSxDQUFDLHNCQUFzQixFQUFFLENBQUM7UUFDOUIsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7SUFDMUIsQ0FBQztJQUVPLEtBQUssQ0FBQyxhQUFhO1FBQ3pCLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDeEQsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLENBQUM7UUFFbkMsZ0RBQWdEO1FBQ2hELHFEQUFxRDtRQUNyRCxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO0lBQ2pELENBQUM7SUFFTyxzQkFBc0I7UUFDNUIseUJBQXlCO1FB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
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
3
|
-
export class ConfigHandler {
|
|
4
|
-
opsServerRef;
|
|
5
|
-
typedrouter = new plugins.typedrequest.TypedRouter();
|
|
6
|
-
constructor(opsServerRef) {
|
|
7
|
-
this.opsServerRef = opsServerRef;
|
|
8
|
-
// Add this handler's router to the parent
|
|
9
|
-
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
|
|
10
|
-
this.registerHandlers();
|
|
11
|
-
}
|
|
12
|
-
registerHandlers() {
|
|
13
|
-
// Get Configuration Handler (read-only)
|
|
14
|
-
this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('getConfiguration', async (dataArg, toolsArg) => {
|
|
15
|
-
const config = await this.getConfiguration(dataArg.section);
|
|
16
|
-
return {
|
|
17
|
-
config,
|
|
18
|
-
section: dataArg.section,
|
|
19
|
-
};
|
|
20
|
-
}));
|
|
21
|
-
}
|
|
22
|
-
async getConfiguration(section) {
|
|
23
|
-
const dcRouter = this.opsServerRef.dcRouterRef;
|
|
24
|
-
// Get email domains if email server is configured
|
|
25
|
-
let emailDomains = [];
|
|
26
|
-
if (dcRouter.emailServer && dcRouter.emailServer.domainRegistry) {
|
|
27
|
-
emailDomains = dcRouter.emailServer.domainRegistry.getAllDomains();
|
|
28
|
-
}
|
|
29
|
-
else if (dcRouter.options.emailConfig?.domains) {
|
|
30
|
-
// Fallback: get domains from email config options
|
|
31
|
-
emailDomains = dcRouter.options.emailConfig.domains.map(d => typeof d === 'string' ? d : d.domain);
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
email: {
|
|
35
|
-
enabled: !!dcRouter.emailServer,
|
|
36
|
-
ports: dcRouter.emailServer ? [25, 465, 587, 2525] : [],
|
|
37
|
-
maxMessageSize: 10 * 1024 * 1024, // 10MB default
|
|
38
|
-
rateLimits: {
|
|
39
|
-
perMinute: 10,
|
|
40
|
-
perHour: 100,
|
|
41
|
-
perDay: 1000,
|
|
42
|
-
},
|
|
43
|
-
domains: emailDomains,
|
|
44
|
-
},
|
|
45
|
-
dns: {
|
|
46
|
-
enabled: !!dcRouter.dnsServer,
|
|
47
|
-
port: 53,
|
|
48
|
-
nameservers: dcRouter.options.dnsNsDomains || [],
|
|
49
|
-
caching: true,
|
|
50
|
-
ttl: 300,
|
|
51
|
-
},
|
|
52
|
-
proxy: {
|
|
53
|
-
enabled: !!dcRouter.smartProxy,
|
|
54
|
-
httpPort: 80,
|
|
55
|
-
httpsPort: 443,
|
|
56
|
-
maxConnections: 1000,
|
|
57
|
-
},
|
|
58
|
-
security: {
|
|
59
|
-
blockList: [],
|
|
60
|
-
rateLimit: true,
|
|
61
|
-
spamDetection: true,
|
|
62
|
-
tlsRequired: false,
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmhhbmRsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9vcHNzZXJ2ZXIvaGFuZGxlcnMvY29uZmlnLmhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUU1QyxPQUFPLEtBQUssVUFBVSxNQUFNLGlDQUFpQyxDQUFDO0FBRTlELE1BQU0sT0FBTyxhQUFhO0lBR0o7SUFGYixXQUFXLEdBQUcsSUFBSSxPQUFPLENBQUMsWUFBWSxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBRTVELFlBQW9CLFlBQXVCO1FBQXZCLGlCQUFZLEdBQVosWUFBWSxDQUFXO1FBQ3pDLDBDQUEwQztRQUMxQyxJQUFJLENBQUMsWUFBWSxDQUFDLFdBQVcsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQy9ELElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO0lBQzFCLENBQUM7SUFFTyxnQkFBZ0I7UUFDdEIsd0NBQXdDO1FBQ3hDLElBQUksQ0FBQyxXQUFXLENBQUMsZUFBZSxDQUM5QixJQUFJLE9BQU8sQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUNuQyxrQkFBa0IsRUFDbEIsS0FBSyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsRUFBRTtZQUMxQixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDNUQsT0FBTztnQkFDTCxNQUFNO2dCQUNOLE9BQU8sRUFBRSxPQUFPLENBQUMsT0FBTzthQUN6QixDQUFDO1FBQ0osQ0FBQyxDQUNGLENBQ0YsQ0FBQztJQUNKLENBQUM7SUFFTyxLQUFLLENBQUMsZ0JBQWdCLENBQUMsT0FBZ0I7UUFnQzdDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsV0FBVyxDQUFDO1FBRS9DLGtEQUFrRDtRQUNsRCxJQUFJLFlBQVksR0FBYSxFQUFFLENBQUM7UUFDaEMsSUFBSSxRQUFRLENBQUMsV0FBVyxJQUFJLFFBQVEsQ0FBQyxXQUFXLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDaEUsWUFBWSxHQUFHLFFBQVEsQ0FBQyxXQUFXLENBQUMsY0FBYyxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ3JFLENBQUM7YUFBTSxJQUFJLFFBQVEsQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLE9BQU8sRUFBRSxDQUFDO1lBQ2pELGtEQUFrRDtZQUNsRCxZQUFZLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUMxRCxPQUFPLENBQUMsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FDckMsQ0FBQztRQUNKLENBQUM7UUFFRCxPQUFPO1lBQ0wsS0FBSyxFQUFFO2dCQUNMLE9BQU8sRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLFdBQVc7Z0JBQy9CLEtBQUssRUFBRSxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFO2dCQUN2RCxjQUFjLEVBQUUsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLEVBQUUsZUFBZTtnQkFDakQsVUFBVSxFQUFFO29CQUNWLFNBQVMsRUFBRSxFQUFFO29CQUNiLE9BQU8sRUFBRSxHQUFHO29CQUNaLE1BQU0sRUFBRSxJQUFJO2lCQUNiO2dCQUNELE9BQU8sRUFBRSxZQUFZO2FBQ3RCO1lBQ0QsR0FBRyxFQUFFO2dCQUNILE9BQU8sRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLFNBQVM7Z0JBQzdCLElBQUksRUFBRSxFQUFFO2dCQUNSLFdBQVcsRUFBRSxRQUFRLENBQUMsT0FBTyxDQUFDLFlBQVksSUFBSSxFQUFFO2dCQUNoRCxPQUFPLEVBQUUsSUFBSTtnQkFDYixHQUFHLEVBQUUsR0FBRzthQUNUO1lBQ0QsS0FBSyxFQUFFO2dCQUNMLE9BQU8sRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLFVBQVU7Z0JBQzlCLFFBQVEsRUFBRSxFQUFFO2dCQUNaLFNBQVMsRUFBRSxHQUFHO2dCQUNkLGNBQWMsRUFBRSxJQUFJO2FBQ3JCO1lBQ0QsUUFBUSxFQUFFO2dCQUNSLFNBQVMsRUFBRSxFQUFFO2dCQUNiLFNBQVMsRUFBRSxJQUFJO2dCQUNmLGFBQWEsRUFBRSxJQUFJO2dCQUNuQixXQUFXLEVBQUUsS0FBSzthQUNuQjtTQUNGLENBQUM7SUFDSixDQUFDO0NBQ0YifQ==
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import type { OpsServer } from '../classes.opsserver.js';
|
|
3
|
-
export declare class LogsHandler {
|
|
4
|
-
private opsServerRef;
|
|
5
|
-
typedrouter: plugins.typedrequest.TypedRouter;
|
|
6
|
-
constructor(opsServerRef: OpsServer);
|
|
7
|
-
private registerHandlers;
|
|
8
|
-
private static mapLogLevel;
|
|
9
|
-
private static deriveCategory;
|
|
10
|
-
private getRecentLogs;
|
|
11
|
-
/**
|
|
12
|
-
* Add a log destination to the base logger that pushes entries
|
|
13
|
-
* to all connected ops_dashboard TypedSocket clients.
|
|
14
|
-
*/
|
|
15
|
-
private setupLogPushDestination;
|
|
16
|
-
private setupLogStream;
|
|
17
|
-
}
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
3
|
-
import { logBuffer, baseLogger } from '../../logger.js';
|
|
4
|
-
export class LogsHandler {
|
|
5
|
-
opsServerRef;
|
|
6
|
-
typedrouter = new plugins.typedrequest.TypedRouter();
|
|
7
|
-
constructor(opsServerRef) {
|
|
8
|
-
this.opsServerRef = opsServerRef;
|
|
9
|
-
// Add this handler's router to the parent
|
|
10
|
-
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
|
|
11
|
-
this.registerHandlers();
|
|
12
|
-
this.setupLogPushDestination();
|
|
13
|
-
}
|
|
14
|
-
registerHandlers() {
|
|
15
|
-
// Get Recent Logs Handler
|
|
16
|
-
this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('getRecentLogs', async (dataArg, toolsArg) => {
|
|
17
|
-
const logs = await this.getRecentLogs(dataArg.level, dataArg.category, dataArg.limit || 100, dataArg.offset || 0, dataArg.search, dataArg.timeRange);
|
|
18
|
-
return {
|
|
19
|
-
logs,
|
|
20
|
-
total: logs.length, // TODO: Implement proper total count
|
|
21
|
-
hasMore: false, // TODO: Implement proper pagination
|
|
22
|
-
};
|
|
23
|
-
}));
|
|
24
|
-
// Get Log Stream Handler
|
|
25
|
-
this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('getLogStream', async (dataArg, toolsArg) => {
|
|
26
|
-
// Create a virtual stream for log streaming
|
|
27
|
-
const virtualStream = new plugins.typedrequest.VirtualStream();
|
|
28
|
-
// Set up log streaming
|
|
29
|
-
const streamLogs = this.setupLogStream(virtualStream, dataArg.filters?.level, dataArg.filters?.category, dataArg.follow);
|
|
30
|
-
// Start streaming
|
|
31
|
-
streamLogs.start();
|
|
32
|
-
// VirtualStream handles cleanup automatically
|
|
33
|
-
return {
|
|
34
|
-
logStream: virtualStream, // Cast to IVirtualStream interface
|
|
35
|
-
};
|
|
36
|
-
}));
|
|
37
|
-
}
|
|
38
|
-
static mapLogLevel(smartlogLevel) {
|
|
39
|
-
switch (smartlogLevel) {
|
|
40
|
-
case 'silly':
|
|
41
|
-
case 'debug':
|
|
42
|
-
return 'debug';
|
|
43
|
-
case 'warn':
|
|
44
|
-
return 'warn';
|
|
45
|
-
case 'error':
|
|
46
|
-
return 'error';
|
|
47
|
-
default:
|
|
48
|
-
return 'info';
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
static deriveCategory(zone, message) {
|
|
52
|
-
const msg = (message || '').toLowerCase();
|
|
53
|
-
if (msg.includes('[security:') || msg.includes('security'))
|
|
54
|
-
return 'security';
|
|
55
|
-
if (zone === 'email' || msg.includes('email') || msg.includes('smtp') || msg.includes('mta'))
|
|
56
|
-
return 'email';
|
|
57
|
-
if (zone === 'dns' || msg.includes('dns'))
|
|
58
|
-
return 'dns';
|
|
59
|
-
if (msg.includes('smtp'))
|
|
60
|
-
return 'smtp';
|
|
61
|
-
return 'system';
|
|
62
|
-
}
|
|
63
|
-
async getRecentLogs(level, category, limit = 100, offset = 0, search, timeRange) {
|
|
64
|
-
// Compute a timestamp cutoff from timeRange
|
|
65
|
-
let since;
|
|
66
|
-
if (timeRange) {
|
|
67
|
-
const rangeMs = {
|
|
68
|
-
'1h': 3600000,
|
|
69
|
-
'6h': 21600000,
|
|
70
|
-
'24h': 86400000,
|
|
71
|
-
'7d': 604800000,
|
|
72
|
-
'30d': 2592000000,
|
|
73
|
-
};
|
|
74
|
-
since = Date.now() - (rangeMs[timeRange] || 86400000);
|
|
75
|
-
}
|
|
76
|
-
// Map the UI level to smartlog levels for filtering
|
|
77
|
-
const smartlogLevels = level
|
|
78
|
-
? level === 'debug'
|
|
79
|
-
? ['debug', 'silly']
|
|
80
|
-
: level === 'info'
|
|
81
|
-
? ['info', 'ok', 'success', 'note', 'lifecycle']
|
|
82
|
-
: [level]
|
|
83
|
-
: undefined;
|
|
84
|
-
// Fetch a larger batch from buffer, then apply category filter client-side
|
|
85
|
-
const rawEntries = logBuffer.getEntries({
|
|
86
|
-
level: smartlogLevels,
|
|
87
|
-
search,
|
|
88
|
-
since,
|
|
89
|
-
limit: limit * 3, // over-fetch to compensate for category filtering
|
|
90
|
-
offset: 0,
|
|
91
|
-
});
|
|
92
|
-
// Map ILogPackage → UI log format and apply category filter
|
|
93
|
-
const mapped = [];
|
|
94
|
-
for (const pkg of rawEntries) {
|
|
95
|
-
const uiLevel = LogsHandler.mapLogLevel(pkg.level);
|
|
96
|
-
const uiCategory = LogsHandler.deriveCategory(pkg.context?.zone, pkg.message);
|
|
97
|
-
if (category && uiCategory !== category)
|
|
98
|
-
continue;
|
|
99
|
-
mapped.push({
|
|
100
|
-
timestamp: pkg.timestamp,
|
|
101
|
-
level: uiLevel,
|
|
102
|
-
category: uiCategory,
|
|
103
|
-
message: pkg.message,
|
|
104
|
-
metadata: pkg.data,
|
|
105
|
-
});
|
|
106
|
-
if (mapped.length >= limit)
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
return mapped;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Add a log destination to the base logger that pushes entries
|
|
113
|
-
* to all connected ops_dashboard TypedSocket clients.
|
|
114
|
-
*/
|
|
115
|
-
setupLogPushDestination() {
|
|
116
|
-
const opsServerRef = this.opsServerRef;
|
|
117
|
-
baseLogger.addLogDestination({
|
|
118
|
-
async handleLog(logPackage) {
|
|
119
|
-
// Access the TypedSocket server instance from OpsServer
|
|
120
|
-
const typedsocket = opsServerRef.server?.typedserver?.typedsocket;
|
|
121
|
-
if (!typedsocket)
|
|
122
|
-
return;
|
|
123
|
-
let connections;
|
|
124
|
-
try {
|
|
125
|
-
connections = await typedsocket.findAllTargetConnectionsByTag('role', 'ops_dashboard');
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
if (connections.length === 0)
|
|
131
|
-
return;
|
|
132
|
-
const entry = {
|
|
133
|
-
timestamp: logPackage.timestamp || Date.now(),
|
|
134
|
-
level: LogsHandler.mapLogLevel(logPackage.level),
|
|
135
|
-
category: LogsHandler.deriveCategory(logPackage.context?.zone, logPackage.message),
|
|
136
|
-
message: logPackage.message,
|
|
137
|
-
metadata: logPackage.data,
|
|
138
|
-
};
|
|
139
|
-
for (const conn of connections) {
|
|
140
|
-
try {
|
|
141
|
-
const push = typedsocket.createTypedRequest('pushLogEntry', conn);
|
|
142
|
-
push.fire({ entry }).catch(() => { }); // fire-and-forget
|
|
143
|
-
}
|
|
144
|
-
catch {
|
|
145
|
-
// connection may have closed
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
setupLogStream(virtualStream, levelFilter, categoryFilter, follow = true) {
|
|
152
|
-
let intervalId = null;
|
|
153
|
-
let logIndex = 0;
|
|
154
|
-
const start = () => {
|
|
155
|
-
if (!follow) {
|
|
156
|
-
// Send existing logs and close
|
|
157
|
-
this.getRecentLogs(levelFilter?.[0], categoryFilter?.[0], 100, 0).then(logs => {
|
|
158
|
-
logs.forEach(log => {
|
|
159
|
-
const logData = JSON.stringify(log);
|
|
160
|
-
const encoder = new TextEncoder();
|
|
161
|
-
virtualStream.sendData(encoder.encode(logData));
|
|
162
|
-
});
|
|
163
|
-
// VirtualStream doesn't have end() method - it closes automatically
|
|
164
|
-
});
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
// For follow mode, simulate real-time log streaming
|
|
168
|
-
intervalId = setInterval(async () => {
|
|
169
|
-
const categories = ['smtp', 'dns', 'security', 'system', 'email'];
|
|
170
|
-
const levels = ['info', 'warn', 'error', 'debug'];
|
|
171
|
-
const mockCategory = categories[Math.floor(Math.random() * categories.length)];
|
|
172
|
-
const mockLevel = levels[Math.floor(Math.random() * levels.length)];
|
|
173
|
-
// Filter by requested criteria
|
|
174
|
-
if (levelFilter && !levelFilter.includes(mockLevel))
|
|
175
|
-
return;
|
|
176
|
-
if (categoryFilter && !categoryFilter.includes(mockCategory))
|
|
177
|
-
return;
|
|
178
|
-
const logEntry = {
|
|
179
|
-
timestamp: Date.now(),
|
|
180
|
-
level: mockLevel,
|
|
181
|
-
category: mockCategory,
|
|
182
|
-
message: `Real-time log ${logIndex++} from ${mockCategory}`,
|
|
183
|
-
metadata: {
|
|
184
|
-
requestId: plugins.uuid.v4(),
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
const logData = JSON.stringify(logEntry);
|
|
188
|
-
const encoder = new TextEncoder();
|
|
189
|
-
try {
|
|
190
|
-
await virtualStream.sendData(encoder.encode(logData));
|
|
191
|
-
}
|
|
192
|
-
catch {
|
|
193
|
-
// Stream closed or errored — clean up to prevent interval leak
|
|
194
|
-
clearInterval(intervalId);
|
|
195
|
-
intervalId = null;
|
|
196
|
-
}
|
|
197
|
-
}, 2000); // Send a log every 2 seconds
|
|
198
|
-
// TODO: Hook into actual logger events
|
|
199
|
-
// logger.on('log', (logEntry) => {
|
|
200
|
-
// if (matchesCriteria(logEntry, level, service)) {
|
|
201
|
-
// virtualStream.sendData(formatLogEntry(logEntry));
|
|
202
|
-
// }
|
|
203
|
-
// });
|
|
204
|
-
};
|
|
205
|
-
const stop = () => {
|
|
206
|
-
if (intervalId) {
|
|
207
|
-
clearInterval(intervalId);
|
|
208
|
-
intervalId = null;
|
|
209
|
-
}
|
|
210
|
-
// TODO: Unhook from logger events
|
|
211
|
-
};
|
|
212
|
-
return { start, stop };
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9ncy5oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvb3Bzc2VydmVyL2hhbmRsZXJzL2xvZ3MuaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBRTVDLE9BQU8sS0FBSyxVQUFVLE1BQU0saUNBQWlDLENBQUM7QUFDOUQsT0FBTyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUV4RCxNQUFNLE9BQU8sV0FBVztJQUdGO0lBRmIsV0FBVyxHQUFHLElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUU1RCxZQUFvQixZQUF1QjtRQUF2QixpQkFBWSxHQUFaLFlBQVksQ0FBVztRQUN6QywwQ0FBMEM7UUFDMUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUMvRCxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUN4QixJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztJQUNqQyxDQUFDO0lBRU8sZ0JBQWdCO1FBQ3RCLDBCQUEwQjtRQUMxQixJQUFJLENBQUMsV0FBVyxDQUFDLGVBQWUsQ0FDOUIsSUFBSSxPQUFPLENBQUMsWUFBWSxDQUFDLFlBQVksQ0FDbkMsZUFBZSxFQUNmLEtBQUssRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLEVBQUU7WUFDMUIsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsYUFBYSxDQUNuQyxPQUFPLENBQUMsS0FBSyxFQUNiLE9BQU8sQ0FBQyxRQUFRLEVBQ2hCLE9BQU8sQ0FBQyxLQUFLLElBQUksR0FBRyxFQUNwQixPQUFPLENBQUMsTUFBTSxJQUFJLENBQUMsRUFDbkIsT0FBTyxDQUFDLE1BQU0sRUFDZCxPQUFPLENBQUMsU0FBUyxDQUNsQixDQUFDO1lBRUYs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
|