|
|
@@ -276,6 +276,118 @@ export class QdrantEndpoint extends BaseEndpointComponent {
|
|
|
}
|
|
|
),
|
|
|
|
|
|
+ // Shop-specific endpoints (new URL structure)
|
|
|
+ this.createEndpoint(
|
|
|
+ 'GET',
|
|
|
+ '/api/qdrant/shops/:shopId',
|
|
|
+ this.getShopQdrantInfo.bind(this),
|
|
|
+ {
|
|
|
+ summary: 'Get shop Qdrant info',
|
|
|
+ description: 'Get Qdrant status and information for a specific shop',
|
|
|
+ tags: ['Qdrant'],
|
|
|
+ requiresAuth: true,
|
|
|
+ parameters: [
|
|
|
+ {
|
|
|
+ name: 'shopId',
|
|
|
+ in: 'path',
|
|
|
+ type: 'string',
|
|
|
+ required: true,
|
|
|
+ description: 'Shop ID'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ responses: {
|
|
|
+ '200': { description: 'Shop Qdrant information' }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ),
|
|
|
+
|
|
|
+ this.createEndpoint(
|
|
|
+ 'GET',
|
|
|
+ '/api/qdrant/shops/:shopId/embeddings',
|
|
|
+ this.getShopQdrantEmbeddings.bind(this),
|
|
|
+ {
|
|
|
+ summary: 'Get shop embeddings',
|
|
|
+ description: 'Get all embeddings for a specific shop',
|
|
|
+ tags: ['Qdrant'],
|
|
|
+ requiresAuth: true,
|
|
|
+ parameters: [
|
|
|
+ {
|
|
|
+ name: 'shopId',
|
|
|
+ in: 'path',
|
|
|
+ type: 'string',
|
|
|
+ required: true,
|
|
|
+ description: 'Shop ID'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ responses: {
|
|
|
+ '200': { description: 'Shop embeddings list' }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ),
|
|
|
+
|
|
|
+ this.createEndpoint(
|
|
|
+ 'GET',
|
|
|
+ '/api/qdrant/shops/:shopId/errors',
|
|
|
+ this.getShopQdrantErrorsNew.bind(this),
|
|
|
+ {
|
|
|
+ summary: 'Get shop Qdrant errors',
|
|
|
+ description: 'Get all Qdrant errors for a specific shop',
|
|
|
+ tags: ['Qdrant'],
|
|
|
+ requiresAuth: true,
|
|
|
+ parameters: [
|
|
|
+ {
|
|
|
+ name: 'shopId',
|
|
|
+ in: 'path',
|
|
|
+ type: 'string',
|
|
|
+ required: true,
|
|
|
+ description: 'Shop ID'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ responses: {
|
|
|
+ '200': { description: 'Shop Qdrant errors' }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ),
|
|
|
+
|
|
|
+ this.createEndpoint(
|
|
|
+ 'POST',
|
|
|
+ '/api/qdrant/cleanup',
|
|
|
+ this.triggerCleanup.bind(this),
|
|
|
+ {
|
|
|
+ summary: 'Trigger Qdrant cleanup',
|
|
|
+ description: 'Manually trigger Qdrant cleanup process',
|
|
|
+ tags: ['Qdrant'],
|
|
|
+ requiresAuth: true,
|
|
|
+ responses: {
|
|
|
+ '202': { description: 'Cleanup triggered' }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ),
|
|
|
+
|
|
|
+ this.createEndpoint(
|
|
|
+ 'POST',
|
|
|
+ '/api/qdrant/shops/:shopId/schedule-deletion',
|
|
|
+ this.scheduleShopDeletion.bind(this),
|
|
|
+ {
|
|
|
+ summary: 'Schedule shop deletion',
|
|
|
+ description: 'Schedule deletion of shop data from Qdrant',
|
|
|
+ tags: ['Qdrant'],
|
|
|
+ requiresAuth: true,
|
|
|
+ parameters: [
|
|
|
+ {
|
|
|
+ name: 'shopId',
|
|
|
+ in: 'path',
|
|
|
+ type: 'string',
|
|
|
+ required: true,
|
|
|
+ description: 'Shop ID'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ responses: {
|
|
|
+ '202': { description: 'Deletion scheduled' }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ),
|
|
|
+
|
|
|
// Embedding Management
|
|
|
this.createEndpoint(
|
|
|
'POST',
|
|
|
@@ -694,4 +806,154 @@ export class QdrantEndpoint extends BaseEndpointComponent {
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // New endpoint handlers for the correct URL structure
|
|
|
+ private async getShopQdrantInfo(req: Request, res: Response): Promise<void> {
|
|
|
+ try {
|
|
|
+ const { shopId } = req.params;
|
|
|
+
|
|
|
+ // Get shop from database
|
|
|
+ const shop = this.db.getShopByCustomId(shopId);
|
|
|
+ if (!shop) {
|
|
|
+ res.status(404).json({ error: 'Shop not found' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get embeddings count
|
|
|
+ const embeddings = this.db.getShopContent(shop.id);
|
|
|
+
|
|
|
+ res.json({
|
|
|
+ data: {
|
|
|
+ enabled: shop.qdrant_enabled || false,
|
|
|
+ custom_id: shop.custom_id,
|
|
|
+ collections: [], // Would need to implement collection listing
|
|
|
+ embedding_stats: {
|
|
|
+ total_embeddings: embeddings.length,
|
|
|
+ completed: embeddings.filter(e => e.embedding_status === 'completed').length,
|
|
|
+ failed: embeddings.filter(e => e.embedding_status === 'failed').length
|
|
|
+ }
|
|
|
+ },
|
|
|
+ success: true
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Failed to get shop Qdrant info:', error);
|
|
|
+ res.status(500).json({ error: 'Internal server error' });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async getShopQdrantEmbeddings(req: Request, res: Response): Promise<void> {
|
|
|
+ try {
|
|
|
+ const { shopId } = req.params;
|
|
|
+
|
|
|
+ // Get shop from database
|
|
|
+ const shop = this.db.getShopByCustomId(shopId);
|
|
|
+ if (!shop) {
|
|
|
+ res.status(404).json({ error: 'Shop not found' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get shop embeddings from database
|
|
|
+ const embeddings = this.db.getShopContent(shop.id);
|
|
|
+
|
|
|
+ res.json({
|
|
|
+ data: {
|
|
|
+ embeddings: embeddings.map(embedding => ({
|
|
|
+ id: `emb-${embedding.id}`,
|
|
|
+ shop_content_id: `content-${embedding.id}`,
|
|
|
+ collection_name: `${shopId}-${embedding.content_type}_1`,
|
|
|
+ point_id: embedding.qdrant_point_id || null,
|
|
|
+ embedding_status: embedding.embedding_status || 'pending',
|
|
|
+ created_at: embedding.created_at,
|
|
|
+ updated_at: embedding.updated_at
|
|
|
+ }))
|
|
|
+ },
|
|
|
+ success: true
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Failed to get shop embeddings:', error);
|
|
|
+ res.status(500).json({ error: 'Internal server error' });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async getShopQdrantErrorsNew(req: Request, res: Response): Promise<void> {
|
|
|
+ try {
|
|
|
+ const { shopId } = req.params;
|
|
|
+
|
|
|
+ // Get shop from database
|
|
|
+ const shop = this.db.getShopByCustomId(shopId);
|
|
|
+ if (!shop) {
|
|
|
+ res.status(404).json({ error: 'Shop not found' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const errors = this.db.getQdrantErrors(shop.id);
|
|
|
+
|
|
|
+ res.json({
|
|
|
+ data: {
|
|
|
+ errors: errors.map(error => ({
|
|
|
+ id: error.id,
|
|
|
+ shop_id: error.shop_id,
|
|
|
+ operation: error.operation_type,
|
|
|
+ error_message: error.error_message,
|
|
|
+ error_details: error.error_details,
|
|
|
+ created_at: error.created_at
|
|
|
+ }))
|
|
|
+ },
|
|
|
+ success: true
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Failed to get shop Qdrant errors:', error);
|
|
|
+ res.status(500).json({ error: 'Internal server error' });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async triggerCleanup(req: Request, res: Response): Promise<void> {
|
|
|
+ try {
|
|
|
+ // Trigger manual cleanup using the scheduler service
|
|
|
+ if (this.schedulerService) {
|
|
|
+ await this.schedulerService.runTask('cleanup');
|
|
|
+ }
|
|
|
+
|
|
|
+ res.status(202).json({
|
|
|
+ message: 'Cleanup triggered successfully',
|
|
|
+ success: true
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Failed to trigger cleanup:', error);
|
|
|
+ res.status(500).json({ error: 'Internal server error' });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async scheduleShopDeletion(req: Request, res: Response): Promise<void> {
|
|
|
+ try {
|
|
|
+ const { shopId } = req.params;
|
|
|
+
|
|
|
+ // Get shop from database
|
|
|
+ const shop = this.db.getShopByCustomId(shopId);
|
|
|
+ if (!shop) {
|
|
|
+ res.status(404).json({ error: 'Shop not found' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Schedule deletion using the existing method
|
|
|
+ this.db.createQdrantDeletionItem({
|
|
|
+ id: `del-${Date.now()}`,
|
|
|
+ shop_id: shop.id,
|
|
|
+ collection_names: JSON.stringify([]),
|
|
|
+ scheduled_deletion_at: new Date().toISOString(),
|
|
|
+ reason: 'shop_deleted',
|
|
|
+ status: 'pending',
|
|
|
+ created_at: new Date().toISOString()
|
|
|
+ });
|
|
|
+
|
|
|
+ res.status(202).json({
|
|
|
+ message: 'Shop deletion scheduled',
|
|
|
+ shop_id: shopId,
|
|
|
+ success: true
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ logger.error('Failed to schedule shop deletion:', error);
|
|
|
+ res.status(500).json({ error: 'Internal server error' });
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|