···
status: "pending" | "in-progress" | "verifying" | "completed" | "error";
33
+
isVerificationError?: boolean;
···
const [migrationState, setMigrationState] = useState<
MigrationStateInfo | null
47
+
const [retryAttempts, setRetryAttempts] = useState<Record<number, number>>(
50
+
const [showContinueAnyway, setShowContinueAnyway] = useState<
51
+
Record<number, boolean>
const [steps, setSteps] = useState<MigrationStep[]>([
{ name: "Create Account", status: "pending" },
···
status: MigrationStep["status"],
65
+
isVerificationError?: boolean,
`Updating step ${index} to ${status}${
···
prevSteps.map((step, i) =>
67
-
? { ...step, status, error }
75
+
? { ...step, status, error, isVerificationError }
69
-
? { ...step, status: "pending", error: undefined }
81
+
isVerificationError: undefined,
···
updateStepStatus(0, "verifying");
const verified = await verifyStep(0);
231
-
throw new Error("Account creation verification failed");
237
-
error instanceof Error ? error.message : String(error),
242
-
// Step 2: Migrate Data
243
-
updateStepStatus(1, "in-progress");
244
-
console.log("Starting data migration...");
247
-
// Step 2.1: Migrate Repo
248
-
console.log("Data migration: Starting repo migration");
249
-
const repoRes = await fetch("/api/migrate/data/repo", {
251
-
headers: { "Content-Type": "application/json" },
254
-
console.log("Repo migration: Response status:", repoRes.status);
255
-
const repoText = await repoRes.text();
256
-
console.log("Repo migration: Raw response:", repoText);
260
-
const json = JSON.parse(repoText);
261
-
console.error("Repo migration: Error response:", json);
262
-
throw new Error(json.message || "Failed to migrate repo");
264
-
console.error("Repo migration: Non-JSON error response:", repoText);
265
-
throw new Error(repoText || "Failed to migrate repo");
269
-
// Step 2.2: Migrate Blobs
270
-
console.log("Data migration: Starting blob migration");
271
-
const blobsRes = await fetch("/api/migrate/data/blobs", {
273
-
headers: { "Content-Type": "application/json" },
276
-
console.log("Blob migration: Response status:", blobsRes.status);
277
-
const blobsText = await blobsRes.text();
278
-
console.log("Blob migration: Raw response:", blobsText);
280
-
if (!blobsRes.ok) {
282
-
const json = JSON.parse(blobsText);
283
-
console.error("Blob migration: Error response:", json);
284
-
throw new Error(json.message || "Failed to migrate blobs");
287
-
"Blob migration: Non-JSON error response:",
290
-
throw new Error(blobsText || "Failed to migrate blobs");
294
-
// Step 2.3: Migrate Preferences
295
-
console.log("Data migration: Starting preferences migration");
296
-
const prefsRes = await fetch("/api/migrate/data/prefs", {
298
-
headers: { "Content-Type": "application/json" },
301
-
console.log("Preferences migration: Response status:", prefsRes.status);
302
-
const prefsText = await prefsRes.text();
303
-
console.log("Preferences migration: Raw response:", prefsText);
305
-
if (!prefsRes.ok) {
307
-
const json = JSON.parse(prefsText);
308
-
console.error("Preferences migration: Error response:", json);
309
-
throw new Error(json.message || "Failed to migrate preferences");
312
-
"Preferences migration: Non-JSON error response:",
315
-
throw new Error(prefsText || "Failed to migrate preferences");
319
-
console.log("Data migration: Starting verification");
320
-
updateStepStatus(1, "verifying");
321
-
const verified = await verifyStep(1);
322
-
console.log("Data migration: Verification result:", verified);
324
-
throw new Error("Data migration verification failed");
327
-
console.error("Data migration: Error caught:", error);
331
-
error instanceof Error ? error.message : String(error),
336
-
// Step 3: Request Identity Migration
337
-
updateStepStatus(2, "in-progress");
338
-
console.log("Requesting identity migration...");
341
-
const requestRes = await fetch("/api/migrate/identity/request", {
343
-
headers: { "Content-Type": "application/json" },
346
-
console.log("Identity request response status:", requestRes.status);
347
-
const requestText = await requestRes.text();
348
-
console.log("Identity request response:", requestText);
350
-
if (!requestRes.ok) {
352
-
const json = JSON.parse(requestText);
354
-
json.message || "Failed to request identity migration",
358
-
requestText || "Failed to request identity migration",
364
-
const jsonData = JSON.parse(requestText);
365
-
if (!jsonData.success) {
367
-
jsonData.message || "Identity migration request failed",
370
-
console.log("Identity migration requested successfully");
372
-
// Update step name to prompt for token
373
-
setSteps((prevSteps) =>
374
-
prevSteps.map((step, i) =>
379
-
"Enter the token sent to your email to complete identity migration",
245
+
"Account creation: Verification failed, waiting for user action",
384
-
// Don't continue with migration - wait for token input
387
-
console.error("Failed to parse identity request response:", e);
389
-
"Invalid response from server during identity request",
250
+
// If verification succeeds, continue to data migration
251
+
await startDataMigration();
error instanceof Error ? error.message : String(error),
···
updateStepStatus(2, "verifying");
const verified = await verifyStep(2);
444
-
throw new Error("Identity migration verification failed");
447
-
// Step 4: Finalize Migration
448
-
updateStepStatus(3, "in-progress");
450
-
const finalizeRes = await fetch("/api/migrate/finalize", {
452
-
headers: { "Content-Type": "application/json" },
455
-
const finalizeData = await finalizeRes.text();
456
-
if (!finalizeRes.ok) {
458
-
const json = JSON.parse(finalizeData);
459
-
throw new Error(json.message || "Failed to finalize migration");
461
-
throw new Error(finalizeData || "Failed to finalize migration");
466
-
const jsonData = JSON.parse(finalizeData);
467
-
if (!jsonData.success) {
468
-
throw new Error(jsonData.message || "Finalization failed");
471
-
throw new Error("Invalid response from server during finalization");
474
-
updateStepStatus(3, "verifying");
475
-
const verified = await verifyStep(3);
477
-
throw new Error("Migration finalization verification failed");
483
-
error instanceof Error ? error.message : String(error),
305
+
"Identity migration: Verification failed, waiting for user action",
310
+
// If verification succeeds, continue to finalization
311
+
await startFinalization();
console.error("Identity migration error:", error);
···
console.log(`Verification: Step ${stepNum + 1} is ready`);
updateStepStatus(stepNum, "completed");
412
+
// Reset retry state on success
413
+
setRetryAttempts((prev) => ({ ...prev, [stepNum]: 0 }));
414
+
setShowContinueAnyway((prev) => ({ ...prev, [stepNum]: false }));
416
+
// Continue to next step if not the last one
418
+
setTimeout(() => continueToNextStep(stepNum + 1), 500);
···
data.reason || "Verification failed"
}\nStatus details: ${JSON.stringify(statusDetails, null, 2)}`;
612
-
updateStepStatus(stepNum, "error", errorMessage);
447
+
// Track retry attempts
448
+
const currentAttempts = retryAttempts[stepNum] || 0;
449
+
setRetryAttempts((prev) => ({
451
+
[stepNum]: currentAttempts + 1,
454
+
// Show continue anyway option if this is the second failure
455
+
if (currentAttempts >= 1) {
456
+
setShowContinueAnyway((prev) => ({ ...prev, [stepNum]: true }));
459
+
updateStepStatus(stepNum, "error", errorMessage, true);
console.error(`Verification: Error in step ${stepNum + 1}:`, e);
464
+
const currentAttempts = retryAttempts[stepNum] || 0;
465
+
setRetryAttempts((prev) => ({ ...prev, [stepNum]: currentAttempts + 1 }));
467
+
// Show continue anyway option if this is the second failure
468
+
if (currentAttempts >= 1) {
469
+
setShowContinueAnyway((prev) => ({ ...prev, [stepNum]: true }));
e instanceof Error ? e.message : String(e),
482
+
const retryVerification = async (stepNum: number) => {
483
+
console.log(`Retrying verification for step ${stepNum + 1}`);
484
+
await verifyStep(stepNum);
487
+
const continueAnyway = (stepNum: number) => {
488
+
console.log(`Continuing anyway for step ${stepNum + 1}`);
489
+
updateStepStatus(stepNum, "completed");
490
+
setShowContinueAnyway((prev) => ({ ...prev, [stepNum]: false }));
492
+
// Continue with next step if not the last one
494
+
continueToNextStep(stepNum + 1);
498
+
const continueToNextStep = async (stepNum: number) => {
501
+
// Continue to data migration
502
+
await startDataMigration();
505
+
// Continue to identity migration
506
+
await startIdentityMigration();
509
+
// Continue to finalization
510
+
await startFinalization();
515
+
const startDataMigration = async () => {
516
+
// Step 2: Migrate Data
517
+
updateStepStatus(1, "in-progress");
518
+
console.log("Starting data migration...");
521
+
// Step 2.1: Migrate Repo
522
+
console.log("Data migration: Starting repo migration");
523
+
const repoRes = await fetch("/api/migrate/data/repo", {
525
+
headers: { "Content-Type": "application/json" },
528
+
console.log("Repo migration: Response status:", repoRes.status);
529
+
const repoText = await repoRes.text();
530
+
console.log("Repo migration: Raw response:", repoText);
534
+
const json = JSON.parse(repoText);
535
+
console.error("Repo migration: Error response:", json);
536
+
throw new Error(json.message || "Failed to migrate repo");
538
+
console.error("Repo migration: Non-JSON error response:", repoText);
539
+
throw new Error(repoText || "Failed to migrate repo");
543
+
// Step 2.2: Migrate Blobs
544
+
console.log("Data migration: Starting blob migration");
545
+
const blobsRes = await fetch("/api/migrate/data/blobs", {
547
+
headers: { "Content-Type": "application/json" },
550
+
console.log("Blob migration: Response status:", blobsRes.status);
551
+
const blobsText = await blobsRes.text();
552
+
console.log("Blob migration: Raw response:", blobsText);
554
+
if (!blobsRes.ok) {
556
+
const json = JSON.parse(blobsText);
557
+
console.error("Blob migration: Error response:", json);
558
+
throw new Error(json.message || "Failed to migrate blobs");
561
+
"Blob migration: Non-JSON error response:",
564
+
throw new Error(blobsText || "Failed to migrate blobs");
568
+
// Step 2.3: Migrate Preferences
569
+
console.log("Data migration: Starting preferences migration");
570
+
const prefsRes = await fetch("/api/migrate/data/prefs", {
572
+
headers: { "Content-Type": "application/json" },
575
+
console.log("Preferences migration: Response status:", prefsRes.status);
576
+
const prefsText = await prefsRes.text();
577
+
console.log("Preferences migration: Raw response:", prefsText);
579
+
if (!prefsRes.ok) {
581
+
const json = JSON.parse(prefsText);
582
+
console.error("Preferences migration: Error response:", json);
583
+
throw new Error(json.message || "Failed to migrate preferences");
586
+
"Preferences migration: Non-JSON error response:",
589
+
throw new Error(prefsText || "Failed to migrate preferences");
593
+
console.log("Data migration: Starting verification");
594
+
updateStepStatus(1, "verifying");
595
+
const verified = await verifyStep(1);
596
+
console.log("Data migration: Verification result:", verified);
599
+
"Data migration: Verification failed, waiting for user action",
604
+
// If verification succeeds, continue to next step
605
+
await startIdentityMigration();
607
+
console.error("Data migration: Error caught:", error);
611
+
error instanceof Error ? error.message : String(error),
617
+
const startIdentityMigration = async () => {
618
+
// Step 3: Request Identity Migration
619
+
updateStepStatus(2, "in-progress");
620
+
console.log("Requesting identity migration...");
623
+
const requestRes = await fetch("/api/migrate/identity/request", {
625
+
headers: { "Content-Type": "application/json" },
628
+
console.log("Identity request response status:", requestRes.status);
629
+
const requestText = await requestRes.text();
630
+
console.log("Identity request response:", requestText);
632
+
if (!requestRes.ok) {
634
+
const json = JSON.parse(requestText);
636
+
json.message || "Failed to request identity migration",
640
+
requestText || "Failed to request identity migration",
646
+
const jsonData = JSON.parse(requestText);
647
+
if (!jsonData.success) {
649
+
jsonData.message || "Identity migration request failed",
652
+
console.log("Identity migration requested successfully");
654
+
// Update step name to prompt for token
655
+
setSteps((prevSteps) =>
656
+
prevSteps.map((step, i) =>
661
+
"Enter the token sent to your email to complete identity migration",
666
+
// Don't continue with migration - wait for token input
669
+
console.error("Failed to parse identity request response:", e);
671
+
"Invalid response from server during identity request",
678
+
error instanceof Error ? error.message : String(error),
684
+
const startFinalization = async () => {
685
+
// Step 4: Finalize Migration
686
+
updateStepStatus(3, "in-progress");
688
+
const finalizeRes = await fetch("/api/migrate/finalize", {
690
+
headers: { "Content-Type": "application/json" },
693
+
const finalizeData = await finalizeRes.text();
694
+
if (!finalizeRes.ok) {
696
+
const json = JSON.parse(finalizeData);
697
+
throw new Error(json.message || "Failed to finalize migration");
699
+
throw new Error(finalizeData || "Failed to finalize migration");
704
+
const jsonData = JSON.parse(finalizeData);
705
+
if (!jsonData.success) {
706
+
throw new Error(jsonData.message || "Finalization failed");
709
+
throw new Error("Invalid response from server during finalization");
712
+
updateStepStatus(3, "verifying");
713
+
const verified = await verifyStep(3);
716
+
"Finalization: Verification failed, waiting for user action",
724
+
error instanceof Error ? error.message : String(error),
{/* Migration state alert */}
···
{getStepDisplayName(step, index)}
678
-
<p class="text-sm text-red-600 dark:text-red-400 mt-1">
681
-
const err = JSON.parse(step.error);
682
-
return err.message || step.error;
783
+
<p class="text-sm text-red-600 dark:text-red-400">
786
+
const err = JSON.parse(step.error);
787
+
return err.message || step.error;
793
+
{step.isVerificationError && (
794
+
<div class="flex space-x-2 mt-2">
797
+
onClick={() => retryVerification(index)}
798
+
class="px-3 py-1 text-xs bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors duration-200 dark:bg-blue-500 dark:hover:bg-blue-400"
802
+
{showContinueAnyway[index] && (
805
+
onClick={() => continueAnyway(index)}
806
+
class="px-3 py-1 text-xs bg-white border border-gray-300 text-gray-700 hover:bg-gray-100 rounded transition-colors duration-200
807
+
dark:bg-gray-800 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-gray-700"
{index === 2 && step.status === "in-progress" &&