@oas-tools/oas-telemetry 0.7.0-alpha.4 → 0.7.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.
Files changed (112) hide show
  1. package/.env.example +6 -2
  2. package/README.md +35 -17
  3. package/dist/cjs/config/bootConfig.cjs +3 -1
  4. package/dist/cjs/config/config.cjs +7 -5
  5. package/dist/cjs/docs/openapi.yaml +1399 -0
  6. package/dist/cjs/routesManager.cjs +36 -48
  7. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.cjs +43 -13
  8. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.cjs +10 -2
  9. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.cjs +21 -16
  10. package/dist/cjs/telemetry/initializeTelemetry.cjs +39 -15
  11. package/dist/cjs/telemetry/telemetryConfigurator.cjs +6 -9
  12. package/dist/cjs/telemetry/telemetryRegistry.cjs +11 -8
  13. package/dist/cjs/tlm-ai/agent.cjs +54 -84
  14. package/dist/cjs/tlm-ai/aiController.cjs +69 -47
  15. package/dist/cjs/tlm-ai/aiRoutes.cjs +10 -3
  16. package/dist/cjs/tlm-ai/aiService.cjs +109 -0
  17. package/dist/cjs/tlm-ai/tools.cjs +30 -268
  18. package/dist/cjs/tlm-auth/authController.cjs +91 -26
  19. package/dist/cjs/tlm-auth/authMiddleware.cjs +20 -7
  20. package/dist/cjs/tlm-auth/authRoutes.cjs +3 -2
  21. package/dist/cjs/tlm-log/logController.cjs +30 -36
  22. package/dist/cjs/tlm-log/logRoutes.cjs +3 -2
  23. package/dist/cjs/tlm-metric/metricsController.cjs +15 -8
  24. package/dist/cjs/tlm-metric/metricsRoutes.cjs +2 -1
  25. package/dist/cjs/tlm-plugin/pluginController.cjs +11 -1
  26. package/dist/cjs/tlm-plugin/pluginProcess.cjs +4 -2
  27. package/dist/cjs/tlm-plugin/pluginService.cjs +3 -0
  28. package/dist/cjs/tlm-trace/traceController.cjs +16 -9
  29. package/dist/cjs/tlm-trace/traceRoutes.cjs +2 -1
  30. package/dist/cjs/tlm-util/utilController.cjs +23 -2
  31. package/dist/cjs/tlm-util/utilRoutes.cjs +44 -5
  32. package/dist/cjs/utils/logger.cjs +35 -13
  33. package/dist/esm/config/bootConfig.js +2 -0
  34. package/dist/esm/config/config.js +4 -2
  35. package/dist/esm/docs/openapi.yaml +1399 -0
  36. package/dist/esm/routesManager.js +37 -49
  37. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.js +32 -11
  38. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.js +10 -2
  39. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.js +20 -13
  40. package/dist/esm/telemetry/initializeTelemetry.js +22 -14
  41. package/dist/esm/telemetry/telemetryConfigurator.js +7 -10
  42. package/dist/esm/telemetry/telemetryRegistry.js +10 -7
  43. package/dist/esm/tlm-ai/agent.js +37 -78
  44. package/dist/esm/tlm-ai/aiController.js +56 -39
  45. package/dist/esm/tlm-ai/aiRoutes.js +11 -4
  46. package/dist/esm/tlm-ai/aiService.js +94 -0
  47. package/dist/esm/tlm-ai/tools.js +29 -255
  48. package/dist/esm/tlm-auth/authController.js +62 -20
  49. package/dist/esm/tlm-auth/authMiddleware.js +18 -9
  50. package/dist/esm/tlm-auth/authRoutes.js +4 -3
  51. package/dist/esm/tlm-log/logController.js +26 -28
  52. package/dist/esm/tlm-log/logRoutes.js +4 -3
  53. package/dist/esm/tlm-metric/metricsController.js +10 -6
  54. package/dist/esm/tlm-metric/metricsRoutes.js +3 -2
  55. package/dist/esm/tlm-plugin/pluginController.js +2 -1
  56. package/dist/esm/tlm-plugin/pluginProcess.js +4 -2
  57. package/dist/esm/tlm-plugin/pluginService.js +4 -0
  58. package/dist/esm/tlm-trace/traceController.js +11 -7
  59. package/dist/esm/tlm-trace/traceRoutes.js +3 -2
  60. package/dist/esm/tlm-util/utilController.js +22 -0
  61. package/dist/esm/tlm-util/utilRoutes.js +40 -5
  62. package/dist/esm/utils/logger.js +35 -12
  63. package/dist/types/config/bootConfig.d.ts +1 -0
  64. package/dist/types/config/config.d.ts +6 -3
  65. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.d.ts +7 -1
  66. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.d.ts +1 -0
  67. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.d.ts +1 -0
  68. package/dist/types/telemetry/telemetryRegistry.d.ts +22 -6
  69. package/dist/types/tlm-ai/agent.d.ts +2 -2
  70. package/dist/types/tlm-ai/aiController.d.ts +5 -4
  71. package/dist/types/tlm-ai/aiRoutes.d.ts +1 -1
  72. package/dist/types/tlm-ai/aiService.d.ts +38 -0
  73. package/dist/types/tlm-ai/tools.d.ts +5 -14
  74. package/dist/types/tlm-auth/authController.d.ts +2 -1
  75. package/dist/types/tlm-log/logController.d.ts +2 -2
  76. package/dist/types/tlm-metric/metricsController.d.ts +2 -1
  77. package/dist/types/tlm-plugin/pluginService.d.ts +2 -0
  78. package/dist/types/tlm-trace/traceController.d.ts +2 -1
  79. package/dist/types/tlm-util/utilController.d.ts +1 -0
  80. package/dist/types/utils/logger.d.ts +5 -5
  81. package/dist/ui/assets/ApiDocsPage-C_VVPPHa.js +16 -0
  82. package/dist/ui/assets/CollapsibleCard-B3KR_8mL.js +1 -0
  83. package/dist/ui/assets/DevToolsPage-OyZcDcmw.js +1 -0
  84. package/dist/ui/assets/LandingPage-CppFBA6K.js +6 -0
  85. package/dist/ui/assets/LogsPage-9Fq8GArS.js +26 -0
  86. package/dist/ui/assets/NotFoundPage-B3quk3P1.js +1 -0
  87. package/dist/ui/assets/PluginCreatePage-X_aCH4t4.js +50 -0
  88. package/dist/ui/assets/PluginPage-DMDSihrZ.js +27 -0
  89. package/dist/ui/assets/alert-jQ9HCPIf.js +1133 -0
  90. package/dist/ui/assets/badge-CNq0-mH5.js +1 -0
  91. package/dist/ui/assets/card-DFAwwhN3.js +1 -0
  92. package/dist/ui/assets/chevron-down-CPsvsmqj.js +6 -0
  93. package/dist/ui/assets/chevron-up-Df9jMo1X.js +6 -0
  94. package/dist/ui/assets/circle-alert-DOPQPvU8.js +6 -0
  95. package/dist/ui/assets/index-BkD6DijD.js +15 -0
  96. package/dist/ui/assets/index-CERGVYZK.js +292 -0
  97. package/dist/ui/assets/index-CSIPf9qw.css +1 -0
  98. package/dist/ui/assets/input-Dzvg_ZEZ.js +1 -0
  99. package/dist/ui/assets/label-DuVnkZ4q.js +1 -0
  100. package/dist/ui/assets/loader-circle-CrvlRy5o.js +6 -0
  101. package/dist/ui/assets/loginPage-qa4V-B70.js +6 -0
  102. package/dist/ui/assets/select-DhS8YUtJ.js +1 -0
  103. package/dist/ui/assets/separator-isK4chBP.js +6 -0
  104. package/dist/ui/assets/severityOptions-O38dSOfk.js +11 -0
  105. package/dist/ui/assets/switch-Z3mImG9n.js +1 -0
  106. package/dist/ui/assets/tabs-_77MUUQe.js +16 -0
  107. package/dist/ui/assets/upload-C1LT4Gkb.js +16 -0
  108. package/dist/ui/assets/utilService-DNyqzwj0.js +1 -0
  109. package/dist/ui/index.html +2 -2
  110. package/package.json +18 -7
  111. package/dist/ui/assets/index-BzIdRox6.js +0 -1733
  112. package/dist/ui/assets/index-CkoHzrrt.css +0 -1
