···
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 }
77
+
? { ...step, status: "pending", error: undefined, 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",
240
+
"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",
245
+
// If verification succeeds, continue to data migration
246
+
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");
300
+
"Identity migration: Verification failed, waiting for user action",
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
+
// If verification succeeds, continue to finalization
306
+
await startFinalization();
console.error("Identity migration error:", error);
···
console.log(`Verification: Step ${stepNum + 1} is ready`);
updateStepStatus(stepNum, "completed");
407
+
// Reset retry state on success
408
+
setRetryAttempts((prev) => ({ ...prev, [stepNum]: 0 }));
409
+
setShowContinueAnyway((prev) => ({ ...prev, [stepNum]: false }));
411
+
// Continue to next step if not the last one
413
+
setTimeout(() => continueToNextStep(stepNum + 1), 500);
···
data.reason || "Verification failed"
}\nStatus details: ${JSON.stringify(statusDetails, null, 2)}`;
612
-
updateStepStatus(stepNum, "error", errorMessage);
442
+
// Track retry attempts
443
+
const currentAttempts = retryAttempts[stepNum] || 0;
444
+
setRetryAttempts((prev) => ({
446
+
[stepNum]: currentAttempts + 1,
449
+
// Show continue anyway option if this is the second failure
450
+
if (currentAttempts >= 1) {
451
+
setShowContinueAnyway((prev) => ({ ...prev, [stepNum]: true }));
454
+
updateStepStatus(stepNum, "error", errorMessage, true);
console.error(`Verification: Error in step ${stepNum + 1}:`, e);
459
+
const currentAttempts = retryAttempts[stepNum] || 0;
460
+
setRetryAttempts((prev) => ({ ...prev, [stepNum]: currentAttempts + 1 }));
462
+
// Show continue anyway option if this is the second failure
463
+
if (currentAttempts >= 1) {
464
+
setShowContinueAnyway((prev) => ({ ...prev, [stepNum]: true }));
e instanceof Error ? e.message : String(e),
477
+
const retryVerification = async (stepNum: number) => {
478
+
console.log(`Retrying verification for step ${stepNum + 1}`);
479
+
await verifyStep(stepNum);
482
+
const continueAnyway = (stepNum: number) => {
483
+
console.log(`Continuing anyway for step ${stepNum + 1}`);
484
+
updateStepStatus(stepNum, "completed");
485
+
setShowContinueAnyway((prev) => ({ ...prev, [stepNum]: false }));
487
+
// Continue with next step if not the last one
489
+
continueToNextStep(stepNum + 1);
493
+
const continueToNextStep = async (stepNum: number) => {
496
+
// Continue to data migration
497
+
await startDataMigration();
500
+
// Continue to identity migration
501
+
await startIdentityMigration();
504
+
// Continue to finalization
505
+
await startFinalization();
510
+
const startDataMigration = async () => {
511
+
// Step 2: Migrate Data
512
+
updateStepStatus(1, "in-progress");
513
+
console.log("Starting data migration...");
516
+
// Step 2.1: Migrate Repo
517
+
console.log("Data migration: Starting repo migration");
518
+
const repoRes = await fetch("/api/migrate/data/repo", {
520
+
headers: { "Content-Type": "application/json" },
523
+
console.log("Repo migration: Response status:", repoRes.status);
524
+
const repoText = await repoRes.text();
525
+
console.log("Repo migration: Raw response:", repoText);
529
+
const json = JSON.parse(repoText);
530
+
console.error("Repo migration: Error response:", json);
531
+
throw new Error(json.message || "Failed to migrate repo");
533
+
console.error("Repo migration: Non-JSON error response:", repoText);
534
+
throw new Error(repoText || "Failed to migrate repo");
538
+
// Step 2.2: Migrate Blobs
539
+
console.log("Data migration: Starting blob migration");
540
+
const blobsRes = await fetch("/api/migrate/data/blobs", {
542
+
headers: { "Content-Type": "application/json" },
545
+
console.log("Blob migration: Response status:", blobsRes.status);
546
+
const blobsText = await blobsRes.text();
547
+
console.log("Blob migration: Raw response:", blobsText);
549
+
if (!blobsRes.ok) {
551
+
const json = JSON.parse(blobsText);
552
+
console.error("Blob migration: Error response:", json);
553
+
throw new Error(json.message || "Failed to migrate blobs");
556
+
"Blob migration: Non-JSON error response:",
559
+
throw new Error(blobsText || "Failed to migrate blobs");
563
+
// Step 2.3: Migrate Preferences
564
+
console.log("Data migration: Starting preferences migration");
565
+
const prefsRes = await fetch("/api/migrate/data/prefs", {
567
+
headers: { "Content-Type": "application/json" },
570
+
console.log("Preferences migration: Response status:", prefsRes.status);
571
+
const prefsText = await prefsRes.text();
572
+
console.log("Preferences migration: Raw response:", prefsText);
574
+
if (!prefsRes.ok) {
576
+
const json = JSON.parse(prefsText);
577
+
console.error("Preferences migration: Error response:", json);
578
+
throw new Error(json.message || "Failed to migrate preferences");
581
+
"Preferences migration: Non-JSON error response:",
584
+
throw new Error(prefsText || "Failed to migrate preferences");
588
+
console.log("Data migration: Starting verification");
589
+
updateStepStatus(1, "verifying");
590
+
const verified = await verifyStep(1);
591
+
console.log("Data migration: Verification result:", verified);
594
+
"Data migration: Verification failed, waiting for user action",
599
+
// If verification succeeds, continue to next step
600
+
await startIdentityMigration();
602
+
console.error("Data migration: Error caught:", error);
606
+
error instanceof Error ? error.message : String(error),
612
+
const startIdentityMigration = async () => {
613
+
// Step 3: Request Identity Migration
614
+
updateStepStatus(2, "in-progress");
615
+
console.log("Requesting identity migration...");
618
+
const requestRes = await fetch("/api/migrate/identity/request", {
620
+
headers: { "Content-Type": "application/json" },
623
+
console.log("Identity request response status:", requestRes.status);
624
+
const requestText = await requestRes.text();
625
+
console.log("Identity request response:", requestText);
627
+
if (!requestRes.ok) {
629
+
const json = JSON.parse(requestText);
631
+
json.message || "Failed to request identity migration",
635
+
requestText || "Failed to request identity migration",
641
+
const jsonData = JSON.parse(requestText);
642
+
if (!jsonData.success) {
644
+
jsonData.message || "Identity migration request failed",
647
+
console.log("Identity migration requested successfully");
649
+
// Update step name to prompt for token
650
+
setSteps((prevSteps) =>
651
+
prevSteps.map((step, i) =>
656
+
"Enter the token sent to your email to complete identity migration",
661
+
// Don't continue with migration - wait for token input
664
+
console.error("Failed to parse identity request response:", e);
666
+
"Invalid response from server during identity request",
673
+
error instanceof Error ? error.message : String(error),
679
+
const startFinalization = async () => {
680
+
// Step 4: Finalize Migration
681
+
updateStepStatus(3, "in-progress");
683
+
const finalizeRes = await fetch("/api/migrate/finalize", {
685
+
headers: { "Content-Type": "application/json" },
688
+
const finalizeData = await finalizeRes.text();
689
+
if (!finalizeRes.ok) {
691
+
const json = JSON.parse(finalizeData);
692
+
throw new Error(json.message || "Failed to finalize migration");
694
+
throw new Error(finalizeData || "Failed to finalize migration");
699
+
const jsonData = JSON.parse(finalizeData);
700
+
if (!jsonData.success) {
701
+
throw new Error(jsonData.message || "Finalization failed");
704
+
throw new Error("Invalid response from server during finalization");
707
+
updateStepStatus(3, "verifying");
708
+
const verified = await verifyStep(3);
711
+
"Finalization: Verification failed, waiting for user action",
719
+
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;
778
+
<p class="text-sm text-red-600 dark:text-red-400">
781
+
const err = JSON.parse(step.error);
782
+
return err.message || step.error;
788
+
{step.isVerificationError && (
789
+
<div class="flex space-x-2 mt-2">
792
+
onClick={() => retryVerification(index)}
793
+
class="px-3 py-1 text-xs bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors duration-200"
797
+
{showContinueAnyway[index] && (
800
+
onClick={() => continueAnyway(index)}
801
+
class="px-3 py-1 text-xs bg-yellow-600 hover:bg-yellow-700 text-white rounded transition-colors duration-200"
{index === 2 && step.status === "in-progress" &&