@vezlo/assistant-server 1.4.0 → 2.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 +56 -4
- package/database-schema.sql +193 -33
- package/dist/knexfile.d.ts.map +1 -1
- package/dist/knexfile.js +17 -8
- package/dist/knexfile.js.map +1 -1
- package/dist/src/config/database.d.ts.map +1 -1
- package/dist/src/config/database.js +9 -1
- package/dist/src/config/database.js.map +1 -1
- package/dist/src/config/knex.d.ts.map +1 -1
- package/dist/src/config/knex.js +22 -2
- package/dist/src/config/knex.js.map +1 -1
- package/dist/src/config/swagger.d.ts.map +1 -1
- package/dist/src/config/swagger.js +18 -71
- package/dist/src/config/swagger.js.map +1 -1
- package/dist/src/controllers/ApiKeyController.d.ts +17 -0
- package/dist/src/controllers/ApiKeyController.d.ts.map +1 -0
- package/dist/src/controllers/ApiKeyController.js +84 -0
- package/dist/src/controllers/ApiKeyController.js.map +1 -0
- package/dist/src/controllers/AuthController.d.ts +14 -0
- package/dist/src/controllers/AuthController.d.ts.map +1 -0
- package/dist/src/controllers/AuthController.js +212 -0
- package/dist/src/controllers/AuthController.js.map +1 -0
- package/dist/src/controllers/ChatController.d.ts +8 -5
- package/dist/src/controllers/ChatController.d.ts.map +1 -1
- package/dist/src/controllers/ChatController.js +139 -31
- package/dist/src/controllers/ChatController.js.map +1 -1
- package/dist/src/controllers/KnowledgeController.d.ts +5 -4
- package/dist/src/controllers/KnowledgeController.d.ts.map +1 -1
- package/dist/src/controllers/KnowledgeController.js +54 -16
- package/dist/src/controllers/KnowledgeController.js.map +1 -1
- package/dist/src/middleware/auth.d.ts +51 -0
- package/dist/src/middleware/auth.d.ts.map +1 -0
- package/dist/src/middleware/auth.js +232 -0
- package/dist/src/middleware/auth.js.map +1 -0
- package/dist/src/middleware/errorHandler.d.ts.map +1 -1
- package/dist/src/middleware/errorHandler.js +13 -19
- package/dist/src/middleware/errorHandler.js.map +1 -1
- package/dist/src/migrations/001_initial_schema.d.ts.map +1 -1
- package/dist/src/migrations/001_initial_schema.js +39 -64
- package/dist/src/migrations/001_initial_schema.js.map +1 -1
- package/dist/src/migrations/002_multitenancy_schema.d.ts +4 -0
- package/dist/src/migrations/002_multitenancy_schema.d.ts.map +1 -0
- package/dist/src/migrations/002_multitenancy_schema.js +119 -0
- package/dist/src/migrations/002_multitenancy_schema.js.map +1 -0
- package/dist/src/schemas/AuthSchemas.d.ts +89 -0
- package/dist/src/schemas/AuthSchemas.d.ts.map +1 -0
- package/dist/src/schemas/AuthSchemas.js +63 -0
- package/dist/src/schemas/AuthSchemas.js.map +1 -0
- package/dist/src/schemas/CommonSchemas.d.ts +62 -0
- package/dist/src/schemas/CommonSchemas.d.ts.map +1 -0
- package/dist/src/schemas/CommonSchemas.js +65 -0
- package/dist/src/schemas/CommonSchemas.js.map +1 -0
- package/dist/src/schemas/ConversationSchemas.d.ts +64 -27
- package/dist/src/schemas/ConversationSchemas.d.ts.map +1 -1
- package/dist/src/schemas/ConversationSchemas.js +28 -9
- package/dist/src/schemas/ConversationSchemas.js.map +1 -1
- package/dist/src/schemas/FeedbackSchemas.d.ts +43 -5
- package/dist/src/schemas/FeedbackSchemas.d.ts.map +1 -1
- package/dist/src/schemas/FeedbackSchemas.js +20 -2
- package/dist/src/schemas/FeedbackSchemas.js.map +1 -1
- package/dist/src/schemas/KnowledgeSchemas.d.ts +114 -35
- package/dist/src/schemas/KnowledgeSchemas.d.ts.map +1 -1
- package/dist/src/schemas/KnowledgeSchemas.js +58 -16
- package/dist/src/schemas/KnowledgeSchemas.js.map +1 -1
- package/dist/src/schemas/MessageSchemas.d.ts +57 -8
- package/dist/src/schemas/MessageSchemas.d.ts.map +1 -1
- package/dist/src/schemas/MessageSchemas.js +22 -3
- package/dist/src/schemas/MessageSchemas.js.map +1 -1
- package/dist/src/schemas/index.d.ts +410 -68
- package/dist/src/schemas/index.d.ts.map +1 -1
- package/dist/src/schemas/index.js +8 -2
- package/dist/src/schemas/index.js.map +1 -1
- package/dist/src/server.js +1047 -613
- package/dist/src/server.js.map +1 -1
- package/dist/src/services/AIService.d.ts +1 -1
- package/dist/src/services/AIService.d.ts.map +1 -1
- package/dist/src/services/AIService.js +6 -2
- package/dist/src/services/AIService.js.map +1 -1
- package/dist/src/services/ApiKeyService.d.ts +38 -0
- package/dist/src/services/ApiKeyService.d.ts.map +1 -0
- package/dist/src/services/ApiKeyService.js +123 -0
- package/dist/src/services/ApiKeyService.js.map +1 -0
- package/dist/src/services/KnowledgeBaseService.d.ts +2 -2
- package/dist/src/services/KnowledgeBaseService.d.ts.map +1 -1
- package/dist/src/services/KnowledgeBaseService.js +9 -2
- package/dist/src/services/KnowledgeBaseService.js.map +1 -1
- package/dist/src/services/MigrationService.d.ts +1 -1
- package/dist/src/services/MigrationService.d.ts.map +1 -1
- package/dist/src/services/MigrationService.js +4 -8
- package/dist/src/services/MigrationService.js.map +1 -1
- package/dist/src/services/SetupService.d.ts +102 -0
- package/dist/src/services/SetupService.d.ts.map +1 -0
- package/dist/src/services/SetupService.js +343 -0
- package/dist/src/services/SetupService.js.map +1 -0
- package/dist/src/storage/ConversationRepository.d.ts.map +1 -1
- package/dist/src/storage/ConversationRepository.js +42 -8
- package/dist/src/storage/ConversationRepository.js.map +1 -1
- package/dist/src/storage/MessageRepository.d.ts.map +1 -1
- package/dist/src/storage/MessageRepository.js +23 -27
- package/dist/src/storage/MessageRepository.js.map +1 -1
- package/env.example +5 -0
- package/knexfile.ts +17 -8
- package/package.json +10 -2
- package/scripts/generate-key.js +124 -0
- package/scripts/seed-default.js +72 -0
- package/scripts/setup.js +148 -13
- package/scripts/validate-db.js +22 -6
package/knexfile.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
2
|
import dotenv from 'dotenv';
|
|
3
|
+
const path = require('path');
|
|
3
4
|
|
|
4
5
|
// Load environment variables
|
|
5
6
|
dotenv.config();
|
|
@@ -53,13 +54,14 @@ const config: { [key: string]: Knex.Config } = {
|
|
|
53
54
|
max: 20
|
|
54
55
|
},
|
|
55
56
|
migrations: {
|
|
56
|
-
|
|
57
|
+
// Use absolute path based on __dirname (knexfile.js location in dist/)
|
|
58
|
+
directory: path.join(__dirname, 'src/migrations'),
|
|
57
59
|
tableName: 'knex_migrations',
|
|
58
|
-
extension: '
|
|
60
|
+
extension: 'js'
|
|
59
61
|
},
|
|
60
62
|
seeds: {
|
|
61
|
-
directory: '
|
|
62
|
-
extension: '
|
|
63
|
+
directory: path.join(__dirname, 'src/seeds'),
|
|
64
|
+
extension: 'js'
|
|
63
65
|
}
|
|
64
66
|
},
|
|
65
67
|
|
|
@@ -79,13 +81,20 @@ const config: { [key: string]: Knex.Config } = {
|
|
|
79
81
|
max: 10
|
|
80
82
|
},
|
|
81
83
|
migrations: {
|
|
82
|
-
|
|
84
|
+
// In production (Vercel), knexfile.js is in dist/, so use absolute path
|
|
85
|
+
// In development, use relative path
|
|
86
|
+
directory: process.env.NODE_ENV === 'production'
|
|
87
|
+
? path.join(__dirname, 'src/migrations')
|
|
88
|
+
: path.join(process.cwd(), 'src/migrations'),
|
|
83
89
|
tableName: 'knex_migrations',
|
|
84
|
-
extension: 'ts'
|
|
90
|
+
extension: process.env.NODE_ENV === 'production' ? 'js' : 'ts',
|
|
91
|
+
loadExtensions: process.env.NODE_ENV === 'production' ? ['.js'] : ['.ts']
|
|
85
92
|
},
|
|
86
93
|
seeds: {
|
|
87
|
-
directory: '
|
|
88
|
-
|
|
94
|
+
directory: process.env.NODE_ENV === 'production'
|
|
95
|
+
? path.join(__dirname, 'src/seeds')
|
|
96
|
+
: path.join(process.cwd(), 'src/seeds'),
|
|
97
|
+
extension: process.env.NODE_ENV === 'production' ? 'js' : 'ts'
|
|
89
98
|
}
|
|
90
99
|
}
|
|
91
100
|
};
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vezlo/assistant-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.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
8
|
"vezlo-server": "./bin/vezlo-server.js",
|
|
9
9
|
"vezlo-setup": "./scripts/setup.js",
|
|
10
|
-
"vezlo-validate": "./scripts/validate-db.js"
|
|
10
|
+
"vezlo-validate": "./scripts/validate-db.js",
|
|
11
|
+
"vezlo-seed-default": "./scripts/seed-default.js",
|
|
12
|
+
"vezlo-generate-key": "./scripts/generate-key.js"
|
|
11
13
|
},
|
|
12
14
|
"files": [
|
|
13
15
|
"dist/",
|
|
@@ -27,6 +29,8 @@
|
|
|
27
29
|
"start": "node dist/src/server.js",
|
|
28
30
|
"prepublishOnly": "npm run build",
|
|
29
31
|
"setup": "node scripts/setup.js",
|
|
32
|
+
"seed-default": "node scripts/seed-default.js",
|
|
33
|
+
"generate-key": "node scripts/generate-key.js",
|
|
30
34
|
"validate": "node scripts/validate-db.js",
|
|
31
35
|
"migrate:make": "knex migrate:make",
|
|
32
36
|
"migrate:latest": "knex migrate:latest",
|
|
@@ -39,12 +43,16 @@
|
|
|
39
43
|
},
|
|
40
44
|
"dependencies": {
|
|
41
45
|
"@supabase/supabase-js": "^2.38.0",
|
|
46
|
+
"@types/bcryptjs": "^2.4.6",
|
|
47
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
48
|
+
"bcryptjs": "^3.0.2",
|
|
42
49
|
"compression": "^1.7.4",
|
|
43
50
|
"cors": "^2.8.5",
|
|
44
51
|
"dotenv": "^16.3.1",
|
|
45
52
|
"express": "^4.18.2",
|
|
46
53
|
"express-rate-limit": "^7.1.5",
|
|
47
54
|
"helmet": "^7.1.0",
|
|
55
|
+
"jsonwebtoken": "^9.0.2",
|
|
48
56
|
"knex": "^3.1.0",
|
|
49
57
|
"openai": "^4.20.1",
|
|
50
58
|
"pg": "^8.11.3",
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate API Key Script for Vezlo Assistant Server
|
|
5
|
+
* Creates an API key for the default admin's company
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { config } = require('dotenv');
|
|
9
|
+
const { initializeSupabase } = require('../dist/src/config/database.js');
|
|
10
|
+
const { SetupService } = require('../dist/src/services/SetupService.js');
|
|
11
|
+
const { ApiKeyService } = require('../dist/src/services/ApiKeyService.js');
|
|
12
|
+
const logger = require('../dist/src/config/logger.js');
|
|
13
|
+
|
|
14
|
+
// Load environment variables
|
|
15
|
+
config();
|
|
16
|
+
|
|
17
|
+
async function generateApiKey(options = { quiet: false }) {
|
|
18
|
+
try {
|
|
19
|
+
if (!options.quiet) console.log('🔑 Starting API Key Generation...\n');
|
|
20
|
+
|
|
21
|
+
// Initialize Supabase
|
|
22
|
+
const supabase = initializeSupabase();
|
|
23
|
+
|
|
24
|
+
// Wait for schema cache to refresh
|
|
25
|
+
if (!options.quiet) console.log('⏳ Waiting for schema cache to refresh...');
|
|
26
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
27
|
+
|
|
28
|
+
// Get default credentials from environment
|
|
29
|
+
const credentials = SetupService.getDefaultCredentials();
|
|
30
|
+
if (!options.quiet) console.log(`🔍 Using admin email: ${credentials.adminEmail}`);
|
|
31
|
+
|
|
32
|
+
// Check if default company and admin exist
|
|
33
|
+
if (!options.quiet) console.log('🔍 Checking for default company and admin...');
|
|
34
|
+
|
|
35
|
+
// Find the admin user
|
|
36
|
+
const { data: user, error: userError } = await supabase
|
|
37
|
+
.from('vezlo_users')
|
|
38
|
+
.select('id, uuid, name')
|
|
39
|
+
.eq('email', credentials.adminEmail)
|
|
40
|
+
.single();
|
|
41
|
+
|
|
42
|
+
if (userError || !user) {
|
|
43
|
+
throw new Error(`Admin user not found (${credentials.adminEmail}). Run seed-default first.`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!options.quiet) console.log(`✅ Found admin user: ${user.name} (${user.uuid})`);
|
|
47
|
+
|
|
48
|
+
// Find the admin's company profile
|
|
49
|
+
const { data: profile, error: profileError } = await supabase
|
|
50
|
+
.from('vezlo_user_company_profiles')
|
|
51
|
+
.select(`
|
|
52
|
+
id,
|
|
53
|
+
role,
|
|
54
|
+
company_id,
|
|
55
|
+
companies:company_id(
|
|
56
|
+
id,
|
|
57
|
+
uuid,
|
|
58
|
+
name,
|
|
59
|
+
domain
|
|
60
|
+
)
|
|
61
|
+
`)
|
|
62
|
+
.eq('user_id', user.id)
|
|
63
|
+
.eq('role', 'admin')
|
|
64
|
+
.single();
|
|
65
|
+
|
|
66
|
+
if (profileError || !profile) {
|
|
67
|
+
throw new Error(`Admin profile not found. Run seed-default first.`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!options.quiet) console.log(`✅ Found company: ${profile.companies.name} (${profile.companies.uuid})`);
|
|
71
|
+
|
|
72
|
+
// Generate the API key - use joined company ID with fallback to direct company_id
|
|
73
|
+
const apiKeyService = new ApiKeyService(supabase);
|
|
74
|
+
const companyId = profile.companies?.id || profile.company_id;
|
|
75
|
+
if (!companyId) {
|
|
76
|
+
throw new Error('Could not determine company ID from profile');
|
|
77
|
+
}
|
|
78
|
+
const { uuid, apiKey } = await apiKeyService.generateApiKey(companyId);
|
|
79
|
+
|
|
80
|
+
if (!options.quiet) {
|
|
81
|
+
// Display success summary
|
|
82
|
+
console.log('\n🎉 API Key Generated Successfully!');
|
|
83
|
+
console.log('=====================================');
|
|
84
|
+
console.log('📋 API Key Details:');
|
|
85
|
+
console.log(` Company: ${profile.companies.name}`);
|
|
86
|
+
console.log(` API Key: ${apiKey}`);
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log('⚠️ IMPORTANT: Save this key securely. It will not be shown again.');
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log('🔧 Usage Example:');
|
|
91
|
+
console.log(' curl -X POST https://your-server/api/knowledge/items \\');
|
|
92
|
+
console.log(' -H "X-API-Key: ' + apiKey + '" \\');
|
|
93
|
+
console.log(' -H "Content-Type: application/json" \\');
|
|
94
|
+
console.log(' -d \'{"title": "Example", "type": "document", "content": "Example content"}\'');
|
|
95
|
+
console.log('');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Return the generated API key details
|
|
99
|
+
return {
|
|
100
|
+
success: true,
|
|
101
|
+
uuid,
|
|
102
|
+
apiKey,
|
|
103
|
+
company: profile.companies.name,
|
|
104
|
+
user: {
|
|
105
|
+
name: user.name,
|
|
106
|
+
uuid: user.uuid
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error('❌ API key generation failed:', error.message);
|
|
112
|
+
if (!options.quiet) {
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
return { success: false, error: error.message };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Run if called directly
|
|
120
|
+
if (require.main === module) {
|
|
121
|
+
generateApiKey();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = { generateApiKey };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default Data Setup Script for Vezlo Assistant Server
|
|
5
|
+
* Creates default company and admin user
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { config } = require('dotenv');
|
|
9
|
+
const { initializeSupabase } = require('../dist/src/config/database.js');
|
|
10
|
+
const { SetupService } = require('../dist/src/services/SetupService.js');
|
|
11
|
+
const logger = require('../dist/src/config/logger.js');
|
|
12
|
+
|
|
13
|
+
// Load environment variables
|
|
14
|
+
config();
|
|
15
|
+
|
|
16
|
+
async function runDefaultSetup() {
|
|
17
|
+
try {
|
|
18
|
+
console.log('🚀 Starting Default Data Setup...\n');
|
|
19
|
+
|
|
20
|
+
// Initialize Supabase
|
|
21
|
+
const supabase = initializeSupabase();
|
|
22
|
+
|
|
23
|
+
// Wait for schema cache to refresh (Supabase needs time after migrations)
|
|
24
|
+
console.log('⏳ Waiting for schema cache to refresh...');
|
|
25
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
26
|
+
|
|
27
|
+
const setupService = new SetupService(supabase);
|
|
28
|
+
|
|
29
|
+
// Check if setup is already completed
|
|
30
|
+
const status = await setupService.getSetupStatus();
|
|
31
|
+
if (status.completed) {
|
|
32
|
+
console.log('✅ Default data already exists!');
|
|
33
|
+
console.log(`🏢 Company: ${status.company?.name}`);
|
|
34
|
+
console.log(`👤 Admin User: ${status.adminUser?.email}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Get setup parameters from service (already required at top)
|
|
39
|
+
const credentials = SetupService.getDefaultCredentials();
|
|
40
|
+
|
|
41
|
+
console.log('📋 Setup Configuration:');
|
|
42
|
+
console.log(` Company Name: ${credentials.companyName}`);
|
|
43
|
+
console.log(` Admin Email: ${credentials.adminEmail}`);
|
|
44
|
+
console.log(` Admin Password: ${credentials.adminPassword}`);
|
|
45
|
+
console.log('');
|
|
46
|
+
|
|
47
|
+
// Create default data
|
|
48
|
+
const result = await setupService.createDefaultData(credentials);
|
|
49
|
+
|
|
50
|
+
// Display success summary
|
|
51
|
+
console.log('\n🎉 Default Data Setup Completed!');
|
|
52
|
+
console.log('=====================================');
|
|
53
|
+
console.log('📋 Default Admin Credentials:');
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log(` Company Name: ${result.company.name}`);
|
|
56
|
+
console.log(` Admin Email: ${result.user.email}`);
|
|
57
|
+
console.log(` Admin Password: ${credentials.adminPassword}`);
|
|
58
|
+
console.log(` Admin Name: ${result.user.name}`);
|
|
59
|
+
console.log('');
|
|
60
|
+
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error('❌ Setup failed:', error);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Run setup if called directly
|
|
68
|
+
if (require.main === module) {
|
|
69
|
+
runDefaultSetup();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { runDefaultSetup };
|
package/scripts/setup.js
CHANGED
|
@@ -97,6 +97,31 @@ async function main() {
|
|
|
97
97
|
|
|
98
98
|
const migrationStatus = await setupMigrations(config, validationStatus) || { migrations: validationStatus.database === 'success' ? 'success' : 'skipped' };
|
|
99
99
|
|
|
100
|
+
// Step 6: Default Data Setup (only if migrations succeeded)
|
|
101
|
+
if (migrationStatus.migrations === 'success') {
|
|
102
|
+
log('\n═══════════════════════════════════════════════════════════', 'cyan');
|
|
103
|
+
log(' STEP 6: Default Data Setup', 'bright');
|
|
104
|
+
log('═══════════════════════════════════════════════════════════\n', 'cyan');
|
|
105
|
+
|
|
106
|
+
const defaultDataStatus = await setupDefaultData(config);
|
|
107
|
+
migrationStatus.defaultData = defaultDataStatus;
|
|
108
|
+
|
|
109
|
+
// Step 7: API Key Generation (only if default data setup succeeded)
|
|
110
|
+
if (defaultDataStatus === 'success') {
|
|
111
|
+
log('\n═══════════════════════════════════════════════════════════', 'cyan');
|
|
112
|
+
log(' STEP 7: API Key Generation', 'bright');
|
|
113
|
+
log('═══════════════════════════════════════════════════════════\n', 'cyan');
|
|
114
|
+
|
|
115
|
+
const apiKeyStatus = await setupApiKey(config);
|
|
116
|
+
migrationStatus.apiKey = apiKeyStatus;
|
|
117
|
+
} else {
|
|
118
|
+
migrationStatus.apiKey = 'skipped';
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
migrationStatus.defaultData = 'skipped';
|
|
122
|
+
migrationStatus.apiKey = 'skipped';
|
|
123
|
+
}
|
|
124
|
+
|
|
100
125
|
// Final Instructions / Summary
|
|
101
126
|
log('\n═══════════════════════════════════════════════════════════', 'green');
|
|
102
127
|
log(' 🎉 Setup Complete!', 'bright');
|
|
@@ -108,12 +133,29 @@ async function main() {
|
|
|
108
133
|
log(` Supabase API: ${supaStatus === 'OK' ? colors.green + 'OK' : supaStatus === 'FAILED' ? colors.red + 'FAILED' : colors.yellow + 'UNKNOWN'}${colors.reset}`);
|
|
109
134
|
log(` Database: ${validationStatus.database === 'success' ? colors.green + 'OK' : colors.red + (validationStatus.database === 'skipped' ? 'SKIPPED' : 'FAILED')}${colors.reset}`);
|
|
110
135
|
log(` Migrations: ${migrationStatus.migrations === 'success' ? colors.green + 'OK' : migrationStatus.migrations === 'skipped' ? colors.yellow + 'SKIPPED' : colors.red + 'FAILED'}${colors.reset}`);
|
|
136
|
+
log(` Default Data: ${migrationStatus.defaultData === 'success' ? colors.green + 'OK' : migrationStatus.defaultData === 'skipped' ? colors.yellow + 'SKIPPED' : colors.red + 'FAILED'}${colors.reset}`);
|
|
137
|
+
log(` API Key: ${migrationStatus.apiKey === 'success' ? colors.green + 'OK' : migrationStatus.apiKey === 'skipped' ? colors.yellow + 'SKIPPED' : colors.red + 'FAILED'}${colors.reset}`);
|
|
111
138
|
|
|
112
139
|
log('\nNext steps:');
|
|
113
140
|
log(' 1. Review your .env file');
|
|
114
141
|
if (migrationStatus.migrations !== 'success') {
|
|
142
|
+
log('\n⚠️ IMPORTANT: Migrations were not run. You must run migrations first before seeding default data.', 'yellow');
|
|
115
143
|
log(' 2. Run database migrations: ' + colors.bright + 'npm run migrate:latest' + colors.reset);
|
|
116
|
-
log('
|
|
144
|
+
log(' 3. Then run seed: ' + colors.bright + 'npm run seed-default' + colors.reset + ' (only after migrations complete)', 'yellow');
|
|
145
|
+
log(' 4. Generate API key: ' + colors.bright + 'npm run generate-key' + colors.reset + ' (if not already done)');
|
|
146
|
+
log(' 5. Start the server: ' + colors.bright + 'vezlo-server' + colors.reset);
|
|
147
|
+
log(' 6. Visit: ' + colors.bright + 'http://localhost:3000/health' + colors.reset);
|
|
148
|
+
log(' 7. API docs: ' + colors.bright + 'http://localhost:3000/docs' + colors.reset);
|
|
149
|
+
log(' 8. Test API: ' + colors.bright + 'curl http://localhost:3000/health' + colors.reset + '\n');
|
|
150
|
+
} else if (migrationStatus.defaultData !== 'success') {
|
|
151
|
+
log(' 2. Setup default data: ' + colors.bright + 'npm run seed-default' + colors.reset);
|
|
152
|
+
log(' 3. Generate API key: ' + colors.bright + 'npm run generate-key' + colors.reset + ' (after default data is created)');
|
|
153
|
+
log(' 4. Start the server: ' + colors.bright + 'vezlo-server' + colors.reset);
|
|
154
|
+
log(' 5. Visit: ' + colors.bright + 'http://localhost:3000/health' + colors.reset);
|
|
155
|
+
log(' 6. API docs: ' + colors.bright + 'http://localhost:3000/docs' + colors.reset);
|
|
156
|
+
log(' 7. Test API: ' + colors.bright + 'curl http://localhost:3000/health' + colors.reset + '\n');
|
|
157
|
+
} else if (migrationStatus.apiKey !== 'success') {
|
|
158
|
+
log(' 2. Generate API key: ' + colors.bright + 'npm run generate-key' + colors.reset + ' (for library integration)');
|
|
117
159
|
log(' 3. Start the server: ' + colors.bright + 'vezlo-server' + colors.reset);
|
|
118
160
|
log(' 4. Visit: ' + colors.bright + 'http://localhost:3000/health' + colors.reset);
|
|
119
161
|
log(' 5. API docs: ' + colors.bright + 'http://localhost:3000/docs' + colors.reset);
|
|
@@ -165,14 +207,22 @@ async function setupSupabase() {
|
|
|
165
207
|
const client = createClient(supabaseUrl.trim(), supabaseServiceKey.trim());
|
|
166
208
|
const { error } = await client.from('vezlo_conversations').select('count').limit(0);
|
|
167
209
|
|
|
168
|
-
if (error
|
|
169
|
-
|
|
210
|
+
if (error) {
|
|
211
|
+
// Check for table not found errors (normal before migrations run)
|
|
212
|
+
if (error.code === 'PGRST116' ||
|
|
213
|
+
error.message.includes('does not exist') ||
|
|
214
|
+
error.message.includes('Could not find the table')) {
|
|
215
|
+
log('✅ Supabase connection successful!', 'green');
|
|
216
|
+
log('⚠️ Note: Table not found - this is normal before running migrations\n', 'yellow');
|
|
217
|
+
} else {
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
log('✅ Supabase connection successful!\n', 'green');
|
|
170
222
|
}
|
|
171
|
-
|
|
172
|
-
log('✅ Supabase connection successful!\n', 'green');
|
|
173
223
|
} catch (err) {
|
|
174
224
|
log(`❌ Supabase connection failed: ${err.message}`, 'red');
|
|
175
|
-
log('⚠️
|
|
225
|
+
log('⚠️ This might be because migrations haven\'t run yet, or check your credentials.', 'yellow');
|
|
176
226
|
}
|
|
177
227
|
|
|
178
228
|
// Validate database connection (same as validate script)
|
|
@@ -314,11 +364,19 @@ async function validateEnvironment(config) {
|
|
|
314
364
|
const client = createClient(config.SUPABASE_URL, config.SUPABASE_SERVICE_KEY);
|
|
315
365
|
const { error } = await client.from('vezlo_conversations').select('count').limit(0);
|
|
316
366
|
|
|
317
|
-
if (error
|
|
318
|
-
|
|
367
|
+
if (error) {
|
|
368
|
+
// Check for table not found errors (normal before migrations run)
|
|
369
|
+
if (error.code === 'PGRST116' ||
|
|
370
|
+
error.message.includes('does not exist') ||
|
|
371
|
+
error.message.includes('Could not find the table')) {
|
|
372
|
+
log('✅ Supabase API connection validated', 'green');
|
|
373
|
+
log('⚠️ Note: Table not found - this is normal before running migrations', 'yellow');
|
|
374
|
+
} else {
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
log('✅ Supabase API connection validated', 'green');
|
|
319
379
|
}
|
|
320
|
-
|
|
321
|
-
log('✅ Supabase API connection validated', 'green');
|
|
322
380
|
} catch (err) {
|
|
323
381
|
log(`❌ Supabase API validation failed: ${err.message}`, 'red');
|
|
324
382
|
// non-blocking; proceed to DB check anyway
|
|
@@ -414,9 +472,10 @@ async function setupMigrations(config, validationStatus) {
|
|
|
414
472
|
log('✅ Migrations completed successfully!', 'green');
|
|
415
473
|
return { migrations: 'success' };
|
|
416
474
|
} else {
|
|
417
|
-
log('\n⚠️ Migrations skipped
|
|
418
|
-
log(' npm run migrate:latest', '
|
|
419
|
-
log(' Or via API: GET /api/migrate?key=your-migration-secret
|
|
475
|
+
log('\n⚠️ Migrations skipped by user.', 'yellow');
|
|
476
|
+
log(' You can run them later using: ' + colors.cyan + 'npm run migrate:latest' + colors.reset, 'yellow');
|
|
477
|
+
log(' Or via API: ' + colors.cyan + 'GET /api/migrate?key=your-migration-secret' + colors.reset, 'yellow');
|
|
478
|
+
log('\n ⚠️ Note: Default data seeding will be skipped until migrations are run.\n', 'yellow');
|
|
420
479
|
return { migrations: 'skipped' };
|
|
421
480
|
}
|
|
422
481
|
} else {
|
|
@@ -436,11 +495,20 @@ async function setupMigrations(config, validationStatus) {
|
|
|
436
495
|
execSync('npm run migrate:latest', { stdio: 'inherit' });
|
|
437
496
|
|
|
438
497
|
log('✅ Migration check completed!', 'green');
|
|
498
|
+
await client.end();
|
|
439
499
|
return { migrations: 'success' };
|
|
500
|
+
} else {
|
|
501
|
+
log('⚠️ Migration check skipped by user.', 'yellow');
|
|
502
|
+
log(' You can run them later using: ' + colors.cyan + 'npm run migrate:latest' + colors.reset, 'yellow');
|
|
503
|
+
log(' Or via API: ' + colors.cyan + 'GET /api/migrate?key=your-migration-secret' + colors.reset, 'yellow');
|
|
504
|
+
log('\n ⚠️ Note: Default data seeding will be skipped until migrations are run.\n', 'yellow');
|
|
505
|
+
await client.end();
|
|
506
|
+
return { migrations: 'skipped' };
|
|
440
507
|
}
|
|
441
508
|
}
|
|
442
509
|
|
|
443
510
|
await client.end();
|
|
511
|
+
return { migrations: 'skipped' };
|
|
444
512
|
} catch (err) {
|
|
445
513
|
log(`❌ Migration setup failed: ${err.message}`, 'red');
|
|
446
514
|
log('\nYou can run migrations manually later:', 'yellow');
|
|
@@ -463,3 +531,70 @@ main().catch(error => {
|
|
|
463
531
|
rl.close();
|
|
464
532
|
process.exit(1);
|
|
465
533
|
});
|
|
534
|
+
async function setupDefaultData(config) {
|
|
535
|
+
log('🔄 Setting up default company and admin user...', 'yellow');
|
|
536
|
+
|
|
537
|
+
try {
|
|
538
|
+
// Import the setup service dynamically
|
|
539
|
+
const { runDefaultSetup } = await import('./seed-default.js');
|
|
540
|
+
|
|
541
|
+
// Set environment variables for the setup
|
|
542
|
+
process.env.SUPABASE_URL = config.SUPABASE_URL;
|
|
543
|
+
process.env.SUPABASE_SERVICE_KEY = config.SUPABASE_SERVICE_KEY;
|
|
544
|
+
process.env.DEFAULT_ADMIN_EMAIL = config.DEFAULT_ADMIN_EMAIL || 'admin@vezlo.org';
|
|
545
|
+
process.env.DEFAULT_ADMIN_PASSWORD = config.DEFAULT_ADMIN_PASSWORD || 'admin123';
|
|
546
|
+
process.env.ORGANIZATION_NAME = config.ORGANIZATION_NAME || 'Vezlo';
|
|
547
|
+
process.env.JWT_SECRET = config.JWT_SECRET || require('crypto').randomBytes(32).toString('hex');
|
|
548
|
+
|
|
549
|
+
// Run the default setup
|
|
550
|
+
await runDefaultSetup();
|
|
551
|
+
|
|
552
|
+
log('✅ Default data setup completed successfully!', 'green');
|
|
553
|
+
return 'success';
|
|
554
|
+
} catch (err) {
|
|
555
|
+
log(`❌ Default data setup failed: ${err.message}`, 'red');
|
|
556
|
+
log('\nYou can run default data setup manually later:', 'yellow');
|
|
557
|
+
log(' npm run seed-default', 'cyan');
|
|
558
|
+
return 'failed';
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
async function setupApiKey(config) {
|
|
563
|
+
log('🔄 Generating API key for library integration...', 'yellow');
|
|
564
|
+
|
|
565
|
+
try {
|
|
566
|
+
// Import the API key generator dynamically
|
|
567
|
+
const { generateApiKey } = await import('./generate-key.js');
|
|
568
|
+
|
|
569
|
+
// Set environment variables for the setup (already set in setupDefaultData)
|
|
570
|
+
|
|
571
|
+
// Run the API key generator in quiet mode
|
|
572
|
+
const result = await generateApiKey({ quiet: true });
|
|
573
|
+
|
|
574
|
+
if (result.success) {
|
|
575
|
+
// Show API key details
|
|
576
|
+
log('✅ API key generated successfully!', 'green');
|
|
577
|
+
log('\n📋 API Key Details:', 'bright');
|
|
578
|
+
log(` Company: ${result.company}`, 'reset');
|
|
579
|
+
log(` User: ${result.user.name}`, 'reset');
|
|
580
|
+
log(` API Key: ${result.apiKey}`, 'bright');
|
|
581
|
+
log('\n⚠️ IMPORTANT: Save this key securely. It will not be shown again.', 'yellow');
|
|
582
|
+
|
|
583
|
+
// Show usage example
|
|
584
|
+
log('\n🔧 Usage Example:', 'bright');
|
|
585
|
+
log(` curl -X POST http://localhost:3000/api/knowledge/items \\
|
|
586
|
+
-H "X-API-Key: ${result.apiKey}" \\
|
|
587
|
+
-H "Content-Type: application/json" \\
|
|
588
|
+
-d '{"title": "Example", "type": "document", "content": "Example content"}'`, 'cyan');
|
|
589
|
+
|
|
590
|
+
return 'success';
|
|
591
|
+
} else {
|
|
592
|
+
throw new Error(result.error);
|
|
593
|
+
}
|
|
594
|
+
} catch (err) {
|
|
595
|
+
log(`❌ API key generation failed: ${err.message}`, 'red');
|
|
596
|
+
log('\nYou can generate an API key manually later:', 'yellow');
|
|
597
|
+
log(' npm run generate-key', 'cyan');
|
|
598
|
+
return 'failed';
|
|
599
|
+
}
|
|
600
|
+
}
|
package/scripts/validate-db.js
CHANGED
|
@@ -57,15 +57,24 @@ async function validateDatabase() {
|
|
|
57
57
|
process.env.SUPABASE_SERVICE_KEY
|
|
58
58
|
);
|
|
59
59
|
|
|
60
|
-
// Try to query
|
|
60
|
+
// Try to query any table (query will fail if connection is bad)
|
|
61
61
|
const { error } = await supabase.from('vezlo_conversations').select('count').limit(0);
|
|
62
62
|
|
|
63
|
-
if (error
|
|
64
|
-
|
|
63
|
+
if (error) {
|
|
64
|
+
// Check for table not found errors (normal before migrations run)
|
|
65
|
+
if (error.code === 'PGRST116' ||
|
|
66
|
+
error.message.includes('does not exist') ||
|
|
67
|
+
error.message.includes('Could not find the table')) {
|
|
68
|
+
log('✅ Supabase connection successful', 'green');
|
|
69
|
+
log('⚠️ Note: Tables not found yet - this is normal before running migrations\n', 'yellow');
|
|
70
|
+
} else {
|
|
71
|
+
// Only throw if it's a real connection error
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
log('✅ Supabase connection successful\n', 'green');
|
|
65
76
|
}
|
|
66
77
|
|
|
67
|
-
log('✅ Supabase connection successful\n', 'green');
|
|
68
|
-
|
|
69
78
|
} catch (error) {
|
|
70
79
|
log(`❌ Supabase connection failed: ${error.message}\n`, 'red');
|
|
71
80
|
process.exit(1);
|
|
@@ -96,6 +105,10 @@ async function validateDatabase() {
|
|
|
96
105
|
|
|
97
106
|
// Check required tables
|
|
98
107
|
const requiredTables = [
|
|
108
|
+
'vezlo_companies',
|
|
109
|
+
'vezlo_users',
|
|
110
|
+
'vezlo_user_company_profiles',
|
|
111
|
+
'vezlo_api_keys',
|
|
99
112
|
'vezlo_conversations',
|
|
100
113
|
'vezlo_messages',
|
|
101
114
|
'vezlo_message_feedback',
|
|
@@ -142,7 +155,10 @@ async function validateDatabase() {
|
|
|
142
155
|
ORDER BY t.table_name
|
|
143
156
|
`, [requiredTables]);
|
|
144
157
|
|
|
145
|
-
log('\n📊
|
|
158
|
+
log('\n📊 Database Tables Summary:', 'cyan');
|
|
159
|
+
log(` Total tables found: ${schemaCheck.rows.length}`, 'green');
|
|
160
|
+
|
|
161
|
+
log('\n📋 Table Details:', 'cyan');
|
|
146
162
|
schemaCheck.rows.forEach(row => {
|
|
147
163
|
log(` ✓ ${row.table_name} (${row.column_count} columns)`, 'green');
|
|
148
164
|
});
|