@@ -1,40 +1,82 @@
1
1
  import jwt from 'jsonwebtoken';
2
2
  import logger from '../utils/logger.js';
3
+ function generateAccessToken(secret, expiresIn) {
4
+ return jwt.sign({ type: "access" }, secret, { expiresIn: Math.floor(expiresIn / 1000) });
5
+ }
6
+ function generateRefreshToken(secret, expiresIn) {
7
+ return jwt.sign({ type: "refresh" }, secret, { expiresIn: Math.floor(expiresIn / 1000) });
8
+ }
3
9
  export const getLogin = (oasTlmConfig) => (req, res) => {
10
+ if (!oasTlmConfig.auth.enabled) {
11
+ res.status(200).json({ valid: true, message: "Auth disabled" });
12
+ return;
13
+ }
4
14
  try {
5
15
  const { password } = req.body;
6
16
  if (password === oasTlmConfig.auth.password) {
7
- const options = {
8
- maxAge: oasTlmConfig.auth.apiKeyMaxAge,
17
+ const accessToken = generateAccessToken(oasTlmConfig.auth.jwtSecret, oasTlmConfig.auth.accessTokenMaxAge);
18
+ const refreshToken = generateRefreshToken(oasTlmConfig.auth.jwtSecret, oasTlmConfig.auth.refreshTokenMaxAge);
19
+ res.cookie("oas-tlm-access-token", accessToken, {
20
+ maxAge: oasTlmConfig.auth.accessTokenMaxAge,
21
+ httpOnly: true,
22
+ secure: process.env.NODE_ENV === "production",
23
+ sameSite: "lax",
24
+ path: "/"
25
+ });
26
+ res.cookie("oas-tlm-refresh-token", refreshToken, {
27
+ maxAge: oasTlmConfig.auth.refreshTokenMaxAge,
9
28
  httpOnly: true,
10
- secure: true,
11
- signed: false
12
- };
13
- const apiKey = jwt.sign({ password: oasTlmConfig.auth.password }, oasTlmConfig.auth.jwtSecret);
14
- res.cookie('apiKey', apiKey, options);
15
- res.status(200).json({ valid: true, message: 'API Key is valid' });
29
+ secure: process.env.NODE_ENV === "production",
30
+ sameSite: "lax",
31
+ path: oasTlmConfig.general.baseUrl + "/auth/refresh"
32
+ });
33
+ res.status(200).json({ valid: true, message: "Login successful" });
16
34
  return;
17
35
  }
18
- res.status(400).json({ valid: false, message: 'Invalid API Key' });
36
+ res.status(400).json({ valid: false, message: "Invalid password" });
19
37
  }
20
38
  catch (error) {
21
- logger.log("Error: ", error);
22
- res.status(500).json({ valid: false, message: 'Internal server error' });
39
+ logger.error("Login error: ", error);
40
+ res.status(500).json({ valid: false, message: "Internal server error" });
23
41
  }
24
42
  };
