@manojkmfsi/monodog 1.1.7 → 1.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/config/swagger-config.ts +0 -344
- package/src/config-loader.ts +0 -97
- package/src/constants/index.ts +0 -13
- package/src/constants/middleware.ts +0 -83
- package/src/constants/port.ts +0 -20
- package/src/constants/security.ts +0 -78
- package/src/controllers/commit-controller.ts +0 -31
- package/src/controllers/config-controller.ts +0 -42
- package/src/controllers/health-controller.ts +0 -24
- package/src/controllers/package-controller.ts +0 -67
- package/src/get-db-url.ts +0 -9
- package/src/index.ts +0 -9
- package/src/middleware/dashboard-startup.ts +0 -161
- package/src/middleware/error-handler.ts +0 -50
- package/src/middleware/index.ts +0 -20
- package/src/middleware/logger.ts +0 -65
- package/src/middleware/security.ts +0 -90
- package/src/middleware/server-startup.ts +0 -153
- package/src/middleware/swagger-middleware.ts +0 -58
- package/src/repositories/commit-repository.ts +0 -108
- package/src/repositories/dependency-repository.ts +0 -110
- package/src/repositories/index.ts +0 -10
- package/src/repositories/package-health-repository.ts +0 -75
- package/src/repositories/package-repository.ts +0 -142
- package/src/repositories/prisma-client.ts +0 -25
- package/src/routes/commit-routes.ts +0 -10
- package/src/routes/config-routes.ts +0 -14
- package/src/routes/health-routes.ts +0 -14
- package/src/routes/package-routes.ts +0 -22
- package/src/serve.ts +0 -41
- package/src/services/commit-service.ts +0 -44
- package/src/services/config-service.ts +0 -391
- package/src/services/git-service.ts +0 -140
- package/src/services/health-service.ts +0 -162
- package/src/services/package-service.ts +0 -228
- package/src/setup.ts +0 -78
- package/src/types/ci.ts +0 -122
- package/src/types/config.ts +0 -22
- package/src/types/database.ts +0 -67
- package/src/types/git.ts +0 -33
- package/src/types/health.ts +0 -11
- package/src/types/index.ts +0 -23
- package/src/types/package.ts +0 -39
- package/src/types/scanner.ts +0 -31
- package/src/types/swagger-jsdoc.d.ts +0 -15
- package/src/utils/ci-status.ts +0 -535
- package/src/utils/db-utils.ts +0 -92
- package/src/utils/health-utils.ts +0 -67
- package/src/utils/monorepo-scanner.ts +0 -576
- package/src/utils/utilities.ts +0 -427
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { Request, Response } from 'express';
|
|
2
|
-
import { AppLogger } from '../middleware/logger';
|
|
3
|
-
import { getConfigurationFilesService, updateConfigFileService, updatePackageConfigurationService } from '../services/config-service';
|
|
4
|
-
|
|
5
|
-
export const getConfigurationFiles = async (_req: Request, res: Response) => {
|
|
6
|
-
try {
|
|
7
|
-
const rootDir = _req.app.locals.rootPath;
|
|
8
|
-
AppLogger.debug('Monorepo root directory: ' + rootDir);
|
|
9
|
-
|
|
10
|
-
const configFiles = await getConfigurationFilesService(rootDir);
|
|
11
|
-
res.json(configFiles);
|
|
12
|
-
} catch (error) {
|
|
13
|
-
AppLogger.error('Error fetching configuration files', error as Error);
|
|
14
|
-
res.status(500).json({
|
|
15
|
-
success: false,
|
|
16
|
-
error: 'Failed to fetch configuration files',
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const updateConfigFile = async (_req: Request, res: Response) => {
|
|
22
|
-
try {
|
|
23
|
-
const { id } = _req.params;
|
|
24
|
-
const { content } = _req.body;
|
|
25
|
-
|
|
26
|
-
if (!content) {
|
|
27
|
-
return res.status(400).json({
|
|
28
|
-
success: false,
|
|
29
|
-
error: 'Content is required',
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
const rootDir = _req.app.locals.rootPath;
|
|
33
|
-
const result = await updateConfigFileService(id, rootDir, content);
|
|
34
|
-
res.json(result);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
AppLogger.error('Error saving configuration file', error as Error);
|
|
37
|
-
res.status(500).json({
|
|
38
|
-
success: false,
|
|
39
|
-
error: 'Failed to save configuration file',
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Request, Response } from 'express';
|
|
2
|
-
import { AppLogger } from '../middleware/logger';
|
|
3
|
-
import { getHealthSummaryService, healthRefreshService } from '../services/health-service';
|
|
4
|
-
|
|
5
|
-
export const getPackagesHealth = async (_req: Request, res: Response) => {
|
|
6
|
-
try {
|
|
7
|
-
const health = await getHealthSummaryService();
|
|
8
|
-
res.json(health);
|
|
9
|
-
} catch (error) {
|
|
10
|
-
AppLogger.error('Error fetching health data from database:', error as Error);
|
|
11
|
-
res
|
|
12
|
-
.status(500)
|
|
13
|
-
.json({ error: 'Failed to fetch health data from database' });
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const refreshHealth = async (_req: Request, res: Response) => {
|
|
18
|
-
try {
|
|
19
|
-
const health = await healthRefreshService(_req.app.locals.rootPath);
|
|
20
|
-
res.json(health);
|
|
21
|
-
} catch (error) {
|
|
22
|
-
res.status(500).json({ error: 'Failed to fetch health metrics' });
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { Request, Response } from 'express';
|
|
2
|
-
import { AppLogger } from '../middleware/logger';
|
|
3
|
-
import { updatePackageConfigurationService } from '../services/config-service';
|
|
4
|
-
import { getPackageDetailService, getPackagesService, refreshPackagesService } from '../services/package-service';
|
|
5
|
-
|
|
6
|
-
export const getPackages = async (_req: Request, res: Response) => {
|
|
7
|
-
try {
|
|
8
|
-
const transformedPackages = await getPackagesService(_req.app.locals.rootPath);
|
|
9
|
-
res.json(transformedPackages);
|
|
10
|
-
} catch (error) {
|
|
11
|
-
res.status(500).json({ error: 'Failed to fetch packages, ' + error });
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const refreshPackages = async (_req: Request, res: Response) => {
|
|
16
|
-
AppLogger.info('Refreshing packages from source: ' + _req.app.locals.rootPath);
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
const packages = await refreshPackagesService(_req.app.locals.rootPath);
|
|
20
|
-
res.json(packages);
|
|
21
|
-
} catch (error) {
|
|
22
|
-
res.status(500).json({ error: 'Failed to refresh packages' });
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const getPackageDetail = async (_req: Request, res: Response) => {
|
|
27
|
-
const { name } = _req.params;
|
|
28
|
-
try {
|
|
29
|
-
const packageDetail = await getPackageDetailService(name)
|
|
30
|
-
res.json(packageDetail);
|
|
31
|
-
} catch (error) {
|
|
32
|
-
res.status(500).json({ error: 'Failed to fetch package details' });
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export const updatePackageConfig = async (req: Request, res: Response) => {
|
|
37
|
-
try {
|
|
38
|
-
const { packageName, config, packagePath } = req.body;
|
|
39
|
-
|
|
40
|
-
if (!packageName || !config || !packagePath) {
|
|
41
|
-
return res.status(400).json({
|
|
42
|
-
success: false,
|
|
43
|
-
error: 'Package name, configuration, and package path are required',
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
AppLogger.info('Updating package configuration for: ' + packageName);
|
|
48
|
-
AppLogger.debug('Package path: ' + packagePath);
|
|
49
|
-
|
|
50
|
-
const updatedPackage = await updatePackageConfigurationService(packagePath, packageName, config);
|
|
51
|
-
|
|
52
|
-
return res.json({
|
|
53
|
-
success: true,
|
|
54
|
-
message: 'Package configuration updated successfully',
|
|
55
|
-
package: updatedPackage,
|
|
56
|
-
preservedFields: true,
|
|
57
|
-
});
|
|
58
|
-
} catch (error) {
|
|
59
|
-
return res.status(500).json({
|
|
60
|
-
success: false,
|
|
61
|
-
error: 'Failed to update package configuration',
|
|
62
|
-
message:
|
|
63
|
-
error instanceof Error ? error.message : 'Unknown error occurred',
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
package/src/get-db-url.ts
DELETED
package/src/index.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Monodog Application Entry Point
|
|
3
|
-
*
|
|
4
|
-
* This file exports the main server and dashboard startup functions
|
|
5
|
-
* All middleware, security, and error handling logic has been moved to separate files
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export { startServer } from './middleware/server-startup';
|
|
9
|
-
export { serveDashboard } from './middleware/dashboard-startup';
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dashboard server startup logic
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import express from 'express';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import type { Express } from 'express';
|
|
8
|
-
import { httpLogger, AppLogger } from './logger';
|
|
9
|
-
|
|
10
|
-
import { appConfig } from '../config-loader';
|
|
11
|
-
import {
|
|
12
|
-
errorHandler,
|
|
13
|
-
} from './error-handler';
|
|
14
|
-
import {
|
|
15
|
-
createHelmetMiddleware,
|
|
16
|
-
createDashboardCorsMiddleware,
|
|
17
|
-
createTimeoutMiddleware,
|
|
18
|
-
buildApiUrl,
|
|
19
|
-
} from './security';
|
|
20
|
-
import {
|
|
21
|
-
PORT_MIN,
|
|
22
|
-
PORT_MAX,
|
|
23
|
-
PORT_VALIDATION_ERROR_MESSAGE,
|
|
24
|
-
CACHE_CONTROL_NO_CACHE,
|
|
25
|
-
EXPIRES_HEADER,
|
|
26
|
-
PRAGMA_HEADER,
|
|
27
|
-
STATIC_FILE_PATTERN,
|
|
28
|
-
CONTENT_TYPE_JAVASCRIPT,
|
|
29
|
-
ERROR_SERVING_INDEX_HTML,
|
|
30
|
-
MESSAGE_GRACEFUL_SHUTDOWN,
|
|
31
|
-
MESSAGE_DASHBOARD_GRACEFUL_SHUTDOWN,
|
|
32
|
-
MESSAGE_DASHBOARD_CLOSED,
|
|
33
|
-
SUCCESS_DASHBOARD_START,
|
|
34
|
-
ERROR_PORT_IN_USE,
|
|
35
|
-
ERROR_PERMISSION_DENIED,
|
|
36
|
-
} from '../constants';
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Validate port number
|
|
40
|
-
*/
|
|
41
|
-
function validatePort(port: string | number): number {
|
|
42
|
-
const portNum = typeof port === 'string' ? parseInt(port, 10) : port;
|
|
43
|
-
|
|
44
|
-
if (isNaN(portNum) || portNum < PORT_MIN || portNum > PORT_MAX) {
|
|
45
|
-
throw new Error(PORT_VALIDATION_ERROR_MESSAGE(PORT_MIN, PORT_MAX));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return portNum;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Create Express app for dashboard with middleware
|
|
53
|
-
*/
|
|
54
|
-
function createDashboardApp(): Express {
|
|
55
|
-
const app = express();
|
|
56
|
-
|
|
57
|
-
// Timeout middleware
|
|
58
|
-
app.use(createTimeoutMiddleware());
|
|
59
|
-
|
|
60
|
-
// Security setup
|
|
61
|
-
const serverHost = appConfig.server.host === '0.0.0.0'
|
|
62
|
-
? 'localhost'
|
|
63
|
-
: appConfig.server.host;
|
|
64
|
-
const apiUrl = buildApiUrl(serverHost, appConfig.server.port);
|
|
65
|
-
|
|
66
|
-
app.use(createHelmetMiddleware(apiUrl));
|
|
67
|
-
app.use(createDashboardCorsMiddleware());
|
|
68
|
-
|
|
69
|
-
// Environment config endpoint
|
|
70
|
-
app.get('/env-config.js', (_req, res) => {
|
|
71
|
-
res.setHeader('Content-Type', CONTENT_TYPE_JAVASCRIPT);
|
|
72
|
-
res.setHeader('Cache-Control', CACHE_CONTROL_NO_CACHE);
|
|
73
|
-
|
|
74
|
-
res.send(
|
|
75
|
-
`window.ENV = { API_URL: "${apiUrl}" };`
|
|
76
|
-
);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Request logging
|
|
80
|
-
app.use(httpLogger);
|
|
81
|
-
|
|
82
|
-
// SPA routing: serve index.html for non-static routes
|
|
83
|
-
app.use((_req, _res, next) => {
|
|
84
|
-
if (STATIC_FILE_PATTERN.test(_req.path)) {
|
|
85
|
-
next();
|
|
86
|
-
} else {
|
|
87
|
-
_res.header(
|
|
88
|
-
'Cache-Control',
|
|
89
|
-
CACHE_CONTROL_NO_CACHE
|
|
90
|
-
);
|
|
91
|
-
_res.header('Expires', EXPIRES_HEADER);
|
|
92
|
-
_res.header('Pragma', PRAGMA_HEADER);
|
|
93
|
-
_res.sendFile('index.html', {
|
|
94
|
-
root: path.resolve(__dirname, '..', '..', 'monodog-dashboard', 'dist'),
|
|
95
|
-
}, (err: Error | null) => {
|
|
96
|
-
if (err) {
|
|
97
|
-
AppLogger.error(ERROR_SERVING_INDEX_HTML, err);
|
|
98
|
-
_res.status(500).json({ error: 'Internal server error' });
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Static files
|
|
105
|
-
const staticPath = path.join(__dirname, '..', '..', 'monodog-dashboard', 'dist');
|
|
106
|
-
AppLogger.debug('Serving static files from:', { path: staticPath });
|
|
107
|
-
app.use(express.static(staticPath, {
|
|
108
|
-
maxAge: '1d',
|
|
109
|
-
etag: false,
|
|
110
|
-
dotfiles: 'deny',
|
|
111
|
-
}));
|
|
112
|
-
|
|
113
|
-
// Global error handler (must be last)
|
|
114
|
-
app.use(errorHandler);
|
|
115
|
-
|
|
116
|
-
return app;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Start the dashboard server
|
|
121
|
-
*/
|
|
122
|
-
export function serveDashboard(rootPath: string): void {
|
|
123
|
-
try {
|
|
124
|
-
const port = appConfig.dashboard.port;
|
|
125
|
-
const host = appConfig.dashboard.host;
|
|
126
|
-
const validatedPort = validatePort(port);
|
|
127
|
-
|
|
128
|
-
const app = createDashboardApp();
|
|
129
|
-
|
|
130
|
-
const server = app.listen(validatedPort, host, () => {
|
|
131
|
-
console.log(SUCCESS_DASHBOARD_START(host, validatedPort));
|
|
132
|
-
console.log('Press Ctrl+C to quit.');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
136
|
-
if (err.code === 'EADDRINUSE') {
|
|
137
|
-
AppLogger.error(ERROR_PORT_IN_USE(validatedPort), err);
|
|
138
|
-
process.exit(1);
|
|
139
|
-
} else if (err.code === 'EACCES') {
|
|
140
|
-
AppLogger.error(ERROR_PERMISSION_DENIED(validatedPort), err);
|
|
141
|
-
process.exit(1);
|
|
142
|
-
} else {
|
|
143
|
-
AppLogger.error('Server failed to start:', err);
|
|
144
|
-
process.exit(1);
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Graceful shutdown
|
|
149
|
-
process.on('SIGTERM', () => {
|
|
150
|
-
AppLogger.info(MESSAGE_DASHBOARD_GRACEFUL_SHUTDOWN);
|
|
151
|
-
server.close(() => {
|
|
152
|
-
AppLogger.info(MESSAGE_DASHBOARD_CLOSED);
|
|
153
|
-
process.exit(0);
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
} catch (error: unknown) {
|
|
157
|
-
const err = error as Error & { message?: string };
|
|
158
|
-
AppLogger.error('Failed to start dashboard:', err);
|
|
159
|
-
process.exit(1);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error handling middleware for Express
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Request, Response, NextFunction, ErrorRequestHandler } from 'express';
|
|
6
|
-
import { AppLogger } from './logger';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Custom error interface extending Error
|
|
10
|
-
*/
|
|
11
|
-
export interface CustomError extends Error {
|
|
12
|
-
status?: number;
|
|
13
|
-
statusCode?: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Global error handler middleware
|
|
18
|
-
* Must be registered last in the middleware chain
|
|
19
|
-
*/
|
|
20
|
-
export const errorHandler: ErrorRequestHandler = (
|
|
21
|
-
err: CustomError,
|
|
22
|
-
req: Request,
|
|
23
|
-
res: Response,
|
|
24
|
-
_next: NextFunction
|
|
25
|
-
): void => {
|
|
26
|
-
const status = err.status || err.statusCode || 500;
|
|
27
|
-
|
|
28
|
-
AppLogger.error('Request error occurred', {
|
|
29
|
-
status,
|
|
30
|
-
method: req.method,
|
|
31
|
-
path: req.path,
|
|
32
|
-
message: err.message,
|
|
33
|
-
timestamp: new Date().toISOString(),
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
res.status(status).json({
|
|
37
|
-
error: 'Internal server error',
|
|
38
|
-
timestamp: Date.now(),
|
|
39
|
-
});
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 404 Not Found handler
|
|
44
|
-
*/
|
|
45
|
-
export const notFoundHandler = (_req: Request, res: Response): void => {
|
|
46
|
-
res.status(404).json({
|
|
47
|
-
error: 'Endpoint not found',
|
|
48
|
-
timestamp: Date.now(),
|
|
49
|
-
});
|
|
50
|
-
};
|
package/src/middleware/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Middleware exports
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { errorHandler, notFoundHandler } from './error-handler';
|
|
6
|
-
export type { CustomError } from './error-handler';
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
createHelmetMiddleware,
|
|
10
|
-
createApiCorsMiddleware,
|
|
11
|
-
createDashboardCorsMiddleware,
|
|
12
|
-
createTimeoutMiddleware,
|
|
13
|
-
buildApiUrl,
|
|
14
|
-
buildDashboardUrl,
|
|
15
|
-
} from './security';
|
|
16
|
-
|
|
17
|
-
export { startServer } from './server-startup';
|
|
18
|
-
export { serveDashboard } from './dashboard-startup';
|
|
19
|
-
|
|
20
|
-
export { httpLogger, AppLogger } from './logger';
|
package/src/middleware/logger.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Logger configuration using Morgan
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import morgan from 'morgan';
|
|
6
|
-
import fs from 'fs';
|
|
7
|
-
import path from 'path';
|
|
8
|
-
|
|
9
|
-
const accessLogStream = fs.createWriteStream(path.join(__dirname, '../../','access.log'),{flags : 'a'});
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* HTTP request logger middleware using Morgan, only log error responses
|
|
13
|
-
*/
|
|
14
|
-
export const httpLogger = morgan('combined', {
|
|
15
|
-
stream : accessLogStream,
|
|
16
|
-
// skip: function (req, res) { return res.statusCode < 400 }
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Application logger for non-HTTP events
|
|
21
|
-
*/
|
|
22
|
-
export class AppLogger {
|
|
23
|
-
private static readonly prefix = '[APP]';
|
|
24
|
-
|
|
25
|
-
static info(message: string, data?: Record<string, unknown>): void {
|
|
26
|
-
if (process.env.LOG_LEVEL == 'info' || process.env.LOG_LEVEL == 'debug') {
|
|
27
|
-
if (data) {
|
|
28
|
-
console.log(`${this.prefix} [INFO]`, message, JSON.stringify(data, null, 2));
|
|
29
|
-
} else {
|
|
30
|
-
console.log(`${this.prefix} [INFO]`, message);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
static error(message: string, error?: Error | Record<string, unknown>): void {
|
|
36
|
-
if (error instanceof Error) {
|
|
37
|
-
console.error(`${this.prefix} [ERROR]`, message, {
|
|
38
|
-
message: error.message,
|
|
39
|
-
stack: error.stack,
|
|
40
|
-
});
|
|
41
|
-
} else if (error) {
|
|
42
|
-
console.error(`${this.prefix} [ERROR]`, message, error);
|
|
43
|
-
} else {
|
|
44
|
-
console.error(`${this.prefix} [ERROR]`, message);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
static warn(message: string, data?: Record<string, unknown>): void {
|
|
49
|
-
if (data) {
|
|
50
|
-
console.warn(`${this.prefix} [WARN]`, message, JSON.stringify(data, null, 2));
|
|
51
|
-
} else {
|
|
52
|
-
console.warn(`${this.prefix} [WARN]`, message);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
static debug(message: string, data?: Record<string, unknown>): void {
|
|
57
|
-
if (process.env.LOG_LEVEL == 'debug') {
|
|
58
|
-
if (data) {
|
|
59
|
-
console.log(`${this.prefix} [DEBUG]`, message, JSON.stringify(data, null, 2));
|
|
60
|
-
} else {
|
|
61
|
-
console.log(`${this.prefix} [DEBUG]`, message);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Security middleware and configuration
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Request, Response, NextFunction } from 'express';
|
|
6
|
-
import helmet from 'helmet';
|
|
7
|
-
import cors, { CorsOptions } from 'cors';
|
|
8
|
-
import type { MonodogConfig } from '../types/config';
|
|
9
|
-
import {
|
|
10
|
-
REQUEST_TIMEOUT,
|
|
11
|
-
RESPONSE_TIMEOUT,
|
|
12
|
-
CORS_API_METHODS,
|
|
13
|
-
CORS_ALLOWED_HEADERS,
|
|
14
|
-
DEFAULT_LOCALHOST,
|
|
15
|
-
WILDCARD_ADDRESS,
|
|
16
|
-
HTTP_PROTOCOL,
|
|
17
|
-
} from '../constants';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Create Helmet security middleware with Content Security Policy
|
|
21
|
-
*/
|
|
22
|
-
export function createHelmetMiddleware(apiUrl: string) {
|
|
23
|
-
return helmet({
|
|
24
|
-
contentSecurityPolicy: {
|
|
25
|
-
directives: {
|
|
26
|
-
defaultSrc: ["'self'"],
|
|
27
|
-
connectSrc: ["'self'", apiUrl, 'http://localhost:*', 'http://127.0.0.1:*'],
|
|
28
|
-
scriptSrc: ["'self'"],
|
|
29
|
-
imgSrc: ["'self'", 'data:', 'https:'],
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Create CORS middleware for API server
|
|
37
|
-
*/
|
|
38
|
-
export function createApiCorsMiddleware(dashboardUrl: string) {
|
|
39
|
-
const corsOptions: CorsOptions = {
|
|
40
|
-
origin: dashboardUrl,
|
|
41
|
-
credentials: true,
|
|
42
|
-
methods: [...CORS_API_METHODS],
|
|
43
|
-
allowedHeaders: [...CORS_ALLOWED_HEADERS],
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
return cors(corsOptions);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Create CORS middleware for dashboard (no cross-origin)
|
|
51
|
-
*/
|
|
52
|
-
export function createDashboardCorsMiddleware() {
|
|
53
|
-
const corsOptions: CorsOptions = {
|
|
54
|
-
origin: false, // Don't allow any origin for static assets
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
return cors(corsOptions);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Request timeout middleware (30 seconds)
|
|
62
|
-
*/
|
|
63
|
-
export function createTimeoutMiddleware() {
|
|
64
|
-
return (req: Request, res: Response, next: NextFunction): void => {
|
|
65
|
-
req.setTimeout(REQUEST_TIMEOUT);
|
|
66
|
-
res.setTimeout(RESPONSE_TIMEOUT);
|
|
67
|
-
next();
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Build API URL based on config
|
|
73
|
-
*/
|
|
74
|
-
export function buildApiUrl(
|
|
75
|
-
host: string,
|
|
76
|
-
port: number
|
|
77
|
-
): string {
|
|
78
|
-
const apiHost = host === WILDCARD_ADDRESS ? DEFAULT_LOCALHOST : host;
|
|
79
|
-
return `${HTTP_PROTOCOL}${apiHost}:${port}`;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Build dashboard URL based on config
|
|
84
|
-
*/
|
|
85
|
-
export function buildDashboardUrl(config: MonodogConfig): string {
|
|
86
|
-
const dashboardHost = config.dashboard.host === WILDCARD_ADDRESS
|
|
87
|
-
? DEFAULT_LOCALHOST
|
|
88
|
-
: config.dashboard.host;
|
|
89
|
-
return `${HTTP_PROTOCOL}${dashboardHost}:${config.dashboard.port}`;
|
|
90
|
-
}
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server startup logic for the API backend
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import express from 'express';
|
|
6
|
-
import { json } from 'body-parser';
|
|
7
|
-
import type { Express } from 'express';
|
|
8
|
-
import { httpLogger, AppLogger } from './logger';
|
|
9
|
-
|
|
10
|
-
import { appConfig } from '../config-loader';
|
|
11
|
-
import {
|
|
12
|
-
errorHandler,
|
|
13
|
-
notFoundHandler,
|
|
14
|
-
} from './error-handler';
|
|
15
|
-
import {
|
|
16
|
-
createHelmetMiddleware,
|
|
17
|
-
createApiCorsMiddleware,
|
|
18
|
-
createTimeoutMiddleware,
|
|
19
|
-
buildApiUrl,
|
|
20
|
-
buildDashboardUrl,
|
|
21
|
-
} from './security';
|
|
22
|
-
import { setupSwaggerDocs } from './swagger-middleware';
|
|
23
|
-
|
|
24
|
-
import packageRouter from '../routes/package-routes';
|
|
25
|
-
import commitRouter from '../routes/commit-routes';
|
|
26
|
-
import healthRouter from '../routes/health-routes';
|
|
27
|
-
import configRouter from '../routes/config-routes';
|
|
28
|
-
import {
|
|
29
|
-
PORT_MIN,
|
|
30
|
-
PORT_MAX,
|
|
31
|
-
PORT_VALIDATION_ERROR_MESSAGE,
|
|
32
|
-
BODY_PARSER_LIMIT,
|
|
33
|
-
SUCCESS_SERVER_START,
|
|
34
|
-
ERROR_PORT_IN_USE,
|
|
35
|
-
ERROR_PERMISSION_DENIED,
|
|
36
|
-
MESSAGE_GRACEFUL_SHUTDOWN,
|
|
37
|
-
MESSAGE_SERVER_CLOSED,
|
|
38
|
-
} from '../constants';
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Validate port number
|
|
42
|
-
*/
|
|
43
|
-
function validatePort(port: string | number): number {
|
|
44
|
-
const portNum = typeof port === 'string' ? parseInt(port, 10) : port;
|
|
45
|
-
|
|
46
|
-
if (isNaN(portNum) || portNum < PORT_MIN || portNum > PORT_MAX) {
|
|
47
|
-
throw new Error(PORT_VALIDATION_ERROR_MESSAGE(PORT_MIN, PORT_MAX));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return portNum;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Create Express app with middleware configuration
|
|
55
|
-
*/
|
|
56
|
-
function createApp(rootPath: string): Express {
|
|
57
|
-
const app = express();
|
|
58
|
-
|
|
59
|
-
// Timeout middleware
|
|
60
|
-
app.use(createTimeoutMiddleware());
|
|
61
|
-
|
|
62
|
-
// Store root path for routes
|
|
63
|
-
app.locals.rootPath = rootPath;
|
|
64
|
-
|
|
65
|
-
// Security and CORS setup
|
|
66
|
-
const dashboardUrl = buildDashboardUrl(appConfig);
|
|
67
|
-
const apiUrl = buildApiUrl(appConfig.server.host, appConfig.server.port);
|
|
68
|
-
|
|
69
|
-
app.use(createHelmetMiddleware(apiUrl));
|
|
70
|
-
app.use(createApiCorsMiddleware(dashboardUrl));
|
|
71
|
-
|
|
72
|
-
// Body parser
|
|
73
|
-
app.use(json({ limit: BODY_PARSER_LIMIT }));
|
|
74
|
-
|
|
75
|
-
// HTTP request logging with Morgan
|
|
76
|
-
app.use(httpLogger);
|
|
77
|
-
|
|
78
|
-
// Setup Swagger documentation
|
|
79
|
-
setupSwaggerDocs(app);
|
|
80
|
-
|
|
81
|
-
// Routes
|
|
82
|
-
app.use('/api/packages', packageRouter);
|
|
83
|
-
app.use('/api/commits/', commitRouter);
|
|
84
|
-
app.use('/api/health/', healthRouter);
|
|
85
|
-
app.use('/api/config/', configRouter);
|
|
86
|
-
|
|
87
|
-
// 404 handler
|
|
88
|
-
app.use('*', notFoundHandler);
|
|
89
|
-
|
|
90
|
-
// Global error handler (must be last)
|
|
91
|
-
app.use(errorHandler);
|
|
92
|
-
|
|
93
|
-
return app;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Start the API server
|
|
98
|
-
*/
|
|
99
|
-
export function startServer(rootPath: string): void {
|
|
100
|
-
try {
|
|
101
|
-
const port = appConfig.server.port;
|
|
102
|
-
const host = appConfig.server.host;
|
|
103
|
-
const validatedPort = validatePort(port);
|
|
104
|
-
|
|
105
|
-
AppLogger.info(`Starting Monodog API server...`);
|
|
106
|
-
AppLogger.info(`Analyzing monorepo at root: ${rootPath}`);
|
|
107
|
-
|
|
108
|
-
const app = createApp(rootPath);
|
|
109
|
-
|
|
110
|
-
const server = app.listen(validatedPort, host, () => {
|
|
111
|
-
console.log(SUCCESS_SERVER_START(host, validatedPort));
|
|
112
|
-
AppLogger.info('API endpoints available:', {
|
|
113
|
-
endpoints: [
|
|
114
|
-
'POST /api/packages/refresh',
|
|
115
|
-
'GET /api/packages',
|
|
116
|
-
'GET /api/packages/:name',
|
|
117
|
-
'PUT /api/packages/update-config',
|
|
118
|
-
'GET /api/commits/:packagePath',
|
|
119
|
-
'GET /api/health/packages',
|
|
120
|
-
'POST /api/health/refresh',
|
|
121
|
-
'PUT /api/config/files/:id',
|
|
122
|
-
'GET /api/config/files',
|
|
123
|
-
],
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
128
|
-
if (err.code === 'EADDRINUSE') {
|
|
129
|
-
AppLogger.error(ERROR_PORT_IN_USE(validatedPort), err);
|
|
130
|
-
process.exit(1);
|
|
131
|
-
} else if (err.code === 'EACCES') {
|
|
132
|
-
AppLogger.error(ERROR_PERMISSION_DENIED(validatedPort), err);
|
|
133
|
-
process.exit(1);
|
|
134
|
-
} else {
|
|
135
|
-
AppLogger.error('Server failed to start:', err);
|
|
136
|
-
process.exit(1);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// Graceful shutdown
|
|
141
|
-
process.on('SIGTERM', () => {
|
|
142
|
-
AppLogger.info(MESSAGE_GRACEFUL_SHUTDOWN);
|
|
143
|
-
server.close(() => {
|
|
144
|
-
AppLogger.info(MESSAGE_SERVER_CLOSED);
|
|
145
|
-
process.exit(0);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
} catch (error: unknown) {
|
|
149
|
-
const err = error as Error & { message?: string };
|
|
150
|
-
AppLogger.error('Failed to start server:', err);
|
|
151
|
-
process.exit(1);
|
|
152
|
-
}
|
|
153
|
-
}
|