diff --git a/app/components/@settings/tabs/update/UpdateTab.tsx b/app/components/@settings/tabs/update/UpdateTab.tsx index 45fdddb6..a279af47 100644 --- a/app/components/@settings/tabs/update/UpdateTab.tsx +++ b/app/components/@settings/tabs/update/UpdateTab.tsx @@ -19,6 +19,7 @@ interface UpdateProgress { totalSize?: string; currentCommit?: string; remoteCommit?: string; + updateReady?: boolean; }; } @@ -113,7 +114,10 @@ const UpdateTab = () => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ branch: branchToCheck }), + body: JSON.stringify({ + branch: branchToCheck, + autoUpdate: updateSettings.autoUpdate, + }), }); if (!response.ok) { @@ -152,10 +156,11 @@ const UpdateTab = () => { setIsChecking(false); if (!progress.error) { - // Update was successful + // 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); } } @@ -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 (
{

Update Status

- )} - disabled={isChecking} - > - {isChecking ? ( -
- - Checking... -
- ) : ( - <> -
- Check for Updates - - )} - + +
{/* Show progress information */} {updateProgress && } {error &&
{error}
} + + {/* Show update source information */} +
+

+ Updates are fetched from: stackblitz-labs/bolt.diy ( + {isLatestBranch ? 'main' : 'stable'} branch) +

+ {updateProgress?.details?.currentCommit && updateProgress?.details?.remoteCommit && ( +

+ Current version: {updateProgress.details.currentCommit} + + Latest version: {updateProgress.details.remoteCommit} +

+ )} +
{/* Update dialog */} @@ -331,34 +430,53 @@ const UpdateTab = () => { Update Available - {updateProgress?.details?.changedFiles && ( -
-

Changes:

-
    - {updateProgress.details.changedFiles.map((file, index) => ( -
  • - {file} -
  • - ))} -
- {updateProgress.details.totalSize && ( -

Total size: {updateProgress.details.totalSize}

- )} -
- )} +
+

+ A new version is available from stackblitz-labs/bolt.diy ( + {isLatestBranch ? 'main' : 'stable'} branch) +

+ {updateProgress?.details?.commitMessages && updateProgress.details.commitMessages.length > 0 && ( +
+

Commit Messages:

+
    + {updateProgress.details.commitMessages.map((msg, index) => ( +
  • + {msg} +
  • + ))} +
+
+ )} + {updateProgress?.details?.changedFiles && ( +
+

Changed Files:

+
    + {updateProgress.details.changedFiles.map((file, index) => ( +
  • + {file} +
  • + ))} +
+
+ )} + {updateProgress?.details?.totalSize && ( +

+ Total size: {updateProgress.details.totalSize} +

+ )} + {updateProgress?.details?.additions !== undefined && updateProgress?.details?.deletions !== undefined && ( +

+ Changes: +{updateProgress.details.additions}{' '} + -{updateProgress.details.deletions} +

+ )} +
-
+
setShowUpdateDialog(false)}> Cancel - { - setShowUpdateDialog(false); - - // Handle update initiation here - }} - > + Update Now
diff --git a/app/routes/api.update.ts b/app/routes/api.update.ts index 6713cf4b..d25a3883 100644 --- a/app/routes/api.update.ts +++ b/app/routes/api.update.ts @@ -7,6 +7,7 @@ const execAsync = promisify(exec); interface UpdateRequestBody { branch: string; + autoUpdate?: boolean; } interface UpdateProgress { @@ -22,6 +23,7 @@ interface UpdateProgress { totalSize?: string; currentCommit?: 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 }); } - const { branch } = body as UpdateRequestBody; + const { branch, autoUpdate = false } = body as UpdateRequestBody; // Create a ReadableStream to send progress updates const stream = new ReadableStream({ @@ -271,9 +273,30 @@ export const action: ActionFunction = async ({ request }) => { totalSize: formatSize(totalSizeInBytes), currentCommit: currentCommit.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 sendProgress({ stage: 'pull',