25
43
  export const getLogout = (oasTlmConfig) => (req, res) => {
26
- res.clearCookie('apiKey');
27
- res.redirect(oasTlmConfig.general.baseUrl + oasTlmConfig.general.uiPath + '/login');
44
+ if (!oasTlmConfig.auth.enabled) {
45
+ res.status(200).json({ valid: true, message: "Auth disabled" });
46
+ return;
47
+ }
48
+ res.clearCookie('oas-tlm-access-token', { path: '/' });
49
+ res.clearCookie('oas-tlm-refresh-token', { path: oasTlmConfig.general.baseUrl + '/auth/refresh' });
50
+ res.status(200).json({ valid: true, message: "Logged out" });
28
51
  };
29
- export const getCheck = (oasTlmConfig) => (req, res) => {
30
- if (!req.cookies.apiKey) {
31
- res.status(200).json({ valid: false, message: 'API Key is invalid' });
52
+ export const getRefresh = (oasTlmConfig) => (req, res) => {
53
+ if (!oasTlmConfig.auth.enabled) {
54
+ res.status(200).json({ valid: true, message: "Auth disabled" });
32
55
  return;
33
56
  }
34
- const decoded = jwt.verify(req.cookies.apiKey, oasTlmConfig.auth.jwtSecret);
35
- if (decoded.password === oasTlmConfig.auth.password) {
36
- res.status(200).json({ valid: true, message: 'API Key is valid' });
57
+ const refreshToken = req.cookies["oas-tlm-refresh-token"];
58
+ if (!refreshToken) {
59
+ res.status(401).json({ valid: false, message: "No refresh token" });
37
60
  return;
38
61
  }
39
- res.status(200).json({ valid: false, message: 'Invalid API Key' });
62
+ try {
63
+ const payload = jwt.verify(refreshToken, oasTlmConfig.auth.jwtSecret);
64
+ if (payload.type !== "refresh")
65
+ throw new Error("Invalid token type");
66
+ const accessToken = generateAccessToken(oasTlmConfig.auth.jwtSecret, oasTlmConfig.auth.accessTokenMaxAge);
67
+ res.cookie("oas-tlm-access-token", accessToken, {
68
+ maxAge: oasTlmConfig.auth.accessTokenMaxAge,
69
+ httpOnly: true,
70
+ secure: process.env.NODE_ENV === "production",
71
+ sameSite: "lax",
72
+ path: "/"
73
+ });
74
+ res.status(200).json({ valid: true, message: "Refreshed" });
75
+ }
76
+ catch {
77
+ res.status(401).json({ valid: false, message: "Invalid refresh token" });
78
+ }
79
+ };
80
+ export const getAuthEnabled = (oasTlmConfig) => (req, res) => {
81
+ res.status(200).json({ enabled: !!oasTlmConfig.auth.enabled });
40
82
  };
@@ -1,13 +1,22 @@
1
- import jwt from 'jsonwebtoken';
1
+ import jwt from "jsonwebtoken";
2
2
  export function getAuthMiddleware(oasTlmConfig) {
3
3
  return function authMiddleware(req, res, next) {
4
- const apiKey = req.cookies.apiKey;
5
- if (apiKey) {
6
- const decoded = jwt.verify(apiKey, oasTlmConfig.auth.jwtSecret);
7
- if (decoded.password === oasTlmConfig.auth.password) {
8
- return next();
9
- }
10
- }
11
- res.status(401).redirect(oasTlmConfig.general.baseUrl + oasTlmConfig.general.uiPath + '/login');
4
+ if (!oasTlmConfig.auth.enabled) {
5
+ return next();
6
+ }
7
+ const token = req.cookies["oas-tlm-access-token"];
8
+ if (!token) {
9
+ res.status(401).json({ valid: false, message: "No access token" });
10
+ return;
11
+ }
12
+ try {
13
+ const payload = jwt.verify(token, oasTlmConfig.auth.jwtSecret);
14
+ if (payload.type !== "access")
15
+ throw new Error("Invalid token type");
16
+ return next();
17
+ }
18
+ catch {
19
+ res.status(401).json({ valid: false, message: "Invalid access token" });
20
+ }
12
21
  };
13
22
  }
@@ -1,9 +1,10 @@
1
1
  import { Router } from 'express';
2
- import { getLogin, getLogout, getCheck } from './authController.js';
2
+ import { getLogin, getLogout, getRefresh, getAuthEnabled } from './authController.js';
3
3
  export const getAuthRoutes = (oasTlmConfig) => {
4
4
  const router = Router();
5
5
  router.post('/login', getLogin(oasTlmConfig));
6
- router.get('/logout', getLogout(oasTlmConfig));
7
- router.get('/check', getCheck(oasTlmConfig));
6
+ router.post('/refresh', getRefresh(oasTlmConfig));
7
+ router.post('/logout', getLogout(oasTlmConfig));
8
+ router.get('/enabled', getAuthEnabled(oasTlmConfig));
8
9
  return router;
9
10
  };
@@ -1,21 +1,13 @@
1
1
  import { inMemoryDbLogExporter } from '../telemetry/telemetryRegistry.js';
2
2
  import logger from '../utils/logger.js';
3
3
  import { convertRegexRecursively } from '../utils/regexUtils.js';
4
- export const listLogs = async (req, res) => {
5
- try {
6
- const logs = inMemoryDbLogExporter.getFinishedLogs();
7
- res.send({ logsCount: logs.length, logs: logs });
8
- }
9
- catch (err) {
10
- logger.error(err);
11
- res.status(500).send({ error: 'Failed to list log data' });
12
- }
13
- };
14
4
  export const findLogs = async (req, res) => {
15
- const body = req.body;
16
- const messageSearch = body?.textSearch || null; // Search term for MiniSearch
17
- const findQuery = body?.query || {}; // Query for NeDB
18
- logger.debug(`findLogs called with query: ${JSON.stringify(findQuery)} and search ${messageSearch}`, { depth: 3 });
5
+ const body = req.body || {};
6
+ const messageSearch = body.textSearch || null;
7
+ const findQuery = body.query || {};
8
+ const limit = parseInt(body.limit) || 50;
9
+ const sortOrder = body.sort || null;
10
+ logger.debug(`findLogs called with query: ${JSON.stringify(findQuery)} and search: ${messageSearch}`, { depth: 3 });
19
11
  let processedQuery;
20
12
  try {
21
13
  processedQuery = convertRegexRecursively(findQuery);
@@ -26,15 +18,17 @@ export const findLogs = async (req, res) => {
26
18
  return; // Exit if invalid regex was encountered
27
19
  }
28
20
  try {
29
- const results = await new Promise((resolve, reject) => {
30
- inMemoryDbLogExporter.find(processedQuery, messageSearch, (err, docs) => {
31
- if (err)
32
- return reject(err);
33
- resolve(docs);
34
- });
21
+ // Use findConfig object
22
+ const findConfig = {
23
+ query: processedQuery,
24
+ messageSearch,
25
+ limit,
26
+ sortOrder
27
+ };
28
+ const docs = await inMemoryDbLogExporter.find(findConfig);
29
+ res.send({
30
+ items: docs,
35
31
  });
36
- const typedResults = results;
37
- res.send({ logsCount: typedResults.length, logs: typedResults });
38
32
  }
39
33
  catch (err) {
40
34
  logger.error(err);
@@ -49,7 +43,7 @@ export const insertLogsToDb = async (req, res) => {
49
43
  const jsonContent = req.body.logs;
50
44
  const resetData = req.query.reset === 'true';
51
45
  if (!Array.isArray(jsonContent)) {
52
- res.status(400).send({ error: 'Invalid data format. Expected an array of JSON objects.' });
46
+ res.status(400).send({ error: 'Invalid data format.' });
53
47
  return;
54
48
  }
55
49
  const cleanedLogs = jsonContent.map((log) => {
@@ -92,12 +86,16 @@ export const statusLogs = (req, res) => {
92
86
  const isRunning = inMemoryDbLogExporter.isEnabled() || false;
93
87
  res.send({ active: isRunning });
94
88
  };
95
- export const setRetentionTimeLogs = (req, res) => {
96
- const retentionTime = req.body.retentionTime;
97
- if (typeof retentionTime !== 'number' || retentionTime <= 0) {
89
+ export const setLogRetentionTime = (req, res) => {
90
+ const retentionTimeInSeconds = req.body.retentionTimeInSeconds;
91
+ if (typeof retentionTimeInSeconds !== 'number' || retentionTimeInSeconds <= 0) {
98
92
  res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
99
93
  return;
100
94
  }
101
- inMemoryDbLogExporter.retentionTimeInSeconds = retentionTime;
102
- res.send({ message: `Retention time set to ${retentionTime} seconds.` });
95
+ inMemoryDbLogExporter.retentionTimeInSeconds = retentionTimeInSeconds;
96
+ res.send({ message: `Retention time set to ${retentionTimeInSeconds} seconds.` });
97
+ };
98
+ export const getLogRetentionTime = (req, res) => {
99
+ const retentionTimeInSeconds = inMemoryDbLogExporter.retentionTimeInSeconds || 0;
100
+ res.send({ retentionTimeInSeconds: retentionTimeInSeconds });
103
101
  };
@@ -1,5 +1,5 @@
1
1
  import { Router } from 'express';
2
- import { startLogs, stopLogs, statusLogs, resetLogs, listLogs, findLogs, insertLogsToDb, setRetentionTimeLogs } from './logController.js';
2
+ import { startLogs, stopLogs, statusLogs, resetLogs, findLogs, insertLogsToDb, setLogRetentionTime, getLogRetentionTime } from './logController.js';
3
3
  export const getLogRoutes = () => {
4
4
  const router = Router();
5
5
  // Logs Control
@@ -7,8 +7,9 @@ export const getLogRoutes = () => {
7
7
  router.post('/stop', stopLogs);
8
8
  router.get('/status', statusLogs);
9
9
  router.post('/reset', resetLogs);
10
- router.post('/retention-time', setRetentionTimeLogs);
11
- router.get('/', listLogs);
10
+ router.post('/retention-time', setLogRetentionTime);
11
+ router.get('/retention-time', getLogRetentionTime);
12
+ router.get('/', findLogs);
12
13
  router.post('/', insertLogsToDb);
13
14
  router.post('/find', findLogs);
14
15
  return router;
@@ -40,7 +40,7 @@ export const insertMetricsToDb = async (req, res) => {
40
40
  const jsonContent = req.body.metrics;
41
41
  const resetData = req.query.reset === 'true';
42
42
  if (!Array.isArray(jsonContent)) {
43
- res.status(400).send({ error: 'Invalid data format. Expected an array of JSON objects.' });
43
+ res.status(400).send({ error: 'Invalid data format.' });
44
44
  return;
45
45
  }
46
46
  const cleanedMetrics = jsonContent.map((metric) => {
@@ -83,12 +83,16 @@ export const statusMetrics = (req, res) => {
83
83
  const isRunning = inMemoryDbMetricExporter.isEnabled() || false;
84
84
  res.send({ active: isRunning });
85
85
  };
86
- export const setRetentionTimeMetrics = (req, res) => {
87
- const retentionTime = req.body.retentionTime;
88
- if (typeof retentionTime !== 'number' || retentionTime <= 0) {
86
+ export const setMetricRetentionTime = (req, res) => {
87
+ const retentionTimeInSeconds = req.body.retentionTimeInSeconds;
88
+ if (typeof retentionTimeInSeconds !== 'number' || retentionTimeInSeconds <= 0) {
89
89
  res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
90
90
  return;
91
91
  }
92
- inMemoryDbMetricExporter.retentionTimeInSeconds = retentionTime;
93
- res.send({ message: `Retention time set to ${retentionTime} seconds.` });
92
+ inMemoryDbMetricExporter.retentionTimeInSeconds = retentionTimeInSeconds;
93
+ res.send({ message: `Retention time set to ${retentionTimeInSeconds} seconds.` });
94
+ };
95
+ export const getMetricRetentionTime = (req, res) => {
96
+ const retentionTimeInSeconds = inMemoryDbMetricExporter.retentionTimeInSeconds || 0;
97
+ res.send({ retentionTimeInSeconds: retentionTimeInSeconds });
94
98
  };
@@ -1,5 +1,5 @@
1
1
  import { Router } from 'express';
2
- import { listMetrics, findMetrics, resetMetrics, insertMetricsToDb, startMetrics, stopMetrics, statusMetrics, setRetentionTimeMetrics } from './metricsController.js';
2
+ import { listMetrics, findMetrics, resetMetrics, insertMetricsToDb, startMetrics, stopMetrics, statusMetrics, setMetricRetentionTime, getMetricRetentionTime } from './metricsController.js';
3
3
  export const getMetricsRoutes = () => {
4
4
  const router = Router();
5
5
  // Metrics Control
@@ -7,7 +7,8 @@ export const getMetricsRoutes = () => {
7
7
  router.post('/stop', stopMetrics);
8
8
  router.get('/status', statusMetrics);
9
9
  router.post('/reset', resetMetrics);
10
- router.post('/retention-time', setRetentionTimeMetrics);
10
+ router.post('/retention-time', setMetricRetentionTime);
11
+ router.get('/retention-time', getMetricRetentionTime);
11
12
  router.get('/', listMetrics);
12
13
  router.post('/', insertMetricsToDb);
13
14
  router.post('/find', findMetrics);
@@ -5,7 +5,8 @@ import logger from "../utils/logger.js";
5
5
  import { pluginService } from "./pluginService.js";
6
6
  import { fileURLToPath } from "url";
7
7
  export const listPlugins = (req, res) => {
8
- const plugins = pluginService.getPlugins();
8
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
9
+ const plugins = pluginService.getPlugins().map(({ process, ...rest }) => rest);
9
10
  res.send({
10
11
  pluginsCount: plugins.length,
11
12
  plugins,
@@ -14,12 +14,14 @@ process.on("message", async (msg) => {
14
14
  if (pluginResource.install && Array.isArray(pluginResource.install.dependencies) && pluginResource.install.dependencies.length > 0) {
15
15
  log("Installing dependencies for plugin: " + pluginResource.name);
16
16
  const dependenciesStatus = await installDependencies(pluginResource.install);
17
+ console.dir(dependenciesStatus);
17
18
  if (!dependenciesStatus.success) {
19
+ const detailsFailed = dependenciesStatus.details.filter(detail => detail.success === false);
18
20
  if (pluginResource.install.ignoreErrors === true) {
19
- log(`Warning: Error installing dependencies: ${JSON.stringify(dependenciesStatus.details)}`);
21
+ log(`Warning: Error installing dependencies: ${JSON.stringify(detailsFailed)}. Continuing as ignoreErrors is true.`);
20
22
  }
21
23
  else {
22
- process.send?.({ event: "error", error: `Error installing dependencies: ${JSON.stringify(dependenciesStatus.details)}` });
24
+ process.send?.({ event: "error", error: `Error installing dependencies: ${JSON.stringify(detailsFailed)}` });
23
25
  return;
24
26
  }
25
27
  }
@@ -2,6 +2,7 @@ import logger from "../utils/logger.js";
2
2
  class PluginService {
3
3
  constructor() {
4
4
  this.plugins = [];
5
+ this.enabled = false;
5
6
  }
6
7
  getPlugins() {
7
8
  return this.plugins;
@@ -29,6 +30,8 @@ class PluginService {
29
30
  this.plugins = this.plugins.filter((p) => p.id !== pluginId);
30
31
  }
31
32
  broadcastToPlugins(type, payload) {
33
+ if (!this.enabled)
34
+ return;
32
35
  this.plugins.forEach((plugin, i) => {
33
36
  if (!plugin.active)
34
37
  return;
@@ -65,6 +68,7 @@ class PluginService {
65
68
  }
66
69
  /**
67
70
  * Broadcast a new trace to all active plugins
71
+ * TODO: rename to span (trace is the whole trace, span is a single unit of work within a trace)
68
72
  */
69
73
  broadcastTrace(trace) {
70
74
  this.broadcastToPlugins("newTrace", trace);
@@ -41,7 +41,7 @@ export const findTraces = (req, res) => {
41
41
  inMemoryDbSpanExporter.find(processedQuery, (err, docs) => {
42
42
  if (err) {
43
43
  console.error(err);
44
- res.status(404).send({ spansCount: 0, spans: [], error: err.message });
44
+ res.status(400).send({ spansCount: 0, spans: [], error: err.message });
45
45
  return; // Exit the function to prevent further execution
46
46
  }
47
47
  const spans = docs;
@@ -52,7 +52,7 @@ export const insertTracesToDb = async (req, res) => {
52
52
  const jsonContent = req.body.spans;
53
53
  const resetData = req.query.reset === 'true';
54
54
  if (!Array.isArray(jsonContent)) {
55
- res.status(400).send({ error: 'Invalid data format. Expected an array of JSON objects.' });
55
+ res.status(400).send({ error: 'Invalid data format.' });
56
56
  return;
57
57
  }
58
58
  const cleanedTraces = jsonContent.map((trace) => {
@@ -83,12 +83,16 @@ export const insertTracesToDb = async (req, res) => {
83
83
  res.status(500).send({ error: 'Failed to reset and insert data', details: err.message });
84
84
  }
85
85
  };
86
- export const setRetentionTimeTraces = (req, res) => {
87
- const retentionTime = req.body.retentionTime;
88
- if (typeof retentionTime !== 'number' || retentionTime <= 0) {
86
+ export const setTraceRetentionTime = (req, res) => {
87
+ const retentionTimeInSeconds = req.body.retentionTimeInSeconds;
88
+ if (typeof retentionTimeInSeconds !== 'number' || retentionTimeInSeconds <= 0) {
89
89
  res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
90
90
  return;
91
91
  }
92
- inMemoryDbSpanExporter.retentionTimeInSeconds = retentionTime;
93
- res.send({ message: `Retention time set to ${retentionTime} seconds.` });
92
+ inMemoryDbSpanExporter.retentionTimeInSeconds = retentionTimeInSeconds;
93
+ res.send({ message: `Retention time set to ${retentionTimeInSeconds} seconds.` });
94
+ };
95
+ export const getTraceRetentionTime = (req, res) => {
96
+ const retentionTimeInSeconds = inMemoryDbSpanExporter.retentionTimeInSeconds || 0;
97
+ res.send({ retentionTimeInSeconds: retentionTimeInSeconds });
94
98
  };
@@ -1,5 +1,5 @@
1
1
  import { Router } from 'express';
2
- import { startTraces, stopTraces, statusTraces, resetTraces, listTraces, findTraces, insertTracesToDb, setRetentionTimeTraces } from './traceController.js';
2
+ import { startTraces, stopTraces, statusTraces, resetTraces, listTraces, findTraces, insertTracesToDb, setTraceRetentionTime, getTraceRetentionTime } from './traceController.js';
3
3
  export const getTraceRoutes = () => {
4
4
  const router = Router();
5
5
  // Telemetry Control
@@ -10,7 +10,8 @@ export const getTraceRoutes = () => {
10
10
  router.get('/', listTraces);
11
11
  router.post('/', insertTracesToDb);
12
12
  router.post('/find', findTraces);
13
- router.post('/retention-time', setRetentionTimeTraces);
13
+ router.post('/retention-time', setTraceRetentionTime);
14
+ router.get('/retention-time', getTraceRetentionTime);
14
15
  return router;
15
16
  };
16
17
  export default getTraceRoutes;
@@ -2,6 +2,7 @@ import { readFileSync } from 'fs';
2
2
  import path from 'path';
3
3
  import yaml from 'js-yaml';
4
4
  import v8 from 'node:v8';
5
+ import { fileURLToPath } from 'node:url';
5
6
  export const specLoader = (_req, res, oasTlmConfig) => {
6
7
  if (oasTlmConfig.general.specFileName) {
7
8
  try {
@@ -54,3 +55,24 @@ export const heapStats = (req, res) => {
54
55
  roundedHeapStats['units'] = 'MB';
55
56
  res.send(roundedHeapStats);
56
57
  };
58
+ const isCjs = typeof __filename !== "undefined" && typeof __dirname !== "undefined";
59
+ const __filenameUniversal = isCjs
60
+ ? __filename
61
+ : fileURLToPath(import.meta.url);
62
+ const __dirnameUniversal = isCjs
63
+ ? __dirname
64
+ : path.dirname(__filenameUniversal);
65
+ export const getOasTelemetrySpec = (_req, res) => {
66
+ try {
67
+ const specPath = path.join(__dirnameUniversal, '../docs/openapi.yaml');
68
+ const data = readFileSync(specPath, { encoding: 'utf8', flag: 'r' });
69
+ let json = data;
70
+ json = JSON.stringify(yaml.load(data), null, 2);
71
+ res.setHeader('Content-Type', 'application/json');
72
+ res.send(json);
73
+ }
74
+ catch (e) {
75
+ console.error(`ERROR loading OAS Telemetry OpenAPI spec file: ${e}`);
76
+ res.status(500).send(`ERROR loading OAS Telemetry OpenAPI spec file: ${e}`);
77
+ }
78
+ };
@@ -1,15 +1,33 @@
1
1
  import { Router } from 'express';
2
- import { specLoader, heapStats } from './utilController.js';
2
+ import { specLoader, heapStats, getOasTelemetrySpec } from './utilController.js';
3
3
  export const getUtilsRoutes = (oasTlmConfig) => {
4
4
  const router = Router();
5
5
  router.get('/spec', (req, res) => specLoader(req, res, oasTlmConfig));
6
+ router.get('/oas-telemetry-spec', (req, res) => getOasTelemetrySpec(req, res));
6
7
  router.get('/heapStats', heapStats);
7
- router.get('/generateLog', (req, res) => {
8
- const log = req.query.log || 'Default log message';
9
- console.log(log);
8
+ //This route is NOT ignored by the spanExporter (includes "generate")
9
+ router.post('/generate-log', async (req, res) => {
10
+ const log = req.body.log || 'Default log message';
11
+ const repeat = parseInt(req.body.repeat) || 1;
12
+ const method = req.body.method?.toLowerCase() || 'log';
13
+ if (!['log', 'warn', 'error', 'info', 'debug'].includes(method)) {
14
+ res.status(400).send({ error: 'Invalid method. Use log, warn, error, info, or debug.' });
15
+ return;
16
+ }
10
17
  res.send({ message: 'Log generated', log: log });
18
+ for (let i = 0; i < repeat; i++) {
19
+ console[method](log);
20
+ await new Promise(resolve => setTimeout(resolve, 50)); // Slight delay between logs
21
+ }
11
22
  });
12
- router.get('/wait/:seconds?', async (req, res) => {
23
+ // This route is NOT ignored by the spanExporter (includes "generate")
24
+ router.post('/generate-mock-logs', async (req, res) => {
25
+ const count = parseInt(req.body.count) || 50;
26
+ generateMockLogs(count);
27
+ res.send({ message: 'Started generating mock logs' });
28
+ });
29
+ // This route is NOT ignored by the spanExporter
30
+ router.get('/generate-wait/:seconds?', async (req, res) => {
13
31
  const seconds = parseInt(req.params.seconds ?? "1", 10);
14
32
  const waitTime = isNaN(seconds) ? 1 : seconds;
15
33
  await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
@@ -17,3 +35,20 @@ export const getUtilsRoutes = (oasTlmConfig) => {
17
35
  });
18
36
  return router;
19
37
  };
38
+ const generateMockLogs = async (count) => {
39
+ const methodMessages = {
40
+ log: ['User logged in', 'Data fetched successfully'],
41
+ warn: ['Warning: Disk space low', 'Warning: High memory usage'],
42
+ error: ['Error connecting to database', 'Error: Invalid credentials'],
43
+ info: ['Info: Scheduled job started', 'Info: Configuration loaded'],
44
+ debug: ['Debugging mode enabled', 'Debug: Variable x = 42'],
45
+ };
46
+ const methods = Object.keys(methodMessages);
47
+ for (let i = 0; i < count; i++) {
48
+ await new Promise(resolve => setTimeout(resolve, 50)); // Slight delay between logs
49
+ const method = methods[Math.floor(Math.random() * methods.length)];
50
+ const messages = methodMessages[method];
51
+ const message = messages[Math.floor(Math.random() * messages.length)];
52
+ console[method](`[${new Date().toISOString()}][MOCK LOG][${method.toUpperCase()}] -${i + 1}- ${message}`);
53
+ }
54
+ };
@@ -1,18 +1,41 @@
1
1
  import { bootEnvVariables } from "../config/bootConfig.js";
2
+ import { originalConsoleMethods } from "../telemetry/telemetryRegistry.js";
2
3
  const LOG_LEVELS = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'NONE'];
3
- const currentLogLevel = (bootEnvVariables.OASTLM_BOOT_LOG_LEVEL || 'INFO').toUpperCase();
4
- const serviceName = 'OAS-Telemetry';
5
- function log(level, ...messages) {
6
- if (LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(currentLogLevel)) {
7
- const timestamp = new Date().toISOString();
8
- console.log(`${timestamp} [${serviceName}] [${level}]:`, ...messages);
9
- }
4
+ const currentLogLevel = (bootEnvVariables.OASTLM_BOOT_LOG_LEVEL).toUpperCase();
5
+ const serviceName = 'OAS-TLM-@-' + bootEnvVariables.OASTLM_BOOT_SERVICE_NAME;
6
+ function shouldLog(level) {
7
+ return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(currentLogLevel);
10
8
  }
11
9
  export default {
12
- debug: (...messages) => log('DEBUG', ...messages),
13
- info: (...messages) => log('INFO', ...messages),
14
- log: (...messages) => log('INFO', ...messages), // Alias for info
15
- warn: (...messages) => log('WARN', ...messages),
16
- error: (...messages) => log('ERROR', ...messages),
10
+ debug: (...messages) => {
11
+ if (shouldLog('DEBUG')) {
12
+ const timestamp = new Date().toISOString();
13
+ originalConsoleMethods.debug(`${timestamp} [${serviceName}] [DEBUG]:`, ...messages);
14
+ }
15
+ },
16
+ info: (...messages) => {
17
+ if (shouldLog('INFO')) {
18
+ const timestamp = new Date().toISOString();
19
+ originalConsoleMethods.info(`${timestamp} [${serviceName}] [INFO]:`, ...messages);
20
+ }
21
+ },
22
+ log: (...messages) => {
23
+ if (shouldLog('INFO')) {
24
+ const timestamp = new Date().toISOString();
25
+ originalConsoleMethods.log(`${timestamp} [${serviceName}] [INFO]:`, ...messages);
26
+ }
27
+ },
28
+ warn: (...messages) => {
29
+ if (shouldLog('WARN')) {
30
+ const timestamp = new Date().toISOString();
31
+ originalConsoleMethods.warn(`${timestamp} [${serviceName}] [WARN]:`, ...messages);
32
+ }
33
+ },
34
+ error: (...messages) => {
35
+ if (shouldLog('ERROR')) {
36
+ const timestamp = new Date().toISOString();
37
+ originalConsoleMethods.error(`${timestamp} [${serviceName}] [ERROR]:`, ...messages);
38
+ }
39
+ },
17
40
  currentLogLevel
18
41
  };
@@ -2,4 +2,5 @@ export declare const bootEnvVariables: {
2
2
  OASTLM_BOOT_ENV: string;
3
3
  OASTLM_BOOT_MODULE_DISABLED: boolean;
4
4
  OASTLM_BOOT_LOG_LEVEL: string;
5
+ OASTLM_BOOT_SERVICE_NAME: string;
5
6
  };
@@ -13,9 +13,10 @@ export declare const defaultConfig: {
13
13
  };
14
14
  auth: {
15
15
  enabled: boolean;
16
- apiKeyMaxAge: number;
17
16
  password: string;
18
17
  jwtSecret: string;
18
+ accessTokenMaxAge: number;
19
+ refreshTokenMaxAge: number;
19
20
  };
20
21
  ai: {
21
22
  openAIKey: string | null;
@@ -70,9 +71,10 @@ export declare const getConfig: (userConfig?: UserConfig, fallbackConfig?: OasTl
70
71
  };
71
72
  auth: {
72
73
  enabled: boolean;
73
- apiKeyMaxAge: number;
74
74
  password: string;
75
75
  jwtSecret: string;
76
+ accessTokenMaxAge: number;
77
+ refreshTokenMaxAge: number;
76
78
  };
77
79
  ai: {
78
80
  openAIKey: string | null;
@@ -126,9 +128,10 @@ export declare const getConfig: (userConfig?: UserConfig, fallbackConfig?: OasTl
126
128
  } | undefined;
127
129
  auth?: {
128
130
  enabled?: boolean | undefined;
129
- apiKeyMaxAge?: number | undefined;
130
131
  password?: string | undefined;
131
132
  jwtSecret?: string | undefined;
133
+ accessTokenMaxAge?: number | undefined;
134
+ refreshTokenMaxAge?: number | undefined;
132
135
  } | undefined;
133
136
  ai?: {
134
137
  openAIKey?: string | null | undefined;
@@ -17,7 +17,12 @@ export declare class InMemoryDbLogExporter extends Enabler implements LogRecordE
17
17
  * Shutdown the exporter.
18
18
  */
19
19
  shutdown(): Promise<void>;
20
- find(query: any, messageSearch: string | null, callback: (err: any, docs: any) => void): void;
20
+ find(findConfig: {
21
+ query: any;
22
+ messageSearch: string | null;
23
+ limit: number;
24
+ sortOrder?: any;
25
+ }): Promise<any[]>;
21
26
  insert(data: any[], callback: (err: any, newDocs: any[]) => void): void;
22
27
  getFinishedLogs(): any[];
23
28
  /**
@@ -28,6 +33,7 @@ export declare class InMemoryDbLogExporter extends Enabler implements LogRecordE
28
33
  */
29
34
  private _formatLogRecord;
30
35
  set retentionTimeInSeconds(retentionTimeInSeconds: number);
36
+ get retentionTimeInSeconds(): number;
31
37
  private _insertLogs;
32
38
  private _startCleanupJob;
33
39
  }
@@ -17,5 +17,6 @@ export declare class InMemoryDbMetricExporter extends Enabler implements PushMet
17
17
  */
18
18
  insert(metrics: any[], callback: (err: any, newDocs: any[]) => void): void;
19
19
  set retentionTimeInSeconds(retentionTimeInSeconds: number);
20
+ get retentionTimeInSeconds(): number;
20
21
  private _startCleanupJob;
21
22
  }