Graphical PDS migrator for AT Protocol
at main 4.8 kB view raw
1import { getSessionAgent } from "../../../lib/sessions.ts"; 2import { define } from "../../../utils.ts"; 3import * as plc from "@did-plc/lib"; 4 5/** 6 * Handle PLC update operation 7 * Body must contain: 8 * - key: The new rotation key to add 9 * - token: The email token received from requestPlcOperationSignature 10 * @param ctx - The context object containing the request and response 11 * @returns A response object with the update result 12 */ 13export const handler = define.handlers({ 14 async POST(ctx) { 15 const res = new Response(); 16 try { 17 console.log("=== PLC Update Debug ==="); 18 const body = await ctx.req.json(); 19 const { key: newKey, token } = body; 20 console.log("Request body:", { newKey, hasToken: !!token }); 21 22 if (!newKey) { 23 console.log("Missing key in request"); 24 return new Response("Missing param key in request body", { 25 status: 400, 26 }); 27 } 28 29 if (!token) { 30 console.log("Missing token in request"); 31 return new Response("Missing param token in request body", { 32 status: 400, 33 }); 34 } 35 36 const agent = await getSessionAgent(ctx.req, res); 37 if (!agent) { 38 console.log("No agent found"); 39 return new Response("Unauthorized", { status: 401 }); 40 } 41 42 const session = await agent.com.atproto.server.getSession(); 43 const did = session.data.did; 44 if (!did) { 45 console.log("No DID found in session"); 46 return new Response( 47 JSON.stringify({ 48 success: false, 49 message: "No DID found in your session", 50 }), 51 { 52 status: 400, 53 headers: { "Content-Type": "application/json" }, 54 }, 55 ); 56 } 57 console.log("Using agent DID:", did); 58 59 // Get recommended credentials first 60 console.log("Getting did:plc document..."); 61 const plcClient = new plc.Client("https://plc.directory"); 62 const didDoc = await plcClient.getDocumentData(did); 63 if (!didDoc) { 64 console.log("No DID document found for agent DID"); 65 return new Response( 66 JSON.stringify({ 67 success: false, 68 message: "No DID document found for your account", 69 }), 70 { 71 status: 400, 72 headers: { "Content-Type": "application/json" }, 73 }, 74 ); 75 } 76 console.log("Got DID document:", didDoc); 77 78 const rotationKeys = didDoc.rotationKeys ?? []; 79 if (!rotationKeys.length) { 80 console.log("No existing rotation keys found"); 81 throw new Error("No rotation keys provided in recommended credentials"); 82 } 83 84 // Check if the key is already in rotation keys 85 if (rotationKeys.includes(newKey)) { 86 console.log("Key already exists in rotation keys"); 87 return new Response( 88 JSON.stringify({ 89 success: false, 90 message: "This key is already in your rotation keys", 91 }), 92 { 93 status: 400, 94 headers: { "Content-Type": "application/json" }, 95 }, 96 ); 97 } 98 99 // Perform the actual PLC update with the provided token 100 console.log("Signing PLC operation..."); 101 const plcOp = await agent.com.atproto.identity.signPlcOperation({ 102 token, 103 rotationKeys: [newKey, ...rotationKeys], 104 }); 105 console.log("PLC operation signed successfully:", plcOp.data); 106 107 console.log("Submitting PLC operation..."); 108 const plcSubmit = await agent.com.atproto.identity.submitPlcOperation({ 109 operation: plcOp.data.operation, 110 }); 111 console.log("PLC operation submitted successfully:", plcSubmit); 112 113 return new Response( 114 JSON.stringify({ 115 success: true, 116 message: "PLC update completed successfully", 117 did: plcOp.data, 118 newKey, 119 rotationKeys: [newKey, ...rotationKeys], 120 }), 121 { 122 status: 200, 123 headers: { 124 "Content-Type": "application/json", 125 ...Object.fromEntries(res.headers), // Include session cookie headers 126 }, 127 }, 128 ); 129 } catch (error) { 130 console.error("PLC update error:", error); 131 const errorMessage = error instanceof Error 132 ? error.message 133 : "Failed to update your PLC"; 134 console.log("Sending error response:", errorMessage); 135 136 return new Response( 137 JSON.stringify({ 138 success: false, 139 message: errorMessage, 140 error: error instanceof Error 141 ? { 142 name: error.name, 143 message: error.message, 144 stack: error.stack, 145 } 146 : String(error), 147 }), 148 { 149 status: 400, 150 headers: { "Content-Type": "application/json" }, 151 }, 152 ); 153 } 154 }, 155});