Skip to content

Commit a4cecc9

Browse files
committed
wip
1 parent 86fb98a commit a4cecc9

8 files changed

Lines changed: 96 additions & 87 deletions

File tree

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#secure password, can use openssl rand --hex 32
2+
NUXT_SESSION_PASSWORD=""

app/composables/useRepoMeta.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ const tangledAdapter: ProviderAdapter = {
595595
try {
596596
//Get counts of records that reference this repo in the atmosphere using constellation
597597
const allLinks = await cachedFetch<ConstellationAllLinksResponse>(
598-
`https://constellation.microcosm.blue/links/all?target=${atUri}`,
598+
`${CONSTELLATION_ENDPOINT}/links/all?target=${atUri}`,
599599
{ headers: { 'User-Agent': 'npmx' } },
600600
REPO_META_TTL,
601601
)

server/api/auth/atproto.get.ts

Lines changed: 6 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
1-
import type { H3Event } from 'h3'
21
import { Agent } from '@atproto/api'
32
import { NodeOAuthClient } from '@atproto/oauth-client-node'
4-
import type {
5-
NodeSavedSession,
6-
NodeSavedSessionStore,
7-
NodeSavedState,
8-
NodeSavedStateStore,
9-
} from '@atproto/oauth-client-node'
10-
import { scope, getOauthClientMetadata } from '~~/server/utils/atproto'
11-
import { createError, getQuery, sendRedirect, getCookie, setCookie, deleteCookie } from 'h3'
3+
import { createError, getQuery, sendRedirect } from 'h3'
4+
import { OAuthSessionStore, OAuthStateStore } from '#server/utils/atproto/storage'
5+
import { SLINGSHOT_ENDPOINT } from '#shared/utils/constants'
126

137
export default defineEventHandler(async event => {
148
const query = getQuery(event)
159
const clientMetadata = getOauthClientMetadata()
16-
const stateStore = new StateStore(event)
17-
const sessionStore = new SessionStore(event)
10+
const stateStore = new OAuthStateStore(event)
11+
const sessionStore = new OAuthSessionStore(event)
1812
const atclient = new NodeOAuthClient({
1913
stateStore,
2014
sessionStore,
@@ -41,13 +35,12 @@ export default defineEventHandler(async event => {
4135
const agent = new Agent(authSession)
4236
event.context.agent = agent
4337

44-
//TODO prob do server side kv store here too?
4538
const session = await useSession(event, {
4639
password: process.env.NUXT_SESSION_PASSWORD as string,
4740
})
4841

4942
const response = await fetch(
50-
`https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${agent.did}`,
43+
`${SLINGSHOT_ENDPOINT}/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${agent.did}`,
5144
{ headers: { 'User-Agent': 'npmx' } },
5245
)
5346
const miniDoc = (await response.json()) as { did: string; handle: string; pds: string }
@@ -56,74 +49,5 @@ export default defineEventHandler(async event => {
5649
miniDoc,
5750
})
5851

59-
// await sessionStore.del()
60-
6152
return sendRedirect(event, '/')
6253
})
63-
64-
/**
65-
* Storage key prefix for oauth state storage.
66-
*/
67-
export const OAUTH_STATE_CACHE_STORAGE_BASE = 'oauth-atproto-state'
68-
69-
export class StateStore implements NodeSavedStateStore {
70-
private readonly cookieKey = 'oauth:atproto:state'
71-
private readonly storage = useStorage(OAUTH_STATE_CACHE_STORAGE_BASE)
72-
73-
constructor(private event: H3Event) {}
74-
75-
async get(): Promise<NodeSavedState | undefined> {
76-
const stateKey = getCookie(this.event, this.cookieKey)
77-
if (!stateKey) return
78-
const result = await this.storage.getItem<NodeSavedState>(stateKey)
79-
if (!result) return
80-
return result
81-
}
82-
83-
async set(key: string, val: NodeSavedState) {
84-
setCookie(this.event, this.cookieKey, key)
85-
await this.storage.setItem<NodeSavedState>(key, val)
86-
}
87-
88-
async del() {
89-
let stateKey = getCookie(this.event, this.cookieKey)
90-
deleteCookie(this.event, this.cookieKey)
91-
if (stateKey) {
92-
await this.storage.del(stateKey)
93-
}
94-
}
95-
}
96-
97-
/**
98-
* Storage key prefix for oauth session storage.
99-
*/
100-
export const OAUTH_SESSION_CACHE_STORAGE_BASE = 'oauth-atproto-session'
101-
102-
export class SessionStore implements NodeSavedSessionStore {
103-
//TODO not sure if we will support multi accounts, but if we do in the future will need to change this around
104-
private readonly cookieKey = 'oauth:atproto:session'
105-
private readonly storage = useStorage(OAUTH_SESSION_CACHE_STORAGE_BASE)
106-
107-
constructor(private event: H3Event) {}
108-
109-
async get(): Promise<NodeSavedSession | undefined> {
110-
const sessionKey = getCookie(this.event, this.cookieKey)
111-
if (!sessionKey) return
112-
let result = await this.storage.getItem<NodeSavedSession>(sessionKey)
113-
if (!result) return
114-
return result
115-
}
116-
117-
async set(key: string, val: NodeSavedSession) {
118-
setCookie(this.event, this.cookieKey, key)
119-
await this.storage.setItem<NodeSavedSession>(key, val)
120-
}
121-
122-
async del() {
123-
let sessionKey = getCookie(this.event, this.cookieKey)
124-
if (sessionKey) {
125-
await this.storage.del(sessionKey)
126-
}
127-
deleteCookie(this.event, this.cookieKey)
128-
}
129-
}

server/api/auth/session.delete.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { eventHandlerWithOAuthSession } from '#server/utils/atproto'
1+
import { eventHandlerWithOAuthSession } from '#server/utils/oauth'
22

33
export default eventHandlerWithOAuthSession(async (event, oAuthSession) => {
44
const session = await useSession(event, {
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { OAuthClientMetadataInput } from '@atproto/oauth-client-node'
22
import type { EventHandlerRequest, H3Event } from 'h3'
33
import type { OAuthSession } from '@atproto/oauth-client-node'
44
import { NodeOAuthClient } from '@atproto/oauth-client-node'
5-
import { SessionStore, StateStore } from '#server/api/auth/atproto.get'
5+
import { OAuthSessionStore, OAuthStateStore } from '#server/utils/atproto/storage'
66

77
// TODO: limit scope as features gets added. atproto just allows login so no scary login screen till we have scopes
88
export const scope = 'atproto'
@@ -38,8 +38,8 @@ type EventHandlerWithOAuthSession<T extends EventHandlerRequest, D> = (
3838

3939
async function getOAuthSession(event: H3Event): Promise<OAuthSession | undefined> {
4040
const clientMetadata = getOauthClientMetadata()
41-
const stateStore = new StateStore(event)
42-
const sessionStore = new SessionStore(event)
41+
const stateStore = new OAuthStateStore(event)
42+
const sessionStore = new OAuthSessionStore(event)
4343

4444
const client = new NodeOAuthClient({
4545
stateStore,

server/utils/atproto/storage.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import type {
2+
NodeSavedSession,
3+
NodeSavedSessionStore,
4+
NodeSavedState,
5+
NodeSavedStateStore,
6+
} from '@atproto/oauth-client-node'
7+
import type { H3Event } from 'h3'
8+
9+
/**
10+
* Storage key prefix for oauth state storage.
11+
*/
12+
export const OAUTH_STATE_CACHE_STORAGE_BASE = 'oauth-atproto-state'
13+
14+
export class OAuthStateStore implements NodeSavedStateStore {
15+
private readonly cookieKey = 'oauth:atproto:state'
16+
private readonly storage = useStorage(OAUTH_STATE_CACHE_STORAGE_BASE)
17+
18+
constructor(private event: H3Event) {}
19+
20+
async get(): Promise<NodeSavedState | undefined> {
21+
const stateKey = getCookie(this.event, this.cookieKey)
22+
if (!stateKey) return
23+
const result = await this.storage.getItem<NodeSavedState>(stateKey)
24+
if (!result) return
25+
return result
26+
}
27+
28+
async set(key: string, val: NodeSavedState) {
29+
setCookie(this.event, this.cookieKey, key)
30+
await this.storage.setItem<NodeSavedState>(key, val)
31+
}
32+
33+
async del() {
34+
let stateKey = getCookie(this.event, this.cookieKey)
35+
deleteCookie(this.event, this.cookieKey)
36+
if (stateKey) {
37+
await this.storage.del(stateKey)
38+
}
39+
}
40+
}
41+
42+
/**
43+
* Storage key prefix for oauth session storage.
44+
*/
45+
export const OAUTH_SESSION_CACHE_STORAGE_BASE = 'oauth-atproto-session'
46+
47+
export class OAuthSessionStore implements NodeSavedSessionStore {
48+
//TODO not sure if we will support multi accounts, but if we do in the future will need to change this around
49+
private readonly cookieKey = 'oauth:atproto:session'
50+
private readonly storage = useStorage(OAUTH_SESSION_CACHE_STORAGE_BASE)
51+
52+
constructor(private event: H3Event) {}
53+
54+
async get(): Promise<NodeSavedSession | undefined> {
55+
const sessionKey = getCookie(this.event, this.cookieKey)
56+
if (!sessionKey) return
57+
let result = await this.storage.getItem<NodeSavedSession>(sessionKey)
58+
if (!result) return
59+
return result
60+
}
61+
62+
async set(key: string, val: NodeSavedSession) {
63+
setCookie(this.event, this.cookieKey, key)
64+
await this.storage.setItem<NodeSavedSession>(key, val)
65+
}
66+
67+
async del() {
68+
let sessionKey = getCookie(this.event, this.cookieKey)
69+
if (sessionKey) {
70+
await this.storage.del(sessionKey)
71+
}
72+
deleteCookie(this.event, this.cookieKey)
73+
}
74+
}

shared/utils/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ export const ERROR_JSR_FETCH_FAILED = 'Failed to fetch package from JSR registry
1717
export const ERROR_NPM_FETCH_FAILED = 'Failed to fetch package from npm registry.'
1818
export const ERROR_SUGGESTIONS_FETCH_FAILED = 'Failed to fetch suggestions.'
1919

20+
// microcosm services
21+
export const CONSTELLATION_ENDPOINT = 'https://constellation.microcosm.blue'
22+
export const SLINGSHOT_ENDPOINT = 'https://slingshot.microcosm.blue'
23+
2024
// Theming
2125
export const ACCENT_COLORS = {
2226
rose: 'oklch(0.797 0.084 11.056)',

shared/utils/fetch-cache-config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* using Nitro's storage layer (backed by Vercel's runtime cache in production).
66
*/
77

8+
import { CONSTELLATION_ENDPOINT, SLINGSHOT_ENDPOINT } from './constants'
9+
810
/**
911
* Domains that should have their fetch responses cached.
1012
* Only requests to these domains will be intercepted and cached.
@@ -24,6 +26,9 @@ export const FETCH_CACHE_ALLOWED_DOMAINS = [
2426
'api.bitbucket.org', // Bitbucket API
2527
'codeberg.org', // Codeberg (Gitea-based)
2628
'gitee.com', // Gitee API
29+
//microcosm endpoints for atproto data
30+
CONSTELLATION_ENDPOINT,
31+
SLINGSHOT_ENDPOINT,
2732
] as const
2833

2934
/**

0 commit comments

Comments
 (0)