@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.
Files changed (48) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/dist/config/swagger-config.js +345 -0
  4. package/dist/constants/index.js +26 -0
  5. package/dist/constants/middleware.js +71 -0
  6. package/dist/constants/port.js +20 -0
  7. package/dist/constants/security.js +67 -0
  8. package/dist/middleware/dashboard-startup.js +20 -21
  9. package/dist/middleware/error-handler.js +3 -10
  10. package/dist/middleware/index.js +4 -2
  11. package/dist/middleware/logger.js +63 -0
  12. package/dist/middleware/security.js +11 -10
  13. package/dist/middleware/server-startup.js +32 -25
  14. package/dist/middleware/swagger-middleware.js +54 -0
  15. package/dist/routes/health-routes.js +1 -1
  16. package/dist/routes/package-routes.js +1 -1
  17. package/dist/serve.js +15 -2
  18. package/dist/services/health-service.js +84 -64
  19. package/dist/services/package-service.js +23 -1
  20. package/monodog-dashboard/dist/assets/{index-746f6c13.js → index-45e19f29.js} +1 -1
  21. package/monodog-dashboard/dist/index.html +1 -1
  22. package/package.json +14 -4
  23. package/prisma/schema/commit.prisma +11 -0
  24. package/prisma/schema/dependency-info.prisma +12 -0
  25. package/prisma/schema/health-status.prisma +14 -0
  26. package/prisma/schema/package-health.prisma +15 -0
  27. package/prisma/schema/package.prisma +21 -0
  28. package/prisma/schema/schema.prisma +15 -0
  29. package/src/config/swagger-config.ts +344 -0
  30. package/src/constants/index.ts +13 -0
  31. package/src/constants/middleware.ts +83 -0
  32. package/src/constants/port.ts +20 -0
  33. package/src/constants/security.ts +78 -0
  34. package/src/middleware/dashboard-startup.ts +35 -24
  35. package/src/middleware/error-handler.ts +2 -15
  36. package/src/middleware/index.ts +3 -1
  37. package/src/middleware/logger.ts +58 -0
  38. package/src/middleware/security.ts +19 -10
  39. package/src/middleware/server-startup.ts +43 -30
  40. package/src/middleware/swagger-middleware.ts +57 -0
  41. package/src/routes/health-routes.ts +1 -1
  42. package/src/routes/package-routes.ts +1 -1
  43. package/src/serve.ts +19 -3
  44. package/src/services/health-service.ts +103 -79
  45. package/src/services/package-service.ts +27 -1
  46. package/src/types/swagger-jsdoc.d.ts +15 -0
  47. package/prisma/schema.prisma +0 -116
  48. /package/prisma/migrations/{20251219074511_create_unique_composite_key_for_commits → 20251219090102_composite_key_for_table_commits}/migration.sql +0 -0
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Security Constants
3
+ * Defines security-related configuration and constants
4
+ */
5
+
6
+ /**
7
+ * Request timeout duration in milliseconds (30 seconds)
8
+ */
9
+ export const REQUEST_TIMEOUT = 30000;
10
+
11
+ /**
12
+ * Response timeout duration in milliseconds (30 seconds)
13
+ */
14
+ export const RESPONSE_TIMEOUT = 30000;
15
+
16
+ /**
17
+ * CORS methods allowed for API
18
+ */
19
+ export const CORS_API_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'] as const;
20
+
21
+ /**
22
+ * CORS headers allowed
23
+ */
24
+ export const CORS_ALLOWED_HEADERS = ['Content-Type', 'Authorization'] as const;
25
+
26
+ /**
27
+ * Body parser JSON size limit
28
+ */
29
+ export const BODY_PARSER_LIMIT = '1mb';
30
+
31
+ /**
32
+ * Cache control header for no-cache responses
33
+ */
34
+ export const CACHE_CONTROL_NO_CACHE = 'private, no-cache, no-store, must-revalidate';
35
+
36
+ /**
37
+ * Cache control header for static assets
38
+ */
39
+ export const CACHE_CONTROL_STATIC = '1d';
40
+
41
+ /**
42
+ * Default localhost hostname
43
+ */
44
+ export const DEFAULT_LOCALHOST = 'localhost';
45
+
46
+ /**
47
+ * Wildcard address for listening on all interfaces
48
+ */
49
+ export const WILDCARD_ADDRESS = '0.0.0.0';
50
+
51
+ /**
52
+ * HTTP protocol prefix
53
+ */
54
+ export const HTTP_PROTOCOL = 'http://';
55
+
56
+ /**
57
+ * CSP directives for Helmet
58
+ */
59
+ export const CSP_DIRECTIVES = {
60
+ defaultSrc: ["'self'"],
61
+ scriptSrc: ["'self'"],
62
+ imgSrc: ["'self'", 'data:', 'https:'],
63
+ } as const;
64
+
65
+ /**
66
+ * Static file extensions pattern
67
+ */
68
+ export const STATIC_FILE_PATTERN = /(.ico|.js|.css|.jpg|.png|.map|.woff|.woff2|.ttf)$/i;
69
+
70
+ /**
71
+ * Expires header for no-cache responses
72
+ */
73
+ export const EXPIRES_HEADER = '-1';
74
+
75
+ /**
76
+ * Pragma header for no-cache responses
77
+ */
78
+ export const PRAGMA_HEADER = 'no-cache';
@@ -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,
@@ -17,10 +17,23 @@ import {
17
17
  createTimeoutMiddleware,
18
18
  buildApiUrl,
19
19
  } from './security';
