agenticaichat 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/README.md +71 -0
- package/bin/cli.js +222 -0
- package/dist/core/ChatbotEngine.d.ts +28 -0
- package/dist/core/ChatbotEngine.js +95 -0
- package/dist/core/DatabaseConnector.d.ts +55 -0
- package/dist/core/DatabaseConnector.js +229 -0
- package/dist/core/NLToSQLConverter.d.ts +26 -0
- package/dist/core/NLToSQLConverter.js +154 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +18 -0
- package/dist/types/index.d.ts +45 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.js +24 -0
- package/dist/widget/ChatbotWidget.d.ts +5 -0
- package/dist/widget/ChatbotWidget.js +218 -0
- package/dist/widget/index.d.ts +1 -0
- package/dist/widget/index.js +5 -0
- package/package.json +90 -0
- package/templates/api-route.template.ts +75 -0
- package/templates/chat-page.template.tsx +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# 🤖 AgenticAIChat
|
|
2
|
+
|
|
3
|
+
AI-powered chatbot for business analytics with natural language database queries.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **5-Minute Setup**: Interactive CLI wizard
|
|
8
|
+
- 💬 **Natural Language**: Ask in English/Hindi
|
|
9
|
+
- 🗄️ **Multi-Database**: PostgreSQL, MySQL, MongoDB
|
|
10
|
+
- 🎨 **Beautiful Widget**: Pre-built React component
|
|
11
|
+
- 🔒 **Secure**: Encrypted API keys
|
|
12
|
+
- 💰 **Free**: User pays OpenAI API only
|
|
13
|
+
- 📦 **TypeScript**: Full type safety
|
|
14
|
+
|
|
15
|
+
## 📦 Installation
|
|
16
|
+
```bash
|
|
17
|
+
npm install agenticaichat
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 🚀 Quick Start
|
|
21
|
+
|
|
22
|
+
### Step 1: Run Setup Wizard
|
|
23
|
+
```bash
|
|
24
|
+
npx agenticai-setup
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Step 2: Add Widget
|
|
28
|
+
```tsx
|
|
29
|
+
// app/dashboard/page.tsx
|
|
30
|
+
import { ChatbotWidget } from 'agenticaichat';
|
|
31
|
+
|
|
32
|
+
export default function Dashboard() {
|
|
33
|
+
return (
|
|
34
|
+
<div>
|
|
35
|
+
<h1>Dashboard</h1>
|
|
36
|
+
<ChatbotWidget />
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Step 3: Full Page Chat (Optional)
|
|
43
|
+
```tsx
|
|
44
|
+
// app/chat/page.tsx
|
|
45
|
+
import { ChatbotWidget } from 'agenticaichat';
|
|
46
|
+
|
|
47
|
+
export default function ChatPage() {
|
|
48
|
+
return <ChatbotWidget fullScreen />;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 📖 Documentation
|
|
53
|
+
|
|
54
|
+
Coming soon!
|
|
55
|
+
|
|
56
|
+
## 🤝 Contributing
|
|
57
|
+
|
|
58
|
+
Contributions welcome!
|
|
59
|
+
|
|
60
|
+
## 📄 License
|
|
61
|
+
|
|
62
|
+
MIT License
|
|
63
|
+
|
|
64
|
+
## 🙋♂️ Support
|
|
65
|
+
|
|
66
|
+
- GitHub: [Issues](https://github.com/yourusername/agenticaichat/issues)
|
|
67
|
+
- Email: your.email@example.com
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
Made with ❤️ for developers
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const inquirer = require('inquirer').default;
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
|
|
10
|
+
async function main() {
|
|
11
|
+
console.log(chalk.bold.cyan(`
|
|
12
|
+
╔════════════════════════════════════════╗
|
|
13
|
+
║ 🤖 AgenticAIChat Setup Wizard ║
|
|
14
|
+
║ Interactive Configuration ║
|
|
15
|
+
╚════════════════════════════════════════╝
|
|
16
|
+
`));
|
|
17
|
+
|
|
18
|
+
console.log(chalk.white('Let\'s set up your AI chatbot!\n'));
|
|
19
|
+
|
|
20
|
+
// Step 1: Database Configuration
|
|
21
|
+
console.log(chalk.bold.blue('📦 Step 1: Database Configuration\n'));
|
|
22
|
+
|
|
23
|
+
const dbAnswers = await inquirer.prompt([
|
|
24
|
+
{
|
|
25
|
+
type: 'list',
|
|
26
|
+
name: 'dbType',
|
|
27
|
+
message: 'Select your database type:',
|
|
28
|
+
choices: ['PostgreSQL', 'MySQL', 'MongoDB'],
|
|
29
|
+
default: 'PostgreSQL'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'input',
|
|
33
|
+
name: 'host',
|
|
34
|
+
message: 'Database host:',
|
|
35
|
+
default: 'localhost'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'input',
|
|
39
|
+
name: 'port',
|
|
40
|
+
message: 'Database port:',
|
|
41
|
+
default: (answers) => {
|
|
42
|
+
if (answers.dbType === 'PostgreSQL') return '5432';
|
|
43
|
+
if (answers.dbType === 'MySQL') return '3306';
|
|
44
|
+
return '27017';
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: 'input',
|
|
49
|
+
name: 'user',
|
|
50
|
+
message: 'Database username:',
|
|
51
|
+
default: 'postgres'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: 'password',
|
|
55
|
+
name: 'password',
|
|
56
|
+
message: 'Database password:',
|
|
57
|
+
mask: '*'
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
type: 'input',
|
|
61
|
+
name: 'database',
|
|
62
|
+
message: 'Database name:',
|
|
63
|
+
default: 'mydb'
|
|
64
|
+
}
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
// Step 2: OpenAI Configuration
|
|
68
|
+
console.log(chalk.bold.blue('\n🔑 Step 2: OpenAI Configuration\n'));
|
|
69
|
+
|
|
70
|
+
const aiAnswers = await inquirer.prompt([
|
|
71
|
+
{
|
|
72
|
+
type: 'password',
|
|
73
|
+
name: 'apiKey',
|
|
74
|
+
message: 'Enter your OpenAI API key:',
|
|
75
|
+
mask: '*',
|
|
76
|
+
validate: (input) => {
|
|
77
|
+
if (!input.startsWith('sk-')) {
|
|
78
|
+
return 'Invalid API key format! Should start with "sk-"';
|
|
79
|
+
}
|
|
80
|
+
if (input.length < 20) {
|
|
81
|
+
return 'API key too short!';
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: 'list',
|
|
88
|
+
name: 'model',
|
|
89
|
+
message: 'Select GPT model:',
|
|
90
|
+
choices: ['gpt-3.5-turbo', 'gpt-4'],
|
|
91
|
+
default: 'gpt-3.5-turbo'
|
|
92
|
+
}
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
// Step 3: Create configuration files
|
|
96
|
+
console.log(chalk.bold.blue('\n📝 Step 3: Generating Configuration Files\n'));
|
|
97
|
+
|
|
98
|
+
const spinner = ora('Creating files...').start();
|
|
99
|
+
|
|
100
|
+
// Build database URL
|
|
101
|
+
let databaseUrl;
|
|
102
|
+
const dbType = dbAnswers.dbType.toLowerCase();
|
|
103
|
+
|
|
104
|
+
if (dbType === 'postgresql') {
|
|
105
|
+
databaseUrl = `postgresql://${dbAnswers.user}:${dbAnswers.password}@${dbAnswers.host}:${dbAnswers.port}/${dbAnswers.database}`;
|
|
106
|
+
} else if (dbType === 'mysql') {
|
|
107
|
+
databaseUrl = `mysql://${dbAnswers.user}:${dbAnswers.password}@${dbAnswers.host}:${dbAnswers.port}/${dbAnswers.database}`;
|
|
108
|
+
} else {
|
|
109
|
+
databaseUrl = `mongodb://${dbAnswers.user}:${dbAnswers.password}@${dbAnswers.host}:${dbAnswers.port}/${dbAnswers.database}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Create .env.chatbot
|
|
113
|
+
const envContent = `# AgenticAIChat Configuration
|
|
114
|
+
# Generated on ${new Date().toISOString()}
|
|
115
|
+
|
|
116
|
+
OPENAI_API_KEY=${aiAnswers.apiKey}
|
|
117
|
+
DATABASE_URL=${databaseUrl}
|
|
118
|
+
DB_TYPE=${dbType}
|
|
119
|
+
GPT_MODEL=${aiAnswers.model}
|
|
120
|
+
SECRET_KEY=${crypto.randomBytes(32).toString('hex')}
|
|
121
|
+
CHATBOT_ENABLED=true
|
|
122
|
+
`;
|
|
123
|
+
|
|
124
|
+
const envPath = path.join(process.cwd(), '.env.chatbot');
|
|
125
|
+
fs.writeFileSync(envPath, envContent);
|
|
126
|
+
spinner.text = 'Created .env.chatbot';
|
|
127
|
+
|
|
128
|
+
// Create chatbot-config.json
|
|
129
|
+
const configContent = {
|
|
130
|
+
dbType: dbType,
|
|
131
|
+
model: aiAnswers.model,
|
|
132
|
+
setupDate: new Date().toISOString(),
|
|
133
|
+
version: '1.0.0'
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const configPath = path.join(process.cwd(), 'chatbot-config.json');
|
|
137
|
+
fs.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
138
|
+
spinner.text = 'Created chatbot-config.json';
|
|
139
|
+
|
|
140
|
+
// Update .gitignore
|
|
141
|
+
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
142
|
+
let gitignoreContent = '';
|
|
143
|
+
|
|
144
|
+
if (fs.existsSync(gitignorePath)) {
|
|
145
|
+
gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!gitignoreContent.includes('.env.chatbot')) {
|
|
149
|
+
gitignoreContent += '\n# AgenticAIChat\n.env.chatbot\nchatbot-config.json\n';
|
|
150
|
+
fs.writeFileSync(gitignorePath, gitignoreContent);
|
|
151
|
+
spinner.text = 'Updated .gitignore';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
spinner.succeed('Configuration files created successfully!');
|
|
155
|
+
|
|
156
|
+
// Create API route file
|
|
157
|
+
try {
|
|
158
|
+
const apiDir = path.join(process.cwd(), 'app', 'api', 'chatbot', 'query');
|
|
159
|
+
if (!fs.existsSync(apiDir)) {
|
|
160
|
+
fs.mkdirSync(apiDir, { recursive: true });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const apiTemplate = fs.readFileSync(
|
|
164
|
+
path.join(__dirname, '..', 'templates', 'api-route.template.ts'),
|
|
165
|
+
'utf8'
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const apiPath = path.join(apiDir, 'route.ts');
|
|
169
|
+
fs.writeFileSync(apiPath, apiTemplate);
|
|
170
|
+
spinner.text = 'Created API route: app/api/chatbot/query/route.ts';
|
|
171
|
+
} catch (error) {
|
|
172
|
+
spinner.warn('Could not create API route automatically');
|
|
173
|
+
console.log(chalk.yellow('\nℹ️ You can manually create the API route later'));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Create chat page
|
|
177
|
+
try {
|
|
178
|
+
const chatDir = path.join(process.cwd(), 'app', 'chat');
|
|
179
|
+
if (!fs.existsSync(chatDir)) {
|
|
180
|
+
fs.mkdirSync(chatDir, { recursive: true });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const chatTemplate = fs.readFileSync(
|
|
184
|
+
path.join(__dirname, '..', 'templates', 'chat-page.template.tsx'),
|
|
185
|
+
'utf8'
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
const chatPath = path.join(chatDir, 'page.tsx');
|
|
189
|
+
fs.writeFileSync(chatPath, chatTemplate);
|
|
190
|
+
spinner.text = 'Created chat page: app/chat/page.tsx';
|
|
191
|
+
} catch (error) {
|
|
192
|
+
spinner.warn('Could not create chat page automatically');
|
|
193
|
+
console.log(chalk.yellow('\nℹ️ You can manually create the chat page later'));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Success message
|
|
197
|
+
console.log(chalk.green.bold('\n✅ Setup Complete!\n'));
|
|
198
|
+
console.log(chalk.white('Files created:'));
|
|
199
|
+
console.log(chalk.gray(' • .env.chatbot (API keys & database config)'));
|
|
200
|
+
console.log(chalk.gray(' • chatbot-config.json (settings)'));
|
|
201
|
+
console.log(chalk.gray(' • .gitignore (updated)\n'));
|
|
202
|
+
|
|
203
|
+
console.log(chalk.white('Next steps:\n'));
|
|
204
|
+
console.log(chalk.yellow('1. Add the widget to your Next.js app:\n'));
|
|
205
|
+
console.log(chalk.cyan(' import { ChatbotWidget } from "agenticaichat";'));
|
|
206
|
+
console.log(chalk.cyan(' \n <ChatbotWidget />\n'));
|
|
207
|
+
|
|
208
|
+
console.log(chalk.yellow('2. Or create a full-page chat route:\n'));
|
|
209
|
+
console.log(chalk.cyan(' // app/chat/page.tsx'));
|
|
210
|
+
console.log(chalk.cyan(' <ChatbotWidget fullScreen />\n'));
|
|
211
|
+
|
|
212
|
+
console.log(chalk.yellow('3. Start your development server:\n'));
|
|
213
|
+
console.log(chalk.cyan(' npm run dev\n'));
|
|
214
|
+
|
|
215
|
+
console.log(chalk.white('Need help? Visit: ') + chalk.blue('https://github.com/yourusername/agenticaichat\n'));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Run CLI
|
|
219
|
+
main().catch((error) => {
|
|
220
|
+
console.error(chalk.red('\n❌ Setup failed:'), error.message);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ChatbotConfig, QueryRequest, QueryResponse, DatabaseSchema } from '../types';
|
|
2
|
+
export declare class ChatbotEngine {
|
|
3
|
+
private dbConnector;
|
|
4
|
+
private nlConverter;
|
|
5
|
+
private schema;
|
|
6
|
+
private initialized;
|
|
7
|
+
constructor(config: ChatbotConfig);
|
|
8
|
+
/**
|
|
9
|
+
* Initialize chatbot (connect to DB and load schema)
|
|
10
|
+
*/
|
|
11
|
+
initialize(): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Process user query
|
|
14
|
+
*/
|
|
15
|
+
processQuery(request: QueryRequest): Promise<QueryResponse>;
|
|
16
|
+
/**
|
|
17
|
+
* Disconnect from database
|
|
18
|
+
*/
|
|
19
|
+
shutdown(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if initialized
|
|
22
|
+
*/
|
|
23
|
+
isInitialized(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Get current schema
|
|
26
|
+
*/
|
|
27
|
+
getSchema(): DatabaseSchema | null;
|
|
28
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ChatbotEngine = void 0;
|
|
4
|
+
const DatabaseConnector_1 = require("./DatabaseConnector");
|
|
5
|
+
const NLToSQLConverter_1 = require("./NLToSQLConverter");
|
|
6
|
+
const logger_1 = require("../utils/logger");
|
|
7
|
+
class ChatbotEngine {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.schema = null;
|
|
10
|
+
this.initialized = false;
|
|
11
|
+
this.dbConnector = new DatabaseConnector_1.DatabaseConnector(config.database);
|
|
12
|
+
this.nlConverter = new NLToSQLConverter_1.NLToSQLConverter(config.openai);
|
|
13
|
+
if (config.debug) {
|
|
14
|
+
logger_1.logger.info('ChatbotEngine initialized in DEBUG mode');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Initialize chatbot (connect to DB and load schema)
|
|
19
|
+
*/
|
|
20
|
+
async initialize() {
|
|
21
|
+
try {
|
|
22
|
+
logger_1.logger.info('Initializing ChatbotEngine...');
|
|
23
|
+
// Connect to database
|
|
24
|
+
await this.dbConnector.connect();
|
|
25
|
+
// Load schema
|
|
26
|
+
this.schema = await this.dbConnector.getSchema();
|
|
27
|
+
logger_1.logger.info(`Schema loaded: ${this.schema.tables.length} tables found`);
|
|
28
|
+
this.initialized = true;
|
|
29
|
+
logger_1.logger.info('ChatbotEngine initialized successfully');
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logger_1.logger.error('Initialization failed', error);
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Process user query
|
|
38
|
+
*/
|
|
39
|
+
async processQuery(request) {
|
|
40
|
+
if (!this.initialized || !this.schema) {
|
|
41
|
+
throw new Error('ChatbotEngine not initialized. Call initialize() first.');
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
logger_1.logger.info(`Processing query: "${request.query}"`);
|
|
45
|
+
// Step 1: Convert NL to SQL
|
|
46
|
+
const sqlQuery = await this.nlConverter.convertToSQL(request.query, this.schema);
|
|
47
|
+
// Step 2: Validate SQL
|
|
48
|
+
if (!this.nlConverter.validateSQL(sqlQuery)) {
|
|
49
|
+
return {
|
|
50
|
+
answer: 'Sorry, I cannot execute this query for security reasons.',
|
|
51
|
+
error: 'SQL validation failed'
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Step 3: Execute SQL
|
|
55
|
+
const queryResult = await this.dbConnector.executeQuery(sqlQuery);
|
|
56
|
+
// Step 4: Generate human response
|
|
57
|
+
const humanAnswer = await this.nlConverter.generateHumanResponse(request.query, queryResult);
|
|
58
|
+
return {
|
|
59
|
+
answer: humanAnswer,
|
|
60
|
+
sql: sqlQuery,
|
|
61
|
+
data: queryResult
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
logger_1.logger.error('Query processing failed', error);
|
|
66
|
+
return {
|
|
67
|
+
answer: 'Sorry, I encountered an error while processing your question. Please try again.',
|
|
68
|
+
error: error.message
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Disconnect from database
|
|
74
|
+
*/
|
|
75
|
+
async shutdown() {
|
|
76
|
+
if (this.dbConnector.isConnected()) {
|
|
77
|
+
await this.dbConnector.disconnect();
|
|
78
|
+
}
|
|
79
|
+
this.initialized = false;
|
|
80
|
+
logger_1.logger.info('ChatbotEngine shut down');
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if initialized
|
|
84
|
+
*/
|
|
85
|
+
isInitialized() {
|
|
86
|
+
return this.initialized;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get current schema
|
|
90
|
+
*/
|
|
91
|
+
getSchema() {
|
|
92
|
+
return this.schema;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.ChatbotEngine = ChatbotEngine;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { DatabaseConfig, DatabaseSchema } from '../types';
|
|
2
|
+
export declare class DatabaseConnector {
|
|
3
|
+
private config;
|
|
4
|
+
private connection;
|
|
5
|
+
private connected;
|
|
6
|
+
constructor(config: DatabaseConfig);
|
|
7
|
+
/**
|
|
8
|
+
* Connect to database
|
|
9
|
+
*/
|
|
10
|
+
connect(): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* PostgreSQL connection
|
|
13
|
+
*/
|
|
14
|
+
private connectPostgreSQL;
|
|
15
|
+
/**
|
|
16
|
+
* MySQL connection
|
|
17
|
+
*/
|
|
18
|
+
private connectMySQL;
|
|
19
|
+
/**
|
|
20
|
+
* MongoDB connection
|
|
21
|
+
*/
|
|
22
|
+
private connectMongoDB;
|
|
23
|
+
/**
|
|
24
|
+
* Get database schema
|
|
25
|
+
*/
|
|
26
|
+
getSchema(): Promise<DatabaseSchema>;
|
|
27
|
+
/**
|
|
28
|
+
* PostgreSQL schema extraction
|
|
29
|
+
*/
|
|
30
|
+
private getPostgreSQLSchema;
|
|
31
|
+
/**
|
|
32
|
+
* MySQL schema extraction
|
|
33
|
+
*/
|
|
34
|
+
private getMySQLSchema;
|
|
35
|
+
/**
|
|
36
|
+
* MongoDB schema extraction (basic)
|
|
37
|
+
*/
|
|
38
|
+
private getMongoDBSchema;
|
|
39
|
+
/**
|
|
40
|
+
* Format schema from raw data
|
|
41
|
+
*/
|
|
42
|
+
private formatSchema;
|
|
43
|
+
/**
|
|
44
|
+
* Execute SQL query
|
|
45
|
+
*/
|
|
46
|
+
executeQuery(sql: string): Promise<any>;
|
|
47
|
+
/**
|
|
48
|
+
* Disconnect from database
|
|
49
|
+
*/
|
|
50
|
+
disconnect(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Check if connected
|
|
53
|
+
*/
|
|
54
|
+
isConnected(): boolean;
|
|
55
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
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.DatabaseConnector = void 0;
|
|
7
|
+
const pg_1 = require("pg");
|
|
8
|
+
const promise_1 = __importDefault(require("mysql2/promise"));
|
|
9
|
+
const mongodb_1 = require("mongodb");
|
|
10
|
+
const logger_1 = require("../utils/logger");
|
|
11
|
+
class DatabaseConnector {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.connection = null;
|
|
14
|
+
this.connected = false;
|
|
15
|
+
this.config = config;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Connect to database
|
|
19
|
+
*/
|
|
20
|
+
async connect() {
|
|
21
|
+
try {
|
|
22
|
+
logger_1.logger.info(`Connecting to ${this.config.type} database...`);
|
|
23
|
+
if (this.config.type === 'postgresql') {
|
|
24
|
+
await this.connectPostgreSQL();
|
|
25
|
+
}
|
|
26
|
+
else if (this.config.type === 'mysql') {
|
|
27
|
+
await this.connectMySQL();
|
|
28
|
+
}
|
|
29
|
+
else if (this.config.type === 'mongodb') {
|
|
30
|
+
await this.connectMongoDB();
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
throw new Error(`Unsupported database type: ${this.config.type}`);
|
|
34
|
+
}
|
|
35
|
+
this.connected = true;
|
|
36
|
+
logger_1.logger.info('Database connected successfully');
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
logger_1.logger.error('Database connection failed', error);
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* PostgreSQL connection
|
|
45
|
+
*/
|
|
46
|
+
async connectPostgreSQL() {
|
|
47
|
+
this.connection = new pg_1.Client({
|
|
48
|
+
connectionString: this.config.url
|
|
49
|
+
});
|
|
50
|
+
await this.connection.connect();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* MySQL connection
|
|
54
|
+
*/
|
|
55
|
+
async connectMySQL() {
|
|
56
|
+
this.connection = await promise_1.default.createConnection(this.config.url);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* MongoDB connection
|
|
60
|
+
*/
|
|
61
|
+
async connectMongoDB() {
|
|
62
|
+
const client = new mongodb_1.MongoClient(this.config.url);
|
|
63
|
+
await client.connect();
|
|
64
|
+
this.connection = client;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get database schema
|
|
68
|
+
*/
|
|
69
|
+
async getSchema() {
|
|
70
|
+
if (!this.connected) {
|
|
71
|
+
throw new Error('Database not connected');
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
if (this.config.type === 'postgresql') {
|
|
75
|
+
return await this.getPostgreSQLSchema();
|
|
76
|
+
}
|
|
77
|
+
else if (this.config.type === 'mysql') {
|
|
78
|
+
return await this.getMySQLSchema();
|
|
79
|
+
}
|
|
80
|
+
else if (this.config.type === 'mongodb') {
|
|
81
|
+
return await this.getMongoDBSchema();
|
|
82
|
+
}
|
|
83
|
+
throw new Error('Unsupported database type');
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
logger_1.logger.error('Failed to get schema', error);
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* PostgreSQL schema extraction
|
|
92
|
+
*/
|
|
93
|
+
async getPostgreSQLSchema() {
|
|
94
|
+
const query = `
|
|
95
|
+
SELECT
|
|
96
|
+
table_name,
|
|
97
|
+
column_name,
|
|
98
|
+
data_type,
|
|
99
|
+
is_nullable
|
|
100
|
+
FROM information_schema.columns
|
|
101
|
+
WHERE table_schema = 'public'
|
|
102
|
+
ORDER BY table_name, ordinal_position;
|
|
103
|
+
`;
|
|
104
|
+
const result = await this.connection.query(query);
|
|
105
|
+
return this.formatSchema(result.rows);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* MySQL schema extraction
|
|
109
|
+
*/
|
|
110
|
+
async getMySQLSchema() {
|
|
111
|
+
const [rows] = await this.connection.query(`
|
|
112
|
+
SELECT
|
|
113
|
+
TABLE_NAME as table_name,
|
|
114
|
+
COLUMN_NAME as column_name,
|
|
115
|
+
DATA_TYPE as data_type,
|
|
116
|
+
IS_NULLABLE as is_nullable
|
|
117
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
118
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
119
|
+
ORDER BY TABLE_NAME, ORDINAL_POSITION;
|
|
120
|
+
`);
|
|
121
|
+
return this.formatSchema(rows);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* MongoDB schema extraction (basic)
|
|
125
|
+
*/
|
|
126
|
+
async getMongoDBSchema() {
|
|
127
|
+
const db = this.connection.db();
|
|
128
|
+
const collections = await db.listCollections().toArray();
|
|
129
|
+
const tables = [];
|
|
130
|
+
for (const collection of collections) {
|
|
131
|
+
const sampleDoc = await db.collection(collection.name).findOne({});
|
|
132
|
+
const columns = [];
|
|
133
|
+
if (sampleDoc) {
|
|
134
|
+
for (const [key, value] of Object.entries(sampleDoc)) {
|
|
135
|
+
columns.push({
|
|
136
|
+
name: key,
|
|
137
|
+
type: typeof value,
|
|
138
|
+
nullable: true
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
tables.push({
|
|
143
|
+
name: collection.name,
|
|
144
|
+
columns
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return { tables };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Format schema from raw data
|
|
151
|
+
*/
|
|
152
|
+
formatSchema(rows) {
|
|
153
|
+
const tablesMap = new Map();
|
|
154
|
+
for (const row of rows) {
|
|
155
|
+
const tableName = row.table_name;
|
|
156
|
+
const column = {
|
|
157
|
+
name: row.column_name,
|
|
158
|
+
type: row.data_type,
|
|
159
|
+
nullable: row.is_nullable === 'YES' || row.is_nullable === 'true'
|
|
160
|
+
};
|
|
161
|
+
if (!tablesMap.has(tableName)) {
|
|
162
|
+
tablesMap.set(tableName, []);
|
|
163
|
+
}
|
|
164
|
+
tablesMap.get(tableName).push(column);
|
|
165
|
+
}
|
|
166
|
+
const tables = [];
|
|
167
|
+
for (const [name, columns] of tablesMap.entries()) {
|
|
168
|
+
tables.push({ name, columns });
|
|
169
|
+
}
|
|
170
|
+
return { tables };
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Execute SQL query
|
|
174
|
+
*/
|
|
175
|
+
async executeQuery(sql) {
|
|
176
|
+
if (!this.connected) {
|
|
177
|
+
throw new Error('Database not connected');
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
logger_1.logger.debug_log('Executing query:', sql);
|
|
181
|
+
if (this.config.type === 'postgresql') {
|
|
182
|
+
const result = await this.connection.query(sql);
|
|
183
|
+
return result.rows;
|
|
184
|
+
}
|
|
185
|
+
else if (this.config.type === 'mysql') {
|
|
186
|
+
const [rows] = await this.connection.query(sql);
|
|
187
|
+
return rows;
|
|
188
|
+
}
|
|
189
|
+
else if (this.config.type === 'mongodb') {
|
|
190
|
+
throw new Error('MongoDB query execution not yet implemented');
|
|
191
|
+
}
|
|
192
|
+
throw new Error('Unsupported database type');
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
logger_1.logger.error('Query execution failed', error);
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Disconnect from database
|
|
201
|
+
*/
|
|
202
|
+
async disconnect() {
|
|
203
|
+
if (!this.connected)
|
|
204
|
+
return;
|
|
205
|
+
try {
|
|
206
|
+
if (this.config.type === 'postgresql') {
|
|
207
|
+
await this.connection.end();
|
|
208
|
+
}
|
|
209
|
+
else if (this.config.type === 'mysql') {
|
|
210
|
+
await this.connection.end();
|
|
211
|
+
}
|
|
212
|
+
else if (this.config.type === 'mongodb') {
|
|
213
|
+
await this.connection.close();
|
|
214
|
+
}
|
|
215
|
+
this.connected = false;
|
|
216
|
+
logger_1.logger.info('Database disconnected');
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
logger_1.logger.error('Disconnect failed', error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Check if connected
|
|
224
|
+
*/
|
|
225
|
+
isConnected() {
|
|
226
|
+
return this.connected;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exports.DatabaseConnector = DatabaseConnector;
|