Prevent empty-browser auto-sync from clobbering Gist data
On a fresh browser install with auto-sync enabled, performAutoSync would push because config.lastPush was null (!config.lastPush = true). With _getAllData now returning lastModified: 0 for browsers with no saved data, this pushed a payload with lastModified: 0 to the Gist, overwriting the real data from the source browser. Subsequent pulls then saw remoteMod (0) <= local (0) and returned "Already up to date" without ever prompting for a password or importing anything. Three fixes: 1. performAutoSync: add local.lastModified > 0 guard to the push condition so a browser with no saved data never pushes in the background (both Gist and URL paths). 2. pushToGist / pushToUrl: return an explicit error if lastModified is 0, preventing a manual Push click on a fresh browser from clobbering the Gist too. 3. pullFromGist: if the Gist's lastModified is 0 (already clobbered), return a clear error message telling the user to push from the source browser first, rather than silently returning "up to date". https://claude.ai/code/session_01QJnEnLfbXKR5FSCQ3Qfs53
This commit is contained in:
+21
-6
@@ -709,6 +709,12 @@ const SilentSendSync = {
|
|||||||
try {
|
try {
|
||||||
const data = await this._getAllData();
|
const data = await this._getAllData();
|
||||||
|
|
||||||
|
// Don't push if this browser has no saved data — it would overwrite
|
||||||
|
// real data on the Gist with an empty payload.
|
||||||
|
if (data.lastModified === 0) {
|
||||||
|
return { success: false, reason: 'Nothing to push — no data has been saved on this browser yet. Pull first.' };
|
||||||
|
}
|
||||||
|
|
||||||
// Encrypt if enabled
|
// Encrypt if enabled
|
||||||
const encResult = await this._encryptForSync(data);
|
const encResult = await this._encryptForSync(data);
|
||||||
if (encResult.needsAuth) {
|
if (encResult.needsAuth) {
|
||||||
@@ -804,9 +810,14 @@ const SilentSendSync = {
|
|||||||
const rawResp = await fetch(file.raw_url);
|
const rawResp = await fetch(file.raw_url);
|
||||||
let data = JSON.parse(await rawResp.text());
|
let data = JSON.parse(await rawResp.text());
|
||||||
|
|
||||||
// Check if new data exists before requiring auth
|
// Check if new data exists before requiring auth.
|
||||||
|
// remoteMod === 0 means the Gist was clobbered by an empty push — treat
|
||||||
|
// as stale rather than skipping so the user sees a useful error.
|
||||||
const local = await this._getAllData();
|
const local = await this._getAllData();
|
||||||
const remoteMod = data._ssEncrypted ? data.lastModified : data.lastModified;
|
const remoteMod = data.lastModified || 0;
|
||||||
|
if (remoteMod === 0) {
|
||||||
|
return { success: false, reason: 'Gist contains no data (timestamp is 0). Push from the source browser first.' };
|
||||||
|
}
|
||||||
if (remoteMod <= (local.lastModified || 0)) {
|
if (remoteMod <= (local.lastModified || 0)) {
|
||||||
return { success: true, imported: false };
|
return { success: true, imported: false };
|
||||||
}
|
}
|
||||||
@@ -839,6 +850,10 @@ const SilentSendSync = {
|
|||||||
try {
|
try {
|
||||||
const data = await this._getAllData();
|
const data = await this._getAllData();
|
||||||
|
|
||||||
|
if (data.lastModified === 0) {
|
||||||
|
return { success: false, reason: 'Nothing to push — no data has been saved on this browser yet. Pull first.' };
|
||||||
|
}
|
||||||
|
|
||||||
const encResult = await this._encryptForSync(data);
|
const encResult = await this._encryptForSync(data);
|
||||||
if (encResult.needsAuth) {
|
if (encResult.needsAuth) {
|
||||||
return { success: false, needsAuth: true, reason: 'Authentication required.' };
|
return { success: false, needsAuth: true, reason: 'Authentication required.' };
|
||||||
@@ -1032,9 +1047,9 @@ const SilentSendSync = {
|
|||||||
const pullResult = await this.pullFromGist(config.gistToken);
|
const pullResult = await this.pullFromGist(config.gistToken);
|
||||||
if (pullResult.success && pullResult.imported) pulled = true;
|
if (pullResult.success && pullResult.imported) pulled = true;
|
||||||
|
|
||||||
// Push if local data changed since last push
|
// Push if local data changed since last push (skip if no real data yet)
|
||||||
const local = await this._getAllData();
|
const local = await this._getAllData();
|
||||||
if (!config.lastPush || local.lastModified > config.lastPush) {
|
if (local.lastModified > 0 && (!config.lastPush || local.lastModified > config.lastPush)) {
|
||||||
const pushResult = await this.pushToGist(config.gistToken);
|
const pushResult = await this.pushToGist(config.gistToken);
|
||||||
if (pushResult.success) {
|
if (pushResult.success) {
|
||||||
pushed = true;
|
pushed = true;
|
||||||
@@ -1047,9 +1062,9 @@ const SilentSendSync = {
|
|||||||
const pullResult = await this.pullFromUrl({ url: config.url, headers });
|
const pullResult = await this.pullFromUrl({ url: config.url, headers });
|
||||||
if (pullResult.success && pullResult.imported) pulled = true;
|
if (pullResult.success && pullResult.imported) pulled = true;
|
||||||
|
|
||||||
// Push if local data changed since last push
|
// Push if local data changed since last push (skip if no real data yet)
|
||||||
const local = await this._getAllData();
|
const local = await this._getAllData();
|
||||||
if (!config.lastPush || local.lastModified > config.lastPush) {
|
if (local.lastModified > 0 && (!config.lastPush || local.lastModified > config.lastPush)) {
|
||||||
const pushResult = await this.pushToUrl({
|
const pushResult = await this.pushToUrl({
|
||||||
url: config.url,
|
url: config.url,
|
||||||
method: config.httpMethod || 'PUT',
|
method: config.httpMethod || 'PUT',
|
||||||
|
|||||||
Reference in New Issue
Block a user