server-devtools 0.1.5 → 0.1.8
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/README.md +150 -6
- package/dist/dashboard/assets/alert-dialog-DOm6Ua9Q.js +1 -0
- package/dist/dashboard/assets/button-CXALLF_v.js +1 -0
- package/dist/dashboard/assets/index-9FEJIlQV.css +2 -0
- package/dist/dashboard/assets/index-B9G80X8M.js +10 -0
- package/dist/dashboard/assets/overview-page-CZhNooRx.js +53 -0
- package/dist/dashboard/assets/requests-page-DvONd75O.js +1 -0
- package/dist/dashboard/assets/rolldown-runtime-hePW80VL.js +1 -0
- package/dist/dashboard/assets/trace-details-page-Dze7uRsF.js +1 -0
- package/dist/dashboard/assets/with-selector-B8m4Ky1i.js +1 -0
- package/dist/dashboard/index.html +22 -2
- package/dist/src/config/index.d.ts +16 -0
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/index.js +31 -2
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/core/repeated-database-queries/index.d.ts +32 -0
- package/dist/src/core/repeated-database-queries/index.d.ts.map +1 -0
- package/dist/src/core/repeated-database-queries/index.js +130 -0
- package/dist/src/core/repeated-database-queries/index.js.map +1 -0
- package/dist/src/core/repeated-database-queries/mongodb.d.ts +2 -0
- package/dist/src/core/repeated-database-queries/mongodb.d.ts.map +1 -0
- package/dist/src/core/repeated-database-queries/mongodb.js +43 -0
- package/dist/src/core/repeated-database-queries/mongodb.js.map +1 -0
- package/dist/src/core/repeated-database-queries/mysql.d.ts +2 -0
- package/dist/src/core/repeated-database-queries/mysql.d.ts.map +1 -0
- package/dist/src/core/repeated-database-queries/mysql.js +16 -0
- package/dist/src/core/repeated-database-queries/mysql.js.map +1 -0
- package/dist/src/core/repeated-database-queries/postgres.d.ts +2 -0
- package/dist/src/core/repeated-database-queries/postgres.d.ts.map +1 -0
- package/dist/src/core/repeated-database-queries/postgres.js +15 -0
- package/dist/src/core/repeated-database-queries/postgres.js.map +1 -0
- package/dist/src/core/repeated-database-queries/redis.d.ts +8 -0
- package/dist/src/core/repeated-database-queries/redis.d.ts.map +1 -0
- package/dist/src/core/repeated-database-queries/redis.js +91 -0
- package/dist/src/core/repeated-database-queries/redis.js.map +1 -0
- package/dist/src/core/repeated-database-queries/sql-template.d.ts +2 -0
- package/dist/src/core/repeated-database-queries/sql-template.d.ts.map +1 -0
- package/dist/src/core/repeated-database-queries/sql-template.js +12 -0
- package/dist/src/core/repeated-database-queries/sql-template.js.map +1 -0
- package/dist/src/core/slow-span.d.ts +5 -0
- package/dist/src/core/slow-span.d.ts.map +1 -0
- package/dist/src/core/slow-span.js +37 -0
- package/dist/src/core/slow-span.js.map +1 -0
- package/dist/src/dashboard/dashboard-server.d.ts +12 -1
- package/dist/src/dashboard/dashboard-server.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard-server.js +344 -27
- package/dist/src/dashboard/dashboard-server.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +35 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/instrumentation/exporter.d.ts +2 -1
- package/dist/src/instrumentation/exporter.d.ts.map +1 -1
- package/dist/src/instrumentation/exporter.js +3 -2
- package/dist/src/instrumentation/exporter.js.map +1 -1
- package/dist/src/instrumentation/instrumentation.d.ts +2 -1
- package/dist/src/instrumentation/instrumentation.d.ts.map +1 -1
- package/dist/src/instrumentation/instrumentation.js +4 -3
- package/dist/src/instrumentation/instrumentation.js.map +1 -1
- package/dist/src/instrumentation/outbound-http/outbound-http-capture.d.ts +8 -1
- package/dist/src/instrumentation/outbound-http/outbound-http-capture.d.ts.map +1 -1
- package/dist/src/instrumentation/outbound-http/outbound-http-capture.js +158 -32
- package/dist/src/instrumentation/outbound-http/outbound-http-capture.js.map +1 -1
- package/dist/src/instrumentation/span-mapper.d.ts +4 -0
- package/dist/src/instrumentation/span-mapper.d.ts.map +1 -1
- package/dist/src/instrumentation/span-mapper.js +25 -3
- package/dist/src/instrumentation/span-mapper.js.map +1 -1
- package/dist/src/security/auth.service.d.ts +6 -0
- package/dist/src/security/auth.service.d.ts.map +1 -1
- package/dist/src/security/auth.service.js +62 -4
- package/dist/src/security/auth.service.js.map +1 -1
- package/dist/src/security/redaction.service.d.ts +8 -0
- package/dist/src/security/redaction.service.d.ts.map +1 -0
- package/dist/src/security/redaction.service.js +35 -0
- package/dist/src/security/redaction.service.js.map +1 -0
- package/dist/src/storage/sqlite-storage.d.ts +11 -2
- package/dist/src/storage/sqlite-storage.d.ts.map +1 -1
- package/dist/src/storage/sqlite-storage.js +194 -13
- package/dist/src/storage/sqlite-storage.js.map +1 -1
- package/dist/src/types/dashboard.d.ts +25 -0
- package/dist/src/types/dashboard.d.ts.map +1 -1
- package/dist/src/types/index.d.ts +1 -0
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +1 -0
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/misc.d.ts +5 -0
- package/dist/src/types/misc.d.ts.map +1 -1
- package/dist/src/types/outbound-http.d.ts +9 -0
- package/dist/src/types/outbound-http.d.ts.map +1 -0
- package/dist/src/types/outbound-http.js +3 -0
- package/dist/src/types/outbound-http.js.map +1 -0
- package/dist/src/utils/allowed-ips.d.ts +4 -0
- package/dist/src/utils/allowed-ips.d.ts.map +1 -0
- package/dist/src/utils/allowed-ips.js +34 -0
- package/dist/src/utils/allowed-ips.js.map +1 -0
- package/dist/src/utils/helpers.d.ts +2 -0
- package/dist/src/utils/helpers.d.ts.map +1 -0
- package/dist/src/utils/helpers.js +13 -0
- package/dist/src/utils/helpers.js.map +1 -0
- package/package.json +3 -2
- package/dist/dashboard/assets/index-BXbomG5W.js +0 -9
- package/dist/dashboard/assets/index-DYvJ5RTl.css +0 -2
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const api_1 = require("@opentelemetry/api");
|
|
4
4
|
class SpanMapper {
|
|
5
|
+
redactionService;
|
|
6
|
+
constructor(redactionService) {
|
|
7
|
+
this.redactionService = redactionService;
|
|
8
|
+
}
|
|
5
9
|
map(span) {
|
|
6
10
|
const context = span.spanContext();
|
|
7
11
|
const exceptionEvent = span?.events?.find((event) => event.name === 'exception');
|
|
@@ -13,9 +17,7 @@ class SpanMapper {
|
|
|
13
17
|
name: span.name,
|
|
14
18
|
startedAt: this.hrTimeToMilliseconds(span.startTime),
|
|
15
19
|
durationMs: this.hrTimeToMilliseconds(span.duration),
|
|
16
|
-
attributes:
|
|
17
|
-
...span.attributes,
|
|
18
|
-
},
|
|
20
|
+
attributes: this.redactHeaderAttributes(span.attributes),
|
|
19
21
|
status: {
|
|
20
22
|
code: span.status.code,
|
|
21
23
|
message: span.status.message,
|
|
@@ -29,6 +31,26 @@ class SpanMapper {
|
|
|
29
31
|
: undefined,
|
|
30
32
|
};
|
|
31
33
|
}
|
|
34
|
+
redactHeaderAttributes(attributes) {
|
|
35
|
+
const redactedAttributes = { ...attributes };
|
|
36
|
+
const headerPrefixes = [
|
|
37
|
+
"http.request.header.",
|
|
38
|
+
"http.response.header.",
|
|
39
|
+
];
|
|
40
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
41
|
+
const normalizedKey = key.toLowerCase();
|
|
42
|
+
const prefix = headerPrefixes.find((candidate) => normalizedKey.startsWith(candidate));
|
|
43
|
+
if (!prefix) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const headerName = key.slice(prefix.length);
|
|
47
|
+
const redactedHeader = this.redactionService.redactData({
|
|
48
|
+
[headerName]: value,
|
|
49
|
+
});
|
|
50
|
+
redactedAttributes[key] = redactedHeader[headerName];
|
|
51
|
+
}
|
|
52
|
+
return redactedAttributes;
|
|
53
|
+
}
|
|
32
54
|
getType(span) {
|
|
33
55
|
if (span.attributes['db.system.name']) {
|
|
34
56
|
return 'database';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"span-mapper.js","sourceRoot":"","sources":["../../../src/instrumentation/span-mapper.ts"],"names":[],"mappings":";;AAEA,4CAA8C;
|
|
1
|
+
{"version":3,"file":"span-mapper.js","sourceRoot":"","sources":["../../../src/instrumentation/span-mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,13 @@ declare class AuthService {
|
|
|
4
4
|
private readonly username;
|
|
5
5
|
private readonly password;
|
|
6
6
|
private readonly storage;
|
|
7
|
+
private readonly sessionKey;
|
|
8
|
+
private verifier;
|
|
7
9
|
constructor(username: string, password: string, storage: SQLiteStorage);
|
|
10
|
+
private initializeSessions;
|
|
11
|
+
private deriveCredentialHash;
|
|
12
|
+
private credentialsMatch;
|
|
13
|
+
private reconcileCredentialVerifier;
|
|
8
14
|
private generateSessionToken;
|
|
9
15
|
private hashSessionToken;
|
|
10
16
|
validateCredentials(username: string, password: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../src/security/auth.service.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,2BAA2B,CAAC;AACtD,OAAO,
|
|
1
|
+
{"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../src/security/auth.service.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAA2B,QAAQ,EAAE,MAAM,UAAU,CAAC;AAI7D,cAAM,WAAW;IAOT,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAA2B;IAE3C,YACqB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,EAa1C;IAED,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,2BAA2B;IAenC,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,gBAAgB;IAQxB,mBAAmB,CACf,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACjB,OAAO,CAUT;IAGD,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAkCtC;IAED,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CA6B9C;IAED,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CASjC;CACJ;eAGc,WAAW"}
|
|
@@ -9,23 +9,76 @@ class AuthService {
|
|
|
9
9
|
username;
|
|
10
10
|
password;
|
|
11
11
|
storage;
|
|
12
|
+
// Session hashes are keyed by the configured credentials so that changing
|
|
13
|
+
// the username or password invalidates every previously issued session.
|
|
14
|
+
sessionKey;
|
|
15
|
+
verifier;
|
|
12
16
|
constructor(username, password, storage) {
|
|
13
17
|
this.username = username;
|
|
14
18
|
this.password = password;
|
|
15
19
|
this.storage = storage;
|
|
16
20
|
this.username = this.username ? this.username?.toLowerCase()?.trim() : '';
|
|
21
|
+
this.sessionKey = node_crypto_1.default
|
|
22
|
+
.createHash("sha256")
|
|
23
|
+
.update(JSON.stringify([
|
|
24
|
+
"server-devtools-session-v1",
|
|
25
|
+
this.username,
|
|
26
|
+
this.password,
|
|
27
|
+
]))
|
|
28
|
+
.digest();
|
|
29
|
+
this.initializeSessions();
|
|
30
|
+
}
|
|
31
|
+
initializeSessions() {
|
|
32
|
+
try {
|
|
33
|
+
this.reconcileCredentialVerifier();
|
|
34
|
+
this.storage.deleteExpiredSessions(Date.now());
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
(0, logger_1.debugLog)("Dashboard session initialization failed", {
|
|
38
|
+
errorName: error instanceof Error ? error.name : typeof error,
|
|
39
|
+
});
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
deriveCredentialHash(salt) {
|
|
44
|
+
return node_crypto_1.default.scryptSync(JSON.stringify([
|
|
45
|
+
"server-devtools-credentials-v1",
|
|
46
|
+
this.username,
|
|
47
|
+
this.password,
|
|
48
|
+
]), salt, 32);
|
|
49
|
+
}
|
|
50
|
+
credentialsMatch(salt, hash) {
|
|
51
|
+
const expected = Buffer.from(hash, "hex");
|
|
52
|
+
const actual = this.deriveCredentialHash(Buffer.from(salt, "hex"));
|
|
53
|
+
return expected.length === actual.length &&
|
|
54
|
+
node_crypto_1.default.timingSafeEqual(expected, actual);
|
|
55
|
+
}
|
|
56
|
+
// Sessions are deleted whenever the stored verifier is missing or does not
|
|
57
|
+
// match, because they may have been issued under different credentials.
|
|
58
|
+
reconcileCredentialVerifier() {
|
|
59
|
+
this.verifier = this.storage.reconcileAuthCredentialVerifier(stored => this.credentialsMatch(stored.salt, stored.hash), () => {
|
|
60
|
+
const salt = node_crypto_1.default.randomBytes(16);
|
|
61
|
+
return {
|
|
62
|
+
salt: salt.toString("hex"),
|
|
63
|
+
hash: this.deriveCredentialHash(salt).toString("hex"),
|
|
64
|
+
updatedAt: Date.now(),
|
|
65
|
+
};
|
|
66
|
+
});
|
|
17
67
|
}
|
|
18
68
|
generateSessionToken() {
|
|
19
69
|
return node_crypto_1.default.randomBytes(32).toString("base64url");
|
|
20
70
|
}
|
|
21
71
|
hashSessionToken(token) {
|
|
22
72
|
return node_crypto_1.default
|
|
23
|
-
.
|
|
73
|
+
.createHmac("sha256", this.sessionKey)
|
|
24
74
|
.update(token)
|
|
25
75
|
.digest("hex");
|
|
26
76
|
}
|
|
27
77
|
validateCredentials(username, password) {
|
|
28
|
-
|
|
78
|
+
if (typeof username !== "string" || typeof password !== "string") {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const normalizedUsername = username ? username.toLowerCase().trim() : null;
|
|
29
82
|
return (normalizedUsername === this.username &&
|
|
30
83
|
password === this.password);
|
|
31
84
|
}
|
|
@@ -36,12 +89,15 @@ class AuthService {
|
|
|
36
89
|
const expiresAt = createdAt + (24 * 60 * 60 * 1000);
|
|
37
90
|
const normalizedUsername = username?.toLowerCase()?.trim();
|
|
38
91
|
try {
|
|
39
|
-
this.storage.
|
|
92
|
+
const saved = this.storage.saveSessionForVerifier({
|
|
40
93
|
sessionHash,
|
|
41
94
|
username: normalizedUsername,
|
|
42
95
|
createdAt,
|
|
43
96
|
expiresAt,
|
|
44
|
-
});
|
|
97
|
+
}, this.verifier, createdAt);
|
|
98
|
+
if (!saved) {
|
|
99
|
+
throw new Error("Dashboard credentials changed since this process started");
|
|
100
|
+
}
|
|
45
101
|
}
|
|
46
102
|
catch (error) {
|
|
47
103
|
(0, logger_1.debugLog)("Session creation persistence failed", {
|
|
@@ -66,6 +122,8 @@ class AuthService {
|
|
|
66
122
|
}
|
|
67
123
|
if (!session)
|
|
68
124
|
return undefined;
|
|
125
|
+
if (session.username !== this.username)
|
|
126
|
+
return undefined;
|
|
69
127
|
const hasExpired = session.expiresAt <= currentDate;
|
|
70
128
|
if (hasExpired) {
|
|
71
129
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../src/security/auth.service.ts"],"names":[],"mappings":";;;;;AAEA,4CAA2C;AAC3C,8DAAiC;AAEjC,MAAM,WAAW;
|
|
1
|
+
{"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../src/security/auth.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare class RedactionService {
|
|
2
|
+
private readonly redactFields;
|
|
3
|
+
private readonly allowRedacting;
|
|
4
|
+
constructor(allowRedacting: boolean, redactFields: string[]);
|
|
5
|
+
redactData(data: unknown): unknown;
|
|
6
|
+
}
|
|
7
|
+
export default RedactionService;
|
|
8
|
+
//# sourceMappingURL=redaction.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.service.d.ts","sourceRoot":"","sources":["../../../src/security/redaction.service.ts"],"names":[],"mappings":"AAIA,cAAM,gBAAgB;IAClB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC,YACI,cAAc,EAAE,OAAO,EACvB,YAAY,EAAE,MAAM,EAAE,EAQzB;IAED,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAuBjC;CAIJ;eAEc,gBAAgB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const config_1 = __importDefault(require("../config"));
|
|
7
|
+
class RedactionService {
|
|
8
|
+
redactFields;
|
|
9
|
+
allowRedacting;
|
|
10
|
+
constructor(allowRedacting, redactFields) {
|
|
11
|
+
this.allowRedacting = allowRedacting;
|
|
12
|
+
this.redactFields = new Set(redactFields
|
|
13
|
+
.map(field => field?.trim().toLowerCase())
|
|
14
|
+
.filter(Boolean));
|
|
15
|
+
}
|
|
16
|
+
redactData(data) {
|
|
17
|
+
if (!this.allowRedacting)
|
|
18
|
+
return data;
|
|
19
|
+
if (Array.isArray(data)) {
|
|
20
|
+
return data.map(item => this.redactData(item));
|
|
21
|
+
}
|
|
22
|
+
if (data !== null && typeof data === "object") {
|
|
23
|
+
return Object.fromEntries(Object.entries(data).map(([key, value]) => {
|
|
24
|
+
if (this.redactFields.has(key.toLowerCase()) &&
|
|
25
|
+
value !== undefined) {
|
|
26
|
+
return [key, config_1.default.REDACTED_KEY];
|
|
27
|
+
}
|
|
28
|
+
return [key, this.redactData(value)];
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
return data;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.default = RedactionService;
|
|
35
|
+
//# sourceMappingURL=redaction.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.service.js","sourceRoot":"","sources":["../../../src/security/redaction.service.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+B;AAI/B,MAAM,gBAAgB;IACD,YAAY,CAAc;IAC1B,cAAc,CAAS;IAExC,YACI,cAAuB,EACvB,YAAsB;QAEtB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAA;QACpC,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,CACvB,YAAY;aACP,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;aACzC,MAAM,CAAC,OAAO,CAAC,CACvB,CAAC;IACN,CAAC;IAED,UAAU,CAAC,IAAa;QACpB,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,OAAO,IAAI,CAAC;QAEtC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC5C,OAAO,MAAM,CAAC,WAAW,CACrB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;gBACtC,IACI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;oBACxC,KAAK,KAAK,SAAS,EACrB,CAAC;oBACC,OAAO,CAAC,GAAG,EAAE,gBAAM,CAAC,YAAY,CAAC,CAAC;gBACtC,CAAC;gBAED,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;YACzC,CAAC,CAAC,CACL,CAAC;QACN,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;CAIJ;kBAEc,gBAAgB"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { IDevToolsCurrentUser, IDevToolsSpan, IDevToolsTrace, IHttpClientDetails, ISession, ITraceMetadata, ITraceSummary } from "../types";
|
|
1
|
+
import { DashboardOverviewRange, IAuthCredentialVerifier, IDevToolsCurrentUser, IDevToolsSpan, IDevToolsTrace, IHttpClientDetails, IOverviewMetrics, ISession, ITraceMetadata, ITraceSummary } from "../types";
|
|
2
2
|
import EncryptDecryptService from "../security/encrypt-decrypt.service";
|
|
3
|
+
export type TraceSummarySortField = "startedAt" | "durationMs" | "statusCode" | "method" | "path";
|
|
4
|
+
export type TraceSummarySortDirection = "asc" | "desc";
|
|
3
5
|
export default class SQLiteStorage {
|
|
4
6
|
private readonly encryptionService?;
|
|
5
7
|
private readonly db;
|
|
@@ -7,7 +9,9 @@ export default class SQLiteStorage {
|
|
|
7
9
|
private getSchemaVersion;
|
|
8
10
|
private setSchemaVersion;
|
|
9
11
|
private initializeV1Schema;
|
|
12
|
+
private initializeV2Schema;
|
|
10
13
|
private initialize;
|
|
14
|
+
private runImmediateTransaction;
|
|
11
15
|
private deserializeSensitiveValue;
|
|
12
16
|
private serializeSensitiveValue;
|
|
13
17
|
saveSpan(span: IDevToolsSpan): void;
|
|
@@ -15,10 +19,11 @@ export default class SQLiteStorage {
|
|
|
15
19
|
getTrace(traceId: string): IDevToolsTrace | undefined;
|
|
16
20
|
saveTraceSummary(span: IDevToolsSpan): void;
|
|
17
21
|
getTraceSummaries(): ITraceSummary[];
|
|
18
|
-
getPaginatedTraceSummaries(page: number, limit: number, search?: string, method?: string, statusCode?: number): {
|
|
22
|
+
getPaginatedTraceSummaries(page: number, limit: number, search?: string, method?: string, statusCode?: number, startedAtFrom?: number, startedAtTo?: number, sortField?: TraceSummarySortField, sortDirection?: TraceSummarySortDirection): {
|
|
19
23
|
summaries: ITraceSummary[];
|
|
20
24
|
total: number;
|
|
21
25
|
};
|
|
26
|
+
getOverview(range: DashboardOverviewRange, now?: number): IOverviewMetrics;
|
|
22
27
|
saveRequestBody(traceId: string, body: unknown): void;
|
|
23
28
|
saveResponseBody(traceId: string, body: unknown): void;
|
|
24
29
|
getTraceMetadata(traceId: string): ITraceMetadata | undefined;
|
|
@@ -27,6 +32,10 @@ export default class SQLiteStorage {
|
|
|
27
32
|
saveSession(session: ISession): void;
|
|
28
33
|
getSessionByHash(sessionHash: string): ISession | undefined;
|
|
29
34
|
deleteSession(sessionHash: string): void;
|
|
35
|
+
deleteExpiredSessions(now: number): number;
|
|
36
|
+
getAuthCredentialVerifier(): IAuthCredentialVerifier | undefined;
|
|
37
|
+
reconcileAuthCredentialVerifier(matches: (stored: IAuthCredentialVerifier) => boolean, createVerifier: () => IAuthCredentialVerifier): IAuthCredentialVerifier;
|
|
38
|
+
saveSessionForVerifier(session: ISession, verifier: IAuthCredentialVerifier, now: number): boolean;
|
|
30
39
|
saveCurrentUser(traceId: string, user: IDevToolsCurrentUser): void;
|
|
31
40
|
updateHttpClientDetails(spanId: string, details: Partial<Omit<IHttpClientDetails, "spanId">>): void;
|
|
32
41
|
getHttpClientDetails(spanId: string): IHttpClientDetails | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-storage.d.ts","sourceRoot":"","sources":["../../../src/storage/sqlite-storage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite-storage.d.ts","sourceRoot":"","sources":["../../../src/storage/sqlite-storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -91,23 +91,64 @@ class SQLiteStorage {
|
|
|
91
91
|
this.db.exec(`ALTER TABLE trace_metadata ADD COLUMN user_json TEXT`);
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
initializeV2Schema() {
|
|
95
|
+
this.db.exec(`
|
|
96
|
+
CREATE TABLE IF NOT EXISTS auth_credentials (
|
|
97
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
98
|
+
salt TEXT NOT NULL,
|
|
99
|
+
hash TEXT NOT NULL,
|
|
100
|
+
updated_at INTEGER NOT NULL
|
|
101
|
+
);
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
94
104
|
initialize() {
|
|
95
|
-
|
|
96
|
-
if (version === config_1.default.STORAGE.SCHEMA_VERSION) {
|
|
105
|
+
if (this.getSchemaVersion() >= config_1.default.STORAGE.SCHEMA_VERSION) {
|
|
97
106
|
return;
|
|
98
107
|
}
|
|
99
|
-
|
|
100
|
-
this.
|
|
101
|
-
|
|
108
|
+
this.runImmediateTransaction(() => {
|
|
109
|
+
// Another process may have migrated while this one waited for the lock.
|
|
110
|
+
const version = this.getSchemaVersion();
|
|
111
|
+
if (version >= config_1.default.STORAGE.SCHEMA_VERSION) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (version < 1) {
|
|
102
115
|
this.initializeV1Schema();
|
|
103
|
-
|
|
116
|
+
}
|
|
117
|
+
if (version < 2) {
|
|
118
|
+
this.initializeV2Schema();
|
|
119
|
+
}
|
|
120
|
+
this.setSchemaVersion(config_1.default.STORAGE.SCHEMA_VERSION);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// Takes the write lock up front and waits for other processes holding it,
|
|
124
|
+
// so read-check-write sequences cannot interleave across connections.
|
|
125
|
+
runImmediateTransaction(operation) {
|
|
126
|
+
const { timeout: previousTimeout } = this.db
|
|
127
|
+
.prepare("PRAGMA busy_timeout")
|
|
128
|
+
.get();
|
|
129
|
+
this.db.exec(`PRAGMA busy_timeout = ${Math.max(0, Math.floor(config_1.default.STORAGE.BUSY_TIMEOUT_MS))}`);
|
|
130
|
+
try {
|
|
131
|
+
this.db.exec("BEGIN IMMEDIATE");
|
|
132
|
+
try {
|
|
133
|
+
const result = operation();
|
|
104
134
|
this.db.exec("COMMIT");
|
|
135
|
+
return result;
|
|
105
136
|
}
|
|
106
137
|
catch (error) {
|
|
107
|
-
this.db.
|
|
138
|
+
if (this.db.isTransaction) {
|
|
139
|
+
try {
|
|
140
|
+
this.db.exec("ROLLBACK");
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Preserve the original failure.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
108
146
|
throw error;
|
|
109
147
|
}
|
|
110
148
|
}
|
|
149
|
+
finally {
|
|
150
|
+
this.db.exec(`PRAGMA busy_timeout = ${previousTimeout}`);
|
|
151
|
+
}
|
|
111
152
|
}
|
|
112
153
|
deserializeSensitiveValue(value) {
|
|
113
154
|
const parsed = JSON.parse(value);
|
|
@@ -142,7 +183,7 @@ class SQLiteStorage {
|
|
|
142
183
|
: null);
|
|
143
184
|
// Handles the case where the trace summary already exists
|
|
144
185
|
// when an errored child span arrives.
|
|
145
|
-
if (span.error) {
|
|
186
|
+
if (span.status.code === 2 || span.error !== undefined) {
|
|
146
187
|
this.db.prepare(`
|
|
147
188
|
UPDATE traces
|
|
148
189
|
SET has_error = 1
|
|
@@ -218,7 +259,10 @@ class SQLiteStorage {
|
|
|
218
259
|
SELECT 1
|
|
219
260
|
FROM spans
|
|
220
261
|
WHERE trace_id = ?
|
|
221
|
-
AND
|
|
262
|
+
AND (
|
|
263
|
+
json_extract(status_json, '$.code') = 2
|
|
264
|
+
OR error_json IS NOT NULL
|
|
265
|
+
)
|
|
222
266
|
LIMIT 1
|
|
223
267
|
`).get(span.traceId);
|
|
224
268
|
const hasError = errorSpan ? 1 : 0;
|
|
@@ -287,14 +331,15 @@ class SQLiteStorage {
|
|
|
287
331
|
};
|
|
288
332
|
});
|
|
289
333
|
}
|
|
290
|
-
getPaginatedTraceSummaries(page, limit, search = "", method, statusCode) {
|
|
334
|
+
getPaginatedTraceSummaries(page, limit, search = "", method, statusCode, startedAtFrom, startedAtTo, sortField = "startedAt", sortDirection = "desc") {
|
|
291
335
|
const offset = (page - 1) * limit;
|
|
292
336
|
const conditions = [];
|
|
293
337
|
const parameters = [];
|
|
294
338
|
const normalizedSearch = search.trim();
|
|
295
339
|
if (normalizedSearch) {
|
|
296
340
|
conditions.push(`(
|
|
297
|
-
t.
|
|
341
|
+
t.trace_id LIKE ? COLLATE NOCASE ESCAPE '\\'
|
|
342
|
+
OR t.path LIKE ? COLLATE NOCASE ESCAPE '\\'
|
|
298
343
|
OR t.route LIKE ? COLLATE NOCASE ESCAPE '\\'
|
|
299
344
|
OR EXISTS (
|
|
300
345
|
SELECT 1
|
|
@@ -303,10 +348,18 @@ class SQLiteStorage {
|
|
|
303
348
|
AND s.span_id = t.root_span_id
|
|
304
349
|
AND s.name LIKE ? COLLATE NOCASE ESCAPE '\\'
|
|
305
350
|
)
|
|
351
|
+
OR EXISTS (
|
|
352
|
+
SELECT 1
|
|
353
|
+
FROM spans s
|
|
354
|
+
WHERE s.trace_id = t.trace_id
|
|
355
|
+
AND s.span_id = t.root_span_id
|
|
356
|
+
AND json_extract(s.attributes_json, '$."url.query"')
|
|
357
|
+
LIKE ? COLLATE NOCASE ESCAPE '\\'
|
|
358
|
+
)
|
|
306
359
|
)`);
|
|
307
360
|
const escapedSearch = normalizedSearch.replace(/[\\%_]/g, character => `\\${character}`);
|
|
308
361
|
const searchPattern = `%${escapedSearch}%`;
|
|
309
|
-
parameters.push(searchPattern, searchPattern, searchPattern);
|
|
362
|
+
parameters.push(searchPattern, searchPattern, searchPattern, searchPattern, searchPattern);
|
|
310
363
|
}
|
|
311
364
|
const normalizedMethod = method?.trim();
|
|
312
365
|
if (normalizedMethod) {
|
|
@@ -317,9 +370,27 @@ class SQLiteStorage {
|
|
|
317
370
|
conditions.push("t.status_code = ?");
|
|
318
371
|
parameters.push(statusCode);
|
|
319
372
|
}
|
|
373
|
+
if (startedAtFrom !== undefined) {
|
|
374
|
+
conditions.push("t.started_at >= ?");
|
|
375
|
+
parameters.push(startedAtFrom);
|
|
376
|
+
}
|
|
377
|
+
if (startedAtTo !== undefined) {
|
|
378
|
+
conditions.push("t.started_at < ?");
|
|
379
|
+
parameters.push(startedAtTo);
|
|
380
|
+
}
|
|
320
381
|
const whereClause = conditions.length > 0
|
|
321
382
|
? `WHERE ${conditions.join(" AND ")}`
|
|
322
383
|
: "";
|
|
384
|
+
const sortColumns = {
|
|
385
|
+
startedAt: "t.started_at",
|
|
386
|
+
durationMs: "t.duration_ms",
|
|
387
|
+
statusCode: "t.status_code",
|
|
388
|
+
method: "t.method",
|
|
389
|
+
path: "t.path",
|
|
390
|
+
};
|
|
391
|
+
const sortColumn = sortColumns[sortField];
|
|
392
|
+
const sortNullsLast = `CASE WHEN ${sortColumn} IS NULL THEN 1 ELSE 0 END ASC`;
|
|
393
|
+
const sortOrder = `${sortColumn} ${sortDirection.toUpperCase()}`;
|
|
323
394
|
const totalRow = this.db.prepare(`
|
|
324
395
|
SELECT COUNT(*) AS total
|
|
325
396
|
FROM traces t
|
|
@@ -335,7 +406,7 @@ class SQLiteStorage {
|
|
|
335
406
|
LEFT JOIN trace_metadata tm
|
|
336
407
|
ON tm.trace_id = t.trace_id
|
|
337
408
|
${whereClause}
|
|
338
|
-
ORDER BY t.
|
|
409
|
+
ORDER BY ${sortNullsLast}, ${sortOrder}, t.trace_id ASC
|
|
339
410
|
LIMIT ? OFFSET ?
|
|
340
411
|
`).all(...parameters, limit, offset);
|
|
341
412
|
const summaries = rows.map((row) => {
|
|
@@ -361,6 +432,64 @@ class SQLiteStorage {
|
|
|
361
432
|
});
|
|
362
433
|
return { summaries, total: totalRow.total };
|
|
363
434
|
}
|
|
435
|
+
getOverview(range, now = Date.now()) {
|
|
436
|
+
const rangeMs = {
|
|
437
|
+
"15m": 15 * 60 * 1000,
|
|
438
|
+
"1h": 60 * 60 * 1000,
|
|
439
|
+
"6h": 6 * 60 * 60 * 1000,
|
|
440
|
+
"24h": 24 * 60 * 60 * 1000,
|
|
441
|
+
};
|
|
442
|
+
const to = now;
|
|
443
|
+
const from = to - rangeMs[range];
|
|
444
|
+
const bucketCount = range === "15m" ? 15 : range === "1h" ? 12 : 24;
|
|
445
|
+
const bucketWidth = rangeMs[range] / bucketCount;
|
|
446
|
+
const rows = this.db.prepare(`
|
|
447
|
+
SELECT trace_id, started_at, duration_ms, method, path, route, status_code, has_error
|
|
448
|
+
FROM traces
|
|
449
|
+
WHERE started_at >= ? AND started_at < ?
|
|
450
|
+
ORDER BY started_at DESC
|
|
451
|
+
`).all(from, to);
|
|
452
|
+
const durations = rows
|
|
453
|
+
.map(row => row.duration_ms)
|
|
454
|
+
.filter((duration) => duration !== null && Number.isFinite(duration));
|
|
455
|
+
const failures = rows
|
|
456
|
+
.filter(row => row.status_code !== null && row.status_code >= 500 && row.status_code < 600)
|
|
457
|
+
.slice(0, 10)
|
|
458
|
+
.map(row => ({
|
|
459
|
+
id: row.trace_id,
|
|
460
|
+
method: row.method ?? "—",
|
|
461
|
+
path: row.path ?? "—",
|
|
462
|
+
route: row.route ?? undefined,
|
|
463
|
+
statusCode: row.status_code ?? undefined,
|
|
464
|
+
durationMs: row.duration_ms ?? undefined,
|
|
465
|
+
startedAt: row.started_at,
|
|
466
|
+
hasError: row.has_error === 1,
|
|
467
|
+
}));
|
|
468
|
+
const volume = Array.from({ length: bucketCount }, (_, index) => {
|
|
469
|
+
const startAt = from + index * bucketWidth;
|
|
470
|
+
const endAt = index === bucketCount - 1 ? to : startAt + bucketWidth;
|
|
471
|
+
return {
|
|
472
|
+
startAt,
|
|
473
|
+
endAt,
|
|
474
|
+
count: rows.filter(row => row.started_at >= startAt && row.started_at < endAt).length,
|
|
475
|
+
};
|
|
476
|
+
});
|
|
477
|
+
return {
|
|
478
|
+
range,
|
|
479
|
+
from,
|
|
480
|
+
to,
|
|
481
|
+
totalRequests: rows.length,
|
|
482
|
+
errorRate: rows.length === 0 ? 0 : failures.length > 0
|
|
483
|
+
? rows.filter(row => row.status_code !== null && row.status_code >= 500 && row.status_code < 600).length / rows.length
|
|
484
|
+
: 0,
|
|
485
|
+
averageLatencyMs: durations.length === 0
|
|
486
|
+
? 0
|
|
487
|
+
: durations.reduce((sum, duration) => sum + duration, 0) / durations.length,
|
|
488
|
+
slowRequests: durations.filter(duration => duration > 500).length,
|
|
489
|
+
volume,
|
|
490
|
+
recentFailures: failures,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
364
493
|
saveRequestBody(traceId, body) {
|
|
365
494
|
const statement = this.db.prepare(`
|
|
366
495
|
INSERT INTO trace_metadata (
|
|
@@ -507,6 +636,58 @@ class SQLiteStorage {
|
|
|
507
636
|
WHERE session_hash = ?
|
|
508
637
|
`).run(sessionHash);
|
|
509
638
|
}
|
|
639
|
+
deleteExpiredSessions(now) {
|
|
640
|
+
const result = this.db.prepare(`
|
|
641
|
+
DELETE FROM sessions
|
|
642
|
+
WHERE expires_at <= ?
|
|
643
|
+
`).run(now);
|
|
644
|
+
return Number(result.changes);
|
|
645
|
+
}
|
|
646
|
+
getAuthCredentialVerifier() {
|
|
647
|
+
const row = this.db.prepare(`
|
|
648
|
+
SELECT salt, hash, updated_at
|
|
649
|
+
FROM auth_credentials
|
|
650
|
+
WHERE id = 1
|
|
651
|
+
`).get();
|
|
652
|
+
if (!row)
|
|
653
|
+
return undefined;
|
|
654
|
+
return {
|
|
655
|
+
salt: row.salt,
|
|
656
|
+
hash: row.hash,
|
|
657
|
+
updatedAt: row.updated_at,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
reconcileAuthCredentialVerifier(matches, createVerifier) {
|
|
661
|
+
return this.runImmediateTransaction(() => {
|
|
662
|
+
const stored = this.getAuthCredentialVerifier();
|
|
663
|
+
if (stored && matches(stored)) {
|
|
664
|
+
return stored;
|
|
665
|
+
}
|
|
666
|
+
const verifier = createVerifier();
|
|
667
|
+
this.db.exec("DELETE FROM sessions");
|
|
668
|
+
this.db.prepare(`
|
|
669
|
+
INSERT OR REPLACE INTO auth_credentials (
|
|
670
|
+
id,
|
|
671
|
+
salt,
|
|
672
|
+
hash,
|
|
673
|
+
updated_at
|
|
674
|
+
)
|
|
675
|
+
VALUES (1, ?, ?, ?)
|
|
676
|
+
`).run(verifier.salt, verifier.hash, verifier.updatedAt);
|
|
677
|
+
return verifier;
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
saveSessionForVerifier(session, verifier, now) {
|
|
681
|
+
return this.runImmediateTransaction(() => {
|
|
682
|
+
const stored = this.getAuthCredentialVerifier();
|
|
683
|
+
if (!stored || stored.salt !== verifier.salt || stored.hash !== verifier.hash) {
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
this.deleteExpiredSessions(now);
|
|
687
|
+
this.saveSession(session);
|
|
688
|
+
return true;
|
|
689
|
+
});
|
|
690
|
+
}
|
|
510
691
|
saveCurrentUser(traceId, user) {
|
|
511
692
|
const processedUser = this.encryptionService
|
|
512
693
|
? this.encryptionService.encryptData(user)
|