267 lines
8.0 KiB
JavaScript
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
|
|
}
|
|
};
|
|
}
|
|
});
|