mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-05-10 06:50:53 +00:00
fix: fix git proxy to work with other git provider (#1466)
This commit is contained in:
parent
9780393b17
commit
cd4a5e8380
@ -69,7 +69,7 @@ export function useChatHistory() {
|
|||||||
const summary = snapshot.summary;
|
const summary = snapshot.summary;
|
||||||
|
|
||||||
const rewindId = searchParams.get('rewindTo');
|
const rewindId = searchParams.get('rewindTo');
|
||||||
let startingIdx = 0;
|
let startingIdx = -1;
|
||||||
const endingIdx = rewindId
|
const endingIdx = rewindId
|
||||||
? storedMessages.messages.findIndex((m) => m.id === rewindId) + 1
|
? storedMessages.messages.findIndex((m) => m.id === rewindId) + 1
|
||||||
: storedMessages.messages.length;
|
: storedMessages.messages.length;
|
||||||
@ -80,13 +80,13 @@ export function useChatHistory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (snapshotIndex > 0 && storedMessages.messages[snapshotIndex].id == rewindId) {
|
if (snapshotIndex > 0 && storedMessages.messages[snapshotIndex].id == rewindId) {
|
||||||
startingIdx = 0;
|
startingIdx = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let filteredMessages = storedMessages.messages.slice(startingIdx + 1, endingIdx);
|
let filteredMessages = storedMessages.messages.slice(startingIdx + 1, endingIdx);
|
||||||
let archivedMessages: Message[] = [];
|
let archivedMessages: Message[] = [];
|
||||||
|
|
||||||
if (startingIdx > 0) {
|
if (startingIdx >= 0) {
|
||||||
archivedMessages = storedMessages.messages.slice(0, startingIdx + 1);
|
archivedMessages = storedMessages.messages.slice(0, startingIdx + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,47 @@
|
|||||||
import { json } from '@remix-run/cloudflare';
|
import { json } from '@remix-run/cloudflare';
|
||||||
import type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/cloudflare';
|
import type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/cloudflare';
|
||||||
|
|
||||||
|
// Allowed headers to forward to the target server
|
||||||
|
const ALLOW_HEADERS = [
|
||||||
|
'accept-encoding',
|
||||||
|
'accept-language',
|
||||||
|
'accept',
|
||||||
|
'access-control-allow-origin',
|
||||||
|
'authorization',
|
||||||
|
'cache-control',
|
||||||
|
'connection',
|
||||||
|
'content-length',
|
||||||
|
'content-type',
|
||||||
|
'dnt',
|
||||||
|
'pragma',
|
||||||
|
'range',
|
||||||
|
'referer',
|
||||||
|
'user-agent',
|
||||||
|
'x-authorization',
|
||||||
|
'x-http-method-override',
|
||||||
|
'x-requested-with',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Headers to expose from the target server's response
|
||||||
|
const EXPOSE_HEADERS = [
|
||||||
|
'accept-ranges',
|
||||||
|
'age',
|
||||||
|
'cache-control',
|
||||||
|
'content-length',
|
||||||
|
'content-language',
|
||||||
|
'content-type',
|
||||||
|
'date',
|
||||||
|
'etag',
|
||||||
|
'expires',
|
||||||
|
'last-modified',
|
||||||
|
'pragma',
|
||||||
|
'server',
|
||||||
|
'transfer-encoding',
|
||||||
|
'vary',
|
||||||
|
'x-github-request-id',
|
||||||
|
'x-redirected-url',
|
||||||
|
];
|
||||||
|
|
||||||
// Handle all HTTP methods
|
// Handle all HTTP methods
|
||||||
export async function action({ request, params }: ActionFunctionArgs) {
|
export async function action({ request, params }: ActionFunctionArgs) {
|
||||||
return handleProxyRequest(request, params['*']);
|
return handleProxyRequest(request, params['*']);
|
||||||
@ -16,50 +57,117 @@ async function handleProxyRequest(request: Request, path: string | undefined) {
|
|||||||
return json({ error: 'Invalid proxy URL format' }, { status: 400 });
|
return json({ error: 'Invalid proxy URL format' }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(request.url);
|
// Handle CORS preflight request
|
||||||
|
|
||||||
// Reconstruct the target URL
|
|
||||||
const targetURL = `https://${path}${url.search}`;
|
|
||||||
|
|
||||||
// Forward the request to the target URL
|
|
||||||
const response = await fetch(targetURL, {
|
|
||||||
method: request.method,
|
|
||||||
headers: {
|
|
||||||
...Object.fromEntries(request.headers),
|
|
||||||
|
|
||||||
// Override host header with the target host
|
|
||||||
host: new URL(targetURL).host,
|
|
||||||
},
|
|
||||||
body: ['GET', 'HEAD'].includes(request.method) ? null : await request.arrayBuffer(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create response with CORS headers
|
|
||||||
const corsHeaders = {
|
|
||||||
'Access-Control-Allow-Origin': '*',
|
|
||||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
||||||
'Access-Control-Allow-Headers': '*',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle preflight requests
|
|
||||||
if (request.method === 'OPTIONS') {
|
if (request.method === 'OPTIONS') {
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
headers: corsHeaders,
|
status: 200,
|
||||||
status: 204,
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
||||||
|
'Access-Control-Allow-Headers': ALLOW_HEADERS.join(', '),
|
||||||
|
'Access-Control-Expose-Headers': EXPOSE_HEADERS.join(', '),
|
||||||
|
'Access-Control-Max-Age': '86400',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward the response with CORS headers
|
// Extract domain and remaining path
|
||||||
const responseHeaders = new Headers(response.headers);
|
const parts = path.match(/([^\/]+)\/?(.*)/);
|
||||||
Object.entries(corsHeaders).forEach(([key, value]) => {
|
|
||||||
responseHeaders.set(key, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (!parts) {
|
||||||
|
return json({ error: 'Invalid path format' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const domain = parts[1];
|
||||||
|
const remainingPath = parts[2] || '';
|
||||||
|
|
||||||
|
// Reconstruct the target URL with query parameters
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const targetURL = `https://${domain}/${remainingPath}${url.search}`;
|
||||||
|
|
||||||
|
console.log('Target URL:', targetURL);
|
||||||
|
|
||||||
|
// Filter and prepare headers
|
||||||
|
const headers = new Headers();
|
||||||
|
|
||||||
|
// Only forward allowed headers
|
||||||
|
for (const header of ALLOW_HEADERS) {
|
||||||
|
if (request.headers.has(header)) {
|
||||||
|
headers.set(header, request.headers.get(header)!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the host header
|
||||||
|
headers.set('Host', domain);
|
||||||
|
|
||||||
|
// Set Git user agent if not already present
|
||||||
|
if (!headers.has('user-agent') || !headers.get('user-agent')?.startsWith('git/')) {
|
||||||
|
headers.set('User-Agent', 'git/@isomorphic-git/cors-proxy');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Request headers:', Object.fromEntries(headers.entries()));
|
||||||
|
|
||||||
|
// Prepare fetch options
|
||||||
|
const fetchOptions: RequestInit = {
|
||||||
|
method: request.method,
|
||||||
|
headers,
|
||||||
|
redirect: 'follow',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add body and duplex option for non-GET/HEAD requests
|
||||||
|
if (!['GET', 'HEAD'].includes(request.method)) {
|
||||||
|
fetchOptions.body = request.body;
|
||||||
|
fetchOptions.duplex = 'half'; // This fixes the "duplex option is required when sending a body" error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward the request to the target URL
|
||||||
|
const response = await fetch(targetURL, fetchOptions);
|
||||||
|
|
||||||
|
console.log('Response status:', response.status);
|
||||||
|
|
||||||
|
// Create response headers
|
||||||
|
const responseHeaders = new Headers();
|
||||||
|
|
||||||
|
// Add CORS headers
|
||||||
|
responseHeaders.set('Access-Control-Allow-Origin', '*');
|
||||||
|
responseHeaders.set('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
|
||||||
|
responseHeaders.set('Access-Control-Allow-Headers', ALLOW_HEADERS.join(', '));
|
||||||
|
responseHeaders.set('Access-Control-Expose-Headers', EXPOSE_HEADERS.join(', '));
|
||||||
|
|
||||||
|
// Copy exposed headers from the target response
|
||||||
|
for (const header of EXPOSE_HEADERS) {
|
||||||
|
// Skip content-length as we'll use the original response's content-length
|
||||||
|
if (header === 'content-length') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.headers.has(header)) {
|
||||||
|
responseHeaders.set(header, response.headers.get(header)!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the response was redirected, add the x-redirected-url header
|
||||||
|
if (response.redirected) {
|
||||||
|
responseHeaders.set('x-redirected-url', response.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Response headers:', Object.fromEntries(responseHeaders.entries()));
|
||||||
|
|
||||||
|
// Return the response with the target's body stream piped directly
|
||||||
return new Response(response.body, {
|
return new Response(response.body, {
|
||||||
status: response.status,
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
headers: responseHeaders,
|
headers: responseHeaders,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Git proxy error:', error);
|
console.error('Proxy error:', error);
|
||||||
return json({ error: 'Proxy error' }, { status: 500 });
|
return json(
|
||||||
|
{
|
||||||
|
error: 'Proxy error',
|
||||||
|
message: error instanceof Error ? error.message : 'Unknown error',
|
||||||
|
url: path ? `https://${path}` : 'Invalid URL',
|
||||||
|
},
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user