@manojkmfsi/monodog 1.0.24 → 1.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +12 -0
- package/dist/config/swagger-config.js +345 -0
- package/dist/constants/index.js +26 -0
- package/dist/constants/middleware.js +71 -0
- package/dist/constants/port.js +20 -0
- package/dist/constants/security.js +67 -0
- package/dist/middleware/dashboard-startup.js +20 -21
- package/dist/middleware/error-handler.js +3 -10
- package/dist/middleware/index.js +4 -2
- package/dist/middleware/logger.js +63 -0
- package/dist/middleware/security.js +11 -10
- package/dist/middleware/server-startup.js +32 -25
- package/dist/middleware/swagger-middleware.js +54 -0
- package/dist/routes/health-routes.js +1 -1
- package/dist/routes/package-routes.js +1 -1
- package/dist/serve.js +15 -2
- package/dist/services/health-service.js +84 -64
- package/dist/services/package-service.js +23 -1
- package/monodog-dashboard/dist/assets/{index-746f6c13.js → index-45e19f29.js} +1 -1
- package/monodog-dashboard/dist/index.html +1 -1
- package/package.json +14 -4
- package/prisma/schema/commit.prisma +11 -0
- package/prisma/schema/dependency-info.prisma +12 -0
- package/prisma/schema/health-status.prisma +14 -0
- package/prisma/schema/package-health.prisma +15 -0
- package/prisma/schema/package.prisma +21 -0
- package/prisma/schema/schema.prisma +15 -0
- package/src/config/swagger-config.ts +344 -0
- package/src/constants/index.ts +13 -0
- package/src/constants/middleware.ts +83 -0
- package/src/constants/port.ts +20 -0
- package/src/constants/security.ts +78 -0
- package/src/middleware/dashboard-startup.ts +35 -24
- package/src/middleware/error-handler.ts +2 -15
- package/src/middleware/index.ts +3 -1
- package/src/middleware/logger.ts +58 -0
- package/src/middleware/security.ts +19 -10
- package/src/middleware/server-startup.ts +43 -30
- package/src/middleware/swagger-middleware.ts +57 -0
- package/src/routes/health-routes.ts +1 -1
- package/src/routes/package-routes.ts +1 -1
- package/src/serve.ts +19 -3
- package/src/services/health-service.ts +103 -79
- package/src/services/package-service.ts +27 -1
- package/src/types/swagger-jsdoc.d.ts +15 -0
- package/prisma/schema.prisma +0 -116
- /package/prisma/migrations/{20251219074511_create_unique_composite_key_for_commits → 20251219090102_composite_key_for_table_commits}/migration.sql +0 -0
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
* Error handling middleware for Express
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.notFoundHandler = exports.errorHandler = void 0;
|
|
7
|
+
const logger_1 = require("./logger");
|
|
7
8
|
/**
|
|
8
9
|
* Global error handler middleware
|
|
9
10
|
* Must be registered last in the middleware chain
|
|
10
11
|
*/
|
|
11
12
|
const errorHandler = (err, req, res, _next) => {
|
|
12
13
|
const status = err.status || err.statusCode || 500;
|
|
13
|
-
|
|
14
|
+
logger_1.AppLogger.error('Request error occurred', {
|
|
14
15
|
status,
|
|
15
16
|
method: req.method,
|
|
16
17
|
path: req.path,
|
|
@@ -33,11 +34,3 @@ const notFoundHandler = (_req, res) => {
|
|
|
33
34
|
});
|
|
34
35
|
};
|
|
35
36
|
exports.notFoundHandler = notFoundHandler;
|
|
36
|
-
/**
|
|
37
|
-
* Request logging middleware
|
|
38
|
-
*/
|
|
39
|
-
const requestLogger = (req, _res, next) => {
|
|
40
|
-
console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
|
|
41
|
-
next();
|
|
42
|
-
};
|
|
43
|
-
exports.requestLogger = requestLogger;
|
package/dist/middleware/index.js
CHANGED
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
* Middleware exports
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.serveDashboard = exports.startServer = exports.buildDashboardUrl = exports.buildApiUrl = exports.createTimeoutMiddleware = exports.createDashboardCorsMiddleware = exports.createApiCorsMiddleware = exports.createHelmetMiddleware = exports.
|
|
6
|
+
exports.AppLogger = exports.httpLogger = exports.serveDashboard = exports.startServer = exports.buildDashboardUrl = exports.buildApiUrl = exports.createTimeoutMiddleware = exports.createDashboardCorsMiddleware = exports.createApiCorsMiddleware = exports.createHelmetMiddleware = exports.notFoundHandler = exports.errorHandler = void 0;
|
|
7
7
|
var error_handler_1 = require("./error-handler");
|
|
8
8
|
Object.defineProperty(exports, "errorHandler", { enumerable: true, get: function () { return error_handler_1.errorHandler; } });
|
|
9
9
|
Object.defineProperty(exports, "notFoundHandler", { enumerable: true, get: function () { return error_handler_1.notFoundHandler; } });
|
|
10
|
-
Object.defineProperty(exports, "requestLogger", { enumerable: true, get: function () { return error_handler_1.requestLogger; } });
|
|
11
10
|
var security_1 = require("./security");
|
|
12
11
|
Object.defineProperty(exports, "createHelmetMiddleware", { enumerable: true, get: function () { return security_1.createHelmetMiddleware; } });
|
|
13
12
|
Object.defineProperty(exports, "createApiCorsMiddleware", { enumerable: true, get: function () { return security_1.createApiCorsMiddleware; } });
|
|
@@ -19,3 +18,6 @@ var server_startup_1 = require("./server-startup");
|
|
|
19
18
|
Object.defineProperty(exports, "startServer", { enumerable: true, get: function () { return server_startup_1.startServer; } });
|
|
20
19
|
var dashboard_startup_1 = require("./dashboard-startup");
|
|
21
20
|
Object.defineProperty(exports, "serveDashboard", { enumerable: true, get: function () { return dashboard_startup_1.serveDashboard; } });
|
|
21
|
+
var logger_1 = require("./logger");
|
|
22
|
+
Object.defineProperty(exports, "httpLogger", { enumerable: true, get: function () { return logger_1.httpLogger; } });
|
|
23
|
+
Object.defineProperty(exports, "AppLogger", { enumerable: true, get: function () { return logger_1.AppLogger; } });
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Logger configuration using Morgan
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AppLogger = exports.httpLogger = void 0;
|
|
10
|
+
const morgan_1 = __importDefault(require("morgan"));
|
|
11
|
+
/**
|
|
12
|
+
* HTTP request logger middleware using Morgan
|
|
13
|
+
*/
|
|
14
|
+
exports.httpLogger = (0, morgan_1.default)('dev');
|
|
15
|
+
/**
|
|
16
|
+
* Application logger for non-HTTP events
|
|
17
|
+
*/
|
|
18
|
+
class AppLogger {
|
|
19
|
+
static info(message, data) {
|
|
20
|
+
if (process.env.LOG_LEVEL == 'info' || process.env.LOG_LEVEL == 'debug') {
|
|
21
|
+
if (data) {
|
|
22
|
+
console.log(`${this.prefix} [INFO]`, message, JSON.stringify(data, null, 2));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
console.log(`${this.prefix} [INFO]`, message);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
static error(message, error) {
|
|
30
|
+
if (error instanceof Error) {
|
|
31
|
+
console.error(`${this.prefix} [ERROR]`, message, {
|
|
32
|
+
message: error.message,
|
|
33
|
+
stack: error.stack,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else if (error) {
|
|
37
|
+
console.error(`${this.prefix} [ERROR]`, message, error);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.error(`${this.prefix} [ERROR]`, message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
static warn(message, data) {
|
|
44
|
+
if (data) {
|
|
45
|
+
console.warn(`${this.prefix} [WARN]`, message, JSON.stringify(data, null, 2));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.warn(`${this.prefix} [WARN]`, message);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
static debug(message, data) {
|
|
52
|
+
if (process.env.LOG_LEVEL == 'debug') {
|
|
53
|
+
if (data) {
|
|
54
|
+
console.log(`${this.prefix} [DEBUG]`, message, JSON.stringify(data, null, 2));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.log(`${this.prefix} [DEBUG]`, message);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.AppLogger = AppLogger;
|
|
63
|
+
AppLogger.prefix = '[APP]';
|
|
@@ -14,6 +14,7 @@ exports.buildApiUrl = buildApiUrl;
|
|
|
14
14
|
exports.buildDashboardUrl = buildDashboardUrl;
|
|
15
15
|
const helmet_1 = __importDefault(require("helmet"));
|
|
16
16
|
const cors_1 = __importDefault(require("cors"));
|
|
17
|
+
const constants_1 = require("../constants");
|
|
17
18
|
/**
|
|
18
19
|
* Create Helmet security middleware with Content Security Policy
|
|
19
20
|
*/
|
|
@@ -34,10 +35,10 @@ function createHelmetMiddleware(apiUrl) {
|
|
|
34
35
|
*/
|
|
35
36
|
function createApiCorsMiddleware(dashboardUrl) {
|
|
36
37
|
const corsOptions = {
|
|
37
|
-
origin:
|
|
38
|
+
origin: dashboardUrl,
|
|
38
39
|
credentials: true,
|
|
39
|
-
methods: [
|
|
40
|
-
allowedHeaders: [
|
|
40
|
+
methods: [...constants_1.CORS_API_METHODS],
|
|
41
|
+
allowedHeaders: [...constants_1.CORS_ALLOWED_HEADERS],
|
|
41
42
|
};
|
|
42
43
|
return (0, cors_1.default)(corsOptions);
|
|
43
44
|
}
|
|
@@ -55,8 +56,8 @@ function createDashboardCorsMiddleware() {
|
|
|
55
56
|
*/
|
|
56
57
|
function createTimeoutMiddleware() {
|
|
57
58
|
return (req, res, next) => {
|
|
58
|
-
req.setTimeout(
|
|
59
|
-
res.setTimeout(
|
|
59
|
+
req.setTimeout(constants_1.REQUEST_TIMEOUT);
|
|
60
|
+
res.setTimeout(constants_1.RESPONSE_TIMEOUT);
|
|
60
61
|
next();
|
|
61
62
|
};
|
|
62
63
|
}
|
|
@@ -64,15 +65,15 @@ function createTimeoutMiddleware() {
|
|
|
64
65
|
* Build API URL based on config
|
|
65
66
|
*/
|
|
66
67
|
function buildApiUrl(host, port) {
|
|
67
|
-
const apiHost = host ===
|
|
68
|
-
return
|
|
68
|
+
const apiHost = host === constants_1.WILDCARD_ADDRESS ? constants_1.DEFAULT_LOCALHOST : host;
|
|
69
|
+
return `${constants_1.HTTP_PROTOCOL}${apiHost}:${port}`;
|
|
69
70
|
}
|
|
70
71
|
/**
|
|
71
72
|
* Build dashboard URL based on config
|
|
72
73
|
*/
|
|
73
74
|
function buildDashboardUrl(config) {
|
|
74
|
-
const dashboardHost = config.dashboard.host ===
|
|
75
|
-
?
|
|
75
|
+
const dashboardHost = config.dashboard.host === constants_1.WILDCARD_ADDRESS
|
|
76
|
+
? constants_1.DEFAULT_LOCALHOST
|
|
76
77
|
: config.dashboard.host;
|
|
77
|
-
return
|
|
78
|
+
return `${constants_1.HTTP_PROTOCOL}${dashboardHost}:${config.dashboard.port}`;
|
|
78
79
|
}
|
|
@@ -9,23 +9,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.startServer = startServer;
|
|
10
10
|
const express_1 = __importDefault(require("express"));
|
|
11
11
|
const body_parser_1 = require("body-parser");
|
|
12
|
+
const logger_1 = require("./logger");
|
|
12
13
|
const config_loader_1 = require("../config-loader");
|
|
13
14
|
const error_handler_1 = require("./error-handler");
|
|
14
15
|
const security_1 = require("./security");
|
|
16
|
+
const swagger_middleware_1 = require("./swagger-middleware");
|
|
15
17
|
const package_routes_1 = __importDefault(require("../routes/package-routes"));
|
|
16
18
|
const commit_routes_1 = __importDefault(require("../routes/commit-routes"));
|
|
17
19
|
const health_routes_1 = __importDefault(require("../routes/health-routes"));
|
|
18
20
|
const config_routes_1 = __importDefault(require("../routes/config-routes"));
|
|
19
|
-
|
|
20
|
-
const PORT_MIN = 1024;
|
|
21
|
-
const PORT_MAX = 65535;
|
|
21
|
+
const constants_1 = require("../constants");
|
|
22
22
|
/**
|
|
23
23
|
* Validate port number
|
|
24
24
|
*/
|
|
25
25
|
function validatePort(port) {
|
|
26
26
|
const portNum = typeof port === 'string' ? parseInt(port, 10) : port;
|
|
27
|
-
if (isNaN(portNum) || portNum < PORT_MIN || portNum > PORT_MAX) {
|
|
28
|
-
throw new Error(
|
|
27
|
+
if (isNaN(portNum) || portNum < constants_1.PORT_MIN || portNum > constants_1.PORT_MAX) {
|
|
28
|
+
throw new Error((0, constants_1.PORT_VALIDATION_ERROR_MESSAGE)(constants_1.PORT_MIN, constants_1.PORT_MAX));
|
|
29
29
|
}
|
|
30
30
|
return portNum;
|
|
31
31
|
}
|
|
@@ -44,9 +44,11 @@ function createApp(rootPath) {
|
|
|
44
44
|
app.use((0, security_1.createHelmetMiddleware)(apiUrl));
|
|
45
45
|
app.use((0, security_1.createApiCorsMiddleware)(dashboardUrl));
|
|
46
46
|
// Body parser
|
|
47
|
-
app.use((0, body_parser_1.json)({ limit:
|
|
48
|
-
//
|
|
49
|
-
app.use(
|
|
47
|
+
app.use((0, body_parser_1.json)({ limit: constants_1.BODY_PARSER_LIMIT }));
|
|
48
|
+
// HTTP request logging with Morgan
|
|
49
|
+
app.use(logger_1.httpLogger);
|
|
50
|
+
// Setup Swagger documentation
|
|
51
|
+
(0, swagger_middleware_1.setupSwaggerDocs)(app);
|
|
50
52
|
// Routes
|
|
51
53
|
app.use('/api/packages', package_routes_1.default);
|
|
52
54
|
app.use('/api/commits/', commit_routes_1.default);
|
|
@@ -66,46 +68,51 @@ function startServer(rootPath) {
|
|
|
66
68
|
const port = config_loader_1.appConfig.server.port;
|
|
67
69
|
const host = config_loader_1.appConfig.server.host;
|
|
68
70
|
const validatedPort = validatePort(port);
|
|
71
|
+
logger_1.AppLogger.info(`Starting Monodog API server...`);
|
|
72
|
+
logger_1.AppLogger.info(`Analyzing monorepo at root: ${rootPath}`);
|
|
69
73
|
const app = createApp(rootPath);
|
|
70
74
|
const server = app.listen(validatedPort, host, () => {
|
|
71
|
-
console.log(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
console.log((0, constants_1.SUCCESS_SERVER_START)(host, validatedPort));
|
|
76
|
+
logger_1.AppLogger.info('API endpoints available:', {
|
|
77
|
+
endpoints: [
|
|
78
|
+
'GET /api/health',
|
|
79
|
+
'GET /api/packages/refresh',
|
|
80
|
+
'GET /api/packages',
|
|
81
|
+
'GET /api/packages/:name',
|
|
82
|
+
'PUT /api/packages/update-config',
|
|
83
|
+
'GET /api/commits/:packagePath',
|
|
84
|
+
'GET /api/health/packages',
|
|
85
|
+
'PUT /api/config/files/:id',
|
|
86
|
+
'GET /api/config/files',
|
|
87
|
+
],
|
|
88
|
+
});
|
|
82
89
|
});
|
|
83
90
|
server.on('error', (err) => {
|
|
84
91
|
if (err.code === 'EADDRINUSE') {
|
|
85
|
-
|
|
92
|
+
logger_1.AppLogger.error((0, constants_1.ERROR_PORT_IN_USE)(validatedPort), err);
|
|
86
93
|
process.exit(1);
|
|
87
94
|
}
|
|
88
95
|
else if (err.code === 'EACCES') {
|
|
89
|
-
|
|
96
|
+
logger_1.AppLogger.error((0, constants_1.ERROR_PERMISSION_DENIED)(validatedPort), err);
|
|
90
97
|
process.exit(1);
|
|
91
98
|
}
|
|
92
99
|
else {
|
|
93
|
-
|
|
100
|
+
logger_1.AppLogger.error('Server failed to start:', err);
|
|
94
101
|
process.exit(1);
|
|
95
102
|
}
|
|
96
103
|
});
|
|
97
104
|
// Graceful shutdown
|
|
98
105
|
process.on('SIGTERM', () => {
|
|
99
|
-
|
|
106
|
+
logger_1.AppLogger.info(constants_1.MESSAGE_GRACEFUL_SHUTDOWN);
|
|
100
107
|
server.close(() => {
|
|
101
|
-
|
|
108
|
+
logger_1.AppLogger.info(constants_1.MESSAGE_SERVER_CLOSED);
|
|
102
109
|
process.exit(0);
|
|
103
110
|
});
|
|
104
111
|
});
|
|
105
112
|
}
|
|
106
113
|
catch (error) {
|
|
107
114
|
const err = error;
|
|
108
|
-
|
|
115
|
+
logger_1.AppLogger.error('Failed to start server:', err);
|
|
109
116
|
process.exit(1);
|
|
110
117
|
}
|
|
111
118
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Swagger Documentation Middleware
|
|
4
|
+
* Sets up Swagger UI for API documentation
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.setupSwaggerDocs = setupSwaggerDocs;
|
|
11
|
+
const swagger_ui_express_1 = __importDefault(require("swagger-ui-express"));
|
|
12
|
+
const swagger_jsdoc_1 = __importDefault(require("swagger-jsdoc"));
|
|
13
|
+
const swagger_config_1 = require("../config/swagger-config");
|
|
14
|
+
/**
|
|
15
|
+
* Setup Swagger documentation endpoint
|
|
16
|
+
* @param app Express application instance
|
|
17
|
+
*/
|
|
18
|
+
function setupSwaggerDocs(app) {
|
|
19
|
+
try {
|
|
20
|
+
const specs = (0, swagger_jsdoc_1.default)(swagger_config_1.swaggerOptions);
|
|
21
|
+
// Serve raw Swagger JSON FIRST (before the middleware catches all /api-docs paths)
|
|
22
|
+
app.get('/api-docs/swagger.json', (_req, res) => {
|
|
23
|
+
res.setHeader('Content-Type', 'application/json');
|
|
24
|
+
res.send(specs);
|
|
25
|
+
});
|
|
26
|
+
// Serve Swagger UI at /api-docs
|
|
27
|
+
app.use('/api-docs', swagger_ui_express_1.default.serve, swagger_ui_express_1.default.setup(specs, {
|
|
28
|
+
swaggerOptions: {
|
|
29
|
+
url: '/api-docs/swagger.json',
|
|
30
|
+
persistAuthorization: true,
|
|
31
|
+
displayOperationId: true,
|
|
32
|
+
filter: true,
|
|
33
|
+
showExtensions: true,
|
|
34
|
+
},
|
|
35
|
+
customCss: `
|
|
36
|
+
.swagger-ui .topbar {
|
|
37
|
+
background-color: #2c3e50;
|
|
38
|
+
}
|
|
39
|
+
.swagger-ui .info .title {
|
|
40
|
+
color: #2c3e50;
|
|
41
|
+
font-weight: bold;
|
|
42
|
+
}
|
|
43
|
+
.swagger-ui .btn-box .btn {
|
|
44
|
+
background-color: #2c3e50;
|
|
45
|
+
}
|
|
46
|
+
`,
|
|
47
|
+
customCssUrl: 'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui.min.css',
|
|
48
|
+
}));
|
|
49
|
+
console.log('Swagger documentation available at /api-docs');
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error('Failed to setup Swagger documentation:', error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -8,7 +8,7 @@ const health_controller_1 = require("../controllers/health-controller");
|
|
|
8
8
|
const healthRouter = express_1.default.Router();
|
|
9
9
|
healthRouter
|
|
10
10
|
.route('/refresh')
|
|
11
|
-
.
|
|
11
|
+
.post(health_controller_1.refreshHealth);
|
|
12
12
|
healthRouter
|
|
13
13
|
.route('/packages')
|
|
14
14
|
.get(health_controller_1.getPackagesHealth);
|
|
@@ -8,7 +8,7 @@ const package_controller_1 = require("../controllers/package-controller");
|
|
|
8
8
|
const packageRouter = express_1.default.Router();
|
|
9
9
|
packageRouter
|
|
10
10
|
.route('/refresh')
|
|
11
|
-
.
|
|
11
|
+
.post(package_controller_1.refreshPackages);
|
|
12
12
|
packageRouter
|
|
13
13
|
.route('/update-config')
|
|
14
14
|
.put(package_controller_1.updatePackageConfig);
|
package/dist/serve.js
CHANGED
|
@@ -11,9 +11,22 @@
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const index_1 = require("./index");
|
|
13
13
|
const utilities_1 = require("./utils/utilities");
|
|
14
|
+
let logLevel = '';
|
|
15
|
+
let nodeEnv = 'production';
|
|
16
|
+
const args = process.argv;
|
|
17
|
+
if (args.includes('--dev')) {
|
|
18
|
+
nodeEnv = 'development';
|
|
19
|
+
}
|
|
20
|
+
// Priority: Check for debug first, then fall back to info
|
|
21
|
+
if (args.includes('--debug')) {
|
|
22
|
+
logLevel = 'debug';
|
|
23
|
+
}
|
|
24
|
+
else if (args.includes('--info')) {
|
|
25
|
+
logLevel = 'info';
|
|
26
|
+
}
|
|
27
|
+
process.env.LOG_LEVEL = logLevel;
|
|
28
|
+
process.env.NODE_ENV = nodeEnv;
|
|
14
29
|
const rootPath = (0, utilities_1.findMonorepoRoot)();
|
|
15
|
-
console.log(`Starting Monodog API server...`);
|
|
16
|
-
console.log(`Analyzing monorepo at root: ${rootPath}`);
|
|
17
30
|
// Start the Express server and dashboard
|
|
18
31
|
(0, index_1.startServer)(rootPath);
|
|
19
32
|
(0, index_1.serveDashboard)(rootPath);
|
|
@@ -4,6 +4,8 @@ exports.healthRefreshService = exports.getHealthSummaryService = void 0;
|
|
|
4
4
|
const utilities_1 = require("../utils/utilities");
|
|
5
5
|
const monorepo_scanner_1 = require("../utils/monorepo-scanner");
|
|
6
6
|
const repositories_1 = require("../repositories");
|
|
7
|
+
// Track in-flight health refresh requests to prevent duplicates
|
|
8
|
+
let inFlightHealthRefresh = null;
|
|
7
9
|
const getHealthSummaryService = async () => {
|
|
8
10
|
const packageHealthData = await repositories_1.PackageHealthRepository.findAll();
|
|
9
11
|
console.log('packageHealthData -->', packageHealthData.length);
|
|
@@ -42,73 +44,91 @@ const getHealthSummaryService = async () => {
|
|
|
42
44
|
};
|
|
43
45
|
exports.getHealthSummaryService = getHealthSummaryService;
|
|
44
46
|
const healthRefreshService = async (rootDir) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
// If a health refresh is already in progress, return the in-flight promise
|
|
48
|
+
if (inFlightHealthRefresh) {
|
|
49
|
+
console.log('Health refresh already in progress, returning cached promise');
|
|
50
|
+
return inFlightHealthRefresh;
|
|
51
|
+
}
|
|
52
|
+
// Create and store the health refresh promise
|
|
53
|
+
inFlightHealthRefresh = (async () => {
|
|
48
54
|
try {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
55
|
+
const packages = (0, utilities_1.scanMonorepo)(rootDir);
|
|
56
|
+
console.log('packages -->', packages.length);
|
|
57
|
+
const healthMetrics = await Promise.all(packages.map(async (pkg) => {
|
|
58
|
+
try {
|
|
59
|
+
// Await each health check function since they return promises
|
|
60
|
+
const buildStatus = await (0, monorepo_scanner_1.funCheckBuildStatus)(pkg);
|
|
61
|
+
const testCoverage = 0; //await funCheckTestCoverage(pkg); // skip test coverage for now
|
|
62
|
+
const lintStatus = await (0, monorepo_scanner_1.funCheckLintStatus)(pkg);
|
|
63
|
+
const securityAudit = await (0, monorepo_scanner_1.funCheckSecurityAudit)(pkg);
|
|
64
|
+
// Calculate overall health score
|
|
65
|
+
const overallScore = (0, utilities_1.calculatePackageHealth)(buildStatus, testCoverage, lintStatus, securityAudit);
|
|
66
|
+
const health = {
|
|
67
|
+
buildStatus: buildStatus,
|
|
68
|
+
testCoverage: testCoverage,
|
|
69
|
+
lintStatus: lintStatus,
|
|
70
|
+
securityAudit: securityAudit,
|
|
71
|
+
overallScore: overallScore.overallScore,
|
|
72
|
+
};
|
|
73
|
+
const packageStatus = health.overallScore >= 80
|
|
74
|
+
? 'healthy'
|
|
75
|
+
: health.overallScore >= 60 && health.overallScore < 80
|
|
76
|
+
? 'warning'
|
|
77
|
+
: 'error';
|
|
78
|
+
console.log(pkg.name, '-->', health, packageStatus);
|
|
79
|
+
await repositories_1.PackageHealthRepository.upsert({
|
|
80
|
+
packageName: pkg.name,
|
|
81
|
+
packageOverallScore: overallScore.overallScore,
|
|
82
|
+
packageBuildStatus: buildStatus,
|
|
83
|
+
packageTestCoverage: testCoverage,
|
|
84
|
+
packageLintStatus: lintStatus,
|
|
85
|
+
packageSecurity: securityAudit,
|
|
86
|
+
packageDependencies: '',
|
|
87
|
+
});
|
|
88
|
+
// update related package status as well
|
|
89
|
+
await repositories_1.PackageRepository.updateStatus(pkg.name, packageStatus);
|
|
90
|
+
return {
|
|
91
|
+
packageName: pkg.name,
|
|
92
|
+
health,
|
|
93
|
+
isHealthy: health.overallScore >= 80,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return {
|
|
98
|
+
packageName: pkg.name,
|
|
99
|
+
health: {
|
|
100
|
+
"buildStatus": "",
|
|
101
|
+
"testCoverage": 0,
|
|
102
|
+
"lintStatus": "",
|
|
103
|
+
"securityAudit": "",
|
|
104
|
+
"overallScore": 0
|
|
105
|
+
},
|
|
106
|
+
isHealthy: false,
|
|
107
|
+
error: 'Failed to fetch health metrics1',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
111
|
+
const result = {
|
|
112
|
+
packages: healthMetrics.filter(h => !h.error),
|
|
113
|
+
summary: {
|
|
114
|
+
total: packages.length,
|
|
115
|
+
healthy: healthMetrics.filter(h => h.isHealthy).length,
|
|
116
|
+
unhealthy: healthMetrics.filter(h => !h.isHealthy).length,
|
|
117
|
+
averageScore: healthMetrics.filter(h => h.health).length > 0
|
|
118
|
+
? healthMetrics
|
|
119
|
+
.filter(h => h.health)
|
|
120
|
+
.reduce((sum, h) => sum + h.health.overallScore, 0) /
|
|
121
|
+
healthMetrics.filter(h => h.health).length
|
|
122
|
+
: 0,
|
|
95
123
|
},
|
|
96
|
-
isHealthy: false,
|
|
97
|
-
error: 'Failed to fetch health metrics1',
|
|
98
124
|
};
|
|
125
|
+
return result;
|
|
99
126
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
unhealthy: healthMetrics.filter(h => !h.isHealthy).length,
|
|
107
|
-
averageScore: healthMetrics
|
|
108
|
-
.filter(h => h.health)
|
|
109
|
-
.reduce((sum, h) => sum + h.health.overallScore, 0) /
|
|
110
|
-
healthMetrics.filter(h => h.health).length,
|
|
111
|
-
},
|
|
112
|
-
};
|
|
127
|
+
finally {
|
|
128
|
+
// Clear the in-flight promise after completion
|
|
129
|
+
inFlightHealthRefresh = null;
|
|
130
|
+
}
|
|
131
|
+
})();
|
|
132
|
+
return inFlightHealthRefresh;
|
|
113
133
|
};
|
|
114
134
|
exports.healthRefreshService = healthRefreshService;
|
|
@@ -128,7 +128,29 @@ const refreshPackagesService = async (rootPath) => {
|
|
|
128
128
|
for (const pkg of packages) {
|
|
129
129
|
await storePackage(pkg);
|
|
130
130
|
}
|
|
131
|
-
|
|
131
|
+
// Return transformed packages like getPackagesService
|
|
132
|
+
const dbPackages = await repositories_1.PackageRepository.findAll();
|
|
133
|
+
const transformedPackages = dbPackages.map((pkg) => {
|
|
134
|
+
const transformedPkg = { ...pkg };
|
|
135
|
+
transformedPkg.maintainers = pkg.maintainers
|
|
136
|
+
? JSON.parse(pkg.maintainers)
|
|
137
|
+
: [];
|
|
138
|
+
transformedPkg.scripts = pkg.scripts ? JSON.parse(pkg.scripts) : {};
|
|
139
|
+
transformedPkg.repository = pkg.repository
|
|
140
|
+
? JSON.parse(pkg.repository)
|
|
141
|
+
: {};
|
|
142
|
+
transformedPkg.dependencies = pkg.dependencies
|
|
143
|
+
? JSON.parse(pkg.dependencies)
|
|
144
|
+
: [];
|
|
145
|
+
transformedPkg.devDependencies = pkg.devDependencies
|
|
146
|
+
? JSON.parse(pkg.devDependencies)
|
|
147
|
+
: [];
|
|
148
|
+
transformedPkg.peerDependencies = pkg.peerDependencies
|
|
149
|
+
? JSON.parse(pkg.peerDependencies)
|
|
150
|
+
: [];
|
|
151
|
+
return transformedPkg;
|
|
152
|
+
});
|
|
153
|
+
return transformedPackages;
|
|
132
154
|
};
|
|
133
155
|
exports.refreshPackagesService = refreshPackagesService;
|
|
134
156
|
const getPackageDetailService = async (name) => {
|