@manojkmfsi/monodog 1.0.24 → 1.0.25
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 +6 -0
- package/dist/middleware/dashboard-startup.js +11 -9
- 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 +2 -2
- package/dist/middleware/server-startup.js +25 -19
- package/dist/serve.js +15 -2
- package/package.json +10 -3
- 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/middleware/dashboard-startup.ts +11 -12
- 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 +4 -4
- package/src/middleware/server-startup.ts +26 -24
- package/src/serve.ts +19 -3
- 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
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.serveDashboard = serveDashboard;
|
|
10
10
|
const express_1 = __importDefault(require("express"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
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");
|
|
@@ -46,7 +47,8 @@ function createDashboardApp() {
|
|
|
46
47
|
res.send(`window.ENV = { API_URL: "${apiUrl}" };`);
|
|
47
48
|
});
|
|
48
49
|
// Request logging
|
|
49
|
-
app.use(
|
|
50
|
+
app.use(logger_1.httpLogger);
|
|
51
|
+
// app.use(requestLogger);
|
|
50
52
|
// SPA routing: serve index.html for non-static routes
|
|
51
53
|
app.use((_req, _res, next) => {
|
|
52
54
|
if (/(.ico|.js|.css|.jpg|.png|.map|.woff|.woff2|.ttf)$/i.test(_req.path)) {
|
|
@@ -60,7 +62,7 @@ function createDashboardApp() {
|
|
|
60
62
|
root: path_1.default.resolve(__dirname, '..', '..', 'monodog-dashboard', 'dist'),
|
|
61
63
|
}, (err) => {
|
|
62
64
|
if (err) {
|
|
63
|
-
|
|
65
|
+
logger_1.AppLogger.error('Error serving index.html:', err);
|
|
64
66
|
_res.status(500).json({ error: 'Internal server error' });
|
|
65
67
|
}
|
|
66
68
|
});
|
|
@@ -68,7 +70,7 @@ function createDashboardApp() {
|
|
|
68
70
|
});
|
|
69
71
|
// Static files
|
|
70
72
|
const staticPath = path_1.default.join(__dirname, '..', '..', 'monodog-dashboard', 'dist');
|
|
71
|
-
|
|
73
|
+
logger_1.AppLogger.debug('Serving static files from:', { path: staticPath });
|
|
72
74
|
app.use(express_1.default.static(staticPath, {
|
|
73
75
|
maxAge: '1d',
|
|
74
76
|
etag: false,
|
|
@@ -93,30 +95,30 @@ function serveDashboard(rootPath) {
|
|
|
93
95
|
});
|
|
94
96
|
server.on('error', (err) => {
|
|
95
97
|
if (err.code === 'EADDRINUSE') {
|
|
96
|
-
|
|
98
|
+
logger_1.AppLogger.error(`Port ${validatedPort} is already in use.`, err);
|
|
97
99
|
process.exit(1);
|
|
98
100
|
}
|
|
99
101
|
else if (err.code === 'EACCES') {
|
|
100
|
-
|
|
102
|
+
logger_1.AppLogger.error(`Permission denied to listen on port ${validatedPort}.`, err);
|
|
101
103
|
process.exit(1);
|
|
102
104
|
}
|
|
103
105
|
else {
|
|
104
|
-
|
|
106
|
+
logger_1.AppLogger.error('Server failed to start:', err);
|
|
105
107
|
process.exit(1);
|
|
106
108
|
}
|
|
107
109
|
});
|
|
108
110
|
// Graceful shutdown
|
|
109
111
|
process.on('SIGTERM', () => {
|
|
110
|
-
|
|
112
|
+
logger_1.AppLogger.info('SIGTERM signal received: closing dashboard server');
|
|
111
113
|
server.close(() => {
|
|
112
|
-
|
|
114
|
+
logger_1.AppLogger.info('Dashboard server closed');
|
|
113
115
|
process.exit(0);
|
|
114
116
|
});
|
|
115
117
|
});
|
|
116
118
|
}
|
|
117
119
|
catch (error) {
|
|
118
120
|
const err = error;
|
|
119
|
-
|
|
121
|
+
logger_1.AppLogger.error('Failed to start dashboard:', err);
|
|
120
122
|
process.exit(1);
|
|
121
123
|
}
|
|
122
124
|
}
|
|
@@ -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]';
|
|
@@ -34,7 +34,7 @@ function createHelmetMiddleware(apiUrl) {
|
|
|
34
34
|
*/
|
|
35
35
|
function createApiCorsMiddleware(dashboardUrl) {
|
|
36
36
|
const corsOptions = {
|
|
37
|
-
origin:
|
|
37
|
+
origin: dashboardUrl,
|
|
38
38
|
credentials: true,
|
|
39
39
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
40
40
|
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
@@ -65,7 +65,7 @@ function createTimeoutMiddleware() {
|
|
|
65
65
|
*/
|
|
66
66
|
function buildApiUrl(host, port) {
|
|
67
67
|
const apiHost = host === '0.0.0.0' ? 'localhost' : host;
|
|
68
|
-
return
|
|
68
|
+
return `http://${apiHost}:${port}`;
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
71
|
* Build dashboard URL based on config
|
|
@@ -9,6 +9,7 @@ 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");
|
|
@@ -45,8 +46,8 @@ function createApp(rootPath) {
|
|
|
45
46
|
app.use((0, security_1.createApiCorsMiddleware)(dashboardUrl));
|
|
46
47
|
// Body parser
|
|
47
48
|
app.use((0, body_parser_1.json)({ limit: '1mb' }));
|
|
48
|
-
//
|
|
49
|
-
app.use(
|
|
49
|
+
// HTTP request logging with Morgan
|
|
50
|
+
app.use(logger_1.httpLogger);
|
|
50
51
|
// Routes
|
|
51
52
|
app.use('/api/packages', package_routes_1.default);
|
|
52
53
|
app.use('/api/commits/', commit_routes_1.default);
|
|
@@ -66,46 +67,51 @@ function startServer(rootPath) {
|
|
|
66
67
|
const port = config_loader_1.appConfig.server.port;
|
|
67
68
|
const host = config_loader_1.appConfig.server.host;
|
|
68
69
|
const validatedPort = validatePort(port);
|
|
70
|
+
logger_1.AppLogger.info(`Starting Monodog API server...`);
|
|
71
|
+
logger_1.AppLogger.info(`Analyzing monorepo at root: ${rootPath}`);
|
|
69
72
|
const app = createApp(rootPath);
|
|
70
73
|
const server = app.listen(validatedPort, host, () => {
|
|
71
|
-
console.log(`Backend server
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
console.log(`Backend server listening on http://${host}:${validatedPort}`);
|
|
75
|
+
logger_1.AppLogger.info('API endpoints available:', {
|
|
76
|
+
endpoints: [
|
|
77
|
+
'GET /api/health',
|
|
78
|
+
'GET /api/packages/refresh',
|
|
79
|
+
'GET /api/packages',
|
|
80
|
+
'GET /api/packages/:name',
|
|
81
|
+
'PUT /api/packages/update-config',
|
|
82
|
+
'GET /api/commits/:packagePath',
|
|
83
|
+
'GET /api/health/packages',
|
|
84
|
+
'PUT /api/config/files/:id',
|
|
85
|
+
'GET /api/config/files',
|
|
86
|
+
],
|
|
87
|
+
});
|
|
82
88
|
});
|
|
83
89
|
server.on('error', (err) => {
|
|
84
90
|
if (err.code === 'EADDRINUSE') {
|
|
85
|
-
|
|
91
|
+
logger_1.AppLogger.error(`Port ${validatedPort} is already in use. Please specify a different port.`, err);
|
|
86
92
|
process.exit(1);
|
|
87
93
|
}
|
|
88
94
|
else if (err.code === 'EACCES') {
|
|
89
|
-
|
|
95
|
+
logger_1.AppLogger.error(`Permission denied to listen on port ${validatedPort}. Use a port above 1024.`, err);
|
|
90
96
|
process.exit(1);
|
|
91
97
|
}
|
|
92
98
|
else {
|
|
93
|
-
|
|
99
|
+
logger_1.AppLogger.error('Server failed to start:', err);
|
|
94
100
|
process.exit(1);
|
|
95
101
|
}
|
|
96
102
|
});
|
|
97
103
|
// Graceful shutdown
|
|
98
104
|
process.on('SIGTERM', () => {
|
|
99
|
-
|
|
105
|
+
logger_1.AppLogger.info('SIGTERM signal received: closing HTTP server');
|
|
100
106
|
server.close(() => {
|
|
101
|
-
|
|
107
|
+
logger_1.AppLogger.info('HTTP server closed');
|
|
102
108
|
process.exit(0);
|
|
103
109
|
});
|
|
104
110
|
});
|
|
105
111
|
}
|
|
106
112
|
catch (error) {
|
|
107
113
|
const err = error;
|
|
108
|
-
|
|
114
|
+
logger_1.AppLogger.error('Failed to start server:', err);
|
|
109
115
|
process.exit(1);
|
|
110
116
|
}
|
|
111
117
|
}
|
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);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manojkmfsi/monodog",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"description": "App for monodog monorepo",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -22,25 +22,32 @@
|
|
|
22
22
|
"@types/express": "^4.17.25",
|
|
23
23
|
"@types/jest": "^29.5.14",
|
|
24
24
|
"@types/js-yaml": "^4.0.9",
|
|
25
|
+
"@types/morgan": "^1.9.10",
|
|
25
26
|
"@types/node": "^20.19.27",
|
|
26
27
|
"cross-env": "^10.1.0",
|
|
27
28
|
"jest": "^29.7.0",
|
|
28
29
|
"jest-environment-jsdom": "^30.2.0",
|
|
30
|
+
"morgan": "^1.10.1",
|
|
29
31
|
"ts-jest": "^29.4.6",
|
|
30
32
|
"ts-node": "^10.9.2",
|
|
31
33
|
"tsx": "^4.21.0",
|
|
32
34
|
"typescript": "^5.9.3"
|
|
33
35
|
},
|
|
36
|
+
"prisma": {
|
|
37
|
+
"schema": "./prisma/schema"
|
|
38
|
+
},
|
|
34
39
|
"scripts": {
|
|
35
|
-
"dev": "DATABASE_URL=$(npm run db:url --silent 2>/dev/null | tr -d '\\n') tsx watch src/serve.
|
|
40
|
+
"dev": "DATABASE_URL=$(npm run db:url --silent 2>/dev/null | tr -d '\\n') tsx watch src/serve.ts --dev --debug",
|
|
36
41
|
"serve": "DATABASE_URL=$(npm run db:url --silent 2>/dev/null | tr -d '\\n') tsx dist/serve.js",
|
|
37
42
|
"build": "rm -rf dist && tsc",
|
|
38
|
-
"test:coverage": "jest --coverage",
|
|
43
|
+
"test:coverage": "jest --coverage --silent",
|
|
39
44
|
"prestart": "npm run build",
|
|
40
45
|
"clean": "rm -rf dist node_modules/.cache",
|
|
41
46
|
"lint": "eslint .",
|
|
42
47
|
"lint:fix": "eslint . --fix",
|
|
43
48
|
"db:url": "node dist/get-db-url.js",
|
|
49
|
+
"schema:format": "DATABASE_URL=$(npm run db:url --silent 2>/dev/null | tr -d '\\n') prisma format",
|
|
50
|
+
"schema:validate": "DATABASE_URL=$(npm run db:url --silent 2>/dev/null | tr -d '\\n') prisma validate",
|
|
44
51
|
"generate": "DATABASE_URL=$(npm run db:url --silent 2>/dev/null | tr -d '\\n') prisma generate",
|
|
45
52
|
"migrate": "DATABASE_URL=$(npm run db:url --silent 2>/dev/null | tr -d '\\n') prisma migrate dev",
|
|
46
53
|
"migrate:reset": "DATABASE_URL=$(npm run db:url --silent 2>/dev/null | tr -d '\\n') prisma migrate reset --force"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
model DependencyInfo {
|
|
2
|
+
name String
|
|
3
|
+
packageName String
|
|
4
|
+
version String
|
|
5
|
+
type String @default("")
|
|
6
|
+
status String @default("")
|
|
7
|
+
latest String?
|
|
8
|
+
outdated Boolean @default(false)
|
|
9
|
+
package Package @relation(fields: [packageName], references: [name], onDelete: Cascade)
|
|
10
|
+
|
|
11
|
+
@@unique([name, packageName]) // Composite unique constraint
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
model HealthStatus {
|
|
2
|
+
id Int @id @default(autoincrement())
|
|
3
|
+
packageName String @unique
|
|
4
|
+
overallScore Float // Overall health score (0-100)
|
|
5
|
+
buildStatus String // e.g., "passing", "failing", "unknown"
|
|
6
|
+
testCoverage Float // Test coverage percentage (0-100)
|
|
7
|
+
lintStatus String // e.g., "passing", "warning", "failing"
|
|
8
|
+
security String // e.g., "secure", "vulnerabilities", "unknown"
|
|
9
|
+
dependencies String // e.g., "up-to-date", "outdated", "vulnerable"
|
|
10
|
+
createdAt DateTime @default(now())
|
|
11
|
+
updatedAt DateTime @updatedAt
|
|
12
|
+
|
|
13
|
+
@@map("health_status")
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
model PackageHealth {
|
|
2
|
+
id Int @id @default(autoincrement())
|
|
3
|
+
packageName String @unique
|
|
4
|
+
packageOverallScore Float
|
|
5
|
+
packageBuildStatus String
|
|
6
|
+
packageTestCoverage Float?
|
|
7
|
+
packageLintStatus String
|
|
8
|
+
packageSecurity String // Changed from securityAudit to packageSecurity
|
|
9
|
+
packageDependencies String // Changed from dependencies to packageDependencies
|
|
10
|
+
createdAt DateTime @default(now())
|
|
11
|
+
updatedAt DateTime @updatedAt
|
|
12
|
+
package Package @relation(fields: [packageName], references: [name], onDelete: Cascade)
|
|
13
|
+
|
|
14
|
+
@@map("package_health")
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
model Package {
|
|
2
|
+
// Primary Key and Identity Field (using the package name as the unique ID): Example: '@monodog/dashboard'
|
|
3
|
+
name String @id @unique
|
|
4
|
+
version String
|
|
5
|
+
type String // e.g., 'app', 'package'
|
|
6
|
+
createdAt DateTime @default(now())
|
|
7
|
+
lastUpdated DateTime @default(now())
|
|
8
|
+
dependencies String?
|
|
9
|
+
maintainers String
|
|
10
|
+
path String // The relative path in the file system, e.g., 'packages/monoapp'
|
|
11
|
+
description String
|
|
12
|
+
license String
|
|
13
|
+
repository String?
|
|
14
|
+
scripts String?
|
|
15
|
+
status String @default("")
|
|
16
|
+
devDependencies String?
|
|
17
|
+
peerDependencies String?
|
|
18
|
+
dependenciesInfo DependencyInfo[]
|
|
19
|
+
commits Commit[]
|
|
20
|
+
packageHealth PackageHealth?
|
|
21
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// This is your Prisma schema file,
|
|
2
|
+
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
3
|
+
|
|
4
|
+
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
|
5
|
+
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
|
6
|
+
|
|
7
|
+
generator client {
|
|
8
|
+
provider = "prisma-client-js"
|
|
9
|
+
previewFeatures = ["prismaSchemaFolder"]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
datasource db {
|
|
13
|
+
provider = "sqlite"
|
|
14
|
+
url = env("DATABASE_URL")
|
|
15
|
+
}
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
import express from 'express';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import type { Express } from 'express';
|
|
8
|
+
import { httpLogger, AppLogger } from './logger';
|
|
8
9
|
|
|
9
10
|
import { appConfig } from '../config-loader';
|
|
10
11
|
import {
|
|
11
12
|
errorHandler,
|
|
12
|
-
requestLogger,
|
|
13
13
|
} from './error-handler';
|
|
14
14
|
import {
|
|
15
15
|
createHelmetMiddleware,
|
|
@@ -64,7 +64,8 @@ function createDashboardApp(): Express {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
// Request logging
|
|
67
|
-
app.use(
|
|
67
|
+
app.use(httpLogger);
|
|
68
|
+
// app.use(requestLogger);
|
|
68
69
|
|
|
69
70
|
// SPA routing: serve index.html for non-static routes
|
|
70
71
|
app.use((_req, _res, next) => {
|
|
@@ -81,7 +82,7 @@ function createDashboardApp(): Express {
|
|
|
81
82
|
root: path.resolve(__dirname, '..', '..', 'monodog-dashboard', 'dist'),
|
|
82
83
|
}, (err: Error | null) => {
|
|
83
84
|
if (err) {
|
|
84
|
-
|
|
85
|
+
AppLogger.error('Error serving index.html:', err);
|
|
85
86
|
_res.status(500).json({ error: 'Internal server error' });
|
|
86
87
|
}
|
|
87
88
|
});
|
|
@@ -90,7 +91,7 @@ function createDashboardApp(): Express {
|
|
|
90
91
|
|
|
91
92
|
// Static files
|
|
92
93
|
const staticPath = path.join(__dirname, '..', '..', 'monodog-dashboard', 'dist');
|
|
93
|
-
|
|
94
|
+
AppLogger.debug('Serving static files from:', { path: staticPath });
|
|
94
95
|
app.use(express.static(staticPath, {
|
|
95
96
|
maxAge: '1d',
|
|
96
97
|
etag: false,
|
|
@@ -121,30 +122,28 @@ export function serveDashboard(rootPath: string): void {
|
|
|
121
122
|
|
|
122
123
|
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
123
124
|
if (err.code === 'EADDRINUSE') {
|
|
124
|
-
|
|
125
|
+
AppLogger.error(`Port ${validatedPort} is already in use.`, err);
|
|
125
126
|
process.exit(1);
|
|
126
127
|
} else if (err.code === 'EACCES') {
|
|
127
|
-
|
|
128
|
-
`Error: Permission denied to listen on port ${validatedPort}.`
|
|
129
|
-
);
|
|
128
|
+
AppLogger.error(`Permission denied to listen on port ${validatedPort}.`, err);
|
|
130
129
|
process.exit(1);
|
|
131
130
|
} else {
|
|
132
|
-
|
|
131
|
+
AppLogger.error('Server failed to start:', err);
|
|
133
132
|
process.exit(1);
|
|
134
133
|
}
|
|
135
134
|
});
|
|
136
135
|
|
|
137
136
|
// Graceful shutdown
|
|
138
137
|
process.on('SIGTERM', () => {
|
|
139
|
-
|
|
138
|
+
AppLogger.info('SIGTERM signal received: closing dashboard server');
|
|
140
139
|
server.close(() => {
|
|
141
|
-
|
|
140
|
+
AppLogger.info('Dashboard server closed');
|
|
142
141
|
process.exit(0);
|
|
143
142
|
});
|
|
144
143
|
});
|
|
145
144
|
} catch (error: unknown) {
|
|
146
145
|
const err = error as Error & { message?: string };
|
|
147
|
-
|
|
146
|
+
AppLogger.error('Failed to start dashboard:', err);
|
|
148
147
|
process.exit(1);
|
|
149
148
|
}
|
|
150
149
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Request, Response, NextFunction, ErrorRequestHandler } from 'express';
|
|
6
|
+
import { AppLogger } from './logger';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Custom error interface extending Error
|
|
@@ -24,7 +25,7 @@ export const errorHandler: ErrorRequestHandler = (
|
|
|
24
25
|
): void => {
|
|
25
26
|
const status = err.status || err.statusCode || 500;
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
AppLogger.error('Request error occurred', {
|
|
28
29
|
status,
|
|
29
30
|
method: req.method,
|
|
30
31
|
path: req.path,
|
|
@@ -47,17 +48,3 @@ export const notFoundHandler = (_req: Request, res: Response): void => {
|
|
|
47
48
|
timestamp: Date.now(),
|
|
48
49
|
});
|
|
49
50
|
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Request logging middleware
|
|
53
|
-
*/
|
|
54
|
-
export const requestLogger = (
|
|
55
|
-
req: Request,
|
|
56
|
-
_res: Response,
|
|
57
|
-
next: NextFunction
|
|
58
|
-
): void => {
|
|
59
|
-
console.log(
|
|
60
|
-
`[${new Date().toISOString()}] ${req.method} ${req.path}`
|
|
61
|
-
);
|
|
62
|
-
next();
|
|
63
|
-
};
|
package/src/middleware/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Middleware exports
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export { errorHandler, notFoundHandler
|
|
5
|
+
export { errorHandler, notFoundHandler } from './error-handler';
|
|
6
6
|
export type { CustomError } from './error-handler';
|
|
7
7
|
|
|
8
8
|
export {
|
|
@@ -16,3 +16,5 @@ export {
|
|
|
16
16
|
|
|
17
17
|
export { startServer } from './server-startup';
|
|
18
18
|
export { serveDashboard } from './dashboard-startup';
|
|
19
|
+
|
|
20
|
+
export { httpLogger, AppLogger } from './logger';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger configuration using Morgan
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import morgan from 'morgan';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* HTTP request logger middleware using Morgan
|
|
9
|
+
*/
|
|
10
|
+
export const httpLogger = morgan('dev');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Application logger for non-HTTP events
|
|
14
|
+
*/
|
|
15
|
+
export class AppLogger {
|
|
16
|
+
private static readonly prefix = '[APP]';
|
|
17
|
+
|
|
18
|
+
static info(message: string, data?: Record<string, unknown>): void {
|
|
19
|
+
if (process.env.LOG_LEVEL == 'info' || process.env.LOG_LEVEL == 'debug') {
|
|
20
|
+
if (data) {
|
|
21
|
+
console.log(`${this.prefix} [INFO]`, message, JSON.stringify(data, null, 2));
|
|
22
|
+
} else {
|
|
23
|
+
console.log(`${this.prefix} [INFO]`, message);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static error(message: string, error?: Error | Record<string, unknown>): void {
|
|
29
|
+
if (error instanceof Error) {
|
|
30
|
+
console.error(`${this.prefix} [ERROR]`, message, {
|
|
31
|
+
message: error.message,
|
|
32
|
+
stack: error.stack,
|
|
33
|
+
});
|
|
34
|
+
} else if (error) {
|
|
35
|
+
console.error(`${this.prefix} [ERROR]`, message, error);
|
|
36
|
+
} else {
|
|
37
|
+
console.error(`${this.prefix} [ERROR]`, message);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static warn(message: string, data?: Record<string, unknown>): void {
|
|
42
|
+
if (data) {
|
|
43
|
+
console.warn(`${this.prefix} [WARN]`, message, JSON.stringify(data, null, 2));
|
|
44
|
+
} else {
|
|
45
|
+
console.warn(`${this.prefix} [WARN]`, message);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static debug(message: string, data?: Record<string, unknown>): void {
|
|
50
|
+
if (process.env.LOG_LEVEL == 'debug') {
|
|
51
|
+
if (data) {
|
|
52
|
+
console.log(`${this.prefix} [DEBUG]`, message, JSON.stringify(data, null, 2));
|
|
53
|
+
} else {
|
|
54
|
+
console.log(`${this.prefix} [DEBUG]`, message);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -28,7 +28,7 @@ export function createHelmetMiddleware(apiUrl: string) {
|
|
|
28
28
|
*/
|
|
29
29
|
export function createApiCorsMiddleware(dashboardUrl: string) {
|
|
30
30
|
const corsOptions: CorsOptions = {
|
|
31
|
-
origin:
|
|
31
|
+
origin: dashboardUrl,
|
|
32
32
|
credentials: true,
|
|
33
33
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
34
34
|
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
@@ -67,15 +67,15 @@ export function buildApiUrl(
|
|
|
67
67
|
port: number
|
|
68
68
|
): string {
|
|
69
69
|
const apiHost = host === '0.0.0.0' ? 'localhost' : host;
|
|
70
|
-
return
|
|
70
|
+
return `http://${apiHost}:${port}`;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* Build dashboard URL based on config
|
|
75
75
|
*/
|
|
76
76
|
export function buildDashboardUrl(config: MonodogConfig): string {
|
|
77
|
-
const dashboardHost = config.dashboard.host === '0.0.0.0'
|
|
78
|
-
? 'localhost'
|
|
77
|
+
const dashboardHost = config.dashboard.host === '0.0.0.0'
|
|
78
|
+
? 'localhost'
|
|
79
79
|
: config.dashboard.host;
|
|
80
80
|
return `http://${dashboardHost}:${config.dashboard.port}`;
|
|
81
81
|
}
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
import express from 'express';
|
|
6
6
|
import { json } from 'body-parser';
|
|
7
7
|
import type { Express } from 'express';
|
|
8
|
+
import { httpLogger, AppLogger } from './logger';
|
|
8
9
|
|
|
9
10
|
import { appConfig } from '../config-loader';
|
|
10
11
|
import {
|
|
11
12
|
errorHandler,
|
|
12
13
|
notFoundHandler,
|
|
13
|
-
requestLogger,
|
|
14
14
|
} from './error-handler';
|
|
15
15
|
import {
|
|
16
16
|
createHelmetMiddleware,
|
|
@@ -64,8 +64,8 @@ function createApp(rootPath: string): Express {
|
|
|
64
64
|
// Body parser
|
|
65
65
|
app.use(json({ limit: '1mb' }));
|
|
66
66
|
|
|
67
|
-
//
|
|
68
|
-
app.use(
|
|
67
|
+
// HTTP request logging with Morgan
|
|
68
|
+
app.use(httpLogger);
|
|
69
69
|
|
|
70
70
|
// Routes
|
|
71
71
|
app.use('/api/packages', packageRouter);
|
|
@@ -91,50 +91,52 @@ export function startServer(rootPath: string): void {
|
|
|
91
91
|
const host = appConfig.server.host;
|
|
92
92
|
const validatedPort = validatePort(port);
|
|
93
93
|
|
|
94
|
+
AppLogger.info(`Starting Monodog API server...`);
|
|
95
|
+
AppLogger.info(`Analyzing monorepo at root: ${rootPath}`);
|
|
96
|
+
|
|
94
97
|
const app = createApp(rootPath);
|
|
95
98
|
|
|
96
99
|
const server = app.listen(validatedPort, host, () => {
|
|
97
|
-
console.log(`Backend server
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
console.log(`Backend server listening on http://${host}:${validatedPort}`);
|
|
101
|
+
AppLogger.info('API endpoints available:', {
|
|
102
|
+
endpoints: [
|
|
103
|
+
'GET /api/health',
|
|
104
|
+
'GET /api/packages/refresh',
|
|
105
|
+
'GET /api/packages',
|
|
106
|
+
'GET /api/packages/:name',
|
|
107
|
+
'PUT /api/packages/update-config',
|
|
108
|
+
'GET /api/commits/:packagePath',
|
|
109
|
+
'GET /api/health/packages',
|
|
110
|
+
'PUT /api/config/files/:id',
|
|
111
|
+
'GET /api/config/files',
|
|
112
|
+
],
|
|
113
|
+
});
|
|
108
114
|
});
|
|
109
115
|
|
|
110
116
|
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
111
117
|
if (err.code === 'EADDRINUSE') {
|
|
112
|
-
|
|
113
|
-
`Error: Port ${validatedPort} is already in use. Please specify a different port.`
|
|
114
|
-
);
|
|
118
|
+
AppLogger.error(`Port ${validatedPort} is already in use. Please specify a different port.`, err);
|
|
115
119
|
process.exit(1);
|
|
116
120
|
} else if (err.code === 'EACCES') {
|
|
117
|
-
|
|
118
|
-
`Error: Permission denied to listen on port ${validatedPort}. Use a port above 1024.`
|
|
119
|
-
);
|
|
121
|
+
AppLogger.error(`Permission denied to listen on port ${validatedPort}. Use a port above 1024.`, err);
|
|
120
122
|
process.exit(1);
|
|
121
123
|
} else {
|
|
122
|
-
|
|
124
|
+
AppLogger.error('Server failed to start:', err);
|
|
123
125
|
process.exit(1);
|
|
124
126
|
}
|
|
125
127
|
});
|
|
126
128
|
|
|
127
129
|
// Graceful shutdown
|
|
128
130
|
process.on('SIGTERM', () => {
|
|
129
|
-
|
|
131
|
+
AppLogger.info('SIGTERM signal received: closing HTTP server');
|
|
130
132
|
server.close(() => {
|
|
131
|
-
|
|
133
|
+
AppLogger.info('HTTP server closed');
|
|
132
134
|
process.exit(0);
|
|
133
135
|
});
|
|
134
136
|
});
|
|
135
137
|
} catch (error: unknown) {
|
|
136
138
|
const err = error as Error & { message?: string };
|
|
137
|
-
|
|
139
|
+
AppLogger.error('Failed to start server:', err);
|
|
138
140
|
process.exit(1);
|
|
139
141
|
}
|
|
140
142
|
}
|
package/src/serve.ts
CHANGED
|
@@ -12,10 +12,26 @@
|
|
|
12
12
|
import { startServer, serveDashboard } from './index';
|
|
13
13
|
import { findMonorepoRoot } from './utils/utilities';
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
let logLevel = '';
|
|
16
|
+
let nodeEnv = 'production';
|
|
17
|
+
|
|
18
|
+
const args = process.argv;
|
|
19
|
+
|
|
20
|
+
if (args.includes('--dev')) {
|
|
21
|
+
nodeEnv = 'development';
|
|
22
|
+
}
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
// Priority: Check for debug first, then fall back to info
|
|
25
|
+
if (args.includes('--debug')) {
|
|
26
|
+
logLevel = 'debug';
|
|
27
|
+
} else if (args.includes('--info')) {
|
|
28
|
+
logLevel = 'info';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
process.env.LOG_LEVEL = logLevel;
|
|
32
|
+
process.env.NODE_ENV = nodeEnv
|
|
33
|
+
|
|
34
|
+
const rootPath = findMonorepoRoot();
|
|
19
35
|
|
|
20
36
|
// Start the Express server and dashboard
|
|
21
37
|
|
package/prisma/schema.prisma
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
// This is your Prisma schema file,
|
|
2
|
-
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
3
|
-
|
|
4
|
-
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
|
5
|
-
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
|
6
|
-
|
|
7
|
-
generator client {
|
|
8
|
-
provider = "prisma-client-js"
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
datasource db {
|
|
12
|
-
provider = "sqlite"
|
|
13
|
-
url = env("DATABASE_URL")
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// --- MONOREPO DATA MODEL ---
|
|
17
|
-
|
|
18
|
-
/// Represents a single package within the monorepo, based on the pnpm/package.json data.
|
|
19
|
-
model Package {
|
|
20
|
-
// Primary Key and Identity Field (using the package name as the unique ID)
|
|
21
|
-
// Example: '@monodog/dashboard'
|
|
22
|
-
name String @id @unique
|
|
23
|
-
|
|
24
|
-
// Core Package Metadata
|
|
25
|
-
version String
|
|
26
|
-
type String // e.g., 'app', 'package'
|
|
27
|
-
|
|
28
|
-
// Timestamps
|
|
29
|
-
createdAt DateTime @default(now())
|
|
30
|
-
lastUpdated DateTime @default(now())
|
|
31
|
-
|
|
32
|
-
// Key Metrics and Relationships
|
|
33
|
-
dependencies String? // The total number of direct dependencies
|
|
34
|
-
// Manual Serialization Required: Stores a JSON array string of maintainers, e.g., '["team-frontend"]'
|
|
35
|
-
maintainers String
|
|
36
|
-
// Manual Serialization Required: Stores a JSON array string of tags, e.g., '["core", "ui"]'
|
|
37
|
-
path String // The relative path in the file system, e.g., 'apps/dashboard'
|
|
38
|
-
|
|
39
|
-
// Descriptions and Configuration
|
|
40
|
-
description String
|
|
41
|
-
license String
|
|
42
|
-
repository String?
|
|
43
|
-
|
|
44
|
-
// Manual Serialization Required: Stores the scripts object as a JSON string
|
|
45
|
-
// Example: '{"dev": "vite", "build": "tsc && vite build"}'
|
|
46
|
-
scripts String?
|
|
47
|
-
status String @default("")
|
|
48
|
-
|
|
49
|
-
devDependencies String?
|
|
50
|
-
peerDependencies String?
|
|
51
|
-
dependenciesInfo DependencyInfo[]
|
|
52
|
-
commits Commit[]
|
|
53
|
-
packageHealth PackageHealth?
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
model DependencyInfo {
|
|
57
|
-
name String
|
|
58
|
-
packageName String
|
|
59
|
-
version String
|
|
60
|
-
type String @default("")
|
|
61
|
-
status String @default("")
|
|
62
|
-
latest String?
|
|
63
|
-
outdated Boolean @default(false)
|
|
64
|
-
package Package @relation(fields: [packageName], references: [name], onDelete: Cascade)
|
|
65
|
-
|
|
66
|
-
@@unique([name, packageName]) // Composite unique constraint
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
model Commit {
|
|
70
|
-
hash String
|
|
71
|
-
message String
|
|
72
|
-
author String
|
|
73
|
-
date DateTime?
|
|
74
|
-
type String
|
|
75
|
-
packageName String
|
|
76
|
-
package Package @relation(fields: [packageName], references: [name], onDelete: Cascade)
|
|
77
|
-
|
|
78
|
-
@@unique([hash, packageName])
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
model HealthStatus {
|
|
82
|
-
id Int @id @default(autoincrement())
|
|
83
|
-
|
|
84
|
-
// Package reference (without formal relation)
|
|
85
|
-
packageName String @unique
|
|
86
|
-
// package Package @relation(fields: [packageName], references: [name], onDelete: Cascade)
|
|
87
|
-
|
|
88
|
-
// Health Metrics
|
|
89
|
-
overallScore Float // Overall health score (0-100)
|
|
90
|
-
buildStatus String // e.g., "passing", "failing", "unknown"
|
|
91
|
-
testCoverage Float // Test coverage percentage (0-100)
|
|
92
|
-
lintStatus String // e.g., "passing", "warning", "failing"
|
|
93
|
-
security String // e.g., "secure", "vulnerabilities", "unknown"
|
|
94
|
-
dependencies String // e.g., "up-to-date", "outdated", "vulnerable"
|
|
95
|
-
// Timestamps
|
|
96
|
-
createdAt DateTime @default(now())
|
|
97
|
-
updatedAt DateTime @updatedAt
|
|
98
|
-
|
|
99
|
-
@@map("health_status") // Optional: to specify the table name
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
model PackageHealth {
|
|
103
|
-
id Int @id @default(autoincrement())
|
|
104
|
-
packageName String @unique
|
|
105
|
-
packageOverallScore Float
|
|
106
|
-
packageBuildStatus String
|
|
107
|
-
packageTestCoverage Float?
|
|
108
|
-
packageLintStatus String
|
|
109
|
-
packageSecurity String // Changed from securityAudit to packageSecurity
|
|
110
|
-
packageDependencies String // Changed from dependencies to packageDependencies
|
|
111
|
-
createdAt DateTime @default(now())
|
|
112
|
-
updatedAt DateTime @updatedAt
|
|
113
|
-
package Package @relation(fields: [packageName], references: [name], onDelete: Cascade)
|
|
114
|
-
|
|
115
|
-
@@map("package_health")
|
|
116
|
-
}
|