20
-
21
- // Security constants
22
- const PORT_MIN = 1024;
23
- const PORT_MAX = 65535;
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';
24
37
 
25
38
  /**
26
39
  * Validate port number
@@ -29,7 +42,7 @@ function validatePort(port: string | number): number {
29
42
  const portNum = typeof port === 'string' ? parseInt(port, 10) : port;
30
43
 
31
44
  if (isNaN(portNum) || portNum < PORT_MIN || portNum > PORT_MAX) {
32
- throw new Error(`Port must be between ${PORT_MIN} and ${PORT_MAX}`);
45
+ throw new Error(PORT_VALIDATION_ERROR_MESSAGE(PORT_MIN, PORT_MAX));
33
46
  }
34
47
 
35
48
  return portNum;
@@ -55,8 +68,8 @@ function createDashboardApp(): Express {
55
68
 
56
69
  // Environment config endpoint
57
70
  app.get('/env-config.js', (_req, res) => {
58
- res.setHeader('Content-Type', 'application/javascript');
59
- res.setHeader('Cache-Control', 'private, no-cache, no-store, must-revalidate');
71
+ res.setHeader('Content-Type', CONTENT_TYPE_JAVASCRIPT);
72
+ res.setHeader('Cache-Control', CACHE_CONTROL_NO_CACHE);
60
73
 
61
74
  res.send(
62
75
  `window.ENV = { API_URL: "${apiUrl}" };`
@@ -64,24 +77,24 @@ function createDashboardApp(): Express {
64
77
  });
65
78
 
66
79
  // Request logging
67
- app.use(requestLogger);
80
+ app.use(httpLogger);
68
81
 
69
82
  // SPA routing: serve index.html for non-static routes
70
83
  app.use((_req, _res, next) => {
71
- if (/(.ico|.js|.css|.jpg|.png|.map|.woff|.woff2|.ttf)$/i.test(_req.path)) {
84
+ if (STATIC_FILE_PATTERN.test(_req.path)) {
72
85
  next();
73
86
  } else {
74
87
  _res.header(
75
88
  'Cache-Control',
76
- 'private, no-cache, no-store, must-revalidate'
89
+ CACHE_CONTROL_NO_CACHE
77
90
  );
78
- _res.header('Expires', '-1');
79
- _res.header('Pragma', 'no-cache');
91
+ _res.header('Expires', EXPIRES_HEADER);
92
+ _res.header('Pragma', PRAGMA_HEADER);
80
93
  _res.sendFile('index.html', {
81
94
  root: path.resolve(__dirname, '..', '..', 'monodog-dashboard', 'dist'),
82
95
  }, (err: Error | null) => {
83
96
  if (err) {
84
- console.error('Error serving index.html:', (err as Error & { message?: string })?.message);
97
+ AppLogger.error(ERROR_SERVING_INDEX_HTML, err);
85
98
  _res.status(500).json({ error: 'Internal server error' });
86
99
  }
87
100
  });
@@ -90,7 +103,7 @@ function createDashboardApp(): Express {
90
103
 
91
104
  // Static files
92
105
  const staticPath = path.join(__dirname, '..', '..', 'monodog-dashboard', 'dist');
93
- console.log('Serving static files from:', staticPath);
106
+ AppLogger.debug('Serving static files from:', { path: staticPath });
94
107
  app.use(express.static(staticPath, {
95
108
  maxAge: '1d',
96
109
  etag: false,
@@ -115,36 +128,34 @@ export function serveDashboard(rootPath: string): void {
115
128
  const app = createDashboardApp();
116
129
 
117
130
  const server = app.listen(validatedPort, host, () => {
118
- console.log(`Dashboard listening on http://${host}:${validatedPort}`);
131
+ console.log(SUCCESS_DASHBOARD_START(host, validatedPort));
119
132
  console.log('Press Ctrl+C to quit.');
120
133
  });
121
134
 
122
135
  server.on('error', (err: NodeJS.ErrnoException) => {
123
136
  if (err.code === 'EADDRINUSE') {
124
- console.error(`Error: Port ${validatedPort} is already in use.`);
137
+ AppLogger.error(ERROR_PORT_IN_USE(validatedPort), err);
125
138
  process.exit(1);
126
139
  } else if (err.code === 'EACCES') {
127
- console.error(
128
- `Error: Permission denied to listen on port ${validatedPort}.`
129
- );
140
+ AppLogger.error(ERROR_PERMISSION_DENIED(validatedPort), err);
130
141
  process.exit(1);
131
142
  } else {
132
- console.error('Server failed to start:', err.message);
143
+ AppLogger.error('Server failed to start:', err);
133
144
  process.exit(1);
134
145
  }
135
146
  });
136
147
 
137
148
  // Graceful shutdown
138
149
  process.on('SIGTERM', () => {
139
- console.log('SIGTERM signal received: closing dashboard server');
150
+ AppLogger.info(MESSAGE_DASHBOARD_GRACEFUL_SHUTDOWN);
140
151
  server.close(() => {
141
- console.log('Dashboard server closed');
152
+ AppLogger.info(MESSAGE_DASHBOARD_CLOSED);
142
153
  process.exit(0);
143
154
  });
144
155
  });
145
156
  } catch (error: unknown) {
146
157
  const err = error as Error & { message?: string };
147
- console.error('Failed to start dashboard:', err?.message || String(error));
158
+ AppLogger.error('Failed to start dashboard:', err);
148
159
  process.exit(1);
149
160
  }
150
161
  }
@@ -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
- console.error('[ERROR]', {
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
- };
@@ -2,7 +2,7 @@
2
2
  * Middleware exports
3
3
  */
