@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.
Files changed (107) hide show
  1. package/README.md +56 -4
  2. package/database-schema.sql +193 -33
  3. package/dist/knexfile.d.ts.map +1 -1
  4. package/dist/knexfile.js +17 -8
  5. package/dist/knexfile.js.map +1 -1
  6. package/dist/src/config/database.d.ts.map +1 -1
  7. package/dist/src/config/database.js +9 -1
  8. package/dist/src/config/database.js.map +1 -1
  9. package/dist/src/config/knex.d.ts.map +1 -1
  10. package/dist/src/config/knex.js +22 -2
  11. package/dist/src/config/knex.js.map +1 -1
  12. package/dist/src/config/swagger.d.ts.map +1 -1
  13. package/dist/src/config/swagger.js +18 -71
  14. package/dist/src/config/swagger.js.map +1 -1
  15. package/dist/src/controllers/ApiKeyController.d.ts +17 -0
  16. package/dist/src/controllers/ApiKeyController.d.ts.map +1 -0
  17. package/dist/src/controllers/ApiKeyController.js +84 -0
  18. package/dist/src/controllers/ApiKeyController.js.map +1 -0
  19. package/dist/src/controllers/AuthController.d.ts +14 -0
  20. package/dist/src/controllers/AuthController.d.ts.map +1 -0
  21. package/dist/src/controllers/AuthController.js +212 -0
  22. package/dist/src/controllers/AuthController.js.map +1 -0
  23. package/dist/src/controllers/ChatController.d.ts +8 -5
  24. package/dist/src/controllers/ChatController.d.ts.map +1 -1
  25. package/dist/src/controllers/ChatController.js +139 -31
  26. package/dist/src/controllers/ChatController.js.map +1 -1
  27. package/dist/src/controllers/KnowledgeController.d.ts +5 -4
  28. package/dist/src/controllers/KnowledgeController.d.ts.map +1 -1
  29. package/dist/src/controllers/KnowledgeController.js +54 -16
  30. package/dist/src/controllers/KnowledgeController.js.map +1 -1
  31. package/dist/src/middleware/auth.d.ts +51 -0
  32. package/dist/src/middleware/auth.d.ts.map +1 -0
  33. package/dist/src/middleware/auth.js +232 -0
  34. package/dist/src/middleware/auth.js.map +1 -0
  35. package/dist/src/middleware/errorHandler.d.ts.map +1 -1
  36. package/dist/src/middleware/errorHandler.js +13 -19
  37. package/dist/src/middleware/errorHandler.js.map +1 -1
  38. package/dist/src/migrations/001_initial_schema.d.ts.map +1 -1
  39. package/dist/src/migrations/001_initial_schema.js +39 -64
  40. package/dist/src/migrations/001_initial_schema.js.map +1 -1
  41. package/dist/src/migrations/002_multitenancy_schema.d.ts +4 -0
  42. package/dist/src/migrations/002_multitenancy_schema.d.ts.map +1 -0
  43. package/dist/src/migrations/002_multitenancy_schema.js +119 -0
  44. package/dist/src/migrations/002_multitenancy_schema.js.map +1 -0
  45. package/dist/src/schemas/AuthSchemas.d.ts +89 -0
  46. package/dist/src/schemas/AuthSchemas.d.ts.map +1 -0
  47. package/dist/src/schemas/AuthSchemas.js +63 -0
  48. package/dist/src/schemas/AuthSchemas.js.map +1 -0
  49. package/dist/src/schemas/CommonSchemas.d.ts +62 -0
  50. package/dist/src/schemas/CommonSchemas.d.ts.map +1 -0
  51. package/dist/src/schemas/CommonSchemas.js +65 -0
  52. package/dist/src/schemas/CommonSchemas.js.map +1 -0
  53. package/dist/src/schemas/ConversationSchemas.d.ts +64 -27
  54. package/dist/src/schemas/ConversationSchemas.d.ts.map +1 -1
  55. package/dist/src/schemas/ConversationSchemas.js +28 -9
  56. package/dist/src/schemas/ConversationSchemas.js.map +1 -1
  57. package/dist/src/schemas/FeedbackSchemas.d.ts +43 -5
  58. package/dist/src/schemas/FeedbackSchemas.d.ts.map +1 -1
  59. package/dist/src/schemas/FeedbackSchemas.js +20 -2
  60. package/dist/src/schemas/FeedbackSchemas.js.map +1 -1
  61. package/dist/src/schemas/KnowledgeSchemas.d.ts +114 -35
  62. package/dist/src/schemas/KnowledgeSchemas.d.ts.map +1 -1
  63. package/dist/src/schemas/KnowledgeSchemas.js +58 -16
  64. package/dist/src/schemas/KnowledgeSchemas.js.map +1 -1
  65. package/dist/src/schemas/MessageSchemas.d.ts +57 -8
  66. package/dist/src/schemas/MessageSchemas.d.ts.map +1 -1
  67. package/dist/src/schemas/MessageSchemas.js +22 -3
  68. package/dist/src/schemas/MessageSchemas.js.map +1 -1
  69. package/dist/src/schemas/index.d.ts +410 -68
  70. package/dist/src/schemas/index.d.ts.map +1 -1
  71. package/dist/src/schemas/index.js +8 -2
  72. package/dist/src/schemas/index.js.map +1 -1
  73. package/dist/src/server.js +1047 -613
  74. package/dist/src/server.js.map +1 -1
  75. package/dist/src/services/AIService.d.ts +1 -1
  76. package/dist/src/services/AIService.d.ts.map +1 -1
  77. package/dist/src/services/AIService.js +6 -2
  78. package/dist/src/services/AIService.js.map +1 -1
  79. package/dist/src/services/ApiKeyService.d.ts +38 -0
  80. package/dist/src/services/ApiKeyService.d.ts.map +1 -0
  81. package/dist/src/services/ApiKeyService.js +123 -0
  82. package/dist/src/services/ApiKeyService.js.map +1 -0
  83. package/dist/src/services/KnowledgeBaseService.d.ts +2 -2
  84. package/dist/src/services/KnowledgeBaseService.d.ts.map +1 -1
  85. package/dist/src/services/KnowledgeBaseService.js +9 -2
  86. package/dist/src/services/KnowledgeBaseService.js.map +1 -1
  87. package/dist/src/services/MigrationService.d.ts +1 -1
  88. package/dist/src/services/MigrationService.d.ts.map +1 -1
  89. package/dist/src/services/MigrationService.js +4 -8
  90. package/dist/src/services/MigrationService.js.map +1 -1
  91. package/dist/src/services/SetupService.d.ts +102 -0
  92. package/dist/src/services/SetupService.d.ts.map +1 -0
  93. package/dist/src/services/SetupService.js +343 -0
  94. package/dist/src/services/SetupService.js.map +1 -0
  95. package/dist/src/storage/ConversationRepository.d.ts.map +1 -1
  96. package/dist/src/storage/ConversationRepository.js +42 -8
  97. package/dist/src/storage/ConversationRepository.js.map +1 -1
  98. package/dist/src/storage/MessageRepository.d.ts.map +1 -1
  99. package/dist/src/storage/MessageRepository.js +23 -27
  100. package/dist/src/storage/MessageRepository.js.map +1 -1
  101. package/env.example +5 -0
  102. package/knexfile.ts +17 -8
  103. package/package.json +10 -2
  104. package/scripts/generate-key.js +124 -0
  105. package/scripts/seed-default.js +72 -0
  106. package/scripts/setup.js +148 -13
  107. package/scripts/validate-db.js +22 -6
