@vezlo/assistant-server 1.3.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 +140 -43
- package/bin/vezlo-server.js +1 -1
- 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/global.d.ts.map +1 -1
- package/dist/src/config/global.js +5 -2
- package/dist/src/config/global.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 +34 -73
- 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 -615
- package/dist/src/server.js.map +1 -1
- package/dist/src/services/AIService.d.ts +1 -2
- package/dist/src/services/AIService.d.ts.map +1 -1
- package/dist/src/services/AIService.js +6 -32
- 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/dist/src/types/index.d.ts +0 -8
- package/dist/src/types/index.d.ts.map +1 -1
- package/env.example +7 -5
- package/knexfile.ts +17 -8
- package/package.json +25 -16
- package/scripts/generate-key.js +124 -0
- package/scripts/seed-default.js +72 -0
- package/scripts/setup.js +410 -149
- package/scripts/validate-db.js +46 -13
package/dist/src/server.js
CHANGED
|
@@ -43,49 +43,51 @@ const compression_1 = __importDefault(require("compression"));
|
|
|
43
43
|
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
|
|
44
44
|
const http_1 = require("http");
|
|
45
45
|
const socket_io_1 = require("socket.io");
|
|
46
|
-
const
|
|
47
|
-
// Import configurations
|
|
48
|
-
const logger_1 = __importDefault(require("./config/logger"));
|
|
49
|
-
const database_1 = require("./config/database");
|
|
46
|
+
const swagger_ui_express_1 = __importDefault(require("swagger-ui-express"));
|
|
50
47
|
const swagger_1 = require("./config/swagger");
|
|
51
48
|
const global_1 = require("./config/global");
|
|
49
|
+
const logger_1 = __importDefault(require("./config/logger"));
|
|
52
50
|
const errorHandler_1 = require("./middleware/errorHandler");
|
|
53
|
-
|
|
54
|
-
const
|
|
51
|
+
const auth_1 = require("./middleware/auth");
|
|
52
|
+
const ChatController_1 = require("./controllers/ChatController");
|
|
53
|
+
const KnowledgeController_1 = require("./controllers/KnowledgeController");
|
|
54
|
+
const AuthController_1 = require("./controllers/AuthController");
|
|
55
|
+
const ApiKeyController_1 = require("./controllers/ApiKeyController");
|
|
55
56
|
const ChatManager_1 = require("./services/ChatManager");
|
|
56
57
|
const KnowledgeBaseService_1 = require("./services/KnowledgeBaseService");
|
|
58
|
+
const AIService_1 = require("./services/AIService");
|
|
59
|
+
const ApiKeyService_1 = require("./services/ApiKeyService");
|
|
57
60
|
const UnifiedStorage_1 = require("./storage/UnifiedStorage");
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
(0, dotenv_1.config)();
|
|
61
|
+
const supabase_js_1 = require("@supabase/supabase-js");
|
|
62
|
+
// Initialize Supabase client - use SERVICE_KEY for server-side operations
|
|
63
|
+
// Service key bypasses RLS and is required for API key authentication
|
|
64
|
+
const supabase = (0, supabase_js_1.createClient)(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY || process.env.SUPABASE_ANON_KEY);
|
|
63
65
|
// Initialize Express app
|
|
64
66
|
const app = (0, express_1.default)();
|
|
65
67
|
const server = (0, http_1.createServer)(app);
|
|
66
68
|
const io = new socket_io_1.Server(server, {
|
|
67
69
|
cors: {
|
|
68
|
-
origin:
|
|
69
|
-
|
|
70
|
+
origin: "*",
|
|
71
|
+
methods: ["GET", "POST"]
|
|
70
72
|
}
|
|
71
73
|
});
|
|
72
74
|
// Middleware
|
|
73
75
|
app.use((0, helmet_1.default)());
|
|
74
|
-
app.use((0, compression_1.default)());
|
|
75
76
|
app.use((0, cors_1.default)({
|
|
76
77
|
origin: process.env.CORS_ORIGINS?.split(',') || '*',
|
|
77
78
|
credentials: true
|
|
78
79
|
}));
|
|
80
|
+
app.use((0, compression_1.default)());
|
|
79
81
|
app.use(express_1.default.json({ limit: '10mb' }));
|
|
80
82
|
app.use(express_1.default.urlencoded({ extended: true }));
|
|
81
|
-
// Rate limiting
|
|
83
|
+
// Rate limiting
|
|
82
84
|
const limiter = (0, express_rate_limit_1.default)({
|
|
83
85
|
windowMs: global_1.config.api.rateLimiting.windowMs,
|
|
84
86
|
max: global_1.config.api.rateLimiting.maxRequests,
|
|
85
87
|
message: {
|
|
86
88
|
error: {
|
|
87
89
|
code: 'RATE_LIMIT_EXCEEDED',
|
|
88
|
-
message: 'Too many requests
|
|
90
|
+
message: 'Too many requests, please try again later.',
|
|
89
91
|
timestamp: new Date().toISOString()
|
|
90
92
|
},
|
|
91
93
|
success: false
|
|
@@ -94,47 +96,44 @@ const limiter = (0, express_rate_limit_1.default)({
|
|
|
94
96
|
legacyHeaders: false
|
|
95
97
|
});
|
|
96
98
|
app.use('/api/', limiter);
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
// Redirect root to docs
|
|
100
|
+
app.get('/', (req, res) => {
|
|
101
|
+
res.redirect('/docs');
|
|
102
|
+
});
|
|
103
|
+
// API Documentation
|
|
104
|
+
app.use('/docs', swagger_ui_express_1.default.serve, swagger_ui_express_1.default.setup(swagger_1.specs, swagger_1.swaggerUiOptions));
|
|
105
|
+
// Initialize services
|
|
102
106
|
let chatController;
|
|
103
107
|
let knowledgeController;
|
|
108
|
+
let authController;
|
|
109
|
+
let apiKeyController;
|
|
104
110
|
async function initializeServices() {
|
|
105
|
-
logger_1.default.info('Initializing Vezlo services...');
|
|
106
111
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// Initialize
|
|
111
|
-
|
|
112
|
-
// Initialize knowledge base
|
|
113
|
-
knowledgeBase = new KnowledgeBaseService_1.KnowledgeBaseService({
|
|
112
|
+
logger_1.default.info('Initializing Vezlo services...');
|
|
113
|
+
// Initialize storage
|
|
114
|
+
const storage = new UnifiedStorage_1.UnifiedStorage(supabase, 'vezlo');
|
|
115
|
+
// Initialize knowledge base first
|
|
116
|
+
const knowledgeBase = new KnowledgeBaseService_1.KnowledgeBaseService({
|
|
114
117
|
supabase,
|
|
115
118
|
tableName: 'vezlo_knowledge_items'
|
|
116
119
|
});
|
|
117
|
-
// Initialize AI service
|
|
118
|
-
aiService = new AIService_1.AIService({
|
|
120
|
+
// Initialize AI service with knowledge base
|
|
121
|
+
const aiService = new AIService_1.AIService({
|
|
119
122
|
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
120
123
|
organizationName: process.env.ORGANIZATION_NAME || 'Vezlo',
|
|
121
|
-
assistantName: process.env.ASSISTANT_NAME || '
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
maxTokens: parseInt(process.env.AI_MAX_TOKENS || '1000'),
|
|
125
|
-
enableFeedbackDetection: process.env.ENABLE_FEEDBACK_DETECTION === 'true',
|
|
124
|
+
assistantName: process.env.ASSISTANT_NAME || 'AI Assistant',
|
|
125
|
+
platformDescription: process.env.PLATFORM_DESCRIPTION || 'AI-powered assistant platform',
|
|
126
|
+
supportEmail: process.env.SUPPORT_EMAIL || 'support@vezlo.com',
|
|
126
127
|
knowledgeBaseService: knowledgeBase
|
|
127
128
|
});
|
|
128
|
-
|
|
129
|
-
chatManager = new ChatManager_1.ChatManager({
|
|
130
|
-
aiService,
|
|
131
|
-
storage,
|
|
132
|
-
enableConversationManagement: true,
|
|
133
|
-
conversationTimeout: 3600000 // 1 hour
|
|
134
|
-
});
|
|
129
|
+
const chatManager = new ChatManager_1.ChatManager({ aiService, storage });
|
|
135
130
|
// Initialize controllers
|
|
136
|
-
chatController = new ChatController_1.ChatController(chatManager, storage);
|
|
131
|
+
chatController = new ChatController_1.ChatController(chatManager, storage, supabase);
|
|
137
132
|
knowledgeController = new KnowledgeController_1.KnowledgeController(knowledgeBase, aiService);
|
|
133
|
+
authController = new AuthController_1.AuthController(supabase);
|
|
134
|
+
// Initialize API key service and controller
|
|
135
|
+
const apiKeyService = new ApiKeyService_1.ApiKeyService(supabase);
|
|
136
|
+
apiKeyController = new ApiKeyController_1.ApiKeyController(apiKeyService);
|
|
138
137
|
logger_1.default.info('All services initialized successfully');
|
|
139
138
|
}
|
|
140
139
|
catch (error) {
|
|
@@ -142,599 +141,1021 @@ async function initializeServices() {
|
|
|
142
141
|
throw error;
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
* $ref: '#/components/schemas/Error'
|
|
171
|
-
*/
|
|
172
|
-
app.get('/health', async (req, res) => {
|
|
173
|
-
try {
|
|
174
|
-
const healthChecks = {
|
|
175
|
-
server: 'healthy',
|
|
176
|
-
timestamp: new Date().toISOString()
|
|
177
|
-
};
|
|
178
|
-
// Check Supabase connection
|
|
144
|
+
// Setup routes function
|
|
145
|
+
function setupRoutes() {
|
|
146
|
+
logger_1.default.info('Setting up routes...');
|
|
147
|
+
/**
|
|
148
|
+
* @swagger
|
|
149
|
+
* /health:
|
|
150
|
+
* get:
|
|
151
|
+
* summary: Health check endpoint
|
|
152
|
+
* description: Check server and database connectivity status
|
|
153
|
+
* tags: [Health]
|
|
154
|
+
* responses:
|
|
155
|
+
* 200:
|
|
156
|
+
* description: Server is healthy
|
|
157
|
+
* content:
|
|
158
|
+
* application/json:
|
|
159
|
+
* schema:
|
|
160
|
+
* $ref: '#/components/schemas/HealthCheck'
|
|
161
|
+
* 503:
|
|
162
|
+
* description: Server is unhealthy
|
|
163
|
+
* content:
|
|
164
|
+
* application/json:
|
|
165
|
+
* schema:
|
|
166
|
+
* $ref: '#/components/schemas/Error'
|
|
167
|
+
*/
|
|
168
|
+
app.get('/health', async (req, res) => {
|
|
179
169
|
try {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
170
|
+
const healthChecks = {
|
|
171
|
+
server: 'healthy',
|
|
172
|
+
timestamp: new Date().toISOString()
|
|
173
|
+
};
|
|
174
|
+
// Check Supabase connection
|
|
175
|
+
try {
|
|
176
|
+
const { data, error } = await supabase.from('vezlo_conversations').select('count').limit(1);
|
|
177
|
+
healthChecks.database = error ? 'disconnected' : 'connected';
|
|
184
178
|
}
|
|
185
|
-
|
|
186
|
-
healthChecks.
|
|
179
|
+
catch (dbError) {
|
|
180
|
+
healthChecks.database = 'error';
|
|
187
181
|
}
|
|
182
|
+
res.json(healthChecks);
|
|
188
183
|
}
|
|
189
184
|
catch (error) {
|
|
190
|
-
|
|
185
|
+
res.status(503).json({
|
|
186
|
+
server: 'unhealthy',
|
|
187
|
+
timestamp: new Date().toISOString(),
|
|
188
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
189
|
+
});
|
|
191
190
|
}
|
|
192
|
-
res.json({
|
|
193
|
-
status: 'healthy',
|
|
194
|
-
checks: healthChecks
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
catch (error) {
|
|
198
|
-
res.status(503).json({
|
|
199
|
-
status: 'unhealthy',
|
|
200
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
// ============================================================================
|
|
205
|
-
// CONVERSATION APIS (New 2-API Flow)
|
|
206
|
-
// ============================================================================
|
|
207
|
-
/**
|
|
208
|
-
* @swagger
|
|
209
|
-
* /api/conversations:
|
|
210
|
-
* post:
|
|
211
|
-
* summary: Create a new conversation
|
|
212
|
-
* description: Create a new conversation for chat
|
|
213
|
-
* tags: [Conversations]
|
|
214
|
-
* requestBody:
|
|
215
|
-
* required: true
|
|
216
|
-
* content:
|
|
217
|
-
* application/json:
|
|
218
|
-
* schema:
|
|
219
|
-
* $ref: '#/components/schemas/CreateConversationRequest'
|
|
220
|
-
* responses:
|
|
221
|
-
* 200:
|
|
222
|
-
* description: Conversation created successfully
|
|
223
|
-
* content:
|
|
224
|
-
* application/json:
|
|
225
|
-
* schema:
|
|
226
|
-
* $ref: '#/components/schemas/ConversationResponse'
|
|
227
|
-
* 400:
|
|
228
|
-
* description: Bad request
|
|
229
|
-
* 500:
|
|
230
|
-
* description: Internal server error
|
|
231
|
-
*/
|
|
232
|
-
app.post('/api/conversations', (req, res) => chatController.createConversation(req, res));
|
|
233
|
-
/**
|
|
234
|
-
* @swagger
|
|
235
|
-
* /api/conversations/{uuid}:
|
|
236
|
-
* get:
|
|
237
|
-
* summary: Get conversation details with messages
|
|
238
|
-
* description: Retrieve conversation information and message history
|
|
239
|
-
* tags: [Conversations]
|
|
240
|
-
* parameters:
|
|
241
|
-
* - in: path
|
|
242
|
-
* name: uuid
|
|
243
|
-
* required: true
|
|
244
|
-
* schema:
|
|
245
|
-
* type: string
|
|
246
|
-
* description: Conversation UUID
|
|
247
|
-
* responses:
|
|
248
|
-
* 200:
|
|
249
|
-
* description: Conversation details with messages
|
|
250
|
-
* 404:
|
|
251
|
-
* description: Conversation not found
|
|
252
|
-
* 500:
|
|
253
|
-
* description: Internal server error
|
|
254
|
-
*/
|
|
255
|
-
app.get('/api/conversations/:uuid', (req, res) => chatController.getConversation(req, res));
|
|
256
|
-
/**
|
|
257
|
-
* @swagger
|
|
258
|
-
* /api/conversations/{uuid}:
|
|
259
|
-
* delete:
|
|
260
|
-
* summary: Delete conversation
|
|
261
|
-
* description: Soft delete a conversation and its messages
|
|
262
|
-
* tags: [Conversations]
|
|
263
|
-
* parameters:
|
|
264
|
-
* - in: path
|
|
265
|
-
* name: uuid
|
|
266
|
-
* required: true
|
|
267
|
-
* schema:
|
|
268
|
-
* type: string
|
|
269
|
-
* description: Conversation UUID
|
|
270
|
-
* responses:
|
|
271
|
-
* 200:
|
|
272
|
-
* description: Conversation deleted successfully
|
|
273
|
-
* 404:
|
|
274
|
-
* description: Conversation not found
|
|
275
|
-
* 500:
|
|
276
|
-
* description: Internal server error
|
|
277
|
-
*/
|
|
278
|
-
app.delete('/api/conversations/:uuid', (req, res) => chatController.deleteConversation(req, res));
|
|
279
|
-
/**
|
|
280
|
-
* @swagger
|
|
281
|
-
* /api/conversations/{uuid}/messages:
|
|
282
|
-
* post:
|
|
283
|
-
* summary: Create user message in conversation
|
|
284
|
-
* description: Add a user message to the conversation
|
|
285
|
-
* tags: [Messages]
|
|
286
|
-
* parameters:
|
|
287
|
-
* - in: path
|
|
288
|
-
* name: uuid
|
|
289
|
-
* required: true
|
|
290
|
-
* schema:
|
|
291
|
-
* type: string
|
|
292
|
-
* description: Conversation UUID
|
|
293
|
-
* requestBody:
|
|
294
|
-
* required: true
|
|
295
|
-
* content:
|
|
296
|
-
* application/json:
|
|
297
|
-
* schema:
|
|
298
|
-
* $ref: '#/components/schemas/CreateMessageRequest'
|
|
299
|
-
* responses:
|
|
300
|
-
* 200:
|
|
301
|
-
* description: User message created successfully
|
|
302
|
-
* content:
|
|
303
|
-
* application/json:
|
|
304
|
-
* schema:
|
|
305
|
-
* $ref: '#/components/schemas/MessageResponse'
|
|
306
|
-
* 400:
|
|
307
|
-
* description: Bad request
|
|
308
|
-
* 404:
|
|
309
|
-
* description: Conversation not found
|
|
310
|
-
* 500:
|
|
311
|
-
* description: Internal server error
|
|
312
|
-
*/
|
|
313
|
-
app.post('/api/conversations/:uuid/messages', (req, res) => chatController.createUserMessage(req, res));
|
|
314
|
-
/**
|
|
315
|
-
* @swagger
|
|
316
|
-
* /api/messages/{uuid}/generate:
|
|
317
|
-
* post:
|
|
318
|
-
* summary: Generate AI response for user message
|
|
319
|
-
* description: Generate AI assistant response to a user message
|
|
320
|
-
* tags: [Messages]
|
|
321
|
-
* parameters:
|
|
322
|
-
* - in: path
|
|
323
|
-
* name: uuid
|
|
324
|
-
* required: true
|
|
325
|
-
* schema:
|
|
326
|
-
* type: string
|
|
327
|
-
* description: User message UUID
|
|
328
|
-
* responses:
|
|
329
|
-
* 200:
|
|
330
|
-
* description: AI response generated successfully
|
|
331
|
-
* content:
|
|
332
|
-
* application/json:
|
|
333
|
-
* schema:
|
|
334
|
-
* $ref: '#/components/schemas/MessageResponse'
|
|
335
|
-
* 404:
|
|
336
|
-
* description: Message not found
|
|
337
|
-
* 500:
|
|
338
|
-
* description: Internal server error
|
|
339
|
-
*/
|
|
340
|
-
app.post('/api/messages/:uuid/generate', (req, res) => chatController.generateResponse(req, res));
|
|
341
|
-
/**
|
|
342
|
-
* @swagger
|
|
343
|
-
* /api/users/{uuid}/conversations:
|
|
344
|
-
* get:
|
|
345
|
-
* summary: Get user's conversations
|
|
346
|
-
* description: Retrieve all conversations for a specific user
|
|
347
|
-
* tags: [Conversations]
|
|
348
|
-
* parameters:
|
|
349
|
-
* - in: path
|
|
350
|
-
* name: uuid
|
|
351
|
-
* required: true
|
|
352
|
-
* schema:
|
|
353
|
-
* type: string
|
|
354
|
-
* description: User UUID
|
|
355
|
-
* - in: query
|
|
356
|
-
* name: company_uuid
|
|
357
|
-
* schema:
|
|
358
|
-
* type: string
|
|
359
|
-
* description: Optional company UUID filter
|
|
360
|
-
* responses:
|
|
361
|
-
* 200:
|
|
362
|
-
* description: List of user conversations
|
|
363
|
-
* 500:
|
|
364
|
-
* description: Internal server error
|
|
365
|
-
*/
|
|
366
|
-
app.get('/api/users/:uuid/conversations', (req, res) => chatController.getUserConversations(req, res));
|
|
367
|
-
/**
|
|
368
|
-
* @swagger
|
|
369
|
-
* /api/feedback:
|
|
370
|
-
* post:
|
|
371
|
-
* summary: Submit message feedback
|
|
372
|
-
* description: Submit rating and feedback for an AI response
|
|
373
|
-
* tags: [Messages]
|
|
374
|
-
* requestBody:
|
|
375
|
-
* required: true
|
|
376
|
-
* content:
|
|
377
|
-
* application/json:
|
|
378
|
-
* schema:
|
|
379
|
-
* $ref: '#/components/schemas/FeedbackRequest'
|
|
380
|
-
* responses:
|
|
381
|
-
* 200:
|
|
382
|
-
* description: Feedback submitted successfully
|
|
383
|
-
* content:
|
|
384
|
-
* application/json:
|
|
385
|
-
* schema:
|
|
386
|
-
* $ref: '#/components/schemas/FeedbackResponse'
|
|
387
|
-
* 400:
|
|
388
|
-
* description: Missing required fields
|
|
389
|
-
* 500:
|
|
390
|
-
* description: Internal server error
|
|
391
|
-
*/
|
|
392
|
-
app.post('/api/feedback', (req, res) => chatController.submitFeedback(req, res));
|
|
393
|
-
// ============================================================================
|
|
394
|
-
// KNOWLEDGE BASE APIS (Unified Items)
|
|
395
|
-
// ============================================================================
|
|
396
|
-
/**
|
|
397
|
-
* @swagger
|
|
398
|
-
* /api/knowledge/items:
|
|
399
|
-
* post:
|
|
400
|
-
* summary: Create knowledge item
|
|
401
|
-
* description: Create a new knowledge item (folder, document, file, url, etc.)
|
|
402
|
-
* tags: [Knowledge]
|
|
403
|
-
* requestBody:
|
|
404
|
-
* required: true
|
|
405
|
-
* content:
|
|
406
|
-
* application/json:
|
|
407
|
-
* schema:
|
|
408
|
-
* $ref: '#/components/schemas/CreateKnowledgeItemRequest'
|
|
409
|
-
* responses:
|
|
410
|
-
* 200:
|
|
411
|
-
* description: Knowledge item created successfully
|
|
412
|
-
* 400:
|
|
413
|
-
* description: Bad request
|
|
414
|
-
* 500:
|
|
415
|
-
* description: Internal server error
|
|
416
|
-
*/
|
|
417
|
-
app.post('/api/knowledge/items', (req, res) => knowledgeController.createItem(req, res));
|
|
418
|
-
/**
|
|
419
|
-
* @swagger
|
|
420
|
-
* /api/knowledge/items:
|
|
421
|
-
* get:
|
|
422
|
-
* summary: List knowledge items
|
|
423
|
-
* description: Get list of knowledge items with optional filtering and pagination
|
|
424
|
-
* tags: [Knowledge]
|
|
425
|
-
* parameters:
|
|
426
|
-
* - in: query
|
|
427
|
-
* name: parent_uuid
|
|
428
|
-
* schema:
|
|
429
|
-
* type: string
|
|
430
|
-
* description: Filter by parent UUID
|
|
431
|
-
* - in: query
|
|
432
|
-
* name: company_uuid
|
|
433
|
-
* schema:
|
|
434
|
-
* type: integer
|
|
435
|
-
* description: Filter by company UUID
|
|
436
|
-
* - in: query
|
|
437
|
-
* name: type
|
|
438
|
-
* schema:
|
|
439
|
-
* type: string
|
|
440
|
-
* description: Filter by item type
|
|
441
|
-
* - in: query
|
|
442
|
-
* name: limit
|
|
443
|
-
* schema:
|
|
444
|
-
* type: integer
|
|
445
|
-
* default: 50
|
|
446
|
-
* description: Maximum number of items to return
|
|
447
|
-
* - in: query
|
|
448
|
-
* name: offset
|
|
449
|
-
* schema:
|
|
450
|
-
* type: integer
|
|
451
|
-
* default: 0
|
|
452
|
-
* description: Number of items to skip
|
|
453
|
-
* responses:
|
|
454
|
-
* 200:
|
|
455
|
-
* description: List of knowledge items
|
|
456
|
-
* 500:
|
|
457
|
-
* description: Internal server error
|
|
458
|
-
*/
|
|
459
|
-
app.get('/api/knowledge/items', (req, res) => knowledgeController.listItems(req, res));
|
|
460
|
-
/**
|
|
461
|
-
* @swagger
|
|
462
|
-
* /api/knowledge/search:
|
|
463
|
-
* post:
|
|
464
|
-
* summary: Search knowledge items
|
|
465
|
-
* description: Search through knowledge items using semantic or keyword search
|
|
466
|
-
* tags: [Knowledge]
|
|
467
|
-
* requestBody:
|
|
468
|
-
* required: true
|
|
469
|
-
* content:
|
|
470
|
-
* application/json:
|
|
471
|
-
* schema:
|
|
472
|
-
* $ref: '#/components/schemas/KnowledgeSearchRequest'
|
|
473
|
-
* responses:
|
|
474
|
-
* 200:
|
|
475
|
-
* description: Search results
|
|
476
|
-
* 400:
|
|
477
|
-
* description: Bad request
|
|
478
|
-
* 500:
|
|
479
|
-
* description: Internal server error
|
|
480
|
-
*/
|
|
481
|
-
app.post('/api/knowledge/search', (req, res) => knowledgeController.search(req, res));
|
|
482
|
-
/**
|
|
483
|
-
* @swagger
|
|
484
|
-
* /api/search:
|
|
485
|
-
* post:
|
|
486
|
-
* summary: RAG Search
|
|
487
|
-
* description: Perform RAG (Retrieval-Augmented Generation) search that combines knowledge base search with AI response generation
|
|
488
|
-
* tags: [Search]
|
|
489
|
-
* requestBody:
|
|
490
|
-
* required: true
|
|
491
|
-
* content:
|
|
492
|
-
* application/json:
|
|
493
|
-
* schema:
|
|
494
|
-
* $ref: '#/components/schemas/RAGSearchRequest'
|
|
495
|
-
* responses:
|
|
496
|
-
* 200:
|
|
497
|
-
* description: RAG search response
|
|
498
|
-
* content:
|
|
499
|
-
* application/json:
|
|
500
|
-
* schema:
|
|
501
|
-
* $ref: '#/components/schemas/RAGSearchResponse'
|
|
502
|
-
* 400:
|
|
503
|
-
* description: Bad request
|
|
504
|
-
* 500:
|
|
505
|
-
* description: Internal server error
|
|
506
|
-
*/
|
|
507
|
-
app.post('/api/search', (req, res) => knowledgeController.ragSearch(req, res));
|
|
508
|
-
/**
|
|
509
|
-
* @swagger
|
|
510
|
-
* /api/knowledge/items/{uuid}:
|
|
511
|
-
* get:
|
|
512
|
-
* summary: Get knowledge item
|
|
513
|
-
* description: Retrieve a specific knowledge item by UUID
|
|
514
|
-
* tags: [Knowledge]
|
|
515
|
-
* parameters:
|
|
516
|
-
* - in: path
|
|
517
|
-
* name: uuid
|
|
518
|
-
* required: true
|
|
519
|
-
* schema:
|
|
520
|
-
* type: string
|
|
521
|
-
* description: Knowledge item UUID
|
|
522
|
-
* responses:
|
|
523
|
-
* 200:
|
|
524
|
-
* description: Knowledge item details
|
|
525
|
-
* 404:
|
|
526
|
-
* description: Knowledge item not found
|
|
527
|
-
* 500:
|
|
528
|
-
* description: Internal server error
|
|
529
|
-
*/
|
|
530
|
-
app.get('/api/knowledge/items/:uuid', (req, res) => knowledgeController.getItem(req, res));
|
|
531
|
-
/**
|
|
532
|
-
* @swagger
|
|
533
|
-
* /api/knowledge/items/{uuid}:
|
|
534
|
-
* put:
|
|
535
|
-
* summary: Update knowledge item
|
|
536
|
-
* description: Update an existing knowledge item
|
|
537
|
-
* tags: [Knowledge]
|
|
538
|
-
* parameters:
|
|
539
|
-
* - in: path
|
|
540
|
-
* name: uuid
|
|
541
|
-
* required: true
|
|
542
|
-
* schema:
|
|
543
|
-
* type: string
|
|
544
|
-
* description: Knowledge item UUID
|
|
545
|
-
* requestBody:
|
|
546
|
-
* required: true
|
|
547
|
-
* content:
|
|
548
|
-
* application/json:
|
|
549
|
-
* schema:
|
|
550
|
-
* $ref: '#/components/schemas/UpdateKnowledgeItemRequest'
|
|
551
|
-
* responses:
|
|
552
|
-
* 200:
|
|
553
|
-
* description: Knowledge item updated successfully
|
|
554
|
-
* 404:
|
|
555
|
-
* description: Knowledge item not found
|
|
556
|
-
* 500:
|
|
557
|
-
* description: Internal server error
|
|
558
|
-
*/
|
|
559
|
-
app.put('/api/knowledge/items/:uuid', (req, res) => knowledgeController.updateItem(req, res));
|
|
560
|
-
/**
|
|
561
|
-
* @swagger
|
|
562
|
-
* /api/knowledge/items/{uuid}:
|
|
563
|
-
* delete:
|
|
564
|
-
* summary: Delete knowledge item
|
|
565
|
-
* description: Remove a knowledge item from the knowledge base
|
|
566
|
-
* tags: [Knowledge]
|
|
567
|
-
* parameters:
|
|
568
|
-
* - in: path
|
|
569
|
-
* name: uuid
|
|
570
|
-
* required: true
|
|
571
|
-
* schema:
|
|
572
|
-
* type: string
|
|
573
|
-
* description: Knowledge item UUID
|
|
574
|
-
* responses:
|
|
575
|
-
* 200:
|
|
576
|
-
* description: Knowledge item deleted successfully
|
|
577
|
-
* 404:
|
|
578
|
-
* description: Knowledge item not found
|
|
579
|
-
* 500:
|
|
580
|
-
* description: Internal server error
|
|
581
|
-
*/
|
|
582
|
-
app.delete('/api/knowledge/items/:uuid', (req, res) => knowledgeController.deleteItem(req, res));
|
|
583
|
-
// ============================================================================
|
|
584
|
-
// MIGRATION ENDPOINTS
|
|
585
|
-
// ============================================================================
|
|
586
|
-
/**
|
|
587
|
-
* @swagger
|
|
588
|
-
* /api/migrate:
|
|
589
|
-
* get:
|
|
590
|
-
* summary: Run database migrations
|
|
591
|
-
* description: Executes pending database migrations with API key authentication
|
|
592
|
-
* tags: [System]
|
|
593
|
-
* parameters:
|
|
594
|
-
* - in: query
|
|
595
|
-
* name: key
|
|
596
|
-
* required: true
|
|
597
|
-
* schema:
|
|
598
|
-
* type: string
|
|
599
|
-
* description: Migration API key
|
|
600
|
-
* responses:
|
|
601
|
-
* 200:
|
|
602
|
-
* description: Migrations completed successfully
|
|
603
|
-
* content:
|
|
604
|
-
* application/json:
|
|
605
|
-
* schema:
|
|
606
|
-
* type: object
|
|
607
|
-
* properties:
|
|
608
|
-
* success:
|
|
609
|
-
* type: boolean
|
|
610
|
-
* message:
|
|
611
|
-
* type: string
|
|
612
|
-
* currentVersion:
|
|
613
|
-
* type: string
|
|
614
|
-
* details:
|
|
615
|
-
* type: object
|
|
616
|
-
* 400:
|
|
617
|
-
* description: Bad request (missing API key or environment variables)
|
|
618
|
-
* 401:
|
|
619
|
-
* description: Unauthorized (invalid API key)
|
|
620
|
-
* 500:
|
|
621
|
-
* description: Internal server error
|
|
622
|
-
*/
|
|
623
|
-
app.get('/api/migrate', (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
|
624
|
-
const { MigrationService } = await Promise.resolve().then(() => __importStar(require('./services/MigrationService')));
|
|
625
|
-
const apiKey = req.query.key || req.headers['x-migration-key'];
|
|
626
|
-
if (!apiKey) {
|
|
627
|
-
return res.status(400).json({
|
|
628
|
-
success: false,
|
|
629
|
-
message: 'Migration API key is required',
|
|
630
|
-
error: 'MISSING_API_KEY',
|
|
631
|
-
details: {
|
|
632
|
-
usage: 'Add ?key=your-secret-key to the URL or x-migration-key header'
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
const result = await MigrationService.runMigrations(apiKey);
|
|
637
|
-
const statusCode = result.success ? 200 :
|
|
638
|
-
result.error === 'UNAUTHORIZED' ? 401 :
|
|
639
|
-
result.error === 'MISSING_ENV_VARS' || result.error === 'DATABASE_CONNECTION_FAILED' ? 400 : 500;
|
|
640
|
-
res.status(statusCode).json(result);
|
|
641
|
-
}));
|
|
642
|
-
/**
|
|
643
|
-
* @swagger
|
|
644
|
-
* /api/migrate/status:
|
|
645
|
-
* get:
|
|
646
|
-
* summary: Get migration status
|
|
647
|
-
* description: Check current migration status without running migrations
|
|
648
|
-
* tags: [System]
|
|
649
|
-
* parameters:
|
|
650
|
-
* - in: query
|
|
651
|
-
* name: key
|
|
652
|
-
* required: true
|
|
653
|
-
* schema:
|
|
654
|
-
* type: string
|
|
655
|
-
* description: Migration API key
|
|
656
|
-
* responses:
|
|
657
|
-
* 200:
|
|
658
|
-
* description: Migration status retrieved
|
|
659
|
-
* 401:
|
|
660
|
-
* description: Unauthorized (invalid API key)
|
|
661
|
-
* 500:
|
|
662
|
-
* description: Internal server error
|
|
663
|
-
*/
|
|
664
|
-
app.get('/api/migrate/status', (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
|
665
|
-
const { MigrationService } = await Promise.resolve().then(() => __importStar(require('./services/MigrationService')));
|
|
666
|
-
const apiKey = req.query.key || req.headers['x-migration-key'];
|
|
667
|
-
if (!apiKey) {
|
|
668
|
-
return res.status(400).json({
|
|
669
|
-
success: false,
|
|
670
|
-
message: 'Migration API key is required',
|
|
671
|
-
error: 'MISSING_API_KEY'
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
const result = await MigrationService.getStatus(apiKey);
|
|
675
|
-
const statusCode = result.success ? 200 :
|
|
676
|
-
result.error === 'UNAUTHORIZED' ? 401 : 500;
|
|
677
|
-
res.status(statusCode).json(result);
|
|
678
|
-
}));
|
|
679
|
-
// ============================================================================
|
|
680
|
-
// WEBSOCKET HANDLING
|
|
681
|
-
// ============================================================================
|
|
682
|
-
// WebSocket handling
|
|
683
|
-
io.on('connection', (socket) => {
|
|
684
|
-
logger_1.default.info(`Client connected: ${socket.id}`);
|
|
685
|
-
socket.on('join-conversation', (conversationId) => {
|
|
686
|
-
socket.join(`conversation:${conversationId}`);
|
|
687
|
-
logger_1.default.info(`Socket ${socket.id} joined conversation ${conversationId}`);
|
|
688
191
|
});
|
|
689
|
-
|
|
192
|
+
/**
|
|
193
|
+
* @swagger
|
|
194
|
+
* /api/auth/login:
|
|
195
|
+
* post:
|
|
196
|
+
* summary: User login
|
|
197
|
+
* description: Authenticate user and return access token
|
|
198
|
+
* tags: [Authentication]
|
|
199
|
+
* requestBody:
|
|
200
|
+
* required: true
|
|
201
|
+
* content:
|
|
202
|
+
* application/json:
|
|
203
|
+
* schema:
|
|
204
|
+
* $ref: '#/components/schemas/LoginRequest'
|
|
205
|
+
* responses:
|
|
206
|
+
* 200:
|
|
207
|
+
* description: Login successful
|
|
208
|
+
* content:
|
|
209
|
+
* application/json:
|
|
210
|
+
* schema:
|
|
211
|
+
* $ref: '#/components/schemas/LoginResponse'
|
|
212
|
+
* 401:
|
|
213
|
+
* description: Invalid credentials
|
|
214
|
+
* 500:
|
|
215
|
+
* description: Internal server error
|
|
216
|
+
*/
|
|
217
|
+
app.post('/api/auth/login', (req, res) => authController.login(req, res));
|
|
218
|
+
/**
|
|
219
|
+
* @swagger
|
|
220
|
+
* /api/auth/logout:
|
|
221
|
+
* post:
|
|
222
|
+
* summary: User logout
|
|
223
|
+
* description: Logout user and invalidate tokens
|
|
224
|
+
* tags: [Authentication]
|
|
225
|
+
* security:
|
|
226
|
+
* - bearerAuth: []
|
|
227
|
+
* responses:
|
|
228
|
+
* 200:
|
|
229
|
+
* description: Logout successful
|
|
230
|
+
* content:
|
|
231
|
+
* application/json:
|
|
232
|
+
* schema:
|
|
233
|
+
* $ref: '#/components/schemas/LogoutResponse'
|
|
234
|
+
* 401:
|
|
235
|
+
* description: Not authenticated
|
|
236
|
+
* 500:
|
|
237
|
+
* description: Internal server error
|
|
238
|
+
*/
|
|
239
|
+
app.post('/api/auth/logout', (0, auth_1.authenticateUser)(supabase), (req, res) => authController.logout(req, res));
|
|
240
|
+
/**
|
|
241
|
+
* @swagger
|
|
242
|
+
* /api/auth/me:
|
|
243
|
+
* get:
|
|
244
|
+
* summary: Get current user info
|
|
245
|
+
* description: Get current authenticated user information
|
|
246
|
+
* tags: [Authentication]
|
|
247
|
+
* security:
|
|
248
|
+
* - bearerAuth: []
|
|
249
|
+
* responses:
|
|
250
|
+
* 200:
|
|
251
|
+
* description: User information retrieved
|
|
252
|
+
* content:
|
|
253
|
+
* application/json:
|
|
254
|
+
* schema:
|
|
255
|
+
* $ref: '#/components/schemas/MeResponse'
|
|
256
|
+
* 401:
|
|
257
|
+
* description: Not authenticated
|
|
258
|
+
* 500:
|
|
259
|
+
* description: Internal server error
|
|
260
|
+
*/
|
|
261
|
+
app.get('/api/auth/me', (0, auth_1.authenticateUser)(supabase), (req, res) => authController.getMe(req, res));
|
|
262
|
+
// API Key Management Routes
|
|
263
|
+
/**
|
|
264
|
+
* @swagger
|
|
265
|
+
* /api/api-keys:
|
|
266
|
+
* post:
|
|
267
|
+
* summary: Generate or update API key
|
|
268
|
+
* description: Generate or update API key for the authenticated user's company. Only admins can generate API keys.
|
|
269
|
+
* tags: [API Keys]
|
|
270
|
+
* security:
|
|
271
|
+
* - bearerAuth: []
|
|
272
|
+
* responses:
|
|
273
|
+
* 200:
|
|
274
|
+
* description: API key generated successfully
|
|
275
|
+
* content:
|
|
276
|
+
* application/json:
|
|
277
|
+
* schema:
|
|
278
|
+
* type: object
|
|
279
|
+
* properties:
|
|
280
|
+
* success:
|
|
281
|
+
* type: boolean
|
|
282
|
+
* uuid:
|
|
283
|
+
* type: string
|
|
284
|
+
* api_key:
|
|
285
|
+
* type: string
|
|
286
|
+
* message:
|
|
287
|
+
* type: string
|
|
288
|
+
* 403:
|
|
289
|
+
* description: Only admins can generate API keys
|
|
290
|
+
* 401:
|
|
291
|
+
* description: Not authenticated
|
|
292
|
+
* 500:
|
|
293
|
+
* description: Internal server error
|
|
294
|
+
*/
|
|
295
|
+
app.post('/api/api-keys', (0, auth_1.authenticateUser)(supabase), (req, res) => apiKeyController.generateApiKey(req, res));
|
|
296
|
+
/**
|
|
297
|
+
* @swagger
|
|
298
|
+
* /api/api-keys/status:
|
|
299
|
+
* get:
|
|
300
|
+
* summary: Get API key status
|
|
301
|
+
* description: Check if API key exists for the authenticated user's company
|
|
302
|
+
* tags: [API Keys]
|
|
303
|
+
* security:
|
|
304
|
+
* - bearerAuth: []
|
|
305
|
+
* responses:
|
|
306
|
+
* 200:
|
|
307
|
+
* description: API key status retrieved
|
|
308
|
+
* content:
|
|
309
|
+
* application/json:
|
|
310
|
+
* schema:
|
|
311
|
+
* type: object
|
|
312
|
+
* properties:
|
|
313
|
+
* exists:
|
|
314
|
+
* type: boolean
|
|
315
|
+
* uuid:
|
|
316
|
+
* type: string
|
|
317
|
+
* message:
|
|
318
|
+
* type: string
|
|
319
|
+
* 401:
|
|
320
|
+
* description: Not authenticated
|
|
321
|
+
* 500:
|
|
322
|
+
* description: Internal server error
|
|
323
|
+
*/
|
|
324
|
+
app.get('/api/api-keys/status', (0, auth_1.authenticateUser)(supabase), (req, res) => apiKeyController.getApiKeyStatus(req, res));
|
|
325
|
+
// Chat API Routes
|
|
326
|
+
/**
|
|
327
|
+
* @swagger
|
|
328
|
+
* /api/conversations:
|
|
329
|
+
* post:
|
|
330
|
+
* summary: Create a new conversation
|
|
331
|
+
* description: Create a new conversation (Public API - No authentication required)
|
|
332
|
+
* tags: [Chat]
|
|
333
|
+
* requestBody:
|
|
334
|
+
* required: true
|
|
335
|
+
* content:
|
|
336
|
+
* application/json:
|
|
337
|
+
* schema:
|
|
338
|
+
* $ref: '#/components/schemas/CreateConversationRequest'
|
|
339
|
+
* responses:
|
|
340
|
+
* 201:
|
|
341
|
+
* description: Conversation created successfully
|
|
342
|
+
* content:
|
|
343
|
+
* application/json:
|
|
344
|
+
* schema:
|
|
345
|
+
* $ref: '#/components/schemas/CreateConversationResponse'
|
|
346
|
+
* 400:
|
|
347
|
+
* description: Invalid request
|
|
348
|
+
* 500:
|
|
349
|
+
* description: Internal server error
|
|
350
|
+
*/
|
|
351
|
+
app.post('/api/conversations', (req, res) => chatController.createConversation(req, res));
|
|
352
|
+
/**
|
|
353
|
+
* @swagger
|
|
354
|
+
* /api/conversations/{uuid}:
|
|
355
|
+
* get:
|
|
356
|
+
* summary: Get conversation by UUID
|
|
357
|
+
* description: Retrieve a specific conversation by its UUID (Public API - No authentication required)
|
|
358
|
+
* tags: [Chat]
|
|
359
|
+
* parameters:
|
|
360
|
+
* - in: path
|
|
361
|
+
* name: uuid
|
|
362
|
+
* required: true
|
|
363
|
+
* schema:
|
|
364
|
+
* type: string
|
|
365
|
+
* description: Conversation UUID
|
|
366
|
+
* responses:
|
|
367
|
+
* 200:
|
|
368
|
+
* description: Conversation retrieved successfully
|
|
369
|
+
* content:
|
|
370
|
+
* application/json:
|
|
371
|
+
* schema:
|
|
372
|
+
* $ref: '#/components/schemas/GetConversationResponse'
|
|
373
|
+
* 404:
|
|
374
|
+
* description: Conversation not found
|
|
375
|
+
* 500:
|
|
376
|
+
* description: Internal server error
|
|
377
|
+
*/
|
|
378
|
+
app.get('/api/conversations/:uuid', (req, res) => chatController.getConversation(req, res));
|
|
379
|
+
/**
|
|
380
|
+
* @swagger
|
|
381
|
+
* /api/conversations/{uuid}:
|
|
382
|
+
* delete:
|
|
383
|
+
* summary: Delete conversation
|
|
384
|
+
* description: Delete a specific conversation by its UUID
|
|
385
|
+
* tags: [Chat]
|
|
386
|
+
* security:
|
|
387
|
+
* - bearerAuth: []
|
|
388
|
+
* parameters:
|
|
389
|
+
* - in: path
|
|
390
|
+
* name: uuid
|
|
391
|
+
* required: true
|
|
392
|
+
* schema:
|
|
393
|
+
* type: string
|
|
394
|
+
* description: Conversation UUID
|
|
395
|
+
* responses:
|
|
396
|
+
* 200:
|
|
397
|
+
* description: Conversation deleted successfully
|
|
398
|
+
* 404:
|
|
399
|
+
* description: Conversation not found
|
|
400
|
+
* 401:
|
|
401
|
+
* description: Not authenticated
|
|
402
|
+
* 500:
|
|
403
|
+
* description: Internal server error
|
|
404
|
+
*/
|
|
405
|
+
app.delete('/api/conversations/:uuid', (0, auth_1.authenticateUser)(supabase), (req, res) => chatController.deleteConversation(req, res));
|
|
406
|
+
/**
|
|
407
|
+
* @swagger
|
|
408
|
+
* /api/conversations/{uuid}/messages:
|
|
409
|
+
* post:
|
|
410
|
+
* summary: Send a message
|
|
411
|
+
* description: Send a user message to a conversation (Public API - No authentication required)
|
|
412
|
+
* tags: [Chat]
|
|
413
|
+
* parameters:
|
|
414
|
+
* - in: path
|
|
415
|
+
* name: uuid
|
|
416
|
+
* required: true
|
|
417
|
+
* schema:
|
|
418
|
+
* type: string
|
|
419
|
+
* description: Conversation UUID
|
|
420
|
+
* requestBody:
|
|
421
|
+
* required: true
|
|
422
|
+
* content:
|
|
423
|
+
* application/json:
|
|
424
|
+
* schema:
|
|
425
|
+
* $ref: '#/components/schemas/CreateMessageRequest'
|
|
426
|
+
* responses:
|
|
427
|
+
* 201:
|
|
428
|
+
* description: Message sent successfully
|
|
429
|
+
* content:
|
|
430
|
+
* application/json:
|
|
431
|
+
* schema:
|
|
432
|
+
* $ref: '#/components/schemas/SendMessageResponse'
|
|
433
|
+
* 400:
|
|
434
|
+
* description: Invalid request
|
|
435
|
+
* 404:
|
|
436
|
+
* description: Conversation not found
|
|
437
|
+
* 500:
|
|
438
|
+
* description: Internal server error
|
|
439
|
+
*/
|
|
440
|
+
app.post('/api/conversations/:uuid/messages', (req, res) => chatController.createUserMessage(req, res));
|
|
441
|
+
/**
|
|
442
|
+
* @swagger
|
|
443
|
+
* /api/messages/{uuid}/generate:
|
|
444
|
+
* post:
|
|
445
|
+
* summary: Generate AI response
|
|
446
|
+
* description: Generate an AI response for a specific message (Public API - No authentication required)
|
|
447
|
+
* tags: [Chat]
|
|
448
|
+
* parameters:
|
|
449
|
+
* - in: path
|
|
450
|
+
* name: uuid
|
|
451
|
+
* required: true
|
|
452
|
+
* schema:
|
|
453
|
+
* type: string
|
|
454
|
+
* description: Message UUID
|
|
455
|
+
* responses:
|
|
456
|
+
* 200:
|
|
457
|
+
* description: AI response generated successfully
|
|
458
|
+
* content:
|
|
459
|
+
* application/json:
|
|
460
|
+
* schema:
|
|
461
|
+
* $ref: '#/components/schemas/GenerateResponseResponse'
|
|
462
|
+
* 400:
|
|
463
|
+
* description: Invalid request
|
|
464
|
+
* 404:
|
|
465
|
+
* description: Message not found
|
|
466
|
+
* 500:
|
|
467
|
+
* description: Internal server error
|
|
468
|
+
*/
|
|
469
|
+
app.post('/api/messages/:uuid/generate', (req, res) => chatController.generateResponse(req, res));
|
|
470
|
+
/**
|
|
471
|
+
* @swagger
|
|
472
|
+
* /api/conversations:
|
|
473
|
+
* get:
|
|
474
|
+
* summary: Get user conversations
|
|
475
|
+
* description: Get all conversations for the authenticated user
|
|
476
|
+
* tags: [Chat]
|
|
477
|
+
* security:
|
|
478
|
+
* - bearerAuth: []
|
|
479
|
+
* responses:
|
|
480
|
+
* 200:
|
|
481
|
+
* description: Conversations retrieved successfully
|
|
482
|
+
* content:
|
|
483
|
+
* application/json:
|
|
484
|
+
* schema:
|
|
485
|
+
* $ref: '#/components/schemas/ConversationListResponse'
|
|
486
|
+
* 401:
|
|
487
|
+
* description: Not authenticated
|
|
488
|
+
* 500:
|
|
489
|
+
* description: Internal server error
|
|
490
|
+
*/
|
|
491
|
+
app.get('/api/conversations', (0, auth_1.authenticateUser)(supabase), (req, res) => chatController.getUserConversations(req, res));
|
|
492
|
+
/**
|
|
493
|
+
* @swagger
|
|
494
|
+
* /api/feedback:
|
|
495
|
+
* post:
|
|
496
|
+
* summary: Submit message feedback
|
|
497
|
+
* description: Submit feedback for a specific message
|
|
498
|
+
* tags: [Chat]
|
|
499
|
+
* security:
|
|
500
|
+
* - bearerAuth: []
|
|
501
|
+
* requestBody:
|
|
502
|
+
* required: true
|
|
503
|
+
* content:
|
|
504
|
+
* application/json:
|
|
505
|
+
* schema:
|
|
506
|
+
* $ref: '#/components/schemas/FeedbackRequest'
|
|
507
|
+
* responses:
|
|
508
|
+
* 200:
|
|
509
|
+
* description: Feedback submitted successfully
|
|
510
|
+
* content:
|
|
511
|
+
* application/json:
|
|
512
|
+
* schema:
|
|
513
|
+
* $ref: '#/components/schemas/SubmitFeedbackResponse'
|
|
514
|
+
* 400:
|
|
515
|
+
* description: Invalid request
|
|
516
|
+
* 401:
|
|
517
|
+
* description: Not authenticated
|
|
518
|
+
* 500:
|
|
519
|
+
* description: Internal server error
|
|
520
|
+
*/
|
|
521
|
+
app.post('/api/feedback', (0, auth_1.authenticateUser)(supabase), (req, res) => chatController.submitFeedback(req, res));
|
|
522
|
+
// Knowledge Base API Routes
|
|
523
|
+
/**
|
|
524
|
+
* @swagger
|
|
525
|
+
* /api/knowledge/items:
|
|
526
|
+
* post:
|
|
527
|
+
* summary: Create knowledge item
|
|
528
|
+
* description: Create a new knowledge base item. Can be authenticated with Bearer token or X-API-Key header.
|
|
529
|
+
* tags: [Knowledge Base]
|
|
530
|
+
* security:
|
|
531
|
+
* - bearerAuth: []
|
|
532
|
+
* - apiKeyAuth: []
|
|
533
|
+
* requestBody:
|
|
534
|
+
* required: true
|
|
535
|
+
* content:
|
|
536
|
+
* application/json:
|
|
537
|
+
* schema:
|
|
538
|
+
* $ref: '#/components/schemas/CreateKnowledgeItemRequest'
|
|
539
|
+
* responses:
|
|
540
|
+
* 201:
|
|
541
|
+
* description: Knowledge item created successfully
|
|
542
|
+
* content:
|
|
543
|
+
* application/json:
|
|
544
|
+
* schema:
|
|
545
|
+
* $ref: '#/components/schemas/CreateKnowledgeItemResponse'
|
|
546
|
+
* 400:
|
|
547
|
+
* description: Invalid request
|
|
548
|
+
* 401:
|
|
549
|
+
* description: Not authenticated
|
|
550
|
+
* 500:
|
|
551
|
+
* description: Internal server error
|
|
552
|
+
*/
|
|
553
|
+
app.post('/api/knowledge/items', (0, auth_1.authenticateUserOrApiKey)(supabase), (req, res) => knowledgeController.createItem(req, res));
|
|
554
|
+
/**
|
|
555
|
+
* @swagger
|
|
556
|
+
* /api/knowledge/items:
|
|
557
|
+
* get:
|
|
558
|
+
* summary: List knowledge items
|
|
559
|
+
* description: Get all knowledge base items for the authenticated user's company
|
|
560
|
+
* tags: [Knowledge Base]
|
|
561
|
+
* security:
|
|
562
|
+
* - bearerAuth: []
|
|
563
|
+
* parameters:
|
|
564
|
+
* - in: query
|
|
565
|
+
* name: limit
|
|
566
|
+
* schema:
|
|
567
|
+
* type: integer
|
|
568
|
+
* default: 10
|
|
569
|
+
* description: Maximum number of items to return
|
|
570
|
+
* - in: query
|
|
571
|
+
* name: offset
|
|
572
|
+
* schema:
|
|
573
|
+
* type: integer
|
|
574
|
+
* default: 0
|
|
575
|
+
* description: Number of items to skip
|
|
576
|
+
* responses:
|
|
577
|
+
* 200:
|
|
578
|
+
* description: Knowledge items retrieved successfully
|
|
579
|
+
* content:
|
|
580
|
+
* application/json:
|
|
581
|
+
* schema:
|
|
582
|
+
* $ref: '#/components/schemas/KnowledgeItemListResponse'
|
|
583
|
+
* 401:
|
|
584
|
+
* description: Not authenticated
|
|
585
|
+
* 500:
|
|
586
|
+
* description: Internal server error
|
|
587
|
+
*/
|
|
588
|
+
app.get('/api/knowledge/items', (0, auth_1.authenticateUser)(supabase), (req, res) => knowledgeController.listItems(req, res));
|
|
589
|
+
/**
|
|
590
|
+
* @swagger
|
|
591
|
+
* /api/knowledge/search:
|
|
592
|
+
* post:
|
|
593
|
+
* summary: Search knowledge base
|
|
594
|
+
* description: Search the knowledge base for relevant items. Can be authenticated with Bearer token or X-API-Key header.
|
|
595
|
+
* tags: [Search]
|
|
596
|
+
* security:
|
|
597
|
+
* - bearerAuth: []
|
|
598
|
+
* - apiKeyAuth: []
|
|
599
|
+
* requestBody:
|
|
600
|
+
* required: true
|
|
601
|
+
* content:
|
|
602
|
+
* application/json:
|
|
603
|
+
* schema:
|
|
604
|
+
* $ref: '#/components/schemas/KnowledgeSearchRequest'
|
|
605
|
+
* responses:
|
|
606
|
+
* 200:
|
|
607
|
+
* description: Search completed successfully
|
|
608
|
+
* content:
|
|
609
|
+
* application/json:
|
|
610
|
+
* schema:
|
|
611
|
+
* $ref: '#/components/schemas/SearchKnowledgeResponse'
|
|
612
|
+
* 400:
|
|
613
|
+
* description: Invalid request
|
|
614
|
+
* 401:
|
|
615
|
+
* description: Not authenticated
|
|
616
|
+
* 500:
|
|
617
|
+
* description: Internal server error
|
|
618
|
+
*/
|
|
619
|
+
app.post('/api/knowledge/search', (0, auth_1.authenticateUserOrApiKey)(supabase), (req, res) => knowledgeController.search(req, res));
|
|
620
|
+
/**
|
|
621
|
+
* @swagger
|
|
622
|
+
* /api/search:
|
|
623
|
+
* post:
|
|
624
|
+
* summary: RAG search
|
|
625
|
+
* description: Perform Retrieval-Augmented Generation search. Can be authenticated with Bearer token or X-API-Key header.
|
|
626
|
+
* tags: [Search]
|
|
627
|
+
* security:
|
|
628
|
+
* - bearerAuth: []
|
|
629
|
+
* - apiKeyAuth: []
|
|
630
|
+
* requestBody:
|
|
631
|
+
* required: true
|
|
632
|
+
* content:
|
|
633
|
+
* application/json:
|
|
634
|
+
* schema:
|
|
635
|
+
* $ref: '#/components/schemas/RAGSearchRequest'
|
|
636
|
+
* responses:
|
|
637
|
+
* 200:
|
|
638
|
+
* description: RAG search completed successfully
|
|
639
|
+
* content:
|
|
640
|
+
* application/json:
|
|
641
|
+
* schema:
|
|
642
|
+
* $ref: '#/components/schemas/RAGSearchResponse'
|
|
643
|
+
* 400:
|
|
644
|
+
* description: Invalid request
|
|
645
|
+
* 401:
|
|
646
|
+
* description: Not authenticated
|
|
647
|
+
* 500:
|
|
648
|
+
* description: Internal server error
|
|
649
|
+
*/
|
|
650
|
+
app.post('/api/search', (0, auth_1.authenticateUserOrApiKey)(supabase), (req, res) => knowledgeController.ragSearch(req, res));
|
|
651
|
+
/**
|
|
652
|
+
* @swagger
|
|
653
|
+
* /api/knowledge/items/{uuid}:
|
|
654
|
+
* get:
|
|
655
|
+
* summary: Get knowledge item
|
|
656
|
+
* description: Get a specific knowledge base item by UUID
|
|
657
|
+
* tags: [Knowledge Base]
|
|
658
|
+
* security:
|
|
659
|
+
* - bearerAuth: []
|
|
660
|
+
* parameters:
|
|
661
|
+
* - in: path
|
|
662
|
+
* name: uuid
|
|
663
|
+
* required: true
|
|
664
|
+
* schema:
|
|
665
|
+
* type: string
|
|
666
|
+
* description: Knowledge item UUID
|
|
667
|
+
* responses:
|
|
668
|
+
* 200:
|
|
669
|
+
* description: Knowledge item retrieved successfully
|
|
670
|
+
* content:
|
|
671
|
+
* application/json:
|
|
672
|
+
* schema:
|
|
673
|
+
* $ref: '#/components/schemas/GetKnowledgeItemResponse'
|
|
674
|
+
* 404:
|
|
675
|
+
* description: Knowledge item not found
|
|
676
|
+
* 401:
|
|
677
|
+
* description: Not authenticated
|
|
678
|
+
* 500:
|
|
679
|
+
* description: Internal server error
|
|
680
|
+
*/
|
|
681
|
+
app.get('/api/knowledge/items/:uuid', (0, auth_1.authenticateUser)(supabase), (req, res) => knowledgeController.getItem(req, res));
|
|
682
|
+
/**
|
|
683
|
+
* @swagger
|
|
684
|
+
* /api/knowledge/items/{uuid}:
|
|
685
|
+
* put:
|
|
686
|
+
* summary: Update knowledge item
|
|
687
|
+
* description: Update a specific knowledge base item
|
|
688
|
+
* tags: [Knowledge Base]
|
|
689
|
+
* security:
|
|
690
|
+
* - bearerAuth: []
|
|
691
|
+
* parameters:
|
|
692
|
+
* - in: path
|
|
693
|
+
* name: uuid
|
|
694
|
+
* required: true
|
|
695
|
+
* schema:
|
|
696
|
+
* type: string
|
|
697
|
+
* description: Knowledge item UUID
|
|
698
|
+
* requestBody:
|
|
699
|
+
* required: true
|
|
700
|
+
* content:
|
|
701
|
+
* application/json:
|
|
702
|
+
* schema:
|
|
703
|
+
* $ref: '#/components/schemas/UpdateKnowledgeItemRequest'
|
|
704
|
+
* responses:
|
|
705
|
+
* 200:
|
|
706
|
+
* description: Knowledge item updated successfully
|
|
707
|
+
* content:
|
|
708
|
+
* application/json:
|
|
709
|
+
* schema:
|
|
710
|
+
* $ref: '#/components/schemas/UpdateKnowledgeItemResponse'
|
|
711
|
+
* 400:
|
|
712
|
+
* description: Invalid request
|
|
713
|
+
* 401:
|
|
714
|
+
* description: Not authenticated
|
|
715
|
+
* 404:
|
|
716
|
+
* description: Knowledge item not found
|
|
717
|
+
* 500:
|
|
718
|
+
* description: Internal server error
|
|
719
|
+
*/
|
|
720
|
+
app.put('/api/knowledge/items/:uuid', (0, auth_1.authenticateUser)(supabase), (req, res) => knowledgeController.updateItem(req, res));
|
|
721
|
+
/**
|
|
722
|
+
* @swagger
|
|
723
|
+
* /api/knowledge/items/{uuid}:
|
|
724
|
+
* delete:
|
|
725
|
+
* summary: Delete knowledge item
|
|
726
|
+
* description: Delete a specific knowledge base item
|
|
727
|
+
* tags: [Knowledge Base]
|
|
728
|
+
* security:
|
|
729
|
+
* - bearerAuth: []
|
|
730
|
+
* parameters:
|
|
731
|
+
* - in: path
|
|
732
|
+
* name: uuid
|
|
733
|
+
* required: true
|
|
734
|
+
* schema:
|
|
735
|
+
* type: string
|
|
736
|
+
* description: Knowledge item UUID
|
|
737
|
+
* responses:
|
|
738
|
+
* 200:
|
|
739
|
+
* description: Knowledge item deleted successfully
|
|
740
|
+
* 404:
|
|
741
|
+
* description: Knowledge item not found
|
|
742
|
+
* 401:
|
|
743
|
+
* description: Not authenticated
|
|
744
|
+
* 500:
|
|
745
|
+
* description: Internal server error
|
|
746
|
+
*/
|
|
747
|
+
app.delete('/api/knowledge/items/:uuid', (0, auth_1.authenticateUser)(supabase), (req, res) => knowledgeController.deleteItem(req, res));
|
|
748
|
+
// ============================================================================
|
|
749
|
+
// MIGRATION ENDPOINTS
|
|
750
|
+
// ============================================================================
|
|
751
|
+
/**
|
|
752
|
+
* @swagger
|
|
753
|
+
* /api/migrate:
|
|
754
|
+
* get:
|
|
755
|
+
* summary: Run database migrations
|
|
756
|
+
* description: Run pending database migrations. Requires migration secret key.
|
|
757
|
+
* tags: [System]
|
|
758
|
+
* parameters:
|
|
759
|
+
* - in: query
|
|
760
|
+
* name: key
|
|
761
|
+
* required: true
|
|
762
|
+
* schema:
|
|
763
|
+
* type: string
|
|
764
|
+
* description: Migration secret key from MIGRATION_SECRET_KEY environment variable
|
|
765
|
+
* security:
|
|
766
|
+
* - migrationKey: []
|
|
767
|
+
* responses:
|
|
768
|
+
* 200:
|
|
769
|
+
* description: Migrations completed successfully
|
|
770
|
+
* content:
|
|
771
|
+
* application/json:
|
|
772
|
+
* schema:
|
|
773
|
+
* type: object
|
|
774
|
+
* properties:
|
|
775
|
+
* success:
|
|
776
|
+
* type: boolean
|
|
777
|
+
* example: true
|
|
778
|
+
* message:
|
|
779
|
+
* type: string
|
|
780
|
+
* example: "Migrations completed successfully"
|
|
781
|
+
* currentVersion:
|
|
782
|
+
* type: string
|
|
783
|
+
* example: "002_multitenancy_schema.ts"
|
|
784
|
+
* previousVersion:
|
|
785
|
+
* type: string
|
|
786
|
+
* example: "001_initial_schema.ts"
|
|
787
|
+
* details:
|
|
788
|
+
* type: object
|
|
789
|
+
* properties:
|
|
790
|
+
* timestamp:
|
|
791
|
+
* type: string
|
|
792
|
+
* format: date-time
|
|
793
|
+
* 400:
|
|
794
|
+
* description: Missing API key or invalid configuration
|
|
795
|
+
* content:
|
|
796
|
+
* application/json:
|
|
797
|
+
* schema:
|
|
798
|
+
* type: object
|
|
799
|
+
* properties:
|
|
800
|
+
* success:
|
|
801
|
+
* type: boolean
|
|
802
|
+
* example: false
|
|
803
|
+
* message:
|
|
804
|
+
* type: string
|
|
805
|
+
* example: "Migration API key is required"
|
|
806
|
+
* error:
|
|
807
|
+
* type: string
|
|
808
|
+
* example: "MISSING_API_KEY"
|
|
809
|
+
* 401:
|
|
810
|
+
* description: Unauthorized - Invalid migration key
|
|
811
|
+
* content:
|
|
812
|
+
* application/json:
|
|
813
|
+
* schema:
|
|
814
|
+
* type: object
|
|
815
|
+
* properties:
|
|
816
|
+
* success:
|
|
817
|
+
* type: boolean
|
|
818
|
+
* example: false
|
|
819
|
+
* message:
|
|
820
|
+
* type: string
|
|
821
|
+
* example: "Invalid or missing migration API key"
|
|
822
|
+
* error:
|
|
823
|
+
* type: string
|
|
824
|
+
* example: "UNAUTHORIZED"
|
|
825
|
+
* 500:
|
|
826
|
+
* description: Migration failed
|
|
827
|
+
*/
|
|
828
|
+
app.get('/api/migrate', (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
|
829
|
+
const apiKey = req.query.key || req.headers['x-migration-key'];
|
|
830
|
+
// Import MigrationService to use the proper validation
|
|
831
|
+
const { MigrationService } = await Promise.resolve().then(() => __importStar(require('./services/MigrationService')));
|
|
832
|
+
const result = await MigrationService.runMigrations(apiKey);
|
|
833
|
+
const statusCode = result.success ? 200 :
|
|
834
|
+
result.error === 'UNAUTHORIZED' ? 401 :
|
|
835
|
+
result.error === 'MISSING_ENV_VARS' || result.error === 'DATABASE_CONNECTION_FAILED' ? 400 : 500;
|
|
836
|
+
res.status(statusCode).json(result);
|
|
837
|
+
}));
|
|
838
|
+
/**
|
|
839
|
+
* @swagger
|
|
840
|
+
* /api/migrate/status:
|
|
841
|
+
* get:
|
|
842
|
+
* summary: Get migration status
|
|
843
|
+
* description: Get the current status of database migrations. Requires migration secret key.
|
|
844
|
+
* tags: [System]
|
|
845
|
+
* parameters:
|
|
846
|
+
* - in: query
|
|
847
|
+
* name: key
|
|
848
|
+
* required: true
|
|
849
|
+
* schema:
|
|
850
|
+
* type: string
|
|
851
|
+
* description: Migration secret key from MIGRATION_SECRET_KEY environment variable
|
|
852
|
+
* security:
|
|
853
|
+
* - migrationKey: []
|
|
854
|
+
* responses:
|
|
855
|
+
* 200:
|
|
856
|
+
* description: Migration status retrieved
|
|
857
|
+
* content:
|
|
858
|
+
* application/json:
|
|
859
|
+
* schema:
|
|
860
|
+
* type: object
|
|
861
|
+
* properties:
|
|
862
|
+
* success:
|
|
863
|
+
* type: boolean
|
|
864
|
+
* example: true
|
|
865
|
+
* status:
|
|
866
|
+
* type: string
|
|
867
|
+
* example: "completed"
|
|
868
|
+
* data:
|
|
869
|
+
* type: object
|
|
870
|
+
* properties:
|
|
871
|
+
* currentVersion:
|
|
872
|
+
* type: string
|
|
873
|
+
* example: "002_multitenancy_schema.ts"
|
|
874
|
+
* timestamp:
|
|
875
|
+
* type: string
|
|
876
|
+
* format: date-time
|
|
877
|
+
* 400:
|
|
878
|
+
* description: Missing API key
|
|
879
|
+
* content:
|
|
880
|
+
* application/json:
|
|
881
|
+
* schema:
|
|
882
|
+
* type: object
|
|
883
|
+
* properties:
|
|
884
|
+
* success:
|
|
885
|
+
* type: boolean
|
|
886
|
+
* example: false
|
|
887
|
+
* message:
|
|
888
|
+
* type: string
|
|
889
|
+
* example: "Migration API key is required"
|
|
890
|
+
* error:
|
|
891
|
+
* type: string
|
|
892
|
+
* example: "MISSING_API_KEY"
|
|
893
|
+
* 401:
|
|
894
|
+
* description: Unauthorized - Invalid migration key
|
|
895
|
+
* content:
|
|
896
|
+
* application/json:
|
|
897
|
+
* schema:
|
|
898
|
+
* type: object
|
|
899
|
+
* properties:
|
|
900
|
+
* success:
|
|
901
|
+
* type: boolean
|
|
902
|
+
* example: false
|
|
903
|
+
* message:
|
|
904
|
+
* type: string
|
|
905
|
+
* example: "Invalid or missing migration API key"
|
|
906
|
+
* error:
|
|
907
|
+
* type: string
|
|
908
|
+
* example: "UNAUTHORIZED"
|
|
909
|
+
* 500:
|
|
910
|
+
* description: Failed to get migration status
|
|
911
|
+
*/
|
|
912
|
+
app.get('/api/migrate/status', (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
|
913
|
+
const apiKey = req.query.key || req.headers['x-migration-key'];
|
|
914
|
+
// Import MigrationService to use the proper validation
|
|
915
|
+
const { MigrationService } = await Promise.resolve().then(() => __importStar(require('./services/MigrationService')));
|
|
916
|
+
const result = await MigrationService.getStatus(apiKey);
|
|
917
|
+
const statusCode = result.success ? 200 :
|
|
918
|
+
result.error === 'UNAUTHORIZED' ? 401 : 500;
|
|
919
|
+
res.status(statusCode).json(result);
|
|
920
|
+
}));
|
|
921
|
+
/**
|
|
922
|
+
* @swagger
|
|
923
|
+
* /api/seed-default:
|
|
924
|
+
* post:
|
|
925
|
+
* summary: Seed default data
|
|
926
|
+
* description: Creates default company and admin user for initial setup. Uses environment variables for configuration. Returns existing data if already created.
|
|
927
|
+
* tags: [System]
|
|
928
|
+
* parameters:
|
|
929
|
+
* - in: query
|
|
930
|
+
* name: key
|
|
931
|
+
* required: true
|
|
932
|
+
* schema:
|
|
933
|
+
* type: string
|
|
934
|
+
* description: Migration secret key from MIGRATION_SECRET_KEY environment variable
|
|
935
|
+
* security:
|
|
936
|
+
* - migrationKey: []
|
|
937
|
+
* responses:
|
|
938
|
+
* 200:
|
|
939
|
+
* description: Default data seeded successfully or already exists
|
|
940
|
+
* content:
|
|
941
|
+
* application/json:
|
|
942
|
+
* schema:
|
|
943
|
+
* type: object
|
|
944
|
+
* properties:
|
|
945
|
+
* company_name:
|
|
946
|
+
* type: string
|
|
947
|
+
* example: "Vezlo"
|
|
948
|
+
* email:
|
|
949
|
+
* type: string
|
|
950
|
+
* example: "admin@vezlo.org"
|
|
951
|
+
* password:
|
|
952
|
+
* type: string
|
|
953
|
+
* example: "admin123"
|
|
954
|
+
* admin_name:
|
|
955
|
+
* type: string
|
|
956
|
+
* example: "Default Admin"
|
|
957
|
+
* 400:
|
|
958
|
+
* description: Missing API key or invalid request
|
|
959
|
+
* content:
|
|
960
|
+
* application/json:
|
|
961
|
+
* schema:
|
|
962
|
+
* type: object
|
|
963
|
+
* properties:
|
|
964
|
+
* success:
|
|
965
|
+
* type: boolean
|
|
966
|
+
* example: false
|
|
967
|
+
* message:
|
|
968
|
+
* type: string
|
|
969
|
+
* error:
|
|
970
|
+
* type: string
|
|
971
|
+
* 401:
|
|
972
|
+
* description: Unauthorized - Invalid migration key
|
|
973
|
+
* content:
|
|
974
|
+
* application/json:
|
|
975
|
+
* schema:
|
|
976
|
+
* type: object
|
|
977
|
+
* properties:
|
|
978
|
+
* success:
|
|
979
|
+
* type: boolean
|
|
980
|
+
* example: false
|
|
981
|
+
* message:
|
|
982
|
+
* type: string
|
|
983
|
+
* example: "Invalid or missing migration API key"
|
|
984
|
+
* error:
|
|
985
|
+
* type: string
|
|
986
|
+
* example: "UNAUTHORIZED"
|
|
987
|
+
* 500:
|
|
988
|
+
* description: Failed to seed default data
|
|
989
|
+
*/
|
|
990
|
+
app.post('/api/seed-default', (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
|
991
|
+
// Extract API key from query or header
|
|
992
|
+
const apiKey = req.query.key || req.headers['x-migration-key'];
|
|
690
993
|
try {
|
|
691
|
-
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
994
|
+
// Validate API key
|
|
995
|
+
const { MigrationService } = await Promise.resolve().then(() => __importStar(require('./services/MigrationService')));
|
|
996
|
+
const keyValid = MigrationService.validateApiKey(apiKey);
|
|
997
|
+
if (!keyValid) {
|
|
998
|
+
res.status(401).json({
|
|
999
|
+
success: false,
|
|
1000
|
+
message: 'Invalid or missing migration API key',
|
|
1001
|
+
error: 'UNAUTHORIZED'
|
|
1002
|
+
});
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
// Initialize Supabase
|
|
1006
|
+
const supabase = (0, supabase_js_1.createClient)(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY);
|
|
1007
|
+
// Execute seed using SetupService
|
|
1008
|
+
const { SetupService } = await Promise.resolve().then(() => __importStar(require('./services/SetupService')));
|
|
1009
|
+
const setupService = new SetupService(supabase);
|
|
1010
|
+
const response = await setupService.executeSeedDefault();
|
|
1011
|
+
res.status(200).json(response);
|
|
1012
|
+
}
|
|
1013
|
+
catch (error) {
|
|
1014
|
+
logger_1.default.error('Seed default failed:', error);
|
|
1015
|
+
res.status(500).json({
|
|
1016
|
+
success: false,
|
|
1017
|
+
message: 'Failed to seed default data',
|
|
1018
|
+
error: error.message || 'SEED_DEFAULT_FAILED',
|
|
1019
|
+
details: {
|
|
1020
|
+
error: error.message
|
|
1021
|
+
}
|
|
699
1022
|
});
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
1023
|
+
}
|
|
1024
|
+
}));
|
|
1025
|
+
/**
|
|
1026
|
+
* @swagger
|
|
1027
|
+
* /api/generate-key:
|
|
1028
|
+
* post:
|
|
1029
|
+
* summary: Generate API key for the default admin
|
|
1030
|
+
* description: Generates an API key for the default admin user's company
|
|
1031
|
+
* tags: [System]
|
|
1032
|
+
* security:
|
|
1033
|
+
* - migrationKey: []
|
|
1034
|
+
* parameters:
|
|
1035
|
+
* - in: query
|
|
1036
|
+
* name: key
|
|
1037
|
+
* description: Migration secret key
|
|
1038
|
+
* required: true
|
|
1039
|
+
* schema:
|
|
1040
|
+
* type: string
|
|
1041
|
+
* responses:
|
|
1042
|
+
* 200:
|
|
1043
|
+
* description: API key generated successfully
|
|
1044
|
+
* content:
|
|
1045
|
+
* application/json:
|
|
1046
|
+
* schema:
|
|
1047
|
+
* type: object
|
|
1048
|
+
* properties:
|
|
1049
|
+
* success:
|
|
1050
|
+
* type: boolean
|
|
1051
|
+
* example: true
|
|
1052
|
+
* message:
|
|
1053
|
+
* type: string
|
|
1054
|
+
* example: "API key generated successfully"
|
|
1055
|
+
* api_key_details:
|
|
1056
|
+
* type: object
|
|
1057
|
+
* properties:
|
|
1058
|
+
* company_name:
|
|
1059
|
+
* type: string
|
|
1060
|
+
* example: "Vezlo"
|
|
1061
|
+
* user_name:
|
|
1062
|
+
* type: string
|
|
1063
|
+
* example: "Admin User"
|
|
1064
|
+
* api_key:
|
|
1065
|
+
* type: string
|
|
1066
|
+
* example: "v.bzkO2h7Ga.c5MGe0zX-2CU-IeZPqreT6xSRCgq3Tw"
|
|
1067
|
+
* 401:
|
|
1068
|
+
* description: Unauthorized
|
|
1069
|
+
* content:
|
|
1070
|
+
* application/json:
|
|
1071
|
+
* schema:
|
|
1072
|
+
* type: object
|
|
1073
|
+
* properties:
|
|
1074
|
+
* success:
|
|
1075
|
+
* type: boolean
|
|
1076
|
+
* example: false
|
|
1077
|
+
* message:
|
|
1078
|
+
* type: string
|
|
1079
|
+
* example: "Invalid or missing migration API key"
|
|
1080
|
+
* error:
|
|
1081
|
+
* type: string
|
|
1082
|
+
* example: "UNAUTHORIZED"
|
|
1083
|
+
* 500:
|
|
1084
|
+
* description: Failed to generate API key
|
|
1085
|
+
*/
|
|
1086
|
+
app.post('/api/generate-key', (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
|
1087
|
+
// Extract API key from query or header
|
|
1088
|
+
const apiKey = req.query.key || req.headers['x-migration-key'];
|
|
1089
|
+
try {
|
|
1090
|
+
// Validate API key
|
|
1091
|
+
const { MigrationService } = await Promise.resolve().then(() => __importStar(require('./services/MigrationService')));
|
|
1092
|
+
const keyValid = MigrationService.validateApiKey(apiKey);
|
|
1093
|
+
if (!keyValid) {
|
|
1094
|
+
res.status(401).json({
|
|
1095
|
+
success: false,
|
|
1096
|
+
message: 'Invalid or missing migration API key',
|
|
1097
|
+
error: 'UNAUTHORIZED'
|
|
706
1098
|
});
|
|
1099
|
+
return;
|
|
707
1100
|
}
|
|
1101
|
+
// Initialize Supabase
|
|
1102
|
+
const supabase = (0, supabase_js_1.createClient)(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY);
|
|
1103
|
+
// Execute generate-key using SetupService
|
|
1104
|
+
const { SetupService } = await Promise.resolve().then(() => __importStar(require('./services/SetupService')));
|
|
1105
|
+
const setupService = new SetupService(supabase);
|
|
1106
|
+
const response = await setupService.executeGenerateKey();
|
|
1107
|
+
res.status(200).json({
|
|
1108
|
+
success: true,
|
|
1109
|
+
message: 'API key generated successfully',
|
|
1110
|
+
api_key_details: response
|
|
1111
|
+
});
|
|
708
1112
|
}
|
|
709
1113
|
catch (error) {
|
|
710
|
-
|
|
711
|
-
|
|
1114
|
+
logger_1.default.error('Generate key failed:', error);
|
|
1115
|
+
res.status(500).json({
|
|
1116
|
+
success: false,
|
|
1117
|
+
message: 'Failed to generate API key',
|
|
1118
|
+
error: error.message || 'GENERATE_KEY_FAILED',
|
|
1119
|
+
details: {
|
|
1120
|
+
error: error.message
|
|
1121
|
+
}
|
|
712
1122
|
});
|
|
713
1123
|
}
|
|
1124
|
+
}));
|
|
1125
|
+
// Error handling middleware (must be after all routes)
|
|
1126
|
+
app.use(errorHandler_1.errorHandler);
|
|
1127
|
+
app.use(errorHandler_1.notFoundHandler);
|
|
1128
|
+
logger_1.default.info('Routes setup completed');
|
|
1129
|
+
}
|
|
1130
|
+
// WebSocket connection handling
|
|
1131
|
+
io.on('connection', (socket) => {
|
|
1132
|
+
logger_1.default.info(`Client connected: ${socket.id}`);
|
|
1133
|
+
socket.on('join_conversation', (conversationId) => {
|
|
1134
|
+
socket.join(`conversation_${conversationId}`);
|
|
1135
|
+
logger_1.default.info(`Client ${socket.id} joined conversation ${conversationId}`);
|
|
1136
|
+
});
|
|
1137
|
+
socket.on('leave_conversation', (conversationId) => {
|
|
1138
|
+
socket.leave(`conversation_${conversationId}`);
|
|
1139
|
+
logger_1.default.info(`Client ${socket.id} left conversation ${conversationId}`);
|
|
714
1140
|
});
|
|
715
1141
|
socket.on('disconnect', () => {
|
|
716
1142
|
logger_1.default.info(`Client disconnected: ${socket.id}`);
|
|
717
1143
|
});
|
|
718
1144
|
});
|
|
719
|
-
// Error handling middleware
|
|
720
|
-
// Global error handling middleware
|
|
721
|
-
app.use(errorHandler_1.errorHandler);
|
|
722
|
-
// 404 handler for undefined routes
|
|
723
|
-
app.use(errorHandler_1.notFoundHandler);
|
|
724
1145
|
// Start server
|
|
725
|
-
const PORT =
|
|
1146
|
+
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3002;
|
|
726
1147
|
async function start() {
|
|
727
1148
|
try {
|
|
728
1149
|
// Validate configuration
|
|
729
|
-
|
|
1150
|
+
validateConfig();
|
|
730
1151
|
await initializeServices();
|
|
1152
|
+
// Setup routes after services are initialized
|
|
1153
|
+
setupRoutes();
|
|
731
1154
|
server.listen(PORT, '0.0.0.0', () => {
|
|
732
|
-
logger_1.default.info(`🚀
|
|
733
|
-
logger_1.default.info(`📊 Environment: ${
|
|
734
|
-
logger_1.default.info(`🌐 API available at http://localhost:${PORT}
|
|
735
|
-
|
|
736
|
-
logger_1.default.info(`📚 API Documentation: http://localhost:${PORT}${global_1.config.swagger.path}`);
|
|
737
|
-
}
|
|
1155
|
+
logger_1.default.info(`🚀 AI Assistant API v1.0.0 running on port ${PORT}`);
|
|
1156
|
+
logger_1.default.info(`📊 Environment: ${process.env.NODE_ENV || 'development'}`);
|
|
1157
|
+
logger_1.default.info(`🌐 API available at http://localhost:${PORT}/api`);
|
|
1158
|
+
logger_1.default.info(`📚 API Documentation: http://localhost:${PORT}/docs`);
|
|
738
1159
|
logger_1.default.info(`🔌 WebSocket available at ws://localhost:${PORT}`);
|
|
739
1160
|
logger_1.default.info(`💓 Health check: http://localhost:${PORT}/health`);
|
|
740
1161
|
});
|
|
@@ -744,16 +1165,27 @@ async function start() {
|
|
|
744
1165
|
process.exit(1);
|
|
745
1166
|
}
|
|
746
1167
|
}
|
|
1168
|
+
function validateConfig() {
|
|
1169
|
+
const requiredEnvVars = [
|
|
1170
|
+
'SUPABASE_URL',
|
|
1171
|
+
'SUPABASE_SERVICE_KEY',
|
|
1172
|
+
'OPENAI_API_KEY'
|
|
1173
|
+
];
|
|
1174
|
+
const missingVars = requiredEnvVars.filter(varName => !process.env[varName]);
|
|
1175
|
+
if (missingVars.length > 0) {
|
|
1176
|
+
throw new Error(`Missing required environment variables: ${missingVars.join(', ')}`);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
747
1179
|
// Graceful shutdown
|
|
748
|
-
process.on('
|
|
749
|
-
logger_1.default.info('
|
|
1180
|
+
process.on('SIGINT', async () => {
|
|
1181
|
+
logger_1.default.info('SIGINT received, shutting down gracefully...');
|
|
750
1182
|
server.close(() => {
|
|
751
1183
|
logger_1.default.info('HTTP server closed');
|
|
752
1184
|
process.exit(0);
|
|
753
1185
|
});
|
|
754
1186
|
});
|
|
755
|
-
process.on('
|
|
756
|
-
logger_1.default.info('
|
|
1187
|
+
process.on('SIGTERM', async () => {
|
|
1188
|
+
logger_1.default.info('SIGTERM received, shutting down gracefully...');
|
|
757
1189
|
server.close(() => {
|
|
758
1190
|
logger_1.default.info('HTTP server closed');
|
|
759
1191
|
process.exit(0);
|