Graphical PDS migrator for AT Protocol

fix: migrations failures due to duplicate PLC signature requests (#8)

* fix: allow dids as handle

* Fix duplicate identity migration requests and prevent auto-verification of step 2

- Remove redundant direct calls to next migration steps after verification
- Add duplicate call prevention in startIdentityMigration
- Skip automatic verification for identity migration step (requires manual token)
- Simplify control flow to use only verifyStep->continueToNextStep chain
- Fix issue where identity migration was being called multiple times causing errors

* Add proper verification after identity migration token submission

- Call /api/migrate/status after submitting identity token to verify success
- Add isManualSubmission parameter to verifyStep to allow verification for step 2 after token
- Update retryVerification to handle identity migration verification correctly
- Ensure identity migration is properly verified before continuing to finalization

Turtlepaw 51920ada 4d62ca70

Changed files
+39 -17
islands
routes
api
cred
+30 -11
islands/MigrationProgress.tsx
···
return;
}
-
// If verification succeeds, continue to data migration
-
await startDataMigration();
+
// verifyStep will handle continuing to the next step via continueToNextStep
+
// No need to call startDataMigration here
} catch (error) {
updateStepStatus(
0,
···
throw new Error("Invalid response from server");
}
+
// Verify the identity migration succeeded
updateStepStatus(2, "verifying");
-
const verified = await verifyStep(2);
+
const verified = await verifyStep(2, true); // Pass true to allow verification for manual submission
if (!verified) {
console.log(
-
"Identity migration: Verification failed, waiting for user action",
+
"Identity migration: Verification failed after token submission",
);
return;
}
-
-
// If verification succeeds, continue to finalization
+
+
// If verification succeeds, mark as completed and continue
+
updateStepStatus(2, "completed");
await startFinalization();
} catch (error) {
console.error("Identity migration error:", error);
···
};
// Helper to verify a step after completion
-
const verifyStep = async (stepNum: number) => {
+
const verifyStep = async (stepNum: number, isManualSubmission: boolean = false) => {
console.log(`Verification: Starting step ${stepNum + 1}`);
+
+
// Skip automatic verification for step 2 (identity migration) unless it's after manual token submission
+
if (stepNum === 2 && !isManualSubmission) {
+
console.log(`Verification: Skipping automatic verification for identity migration step`);
+
return false;
+
}
+
updateStepStatus(stepNum, "verifying");
try {
console.log(`Verification: Fetching status for step ${stepNum + 1}`);
···
const retryVerification = async (stepNum: number) => {
console.log(`Retrying verification for step ${stepNum + 1}`);
-
await verifyStep(stepNum);
+
// For identity migration step, pass true if it's after manual submission
+
const isManualSubmission = stepNum === 2 && steps[2].name === "Enter the token sent to your email to complete identity migration";
+
await verifyStep(stepNum, isManualSubmission);
};
const continueAnyway = (stepNum: number) => {
···
return;
}
-
// If verification succeeds, continue to next step
-
await startIdentityMigration();
+
// verifyStep will handle continuing to the next step via continueToNextStep
+
// No need to call startIdentityMigration here
} catch (error) {
console.error("Data migration: Error caught:", error);
updateStepStatus(
···
const startIdentityMigration = async () => {
// Step 3: Request Identity Migration
+
// Check if already in progress to prevent duplicate calls
+
if (steps[2].status === "in-progress" || steps[2].status === "completed") {
+
console.log("Identity migration already in progress or completed, skipping duplicate call");
+
return;
+
}
+
updateStepStatus(2, "in-progress");
console.log("Requesting identity migration...");
···
: step
)
);
-
// Don't continue with migration - wait for token input
+
// Don't verify or continue - wait for token input
+
// Skip automatic verification for identity migration step
+
console.log("Identity migration: Waiting for user token input, skipping auto-verification");
return;
} catch (e) {
console.error("Failed to parse identity request response:", e);
+9 -6
routes/api/cred/login.ts
···
{
status: 400,
headers: { "Content-Type": "application/json" },
-
},
+
}
);
}
console.log("Resolving handle:", handle);
-
const did = await resolver.resolveHandleToDid(handle);
+
const did =
+
typeof handle == "string" && handle.startsWith("did:")
+
? handle
+
: await resolver.resolveHandleToDid(handle);
const service = await resolver.resolveDidToPdsUrl(did);
console.log("Resolved service:", service);
···
}),
{
status: 400,
-
},
+
}
);
}
···
{
status: 200,
headers: { "Content-Type": "application/json" },
-
},
+
}
);
// Create and save our client session with tokens
···
{
status: 401,
headers: { "Content-Type": "application/json" },
-
},
+
}
);
}
} catch (error) {
···
{
status: 500,
headers: { "Content-Type": "application/json" },
-
},
+
}
);
}
},