@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.
Files changed (113) hide show
  1. package/README.md +140 -43
  2. package/bin/vezlo-server.js +1 -1
  3. package/database-schema.sql +193 -33
  4. package/dist/knexfile.d.ts.map +1 -1
  5. package/dist/knexfile.js +17 -8
  6. package/dist/knexfile.js.map +1 -1
  7. package/dist/src/config/database.d.ts.map +1 -1
  8. package/dist/src/config/database.js +9 -1
  9. package/dist/src/config/database.js.map +1 -1
  10. package/dist/src/config/global.d.ts.map +1 -1
  11. package/dist/src/config/global.js +5 -2
  12. package/dist/src/config/global.js.map +1 -1
  13. package/dist/src/config/knex.d.ts.map +1 -1
  14. package/dist/src/config/knex.js +22 -2
  15. package/dist/src/config/knex.js.map +1 -1
  16. package/dist/src/config/swagger.d.ts.map +1 -1
  17. package/dist/src/config/swagger.js +34 -73
  18. package/dist/src/config/swagger.js.map +1 -1
  19. package/dist/src/controllers/ApiKeyController.d.ts +17 -0
  20. package/dist/src/controllers/ApiKeyController.d.ts.map +1 -0
  21. package/dist/src/controllers/ApiKeyController.js +84 -0
  22. package/dist/src/controllers/ApiKeyController.js.map +1 -0
  23. package/dist/src/controllers/AuthController.d.ts +14 -0
  24. package/dist/src/controllers/AuthController.d.ts.map +1 -0
  25. package/dist/src/controllers/AuthController.js +212 -0
  26. package/dist/src/controllers/AuthController.js.map +1 -0
  27. package/dist/src/controllers/ChatController.d.ts +8 -5
  28. package/dist/src/controllers/ChatController.d.ts.map +1 -1
  29. package/dist/src/controllers/ChatController.js +139 -31
  30. package/dist/src/controllers/ChatController.js.map +1 -1
  31. package/dist/src/controllers/KnowledgeController.d.ts +5 -4
  32. package/dist/src/controllers/KnowledgeController.d.ts.map +1 -1
  33. package/dist/src/controllers/KnowledgeController.js +54 -16
  34. package/dist/src/controllers/KnowledgeController.js.map +1 -1
  35. package/dist/src/middleware/auth.d.ts +51 -0
  36. package/dist/src/middleware/auth.d.ts.map +1 -0
  37. package/dist/src/middleware/auth.js +232 -0
  38. package/dist/src/middleware/auth.js.map +1 -0
  39. package/dist/src/middleware/errorHandler.d.ts.map +1 -1
  40. package/dist/src/middleware/errorHandler.js +13 -19
  41. package/dist/src/middleware/errorHandler.js.map +1 -1
  42. package/dist/src/migrations/001_initial_schema.d.ts.map +1 -1
  43. package/dist/src/migrations/001_initial_schema.js +39 -64
  44. package/dist/src/migrations/001_initial_schema.js.map +1 -1
  45. package/dist/src/migrations/002_multitenancy_schema.d.ts +4 -0
  46. package/dist/src/migrations/002_multitenancy_schema.d.ts.map +1 -0
  47. package/dist/src/migrations/002_multitenancy_schema.js +119 -0
  48. package/dist/src/migrations/002_multitenancy_schema.js.map +1 -0
  49. package/dist/src/schemas/AuthSchemas.d.ts +89 -0
  50. package/dist/src/schemas/AuthSchemas.d.ts.map +1 -0
  51. package/dist/src/schemas/AuthSchemas.js +63 -0
  52. package/dist/src/schemas/AuthSchemas.js.map +1 -0
  53. package/dist/src/schemas/CommonSchemas.d.ts +62 -0
  54. package/dist/src/schemas/CommonSchemas.d.ts.map +1 -0
  55. package/dist/src/schemas/CommonSchemas.js +65 -0
  56. package/dist/src/schemas/CommonSchemas.js.map +1 -0
  57. package/dist/src/schemas/ConversationSchemas.d.ts +64 -27
  58. package/dist/src/schemas/ConversationSchemas.d.ts.map +1 -1
  59. package/dist/src/schemas/ConversationSchemas.js +28 -9
  60. package/dist/src/schemas/ConversationSchemas.js.map +1 -1
  61. package/dist/src/schemas/FeedbackSchemas.d.ts +43 -5
  62. package/dist/src/schemas/FeedbackSchemas.d.ts.map +1 -1
  63. package/dist/src/schemas/FeedbackSchemas.js +20 -2
  64. package/dist/src/schemas/FeedbackSchemas.js.map +1 -1
  65. package/dist/src/schemas/KnowledgeSchemas.d.ts +114 -35
  66. package/dist/src/schemas/KnowledgeSchemas.d.ts.map +1 -1
  67. package/dist/src/schemas/KnowledgeSchemas.js +58 -16
  68. package/dist/src/schemas/KnowledgeSchemas.js.map +1 -1
  69. package/dist/src/schemas/MessageSchemas.d.ts +57 -8
  70. package/dist/src/schemas/MessageSchemas.d.ts.map +1 -1
  71. package/dist/src/schemas/MessageSchemas.js +22 -3
  72. package/dist/src/schemas/MessageSchemas.js.map +1 -1
  73. package/dist/src/schemas/index.d.ts +410 -68
  74. package/dist/src/schemas/index.d.ts.map +1 -1
  75. package/dist/src/schemas/index.js +8 -2
  76. package/dist/src/schemas/index.js.map +1 -1
  77. package/dist/src/server.js +1047 -615
  78. package/dist/src/server.js.map +1 -1
  79. package/dist/src/services/AIService.d.ts +1 -2
  80. package/dist/src/services/AIService.d.ts.map +1 -1
  81. package/dist/src/services/AIService.js +6 -32
  82. package/dist/src/services/AIService.js.map +1 -1
  83. package/dist/src/services/ApiKeyService.d.ts +38 -0
  84. package/dist/src/services/ApiKeyService.d.ts.map +1 -0
  85. package/dist/src/services/ApiKeyService.js +123 -0
  86. package/dist/src/services/ApiKeyService.js.map +1 -0
  87. package/dist/src/services/KnowledgeBaseService.d.ts +2 -2
  88. package/dist/src/services/KnowledgeBaseService.d.ts.map +1 -1
  89. package/dist/src/services/KnowledgeBaseService.js +9 -2
  90. package/dist/src/services/KnowledgeBaseService.js.map +1 -1
  91. package/dist/src/services/MigrationService.d.ts +1 -1
  92. package/dist/src/services/MigrationService.d.ts.map +1 -1
  93. package/dist/src/services/MigrationService.js +4 -8
  94. package/dist/src/services/MigrationService.js.map +1 -1
  95. package/dist/src/services/SetupService.d.ts +102 -0
  96. package/dist/src/services/SetupService.d.ts.map +1 -0
  97. package/dist/src/services/SetupService.js +343 -0
  98. package/dist/src/services/SetupService.js.map +1 -0
  99. package/dist/src/storage/ConversationRepository.d.ts.map +1 -1
  100. package/dist/src/storage/ConversationRepository.js +42 -8
  101. package/dist/src/storage/ConversationRepository.js.map +1 -1
  102. package/dist/src/storage/MessageRepository.d.ts.map +1 -1
  103. package/dist/src/storage/MessageRepository.js +23 -27
  104. package/dist/src/storage/MessageRepository.js.map +1 -1
  105. package/dist/src/types/index.d.ts +0 -8
  106. package/dist/src/types/index.d.ts.map +1 -1
  107. package/env.example +7 -5
  108. package/knexfile.ts +17 -8
  109. package/package.json +25 -16
  110. package/scripts/generate-key.js +124 -0
  111. package/scripts/seed-default.js +72 -0
  112. package/scripts/setup.js +410 -149
  113. package/scripts/validate-db.js +46 -13
