+2
-1
dev.ts
+2
-1
dev.ts
···
1
1
import { Builder } from "fresh/dev";
2
2
import { tailwind } from "@fresh/plugin-tailwind";
3
+
import { State } from "./utils.ts";
3
4
4
-
const builder = new Builder({ target: "safari12" });
5
+
const builder = new Builder<State>({ target: "safari12" });
5
6
tailwind(builder);
6
7
7
8
if (Deno.args.includes("build")) {
+21
-21
islands/MigrationProgress.tsx
+21
-21
islands/MigrationProgress.tsx
···
37
37
}`,
38
38
);
39
39
setSteps((prevSteps) =>
40
-
prevSteps.map((step, i) =>
41
-
i === index
42
-
? { ...step, status, error, isVerificationError }
43
-
: i > index
44
-
? {
45
-
...step,
46
-
status: "pending",
47
-
error: undefined,
48
-
isVerificationError: undefined,
40
+
prevSteps.map((step, i) => {
41
+
if (i === index) {
42
+
// Update the current step
43
+
return { ...step, status, error, isVerificationError };
44
+
} else if (i > index) {
45
+
// Reset future steps to pending only if current step is erroring
46
+
if (status === "error") {
47
+
return {
48
+
...step,
49
+
status: "pending",
50
+
error: undefined,
51
+
isVerificationError: undefined,
52
+
};
49
53
}
50
-
: step
51
-
)
54
+
// Otherwise keep future steps as they are
55
+
return step;
56
+
} else {
57
+
// Keep previous steps as they are (preserve completed status)
58
+
return step;
59
+
}
60
+
})
52
61
);
53
62
};
54
63
···
152
161
return;
153
162
}
154
163
155
-
try {
156
-
await client.startMigration(props);
157
-
} catch (error) {
158
-
console.error("Unhandled migration error:", error);
159
-
updateStepStatus(
160
-
0,
161
-
"error",
162
-
error as string ?? "Unknown error occurred",
163
-
);
164
-
}
164
+
await client.startMigration(props);
165
165
})();
166
166
}, []);
167
167
+2
-6
lib/client.ts
+2
-6
lib/client.ts
···
205
205
await this.nextStepHook(2);
206
206
}
207
207
208
-
// Step 4: Finalize Migration
209
-
await this.finalizeMigration();
210
-
if (this.nextStepHook) {
211
-
await this.nextStepHook(3);
212
-
}
213
-
208
+
// Stop here - finalization will be called from handleIdentityMigration
209
+
// after user enters the token
214
210
return;
215
211
} catch (error) {
216
212
console.error("Migration error in try/catch:", error);
+19
-11
lib/oauth/client.ts
+19
-11
lib/oauth/client.ts
···
1
1
import { AtprotoOAuthClient } from "@bigmoves/atproto-oauth-client";
2
2
import { SessionStore, StateStore } from "../storage.ts";
3
3
4
+
export const scope = [
5
+
"atproto",
6
+
"account:email",
7
+
"account:status?action=manage",
8
+
"identity:*",
9
+
"rpc:*?aud=did:web:api.bsky.app#bsky_appview",
10
+
"rpc:com.atproto.server.createAccount?aud=*",
11
+
].join(" ");
12
+
const publicUrl = Deno.env.get("PUBLIC_URL");
13
+
const url = publicUrl || `http://127.0.0.1:8000`;
14
+
export const clientId = publicUrl
15
+
? `${url}/oauth-client-metadata.json`
16
+
: `http://localhost?redirect_uri=${
17
+
encodeURIComponent(`${url}/api/oauth/callback`)
18
+
}&scope=${encodeURIComponent(scope)}`;
19
+
console.log(`ClientId: ${clientId}`);
20
+
4
21
/**
5
22
* Create the OAuth client.
6
23
* @param db - The Deno KV instance for the database
···
11
28
throw new Error("PUBLIC_URL is not set");
12
29
}
13
30
14
-
const publicUrl = Deno.env.get("PUBLIC_URL");
15
-
const url = publicUrl || `http://127.0.0.1:8000`;
16
-
const enc = encodeURIComponent;
17
-
const clientId = publicUrl
18
-
? `${url}/oauth-client-metadata.json`
19
-
: `http://localhost?redirect_uri=${
20
-
enc(`${url}/api/oauth/callback`)
21
-
}&scope=${enc("atproto transition:generic transition:chat.bsky")}`;
22
-
console.log(`ClientId: ${clientId}`);
23
-
24
31
return new AtprotoOAuthClient({
25
32
clientMetadata: {
26
33
client_name: "Statusphere React App",
27
34
client_id: clientId,
28
35
client_uri: url,
29
36
redirect_uris: [`${url}/api/oauth/callback`],
30
-
scope: "atproto transition:generic transition:chat.bsky",
37
+
scope: scope,
31
38
grant_types: ["authorization_code", "refresh_token"],
32
39
response_types: ["code"],
33
40
application_type: "web",
···
36
43
},
37
44
stateStore: new StateStore(db),
38
45
sessionStore: new SessionStore(db),
46
+
didCache: undefined,
39
47
});
40
48
};
41
49
+2
-2
routes/api/oauth/initiate.ts
+2
-2
routes/api/oauth/initiate.ts
···
1
1
import { isValidHandle } from "npm:@atproto/syntax";
2
-
import { oauthClient } from "../../../lib/oauth/client.ts";
2
+
import { oauthClient, scope } from "../../../lib/oauth/client.ts";
3
3
import { define } from "../../../utils.ts";
4
4
5
5
function isValidUrl(url: string): boolean {
···
26
26
// Initiate the OAuth flow
27
27
try {
28
28
const url = await oauthClient.authorize(handle, {
29
-
scope: "atproto transition:generic transition:chat.bsky",
29
+
scope,
30
30
});
31
31
return Response.json({ redirectUrl: url.toString() });
32
32
} catch (err) {
+35
routes/oauth-client-metadata.json/index.ts
+35
routes/oauth-client-metadata.json/index.ts
···
1
+
import { clientId, scope } from "../../lib/oauth/client.ts";
2
+
import { define } from "../../utils.ts";
3
+
4
+
/**
5
+
* API endpoint to check the current migration state.
6
+
* Returns the migration state information including whether migrations are allowed.
7
+
*/
8
+
export const handler = define.handlers({
9
+
GET(_ctx) {
10
+
return Response.json({
11
+
client_name: "ATP Airport",
12
+
client_id: clientId,
13
+
client_uri: "https://atpairport.com",
14
+
redirect_uris: [
15
+
"https://atpairport.com/api/oauth/callback",
16
+
],
17
+
scope,
18
+
grant_types: [
19
+
"authorization_code",
20
+
"refresh_token",
21
+
],
22
+
response_types: [
23
+
"code",
24
+
],
25
+
application_type: "web",
26
+
token_endpoint_auth_method: "none",
27
+
dpop_bound_access_tokens: true,
28
+
}, {
29
+
status: 200,
30
+
headers: {
31
+
"Content-Type": "application/json",
32
+
},
33
+
});
34
+
},
35
+
});
-19
static/oauth-client-metadata.json
-19
static/oauth-client-metadata.json
···
1
-
{
2
-
"client_name": "ATP Airport",
3
-
"client_id": "https://atpairport.com/oauth-client-metadata.json",
4
-
"client_uri": "https://atpairport.com",
5
-
"redirect_uris": [
6
-
"https://atpairport.com/api/oauth/callback"
7
-
],
8
-
"scope": "atproto transition:generic transition:chat.bsky",
9
-
"grant_types": [
10
-
"authorization_code",
11
-
"refresh_token"
12
-
],
13
-
"response_types": [
14
-
"code"
15
-
],
16
-
"application_type": "web",
17
-
"token_endpoint_auth_method": "none",
18
-
"dpop_bound_access_tokens": true
19
-
}