Skip to content

Commit

Permalink
prevent workspace from re-importing projects on db reset (#10261)
Browse files Browse the repository at this point in the history
* prevent workspace from re-importing projects on db reset

* handle clients from before this fix
  • Loading branch information
riknoll authored Nov 11, 2024
1 parent 1e4e5e8 commit 8c3820e
Showing 1 changed file with 61 additions and 12 deletions.
73 changes: 61 additions & 12 deletions webapp/src/idbworkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,36 +204,57 @@ async function migrateOldIndexedDbAsync() {
await pxt.BrowserUtils.clearTutorialInfoDbAsync();
});

const previousDbPrefix = "legacy";
const currentDbPrefix = getCurrentDBPrefix() || "default";

try {
await legacyDb.openAsync();
const currentDb = await getCurrentDbAsync();

await copyTableEntriesAsync(legacyDb, currentDb, HEADERS_TABLE, true);
await copyTableEntriesAsync(legacyDb, currentDb, TEXTS_TABLE, true);
await copyTableEntriesAsync(legacyDb, currentDb, HEADERS_TABLE, true, previousDbPrefix, currentDbPrefix);
await copyTableEntriesAsync(legacyDb, currentDb, TEXTS_TABLE, true, previousDbPrefix, currentDbPrefix);
} catch (e) {
pxt.reportException(e);
}
}

async function migratePrefixesAsync() {
if (!getCurrentDBPrefix()) return;
const currentDbPrefix = getCurrentDBPrefix();
if (!currentDbPrefix) return;

const currentVersion = pxt.semver.parse(pxt.appTarget.versions.target);
const currentMajor = currentVersion.major;
const previousMajor = currentMajor - 1;
const previousDbPrefix = previousMajor < 0 ? "" : pxt.appTarget.appTheme.browserDbPrefixes[previousMajor];
const currentDb = await getCurrentDbAsync();
const migrationDb = await getMigrationDbAsync();
const dummyEntryKey = migrationDbPrefixUpgradeKey(previousDbPrefix, currentDbPrefix, "dummy");

// If headers are already in the new db, migration must have already happened
if ((await currentDb.getAllAsync(HEADERS_TABLE)).length) return;

const prevDb = await getDbAsync(previousDbPrefix);
if ((await currentDb.getAllAsync(HEADERS_TABLE)).length) {
// Check to see if we've populated the migration db. This only applies to older clients
// from before we started tracking prefix upgrades in the migration db. Populating this db
// should be a one-time operation and is necessary to ensure that reset works correctly
// in browsers that loaded the page sometime before the migration fix was released.
if (await migrationDb.getAsync(HEADERS_TABLE, dummyEntryKey)) return;

const prevDb = await getDbAsync(previousDbPrefix);
await populatePrefixMigrationDb(prevDb, currentDb, HEADERS_TABLE, previousDbPrefix, currentDbPrefix);
await populatePrefixMigrationDb(prevDb, currentDb, TEXTS_TABLE, previousDbPrefix, currentDbPrefix);
}
else {
// Copy everything over to the current db
const prevDb = await getDbAsync(previousDbPrefix);

await copyTableEntriesAsync(prevDb, currentDb, HEADERS_TABLE, false, previousDbPrefix, currentDbPrefix);
await copyTableEntriesAsync(prevDb, currentDb, TEXTS_TABLE, false, previousDbPrefix, currentDbPrefix);
await copyTableEntriesAsync(prevDb, currentDb, SCRIPT_TABLE, false, previousDbPrefix, currentDbPrefix);
await copyTableEntriesAsync(prevDb, currentDb, HOSTCACHE_TABLE, false, previousDbPrefix, currentDbPrefix);
await copyTableEntriesAsync(prevDb, currentDb, GITHUB_TABLE, false, previousDbPrefix, currentDbPrefix);
}

await copyTableEntriesAsync(prevDb, currentDb, HEADERS_TABLE, false);
await copyTableEntriesAsync(prevDb, currentDb, TEXTS_TABLE, false);
await copyTableEntriesAsync(prevDb, currentDb, SCRIPT_TABLE, false);
await copyTableEntriesAsync(prevDb, currentDb, HOSTCACHE_TABLE, false);
await copyTableEntriesAsync(prevDb, currentDb, GITHUB_TABLE, false);
// Stick a dummy marker in the migration db to indicate that we did migrate everything
await migrationDb.setAsync(HEADERS_TABLE, { id: dummyEntryKey });
}

let _dbPromises: pxt.Map<Promise<pxt.BrowserUtils.IDBWrapper>> = {};
Expand Down Expand Up @@ -269,12 +290,36 @@ async function getDbAsync(prefix = "__default") {
}
}

async function copyTableEntriesAsync(fromDb: pxt.BrowserUtils.IDBWrapper, toDb: pxt.BrowserUtils.IDBWrapper, storeName: string, dontOverwrite: boolean) {
async function copyTableEntriesAsync(fromDb: pxt.BrowserUtils.IDBWrapper, toDb: pxt.BrowserUtils.IDBWrapper, storeName: string, dontOverwrite: boolean, fromPrefix: string, toPrefix: string) {
const migrationDb = await getMigrationDbAsync();

for (const entry of await fromDb.getAllAsync<any>(storeName)) {
const key = migrationDbPrefixUpgradeKey(fromPrefix, toPrefix, entry.id);
if (await migrationDb.getAsync(storeName, key)) continue;

const existing = dontOverwrite && !!(await toDb.getAsync(storeName, entry.id));

if (!existing) {
await toDb.setAsync(storeName, entry);
await migrationDb.setAsync(storeName, {
id: key
});
}
}
}

async function populatePrefixMigrationDb(fromDb: pxt.BrowserUtils.IDBWrapper, toDb: pxt.BrowserUtils.IDBWrapper, storeName: string, fromPrefix: string, toPrefix: string) {
const migrationDb = await getMigrationDbAsync();

for (const entry of await fromDb.getAllAsync<any>(storeName)) {
const key = migrationDbPrefixUpgradeKey(fromPrefix, toPrefix, entry.id);
if (await migrationDb.getAsync(storeName, key)) continue;

// If this header id was actually migrated, add an entry for it in the migration db
if (await toDb.getAsync(storeName, entry.id)) {
await migrationDb.setAsync(storeName, {
id: key
});
}
}
}
Expand Down Expand Up @@ -719,6 +764,10 @@ function migrationDbKey(prefix: string, id: string) {
return `${prefix}--${id}`;
};

function migrationDbPrefixUpgradeKey(oldPrefix: string, newPrefix: string, id: string) {
return `${oldPrefix}--${newPrefix}--${id}`;
}

export const provider: WorkspaceProvider = {
getAsync,
setAsync,
Expand Down

0 comments on commit 8c3820e

Please sign in to comment.