@@ -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,47 +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'),
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
- // Initialize chat manager
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
- // Redirect root to docs
146
- app.get('/', (req, res) => {
147
- res.redirect('/docs');
148
- });
149
- // API Documentation
150
- app.use('/docs', swagger_1.swaggerUi.serve, swagger_1.swaggerUi.setup(swagger_1.specs, swagger_1.swaggerUiOptions));
151
- /**
152
- * @swagger
153
- * /health:
154
- * get:
155
- * summary: Health check endpoint
156
- * description: Check server and database connectivity status
157
- * tags: [Health]
158
- * responses:
159
- * 200:
160
- * description: Server is healthy
161
- * content:
162
- * application/json:
163
- * schema:
164
- * $ref: '#/components/schemas/HealthCheck'
165
- * 503:
166
- * description: Server is unhealthy
167
- * content:
168
- * application/json:
169
- * schema:
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 supabase = (0, database_1.getSupabaseClient)();
181
- const { data, error } = await supabase.from('vezlo_conversations').select('count').limit(1);
182
- if (!error) {
183
- 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';
184
178
  }
185
- else {
186
- healthChecks.supabase = 'error';
179
+ catch (dbError) {
180
+ healthChecks.database = 'error';
187
181
  }
182
+ res.json(healthChecks);
188
183
  }
189
184
  catch (error) {
190
- 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
+ });
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
- 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'];
690
993
  try {
691
- const { message, conversation_id, context } = data;
692
- const response = await chatManager.sendMessage(message, conversation_id, context);
693
- socket.emit('response', {
694
- message: response.content,
695
- conversation_id: response.conversationId,
696
- message_id: response.messageId,
697
- feedback_detection: response.feedbackDetection,
698
- 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
+ }
699
1022
  });
700
- // Broadcast to all clients in the conversation
701
- if (response.conversationId) {
702
- io.to(`conversation:${response.conversationId}`).emit('new-message', {
703
- role: 'assistant',
704
- content: response.content,
705
- 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'
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
- socket.emit('error', {
711
- 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
+ }
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 = global_1.config.app.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
- (0, global_1.validateConfig)();
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(`🚀 ${global_1.config.app.name} v${global_1.config.app.version} running on port ${PORT}`);
733
- logger_1.default.info(`📊 Environment: ${global_1.config.app.environment}`);
734
- logger_1.default.info(`🌐 API available at http://localhost:${PORT}${global_1.config.api.prefix}`);
735
- if (global_1.config.swagger.enabled) {
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('SIGTERM', async () => {
749
- logger_1.default.info('SIGTERM received, shutting down gracefully...');
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('SIGINT', async () => {
756
- logger_1.default.info('SIGINT received, shutting down gracefully...');
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);