mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-04 09:16:54 +00:00
Migration for did:webs specifically
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
let checkingHandle = $state(false)
|
||||
|
||||
const isResumedMigration = $derived(flow.state.progress.repoImported)
|
||||
const isDidWeb = $derived(flow.state.sourceDid.startsWith("did:web:"))
|
||||
|
||||
$effect(() => {
|
||||
if (flow.state.step === 'welcome' || flow.state.step === 'choose-handle') {
|
||||
@@ -187,7 +188,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
const steps = ['Login', 'Handle', 'Review', 'Transfer', 'Verify Email', 'Verify PLC', 'Complete']
|
||||
async function completeDidWeb() {
|
||||
loading = true
|
||||
try {
|
||||
await flow.completeDidWebMigration()
|
||||
} catch (err) {
|
||||
flow.setError((err as Error).message)
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const steps = $derived(isDidWeb
|
||||
? ['Login', 'Handle', 'Review', 'Transfer', 'Verify Email', 'Update DID', 'Complete']
|
||||
: ['Login', 'Handle', 'Review', 'Transfer', 'Verify Email', 'Verify PLC', 'Complete'])
|
||||
function getCurrentStepIndex(): number {
|
||||
switch (flow.state.step) {
|
||||
case 'welcome':
|
||||
@@ -197,6 +211,7 @@
|
||||
case 'migrating': return 3
|
||||
case 'email-verify': return 4
|
||||
case 'plc-token':
|
||||
case 'did-web-update':
|
||||
case 'finalizing': return 5
|
||||
case 'success': return 6
|
||||
default: return 0
|
||||
@@ -589,6 +604,46 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{:else if flow.state.step === 'did-web-update'}
|
||||
<div class="step-content">
|
||||
<h2>{$_('migration.inbound.didWebUpdate.title')}</h2>
|
||||
<p>{$_('migration.inbound.didWebUpdate.desc')}</p>
|
||||
|
||||
<div class="info-box">
|
||||
<p>
|
||||
{$_('migration.inbound.didWebUpdate.yourDid')} <code>{flow.state.sourceDid}</code>
|
||||
</p>
|
||||
<p style="margin-top: 12px;">
|
||||
{$_('migration.inbound.didWebUpdate.updateInstructions')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="code-block">
|
||||
<pre>{`{
|
||||
"id": "${flow.state.sourceDid}",
|
||||
"service": [
|
||||
{
|
||||
"id": "#atproto_pds",
|
||||
"type": "AtprotoPersonalDataServer",
|
||||
"serviceEndpoint": "${window.location.origin}"
|
||||
}
|
||||
]
|
||||
}`}</pre>
|
||||
</div>
|
||||
|
||||
<div class="warning-box">
|
||||
<strong>{$_('migration.inbound.didWebUpdate.important')}</strong> {$_('migration.inbound.didWebUpdate.verifyFirst')}
|
||||
{$_('migration.inbound.didWebUpdate.fileLocation')} <code>https://{flow.state.sourceDid.replace('did:web:', '')}/.well-known/did.json</code>
|
||||
</div>
|
||||
|
||||
<div class="button-row">
|
||||
<button class="ghost" onclick={() => flow.setStep('email-verify')} disabled={loading}>Back</button>
|
||||
<button onclick={completeDidWeb} disabled={loading}>
|
||||
{loading ? $_('migration.inbound.didWebUpdate.completing') : $_('migration.inbound.didWebUpdate.complete')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{:else if flow.state.step === 'finalizing'}
|
||||
<div class="step-content">
|
||||
<h2>Finalizing Migration</h2>
|
||||
@@ -1021,4 +1076,29 @@
|
||||
border-radius: var(--radius-lg);
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
|
||||
.code-block {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-4);
|
||||
margin-bottom: var(--space-5);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.code-block pre {
|
||||
margin: 0;
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-sm);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
background: var(--bg-primary);
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -371,9 +371,13 @@ export function createInboundMigrationFlow() {
|
||||
return;
|
||||
}
|
||||
|
||||
setProgress({ currentOperation: "Requesting PLC operation token..." });
|
||||
await sourceClient.requestPlcOperationSignature();
|
||||
setStep("plc-token");
|
||||
if (state.sourceDid.startsWith("did:web:")) {
|
||||
setStep("did-web-update");
|
||||
} else {
|
||||
setProgress({ currentOperation: "Requesting PLC operation token..." });
|
||||
await sourceClient.requestPlcOperationSignature();
|
||||
setStep("plc-token");
|
||||
}
|
||||
} catch (e) {
|
||||
const err = e as Error & { error?: string; status?: number };
|
||||
const message = err.message || err.error ||
|
||||
@@ -401,8 +405,12 @@ export function createInboundMigrationFlow() {
|
||||
state.targetEmail,
|
||||
state.targetPassword,
|
||||
);
|
||||
await sourceClient.requestPlcOperationSignature();
|
||||
setStep("plc-token");
|
||||
if (state.sourceDid.startsWith("did:web:")) {
|
||||
setStep("did-web-update");
|
||||
} else {
|
||||
await sourceClient.requestPlcOperationSignature();
|
||||
setStep("plc-token");
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
const err = e as Error & { error?: string };
|
||||
@@ -543,6 +551,55 @@ export function createInboundMigrationFlow() {
|
||||
await sourceClient.requestPlcOperationSignature();
|
||||
}
|
||||
|
||||
async function completeDidWebMigration(): Promise<void> {
|
||||
migrationLog("completeDidWebMigration START", {
|
||||
sourceDid: state.sourceDid,
|
||||
sourceHandle: state.sourceHandle,
|
||||
targetHandle: state.targetHandle,
|
||||
});
|
||||
|
||||
if (!sourceClient || !localClient) {
|
||||
migrationLog("completeDidWebMigration ERROR: Not connected to PDSes");
|
||||
throw new Error("Not connected to PDSes");
|
||||
}
|
||||
|
||||
setStep("finalizing");
|
||||
setProgress({ currentOperation: "Activating account..." });
|
||||
|
||||
try {
|
||||
migrationLog("Activating account on NEW PDS");
|
||||
const activateStart = Date.now();
|
||||
await localClient.activateAccount();
|
||||
migrationLog("Account activated", { durationMs: Date.now() - activateStart });
|
||||
setProgress({ activated: true });
|
||||
|
||||
setProgress({ currentOperation: "Deactivating old account..." });
|
||||
migrationLog("Deactivating account on OLD PDS");
|
||||
const deactivateStart = Date.now();
|
||||
try {
|
||||
await sourceClient.deactivateAccount();
|
||||
migrationLog("Account deactivated on OLD PDS", {
|
||||
durationMs: Date.now() - deactivateStart,
|
||||
});
|
||||
setProgress({ deactivated: true });
|
||||
} catch (deactivateErr) {
|
||||
const err = deactivateErr as Error & { error?: string };
|
||||
migrationLog("Could not deactivate on OLD PDS", { error: err.message });
|
||||
}
|
||||
|
||||
migrationLog("completeDidWebMigration SUCCESS");
|
||||
setStep("success");
|
||||
clearMigrationState();
|
||||
} catch (e) {
|
||||
const err = e as Error & { error?: string; status?: number };
|
||||
const message = err.message || err.error ||
|
||||
`Unknown error (status ${err.status || "unknown"})`;
|
||||
migrationLog("completeDidWebMigration FAILED", { error: message });
|
||||
setError(message);
|
||||
setStep("did-web-update");
|
||||
}
|
||||
}
|
||||
|
||||
function reset(): void {
|
||||
state = {
|
||||
direction: "inbound",
|
||||
@@ -614,6 +671,7 @@ export function createInboundMigrationFlow() {
|
||||
requestPlcToken,
|
||||
submitPlcToken,
|
||||
resendPlcToken,
|
||||
completeDidWebMigration,
|
||||
reset,
|
||||
resumeFromState,
|
||||
getLocalSession,
|
||||
|
||||
@@ -6,6 +6,7 @@ export type InboundStep =
|
||||
| "migrating"
|
||||
| "email-verify"
|
||||
| "plc-token"
|
||||
| "did-web-update"
|
||||
| "finalizing"
|
||||
| "success"
|
||||
| "error";
|
||||
|
||||
@@ -1106,6 +1106,17 @@
|
||||
"resend": "Resend Token",
|
||||
"resending": "Resending..."
|
||||
},
|
||||
"didWebUpdate": {
|
||||
"title": "Update Your DID Document",
|
||||
"desc": "Since you're using a did:web identity, you need to update your DID document to point to this PDS.",
|
||||
"yourDid": "Your DID is:",
|
||||
"updateInstructions": "Update the did.json file at your domain to point the atproto_pds service endpoint to this PDS:",
|
||||
"important": "Important:",
|
||||
"verifyFirst": "Make sure your DID document is updated and publicly accessible before completing the migration.",
|
||||
"fileLocation": "The file should be at:",
|
||||
"complete": "Complete Migration",
|
||||
"completing": "Completing..."
|
||||
},
|
||||
"finalizing": {
|
||||
"title": "Finalizing Migration",
|
||||
"desc": "Please wait while we complete the migration...",
|
||||
|
||||
@@ -1106,6 +1106,17 @@
|
||||
"resend": "Lähetä uudelleen",
|
||||
"resending": "Lähetetään..."
|
||||
},
|
||||
"didWebUpdate": {
|
||||
"title": "Päivitä DID-dokumenttisi",
|
||||
"desc": "Koska käytät did:web-identiteettiä, sinun täytyy päivittää DID-dokumenttisi osoittamaan tähän PDS:ään.",
|
||||
"yourDid": "DID:si on:",
|
||||
"updateInstructions": "Päivitä verkkotunnuksesi did.json-tiedosto niin, että atproto_pds-palvelun päätepiste osoittaa tähän PDS:ään:",
|
||||
"important": "Tärkeää:",
|
||||
"verifyFirst": "Varmista, että DID-dokumenttisi on päivitetty ja julkisesti saatavilla ennen siirron viimeistelyä.",
|
||||
"fileLocation": "Tiedoston tulee sijaita:",
|
||||
"complete": "Viimeistele siirto",
|
||||
"completing": "Viimeistellään..."
|
||||
},
|
||||
"finalizing": {
|
||||
"title": "Viimeistellään siirtoa",
|
||||
"desc": "Odota, kun viimeistelemme siirtoa...",
|
||||
|
||||
@@ -1106,6 +1106,17 @@
|
||||
"resend": "再送信",
|
||||
"resending": "送信中..."
|
||||
},
|
||||
"didWebUpdate": {
|
||||
"title": "DIDドキュメントを更新",
|
||||
"desc": "did:webアイデンティティを使用しているため、DIDドキュメントを更新してこのPDSを指すようにする必要があります。",
|
||||
"yourDid": "あなたのDID:",
|
||||
"updateInstructions": "ドメインのdid.jsonファイルを更新して、atproto_pdsサービスエンドポイントをこのPDSに向けてください:",
|
||||
"important": "重要:",
|
||||
"verifyFirst": "移行を完了する前に、DIDドキュメントが更新され、公開アクセス可能であることを確認してください。",
|
||||
"fileLocation": "ファイルの場所:",
|
||||
"complete": "移行を完了",
|
||||
"completing": "完了中..."
|
||||
},
|
||||
"finalizing": {
|
||||
"title": "移行を完了中",
|
||||
"desc": "移行を完了しています...",
|
||||
|
||||
@@ -1106,6 +1106,17 @@
|
||||
"resend": "재전송",
|
||||
"resending": "전송 중..."
|
||||
},
|
||||
"didWebUpdate": {
|
||||
"title": "DID 문서 업데이트",
|
||||
"desc": "did:web 아이덴티티를 사용하고 있으므로 DID 문서를 이 PDS를 가리키도록 업데이트해야 합니다.",
|
||||
"yourDid": "당신의 DID:",
|
||||
"updateInstructions": "도메인의 did.json 파일을 업데이트하여 atproto_pds 서비스 엔드포인트가 이 PDS를 가리키도록 하세요:",
|
||||
"important": "중요:",
|
||||
"verifyFirst": "마이그레이션을 완료하기 전에 DID 문서가 업데이트되고 공개적으로 접근 가능한지 확인하세요.",
|
||||
"fileLocation": "파일 위치:",
|
||||
"complete": "마이그레이션 완료",
|
||||
"completing": "완료 중..."
|
||||
},
|
||||
"finalizing": {
|
||||
"title": "마이그레이션 완료 중",
|
||||
"desc": "마이그레이션을 완료하는 중입니다...",
|
||||
|
||||
@@ -1106,6 +1106,17 @@
|
||||
"resend": "Skicka igen",
|
||||
"resending": "Skickar..."
|
||||
},
|
||||
"didWebUpdate": {
|
||||
"title": "Uppdatera ditt DID-dokument",
|
||||
"desc": "Eftersom du använder en did:web-identitet måste du uppdatera ditt DID-dokument för att peka på denna PDS.",
|
||||
"yourDid": "Ditt DID är:",
|
||||
"updateInstructions": "Uppdatera did.json-filen på din domän så att atproto_pds-tjänstens slutpunkt pekar på denna PDS:",
|
||||
"important": "Viktigt:",
|
||||
"verifyFirst": "Se till att ditt DID-dokument är uppdaterat och offentligt tillgängligt innan du slutför flytten.",
|
||||
"fileLocation": "Filen ska finnas på:",
|
||||
"complete": "Slutför flytt",
|
||||
"completing": "Slutför..."
|
||||
},
|
||||
"finalizing": {
|
||||
"title": "Slutför flytt",
|
||||
"desc": "Vänta medan vi slutför flytten...",
|
||||
|
||||
@@ -1106,6 +1106,17 @@
|
||||
"resend": "重新发送",
|
||||
"resending": "发送中..."
|
||||
},
|
||||
"didWebUpdate": {
|
||||
"title": "更新您的DID文档",
|
||||
"desc": "由于您使用的是did:web身份,您需要更新DID文档以指向此PDS。",
|
||||
"yourDid": "您的DID是:",
|
||||
"updateInstructions": "更新您域名上的did.json文件,将atproto_pds服务端点指向此PDS:",
|
||||
"important": "重要提示:",
|
||||
"verifyFirst": "在完成迁移之前,请确保您的DID文档已更新并可公开访问。",
|
||||
"fileLocation": "文件应位于:",
|
||||
"complete": "完成迁移",
|
||||
"completing": "完成中..."
|
||||
},
|
||||
"finalizing": {
|
||||
"title": "正在完成迁移",
|
||||
"desc": "请稍候,正在完成迁移...",
|
||||
|
||||
@@ -495,10 +495,20 @@
|
||||
.back {
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
padding: var(--space-1) var(--space-2);
|
||||
margin: calc(-1 * var(--space-1)) calc(-1 * var(--space-2));
|
||||
border-radius: var(--radius-sm);
|
||||
transition: background var(--transition-fast), color var(--transition-fast);
|
||||
}
|
||||
|
||||
.back:hover {
|
||||
color: var(--accent);
|
||||
background: var(--accent-muted);
|
||||
}
|
||||
|
||||
.back:focus {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.sep {
|
||||
@@ -508,16 +518,25 @@
|
||||
.breadcrumb-link {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
padding: var(--space-1) var(--space-2);
|
||||
margin: calc(-1 * var(--space-1)) calc(-1 * var(--space-2));
|
||||
color: var(--accent);
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
border-radius: var(--radius-sm);
|
||||
transition: background var(--transition-fast);
|
||||
}
|
||||
|
||||
.breadcrumb-link:hover {
|
||||
background: var(--accent-muted);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.breadcrumb-link:focus {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.current {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
@@ -683,19 +702,29 @@
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: var(--space-3);
|
||||
background: var(--bg-card);
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
color: var(--text-primary);
|
||||
transition: border-color var(--transition-fast);
|
||||
transition: background var(--transition-fast), border-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.collection-link:hover {
|
||||
background: var(--bg-secondary);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.collection-link:focus {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.collection-link:active {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.nsid {
|
||||
font-weight: var(--font-medium);
|
||||
color: var(--accent);
|
||||
@@ -705,6 +734,10 @@
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.collection-link:hover .arrow {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.record-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
@@ -718,19 +751,29 @@
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: var(--space-4);
|
||||
background: var(--bg-card);
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
color: var(--text-primary);
|
||||
transition: border-color var(--transition-fast);
|
||||
transition: background var(--transition-fast), border-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.record-item:hover {
|
||||
background: var(--bg-secondary);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.record-item:focus {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.record-item:active {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.record-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -927,4 +970,14 @@
|
||||
padding: var(--space-6);
|
||||
border-radius: var(--radius-xl);
|
||||
}
|
||||
|
||||
.page ::selection {
|
||||
background: var(--accent);
|
||||
color: var(--text-inverse);
|
||||
}
|
||||
|
||||
.page ::-moz-selection {
|
||||
background: var(--accent);
|
||||
color: var(--text-inverse);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user