mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-23 02:16:08 +00:00
update fixes BETA
This commit is contained in:
parent
d479550c49
commit
07435fc255
@ -19,6 +19,7 @@ interface UpdateProgress {
|
|||||||
totalSize?: string;
|
totalSize?: string;
|
||||||
currentCommit?: string;
|
currentCommit?: string;
|
||||||
remoteCommit?: string;
|
remoteCommit?: string;
|
||||||
|
updateReady?: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +114,10 @@ const UpdateTab = () => {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ branch: branchToCheck }),
|
body: JSON.stringify({
|
||||||
|
branch: branchToCheck,
|
||||||
|
autoUpdate: updateSettings.autoUpdate,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@ -152,10 +156,11 @@ const UpdateTab = () => {
|
|||||||
setIsChecking(false);
|
setIsChecking(false);
|
||||||
|
|
||||||
if (!progress.error) {
|
if (!progress.error) {
|
||||||
// Update was successful
|
// Update check completed
|
||||||
toast.success('Update check completed');
|
toast.success('Update check completed');
|
||||||
|
|
||||||
if (progress.details?.changedFiles?.length) {
|
// Show update dialog only if there are changes and auto-update is disabled
|
||||||
|
if (progress.details?.changedFiles?.length && progress.details.updateReady) {
|
||||||
setShowUpdateDialog(true);
|
setShowUpdateDialog(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,6 +181,69 @@ const UpdateTab = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleUpdate = async () => {
|
||||||
|
setShowUpdateDialog(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const branchToCheck = isLatestBranch ? 'main' : 'stable';
|
||||||
|
|
||||||
|
// Start the update with autoUpdate set to true to force the update
|
||||||
|
const response = await fetch('/api/update', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
branch: branchToCheck,
|
||||||
|
autoUpdate: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Update failed: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the update progress stream
|
||||||
|
const reader = response.body?.getReader();
|
||||||
|
|
||||||
|
if (!reader) {
|
||||||
|
throw new Error('No response stream available');
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chunk = new TextDecoder().decode(value);
|
||||||
|
const lines = chunk.split('\n').filter(Boolean);
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
try {
|
||||||
|
const progress = JSON.parse(line) as UpdateProgress;
|
||||||
|
setUpdateProgress(progress);
|
||||||
|
|
||||||
|
if (progress.error) {
|
||||||
|
setError(progress.error);
|
||||||
|
toast.error('Update failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress.stage === 'complete' && !progress.error) {
|
||||||
|
toast.success('Update completed successfully');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error parsing update progress:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setError(error instanceof Error ? error.message : 'Unknown error occurred');
|
||||||
|
toast.error('Update failed');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
<motion.div
|
<motion.div
|
||||||
@ -286,44 +354,75 @@ const UpdateTab = () => {
|
|||||||
<div className="i-ph:arrows-clockwise text-purple-500 w-5 h-5" />
|
<div className="i-ph:arrows-clockwise text-purple-500 w-5 h-5" />
|
||||||
<h3 className="text-lg font-medium text-bolt-elements-textPrimary">Update Status</h3>
|
<h3 className="text-lg font-medium text-bolt-elements-textPrimary">Update Status</h3>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div className="flex items-center gap-2">
|
||||||
onClick={() => {
|
{updateProgress?.details?.updateReady && !updateSettings.autoUpdate && (
|
||||||
setError(null);
|
<button
|
||||||
checkForUpdates();
|
onClick={handleUpdate}
|
||||||
}}
|
className={classNames(
|
||||||
className={classNames(
|
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
|
||||||
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
|
'bg-purple-500 text-white',
|
||||||
'bg-[#F5F5F5] dark:bg-[#1A1A1A]',
|
'hover:bg-purple-600',
|
||||||
'hover:bg-purple-500/10 hover:text-purple-500',
|
'transition-colors duration-200',
|
||||||
'dark:hover:bg-purple-500/20 dark:hover:text-purple-500',
|
)}
|
||||||
'text-bolt-elements-textPrimary',
|
>
|
||||||
'transition-colors duration-200',
|
<div className="i-ph:arrow-circle-up w-4 h-4" />
|
||||||
'disabled:opacity-50 disabled:cursor-not-allowed',
|
Update Now
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
disabled={isChecking}
|
<button
|
||||||
>
|
onClick={() => {
|
||||||
{isChecking ? (
|
setError(null);
|
||||||
<div className="flex items-center gap-2">
|
checkForUpdates();
|
||||||
<motion.div
|
}}
|
||||||
animate={{ rotate: 360 }}
|
className={classNames(
|
||||||
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
|
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
|
||||||
className="i-ph:arrows-clockwise w-4 h-4"
|
'bg-[#F5F5F5] dark:bg-[#1A1A1A]',
|
||||||
/>
|
'hover:bg-purple-500/10 hover:text-purple-500',
|
||||||
Checking...
|
'dark:hover:bg-purple-500/20 dark:hover:text-purple-500',
|
||||||
</div>
|
'text-bolt-elements-textPrimary',
|
||||||
) : (
|
'transition-colors duration-200',
|
||||||
<>
|
'disabled:opacity-50 disabled:cursor-not-allowed',
|
||||||
<div className="i-ph:arrows-clockwise w-4 h-4" />
|
)}
|
||||||
Check for Updates
|
disabled={isChecking}
|
||||||
</>
|
>
|
||||||
)}
|
{isChecking ? (
|
||||||
</button>
|
<div className="flex items-center gap-2">
|
||||||
|
<motion.div
|
||||||
|
animate={{ rotate: 360 }}
|
||||||
|
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
|
||||||
|
className="i-ph:arrows-clockwise w-4 h-4"
|
||||||
|
/>
|
||||||
|
Checking...
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="i-ph:arrows-clockwise w-4 h-4" />
|
||||||
|
Check for Updates
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Show progress information */}
|
{/* Show progress information */}
|
||||||
{updateProgress && <UpdateProgressDisplay progress={updateProgress} />}
|
{updateProgress && <UpdateProgressDisplay progress={updateProgress} />}
|
||||||
|
|
||||||
{error && <div className="mt-4 p-4 bg-red-100 text-red-700 rounded">{error}</div>}
|
{error && <div className="mt-4 p-4 bg-red-100 text-red-700 rounded">{error}</div>}
|
||||||
|
|
||||||
|
{/* Show update source information */}
|
||||||
|
<div className="mt-4 text-sm text-bolt-elements-textSecondary">
|
||||||
|
<p>
|
||||||
|
Updates are fetched from: <span className="font-mono">stackblitz-labs/bolt.diy</span> (
|
||||||
|
{isLatestBranch ? 'main' : 'stable'} branch)
|
||||||
|
</p>
|
||||||
|
{updateProgress?.details?.currentCommit && updateProgress?.details?.remoteCommit && (
|
||||||
|
<p className="mt-1">
|
||||||
|
Current version: <span className="font-mono">{updateProgress.details.currentCommit}</span>
|
||||||
|
<span className="mx-2">→</span>
|
||||||
|
Latest version: <span className="font-mono">{updateProgress.details.remoteCommit}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Update dialog */}
|
{/* Update dialog */}
|
||||||
@ -331,34 +430,53 @@ const UpdateTab = () => {
|
|||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTitle>Update Available</DialogTitle>
|
<DialogTitle>Update Available</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{updateProgress?.details?.changedFiles && (
|
<div className="mt-4">
|
||||||
<div className="mt-4">
|
<p className="text-sm text-bolt-elements-textSecondary mb-4">
|
||||||
<p className="font-medium">Changes:</p>
|
A new version is available from <span className="font-mono">stackblitz-labs/bolt.diy</span> (
|
||||||
<ul className="list-disc list-inside mt-2">
|
{isLatestBranch ? 'main' : 'stable'} branch)
|
||||||
{updateProgress.details.changedFiles.map((file, index) => (
|
</p>
|
||||||
<li key={index} className="text-sm">
|
{updateProgress?.details?.commitMessages && updateProgress.details.commitMessages.length > 0 && (
|
||||||
{file}
|
<div className="mb-4">
|
||||||
</li>
|
<p className="font-medium mb-2">Commit Messages:</p>
|
||||||
))}
|
<ul className="list-disc list-inside space-y-1">
|
||||||
</ul>
|
{updateProgress.details.commitMessages.map((msg, index) => (
|
||||||
{updateProgress.details.totalSize && (
|
<li key={index} className="text-sm text-bolt-elements-textSecondary">
|
||||||
<p className="mt-2 text-sm">Total size: {updateProgress.details.totalSize}</p>
|
{msg}
|
||||||
)}
|
</li>
|
||||||
</div>
|
))}
|
||||||
)}
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{updateProgress?.details?.changedFiles && (
|
||||||
|
<div>
|
||||||
|
<p className="font-medium mb-2">Changed Files:</p>
|
||||||
|
<ul className="list-disc list-inside space-y-1">
|
||||||
|
{updateProgress.details.changedFiles.map((file, index) => (
|
||||||
|
<li key={index} className="text-sm text-bolt-elements-textSecondary">
|
||||||
|
{file}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{updateProgress?.details?.totalSize && (
|
||||||
|
<p className="mt-4 text-sm text-bolt-elements-textSecondary">
|
||||||
|
Total size: {updateProgress.details.totalSize}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{updateProgress?.details?.additions !== undefined && updateProgress?.details?.deletions !== undefined && (
|
||||||
|
<p className="mt-2 text-sm text-bolt-elements-textSecondary">
|
||||||
|
Changes: <span className="text-green-600">+{updateProgress.details.additions}</span>{' '}
|
||||||
|
<span className="text-red-600">-{updateProgress.details.deletions}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
<div className="flex justify-end gap-2 mt-4">
|
<div className="flex justify-end gap-2 mt-6">
|
||||||
<DialogButton type="secondary" onClick={() => setShowUpdateDialog(false)}>
|
<DialogButton type="secondary" onClick={() => setShowUpdateDialog(false)}>
|
||||||
Cancel
|
Cancel
|
||||||
</DialogButton>
|
</DialogButton>
|
||||||
<DialogButton
|
<DialogButton type="primary" onClick={handleUpdate}>
|
||||||
type="primary"
|
|
||||||
onClick={() => {
|
|
||||||
setShowUpdateDialog(false);
|
|
||||||
|
|
||||||
// Handle update initiation here
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Update Now
|
Update Now
|
||||||
</DialogButton>
|
</DialogButton>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,6 +7,7 @@ const execAsync = promisify(exec);
|
|||||||
|
|
||||||
interface UpdateRequestBody {
|
interface UpdateRequestBody {
|
||||||
branch: string;
|
branch: string;
|
||||||
|
autoUpdate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateProgress {
|
interface UpdateProgress {
|
||||||
@ -22,6 +23,7 @@ interface UpdateProgress {
|
|||||||
totalSize?: string;
|
totalSize?: string;
|
||||||
currentCommit?: string;
|
currentCommit?: string;
|
||||||
remoteCommit?: string;
|
remoteCommit?: string;
|
||||||
|
updateReady?: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +39,7 @@ export const action: ActionFunction = async ({ request }) => {
|
|||||||
return json({ error: 'Invalid request body: branch is required and must be a string' }, { status: 400 });
|
return json({ error: 'Invalid request body: branch is required and must be a string' }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { branch } = body as UpdateRequestBody;
|
const { branch, autoUpdate = false } = body as UpdateRequestBody;
|
||||||
|
|
||||||
// Create a ReadableStream to send progress updates
|
// Create a ReadableStream to send progress updates
|
||||||
const stream = new ReadableStream({
|
const stream = new ReadableStream({
|
||||||
@ -271,9 +273,30 @@ export const action: ActionFunction = async ({ request }) => {
|
|||||||
totalSize: formatSize(totalSizeInBytes),
|
totalSize: formatSize(totalSizeInBytes),
|
||||||
currentCommit: currentCommit.trim().substring(0, 7),
|
currentCommit: currentCommit.trim().substring(0, 7),
|
||||||
remoteCommit: remoteCommit.trim().substring(0, 7),
|
remoteCommit: remoteCommit.trim().substring(0, 7),
|
||||||
|
updateReady: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Only proceed with update if autoUpdate is true
|
||||||
|
if (!autoUpdate) {
|
||||||
|
sendProgress({
|
||||||
|
stage: 'complete',
|
||||||
|
message: 'Update is ready to be applied. Click "Update Now" to proceed.',
|
||||||
|
progress: 100,
|
||||||
|
details: {
|
||||||
|
changedFiles,
|
||||||
|
additions: stats?.[2] ? parseInt(stats[2]) : 0,
|
||||||
|
deletions: stats?.[3] ? parseInt(stats[3]) : 0,
|
||||||
|
commitMessages,
|
||||||
|
totalSize: formatSize(totalSizeInBytes),
|
||||||
|
currentCommit: currentCommit.trim().substring(0, 7),
|
||||||
|
remoteCommit: remoteCommit.trim().substring(0, 7),
|
||||||
|
updateReady: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Pull stage
|
// Pull stage
|
||||||
sendProgress({
|
sendProgress({
|
||||||
stage: 'pull',
|
stage: 'pull',
|
||||||
|
Loading…
Reference in New Issue
Block a user