forked from npmx-dev/npmx.dev
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatproto.get.ts
More file actions
125 lines (112 loc) · 3.72 KB
/
atproto.get.ts
File metadata and controls
125 lines (112 loc) · 3.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { Agent } from '@atproto/api'
import { NodeOAuthClient } from '@atproto/oauth-client-node'
import { createError, getQuery, sendRedirect } from 'h3'
import { getOAuthLock } from '#server/utils/atproto/lock'
import { useOAuthStorage } from '#server/utils/atproto/storage'
import { SLINGSHOT_HOST } from '#shared/utils/constants'
import { useServerSession } from '#server/utils/server-session'
import type { PublicUserSession } from '#shared/schemas/publicUserSession'
import { handleResolver } from '#server/utils/atproto/oauth'
import { Client } from '@atproto/lex'
import * as app from '#shared/types/lexicons/app'
import { ensureValidAtIdentifier } from '@atproto/syntax'
/**
* Fetch the user's profile record to get their avatar blob reference
* @param did
* @param pds
* @returns
*/
async function getAvatar(did: string, pds: string) {
let avatar: string | undefined
try {
const pdsUrl = new URL(pds)
// Only fetch from HTTPS PDS endpoints to prevent SSRF
if (did && pdsUrl.protocol === 'https:') {
ensureValidAtIdentifier(did)
const client = new Client(pdsUrl)
const profileResponse = await client.get(app.bsky.actor.profile, {
repo: did,
rkey: 'self',
})
const validatedResponse = app.bsky.actor.profile.main.validate(profileResponse.value)
if (validatedResponse.avatar?.ref) {
// Use Bluesky CDN for faster image loading
avatar = `https://cdn.bsky.app/img/feed_thumbnail/plain/${did}/${validatedResponse.avatar?.ref}@jpeg`
}
}
} catch {
// Avatar fetch failed, continue without it
}
return avatar
}
export default defineEventHandler(async event => {
const config = useRuntimeConfig(event)
if (!config.sessionPassword) {
throw createError({
status: 500,
message: UNSET_NUXT_SESSION_PASSWORD,
})
}
const query = getQuery(event)
const clientMetadata = getOauthClientMetadata()
const session = await useServerSession(event)
const { stateStore, sessionStore } = useOAuthStorage(session)
const atclient = new NodeOAuthClient({
stateStore,
sessionStore,
clientMetadata,
requestLock: getOAuthLock(),
handleResolver,
})
if (!query.code) {
try {
const handle = query.handle?.toString()
const create = query.create?.toString()
if (!handle) {
throw createError({
statusCode: 401,
message: 'Handle not provided in query',
})
}
return "this api works"
} catch (error) {
const message = error instanceof Error ? error.message : 'Authentication failed.'
return handleApiError(error, {
statusCode: 401,
message: `${message}. Please login and try again.`,
})
}
}
const { session: authSession } = await atclient.callback(
new URLSearchParams(query as Record<string, string>),
)
const agent = new Agent(authSession)
event.context.agent = agent
const response = await fetch(
`https://${SLINGSHOT_HOST}/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${agent.did}`,
{ headers: { 'User-Agent': 'npmx' } },
)
if (response.ok) {
const miniDoc: PublicUserSession = await response.json()
let avatar: string | undefined = await getAvatar(authSession.did, miniDoc.pds)
await session.update({
public: {
...miniDoc,
avatar,
},
})
} else {
//If slingshot fails we still want to set some key info we need.
const pdsBase = (await authSession.getTokenInfo()).aud
let avatar: string | undefined = await getAvatar(authSession.did, pdsBase)
await session.update({
public: {
did: authSession.did,
handle: 'Not available',
pds: pdsBase,
avatar,
},
})
}
return sendRedirect(event, '/')
})