import { Injectable, Inject, HttpException } from '@nestjs/common'; import axios, { AxiosResponse, Method } from 'axios'; import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; import { RequestUser } from '../../decorators/user.decorator'; import { STORAGE_EXPLORER_CONFIG } from './storage-explorer.config'; @Injectable() export class StorageExplorerService { private logger: any; constructor( @Inject(DadosferaLogger) dadosferaLogger: DadosferaLogger, ) { this.logger = dadosferaLogger.logger; } async proxy( method: string, path: string, user: RequestUser, body?: any, query?: Record ): Promise { // Validate customer_id is present for multi-tenant isolation if (!user.customer_id) { throw new HttpException('Customer ID is required for storage operations', 400); } // Get customer-specific storage-explorer URL const baseUrl = STORAGE_EXPLORER_CONFIG.getUrl(user.customer_name); const url = new URL(`${baseUrl}${path}`); // Add query params if (query) { Object.entries(query).forEach(([key, value]) => { if (value !== undefined && value !== null) { url.searchParams.set(key, String(value)); } }); } const headers: Record = { 'content-type': 'application/json', }; this.logger.info('Proxying request to storage-explorer', { method: method.toUpperCase(), path, customer_id: user.customer_id, storage_url: baseUrl, user_id: user.user_id, }); try { const response: AxiosResponse = await axios({ method: method as Method, url: url.href, headers, data: body, timeout: STORAGE_EXPLORER_CONFIG.timeout, validateStatus: () => true, // Don't throw on non-2xx }); // Propagate non-2xx responses as HttpExceptions if (response.status >= 400) { throw new HttpException(response.data, response.status); } return response.data; } catch (error) { this.logger.error('Storage Explorer API proxy error', { error: error.message, status: error.response?.status, path, storage_url: baseUrl, method: method.toUpperCase(), }); if (error instanceof HttpException) { throw error; } if (error.response) { throw new HttpException(error.response.data, error.response.status); } if (error.code === 'ECONNREFUSED') { throw new HttpException('Storage Explorer API service unavailable', 503); } if (error.code === 'ETIMEDOUT' || error.code === 'ECONNABORTED') { throw new HttpException('Storage Explorer API request timeout', 504); } throw new HttpException('Internal server error', 500); } } /** * Proxy with file upload support (multipart/form-data) */ async proxyFormData( method: string, path: string, user: RequestUser, formData: any, query?: Record, ): Promise { // Validate customer_id is present for multi-tenant isolation if (!user.customer_id) { throw new HttpException('Customer ID is required for storage operations', 400); } // Get customer-specific storage-explorer URL const baseUrl = STORAGE_EXPLORER_CONFIG.getUrl(user.customer_name); const url = new URL(`${baseUrl}${path}`); // Add query params if (query) { Object.entries(query).forEach(([key, value]) => { if (value !== undefined && value !== null) { url.searchParams.set(key, String(value)); } }); } const headers: Record = { // Let axios set Content-Type for multipart/form-data with boundary ...formData.getHeaders?.(), }; this.logger.info('Proxying form data request to storage-explorer', { method: method.toUpperCase(), path, customer_id: user.customer_id, storage_url: baseUrl, user_id: user.user_id, }); try { const response: AxiosResponse = await axios({ method: method as Method, url: url.href, headers, data: formData, timeout: STORAGE_EXPLORER_CONFIG.timeout, maxContentLength: Infinity, maxBodyLength: Infinity, validateStatus: () => true, }); if (response.status >= 400) { throw new HttpException(response.data, response.status); } return response.data; } catch (error) { this.logger.error('Storage Explorer API form data proxy error', { error: error.message, status: error.response?.status, path, storage_url: baseUrl, method: method.toUpperCase(), }); if (error instanceof HttpException) { throw error; } if (error.response) { throw new HttpException(error.response.data, error.response.status); } throw new HttpException('Internal server error', 500); } } }