fix: chunk auto-backup writes + spinner on manual "Backup now"
Bridge.writeFile had the same URL-length trap saveFile did: a large
auto-backup base64'd into a single cmd://file/write URL blew past
WebView2's ~2MB navigation cap, so backups of big vaults failed
silently (or blanked the page) — for BOTH the manual "Backup now"
button and the silent scheduled run.
- writeFile now streams payloads over ~1MB in chunks (reusing the same
file/chunk transport as saveFile), committed via a new
file/write-commit. Delphi shares the decode+write logic through a new
WriteDecodedFile helper and the existing FFileSaveChunks buffer.
- runAutoBackupNow(silent): the manual run shows the busy overlay
("Reading vault… N/total" → "Encrypting backup…" → "Writing file… N%")
since a 20MB backup takes ~30s; the scheduled on-unlock run passes
silent=true (no overlay, but still chunked so it no longer fails on
large vaults).
- Fixed the "Backup now" click handler passing the click Event as the
silent arg (truthy → would have suppressed the spinner and swallowed
errors); wrapped in () => runAutoBackupNow().
- Pre-sync backup benefits from the chunked writeFile automatically.
Rebuild: BuildAssets + F9 (UMainForm.pas changed).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -115,6 +115,9 @@ type
|
||||
// (chunked): decode the base64, show the Save dialog, write, and fire
|
||||
// Bridge.onFileSaveResult back to JS.
|
||||
procedure SaveDecodedFile(const AName, AB64, AReqId: string);
|
||||
// Silent write to a fixed path (auto-backup / sync pre-backup). Shared
|
||||
// by cmd://file/write (single-shot) and file/write-commit (chunked).
|
||||
procedure WriteDecodedFile(const APath, AB64, AReqId: string);
|
||||
procedure LogLine(const AMsg: string);
|
||||
procedure UpdateButtons;
|
||||
procedure NavigateToVault;
|
||||
@@ -508,6 +511,40 @@ begin
|
||||
BoolToStr(LOk, True).ToLower + ',"' + LEscPath + '","' + LEscErr + '")');
|
||||
end;
|
||||
|
||||
procedure TMainForm.WriteDecodedFile(const APath, AB64, AReqId: string);
|
||||
var
|
||||
LOk: Boolean;
|
||||
LErr: string;
|
||||
begin
|
||||
LOk := False;
|
||||
LErr := '';
|
||||
try
|
||||
var LBytes := TNetEncoding.Base64.DecodeStringToBytes(AB64);
|
||||
var LStream := TFileStream.Create(APath, fmCreate);
|
||||
try
|
||||
if Length(LBytes) > 0 then
|
||||
LStream.WriteBuffer(LBytes[0], Length(LBytes));
|
||||
finally
|
||||
LStream.Free;
|
||||
end;
|
||||
LOk := True;
|
||||
LogLine(Format('File written: %s (%d bytes)', [APath, Length(LBytes)]));
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
LErr := E.Message;
|
||||
LogLine('File write FAILED for "' + APath + '": ' + LErr);
|
||||
end;
|
||||
end;
|
||||
var LEscReq := StringReplace(AReqId, '"', '\"', [rfReplaceAll]);
|
||||
var LEscErr := StringReplace(LErr, '\', '\\', [rfReplaceAll]);
|
||||
LEscErr := StringReplace(LEscErr, '"', '\"', [rfReplaceAll]);
|
||||
WebBrowser.ExecuteJavaScript(
|
||||
'if(window.Bridge&&Bridge.onFileWriteResult)' +
|
||||
'Bridge.onFileWriteResult("' + LEscReq + '",' +
|
||||
BoolToStr(LOk, True).ToLower + ',"' + LEscErr + '")');
|
||||
end;
|
||||
|
||||
procedure TMainForm.NavigateToVault;
|
||||
begin
|
||||
FPendingURL := 'http://127.0.0.1:' + FServer.BoundPort.ToString + '/index.html';
|
||||
@@ -1240,37 +1277,29 @@ begin
|
||||
// cmd://file/write?path=<full>&data=<base64>&reqId=<id>
|
||||
// Callback: Bridge.onFileWriteResult(reqId, ok, error)
|
||||
else if ACmd = 'file/write' then
|
||||
// Single-shot silent write (small payload).
|
||||
WriteDecodedFile(GetParam('path'), GetParam('data'), GetParam('reqId'))
|
||||
|
||||
// Chunked silent write: chunks accumulated via file/chunk, committed
|
||||
// here (large auto-backups exceed a single cmd:// URL otherwise).
|
||||
else if ACmd = 'file/write-commit' then
|
||||
begin
|
||||
var LPath := GetParam('path');
|
||||
var LData := GetParam('data');
|
||||
var LReqId := GetParam('reqId');
|
||||
var LOk := False;
|
||||
var LErr := '';
|
||||
try
|
||||
var LBytes := TNetEncoding.Base64.DecodeStringToBytes(LData);
|
||||
var LStream := TFileStream.Create(LPath, fmCreate);
|
||||
try
|
||||
if Length(LBytes) > 0 then
|
||||
LStream.WriteBuffer(LBytes[0], Length(LBytes));
|
||||
finally
|
||||
LStream.Free;
|
||||
end;
|
||||
LOk := True;
|
||||
LogLine(Format('File written: %s (%d bytes)', [LPath, Length(LBytes)]));
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
LErr := E.Message;
|
||||
LogLine('File write FAILED for "' + LPath + '": ' + LErr);
|
||||
end;
|
||||
var LSB: TStringBuilder;
|
||||
if FFileSaveChunks.TryGetValue(LReqId, LSB) then
|
||||
begin
|
||||
var LFull := LSB.ToString;
|
||||
LSB.Free;
|
||||
FFileSaveChunks.Remove(LReqId);
|
||||
WriteDecodedFile(GetParam('path'), LFull, LReqId);
|
||||
end
|
||||
else
|
||||
begin
|
||||
var LEscReq := StringReplace(LReqId, '"', '\"', [rfReplaceAll]);
|
||||
WebBrowser.ExecuteJavaScript(
|
||||
'if(window.Bridge&&Bridge.onFileWriteResult)' +
|
||||
'Bridge.onFileWriteResult("' + LEscReq + '",false,"no chunks buffered")');
|
||||
end;
|
||||
var LEscReq := StringReplace(LReqId, '"', '\"', [rfReplaceAll]);
|
||||
var LEscErr := StringReplace(LErr, '\', '\\', [rfReplaceAll]);
|
||||
LEscErr := StringReplace(LEscErr, '"', '\"', [rfReplaceAll]);
|
||||
WebBrowser.ExecuteJavaScript(
|
||||
'if(window.Bridge&&Bridge.onFileWriteResult)' +
|
||||
'Bridge.onFileWriteResult("' + LEscReq + '",' +
|
||||
BoolToStr(LOk, True).ToLower + ',"' + LEscErr + '")');
|
||||
end
|
||||
|
||||
// ---- Auto-backup: list files in dir matching name prefix --------------
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user