Graphical PDS migrator for AT Protocol

logout after migration

Changed files
+40 -7
islands
routes
api
+19 -2
islands/MigrationProgress.tsx
··· 532 532 ))} 533 533 </div> 534 534 535 - 536 - 537 535 {steps[3].status === "completed" && ( 538 536 <div class="p-4 bg-green-50 dark:bg-green-900 rounded-lg border-2 border-green-200 dark:border-green-800"> 539 537 <p class="text-sm text-green-800 dark:text-green-200"> 540 538 Migration completed successfully! You can now close this page. 541 539 </p> 540 + <button 541 + onClick={async () => { 542 + try { 543 + const response = await fetch("/api/logout", { 544 + method: "POST", 545 + credentials: "include", 546 + }); 547 + if (!response.ok) { 548 + throw new Error("Logout failed"); 549 + } 550 + globalThis.location.href = "/"; 551 + } catch (error) { 552 + console.error("Failed to logout:", error); 553 + } 554 + }} 555 + class="mt-4 px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md transition-colors duration-200" 556 + > 557 + Sign Out 558 + </button> 542 559 </div> 543 560 )} 544 561 </div>
+17 -4
islands/MigrationSetup.tsx
··· 37 37 const [confirmationText, setConfirmationText] = useState(""); 38 38 const [passport, setPassport] = useState<UserPassport | null>(null); 39 39 40 + const ensureServiceUrl = (url: string): string => { 41 + if (!url) return url; 42 + try { 43 + // If it already has a protocol, return as is 44 + new URL(url); 45 + return url; 46 + } catch { 47 + // If no protocol, add https:// 48 + return `https://${url}`; 49 + } 50 + }; 51 + 40 52 useEffect(() => { 41 53 if (!IS_BROWSER) return; 42 54 ··· 100 112 }; 101 113 102 114 const handleServiceChange = (value: string) => { 103 - setService(value); 115 + const urlWithProtocol = ensureServiceUrl(value); 116 + setService(urlWithProtocol); 104 117 setError(""); 105 - if (value) { 106 - checkServerDescription(value); 118 + if (urlWithProtocol) { 119 + checkServerDescription(urlWithProtocol); 107 120 } else { 108 121 setAvailableDomains([]); 109 122 setSelectedDomain(""); ··· 384 397 <div class="text-center mb-4 mt-6"> 385 398 <h3 class="text-2xl font-bold text-red-600 mb-2 tracking-wide">Final Boarding Call</h3> 386 399 <p class="text-gray-700 dark:text-gray-300 mb-2 text-base"> 387 - <span class="font-semibold text-red-500">Warning:</span> This migration process can be <strong>irreversible</strong>.<br />Airport is in <strong>alpha</strong> currently, and we don't recommend it for main accounts. 400 + <span class="font-semibold text-red-500">Warning:</span> This migration process can be <strong>irreversible</strong>.<br />Airport is in <strong>alpha</strong> currently, and we don't recommend it for main accounts. Migrate at your own risk. We reccomend backing up your data before proceeding. 388 401 </p> 389 402 <p class="text-gray-700 dark:text-gray-300 mb-4 text-base"> 390 403 Please type <span class="font-mono font-bold text-blue-600">MIGRATE</span> below to confirm and proceed.
+4 -1
routes/api/logout.ts
··· 1 - import { getSession } from "../../lib/sessions.ts"; 1 + import { getSession, destroyAllSessions } from "../../lib/sessions.ts"; 2 2 import { oauthClient } from "../../lib/oauth/client.ts"; 3 3 import { define } from "../../utils.ts"; 4 4 ··· 18 18 // Then destroy the iron session 19 19 session.destroy(); 20 20 } 21 + 22 + // Destroy all sessions including migration session 23 + await destroyAllSessions(req); 21 24 22 25 return response; 23 26 } catch (error: unknown) {