@@ -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 dotenv_1 = require("dotenv");
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
- // Import services
54
- const AIService_1 = require("./services/AIService");
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
- // Import controllers
59
- const ChatController_1 = require("./controllers/ChatController");
60
- const KnowledgeController_1 = require("./controllers/KnowledgeController");
61
- // Load environment variables
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: process.env.CORS_ORIGINS?.split(',') || '*',
69
- credentials: true
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 with global config
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 from this IP, please try again later.',
90
+ message: 'Too many requests, please try again later.',
89
91
  timestamp: new Date().toISOString()
90
92
  },
91
93
  success: false
@@ -94,46 +96,44 @@ const limiter = (0, express_rate_limit_1.default)({
94
96
  legacyHeaders: false
95
97
  });
96
98
  app.use('/api/', limiter);
97
- // Global services
98
- let aiService;
99
- let chatManager;
100
- let knowledgeBase;
101
- let storage;
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
- // Initialize Supabase
108
- const supabase = (0, database_1.initializeSupabase)();
109
- logger_1.default.info('Supabase client initialized');
110
- // Initialize storage with new repository pattern
111
- storage = new UnifiedStorage_1.UnifiedStorage(supabase, 'vezlo');
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 || 'Vezlo Assistant',
122
- model: process.env.AI_MODEL || 'gpt-4',
123
- temperature: parseFloat(process.env.AI_TEMPERATURE || '0.7'),
124
- maxTokens: parseInt(process.env.AI_MAX_TOKENS || '1000'),
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',
125
127
  knowledgeBaseService: knowledgeBase