4
4
 
5
- export { errorHandler, notFoundHandler, requestLogger } from './error-handler';
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
+ }
@@ -6,6 +6,15 @@ import { Request, Response, NextFunction } from 'express';
6
6
  import helmet from 'helmet';
7
7
  import cors, { CorsOptions } from 'cors';
8
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';
9
18
 
10
19
  /**
11
20
  * Create Helmet security middleware with Content Security Policy
@@ -28,10 +37,10 @@ export function createHelmetMiddleware(apiUrl: string) {
28
37
  */
29
38
  export function createApiCorsMiddleware(dashboardUrl: string) {
30
39
  const corsOptions: CorsOptions = {
31
- origin: process.env.CORS_ORIGIN || dashboardUrl,
40
+ origin: dashboardUrl,
32
41
  credentials: true,
33
- methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
34
- allowedHeaders: ['Content-Type', 'Authorization'],
42
+ methods: [...CORS_API_METHODS],
43
+ allowedHeaders: [...CORS_ALLOWED_HEADERS],
35
44
  };
36
45
 
37
46
  return cors(corsOptions);
@@ -53,8 +62,8 @@ export function createDashboardCorsMiddleware() {
53
62
  */
54
63
  export function createTimeoutMiddleware() {
55
64
  return (req: Request, res: Response, next: NextFunction): void => {
56
- req.setTimeout(30000);
57
- res.setTimeout(30000);
65
+ req.setTimeout(REQUEST_TIMEOUT);
66
+ res.setTimeout(RESPONSE_TIMEOUT);
58
67
  next();
59
68
  };
60
69
  }
@@ -66,16 +75,16 @@ export function buildApiUrl(
66
75
  host: string,
67
76
  port: number
68
77
  ): string {
69
- const apiHost = host === '0.0.0.0' ? 'localhost' : host;
70
- return process.env.API_URL || `http://${apiHost}:${port}`;
78
+ const apiHost = host === WILDCARD_ADDRESS ? DEFAULT_LOCALHOST : host;
79
+ return `${HTTP_PROTOCOL}${apiHost}:${port}`;
71
80
  }
72
81
 
73
82
  /**
74
83
  * Build dashboard URL based on config
75
84
  */
76
85
  export function buildDashboardUrl(config: MonodogConfig): string {
77
- const dashboardHost = config.dashboard.host === '0.0.0.0'
78
- ? 'localhost'
86
+ const dashboardHost = config.dashboard.host === WILDCARD_ADDRESS
87
+ ? DEFAULT_LOCALHOST
79
88
  : config.dashboard.host;
80
- return `http://${dashboardHost}:${config.dashboard.port}`;
89
+ return `${HTTP_PROTOCOL}${dashboardHost}:${config.dashboard.port}`;
81
90
  }
@@ -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,
@@ -19,15 +19,23 @@ import {
19
19
  buildApiUrl,
20
20
  buildDashboardUrl,
21
21
  } from './security';
22
+ import { setupSwaggerDocs } from './swagger-middleware';
22
23
 
23
24
  import packageRouter from '../routes/package-routes';
24
25
  import commitRouter from '../routes/commit-routes';
25
26
  import healthRouter from '../routes/health-routes';
26
27
  import configRouter from '../routes/config-routes';
27
-
28
- // Security constants
29
- const PORT_MIN = 1024;
30
- const PORT_MAX = 65535;
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';
31
39
 
32
40
  /**
33
41
  * Validate port number
@@ -36,7 +44,7 @@ function validatePort(port: string | number): number {
36
44
  const portNum = typeof port === 'string' ? parseInt(port, 10) : port;
37
45
 
38
46
  if (isNaN(portNum) || portNum < PORT_MIN || portNum > PORT_MAX) {
39
- throw new Error(`Port must be between ${PORT_MIN} and ${PORT_MAX}`);
47
+ throw new Error(PORT_VALIDATION_ERROR_MESSAGE(PORT_MIN, PORT_MAX));
40
48
  }
41
49
 
42
50
  return portNum;
@@ -62,10 +70,13 @@ function createApp(rootPath: string): Express {
62
70
  app.use(createApiCorsMiddleware(dashboardUrl));
63
71
 
64
72
  // Body parser
65
- app.use(json({ limit: '1mb' }));
73
+ app.use(json({ limit: BODY_PARSER_LIMIT }));
74
+
75
+ // HTTP request logging with Morgan
76
+ app.use(httpLogger);
66
77
 
67
- // Request logging
68
- app.use(requestLogger);
78
+ // Setup Swagger documentation
79
+ setupSwaggerDocs(app);
69
80
 
70
81
  // Routes
71
82
  app.use('/api/packages', packageRouter);
@@ -91,50 +102,52 @@ export function startServer(rootPath: string): void {
91
102
  const host = appConfig.server.host;
92
103
  const validatedPort = validatePort(port);
93
104
 
105
+ AppLogger.info(`Starting Monodog API server...`);
106
+ AppLogger.info(`Analyzing monorepo at root: ${rootPath}`);
107
+
94
108
  const app = createApp(rootPath);
95
109
 
96
110
  const server = app.listen(validatedPort, host, () => {
97
- console.log(`Backend server running on http://${host}:${validatedPort}`);
98
- console.log('API endpoints available:');
99
- console.log(' - GET /api/health');
100
- console.log(' - GET /api/packages/refresh');
101
- console.log(' - GET /api/packages');
102
- console.log(' - GET /api/packages/:name');
103
- console.log(' - PUT /api/packages/update-config');
104
- console.log(' - GET /api/commits/:packagePath');
105
- console.log(' - GET /api/health/packages');
106
- console.log(' - PUT /api/config/files/:id');
107
- console.log(' - GET /api/config/files');
111
+ console.log(SUCCESS_SERVER_START(host, validatedPort));
112
+ AppLogger.info('API endpoints available:', {
113
+ endpoints: [
114
+ 'GET /api/health',
115
+ 'GET /api/packages/refresh',
116
+ 'GET /api/packages',
117
+ 'GET /api/packages/:name',
118
+ 'PUT /api/packages/update-config',
119
+ 'GET /api/commits/:packagePath',
120
+ 'GET /api/health/packages',
121
+ 'PUT /api/config/files/:id',
122
+ 'GET /api/config/files',
123
+ ],
124
+ });
108
125
  });
109
126
 
110
127
  server.on('error', (err: NodeJS.ErrnoException) => {
111
128
  if (err.code === 'EADDRINUSE') {
112
- console.error(
113
- `Error: Port ${validatedPort} is already in use. Please specify a different port.`
114
- );
129
+ AppLogger.error(ERROR_PORT_IN_USE(validatedPort), err);
115
130
  process.exit(1);
116
131
  } else if (err.code === 'EACCES') {
117
- console.error(
118
- `Error: Permission denied to listen on port ${validatedPort}. Use a port above 1024.`
119
- );
132
+ AppLogger.error(ERROR_PERMISSION_DENIED(validatedPort), err);
120
133
  process.exit(1);
121
134
  } else {
122
- console.error('Server failed to start:', err.message);
135
+ AppLogger.error('Server failed to start:', err);
123
136
  process.exit(1);
124
137
  }
125
138
  });
126
139
 
127
140
  // Graceful shutdown
128
141
  process.on('SIGTERM', () => {
129
- console.log('SIGTERM signal received: closing HTTP server');
142
+ AppLogger.info(MESSAGE_GRACEFUL_SHUTDOWN);
130
143
  server.close(() => {
131
- console.log('HTTP server closed');
144
+ AppLogger.info(MESSAGE_SERVER_CLOSED);
132
145
  process.exit(0);
133
146
  });
134
147
  });
135
148
  } catch (error: unknown) {
136
149
  const err = error as Error & { message?: string };
137
- console.error('Failed to start server:', err?.message || String(error));
150
+ AppLogger.error('Failed to start server:', err);
138
151
  process.exit(1);
139
152
  }
140
153
  }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Swagger Documentation Middleware
3
+ * Sets up Swagger UI for API documentation
4
+ */
5
+
6
+ import swaggerUi from 'swagger-ui-express';
7
+ import swaggerJsDoc from 'swagger-jsdoc';
8
+ import type { Express, Request, Response } from 'express';
9
+ import { swaggerOptions } from '../config/swagger-config';
10
+
11
+ /**
12
+ * Setup Swagger documentation endpoint
13
+ * @param app Express application instance
14
+ */
15
+ export function setupSwaggerDocs(app: Express): void {
16
+ try {
17
+ const specs = swaggerJsDoc(swaggerOptions) as Record<string, unknown>;
18
+
19
+ // Serve raw Swagger JSON FIRST (before the middleware catches all /api-docs paths)
20
+ app.get('/api-docs/swagger.json', (_req: Request, res: Response) => {
21
+ res.setHeader('Content-Type', 'application/json');
22
+ res.send(specs);
23
+ });
24
+
25
+ // Serve Swagger UI at /api-docs
26
+ app.use(
27
+ '/api-docs',
28
+ swaggerUi.serve,
29
+ swaggerUi.setup(specs, {
30
+ swaggerOptions: {
31
+ url: '/api-docs/swagger.json',
32
+ persistAuthorization: true,
33
+ displayOperationId: true,
34
+ filter: true,
35
+ showExtensions: true,
36
+ },
37
+ customCss: `
38
+ .swagger-ui .topbar {
39
+ background-color: #2c3e50;
40
+ }
41
+ .swagger-ui .info .title {
42
+ color: #2c3e50;
43
+ font-weight: bold;
44
+ }
45
+ .swagger-ui .btn-box .btn {
46
+ background-color: #2c3e50;
47
+ }
48
+ `,
49
+ customCssUrl: 'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui.min.css',
50
+ })
51
+ );
52
+
53
+ console.log('Swagger documentation available at /api-docs');
54
+ } catch (error) {
55
+ console.error('Failed to setup Swagger documentation:', error);
56
+ }
57
+ }
@@ -5,7 +5,7 @@ const healthRouter = express.Router();
5
5
 
6
6
  healthRouter
7
7
  .route('/refresh')
8
- .get(refreshHealth);
8
+ .post(refreshHealth);
9
9
 
10
10
  healthRouter
11
11
  .route('/packages')
@@ -5,7 +5,7 @@ const packageRouter = express.Router();
5
5
 
6
6
  packageRouter
7
7
  .route('/refresh')
8
- .get(refreshPackages);
8
+ .post(refreshPackages);
9
9
 
10
10
  packageRouter
11
11
  .route('/update-config')
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
- const rootPath = findMonorepoRoot();
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
- console.log(`Starting Monodog API server...`);
18
- console.log(`Analyzing monorepo at root: ${rootPath}`);
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