fix: introduce our own cors proxy for git import to fix 403 errors on isometric git cors proxy (#924)
* Exploration of improving git import * Fix our own git proxy * Clean out file counting for progress, does not seem to work well anyways
This commit is contained in:
@@ -3,6 +3,9 @@ import { useGit } from '~/lib/hooks/useGit';
|
||||
import type { Message } from 'ai';
|
||||
import { detectProjectCommands, createCommandsMessage } from '~/utils/projectCommands';
|
||||
import { generateId } from '~/utils/fileUtils';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { LoadingOverlay } from '~/components/ui/LoadingOverlay';
|
||||
|
||||
const IGNORE_PATTERNS = [
|
||||
'node_modules/**',
|
||||
@@ -37,6 +40,8 @@ interface GitCloneButtonProps {
|
||||
|
||||
export default function GitCloneButton({ importChat }: GitCloneButtonProps) {
|
||||
const { ready, gitClone } = useGit();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const onClick = async (_e: any) => {
|
||||
if (!ready) {
|
||||
return;
|
||||
@@ -45,33 +50,34 @@ export default function GitCloneButton({ importChat }: GitCloneButtonProps) {
|
||||
const repoUrl = prompt('Enter the Git url');
|
||||
|
||||
if (repoUrl) {
|
||||
const { workdir, data } = await gitClone(repoUrl);
|
||||
setLoading(true);
|
||||
|
||||
if (importChat) {
|
||||
const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath));
|
||||
console.log(filePaths);
|
||||
try {
|
||||
const { workdir, data } = await gitClone(repoUrl);
|
||||
|
||||
const textDecoder = new TextDecoder('utf-8');
|
||||
if (importChat) {
|
||||
const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath));
|
||||
console.log(filePaths);
|
||||
|
||||
// Convert files to common format for command detection
|
||||
const fileContents = filePaths
|
||||
.map((filePath) => {
|
||||
const { data: content, encoding } = data[filePath];
|
||||
return {
|
||||
path: filePath,
|
||||
content: encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '',
|
||||
};
|
||||
})
|
||||
.filter((f) => f.content);
|
||||
const textDecoder = new TextDecoder('utf-8');
|
||||
|
||||
// Detect and create commands message
|
||||
const commands = await detectProjectCommands(fileContents);
|
||||
const commandsMessage = createCommandsMessage(commands);
|
||||
const fileContents = filePaths
|
||||
.map((filePath) => {
|
||||
const { data: content, encoding } = data[filePath];
|
||||
return {
|
||||
path: filePath,
|
||||
content:
|
||||
encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '',
|
||||
};
|
||||
})
|
||||
.filter((f) => f.content);
|
||||
|
||||
// Create files message
|
||||
const filesMessage: Message = {
|
||||
role: 'assistant',
|
||||
content: `Cloning the repo ${repoUrl} into ${workdir}
|
||||
const commands = await detectProjectCommands(fileContents);
|
||||
const commandsMessage = createCommandsMessage(commands);
|
||||
|
||||
const filesMessage: Message = {
|
||||
role: 'assistant',
|
||||
content: `Cloning the repo ${repoUrl} into ${workdir}
|
||||
<boltArtifact id="imported-files" title="Git Cloned Files" type="bundled">
|
||||
${fileContents
|
||||
.map(
|
||||
@@ -82,29 +88,38 @@ ${file.content}
|
||||
)
|
||||
.join('\n')}
|
||||
</boltArtifact>`,
|
||||
id: generateId(),
|
||||
createdAt: new Date(),
|
||||
};
|
||||
id: generateId(),
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
const messages = [filesMessage];
|
||||
const messages = [filesMessage];
|
||||
|
||||
if (commandsMessage) {
|
||||
messages.push(commandsMessage);
|
||||
if (commandsMessage) {
|
||||
messages.push(commandsMessage);
|
||||
}
|
||||
|
||||
await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages);
|
||||
}
|
||||
|
||||
await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages);
|
||||
} catch (error) {
|
||||
console.error('Error during import:', error);
|
||||
toast.error('Failed to import repository');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
title="Clone a Git Repo"
|
||||
className="px-4 py-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-all flex items-center gap-2"
|
||||
>
|
||||
<span className="i-ph:git-branch" />
|
||||
Clone a Git Repo
|
||||
</button>
|
||||
<>
|
||||
<button
|
||||
onClick={onClick}
|
||||
title="Clone a Git Repo"
|
||||
className="px-4 py-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-all flex items-center gap-2"
|
||||
>
|
||||
<span className="i-ph:git-branch" />
|
||||
Clone a Git Repo
|
||||
</button>
|
||||
{loading && <LoadingOverlay message="Please wait while we clone the repository..." />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,33 +49,32 @@ export function GitUrlImport() {
|
||||
|
||||
if (repoUrl) {
|
||||
const ig = ignore().add(IGNORE_PATTERNS);
|
||||
const { workdir, data } = await gitClone(repoUrl);
|
||||
|
||||
if (importChat) {
|
||||
const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath));
|
||||
try {
|
||||
const { workdir, data } = await gitClone(repoUrl);
|
||||
|
||||
const textDecoder = new TextDecoder('utf-8');
|
||||
if (importChat) {
|
||||
const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath));
|
||||
const textDecoder = new TextDecoder('utf-8');
|
||||
|
||||
// Convert files to common format for command detection
|
||||
const fileContents = filePaths
|
||||
.map((filePath) => {
|
||||
const { data: content, encoding } = data[filePath];
|
||||
return {
|
||||
path: filePath,
|
||||
content: encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '',
|
||||
};
|
||||
})
|
||||
.filter((f) => f.content);
|
||||
const fileContents = filePaths
|
||||
.map((filePath) => {
|
||||
const { data: content, encoding } = data[filePath];
|
||||
return {
|
||||
path: filePath,
|
||||
content:
|
||||
encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '',
|
||||
};
|
||||
})
|
||||
.filter((f) => f.content);
|
||||
|
||||
// Detect and create commands message
|
||||
const commands = await detectProjectCommands(fileContents);
|
||||
const commandsMessage = createCommandsMessage(commands);
|
||||
const commands = await detectProjectCommands(fileContents);
|
||||
const commandsMessage = createCommandsMessage(commands);
|
||||
|
||||
// Create files message
|
||||
const filesMessage: Message = {
|
||||
role: 'assistant',
|
||||
content: `Cloning the repo ${repoUrl} into ${workdir}
|
||||
<boltArtifact id="imported-files" title="Git Cloned Files" type="bundled">
|
||||
const filesMessage: Message = {
|
||||
role: 'assistant',
|
||||
content: `Cloning the repo ${repoUrl} into ${workdir}
|
||||
<boltArtifact id="imported-files" title="Git Cloned Files" type="bundled">
|
||||
${fileContents
|
||||
.map(
|
||||
(file) =>
|
||||
@@ -85,17 +84,25 @@ ${file.content}
|
||||
)
|
||||
.join('\n')}
|
||||
</boltArtifact>`,
|
||||
id: generateId(),
|
||||
createdAt: new Date(),
|
||||
};
|
||||
id: generateId(),
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
const messages = [filesMessage];
|
||||
const messages = [filesMessage];
|
||||
|
||||
if (commandsMessage) {
|
||||
messages.push(commandsMessage);
|
||||
if (commandsMessage) {
|
||||
messages.push(commandsMessage);
|
||||
}
|
||||
|
||||
await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during import:', error);
|
||||
toast.error('Failed to import repository');
|
||||
setLoading(false);
|
||||
window.location.href = '/';
|
||||
|
||||
await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
export const LoadingOverlay = ({ message = 'Loading...' }) => {
|
||||
export const LoadingOverlay = ({
|
||||
message = 'Loading...',
|
||||
progress,
|
||||
progressText,
|
||||
}: {
|
||||
message?: string;
|
||||
progress?: number;
|
||||
progressText?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div className="fixed inset-0 flex items-center justify-center bg-black/80 z-50 backdrop-blur-sm">
|
||||
{/* Loading content */}
|
||||
<div className="relative flex flex-col items-center gap-4 p-8 rounded-lg bg-bolt-elements-background-depth-2 shadow-lg">
|
||||
<div
|
||||
className={'i-svg-spinners:90-ring-with-bg text-bolt-elements-loader-progress'}
|
||||
style={{ fontSize: '2rem' }}
|
||||
></div>
|
||||
<p className="text-lg text-bolt-elements-textTertiary">{message}</p>
|
||||
{progress !== undefined && (
|
||||
<div className="w-64 flex flex-col gap-2">
|
||||
<div className="w-full h-2 bg-bolt-elements-background-depth-1 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-bolt-elements-loader-progress transition-all duration-300 ease-out rounded-full"
|
||||
style={{ width: `${Math.min(100, Math.max(0, progress))}%` }}
|
||||
/>
|
||||
</div>
|
||||
{progressText && <p className="text-sm text-bolt-elements-textTertiary text-center">{progressText}</p>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user