@vezlo/assistant-server 1.0.0 ā 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -27
- package/package.json +7 -2
- package/scripts/entrypoint.sh +21 -0
- package/scripts/setup.js +339 -0
- package/scripts/validate-db.js +171 -0
package/README.md
CHANGED
|
@@ -32,63 +32,110 @@ cd assistant-server
|
|
|
32
32
|
npm install
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
## š Quick Start
|
|
35
|
+
## š Quick Start (Interactive Setup)
|
|
36
36
|
|
|
37
37
|
### Prerequisites
|
|
38
38
|
- Node.js 20+ and npm 9+
|
|
39
|
-
- Supabase project
|
|
39
|
+
- Supabase project (or PostgreSQL database)
|
|
40
40
|
- OpenAI API key
|
|
41
41
|
|
|
42
|
-
###
|
|
42
|
+
### Easy Setup with Interactive Wizard
|
|
43
|
+
|
|
44
|
+
The fastest way to get started is with our interactive setup wizard:
|
|
43
45
|
|
|
44
46
|
```bash
|
|
45
|
-
# If installed
|
|
46
|
-
|
|
47
|
-
cd my-ai-assistant
|
|
47
|
+
# If installed globally
|
|
48
|
+
vezlo-setup
|
|
48
49
|
|
|
49
|
-
#
|
|
50
|
-
|
|
50
|
+
# If installed locally
|
|
51
|
+
npx vezlo-setup
|
|
51
52
|
|
|
52
53
|
# Or if cloned from GitHub
|
|
54
|
+
npm run setup
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The wizard will guide you through:
|
|
58
|
+
1. **Database Configuration** - Choose Supabase or PostgreSQL
|
|
59
|
+
2. **OpenAI API Setup** - Configure your AI model
|
|
60
|
+
3. **Automatic Table Creation** - Creates all required database tables
|
|
61
|
+
4. **Environment File Generation** - Saves configuration to .env
|
|
62
|
+
|
|
63
|
+
After setup completes, start the server:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
vezlo-server
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Manual Setup (Advanced)
|
|
70
|
+
|
|
71
|
+
If you prefer manual configuration:
|
|
72
|
+
|
|
73
|
+
#### 1. Create Environment File
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Copy example file
|
|
53
77
|
cp env.example .env
|
|
54
78
|
|
|
55
|
-
# Edit
|
|
79
|
+
# Edit with your credentials
|
|
80
|
+
nano .env
|
|
56
81
|
```
|
|
57
82
|
|
|
58
|
-
|
|
83
|
+
#### 2. Configure Database
|
|
84
|
+
|
|
85
|
+
Get your Supabase credentials from:
|
|
86
|
+
- **Dashboard** ā Settings ā API
|
|
87
|
+
- **Database** ā Settings ā Connection string
|
|
59
88
|
|
|
60
|
-
|
|
89
|
+
```env
|
|
90
|
+
# Supabase Configuration
|
|
91
|
+
SUPABASE_URL=https://your-project.supabase.co
|
|
92
|
+
SUPABASE_SERVICE_KEY=your-service-role-key
|
|
93
|
+
SUPABASE_DB_HOST=db.your-project.supabase.co
|
|
94
|
+
SUPABASE_DB_PASSWORD=your-database-password
|
|
95
|
+
|
|
96
|
+
# OpenAI Configuration
|
|
97
|
+
OPENAI_API_KEY=sk-your-api-key
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### 3. Setup Database Tables
|
|
101
|
+
|
|
102
|
+
**Option A: Automated Setup**
|
|
61
103
|
```bash
|
|
62
|
-
#
|
|
63
|
-
|
|
104
|
+
vezlo-setup # Run wizard and choose option 3 to use existing .env
|
|
105
|
+
```
|
|
64
106
|
|
|
65
|
-
|
|
107
|
+
**Option B: Manual SQL**
|
|
108
|
+
```bash
|
|
109
|
+
# Copy schema to Supabase SQL Editor
|
|
66
110
|
cat database-schema.sql
|
|
111
|
+
|
|
112
|
+
# Then execute in Supabase Dashboard ā SQL Editor
|
|
67
113
|
```
|
|
68
114
|
|
|
69
|
-
|
|
115
|
+
#### 4. Validate Setup
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Verify database connection and tables
|
|
119
|
+
vezlo-validate
|
|
120
|
+
|
|
121
|
+
# Or with npm
|
|
122
|
+
npm run validate
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### 5. Start Server
|
|
70
126
|
|
|
71
127
|
```bash
|
|
72
128
|
# If installed globally
|
|
73
129
|
vezlo-server
|
|
74
130
|
|
|
75
|
-
# If installed locally
|
|
131
|
+
# If installed locally
|
|
76
132
|
npx vezlo-server
|
|
77
133
|
|
|
78
|
-
# Or
|
|
134
|
+
# Or from source
|
|
79
135
|
npm run build && npm start
|
|
80
136
|
```
|
|
81
137
|
|
|
82
|
-
###
|
|
83
|
-
```bash
|
|
84
|
-
# Health check
|
|
85
|
-
curl http://localhost:3000/health
|
|
86
|
-
|
|
87
|
-
# API documentation
|
|
88
|
-
open http://localhost:3000/docs
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### 5. Docker Deployment (Optional)
|
|
138
|
+
### Docker Deployment
|
|
92
139
|
|
|
93
140
|
```bash
|
|
94
141
|
# Start with Docker Compose
|
|
@@ -135,6 +182,28 @@ CHUNK_SIZE=1000
|
|
|
135
182
|
CHUNK_OVERLAP=200
|
|
136
183
|
```
|
|
137
184
|
|
|
185
|
+
## š§ CLI Commands
|
|
186
|
+
|
|
187
|
+
The package provides these command-line tools:
|
|
188
|
+
|
|
189
|
+
### vezlo-setup
|
|
190
|
+
Interactive wizard for initial configuration and database setup.
|
|
191
|
+
```bash
|
|
192
|
+
vezlo-setup
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### vezlo-validate
|
|
196
|
+
Validates database connection and verifies all tables exist.
|
|
197
|
+
```bash
|
|
198
|
+
vezlo-validate
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### vezlo-server
|
|
202
|
+
Starts the API server.
|
|
203
|
+
```bash
|
|
204
|
+
vezlo-server
|
|
205
|
+
```
|
|
206
|
+
|
|
138
207
|
## š API Documentation
|
|
139
208
|
|
|
140
209
|
### Base URL
|
package/package.json
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vezlo/assistant-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Production-ready AI Assistant Server with vector search, conversation management, and real-time communication",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"types": "dist/server.d.ts",
|
|
7
7
|
"bin": {
|
|
8
|
-
"vezlo-server": "./bin/vezlo-server.js"
|
|
8
|
+
"vezlo-server": "./bin/vezlo-server.js",
|
|
9
|
+
"vezlo-setup": "./scripts/setup.js",
|
|
10
|
+
"vezlo-validate": "./scripts/validate-db.js"
|
|
9
11
|
},
|
|
10
12
|
"files": [
|
|
11
13
|
"dist/",
|
|
12
14
|
"bin/",
|
|
15
|
+
"scripts/",
|
|
13
16
|
"database-schema.sql",
|
|
14
17
|
"knexfile.ts",
|
|
15
18
|
"env.example",
|
|
@@ -22,6 +25,8 @@
|
|
|
22
25
|
"build": "tsc",
|
|
23
26
|
"start": "node dist/server.js",
|
|
24
27
|
"prepublishOnly": "npm run build",
|
|
28
|
+
"setup": "node scripts/setup.js",
|
|
29
|
+
"validate": "node scripts/validate-db.js",
|
|
25
30
|
"migrate:make": "knex migrate:make",
|
|
26
31
|
"migrate:latest": "knex migrate:latest",
|
|
27
32
|
"migrate:rollback": "knex migrate:rollback",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# Migration runner script for Docker
|
|
4
|
+
# This script runs database migrations before starting the application
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "Starting Vezlo Server with Database Migrations..."
|
|
9
|
+
|
|
10
|
+
# Wait for database to be ready (optional)
|
|
11
|
+
echo "Waiting for database connection..."
|
|
12
|
+
sleep 2
|
|
13
|
+
|
|
14
|
+
# Run migrations
|
|
15
|
+
echo "Running database migrations..."
|
|
16
|
+
npm run migrate:latest
|
|
17
|
+
|
|
18
|
+
# Start the application
|
|
19
|
+
echo "Starting Vezlo Server..."
|
|
20
|
+
exec "$@"
|
|
21
|
+
|
package/scripts/setup.js
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vezlo Assistant Server Setup Wizard
|
|
5
|
+
* Interactive CLI to configure database and environment
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const readline = require('readline');
|
|
11
|
+
const { createClient } = require('@supabase/supabase-js');
|
|
12
|
+
|
|
13
|
+
const rl = readline.createInterface({
|
|
14
|
+
input: process.stdin,
|
|
15
|
+
output: process.stdout
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// ANSI color codes
|
|
19
|
+
const colors = {
|
|
20
|
+
reset: '\x1b[0m',
|
|
21
|
+
bright: '\x1b[1m',
|
|
22
|
+
green: '\x1b[32m',
|
|
23
|
+
yellow: '\x1b[33m',
|
|
24
|
+
blue: '\x1b[34m',
|
|
25
|
+
red: '\x1b[31m',
|
|
26
|
+
cyan: '\x1b[36m'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function log(message, color = 'reset') {
|
|
30
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function question(prompt) {
|
|
34
|
+
return new Promise(resolve => {
|
|
35
|
+
rl.question(`${colors.cyan}${prompt}${colors.reset} `, resolve);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function main() {
|
|
40
|
+
console.clear();
|
|
41
|
+
log('\nš Vezlo Assistant Server Setup Wizard\n', 'bright');
|
|
42
|
+
log('This wizard will help you configure your server in 3 easy steps:\n', 'blue');
|
|
43
|
+
log(' 1. Database Connection (Supabase or PostgreSQL)');
|
|
44
|
+
log(' 2. OpenAI API Configuration');
|
|
45
|
+
log(' 3. Automatic Table Creation\n');
|
|
46
|
+
|
|
47
|
+
// Step 1: Database Type Selection
|
|
48
|
+
log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā', 'cyan');
|
|
49
|
+
log(' STEP 1: Database Configuration', 'bright');
|
|
50
|
+
log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n', 'cyan');
|
|
51
|
+
|
|
52
|
+
log('Choose your database type:');
|
|
53
|
+
log(' [1] Supabase (Recommended)');
|
|
54
|
+
log(' [2] PostgreSQL (Direct Connection)');
|
|
55
|
+
log(' [3] Use existing .env file\n');
|
|
56
|
+
|
|
57
|
+
const dbChoice = await question('Enter your choice (1-3):');
|
|
58
|
+
|
|
59
|
+
let config = {};
|
|
60
|
+
|
|
61
|
+
if (dbChoice === '1') {
|
|
62
|
+
config = await setupSupabase();
|
|
63
|
+
} else if (dbChoice === '2') {
|
|
64
|
+
config = await setupPostgreSQL();
|
|
65
|
+
} else if (dbChoice === '3') {
|
|
66
|
+
config = await loadExistingConfig();
|
|
67
|
+
} else {
|
|
68
|
+
log('\nā Invalid choice. Exiting...', 'red');
|
|
69
|
+
rl.close();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Step 2: OpenAI Configuration
|
|
74
|
+
log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā', 'cyan');
|
|
75
|
+
log(' STEP 2: OpenAI API Configuration', 'bright');
|
|
76
|
+
log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n', 'cyan');
|
|
77
|
+
|
|
78
|
+
const openaiKey = await question('Enter your OpenAI API key (sk-...):');
|
|
79
|
+
config.OPENAI_API_KEY = openaiKey.trim();
|
|
80
|
+
|
|
81
|
+
const aiModel = await question('AI Model (default: gpt-4o):') || 'gpt-4o';
|
|
82
|
+
config.AI_MODEL = aiModel.trim();
|
|
83
|
+
|
|
84
|
+
// Step 3: Save Configuration
|
|
85
|
+
log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā', 'cyan');
|
|
86
|
+
log(' STEP 3: Save Configuration', 'bright');
|
|
87
|
+
log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n', 'cyan');
|
|
88
|
+
|
|
89
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
90
|
+
await saveEnvFile(envPath, config);
|
|
91
|
+
|
|
92
|
+
log('\nā
Configuration saved to .env', 'green');
|
|
93
|
+
|
|
94
|
+
// Step 4: Database Setup
|
|
95
|
+
log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā', 'cyan');
|
|
96
|
+
log(' STEP 4: Database Setup', 'bright');
|
|
97
|
+
log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n', 'cyan');
|
|
98
|
+
|
|
99
|
+
const setupDb = await question('Setup database tables now? (y/n):');
|
|
100
|
+
|
|
101
|
+
if (setupDb.toLowerCase() === 'y') {
|
|
102
|
+
await setupDatabase(config);
|
|
103
|
+
} else {
|
|
104
|
+
log('\nā ļø Skipping database setup.', 'yellow');
|
|
105
|
+
log(' Run "npx vezlo-setup-db" later to create tables.\n', 'yellow');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Final Instructions
|
|
109
|
+
log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā', 'green');
|
|
110
|
+
log(' š Setup Complete!', 'bright');
|
|
111
|
+
log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n', 'green');
|
|
112
|
+
|
|
113
|
+
log('Next steps:');
|
|
114
|
+
log(' 1. Review your .env file');
|
|
115
|
+
log(' 2. Start the server: ' + colors.bright + 'vezlo-server' + colors.reset);
|
|
116
|
+
log(' 3. Visit: ' + colors.bright + 'http://localhost:3000/health' + colors.reset);
|
|
117
|
+
log(' 4. API docs: ' + colors.bright + 'http://localhost:3000/docs' + colors.reset + '\n');
|
|
118
|
+
|
|
119
|
+
rl.close();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function setupSupabase() {
|
|
123
|
+
log('\nš¦ Supabase Configuration\n', 'blue');
|
|
124
|
+
log('You can find these values in your Supabase Dashboard:', 'yellow');
|
|
125
|
+
log(' Settings > API > Project URL & API Keys\n', 'yellow');
|
|
126
|
+
|
|
127
|
+
const supabaseUrl = await question('Supabase Project URL (https://xxx.supabase.co):');
|
|
128
|
+
const supabaseAnonKey = await question('Supabase Anon Key:');
|
|
129
|
+
const supabaseServiceKey = await question('Supabase Service Role Key:');
|
|
130
|
+
|
|
131
|
+
// Validate connection
|
|
132
|
+
log('\nš Testing connection...', 'yellow');
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const client = createClient(supabaseUrl.trim(), supabaseServiceKey.trim());
|
|
136
|
+
const { data, error } = await client.from('_test').select('*').limit(1);
|
|
137
|
+
|
|
138
|
+
// This will fail but confirms we can connect
|
|
139
|
+
if (error && error.code !== 'PGRST204' && error.code !== '42P01') {
|
|
140
|
+
log(`\nā ļø Warning: ${error.message}`, 'yellow');
|
|
141
|
+
log('Continuing with setup...\n', 'yellow');
|
|
142
|
+
} else {
|
|
143
|
+
log('ā
Connection successful!\n', 'green');
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
log(`\nā ļø Warning: Could not verify connection`, 'yellow');
|
|
147
|
+
log('Continuing with setup...\n', 'yellow');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Extract database connection info from Supabase URL
|
|
151
|
+
const projectId = supabaseUrl.match(/https:\/\/(.+?)\.supabase\.co/)?.[1];
|
|
152
|
+
const dbHost = projectId ? `db.${projectId}.supabase.co` : '';
|
|
153
|
+
|
|
154
|
+
log('Database connection details:', 'blue');
|
|
155
|
+
log(` Host: ${dbHost}`);
|
|
156
|
+
log(` Port: 5432`);
|
|
157
|
+
log(` Database: postgres`);
|
|
158
|
+
log(` User: postgres\n`);
|
|
159
|
+
|
|
160
|
+
const dbPassword = await question('Supabase Database Password (from Settings > Database):');
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
SUPABASE_URL: supabaseUrl.trim(),
|
|
164
|
+
SUPABASE_ANON_KEY: supabaseAnonKey.trim(),
|
|
165
|
+
SUPABASE_SERVICE_KEY: supabaseServiceKey.trim(),
|
|
166
|
+
SUPABASE_DB_HOST: dbHost,
|
|
167
|
+
SUPABASE_DB_PORT: '5432',
|
|
168
|
+
SUPABASE_DB_NAME: 'postgres',
|
|
169
|
+
SUPABASE_DB_USER: 'postgres',
|
|
170
|
+
SUPABASE_DB_PASSWORD: dbPassword.trim(),
|
|
171
|
+
PORT: '3000',
|
|
172
|
+
NODE_ENV: 'development',
|
|
173
|
+
CORS_ORIGINS: 'http://localhost:3000,http://localhost:5173'
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function setupPostgreSQL() {
|
|
178
|
+
log('\nšļø PostgreSQL Configuration\n', 'blue');
|
|
179
|
+
|
|
180
|
+
const host = await question('Database Host (localhost):') || 'localhost';
|
|
181
|
+
const port = await question('Database Port (5432):') || '5432';
|
|
182
|
+
const database = await question('Database Name (postgres):') || 'postgres';
|
|
183
|
+
const user = await question('Database User (postgres):') || 'postgres';
|
|
184
|
+
const password = await question('Database Password:');
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
SUPABASE_DB_HOST: host.trim(),
|
|
188
|
+
SUPABASE_DB_PORT: port.trim(),
|
|
189
|
+
SUPABASE_DB_NAME: database.trim(),
|
|
190
|
+
SUPABASE_DB_USER: user.trim(),
|
|
191
|
+
SUPABASE_DB_PASSWORD: password.trim(),
|
|
192
|
+
PORT: '3000',
|
|
193
|
+
NODE_ENV: 'development',
|
|
194
|
+
CORS_ORIGINS: 'http://localhost:3000,http://localhost:5173'
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function loadExistingConfig() {
|
|
199
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
200
|
+
|
|
201
|
+
if (!fs.existsSync(envPath)) {
|
|
202
|
+
log('\nā No .env file found in current directory', 'red');
|
|
203
|
+
throw new Error('.env file not found');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
log('\nā
Loading configuration from .env\n', 'green');
|
|
207
|
+
|
|
208
|
+
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
209
|
+
const config = {};
|
|
210
|
+
|
|
211
|
+
envContent.split('\n').forEach(line => {
|
|
212
|
+
const match = line.match(/^([^=:#]+)=(.*)$/);
|
|
213
|
+
if (match) {
|
|
214
|
+
const key = match[1].trim();
|
|
215
|
+
const value = match[2].trim();
|
|
216
|
+
config[key] = value;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
return config;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function saveEnvFile(envPath, config) {
|
|
224
|
+
const envContent = `# Vezlo Assistant Server Configuration
|
|
225
|
+
# Generated by setup wizard on ${new Date().toISOString()}
|
|
226
|
+
|
|
227
|
+
# Server Configuration
|
|
228
|
+
PORT=${config.PORT || '3000'}
|
|
229
|
+
NODE_ENV=${config.NODE_ENV || 'development'}
|
|
230
|
+
LOG_LEVEL=info
|
|
231
|
+
|
|
232
|
+
# CORS Configuration
|
|
233
|
+
CORS_ORIGINS=${config.CORS_ORIGINS || 'http://localhost:3000,http://localhost:5173'}
|
|
234
|
+
|
|
235
|
+
# Rate Limiting
|
|
236
|
+
RATE_LIMIT_WINDOW=60000
|
|
237
|
+
RATE_LIMIT_MAX=100
|
|
238
|
+
|
|
239
|
+
# Supabase Configuration
|
|
240
|
+
${config.SUPABASE_URL ? `SUPABASE_URL=${config.SUPABASE_URL}` : '# SUPABASE_URL=https://your-project.supabase.co'}
|
|
241
|
+
${config.SUPABASE_ANON_KEY ? `SUPABASE_ANON_KEY=${config.SUPABASE_ANON_KEY}` : '# SUPABASE_ANON_KEY=your-anon-key'}
|
|
242
|
+
${config.SUPABASE_SERVICE_KEY ? `SUPABASE_SERVICE_KEY=${config.SUPABASE_SERVICE_KEY}` : '# SUPABASE_SERVICE_KEY=your-service-role-key'}
|
|
243
|
+
|
|
244
|
+
# Database Configuration
|
|
245
|
+
SUPABASE_DB_HOST=${config.SUPABASE_DB_HOST || 'localhost'}
|
|
246
|
+
SUPABASE_DB_PORT=${config.SUPABASE_DB_PORT || '5432'}
|
|
247
|
+
SUPABASE_DB_NAME=${config.SUPABASE_DB_NAME || 'postgres'}
|
|
248
|
+
SUPABASE_DB_USER=${config.SUPABASE_DB_USER || 'postgres'}
|
|
249
|
+
SUPABASE_DB_PASSWORD=${config.SUPABASE_DB_PASSWORD || ''}
|
|
250
|
+
|
|
251
|
+
# OpenAI Configuration
|
|
252
|
+
OPENAI_API_KEY=${config.OPENAI_API_KEY || 'sk-your-openai-api-key'}
|
|
253
|
+
AI_MODEL=${config.AI_MODEL || 'gpt-4o'}
|
|
254
|
+
AI_TEMPERATURE=0.7
|
|
255
|
+
AI_MAX_TOKENS=1000
|
|
256
|
+
|
|
257
|
+
# Organization Settings
|
|
258
|
+
ORGANIZATION_NAME=Vezlo
|
|
259
|
+
ASSISTANT_NAME=Vezlo Assistant
|
|
260
|
+
|
|
261
|
+
# Knowledge Base
|
|
262
|
+
CHUNK_SIZE=1000
|
|
263
|
+
CHUNK_OVERLAP=200
|
|
264
|
+
`;
|
|
265
|
+
|
|
266
|
+
fs.writeFileSync(envPath, envContent, 'utf8');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function setupDatabase(config) {
|
|
270
|
+
log('\nš Setting up database tables...', 'yellow');
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const { Client } = require('pg');
|
|
274
|
+
|
|
275
|
+
const client = new Client({
|
|
276
|
+
host: config.SUPABASE_DB_HOST,
|
|
277
|
+
port: parseInt(config.SUPABASE_DB_PORT || '5432'),
|
|
278
|
+
database: config.SUPABASE_DB_NAME,
|
|
279
|
+
user: config.SUPABASE_DB_USER,
|
|
280
|
+
password: config.SUPABASE_DB_PASSWORD,
|
|
281
|
+
ssl: { rejectUnauthorized: false }
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
await client.connect();
|
|
285
|
+
log('ā
Connected to database', 'green');
|
|
286
|
+
|
|
287
|
+
// Read schema file
|
|
288
|
+
const schemaPath = path.join(__dirname, '..', 'database-schema.sql');
|
|
289
|
+
|
|
290
|
+
if (!fs.existsSync(schemaPath)) {
|
|
291
|
+
log('ā database-schema.sql not found', 'red');
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const schema = fs.readFileSync(schemaPath, 'utf8');
|
|
296
|
+
|
|
297
|
+
log('š Creating tables...', 'yellow');
|
|
298
|
+
await client.query(schema);
|
|
299
|
+
|
|
300
|
+
log('ā
Database tables created successfully!', 'green');
|
|
301
|
+
|
|
302
|
+
// Verify tables
|
|
303
|
+
const result = await client.query(`
|
|
304
|
+
SELECT table_name
|
|
305
|
+
FROM information_schema.tables
|
|
306
|
+
WHERE table_schema = 'public'
|
|
307
|
+
AND table_name IN ('conversations', 'messages', 'message_feedback', 'knowledge_items')
|
|
308
|
+
ORDER BY table_name
|
|
309
|
+
`);
|
|
310
|
+
|
|
311
|
+
log('\nš Verified tables:', 'blue');
|
|
312
|
+
result.rows.forEach(row => {
|
|
313
|
+
log(` ā ${row.table_name}`, 'green');
|
|
314
|
+
});
|
|
315
|
+
log('');
|
|
316
|
+
|
|
317
|
+
await client.end();
|
|
318
|
+
|
|
319
|
+
} catch (error) {
|
|
320
|
+
log(`\nā Database setup failed: ${error.message}`, 'red');
|
|
321
|
+
log('\nYou can manually run the setup later:', 'yellow');
|
|
322
|
+
log(' 1. Copy database-schema.sql to your Supabase SQL Editor', 'yellow');
|
|
323
|
+
log(' 2. Execute the SQL to create tables\n', 'yellow');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Handle errors and cleanup
|
|
328
|
+
process.on('SIGINT', () => {
|
|
329
|
+
log('\n\nā ļø Setup cancelled by user', 'yellow');
|
|
330
|
+
rl.close();
|
|
331
|
+
process.exit(0);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Run the wizard
|
|
335
|
+
main().catch(error => {
|
|
336
|
+
log(`\nā Setup failed: ${error.message}`, 'red');
|
|
337
|
+
rl.close();
|
|
338
|
+
process.exit(1);
|
|
339
|
+
});
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vezlo Assistant Server - Database Validation
|
|
5
|
+
* Validates database connection and table setup
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const dotenv = require('dotenv');
|
|
9
|
+
const { createClient } = require('@supabase/supabase-js');
|
|
10
|
+
|
|
11
|
+
// Load environment variables
|
|
12
|
+
dotenv.config();
|
|
13
|
+
|
|
14
|
+
const colors = {
|
|
15
|
+
reset: '\x1b[0m',
|
|
16
|
+
green: '\x1b[32m',
|
|
17
|
+
yellow: '\x1b[33m',
|
|
18
|
+
red: '\x1b[31m',
|
|
19
|
+
cyan: '\x1b[36m',
|
|
20
|
+
bright: '\x1b[1m'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function log(message, color = 'reset') {
|
|
24
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function validateDatabase() {
|
|
28
|
+
log('\nš Validating Database Configuration\n', 'cyan');
|
|
29
|
+
|
|
30
|
+
// Check environment variables
|
|
31
|
+
log('Checking environment variables...', 'yellow');
|
|
32
|
+
|
|
33
|
+
const requiredVars = [
|
|
34
|
+
'SUPABASE_URL',
|
|
35
|
+
'SUPABASE_SERVICE_KEY',
|
|
36
|
+
'SUPABASE_DB_HOST',
|
|
37
|
+
'SUPABASE_DB_PASSWORD'
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const missing = requiredVars.filter(key => !process.env[key]);
|
|
41
|
+
|
|
42
|
+
if (missing.length > 0) {
|
|
43
|
+
log(`\nā Missing required environment variables:`, 'red');
|
|
44
|
+
missing.forEach(key => log(` - ${key}`, 'red'));
|
|
45
|
+
log('\nRun the setup wizard: ' + colors.bright + 'npx vezlo-setup' + colors.reset + '\n', 'yellow');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
log('ā
Environment variables configured\n', 'green');
|
|
50
|
+
|
|
51
|
+
// Test Supabase connection
|
|
52
|
+
log('Testing Supabase connection...', 'yellow');
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const supabase = createClient(
|
|
56
|
+
process.env.SUPABASE_URL,
|
|
57
|
+
process.env.SUPABASE_SERVICE_KEY
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Try to query a table
|
|
61
|
+
const { error } = await supabase.from('conversations').select('count').limit(0);
|
|
62
|
+
|
|
63
|
+
if (error && error.code !== 'PGRST116') {
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
log('ā
Supabase connection successful\n', 'green');
|
|
68
|
+
|
|
69
|
+
} catch (error) {
|
|
70
|
+
log(`ā Supabase connection failed: ${error.message}\n`, 'red');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Test database connection and validate tables
|
|
75
|
+
log('Validating database tables...', 'yellow');
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const { Client } = require('pg');
|
|
79
|
+
|
|
80
|
+
const client = new Client({
|
|
81
|
+
host: process.env.SUPABASE_DB_HOST,
|
|
82
|
+
port: parseInt(process.env.SUPABASE_DB_PORT || '5432'),
|
|
83
|
+
database: process.env.SUPABASE_DB_NAME || 'postgres',
|
|
84
|
+
user: process.env.SUPABASE_DB_USER || 'postgres',
|
|
85
|
+
password: process.env.SUPABASE_DB_PASSWORD,
|
|
86
|
+
ssl: { rejectUnauthorized: false }
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
await client.connect();
|
|
90
|
+
|
|
91
|
+
// Check required tables
|
|
92
|
+
const requiredTables = [
|
|
93
|
+
'conversations',
|
|
94
|
+
'messages',
|
|
95
|
+
'message_feedback',
|
|
96
|
+
'knowledge_items'
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
const result = await client.query(`
|
|
100
|
+
SELECT table_name
|
|
101
|
+
FROM information_schema.tables
|
|
102
|
+
WHERE table_schema = 'public'
|
|
103
|
+
AND table_name = ANY($1)
|
|
104
|
+
ORDER BY table_name
|
|
105
|
+
`, [requiredTables]);
|
|
106
|
+
|
|
107
|
+
const existingTables = result.rows.map(row => row.table_name);
|
|
108
|
+
const missingTables = requiredTables.filter(t => !existingTables.includes(t));
|
|
109
|
+
|
|
110
|
+
if (missingTables.length > 0) {
|
|
111
|
+
log(`\nā Missing required tables:`, 'red');
|
|
112
|
+
missingTables.forEach(table => log(` - ${table}`, 'red'));
|
|
113
|
+
log('\nRun the setup wizard: ' + colors.bright + 'npx vezlo-setup' + colors.reset + '\n', 'yellow');
|
|
114
|
+
await client.end();
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
log('ā
All required tables exist\n', 'green');
|
|
119
|
+
|
|
120
|
+
// Check table structure
|
|
121
|
+
log('Checking table structure...', 'yellow');
|
|
122
|
+
|
|
123
|
+
const schemaCheck = await client.query(`
|
|
124
|
+
SELECT
|
|
125
|
+
t.table_name,
|
|
126
|
+
COUNT(c.column_name) as column_count
|
|
127
|
+
FROM information_schema.tables t
|
|
128
|
+
LEFT JOIN information_schema.columns c
|
|
129
|
+
ON c.table_name = t.table_name
|
|
130
|
+
AND c.table_schema = t.table_schema
|
|
131
|
+
WHERE t.table_schema = 'public'
|
|
132
|
+
AND t.table_name = ANY($1)
|
|
133
|
+
GROUP BY t.table_name
|
|
134
|
+
ORDER BY t.table_name
|
|
135
|
+
`, [requiredTables]);
|
|
136
|
+
|
|
137
|
+
log('\nš Table Structure:', 'cyan');
|
|
138
|
+
schemaCheck.rows.forEach(row => {
|
|
139
|
+
log(` ā ${row.table_name} (${row.column_count} columns)`, 'green');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Check for vector extension
|
|
143
|
+
const vectorCheck = await client.query(`
|
|
144
|
+
SELECT EXISTS(
|
|
145
|
+
SELECT 1 FROM pg_extension WHERE extname = 'vector'
|
|
146
|
+
) as has_vector
|
|
147
|
+
`);
|
|
148
|
+
|
|
149
|
+
if (vectorCheck.rows[0].has_vector) {
|
|
150
|
+
log(' ā pgvector extension enabled', 'green');
|
|
151
|
+
} else {
|
|
152
|
+
log(' ā ļø pgvector extension not enabled (semantic search disabled)', 'yellow');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
log('\nā
Database validation complete!\n', 'green');
|
|
156
|
+
log('Your server is ready to start:', 'cyan');
|
|
157
|
+
log(' ' + colors.bright + 'vezlo-server' + colors.reset + '\n');
|
|
158
|
+
|
|
159
|
+
await client.end();
|
|
160
|
+
|
|
161
|
+
} catch (error) {
|
|
162
|
+
log(`\nā Database validation failed: ${error.message}\n`, 'red');
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Run validation
|
|
168
|
+
validateDatabase().catch(error => {
|
|
169
|
+
log(`\nā Validation failed: ${error.message}\n`, 'red');
|
|
170
|
+
process.exit(1);
|
|
171
|
+
});
|