126
128
  });
127
- // Initialize chat manager
128
- chatManager = new ChatManager_1.ChatManager({
129
- aiService,
130
- storage,
131
- enableConversationManagement: true,
132
- conversationTimeout: 3600000 // 1 hour
133
- });
129
+ const chatManager = new ChatManager_1.ChatManager({ aiService, storage });
134
130
  // Initialize controllers
135
- chatController = new ChatController_1.ChatController(chatManager, storage);
131
+ chatController = new ChatController_1.ChatController(chatManager, storage, supabase);
136
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);
137
137
  logger_1.default.info('All services initialized successfully');
138
138
  }
139
139
  catch (error) {
@@ -141,598 +141,1021 @@ async function initializeServices() {
141
141
  throw error;
142
142
  }
143
143
  }
144
- // Redirect root to docs
145
- app.get('/', (req, res) => {
146
- res.redirect('/docs');
147
- });
148
- // API Documentation
149
- app.use('/docs', swagger_1.swaggerUi.serve, swagger_1.swaggerUi.setup(swagger_1.specs, swagger_1.swaggerUiOptions));
150
- /**
151
- * @swagger
152
- * /health:
153
- * get:
154
- * summary: Health check endpoint
155
- * description: Check server and database connectivity status
156
- * tags: [Health]
157
- * responses:
158
- * 200:
159
- * description: Server is healthy
160
- * content:
161
- * application/json:
162
- * schema:
163
- * $ref: '#/components/schemas/HealthCheck'
164
- * 503:
165
- * description: Server is unhealthy
166
- * content:
167
- * application/json:
168
- * schema:
169
- * $ref: '#/components/schemas/Error'
170
- */
171
- app.get('/health', async (req, res) => {
172
- try {
173
- const healthChecks = {
174
- server: 'healthy',
175
- timestamp: new Date().toISOString()
176
- };
177
- // 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) => {
178
169
  try {
179
- const supabase = (0, database_1.getSupabaseClient)();
180
- const { data, error } = await supabase.from('vezlo_conversations').select('count').limit(1);
181
- if (!error) {
182
- healthChecks.supabase = 'connected';
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';
183
178
  }
184
- else {
185
- healthChecks.supabase = 'error';
179
+ catch (dbError) {
180
+ healthChecks.database = 'error';
186
181
  }
182
+ res.json(healthChecks);
187
183
  }
188
184
  catch (error) {
189
- healthChecks.supabase = 'disconnected';
185
+ res.status(503).json({
186
+ server: 'unhealthy',
187
+ timestamp: new Date().toISOString(),
188
+ error: error instanceof Error ? error.message : 'Unknown error'
189
+ });
190
190
  }
191
- res.json({
192
- status: 'healthy',
193
- checks: healthChecks
194
- });
195
- }
196
- catch (error) {
197
- res.status(503).json({
198
- status: 'unhealthy',
199
- error: error instanceof Error ? error.message : 'Unknown error'
200
- });
201
- }
202
- });
203
- // ============================================================================
204
- // CONVERSATION APIS (New 2-API Flow)
205
- // ============================================================================
206
- /**
207
- * @swagger
208
- * /api/conversations:
209
- * post:
210
- * summary: Create a new conversation
211
- * description: Create a new conversation for chat
212
- * tags: [Conversations]
213
- * requestBody:
214
- * required: true
215
- * content:
216
- * application/json:
217
- * schema:
218
- * $ref: '#/components/schemas/CreateConversationRequest'
219
- * responses:
220
- * 200:
221
- * description: Conversation created successfully
222
- * content:
223
- * application/json:
224
- * schema:
225
- * $ref: '#/components/schemas/ConversationResponse'
226
- * 400:
227
- * description: Bad request
228
- * 500:
229
- * description: Internal server error
230
- */
231
- app.post('/api/conversations', (req, res) => chatController.createConversation(req, res));
232
- /**
233
- * @swagger
234
- * /api/conversations/{uuid}:
235
- * get:
236
- * summary: Get conversation details with messages
237
- * description: Retrieve conversation information and message history
238
- * tags: [Conversations]
239
- * parameters:
240
- * - in: path
241
- * name: uuid
242
- * required: true
243
- * schema:
244
- * type: string
245
- * description: Conversation UUID
246
- * responses:
247
- * 200:
248
- * description: Conversation details with messages
249
- * 404:
250
- * description: Conversation not found
251
- * 500:
252
- * description: Internal server error
253
- */
254
- app.get('/api/conversations/:uuid', (req, res) => chatController.getConversation(req, res));
255
- /**
256
- * @swagger
257
- * /api/conversations/{uuid}:
258
- * delete:
259
- * summary: Delete conversation
260
- * description: Soft delete a conversation and its messages
261
- * tags: [Conversations]
262
- * parameters:
263
- * - in: path
264
- * name: uuid
265
- * required: true
266
- * schema:
267
- * type: string
268
- * description: Conversation UUID
269
- * responses:
270
- * 200:
271
- * description: Conversation deleted successfully
272
- * 404:
273
- * description: Conversation not found
274
- * 500:
275
- * description: Internal server error
276
- */
277
- app.delete('/api/conversations/:uuid', (req, res) => chatController.deleteConversation(req, res));
278
- /**
279
- * @swagger
280
- * /api/conversations/{uuid}/messages:
281
- * post:
282
- * summary: Create user message in conversation
283
- * description: Add a user message to the conversation
284
- * tags: [Messages]
285
- * parameters:
286
- * - in: path
287
- * name: uuid
288
- * required: true
289
- * schema:
290
- * type: string
291
- * description: Conversation UUID
292
- * requestBody:
293
- * required: true
294
- * content:
295
- * application/json:
296
- * schema:
297
- * $ref: '#/components/schemas/CreateMessageRequest'
298
- * responses:
299
- * 200:
300
- * description: User message created successfully
301
- * content:
302
- * application/json:
303
- * schema:
304
- * $ref: '#/components/schemas/MessageResponse'
305
- * 400:
306
- * description: Bad request
307
- * 404:
308
- * description: Conversation not found
309
- * 500:
310
- * description: Internal server error
311
- */
312
- app.post('/api/conversations/:uuid/messages', (req, res) => chatController.createUserMessage(req, res));
313
- /**
314
- * @swagger
315
- * /api/messages/{uuid}/generate:
316
- * post:
317
- * summary: Generate AI response for user message
318
- * description: Generate AI assistant response to a user message
319
- * tags: [Messages]
320
- * parameters:
321
- * - in: path
322
- * name: uuid
323
- * required: true
324
- * schema:
325
- * type: string
326
- * description: User message UUID
327
- * responses:
328
- * 200:
329
- * description: AI response generated successfully
330
- * content:
331
- * application/json:
332
- * schema:
333
- * $ref: '#/components/schemas/MessageResponse'
334
- * 404:
335
- * description: Message not found
336
- * 500:
337
- * description: Internal server error
338
- */
339
- app.post('/api/messages/:uuid/generate', (req, res) => chatController.generateResponse(req, res));
340
- /**
341
- * @swagger
342
- * /api/users/{uuid}/conversations:
343
- * get:
344
- * summary: Get user's conversations
345
- * description: Retrieve all conversations for a specific user
346
- * tags: [Conversations]
347
- * parameters:
348
- * - in: path
349
- * name: uuid
350
- * required: true
351
- * schema:
352
- * type: string
353
- * description: User UUID
354
- * - in: query
355
- * name: company_uuid
356
- * schema:
357
- * type: string
358
- * description: Optional company UUID filter
359
- * responses:
360
- * 200:
361
- * description: List of user conversations
362
- * 500:
363
- * description: Internal server error
364
- */
365
- app.get('/api/users/:uuid/conversations', (req, res) => chatController.getUserConversations(req, res));
366
- /**
367
- * @swagger
368
- * /api/feedback:
369
- * post:
370
- * summary: Submit message feedback
371
- * description: Submit rating and feedback for an AI response
372
- * tags: [Messages]
373
- * requestBody:
374
- * required: true
375
- * content:
376
- * application/json:
377
- * schema:
378
- * $ref: '#/components/schemas/FeedbackRequest'
379
- * responses:
380
- * 200:
381
- * description: Feedback submitted successfully
382
- * content:
383
- * application/json:
384
- * schema:
385
- * $ref: '#/components/schemas/FeedbackResponse'
386
- * 400:
387
- * description: Missing required fields
388
- * 500:
389
- * description: Internal server error
390
- */
391
- app.post('/api/feedback', (req, res) => chatController.submitFeedback(req, res));
392
- // ============================================================================
393
- // KNOWLEDGE BASE APIS (Unified Items)
394
- // ============================================================================
395
- /**
396
- * @swagger
397
- * /api/knowledge/items:
398
- * post:
399
- * summary: Create knowledge item
400
- * description: Create a new knowledge item (folder, document, file, url, etc.)
401
- * tags: [Knowledge]
402
- * requestBody:
403
- * required: true
404
- * content:
405
- * application/json:
406
- * schema:
407
- * $ref: '#/components/schemas/CreateKnowledgeItemRequest'
408
- * responses:
409
- * 200:
410
- * description: Knowledge item created successfully
411
- * 400:
412
- * description: Bad request
413
- * 500:
414
- * description: Internal server error
415
- */
416
- app.post('/api/knowledge/items', (req, res) => knowledgeController.createItem(req, res));
417
- /**
418
- * @swagger
419
- * /api/knowledge/items:
420
- * get:
421
- * summary: List knowledge items
422
- * description: Get list of knowledge items with optional filtering and pagination
423
- * tags: [Knowledge]
424
- * parameters:
425
- * - in: query
426
- * name: parent_uuid
427
- * schema:
428
- * type: string
429
- * description: Filter by parent UUID
430
- * - in: query
431
- * name: company_uuid
432
- * schema:
433
- * type: integer
434
- * description: Filter by company UUID
435
- * - in: query
436
- * name: type
437
- * schema:
438
- * type: string
439
- * description: Filter by item type
440
- * - in: query
441
- * name: limit
442
- * schema:
443
- * type: integer
444
- * default: 50
445
- * description: Maximum number of items to return
446
- * - in: query
447
- * name: offset
448
- * schema:
449
- * type: integer
450
- * default: 0
451
- * description: Number of items to skip
452
- * responses:
453
- * 200:
454
- * description: List of knowledge items
455
- * 500:
456
- * description: Internal server error
457
- */
458
- app.get('/api/knowledge/items', (req, res) => knowledgeController.listItems(req, res));
459
- /**
460
- * @swagger
461
- * /api/knowledge/search:
462
- * post:
463
- * summary: Search knowledge items
464
- * description: Search through knowledge items using semantic or keyword search
465
- * tags: [Knowledge]
466
- * requestBody:
467
- * required: true
468
- * content:
469
- * application/json:
470
- * schema:
471
- * $ref: '#/components/schemas/KnowledgeSearchRequest'
472
- * responses:
473
- * 200:
474
- * description: Search results
475
- * 400:
476
- * description: Bad request
477
- * 500:
478
- * description: Internal server error
479
- */
480
- app.post('/api/knowledge/search', (req, res) => knowledgeController.search(req, res));
481
- /**
482
- * @swagger
483
- * /api/search:
484
- * post:
485
- * summary: RAG Search
486
- * description: Perform RAG (Retrieval-Augmented Generation) search that combines knowledge base search with AI response generation
487
- * tags: [Search]
488
- * requestBody:
489
- * required: true
490
- * content:
491
- * application/json:
492
- * schema:
493
- * $ref: '#/components/schemas/RAGSearchRequest'
494
- * responses:
495
- * 200:
496
- * description: RAG search response
497
- * content:
498
- * application/json:
499
- * schema:
500
- * $ref: '#/components/schemas/RAGSearchResponse'
501
- * 400:
502
- * description: Bad request
503
- * 500:
504
- * description: Internal server error
505
- */
506
- app.post('/api/search', (req, res) => knowledgeController.ragSearch(req, res));
507
- /**
508
- * @swagger
509
- * /api/knowledge/items/{uuid}:
510
- * get:
511
- * summary: Get knowledge item
512
- * description: Retrieve a specific knowledge item by UUID
513
- * tags: [Knowledge]
514
- * parameters:
515
- * - in: path
516
- * name: uuid
517
- * required: true
518
- * schema:
519
- * type: string
520
- * description: Knowledge item UUID
521
- * responses:
522
- * 200:
523
- * description: Knowledge item details
524
- * 404:
525
- * description: Knowledge item not found
526
- * 500:
527
- * description: Internal server error
528
- */
529
- app.get('/api/knowledge/items/:uuid', (req, res) => knowledgeController.getItem(req, res));
530
- /**
531
- * @swagger
532
- * /api/knowledge/items/{uuid}:
533
- * put:
534
- * summary: Update knowledge item
535
- * description: Update an existing knowledge item
536
- * tags: [Knowledge]
537
- * parameters:
538
- * - in: path
539
- * name: uuid
540
- * required: true
541
- * schema:
542
- * type: string
543
- * description: Knowledge item UUID
544
- * requestBody:
545
- * required: true
546
- * content:
547
- * application/json:
548
- * schema:
549
- * $ref: '#/components/schemas/UpdateKnowledgeItemRequest'
550
- * responses:
551
- * 200:
552
- * description: Knowledge item updated successfully
553
- * 404:
554
- * description: Knowledge item not found
555
- * 500:
556
- * description: Internal server error
557
- */
558
- app.put('/api/knowledge/items/:uuid', (req, res) => knowledgeController.updateItem(req, res));
559
- /**
560
- * @swagger
561
- * /api/knowledge/items/{uuid}:
562
- * delete:
563
- * summary: Delete knowledge item
564
- * description: Remove a knowledge item from the knowledge base
565
- * tags: [Knowledge]
566
- * parameters:
567
- * - in: path
568
- * name: uuid
569
- * required: true
570
- * schema:
571
- * type: string
572
- * description: Knowledge item UUID
573
- * responses:
574
- * 200:
575
- * description: Knowledge item deleted successfully
576
- * 404:
577
- * description: Knowledge item not found
578
- * 500:
579
- * description: Internal server error
580
- */
581
- app.delete('/api/knowledge/items/:uuid', (req, res) => knowledgeController.deleteItem(req, res));
582
- // ============================================================================
583
- // MIGRATION ENDPOINTS
584
- // ============================================================================
585
- /**
586
- * @swagger
587
- * /api/migrate:
588
- * get:
589
- * summary: Run database migrations
590
- * description: Executes pending database migrations with API key authentication
591
- * tags: [System]
592
- * parameters:
593
- * - in: query
594
- * name: key
595
- * required: true
596
- * schema:
597
- * type: string
598
- * description: Migration API key
599
- * responses:
600
- * 200:
601
- * description: Migrations completed successfully
602
- * content:
603
- * application/json:
604
- * schema:
605
- * type: object
606
- * properties:
607
- * success:
608
- * type: boolean
609
- * message:
610
- * type: string
611
- * currentVersion:
612
- * type: string
613
- * details:
614
- * type: object
615
- * 400:
616
- * description: Bad request (missing API key or environment variables)
617
- * 401:
618
- * description: Unauthorized (invalid API key)
619
- * 500:
620
- * description: Internal server error
621
- */
622
- app.get('/api/migrate', (0, errorHandler_1.asyncHandler)(async (req, res) => {
623
- const { MigrationService } = await Promise.resolve().then(() => __importStar(require('./services/MigrationService')));
624
- const apiKey = req.query.key || req.headers['x-migration-key'];
625
- if (!apiKey) {
626
- return res.status(400).json({
627
- success: false,
628
- message: 'Migration API key is required',
629
- error: 'MISSING_API_KEY',
630
- details: {
631
- usage: 'Add ?key=your-secret-key to the URL or x-migration-key header'
632
- }
633
- });
634
- }
635
- const result = await MigrationService.runMigrations(apiKey);
636
- const statusCode = result.success ? 200 :
637
- result.error === 'UNAUTHORIZED' ? 401 :
638
- result.error === 'MISSING_ENV_VARS' || result.error === 'DATABASE_CONNECTION_FAILED' ? 400 : 500;
639
- res.status(statusCode).json(result);
640
- }));
641
- /**
642
- * @swagger
643
- * /api/migrate/status:
644
- * get:
645
- * summary: Get migration status
646
- * description: Check current migration status without running migrations
647
- * tags: [System]
648
- * parameters:
649
- * - in: query
650
- * name: key
651
- * required: true
652
- * schema:
653
- * type: string
654
- * description: Migration API key
655
- * responses:
656
- * 200:
657
- * description: Migration status retrieved
658
- * 401:
659
- * description: Unauthorized (invalid API key)
660
- * 500:
661
- * description: Internal server error
662
- */
663
- app.get('/api/migrate/status', (0, errorHandler_1.asyncHandler)(async (req, res) => {
664
- const { MigrationService } = await Promise.resolve().then(() => __importStar(require('./services/MigrationService')));
665
- const apiKey = req.query.key || req.headers['x-migration-key'];
666
- if (!apiKey) {
667
- return res.status(400).json({
668
- success: false,
669
- message: 'Migration API key is required',
670
- error: 'MISSING_API_KEY'
671
- });
672
- }
673
- const result = await MigrationService.getStatus(apiKey);
674
- const statusCode = result.success ? 200 :
675
- result.error === 'UNAUTHORIZED' ? 401 : 500;
676
- res.status(statusCode).json(result);
677
- }));
678
- // ============================================================================
679
- // WEBSOCKET HANDLING
680
- // ============================================================================
681
- // WebSocket handling
682
- io.on('connection', (socket) => {
683
- logger_1.default.info(`Client connected: ${socket.id}`);
684
- socket.on('join-conversation', (conversationId) => {
685
- socket.join(`conversation:${conversationId}`);
686
- logger_1.default.info(`Socket ${socket.id} joined conversation ${conversationId}`);
687
191
  });
688
- socket.on('message', async (data) => {
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'];
689
993
  try {
690
- const { message, conversation_id, context } = data;
691
- const response = await chatManager.sendMessage(message, conversation_id, context);
692
- socket.emit('response', {
693
- message: response.content,
694
- conversation_id: response.conversationId,
695
- message_id: response.messageId,
696
- suggested_links: response.suggestedLinks
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
+ }
697
1022
  });
698
- // Broadcast to all clients in the conversation
699
- if (response.conversationId) {
700
- io.to(`conversation:${response.conversationId}`).emit('new-message', {
701
- role: 'assistant',
702
- content: response.content,
703
- timestamp: new Date()
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'
704
1098
  });
1099
+ return;
705
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
+ });
706
1112
  }
707
1113
  catch (error) {
708
- socket.emit('error', {
709
- message: error instanceof Error ? error.message : 'Unknown error'
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
+ }
710
1122
  });
711
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}`);
712
1140
  });
713
1141
  socket.on('disconnect', () => {
714
1142
  logger_1.default.info(`Client disconnected: ${socket.id}`);
715
1143
  });
716
1144
  });
717
- // Error handling middleware
718
- // Global error handling middleware
719
- app.use(errorHandler_1.errorHandler);
720
- // 404 handler for undefined routes
721
- app.use(errorHandler_1.notFoundHandler);
722
1145
  // Start server
723
- const PORT = global_1.config.app.port;
1146
+ const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3002;
724
1147
  async function start() {
725
1148
  try {
726
1149
  // Validate configuration
727
- (0, global_1.validateConfig)();
1150
+ validateConfig();
728
1151
  await initializeServices();
1152
+ // Setup routes after services are initialized
1153
+ setupRoutes();
729
1154
  server.listen(PORT, '0.0.0.0', () => {
730
- logger_1.default.info(`🚀 ${global_1.config.app.name} v${global_1.config.app.version} running on port ${PORT}`);
731
- logger_1.default.info(`📊 Environment: ${global_1.config.app.environment}`);
732
- logger_1.default.info(`🌐 API available at http://localhost:${PORT}${global_1.config.api.prefix}`);
733
- if (global_1.config.swagger.enabled) {
734
- logger_1.default.info(`📚 API Documentation: http://localhost:${PORT}${global_1.config.swagger.path}`);
735
- }
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`);
736
1159
  logger_1.default.info(`🔌 WebSocket available at ws://localhost:${PORT}`);
737
1160
  logger_1.default.info(`💓 Health check: http://localhost:${PORT}/health`);
738
1161
  });
@@ -742,16 +1165,27 @@ async function start() {
742
1165
  process.exit(1);
743
1166
  }
744
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
+ }
745
1179
  // Graceful shutdown
746
- process.on('SIGTERM', async () => {
747
- logger_1.default.info('SIGTERM received, shutting down gracefully...');
1180
+ process.on('SIGINT', async () => {
1181
+ logger_1.default.info('SIGINT received, shutting down gracefully...');
748
1182
  server.close(() => {
749
1183
  logger_1.default.info('HTTP server closed');
750
1184
  process.exit(0);
751
1185
  });
752
1186
  });
753
- process.on('SIGINT', async () => {
754
- logger_1.default.info('SIGINT received, shutting down gracefully...');
1187
+ process.on('SIGTERM', async () => {
1188
+ logger_1.default.info('SIGTERM received, shutting down gracefully...');
755
1189
  server.close(() => {
756
1190
  logger_1.default.info('HTTP server closed');
757
1191
  process.exit(0);