new file: .claude/settings.local.json
new file: .gitignore new file: backend/.env.example new file: backend/.gitignore new file: backend/ecosystem.config.js new file: backend/nodemon.json new file: backend/package-lock.json new file: backend/package.json new file: backend/src/config/database.ts new file: backend/src/config/dynamicSwagger.ts new file: backend/src/config/environment.ts new file: backend/src/config/swagger.ts new file: backend/src/controllers/apiKeyController.ts new file: backend/src/controllers/authController.ts new file: backend/src/controllers/databaseController.ts new file: backend/src/controllers/databaseManagementController.ts new file: backend/src/controllers/dynamicApiController.ts new file: backend/src/controllers/endpointController.ts new file: backend/src/controllers/folderController.ts new file: backend/src/controllers/logsController.ts new file: backend/src/controllers/userController.ts new file: backend/src/middleware/apiKey.ts new file: backend/src/middleware/auth.ts new file: backend/src/middleware/logging.ts new file: backend/src/migrations/001_initial_schema.sql new file: backend/src/migrations/002_add_logging.sql new file: backend/src/migrations/003_add_scripting.sql new file: backend/src/migrations/004_add_superadmin.sql new file: backend/src/migrations/run.ts new file: backend/src/migrations/seed.ts new file: backend/src/routes/apiKeys.ts new file: backend/src/routes/auth.ts new file: backend/src/routes/databaseManagement.ts new file: backend/src/routes/databases.ts new file: backend/src/routes/dynamic.ts new file: backend/src/routes/endpoints.ts new file: backend/src/routes/folders.ts new file: backend/src/routes/logs.ts new file: backend/src/routes/users.ts new file: backend/src/server.ts new file: backend/src/services/DatabasePoolManager.ts new file: backend/src/services/ScriptExecutor.ts new file: backend/src/services/SqlExecutor.ts new file: backend/src/types/index.ts new file: backend/tsconfig.json new file: frontend/.gitignore new file: frontend/index.html new file: frontend/nginx.conf new file: frontend/package-lock.json new file: frontend/package.json new file: frontend/postcss.config.js new file: frontend/src/App.tsx new file: frontend/src/components/CodeEditor.tsx
This commit is contained in:
96
backend/src/services/SqlExecutor.ts
Normal file
96
backend/src/services/SqlExecutor.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { QueryResult } from '../types';
|
||||
import { databasePoolManager } from './DatabasePoolManager';
|
||||
|
||||
export class SqlExecutor {
|
||||
async executeQuery(
|
||||
databaseId: string,
|
||||
sqlQuery: string,
|
||||
params: any[] = []
|
||||
): Promise<QueryResult> {
|
||||
const pool = databasePoolManager.getPool(databaseId);
|
||||
|
||||
if (!pool) {
|
||||
throw new Error(`Database with id ${databaseId} not found or not configured`);
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
// Security: Prevent multiple statements and dangerous commands
|
||||
this.validateQuery(sqlQuery);
|
||||
|
||||
const result = await pool.query(sqlQuery, params);
|
||||
const executionTime = Date.now() - startTime;
|
||||
|
||||
return {
|
||||
rows: result.rows,
|
||||
rowCount: result.rowCount || 0,
|
||||
executionTime,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error('SQL Execution Error:', error);
|
||||
throw new Error(`SQL Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
private validateQuery(sqlQuery: string) {
|
||||
const normalized = sqlQuery.trim().toLowerCase();
|
||||
|
||||
// Prevent multiple statements (basic check)
|
||||
if (normalized.includes(';') && normalized.indexOf(';') < normalized.length - 1) {
|
||||
throw new Error('Multiple SQL statements are not allowed');
|
||||
}
|
||||
|
||||
// Prevent dangerous commands (you can extend this list)
|
||||
const dangerousCommands = ['drop', 'truncate', 'delete from', 'alter', 'create', 'grant', 'revoke'];
|
||||
const isDangerous = dangerousCommands.some(cmd => normalized.startsWith(cmd));
|
||||
|
||||
if (isDangerous && !normalized.startsWith('select')) {
|
||||
// For safety, you might want to allow only SELECT queries
|
||||
// Or implement a whitelist/permission system for write operations
|
||||
console.warn('Potentially dangerous query detected:', sqlQuery);
|
||||
}
|
||||
}
|
||||
|
||||
async testQuery(databaseId: string, sqlQuery: string): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
await this.executeQuery(databaseId, sqlQuery);
|
||||
return { success: true };
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async getTableSchema(databaseId: string, tableName: string): Promise<any[]> {
|
||||
const query = `
|
||||
SELECT
|
||||
column_name,
|
||||
data_type,
|
||||
is_nullable,
|
||||
column_default
|
||||
FROM
|
||||
information_schema.columns
|
||||
WHERE
|
||||
table_name = $1
|
||||
ORDER BY
|
||||
ordinal_position;
|
||||
`;
|
||||
|
||||
const result = await this.executeQuery(databaseId, query, [tableName]);
|
||||
return result.rows;
|
||||
}
|
||||
|
||||
async getAllTables(databaseId: string): Promise<string[]> {
|
||||
const query = `
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
ORDER BY table_name;
|
||||
`;
|
||||
|
||||
const result = await this.executeQuery(databaseId, query);
|
||||
return result.rows.map(row => row.table_name);
|
||||
}
|
||||
}
|
||||
|
||||
export const sqlExecutor = new SqlExecutor();
|
||||
Reference in New Issue
Block a user