Graphical PDS migrator for AT Protocol

polish plc update

Turtlepaw 9bda00df 30a703c9

Changed files
+195 -16
islands
routes
+28 -6
islands/DidPlcProgress.tsx
···
return;
}
-
updateStepStatus(1, "in-progress");
+
updateStepStatus(1, "completed");
try {
+
updateStepStatus(2, "in-progress");
console.log("Submitting update request with token...");
// Send the update request with both key and token
const res = await fetch("/api/plc/update", {
···
setUpdateResult("PLC update completed successfully!");
// Add a delay before marking steps as completed for better UX
-
updateStepStatus(1, "verifying");
-
updateStepStatus(2, "in-progress");
+
updateStepStatus(2, "verifying");
+
+
const verifyRes = await fetch("/api/plc/verify", {
+
method: "POST",
+
headers: { "Content-Type": "application/json" },
+
body: JSON.stringify({
+
key: keyJson.publicKeyDid,
+
}),
+
});
+
+
const verifyText = await verifyRes.text();
+
console.log("Verification response:", verifyText);
+
+
let verifyData;
+
try {
+
verifyData = JSON.parse(verifyText);
+
} catch {
+
throw new Error("Invalid verification response from server");
+
}
-
await new Promise((resolve) => setTimeout(resolve, 1500));
-
updateStepStatus(1, "completed");
+
if (!verifyRes.ok || !verifyData.success) {
+
const errorMessage =
+
verifyData.message || "Failed to verify PLC update";
+
console.error("Verification failed:", errorMessage);
+
throw new Error(errorMessage);
+
}
-
await new Promise((resolve) => setTimeout(resolve, 1000));
+
console.log("Verification successful, marking steps as completed");
updateStepStatus(2, "completed");
} catch (error) {
console.error("Update failed:", error);
+36 -10
routes/api/plc/update.ts
···
import { getSessionAgent } from "../../../lib/sessions.ts";
-
import { setCredentialSession } from "../../../lib/cred/sessions.ts";
-
import { Agent } from "@atproto/api";
import { define } from "../../../utils.ts";
+
import * as plc from "@did-plc/lib";
/**
* Handle PLC update operation
···
return new Response("Unauthorized", { status: 401 });
}
+
const session = await agent.com.atproto.server.getSession();
+
const did = session.data.did;
+
if (!did) {
+
console.log("No DID found in session");
+
return new Response(
+
JSON.stringify({
+
success: false,
+
message: "No DID found in your session",
+
}),
+
{
+
status: 400,
+
headers: { "Content-Type": "application/json" },
+
}
+
);
+
}
+
console.log("Using agent DID:", did);
+
// Get recommended credentials first
-
console.log("Getting recommended credentials...");
-
const getDidCredentials =
-
await agent.com.atproto.identity.getRecommendedDidCredentials();
-
console.log("Got recommended credentials:", {
-
rotationKeys: getDidCredentials.data.rotationKeys,
-
hasRotationKeys: !!getDidCredentials.data.rotationKeys?.length,
-
});
+
console.log("Getting did:plc document...");
+
const plcClient = new plc.Client("https://plc.directory");
+
const didDoc = await plcClient.getDocumentData(did);
+
if (!didDoc) {
+
console.log("No DID document found for agent DID");
+
return new Response(
+
JSON.stringify({
+
success: false,
+
message: "No DID document found for your account",
+
}),
+
{
+
status: 400,
+
headers: { "Content-Type": "application/json" },
+
}
+
);
+
}
+
console.log("Got DID document:", didDoc);
-
const rotationKeys = getDidCredentials.data.rotationKeys ?? [];
+
const rotationKeys = didDoc.rotationKeys ?? [];
if (!rotationKeys.length) {
console.log("No existing rotation keys found");
throw new Error("No rotation keys provided in recommended credentials");
+131
routes/api/plc/verify.ts
···
+
import { getSessionAgent } from "../../../lib/sessions.ts";
+
import { define } from "../../../utils.ts";
+
import * as plc from "@did-plc/lib";
+
+
/**
+
* Verify if a rotation key exists in the PLC document
+
* Body must contain:
+
* - key: The rotation key to verify
+
* @param ctx - The context object containing the request and response
+
* @returns A response object with the verification result
+
*/
+
export const handler = define.handlers({
+
async POST(ctx) {
+
const res = new Response();
+
try {
+
const body = await ctx.req.json();
+
const { key: newKey } = body;
+
console.log("Request body:", { newKey });
+
+
if (!newKey) {
+
console.log("Missing key in request");
+
return new Response("Missing param key in request body", {
+
status: 400,
+
});
+
}
+
+
const agent = await getSessionAgent(ctx.req, res);
+
if (!agent) {
+
console.log("No agent found");
+
return new Response("Unauthorized", { status: 401 });
+
}
+
+
const session = await agent.com.atproto.server.getSession();
+
const did = session.data.did;
+
if (!did) {
+
console.log("No DID found in session");
+
return new Response(
+
JSON.stringify({
+
success: false,
+
message: "No DID found in your session",
+
}),
+
{
+
status: 400,
+
headers: { "Content-Type": "application/json" },
+
}
+
);
+
}
+
console.log("Using agent DID:", did);
+
+
// Fetch the PLC document to check rotation keys
+
console.log("Getting did:plc document...");
+
const plcClient = new plc.Client("https://plc.directory");
+
const didDoc = await plcClient.getDocumentData(did);
+
if (!didDoc) {
+
console.log("No DID document found for agent DID");
+
return new Response(
+
JSON.stringify({
+
success: false,
+
message: "No DID document found for your account",
+
}),
+
{
+
status: 400,
+
headers: { "Content-Type": "application/json" },
+
}
+
);
+
}
+
console.log("Got DID document:", didDoc);
+
+
const rotationKeys = didDoc.rotationKeys ?? [];
+
if (!rotationKeys.length) {
+
console.log("No existing rotation keys found");
+
throw new Error("No rotation keys found in did:plc document");
+
}
+
+
// Check if the key exists in rotation keys
+
if (rotationKeys.includes(newKey)) {
+
return new Response(
+
JSON.stringify({
+
success: true,
+
message: "Rotation key exists in PLC document",
+
}),
+
{
+
status: 200,
+
headers: {
+
"Content-Type": "application/json",
+
...Object.fromEntries(res.headers), // Include session cookie headers
+
},
+
}
+
);
+
}
+
+
// If we get here, the key was not found
+
return new Response(
+
JSON.stringify({
+
success: false,
+
message: "Rotation key not found in PLC document",
+
}),
+
{
+
status: 404,
+
headers: { "Content-Type": "application/json" },
+
}
+
);
+
} catch (error) {
+
console.error("PLC verification error:", error);
+
const errorMessage =
+
error instanceof Error
+
? error.message
+
: "Failed to verify rotation key";
+
console.log("Sending error response:", errorMessage);
+
+
return new Response(
+
JSON.stringify({
+
success: false,
+
message: errorMessage,
+
error:
+
error instanceof Error
+
? {
+
name: error.name,
+
message: error.message,
+
stack: error.stack,
+
}
+
: String(error),
+
}),
+
{
+
status: 400,
+
headers: { "Content-Type": "application/json" },
+
}
+
);
+
}
+
},
+
});