corrad-af-2024/server/api/api-platform/send-request.post.js

267 lines
8.0 KiB
JavaScript

export default defineEventHandler(async (event) => {
try {
const requestData = await readBody(event);
if (!requestData.url) {
return {
statusCode: 400,
message: "URL is required",
};
}
let {
url,
method = 'GET',
headers = {},
params = [],
auth = {},
requestBody = {},
timeout = 30000
} = requestData;
// Fix URL if it doesn't have protocol
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url;
}
const startTime = Date.now();
// Build final URL with query parameters
let urlObj;
try {
urlObj = new URL(url);
} catch (error) {
return {
statusCode: 400,
message: "Invalid URL format",
data: {
status: 400,
statusText: 'Bad Request',
headers: {},
data: { error: 'Invalid URL format. Please check the URL and try again.' },
time: 0,
size: 0
}
};
}
// Add active query parameters
params.forEach(param => {
if (param.active && param.key && param.value) {
urlObj.searchParams.set(param.key, param.value);
}
});
// Add API key to query if specified
if (auth.type === 'apiKey' && auth.apiKey?.key && auth.apiKey?.value && auth.apiKey?.addTo === 'query') {
urlObj.searchParams.set(auth.apiKey.key, auth.apiKey.value);
}
const finalUrl = urlObj.toString();
// Build headers
const requestHeaders = {};
// Add custom headers
headers.forEach(header => {
if (header.active && header.key && header.value) {
requestHeaders[header.key] = header.value;
}
});
// Add authentication headers
if (auth.type === 'bearer' && auth.bearer) {
requestHeaders['Authorization'] = `Bearer ${auth.bearer}`;
} else if (auth.type === 'basic' && auth.basic?.username && auth.basic?.password) {
const credentials = Buffer.from(`${auth.basic.username}:${auth.basic.password}`).toString('base64');
requestHeaders['Authorization'] = `Basic ${credentials}`;
} else if (auth.type === 'apiKey' && auth.apiKey?.key && auth.apiKey?.value && auth.apiKey?.addTo === 'header') {
requestHeaders[auth.apiKey.key] = auth.apiKey.value;
} else if (auth.type === 'oauth2' && auth.oauth2?.accessToken) {
requestHeaders['Authorization'] = `${auth.oauth2.tokenType || 'Bearer'} ${auth.oauth2.accessToken}`;
}
// Build request body
let requestBodyData = null;
let contentType = null;
if (method !== 'GET' && method !== 'HEAD' && requestBody.type && requestBody.type !== 'none') {
if (requestBody.type === 'raw' && requestBody.raw) {
requestBodyData = requestBody.raw;
// Try to parse as JSON to set appropriate content type
try {
JSON.parse(requestBody.raw);
contentType = 'application/json';
} catch (e) {
contentType = 'text/plain';
}
} else if (requestBody.type === 'x-www-form-urlencoded' && requestBody.urlEncoded) {
const urlEncodedData = new URLSearchParams();
requestBody.urlEncoded.forEach(item => {
if (item.active && item.key && item.value) {
urlEncodedData.append(item.key, item.value);
}
});
requestBodyData = urlEncodedData.toString();
contentType = 'application/x-www-form-urlencoded';
} else if (requestBody.type === 'form-data' && requestBody.formData) {
// For form-data, we'll handle files as base64 for now
// In a real implementation, you'd want multipart/form-data support
const formData = new URLSearchParams();
let hasFiles = false;
requestBody.formData.forEach(item => {
if (item.active && item.key) {
if (item.type === 'text' && item.value) {
formData.append(item.key, item.value);
} else if (item.type === 'file' && item.file) {
// For now, we'll send file name as value
// Real file upload would need special handling
formData.append(item.key, `[FILE: ${item.file.name}]`);
hasFiles = true;
}
}
});
if (hasFiles) {
// Log a warning that file uploads aren't fully supported yet
console.warn('File uploads in form-data are not fully supported in the backend proxy yet');
}
requestBodyData = formData.toString();
contentType = 'application/x-www-form-urlencoded'; // Fallback to URL-encoded for now
}
}
// Set content type if not already set
if (contentType && !requestHeaders['Content-Type'] && !requestHeaders['content-type']) {
requestHeaders['Content-Type'] = contentType;
}
// Create fetch options
const fetchOptions = {
method,
headers: requestHeaders,
signal: AbortSignal.timeout(timeout)
};
if (requestBodyData) {
fetchOptions.body = requestBodyData;
}
// Make the actual HTTP request
const response = await fetch(finalUrl, fetchOptions);
const endTime = Date.now();
const responseTime = endTime - startTime;
// Get response data
const responseHeaders = {};
response.headers.forEach((value, key) => {
responseHeaders[key] = value;
});
let responseData;
const contentTypeHeader = response.headers.get('content-type') || '';
if (contentTypeHeader.includes('application/json')) {
try {
responseData = await response.json();
} catch (e) {
responseData = await response.text();
}
} else if (contentTypeHeader.includes('text/') || contentTypeHeader.includes('application/xml')) {
responseData = await response.text();
} else {
// For binary data, convert to text representation
try {
responseData = await response.text();
} catch (e) {
responseData = '[Binary data]';
}
}
// Calculate response size (approximate)
const responseSize = typeof responseData === 'string'
? new Blob([responseData]).size
: new Blob([JSON.stringify(responseData)]).size;
return {
statusCode: 200,
message: "Request completed",
data: {
status: response.status,
statusText: response.statusText,
headers: responseHeaders,
data: responseData,
time: responseTime,
size: responseSize,
url: finalUrl
}
};
} catch (error) {
console.error('API Platform Request Error:', error);
const endTime = Date.now();
// Handle different types of errors
if (error.name === 'AbortError') {
return {
statusCode: 408,
message: "Request timeout",
data: {
status: 408,
statusText: 'Request Timeout',
headers: {},
data: { error: 'Request timed out' },
time: 30000,
size: 0
}
};
}
if (error.name === 'TypeError' && error.message.includes('fetch')) {
return {
statusCode: 400,
message: "Invalid URL or network error",
data: {
status: 400,
statusText: 'Bad Request',
headers: {},
data: { error: 'Invalid URL format or network error' },
time: endTime - Date.now() + 1000,
size: 0
}
};
}
if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
return {
statusCode: 502,
message: "Connection error",
data: {
status: 502,
statusText: 'Bad Gateway',
headers: {},
data: { error: 'Failed to connect to the server' },
time: endTime - Date.now() + 1000,
size: 0
}
};
}
return {
statusCode: 500,
message: "Internal server error",
data: {
status: 500,
statusText: 'Internal Server Error',
headers: {},
data: { error: error.message || 'Something went wrong' },
time: endTime - Date.now() + 1000,
size: 0
}
};
}
});