@monodog/backend 1.2.2 → 1.2.4
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/dist/cli.js +62 -2
- package/dist/config-loader.js +97 -0
- package/dist/index.js +145 -19
- package/get-db-url.ts +14 -0
- package/package.json +22 -16
- package/prisma/migrations/20251111091920_/migration.sql +16 -0
- package/prisma/schema.prisma +1 -1
- package/src/cli.ts +67 -3
- package/src/config-loader.ts +88 -0
- package/src/index.ts +165 -21
- package/LICENCE +0 -21
- package/prisma/monolite.db-journal +0 -0
package/dist/cli.js
CHANGED
|
@@ -45,12 +45,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
const fs = __importStar(require("fs"));
|
|
46
46
|
const path = __importStar(require("path"));
|
|
47
47
|
const index_1 = require("./index"); // Assume index.ts exports this function
|
|
48
|
+
const config_loader_1 = require("./config-loader");
|
|
49
|
+
const appConfig = (0, config_loader_1.loadConfig)();
|
|
48
50
|
// --- Argument Parsing ---
|
|
49
51
|
// 1. Get arguments excluding the node executable and script name
|
|
50
52
|
const args = process.argv.slice(2);
|
|
51
53
|
// Default settings
|
|
54
|
+
const DEFAULT_PORT = 4000;
|
|
52
55
|
let serve = false;
|
|
53
|
-
let rootPath = process.cwd(); // Default to the current working directory
|
|
56
|
+
let rootPath = path.resolve(appConfig.workspace.root_dir ?? process.cwd()); // Default to the current working directory
|
|
57
|
+
let port = appConfig.server.port ?? DEFAULT_PORT; //Default port
|
|
58
|
+
let host = appConfig.server.host ?? 'localhost'; //Default port
|
|
54
59
|
// Simple argument parsing loop
|
|
55
60
|
for (let i = 0; i < args.length; i++) {
|
|
56
61
|
const arg = args[i];
|
|
@@ -68,6 +73,22 @@ for (let i = 0; i < args.length; i++) {
|
|
|
68
73
|
process.exit(1);
|
|
69
74
|
}
|
|
70
75
|
}
|
|
76
|
+
else if (arg === '--port') {
|
|
77
|
+
// Look at the next argument for the port number
|
|
78
|
+
if (i + 1 < args.length) {
|
|
79
|
+
const portValue = parseInt(args[i + 1], 10);
|
|
80
|
+
if (isNaN(portValue) || portValue <= 0 || portValue > 65535) {
|
|
81
|
+
console.error('Error: --port requires a valid port number (1-65535).');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
port = portValue;
|
|
85
|
+
i++; // Skip the next argument
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
console.error('Error: --port requires a number argument.');
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
71
92
|
else if (arg === '-h' || arg === '--help') {
|
|
72
93
|
console.log(`
|
|
73
94
|
Monodog CLI - Monorepo Analysis Engine
|
|
@@ -88,10 +109,12 @@ Example:
|
|
|
88
109
|
}
|
|
89
110
|
// --- Execution Logic ---
|
|
90
111
|
if (serve) {
|
|
112
|
+
console.log(`\nInitializing Configurations...`);
|
|
113
|
+
createConfigFileIfMissing(rootPath ?? process.cwd());
|
|
91
114
|
console.log(`Starting Monodog API server...`);
|
|
92
115
|
console.log(`Analyzing monorepo at root: ${rootPath}`);
|
|
93
116
|
// Start the Express server and begin analysis
|
|
94
|
-
(0, index_1.startServer)(rootPath);
|
|
117
|
+
(0, index_1.startServer)(rootPath, port, host);
|
|
95
118
|
copyPackageToWorkspace(rootPath);
|
|
96
119
|
}
|
|
97
120
|
else {
|
|
@@ -167,3 +190,40 @@ function copyPackageToWorkspace(rootDir) {
|
|
|
167
190
|
process.exit(1);
|
|
168
191
|
}
|
|
169
192
|
}
|
|
193
|
+
function createConfigFileIfMissing(rootPath) {
|
|
194
|
+
// --- CONFIGURATION ---
|
|
195
|
+
const configFileName = 'monodog-conf.json';
|
|
196
|
+
const configFilePath = path.resolve(rootPath, configFileName);
|
|
197
|
+
// The default content for the configuration file
|
|
198
|
+
const defaultContent = {
|
|
199
|
+
"workspace": {
|
|
200
|
+
"root_dir": "./packages/backend"
|
|
201
|
+
},
|
|
202
|
+
"database": {
|
|
203
|
+
"path": "./monodog.db"
|
|
204
|
+
},
|
|
205
|
+
"server": {
|
|
206
|
+
"host": "0.0.0.0",
|
|
207
|
+
"port": 4000
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const contentString = JSON.stringify(defaultContent, null, 2);
|
|
211
|
+
// ---------------------
|
|
212
|
+
console.log(`\n[monodog] Checking for ${configFileName}...`);
|
|
213
|
+
if (fs.existsSync(configFilePath)) {
|
|
214
|
+
console.log(`[monodog] ${configFileName} already exists. Skipping creation.`);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
try {
|
|
218
|
+
// Write the default content to the file
|
|
219
|
+
fs.writeFileSync(configFilePath, contentString, 'utf-8');
|
|
220
|
+
console.log(`[monodog] Successfully generated default ${configFileName} in the workspace root.`);
|
|
221
|
+
console.log('[monodog] Please review and update settings like "host" and "port".');
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
225
|
+
console.error(`[monodog Error] Failed to generate ${configFileName}:`, message);
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadConfig = loadConfig;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
// Global variable to hold the loaded config
|
|
40
|
+
let config = null;
|
|
41
|
+
/**
|
|
42
|
+
* Loads the monodog-conf.json file from the monorepo root.
|
|
43
|
+
* This should be called only once during application startup.
|
|
44
|
+
* @returns The application configuration object.
|
|
45
|
+
*/
|
|
46
|
+
function loadConfig() {
|
|
47
|
+
if (config) {
|
|
48
|
+
return config; // Return cached config if already loaded
|
|
49
|
+
}
|
|
50
|
+
// 1. Determine the path to the config file
|
|
51
|
+
// We assume the backend package is running from the monorepo root (cwd is root)
|
|
52
|
+
// or that we can navigate up to the root from the current file's location.
|
|
53
|
+
const rootPath = path.resolve(__dirname, '../../../'); // Adjust based on your folder depth if needed
|
|
54
|
+
const configPath = path.resolve(rootPath, 'monodog-conf.json');
|
|
55
|
+
if (!fs.existsSync(configPath)) {
|
|
56
|
+
console.error(`ERROR: Configuration file not found at ${configPath}`);
|
|
57
|
+
// A missing config is a critical failure, so we exit
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
// 2. Read and parse the JSON file
|
|
62
|
+
const fileContent = fs.readFileSync(configPath, 'utf-8');
|
|
63
|
+
const parsedConfig = JSON.parse(fileContent);
|
|
64
|
+
// 3. Optional: Add validation logic here (e.g., check if ports are numbers)
|
|
65
|
+
// Cache and return
|
|
66
|
+
config = parsedConfig;
|
|
67
|
+
process.stderr.write(`[Config] Loaded configuration from: ${configPath}`);
|
|
68
|
+
return config;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error("ERROR: Failed to read or parse monodog-conf.json.");
|
|
72
|
+
console.error(error);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// --- Example Usage ---
|
|
77
|
+
// In your main application file (e.g., packages/backend/src/index.ts):
|
|
78
|
+
/*
|
|
79
|
+
|
|
80
|
+
import { loadConfig } from './config-loader';
|
|
81
|
+
|
|
82
|
+
// Load configuration on startup
|
|
83
|
+
const appConfig = loadConfig();
|
|
84
|
+
|
|
85
|
+
// Access the variables easily
|
|
86
|
+
const dbHost = appConfig.database.host;
|
|
87
|
+
const serverPort = appConfig.server.port;
|
|
88
|
+
const workspaceRoot = appConfig.workspace.root_dir;
|
|
89
|
+
|
|
90
|
+
console.log(`\nStarting server on port: ${serverPort}`);
|
|
91
|
+
console.log(`Database connecting to host: ${dbHost}`);
|
|
92
|
+
|
|
93
|
+
// Example server start logic
|
|
94
|
+
// app.listen(serverPort, appConfig.server.host, () => {
|
|
95
|
+
// console.log(`Server running at http://${appConfig.server.host}:${serverPort}`);
|
|
96
|
+
// });
|
|
97
|
+
*/
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,6 @@ const express_1 = __importDefault(require("express"));
|
|
|
8
8
|
const cors_1 = __importDefault(require("cors"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
|
-
// import { glob } from 'glob';
|
|
12
11
|
const body_parser_1 = require("body-parser");
|
|
13
12
|
const monorepo_scanner_1 = require("@monodog/monorepo-scanner");
|
|
14
13
|
const ci_status_1 = require("@monodog/ci-status");
|
|
@@ -19,11 +18,9 @@ const client_1 = require("@prisma/client");
|
|
|
19
18
|
// import { validateConfig } from '../../apps/dashboard/src/components/modules/config-inspector/utils/config.utils';
|
|
20
19
|
const gitService_1 = require("./gitService");
|
|
21
20
|
const prisma = new client_1.PrismaClient();
|
|
22
|
-
const DEFAULT_PORT = 4000;
|
|
23
21
|
// The main function exported and called by the CLI
|
|
24
|
-
function startServer(rootPath) {
|
|
22
|
+
function startServer(rootPath, port, host) {
|
|
25
23
|
const app = (0, express_1.default)();
|
|
26
|
-
const port = process.env.PORT ? parseInt(process.env.PORT) : DEFAULT_PORT;
|
|
27
24
|
// --- Middleware ---
|
|
28
25
|
// 1. Logging Middleware
|
|
29
26
|
app.use((_req, _res, next) => {
|
|
@@ -32,17 +29,6 @@ function startServer(rootPath) {
|
|
|
32
29
|
});
|
|
33
30
|
app.use((0, cors_1.default)());
|
|
34
31
|
app.use((0, body_parser_1.json)());
|
|
35
|
-
// // 2. CORS (Critical for the frontend app to talk to this local server)
|
|
36
|
-
// // In a production setup, this would be highly restricted, but for local monorepo tools,
|
|
37
|
-
// // we often allow all origins or restrict to a known local hostname/port.
|
|
38
|
-
// app.use((_req: Request, res: Response, next: NextFunction) => {
|
|
39
|
-
// res.setHeader('Access-Control-Allow-Origin', '*'); // Adjust this in production
|
|
40
|
-
// res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
|
|
41
|
-
// res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
42
|
-
// next();
|
|
43
|
-
// });
|
|
44
|
-
// // 3. JSON body parser
|
|
45
|
-
// app.use(express.json());
|
|
46
32
|
// Health check
|
|
47
33
|
app.get('/api/health', (_, res) => {
|
|
48
34
|
res.json({
|
|
@@ -1080,6 +1066,144 @@ function startServer(rootPath) {
|
|
|
1080
1066
|
});
|
|
1081
1067
|
}
|
|
1082
1068
|
});
|
|
1069
|
+
// Update package configuration with path from frontend - preserving all fields, no backups
|
|
1070
|
+
app.put('/api/packages/update', async (req, res) => {
|
|
1071
|
+
try {
|
|
1072
|
+
const { packageName, config, packagePath } = req.body;
|
|
1073
|
+
if (!packageName || !config || !packagePath) {
|
|
1074
|
+
return res.status(400).json({
|
|
1075
|
+
success: false,
|
|
1076
|
+
error: 'Package name, configuration, and package path are required',
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
console.log('💾 Updating package configuration for:', packageName);
|
|
1080
|
+
console.log('📁 Package path:', packagePath);
|
|
1081
|
+
// Validate JSON syntax with better error handling
|
|
1082
|
+
let newConfig;
|
|
1083
|
+
try {
|
|
1084
|
+
newConfig = JSON.parse(config);
|
|
1085
|
+
}
|
|
1086
|
+
catch (error) {
|
|
1087
|
+
console.error('JSON parsing error:', error);
|
|
1088
|
+
return res.status(400).json({
|
|
1089
|
+
success: false,
|
|
1090
|
+
error: 'Invalid JSON configuration',
|
|
1091
|
+
message: `JSON parsing error: ${error instanceof Error ? error.message : 'Invalid format'}`,
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
const packageJsonPath = path_1.default.join(packagePath, 'package.json');
|
|
1095
|
+
// Security check: ensure the path is valid
|
|
1096
|
+
if (!fs_1.default.existsSync(packagePath)) {
|
|
1097
|
+
return res.status(404).json({
|
|
1098
|
+
success: false,
|
|
1099
|
+
error: 'Package directory not found',
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
// Check if package.json exists
|
|
1103
|
+
if (!fs_1.default.existsSync(packageJsonPath)) {
|
|
1104
|
+
return res.status(404).json({
|
|
1105
|
+
success: false,
|
|
1106
|
+
error: 'package.json not found in the specified directory',
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
// Read the existing package.json to preserve all fields
|
|
1110
|
+
const existingContent = await fs_1.default.promises.readFile(packageJsonPath, 'utf8');
|
|
1111
|
+
let existingConfig;
|
|
1112
|
+
try {
|
|
1113
|
+
existingConfig = JSON.parse(existingContent);
|
|
1114
|
+
}
|
|
1115
|
+
catch (error) {
|
|
1116
|
+
return res.status(500).json({
|
|
1117
|
+
success: false,
|
|
1118
|
+
error: 'Existing package.json contains invalid JSON',
|
|
1119
|
+
message: `Error parsing existing package.json: ${error instanceof Error ? error.message : 'Invalid JSON'}`,
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
// Merge the new configuration with existing configuration
|
|
1123
|
+
const mergedConfig = {
|
|
1124
|
+
...existingConfig,
|
|
1125
|
+
name: newConfig.name || existingConfig.name,
|
|
1126
|
+
version: newConfig.version || existingConfig.version,
|
|
1127
|
+
description: newConfig.description !== undefined
|
|
1128
|
+
? newConfig.description
|
|
1129
|
+
: existingConfig.description,
|
|
1130
|
+
license: newConfig.license !== undefined
|
|
1131
|
+
? newConfig.license
|
|
1132
|
+
: existingConfig.license,
|
|
1133
|
+
repository: newConfig.repository || existingConfig.repository,
|
|
1134
|
+
scripts: newConfig.scripts || existingConfig.scripts,
|
|
1135
|
+
dependencies: newConfig.dependencies || existingConfig.dependencies,
|
|
1136
|
+
devDependencies: newConfig.devDependencies || existingConfig.devDependencies,
|
|
1137
|
+
peerDependencies: newConfig.peerDependencies || existingConfig.peerDependencies,
|
|
1138
|
+
};
|
|
1139
|
+
// Write the merged configuration back
|
|
1140
|
+
const formattedConfig = JSON.stringify(mergedConfig, null, 2);
|
|
1141
|
+
await fs_1.default.promises.writeFile(packageJsonPath, formattedConfig, 'utf8');
|
|
1142
|
+
// Update the package in the database - use correct field names based on your Prisma schema
|
|
1143
|
+
const updateData = {
|
|
1144
|
+
// Use 'lastUpdated' instead of 'updatedAt' based on the error message
|
|
1145
|
+
lastUpdated: new Date(),
|
|
1146
|
+
};
|
|
1147
|
+
// Only update fields that exist in your Prisma schema
|
|
1148
|
+
if (newConfig.version)
|
|
1149
|
+
updateData.version = newConfig.version;
|
|
1150
|
+
if (newConfig.description !== undefined)
|
|
1151
|
+
updateData.description = newConfig.description || '';
|
|
1152
|
+
if (newConfig.license !== undefined)
|
|
1153
|
+
updateData.license = newConfig.license || '';
|
|
1154
|
+
if (newConfig.scripts)
|
|
1155
|
+
updateData.scripts = JSON.stringify(newConfig.scripts);
|
|
1156
|
+
if (newConfig.repository)
|
|
1157
|
+
updateData.repository = JSON.stringify(newConfig.repository);
|
|
1158
|
+
if (newConfig.dependencies)
|
|
1159
|
+
updateData.dependencies = JSON.stringify(newConfig.dependencies);
|
|
1160
|
+
if (newConfig.devDependencies)
|
|
1161
|
+
updateData.devDependencies = JSON.stringify(newConfig.devDependencies);
|
|
1162
|
+
if (newConfig.peerDependencies)
|
|
1163
|
+
updateData.peerDependencies = JSON.stringify(newConfig.peerDependencies);
|
|
1164
|
+
console.log('📝 Updating database with:', updateData);
|
|
1165
|
+
const updatedPackage = await prisma.package.update({
|
|
1166
|
+
where: { name: packageName },
|
|
1167
|
+
data: updateData,
|
|
1168
|
+
});
|
|
1169
|
+
// Transform the response
|
|
1170
|
+
const transformedPackage = {
|
|
1171
|
+
...updatedPackage,
|
|
1172
|
+
maintainers: updatedPackage.maintainers
|
|
1173
|
+
? JSON.parse(updatedPackage.maintainers)
|
|
1174
|
+
: [],
|
|
1175
|
+
scripts: updatedPackage.scripts ? JSON.parse(updatedPackage.scripts) : {},
|
|
1176
|
+
repository: updatedPackage.repository
|
|
1177
|
+
? JSON.parse(updatedPackage.repository)
|
|
1178
|
+
: {},
|
|
1179
|
+
dependencies: updatedPackage.dependencies
|
|
1180
|
+
? JSON.parse(updatedPackage.dependencies)
|
|
1181
|
+
: {},
|
|
1182
|
+
devDependencies: updatedPackage.devDependencies
|
|
1183
|
+
? JSON.parse(updatedPackage.devDependencies)
|
|
1184
|
+
: {},
|
|
1185
|
+
peerDependencies: updatedPackage.peerDependencies
|
|
1186
|
+
? JSON.parse(updatedPackage.peerDependencies)
|
|
1187
|
+
: {},
|
|
1188
|
+
};
|
|
1189
|
+
// Return success response
|
|
1190
|
+
return res.json({
|
|
1191
|
+
success: true,
|
|
1192
|
+
message: 'Package configuration updated successfully',
|
|
1193
|
+
package: transformedPackage,
|
|
1194
|
+
preservedFields: true,
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
catch (error) {
|
|
1198
|
+
console.error('Error updating package configuration:', error);
|
|
1199
|
+
// Ensure we always return JSON, even for errors
|
|
1200
|
+
return res.status(500).json({
|
|
1201
|
+
success: false,
|
|
1202
|
+
error: 'Failed to update package configuration',
|
|
1203
|
+
message: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1083
1207
|
// Error handling middleware
|
|
1084
1208
|
app.use((error, _req, res, _next) => {
|
|
1085
1209
|
console.error('Error:', error);
|
|
@@ -1096,9 +1220,11 @@ function startServer(rootPath) {
|
|
|
1096
1220
|
timestamp: Date.now(),
|
|
1097
1221
|
});
|
|
1098
1222
|
});
|
|
1099
|
-
const PORT =
|
|
1100
|
-
app.listen(PORT, () => {
|
|
1101
|
-
|
|
1223
|
+
const PORT = parseInt(port ? port.toString() : '4000');
|
|
1224
|
+
app.listen(PORT, host, async () => {
|
|
1225
|
+
const pcount = await prisma.package.count();
|
|
1226
|
+
console.log(`[Database] Total packages found: ${pcount}`);
|
|
1227
|
+
console.log(`🚀 Backend server running on http://${host}:${PORT}`);
|
|
1102
1228
|
console.log(`📊 API endpoints available:`);
|
|
1103
1229
|
console.log(` - GET /api/health`);
|
|
1104
1230
|
console.log(` - GET /api/packages/refresh`);
|
|
@@ -1117,7 +1243,7 @@ function startServer(rootPath) {
|
|
|
1117
1243
|
}).on('error', (err) => {
|
|
1118
1244
|
// Handle common errors like EADDRINUSE (port already in use)
|
|
1119
1245
|
if (err.message.includes('EADDRINUSE')) {
|
|
1120
|
-
console.error(`Error: Port ${port} is already in use. Please specify a different port via
|
|
1246
|
+
console.error(`Error: Port ${port} is already in use. Please specify a different port via configuration file.`);
|
|
1121
1247
|
process.exit(1);
|
|
1122
1248
|
}
|
|
1123
1249
|
else {
|
package/get-db-url.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { loadConfig } from './src/config-loader';
|
|
2
|
+
|
|
3
|
+
function generateUrl() {
|
|
4
|
+
const appConfig = loadConfig();
|
|
5
|
+
|
|
6
|
+
const DATABASE_URL = `${appConfig.database.path}`;
|
|
7
|
+
process.env.DATABASE_URL = DATABASE_URL;
|
|
8
|
+
process.env.DATABASE_PORT = `${appConfig.database.port}`;
|
|
9
|
+
|
|
10
|
+
process.stdout.write(DATABASE_URL);
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
generateUrl();
|
package/package.json
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monodog/backend",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Backend API server for monodog monorepo dashboard",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
8
8
|
"monodog-cli": "src/cli.ts"
|
|
9
9
|
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "tsx watch src/index.ts",
|
|
12
|
+
"start": "tsx index.ts",
|
|
13
|
+
"prepublishOnly": "pnpm run build",
|
|
14
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
15
|
+
"prestart": "npm run build",
|
|
16
|
+
"cli": "pnpm run build && node dist/cli.js",
|
|
17
|
+
"test": "jest --coverage",
|
|
18
|
+
"clean": "rm -rf dist node_modules/.cache",
|
|
19
|
+
"test:coverage": "jest --coverage",
|
|
20
|
+
"lint": "eslint .",
|
|
21
|
+
"lint:fix": "eslint . --fix",
|
|
22
|
+
"db:url": "npx ts-node get-db-url.ts",
|
|
23
|
+
"migrate": "DATABASE_URL=$(npm run db:url --silent 2>/dev/null | tr -d '\\n') npx prisma migrate dev",
|
|
24
|
+
"serve": "DATABASE_URL=$(npm run db:url --silent 2>/dev/null | tr -d '\\n') ts-node src/cli.ts --serve "
|
|
25
|
+
},
|
|
10
26
|
"dependencies": {
|
|
11
27
|
"@monodog/ci-status": "1.1.1",
|
|
12
28
|
"@monodog/monorepo-scanner": "1.0.7",
|
|
@@ -19,24 +35,14 @@
|
|
|
19
35
|
"prisma": "^5.22.0"
|
|
20
36
|
},
|
|
21
37
|
"devDependencies": {
|
|
22
|
-
"typescript": "^5.3.8",
|
|
23
38
|
"@types/body-parser": "^1.19.5",
|
|
24
39
|
"@types/cors": "^2.8.17",
|
|
25
40
|
"@types/dotenv": "^8.2.0",
|
|
26
41
|
"@types/express": "^4.17.21",
|
|
27
42
|
"@types/node": "^20.10.0",
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"start": "tsx index.ts",
|
|
33
|
-
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
34
|
-
"prestart": "npm run build",
|
|
35
|
-
"cli": "pnpm run build && node dist/cli.js",
|
|
36
|
-
"test": "jest --coverage",
|
|
37
|
-
"clean": "rm -rf dist node_modules/.cache",
|
|
38
|
-
"test:coverage": "jest --coverage",
|
|
39
|
-
"lint": "eslint .",
|
|
40
|
-
"lint:fix": "eslint . --fix"
|
|
43
|
+
"cross-env": "^10.1.0",
|
|
44
|
+
"ts-node": "^10.0.0",
|
|
45
|
+
"tsx": "^4.6.0",
|
|
46
|
+
"typescript": "^5.3.8"
|
|
41
47
|
}
|
|
42
|
-
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Warnings:
|
|
3
|
+
|
|
4
|
+
- You are about to drop the `Post` table. If the table is not empty, all the data it contains will be lost.
|
|
5
|
+
- You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost.
|
|
6
|
+
|
|
7
|
+
*/
|
|
8
|
+
-- DropTable
|
|
9
|
+
PRAGMA foreign_keys=off;
|
|
10
|
+
DROP TABLE "Post";
|
|
11
|
+
PRAGMA foreign_keys=on;
|
|
12
|
+
|
|
13
|
+
-- DropTable
|
|
14
|
+
PRAGMA foreign_keys=off;
|
|
15
|
+
DROP TABLE "User";
|
|
16
|
+
PRAGMA foreign_keys=on;
|
package/prisma/schema.prisma
CHANGED
package/src/cli.ts
CHANGED
|
@@ -13,14 +13,21 @@ import * as fs from 'fs';
|
|
|
13
13
|
import * as path from 'path';
|
|
14
14
|
import { startServer } from './index'; // Assume index.ts exports this function
|
|
15
15
|
|
|
16
|
+
import { loadConfig } from './config-loader';
|
|
17
|
+
|
|
18
|
+
const appConfig = loadConfig();
|
|
19
|
+
|
|
16
20
|
// --- Argument Parsing ---
|
|
17
21
|
|
|
18
22
|
// 1. Get arguments excluding the node executable and script name
|
|
19
23
|
const args = process.argv.slice(2);
|
|
20
24
|
|
|
21
25
|
// Default settings
|
|
26
|
+
const DEFAULT_PORT = 4000;
|
|
22
27
|
let serve = false;
|
|
23
|
-
let rootPath = process.cwd(); // Default to the current working directory
|
|
28
|
+
let rootPath = path.resolve(appConfig.workspace.root_dir ?? process.cwd()); // Default to the current working directory
|
|
29
|
+
let port = appConfig.server.port ?? DEFAULT_PORT; //Default port
|
|
30
|
+
let host = appConfig.server.host ?? 'localhost'; //Default port
|
|
24
31
|
|
|
25
32
|
// Simple argument parsing loop
|
|
26
33
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -37,7 +44,21 @@ for (let i = 0; i < args.length; i++) {
|
|
|
37
44
|
console.error('Error: --root requires a path argument.');
|
|
38
45
|
process.exit(1);
|
|
39
46
|
}
|
|
40
|
-
} else if (arg === '
|
|
47
|
+
} else if (arg === '--port') {
|
|
48
|
+
// Look at the next argument for the port number
|
|
49
|
+
if (i + 1 < args.length) {
|
|
50
|
+
const portValue = parseInt(args[i + 1], 10);
|
|
51
|
+
if (isNaN(portValue) || portValue <= 0 || portValue > 65535) {
|
|
52
|
+
console.error('Error: --port requires a valid port number (1-65535).');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
port = portValue;
|
|
56
|
+
i++; // Skip the next argument
|
|
57
|
+
} else {
|
|
58
|
+
console.error('Error: --port requires a number argument.');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
} else if (arg === '-h' || arg === '--help') {
|
|
41
62
|
console.log(`
|
|
42
63
|
Monodog CLI - Monorepo Analysis Engine
|
|
43
64
|
|
|
@@ -59,10 +80,13 @@ Example:
|
|
|
59
80
|
// --- Execution Logic ---
|
|
60
81
|
|
|
61
82
|
if (serve) {
|
|
83
|
+
console.log(`\nInitializing Configurations...`);
|
|
84
|
+
createConfigFileIfMissing(rootPath ?? process.cwd());
|
|
85
|
+
|
|
62
86
|
console.log(`Starting Monodog API server...`);
|
|
63
87
|
console.log(`Analyzing monorepo at root: ${rootPath}`);
|
|
64
88
|
// Start the Express server and begin analysis
|
|
65
|
-
startServer(rootPath);
|
|
89
|
+
startServer(rootPath, port, host);
|
|
66
90
|
copyPackageToWorkspace(rootPath);
|
|
67
91
|
} else {
|
|
68
92
|
// Default mode: print usage or run a default report if no command is specified
|
|
@@ -151,3 +175,43 @@ function copyPackageToWorkspace(rootDir: string): void {
|
|
|
151
175
|
}
|
|
152
176
|
}
|
|
153
177
|
|
|
178
|
+
function createConfigFileIfMissing(rootPath: string): void {
|
|
179
|
+
// --- CONFIGURATION ---
|
|
180
|
+
const configFileName = 'monodog-conf.json';
|
|
181
|
+
const configFilePath = path.resolve(rootPath, configFileName);
|
|
182
|
+
|
|
183
|
+
// The default content for the configuration file
|
|
184
|
+
const defaultContent = {
|
|
185
|
+
"workspace": {
|
|
186
|
+
"root_dir": "./packages/backend"
|
|
187
|
+
},
|
|
188
|
+
"database": {
|
|
189
|
+
"path": "./monodog.db"
|
|
190
|
+
},
|
|
191
|
+
"server": {
|
|
192
|
+
"host": "0.0.0.0",
|
|
193
|
+
"port": 4000
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const contentString = JSON.stringify(defaultContent, null, 2);
|
|
198
|
+
// ---------------------
|
|
199
|
+
|
|
200
|
+
console.log(`\n[monodog] Checking for ${configFileName}...`);
|
|
201
|
+
|
|
202
|
+
if (fs.existsSync(configFilePath)) {
|
|
203
|
+
console.log(`[monodog] ${configFileName} already exists. Skipping creation.`);
|
|
204
|
+
} else {
|
|
205
|
+
try {
|
|
206
|
+
// Write the default content to the file
|
|
207
|
+
fs.writeFileSync(configFilePath, contentString, 'utf-8');
|
|
208
|
+
console.log(`[monodog] Successfully generated default ${configFileName} in the workspace root.`);
|
|
209
|
+
console.log('[monodog] Please review and update settings like "host" and "port".');
|
|
210
|
+
} catch (err: unknown) {
|
|
211
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
212
|
+
console.error(`[monodog Error] Failed to generate ${configFileName}:`, message);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
// Define a type/interface for your configuration structure
|
|
5
|
+
interface MonodogConfig {
|
|
6
|
+
workspace: {
|
|
7
|
+
root_dir: string;
|
|
8
|
+
};
|
|
9
|
+
database: {
|
|
10
|
+
type: 'postgres' | 'mysql' | 'sqlite';
|
|
11
|
+
host: string;
|
|
12
|
+
port: number;
|
|
13
|
+
user: string;
|
|
14
|
+
path: string; // Used for SQLite path or general data storage path
|
|
15
|
+
};
|
|
16
|
+
server: {
|
|
17
|
+
host: string;
|
|
18
|
+
port: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Global variable to hold the loaded config
|
|
23
|
+
let config: MonodogConfig | null = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Loads the monodog-conf.json file from the monorepo root.
|
|
27
|
+
* This should be called only once during application startup.
|
|
28
|
+
* @returns The application configuration object.
|
|
29
|
+
*/
|
|
30
|
+
export function loadConfig(): MonodogConfig {
|
|
31
|
+
if (config) {
|
|
32
|
+
return config; // Return cached config if already loaded
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 1. Determine the path to the config file
|
|
36
|
+
// We assume the backend package is running from the monorepo root (cwd is root)
|
|
37
|
+
// or that we can navigate up to the root from the current file's location.
|
|
38
|
+
const rootPath = path.resolve(__dirname, '../../../'); // Adjust based on your folder depth if needed
|
|
39
|
+
const configPath = path.resolve(rootPath, 'monodog-conf.json');
|
|
40
|
+
|
|
41
|
+
if (!fs.existsSync(configPath)) {
|
|
42
|
+
console.error(`ERROR: Configuration file not found at ${configPath}`);
|
|
43
|
+
// A missing config is a critical failure, so we exit
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
// 2. Read and parse the JSON file
|
|
49
|
+
const fileContent = fs.readFileSync(configPath, 'utf-8');
|
|
50
|
+
const parsedConfig = JSON.parse(fileContent) as MonodogConfig;
|
|
51
|
+
|
|
52
|
+
// 3. Optional: Add validation logic here (e.g., check if ports are numbers)
|
|
53
|
+
|
|
54
|
+
// Cache and return
|
|
55
|
+
config = parsedConfig;
|
|
56
|
+
process.stderr.write(`[Config] Loaded configuration from: ${configPath}`);
|
|
57
|
+
return config;
|
|
58
|
+
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error("ERROR: Failed to read or parse monodog-conf.json.");
|
|
61
|
+
console.error(error);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// --- Example Usage ---
|
|
67
|
+
|
|
68
|
+
// In your main application file (e.g., packages/backend/src/index.ts):
|
|
69
|
+
/*
|
|
70
|
+
|
|
71
|
+
import { loadConfig } from './config-loader';
|
|
72
|
+
|
|
73
|
+
// Load configuration on startup
|
|
74
|
+
const appConfig = loadConfig();
|
|
75
|
+
|
|
76
|
+
// Access the variables easily
|
|
77
|
+
const dbHost = appConfig.database.host;
|
|
78
|
+
const serverPort = appConfig.server.port;
|
|
79
|
+
const workspaceRoot = appConfig.workspace.root_dir;
|
|
80
|
+
|
|
81
|
+
console.log(`\nStarting server on port: ${serverPort}`);
|
|
82
|
+
console.log(`Database connecting to host: ${dbHost}`);
|
|
83
|
+
|
|
84
|
+
// Example server start logic
|
|
85
|
+
// app.listen(serverPort, appConfig.server.host, () => {
|
|
86
|
+
// console.log(`Server running at http://${appConfig.server.host}:${serverPort}`);
|
|
87
|
+
// });
|
|
88
|
+
*/
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import express, { type Request, type Response, type NextFunction } from 'express';
|
|
2
2
|
import cors from 'cors';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import
|
|
5
|
-
// import { glob } from 'glob';
|
|
4
|
+
import fs from 'fs';
|
|
6
5
|
import { json } from 'body-parser';
|
|
7
6
|
import {
|
|
8
7
|
scanner,
|
|
@@ -41,11 +40,9 @@ export interface HealthMetric {
|
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
const prisma = new PrismaClient();
|
|
44
|
-
const DEFAULT_PORT = 4000;
|
|
45
43
|
// The main function exported and called by the CLI
|
|
46
|
-
export function startServer(rootPath: string) {
|
|
44
|
+
export function startServer(rootPath: string, port: number|string, host: string): void {
|
|
47
45
|
const app = express();
|
|
48
|
-
const port = process.env.PORT ? parseInt(process.env.PORT) : DEFAULT_PORT;
|
|
49
46
|
|
|
50
47
|
// --- Middleware ---
|
|
51
48
|
|
|
@@ -56,18 +53,6 @@ const app = express();
|
|
|
56
53
|
});
|
|
57
54
|
app.use(cors());
|
|
58
55
|
app.use(json());
|
|
59
|
-
// // 2. CORS (Critical for the frontend app to talk to this local server)
|
|
60
|
-
// // In a production setup, this would be highly restricted, but for local monorepo tools,
|
|
61
|
-
// // we often allow all origins or restrict to a known local hostname/port.
|
|
62
|
-
// app.use((_req: Request, res: Response, next: NextFunction) => {
|
|
63
|
-
// res.setHeader('Access-Control-Allow-Origin', '*'); // Adjust this in production
|
|
64
|
-
// res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
|
|
65
|
-
// res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
66
|
-
// next();
|
|
67
|
-
// });
|
|
68
|
-
|
|
69
|
-
// // 3. JSON body parser
|
|
70
|
-
// app.use(express.json());
|
|
71
56
|
|
|
72
57
|
// Health check
|
|
73
58
|
app.get('/api/health', (_, res) => {
|
|
@@ -1244,6 +1229,162 @@ app.put('/api/config/files/:id', async (_req, res) => {
|
|
|
1244
1229
|
}
|
|
1245
1230
|
});
|
|
1246
1231
|
|
|
1232
|
+
// Update package configuration with path from frontend - preserving all fields, no backups
|
|
1233
|
+
app.put('/api/packages/update', async (req, res) => {
|
|
1234
|
+
try {
|
|
1235
|
+
const { packageName, config, packagePath } = req.body;
|
|
1236
|
+
|
|
1237
|
+
if (!packageName || !config || !packagePath) {
|
|
1238
|
+
return res.status(400).json({
|
|
1239
|
+
success: false,
|
|
1240
|
+
error: 'Package name, configuration, and package path are required',
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
console.log('💾 Updating package configuration for:', packageName);
|
|
1245
|
+
console.log('📁 Package path:', packagePath);
|
|
1246
|
+
|
|
1247
|
+
// Validate JSON syntax with better error handling
|
|
1248
|
+
let newConfig;
|
|
1249
|
+
try {
|
|
1250
|
+
newConfig = JSON.parse(config);
|
|
1251
|
+
} catch (error) {
|
|
1252
|
+
console.error('JSON parsing error:', error);
|
|
1253
|
+
return res.status(400).json({
|
|
1254
|
+
success: false,
|
|
1255
|
+
error: 'Invalid JSON configuration',
|
|
1256
|
+
message: `JSON parsing error: ${error instanceof Error ? error.message : 'Invalid format'}`,
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
1261
|
+
|
|
1262
|
+
// Security check: ensure the path is valid
|
|
1263
|
+
if (!fs.existsSync(packagePath)) {
|
|
1264
|
+
return res.status(404).json({
|
|
1265
|
+
success: false,
|
|
1266
|
+
error: 'Package directory not found',
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// Check if package.json exists
|
|
1271
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
1272
|
+
return res.status(404).json({
|
|
1273
|
+
success: false,
|
|
1274
|
+
error: 'package.json not found in the specified directory',
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// Read the existing package.json to preserve all fields
|
|
1279
|
+
const existingContent = await fs.promises.readFile(packageJsonPath, 'utf8');
|
|
1280
|
+
let existingConfig;
|
|
1281
|
+
try {
|
|
1282
|
+
existingConfig = JSON.parse(existingContent);
|
|
1283
|
+
} catch (error) {
|
|
1284
|
+
return res.status(500).json({
|
|
1285
|
+
success: false,
|
|
1286
|
+
error: 'Existing package.json contains invalid JSON',
|
|
1287
|
+
message: `Error parsing existing package.json: ${error instanceof Error ? error.message : 'Invalid JSON'}`,
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// Merge the new configuration with existing configuration
|
|
1292
|
+
const mergedConfig = {
|
|
1293
|
+
...existingConfig,
|
|
1294
|
+
name: newConfig.name || existingConfig.name,
|
|
1295
|
+
version: newConfig.version || existingConfig.version,
|
|
1296
|
+
description:
|
|
1297
|
+
newConfig.description !== undefined
|
|
1298
|
+
? newConfig.description
|
|
1299
|
+
: existingConfig.description,
|
|
1300
|
+
license:
|
|
1301
|
+
newConfig.license !== undefined
|
|
1302
|
+
? newConfig.license
|
|
1303
|
+
: existingConfig.license,
|
|
1304
|
+
repository: newConfig.repository || existingConfig.repository,
|
|
1305
|
+
scripts: newConfig.scripts || existingConfig.scripts,
|
|
1306
|
+
dependencies: newConfig.dependencies || existingConfig.dependencies,
|
|
1307
|
+
devDependencies:
|
|
1308
|
+
newConfig.devDependencies || existingConfig.devDependencies,
|
|
1309
|
+
peerDependencies:
|
|
1310
|
+
newConfig.peerDependencies || existingConfig.peerDependencies,
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1313
|
+
// Write the merged configuration back
|
|
1314
|
+
const formattedConfig = JSON.stringify(mergedConfig, null, 2);
|
|
1315
|
+
await fs.promises.writeFile(packageJsonPath, formattedConfig, 'utf8');
|
|
1316
|
+
|
|
1317
|
+
// Update the package in the database - use correct field names based on your Prisma schema
|
|
1318
|
+
const updateData: any = {
|
|
1319
|
+
// Use 'lastUpdated' instead of 'updatedAt' based on the error message
|
|
1320
|
+
lastUpdated: new Date(),
|
|
1321
|
+
};
|
|
1322
|
+
|
|
1323
|
+
// Only update fields that exist in your Prisma schema
|
|
1324
|
+
if (newConfig.version) updateData.version = newConfig.version;
|
|
1325
|
+
if (newConfig.description !== undefined)
|
|
1326
|
+
updateData.description = newConfig.description || '';
|
|
1327
|
+
if (newConfig.license !== undefined)
|
|
1328
|
+
updateData.license = newConfig.license || '';
|
|
1329
|
+
if (newConfig.scripts)
|
|
1330
|
+
updateData.scripts = JSON.stringify(newConfig.scripts);
|
|
1331
|
+
if (newConfig.repository)
|
|
1332
|
+
updateData.repository = JSON.stringify(newConfig.repository);
|
|
1333
|
+
if (newConfig.dependencies)
|
|
1334
|
+
updateData.dependencies = JSON.stringify(newConfig.dependencies);
|
|
1335
|
+
if (newConfig.devDependencies)
|
|
1336
|
+
updateData.devDependencies = JSON.stringify(newConfig.devDependencies);
|
|
1337
|
+
if (newConfig.peerDependencies)
|
|
1338
|
+
updateData.peerDependencies = JSON.stringify(newConfig.peerDependencies);
|
|
1339
|
+
|
|
1340
|
+
console.log('📝 Updating database with:', updateData);
|
|
1341
|
+
|
|
1342
|
+
const updatedPackage = await prisma.package.update({
|
|
1343
|
+
where: { name: packageName },
|
|
1344
|
+
data: updateData,
|
|
1345
|
+
});
|
|
1346
|
+
|
|
1347
|
+
// Transform the response
|
|
1348
|
+
const transformedPackage = {
|
|
1349
|
+
...updatedPackage,
|
|
1350
|
+
maintainers: updatedPackage.maintainers
|
|
1351
|
+
? JSON.parse(updatedPackage.maintainers)
|
|
1352
|
+
: [],
|
|
1353
|
+
scripts: updatedPackage.scripts ? JSON.parse(updatedPackage.scripts) : {},
|
|
1354
|
+
repository: updatedPackage.repository
|
|
1355
|
+
? JSON.parse(updatedPackage.repository)
|
|
1356
|
+
: {},
|
|
1357
|
+
dependencies: updatedPackage.dependencies
|
|
1358
|
+
? JSON.parse(updatedPackage.dependencies)
|
|
1359
|
+
: {},
|
|
1360
|
+
devDependencies: updatedPackage.devDependencies
|
|
1361
|
+
? JSON.parse(updatedPackage.devDependencies)
|
|
1362
|
+
: {},
|
|
1363
|
+
peerDependencies: updatedPackage.peerDependencies
|
|
1364
|
+
? JSON.parse(updatedPackage.peerDependencies)
|
|
1365
|
+
: {},
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
// Return success response
|
|
1369
|
+
return res.json({
|
|
1370
|
+
success: true,
|
|
1371
|
+
message: 'Package configuration updated successfully',
|
|
1372
|
+
package: transformedPackage,
|
|
1373
|
+
preservedFields: true,
|
|
1374
|
+
});
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
console.error('Error updating package configuration:', error);
|
|
1377
|
+
|
|
1378
|
+
// Ensure we always return JSON, even for errors
|
|
1379
|
+
return res.status(500).json({
|
|
1380
|
+
success: false,
|
|
1381
|
+
error: 'Failed to update package configuration',
|
|
1382
|
+
message:
|
|
1383
|
+
error instanceof Error ? error.message : 'Unknown error occurred',
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1247
1388
|
// Error handling middleware
|
|
1248
1389
|
app.use(
|
|
1249
1390
|
(
|
|
@@ -1269,10 +1410,13 @@ app.use('*', (_, res) => {
|
|
|
1269
1410
|
});
|
|
1270
1411
|
});
|
|
1271
1412
|
|
|
1272
|
-
const PORT =
|
|
1413
|
+
const PORT = parseInt(port ? port.toString() : '4000');
|
|
1414
|
+
|
|
1415
|
+
app.listen(PORT, host ,async () => {
|
|
1416
|
+
const pcount = await prisma.package.count();
|
|
1417
|
+
console.log(`[Database] Total packages found: ${pcount}`);
|
|
1273
1418
|
|
|
1274
|
-
|
|
1275
|
-
console.log(`🚀 Backend server running on http://localhost:${PORT}`);
|
|
1419
|
+
console.log(`🚀 Backend server running on http://${host}:${PORT}`);
|
|
1276
1420
|
console.log(`📊 API endpoints available:`);
|
|
1277
1421
|
console.log(` - GET /api/health`);
|
|
1278
1422
|
console.log(` - GET /api/packages/refresh`);
|
|
@@ -1291,7 +1435,7 @@ app.listen(PORT, () => {
|
|
|
1291
1435
|
}).on('error', (err) => {
|
|
1292
1436
|
// Handle common errors like EADDRINUSE (port already in use)
|
|
1293
1437
|
if (err.message.includes('EADDRINUSE')) {
|
|
1294
|
-
console.error(`Error: Port ${port} is already in use. Please specify a different port via
|
|
1438
|
+
console.error(`Error: Port ${port} is already in use. Please specify a different port via configuration file.`);
|
|
1295
1439
|
process.exit(1);
|
|
1296
1440
|
} else {
|
|
1297
1441
|
console.error('Server failed to start:', err);
|
package/LICENCE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 Lakin Mohapatra
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
Binary file
|