@berthojoris/mcp-mysql-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1142 -0
- package/bin/mcp-mysql.js +122 -0
- package/dist/auth/authService.d.ts +29 -0
- package/dist/auth/authService.js +114 -0
- package/dist/config/config.d.ts +12 -0
- package/dist/config/config.js +19 -0
- package/dist/config/featureConfig.d.ts +51 -0
- package/dist/config/featureConfig.js +130 -0
- package/dist/db/connection.d.ts +22 -0
- package/dist/db/connection.js +135 -0
- package/dist/index.d.ts +236 -0
- package/dist/index.js +273 -0
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +748 -0
- package/dist/security/securityLayer.d.ts +52 -0
- package/dist/security/securityLayer.js +213 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +283 -0
- package/dist/tools/crudTools.d.ts +59 -0
- package/dist/tools/crudTools.js +443 -0
- package/dist/tools/databaseTools.d.ts +33 -0
- package/dist/tools/databaseTools.js +108 -0
- package/dist/tools/ddlTools.d.ts +69 -0
- package/dist/tools/ddlTools.js +199 -0
- package/dist/tools/queryTools.d.ts +29 -0
- package/dist/tools/queryTools.js +119 -0
- package/dist/tools/storedProcedureTools.d.ts +80 -0
- package/dist/tools/storedProcedureTools.js +411 -0
- package/dist/tools/transactionTools.d.ts +45 -0
- package/dist/tools/transactionTools.js +130 -0
- package/dist/tools/utilityTools.d.ts +30 -0
- package/dist/tools/utilityTools.js +121 -0
- package/dist/validation/schemas.d.ts +423 -0
- package/dist/validation/schemas.js +287 -0
- package/manifest.json +248 -0
- package/package.json +83 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CrudTools = void 0;
|
|
7
|
+
const connection_1 = __importDefault(require("../db/connection"));
|
|
8
|
+
const schemas_1 = require("../validation/schemas");
|
|
9
|
+
class CrudTools {
|
|
10
|
+
constructor(security) {
|
|
11
|
+
this.db = connection_1.default.getInstance();
|
|
12
|
+
this.security = security;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create a new record in the specified table
|
|
16
|
+
*/
|
|
17
|
+
async createRecord(params) {
|
|
18
|
+
// Validate input schema
|
|
19
|
+
if (!(0, schemas_1.validateCreateRecord)(params)) {
|
|
20
|
+
return {
|
|
21
|
+
status: 'error',
|
|
22
|
+
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateCreateRecord.errors)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const { table_name, data } = params;
|
|
27
|
+
// Validate table name
|
|
28
|
+
const tableValidation = this.security.validateIdentifier(table_name);
|
|
29
|
+
if (!tableValidation.valid) {
|
|
30
|
+
return {
|
|
31
|
+
status: 'error',
|
|
32
|
+
error: `Invalid table name: ${tableValidation.error}`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// Validate column names
|
|
36
|
+
const columns = Object.keys(data);
|
|
37
|
+
for (const column of columns) {
|
|
38
|
+
const columnValidation = this.security.validateIdentifier(column);
|
|
39
|
+
if (!columnValidation.valid) {
|
|
40
|
+
return {
|
|
41
|
+
status: 'error',
|
|
42
|
+
error: `Invalid column name '${column}': ${columnValidation.error}`
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Validate and sanitize parameter values
|
|
47
|
+
const values = Object.values(data);
|
|
48
|
+
const paramValidation = this.security.validateParameters(values);
|
|
49
|
+
if (!paramValidation.valid) {
|
|
50
|
+
return {
|
|
51
|
+
status: 'error',
|
|
52
|
+
error: `Invalid parameter values: ${paramValidation.error}`
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Build the query with escaped identifiers
|
|
56
|
+
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
57
|
+
const escapedColumns = columns.map(col => this.security.escapeIdentifier(col));
|
|
58
|
+
const placeholders = columns.map(() => '?').join(', ');
|
|
59
|
+
const query = `INSERT INTO ${escapedTableName} (${escapedColumns.join(', ')}) VALUES (${placeholders})`;
|
|
60
|
+
// Execute the query with sanitized parameters
|
|
61
|
+
const result = await this.db.query(query, paramValidation.sanitizedParams);
|
|
62
|
+
return {
|
|
63
|
+
status: 'success',
|
|
64
|
+
data: {
|
|
65
|
+
insertId: result.insertId,
|
|
66
|
+
affectedRows: result.affectedRows
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return {
|
|
72
|
+
status: 'error',
|
|
73
|
+
error: error.message
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Read records from the specified table with optional filters, pagination, and sorting
|
|
79
|
+
*/
|
|
80
|
+
async readRecords(params) {
|
|
81
|
+
// Validate input schema
|
|
82
|
+
if (!(0, schemas_1.validateReadRecords)(params)) {
|
|
83
|
+
return {
|
|
84
|
+
status: 'error',
|
|
85
|
+
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateReadRecords.errors)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const { table_name, filters, pagination, sorting } = params;
|
|
90
|
+
// Validate table name
|
|
91
|
+
const tableValidation = this.security.validateIdentifier(table_name);
|
|
92
|
+
if (!tableValidation.valid) {
|
|
93
|
+
return {
|
|
94
|
+
status: 'error',
|
|
95
|
+
error: `Invalid table name: ${tableValidation.error}`
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// Validate sorting field if provided
|
|
99
|
+
if (sorting) {
|
|
100
|
+
const sortFieldValidation = this.security.validateIdentifier(sorting.field);
|
|
101
|
+
if (!sortFieldValidation.valid) {
|
|
102
|
+
return {
|
|
103
|
+
status: 'error',
|
|
104
|
+
error: `Invalid sorting field: ${sortFieldValidation.error}`
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Validate filter fields if provided
|
|
109
|
+
if (filters && filters.length > 0) {
|
|
110
|
+
for (const filter of filters) {
|
|
111
|
+
const fieldValidation = this.security.validateIdentifier(filter.field);
|
|
112
|
+
if (!fieldValidation.valid) {
|
|
113
|
+
return {
|
|
114
|
+
status: 'error',
|
|
115
|
+
error: `Invalid filter field '${filter.field}': ${fieldValidation.error}`
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Build the WHERE clause if filters are provided
|
|
121
|
+
let whereClause = '';
|
|
122
|
+
let queryParams = [];
|
|
123
|
+
if (filters && filters.length > 0) {
|
|
124
|
+
const conditions = filters.map(filter => {
|
|
125
|
+
const escapedField = this.security.escapeIdentifier(filter.field);
|
|
126
|
+
switch (filter.operator) {
|
|
127
|
+
case 'eq':
|
|
128
|
+
queryParams.push(filter.value);
|
|
129
|
+
return `${escapedField} = ?`;
|
|
130
|
+
case 'neq':
|
|
131
|
+
queryParams.push(filter.value);
|
|
132
|
+
return `${escapedField} != ?`;
|
|
133
|
+
case 'gt':
|
|
134
|
+
queryParams.push(filter.value);
|
|
135
|
+
return `${escapedField} > ?`;
|
|
136
|
+
case 'gte':
|
|
137
|
+
queryParams.push(filter.value);
|
|
138
|
+
return `${escapedField} >= ?`;
|
|
139
|
+
case 'lt':
|
|
140
|
+
queryParams.push(filter.value);
|
|
141
|
+
return `${escapedField} < ?`;
|
|
142
|
+
case 'lte':
|
|
143
|
+
queryParams.push(filter.value);
|
|
144
|
+
return `${escapedField} <= ?`;
|
|
145
|
+
case 'like':
|
|
146
|
+
queryParams.push(`%${filter.value}%`);
|
|
147
|
+
return `${escapedField} LIKE ?`;
|
|
148
|
+
case 'in':
|
|
149
|
+
if (Array.isArray(filter.value)) {
|
|
150
|
+
const placeholders = filter.value.map(() => '?').join(', ');
|
|
151
|
+
queryParams.push(...filter.value);
|
|
152
|
+
return `${escapedField} IN (${placeholders})`;
|
|
153
|
+
}
|
|
154
|
+
return '1=1'; // Default true condition if value is not an array
|
|
155
|
+
default:
|
|
156
|
+
return '1=1'; // Default true condition
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
whereClause = 'WHERE ' + conditions.join(' AND ');
|
|
160
|
+
}
|
|
161
|
+
// Validate all query parameters
|
|
162
|
+
const paramValidation = this.security.validateParameters(queryParams);
|
|
163
|
+
if (!paramValidation.valid) {
|
|
164
|
+
return {
|
|
165
|
+
status: 'error',
|
|
166
|
+
error: `Invalid query parameters: ${paramValidation.error}`
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
// Build the ORDER BY clause if sorting is provided
|
|
170
|
+
let orderByClause = '';
|
|
171
|
+
if (sorting) {
|
|
172
|
+
const escapedSortField = this.security.escapeIdentifier(sorting.field);
|
|
173
|
+
orderByClause = `ORDER BY ${escapedSortField} ${sorting.direction.toUpperCase()}`;
|
|
174
|
+
}
|
|
175
|
+
// Build the LIMIT clause if pagination is provided
|
|
176
|
+
let limitClause = '';
|
|
177
|
+
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
178
|
+
if (pagination) {
|
|
179
|
+
const offset = (pagination.page - 1) * pagination.limit;
|
|
180
|
+
limitClause = `LIMIT ${offset}, ${pagination.limit}`;
|
|
181
|
+
// Get total count for pagination
|
|
182
|
+
const countQuery = `SELECT COUNT(*) as total FROM ${escapedTableName} ${whereClause}`;
|
|
183
|
+
const countResult = await this.db.query(countQuery, paramValidation.sanitizedParams);
|
|
184
|
+
const total = countResult[0].total;
|
|
185
|
+
// Execute the main query with pagination
|
|
186
|
+
const query = `SELECT * FROM ${escapedTableName} ${whereClause} ${orderByClause} ${limitClause}`;
|
|
187
|
+
const results = await this.db.query(query, paramValidation.sanitizedParams);
|
|
188
|
+
return {
|
|
189
|
+
status: 'success',
|
|
190
|
+
data: results,
|
|
191
|
+
total
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// Execute the query without pagination
|
|
196
|
+
const query = `SELECT * FROM ${escapedTableName} ${whereClause} ${orderByClause}`;
|
|
197
|
+
const results = await this.db.query(query, paramValidation.sanitizedParams);
|
|
198
|
+
return {
|
|
199
|
+
status: 'success',
|
|
200
|
+
data: results,
|
|
201
|
+
total: results.length
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
return {
|
|
207
|
+
status: 'error',
|
|
208
|
+
error: error.message
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Update records in the specified table based on conditions
|
|
214
|
+
*/
|
|
215
|
+
async updateRecord(params) {
|
|
216
|
+
// Validate input schema
|
|
217
|
+
if (!(0, schemas_1.validateUpdateRecord)(params)) {
|
|
218
|
+
return {
|
|
219
|
+
status: 'error',
|
|
220
|
+
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateUpdateRecord.errors)
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const { table_name, data, conditions } = params;
|
|
225
|
+
// Validate table name
|
|
226
|
+
const tableValidation = this.security.validateIdentifier(table_name);
|
|
227
|
+
if (!tableValidation.valid) {
|
|
228
|
+
return {
|
|
229
|
+
status: 'error',
|
|
230
|
+
error: `Invalid table name: ${tableValidation.error}`
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// Validate column names in data
|
|
234
|
+
const columns = Object.keys(data);
|
|
235
|
+
for (const column of columns) {
|
|
236
|
+
const columnValidation = this.security.validateIdentifier(column);
|
|
237
|
+
if (!columnValidation.valid) {
|
|
238
|
+
return {
|
|
239
|
+
status: 'error',
|
|
240
|
+
error: `Invalid column name '${column}': ${columnValidation.error}`
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// Validate condition fields
|
|
245
|
+
for (const condition of conditions) {
|
|
246
|
+
const fieldValidation = this.security.validateIdentifier(condition.field);
|
|
247
|
+
if (!fieldValidation.valid) {
|
|
248
|
+
return {
|
|
249
|
+
status: 'error',
|
|
250
|
+
error: `Invalid condition field '${condition.field}': ${fieldValidation.error}`
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Build SET clause with escaped identifiers
|
|
255
|
+
const setClause = Object.entries(data)
|
|
256
|
+
.map(([column, _]) => `${this.security.escapeIdentifier(column)} = ?`)
|
|
257
|
+
.join(', ');
|
|
258
|
+
const setValues = Object.values(data);
|
|
259
|
+
// Build the WHERE clause
|
|
260
|
+
const whereConditions = [];
|
|
261
|
+
const whereValues = [];
|
|
262
|
+
conditions.forEach(condition => {
|
|
263
|
+
const escapedField = this.security.escapeIdentifier(condition.field);
|
|
264
|
+
switch (condition.operator) {
|
|
265
|
+
case 'eq':
|
|
266
|
+
whereConditions.push(`${escapedField} = ?`);
|
|
267
|
+
whereValues.push(condition.value);
|
|
268
|
+
break;
|
|
269
|
+
case 'neq':
|
|
270
|
+
whereConditions.push(`${escapedField} != ?`);
|
|
271
|
+
whereValues.push(condition.value);
|
|
272
|
+
break;
|
|
273
|
+
case 'gt':
|
|
274
|
+
whereConditions.push(`${escapedField} > ?`);
|
|
275
|
+
whereValues.push(condition.value);
|
|
276
|
+
break;
|
|
277
|
+
case 'gte':
|
|
278
|
+
whereConditions.push(`${escapedField} >= ?`);
|
|
279
|
+
whereValues.push(condition.value);
|
|
280
|
+
break;
|
|
281
|
+
case 'lt':
|
|
282
|
+
whereConditions.push(`${escapedField} < ?`);
|
|
283
|
+
whereValues.push(condition.value);
|
|
284
|
+
break;
|
|
285
|
+
case 'lte':
|
|
286
|
+
whereConditions.push(`${escapedField} <= ?`);
|
|
287
|
+
whereValues.push(condition.value);
|
|
288
|
+
break;
|
|
289
|
+
case 'like':
|
|
290
|
+
whereConditions.push(`${escapedField} LIKE ?`);
|
|
291
|
+
whereValues.push(`%${condition.value}%`);
|
|
292
|
+
break;
|
|
293
|
+
case 'in':
|
|
294
|
+
if (Array.isArray(condition.value)) {
|
|
295
|
+
const placeholders = condition.value.map(() => '?').join(', ');
|
|
296
|
+
whereConditions.push(`${escapedField} IN (${placeholders})`);
|
|
297
|
+
whereValues.push(...condition.value);
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
const whereClause = whereConditions.length > 0
|
|
303
|
+
? 'WHERE ' + whereConditions.join(' AND ')
|
|
304
|
+
: '';
|
|
305
|
+
// Validate all parameters
|
|
306
|
+
const allParams = [...setValues, ...whereValues];
|
|
307
|
+
const paramValidation = this.security.validateParameters(allParams);
|
|
308
|
+
if (!paramValidation.valid) {
|
|
309
|
+
return {
|
|
310
|
+
status: 'error',
|
|
311
|
+
error: `Invalid parameters: ${paramValidation.error}`
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
// Build the query with escaped table name
|
|
315
|
+
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
316
|
+
const query = `UPDATE ${escapedTableName} SET ${setClause} ${whereClause}`;
|
|
317
|
+
// Execute the query with sanitized parameters
|
|
318
|
+
const result = await this.db.query(query, paramValidation.sanitizedParams);
|
|
319
|
+
return {
|
|
320
|
+
status: 'success',
|
|
321
|
+
data: {
|
|
322
|
+
affectedRows: result.affectedRows
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
return {
|
|
328
|
+
status: 'error',
|
|
329
|
+
error: error.message
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Delete records from the specified table based on conditions
|
|
335
|
+
*/
|
|
336
|
+
async deleteRecord(params) {
|
|
337
|
+
// Validate input schema
|
|
338
|
+
if (!(0, schemas_1.validateDeleteRecord)(params)) {
|
|
339
|
+
return {
|
|
340
|
+
status: 'error',
|
|
341
|
+
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateDeleteRecord.errors)
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
const { table_name, conditions } = params;
|
|
346
|
+
// Validate table name
|
|
347
|
+
const tableValidation = this.security.validateIdentifier(table_name);
|
|
348
|
+
if (!tableValidation.valid) {
|
|
349
|
+
return {
|
|
350
|
+
status: 'error',
|
|
351
|
+
error: `Invalid table name: ${tableValidation.error}`
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
// Ensure there are conditions for safety
|
|
355
|
+
if (!conditions || conditions.length === 0) {
|
|
356
|
+
return {
|
|
357
|
+
status: 'error',
|
|
358
|
+
error: 'DELETE operations require at least one condition for safety'
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
// Validate condition fields
|
|
362
|
+
for (const condition of conditions) {
|
|
363
|
+
const fieldValidation = this.security.validateIdentifier(condition.field);
|
|
364
|
+
if (!fieldValidation.valid) {
|
|
365
|
+
return {
|
|
366
|
+
status: 'error',
|
|
367
|
+
error: `Invalid condition field '${condition.field}': ${fieldValidation.error}`
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Build the WHERE clause
|
|
372
|
+
const whereConditions = [];
|
|
373
|
+
const whereValues = [];
|
|
374
|
+
conditions.forEach(condition => {
|
|
375
|
+
const escapedField = this.security.escapeIdentifier(condition.field);
|
|
376
|
+
switch (condition.operator) {
|
|
377
|
+
case 'eq':
|
|
378
|
+
whereConditions.push(`${escapedField} = ?`);
|
|
379
|
+
whereValues.push(condition.value);
|
|
380
|
+
break;
|
|
381
|
+
case 'neq':
|
|
382
|
+
whereConditions.push(`${escapedField} != ?`);
|
|
383
|
+
whereValues.push(condition.value);
|
|
384
|
+
break;
|
|
385
|
+
case 'gt':
|
|
386
|
+
whereConditions.push(`${escapedField} > ?`);
|
|
387
|
+
whereValues.push(condition.value);
|
|
388
|
+
break;
|
|
389
|
+
case 'gte':
|
|
390
|
+
whereConditions.push(`${escapedField} >= ?`);
|
|
391
|
+
whereValues.push(condition.value);
|
|
392
|
+
break;
|
|
393
|
+
case 'lt':
|
|
394
|
+
whereConditions.push(`${escapedField} < ?`);
|
|
395
|
+
whereValues.push(condition.value);
|
|
396
|
+
break;
|
|
397
|
+
case 'lte':
|
|
398
|
+
whereConditions.push(`${escapedField} <= ?`);
|
|
399
|
+
whereValues.push(condition.value);
|
|
400
|
+
break;
|
|
401
|
+
case 'like':
|
|
402
|
+
whereConditions.push(`${escapedField} LIKE ?`);
|
|
403
|
+
whereValues.push(`%${condition.value}%`);
|
|
404
|
+
break;
|
|
405
|
+
case 'in':
|
|
406
|
+
if (Array.isArray(condition.value)) {
|
|
407
|
+
const placeholders = condition.value.map(() => '?').join(', ');
|
|
408
|
+
whereConditions.push(`${escapedField} IN (${placeholders})`);
|
|
409
|
+
whereValues.push(...condition.value);
|
|
410
|
+
}
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
const whereClause = 'WHERE ' + whereConditions.join(' AND ');
|
|
415
|
+
// Validate all parameters
|
|
416
|
+
const paramValidation = this.security.validateParameters(whereValues);
|
|
417
|
+
if (!paramValidation.valid) {
|
|
418
|
+
return {
|
|
419
|
+
status: 'error',
|
|
420
|
+
error: `Invalid parameters: ${paramValidation.error}`
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
// Build the query with escaped table name
|
|
424
|
+
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
425
|
+
const query = `DELETE FROM ${escapedTableName} ${whereClause}`;
|
|
426
|
+
// Execute the query with sanitized parameters
|
|
427
|
+
const result = await this.db.query(query, paramValidation.sanitizedParams);
|
|
428
|
+
return {
|
|
429
|
+
status: 'success',
|
|
430
|
+
data: {
|
|
431
|
+
affectedRows: result.affectedRows
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
return {
|
|
437
|
+
status: 'error',
|
|
438
|
+
error: error.message
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
exports.CrudTools = CrudTools;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { TableInfo, ColumnInfo } from '../validation/schemas';
|
|
2
|
+
export declare class DatabaseTools {
|
|
3
|
+
private db;
|
|
4
|
+
constructor();
|
|
5
|
+
/**
|
|
6
|
+
* List all available databases
|
|
7
|
+
*/
|
|
8
|
+
listDatabases(): Promise<{
|
|
9
|
+
status: string;
|
|
10
|
+
data?: string[];
|
|
11
|
+
error?: string;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* List all tables in the selected database
|
|
15
|
+
*/
|
|
16
|
+
listTables(params: {
|
|
17
|
+
database?: string;
|
|
18
|
+
}): Promise<{
|
|
19
|
+
status: string;
|
|
20
|
+
data?: TableInfo[];
|
|
21
|
+
error?: string;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Read table schema (columns, types, keys, etc.)
|
|
25
|
+
*/
|
|
26
|
+
readTableSchema(params: {
|
|
27
|
+
table_name: string;
|
|
28
|
+
}): Promise<{
|
|
29
|
+
status: string;
|
|
30
|
+
data?: ColumnInfo[];
|
|
31
|
+
error?: string;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DatabaseTools = void 0;
|
|
7
|
+
const connection_1 = __importDefault(require("../db/connection"));
|
|
8
|
+
const schemas_1 = require("../validation/schemas");
|
|
9
|
+
class DatabaseTools {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.db = connection_1.default.getInstance();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* List all available databases
|
|
15
|
+
*/
|
|
16
|
+
async listDatabases() {
|
|
17
|
+
try {
|
|
18
|
+
const results = await this.db.query('SHOW DATABASES');
|
|
19
|
+
const databases = results.map(row => row.Database);
|
|
20
|
+
return {
|
|
21
|
+
status: 'success',
|
|
22
|
+
data: databases
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return {
|
|
27
|
+
status: 'error',
|
|
28
|
+
error: error.message
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* List all tables in the selected database
|
|
34
|
+
*/
|
|
35
|
+
async listTables(params) {
|
|
36
|
+
// Validate input
|
|
37
|
+
if (!(0, schemas_1.validateListTables)(params)) {
|
|
38
|
+
return {
|
|
39
|
+
status: 'error',
|
|
40
|
+
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateListTables.errors)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
let query = 'SHOW TABLES';
|
|
45
|
+
// If database is specified, use it
|
|
46
|
+
if (params.database) {
|
|
47
|
+
query = `SHOW TABLES FROM \`${params.database}\``;
|
|
48
|
+
}
|
|
49
|
+
const results = await this.db.query(query);
|
|
50
|
+
const tables = results.map(row => {
|
|
51
|
+
// Extract the table name from the first column (which might have different names)
|
|
52
|
+
const firstColumnName = Object.keys(row)[0];
|
|
53
|
+
return { table_name: row[firstColumnName] };
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
status: 'success',
|
|
57
|
+
data: tables
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
return {
|
|
62
|
+
status: 'error',
|
|
63
|
+
error: error.message
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Read table schema (columns, types, keys, etc.)
|
|
69
|
+
*/
|
|
70
|
+
async readTableSchema(params) {
|
|
71
|
+
// Validate input
|
|
72
|
+
if (!(0, schemas_1.validateReadTableSchema)(params)) {
|
|
73
|
+
return {
|
|
74
|
+
status: 'error',
|
|
75
|
+
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateReadTableSchema.errors)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const query = `
|
|
80
|
+
SELECT
|
|
81
|
+
COLUMN_NAME as column_name,
|
|
82
|
+
DATA_TYPE as data_type,
|
|
83
|
+
IS_NULLABLE as is_nullable,
|
|
84
|
+
COLUMN_KEY as column_key,
|
|
85
|
+
COLUMN_DEFAULT as column_default,
|
|
86
|
+
EXTRA as extra
|
|
87
|
+
FROM
|
|
88
|
+
INFORMATION_SCHEMA.COLUMNS
|
|
89
|
+
WHERE
|
|
90
|
+
TABLE_NAME = ?
|
|
91
|
+
ORDER BY
|
|
92
|
+
ORDINAL_POSITION
|
|
93
|
+
`;
|
|
94
|
+
const results = await this.db.query(query, [params.table_name]);
|
|
95
|
+
return {
|
|
96
|
+
status: 'success',
|
|
97
|
+
data: results
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
return {
|
|
102
|
+
status: 'error',
|
|
103
|
+
error: error.message
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.DatabaseTools = DatabaseTools;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export declare class DdlTools {
|
|
2
|
+
private db;
|
|
3
|
+
constructor();
|
|
4
|
+
/**
|
|
5
|
+
* Create a new table
|
|
6
|
+
*/
|
|
7
|
+
createTable(params: {
|
|
8
|
+
table_name: string;
|
|
9
|
+
columns: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
type: string;
|
|
12
|
+
nullable?: boolean;
|
|
13
|
+
primary_key?: boolean;
|
|
14
|
+
auto_increment?: boolean;
|
|
15
|
+
default?: string;
|
|
16
|
+
}>;
|
|
17
|
+
indexes?: Array<{
|
|
18
|
+
name: string;
|
|
19
|
+
columns: string[];
|
|
20
|
+
unique?: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
status: string;
|
|
24
|
+
data?: any;
|
|
25
|
+
error?: string;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Alter an existing table
|
|
29
|
+
*/
|
|
30
|
+
alterTable(params: {
|
|
31
|
+
table_name: string;
|
|
32
|
+
operations: Array<{
|
|
33
|
+
type: 'add_column' | 'drop_column' | 'modify_column' | 'rename_column' | 'add_index' | 'drop_index';
|
|
34
|
+
column_name?: string;
|
|
35
|
+
new_column_name?: string;
|
|
36
|
+
column_type?: string;
|
|
37
|
+
nullable?: boolean;
|
|
38
|
+
default?: string;
|
|
39
|
+
index_name?: string;
|
|
40
|
+
index_columns?: string[];
|
|
41
|
+
unique?: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
}): Promise<{
|
|
44
|
+
status: string;
|
|
45
|
+
data?: any;
|
|
46
|
+
error?: string;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Drop a table
|
|
50
|
+
*/
|
|
51
|
+
dropTable(params: {
|
|
52
|
+
table_name: string;
|
|
53
|
+
if_exists?: boolean;
|
|
54
|
+
}): Promise<{
|
|
55
|
+
status: string;
|
|
56
|
+
data?: any;
|
|
57
|
+
error?: string;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* Execute raw DDL SQL
|
|
61
|
+
*/
|
|
62
|
+
executeDdl(params: {
|
|
63
|
+
query: string;
|
|
64
|
+
}): Promise<{
|
|
65
|
+
status: string;
|
|
66
|
+
data?: any;
|
|
67
|
+
error?: string;
|
|
68
|
+
}>;
|
|
69
|
+
}
|