@alteran/astro 0.1.14 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -10
- package/index.js +2 -4
- package/migrations/0006_adorable_spectrum.sql +11 -0
- package/migrations/meta/0006_snapshot.json +429 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +7 -3
- package/src/db/account.ts +145 -0
- package/src/db/dal.ts +27 -9
- package/src/db/repo.ts +9 -8
- package/src/db/schema.ts +29 -11
- package/src/lib/actor.ts +133 -0
- package/src/lib/appview.ts +508 -0
- package/src/lib/auth.ts +22 -2
- package/src/lib/blob-refs.ts +9 -13
- package/src/lib/chat.ts +238 -0
- package/src/lib/config.ts +15 -7
- package/src/lib/feed.ts +165 -0
- package/src/lib/jwt.ts +231 -79
- package/src/lib/labeler.ts +91 -0
- package/src/lib/mst/blockstore.ts +98 -14
- package/src/lib/password.ts +40 -0
- package/src/lib/preferences.ts +73 -0
- package/src/lib/relay.ts +101 -0
- package/src/lib/secrets.ts +29 -21
- package/src/lib/session-tokens.ts +202 -0
- package/src/lib/token-cleanup.ts +3 -12
- package/src/lib/util.ts +17 -2
- package/src/middleware.ts +20 -21
- package/src/pages/.well-known/did.json.ts +45 -32
- package/src/pages/xrpc/app.bsky.actor.getPreferences.ts +23 -0
- package/src/pages/xrpc/app.bsky.actor.getProfile.ts +34 -0
- package/src/pages/xrpc/app.bsky.actor.getProfiles.ts +42 -0
- package/src/pages/xrpc/app.bsky.actor.putPreferences.ts +36 -0
- package/src/pages/xrpc/app.bsky.feed.getAuthorFeed.ts +42 -0
- package/src/pages/xrpc/app.bsky.feed.getPostThread.ts +37 -0
- package/src/pages/xrpc/app.bsky.feed.getPosts.ts +26 -0
- package/src/pages/xrpc/app.bsky.feed.getTimeline.ts +35 -0
- package/src/pages/xrpc/app.bsky.graph.getFollowers.ts +29 -0
- package/src/pages/xrpc/app.bsky.graph.getFollows.ts +29 -0
- package/src/pages/xrpc/app.bsky.labeler.getServices.ts +29 -0
- package/src/pages/xrpc/app.bsky.notification.getUnreadCount.ts +20 -0
- package/src/pages/xrpc/app.bsky.notification.listNotifications.ts +27 -0
- package/src/pages/xrpc/app.bsky.unspecced.getAgeAssuranceState.ts +19 -0
- package/src/pages/xrpc/app.bsky.unspecced.getConfig.ts +15 -0
- package/src/pages/xrpc/chat.bsky.convo.getLog.ts +26 -0
- package/src/pages/xrpc/chat.bsky.convo.listConvos.ts +37 -0
- package/src/pages/xrpc/com.atproto.identity.getRecommendedDidCredentials.ts +64 -66
- package/src/pages/xrpc/com.atproto.identity.requestPlcOperationSignature.ts +24 -0
- package/src/pages/xrpc/com.atproto.identity.signPlcOperation.ts +127 -0
- package/src/pages/xrpc/com.atproto.identity.submitPlcOperation.ts +91 -0
- package/src/pages/xrpc/com.atproto.repo.uploadBlob.ts +6 -2
- package/src/pages/xrpc/com.atproto.server.createSession.ts +36 -8
- package/src/pages/xrpc/com.atproto.server.describeServer.ts +37 -4
- package/src/pages/xrpc/com.atproto.server.getServiceAuth.ts +64 -0
- package/src/pages/xrpc/com.atproto.server.refreshSession.ts +55 -32
- package/src/services/repo-manager.ts +15 -6
- package/src/worker/runtime.ts +21 -0
- package/types/env.d.ts +11 -2
- package/src/pages/xrpc/com.atproto.repo.importRepo.ts +0 -142
- package/src/pages/xrpc/com.atproto.server.activateAccount.ts +0 -53
- package/src/pages/xrpc/com.atproto.server.createAccount.ts +0 -99
- package/src/pages/xrpc/com.atproto.server.deactivateAccount.ts +0 -53
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
2
|
import { withCache, CACHE_CONFIGS } from '../../lib/cache';
|
|
3
3
|
import { base58btc } from 'multiformats/bases/base58';
|
|
4
|
+
import { resolveSecret } from '../../lib/secrets';
|
|
5
|
+
import { Secp256k1Keypair } from '@atproto/crypto';
|
|
6
|
+
import { formatMultikey } from '@atproto/crypto/dist/did';
|
|
4
7
|
|
|
5
8
|
export const prerender = false;
|
|
6
9
|
|
|
7
|
-
/**
|
|
8
|
-
* DID Document endpoint for did:web
|
|
9
|
-
*
|
|
10
|
-
* Returns a DID document with:
|
|
11
|
-
* - Service endpoints (PDS, firehose)
|
|
12
|
-
* - Verification methods (signing key)
|
|
13
|
-
*
|
|
14
|
-
* Spec: https://w3c-ccg.github.io/did-method-web/
|
|
15
|
-
*/
|
|
16
10
|
export async function GET({ locals, request }: APIContext) {
|
|
17
11
|
const { env } = locals.runtime;
|
|
18
12
|
|
|
@@ -23,39 +17,57 @@ export async function GET({ locals, request }: APIContext) {
|
|
|
23
17
|
const handle = env.PDS_HANDLE ?? 'user.example.com';
|
|
24
18
|
const hostname = env.PDS_HOSTNAME ?? new URL(request.url).hostname;
|
|
25
19
|
|
|
26
|
-
// Public repository signing key (raw 32-byte Ed25519) base64-encoded
|
|
27
|
-
const pubKeyB64: string | undefined = (env as any).REPO_SIGNING_PUBLIC_KEY;
|
|
28
20
|
let publicKeyMultibase: string | undefined;
|
|
29
|
-
|
|
21
|
+
|
|
22
|
+
const serviceKeyHex = await resolveSecret(env.PDS_SERVICE_SIGNING_KEY_HEX as any);
|
|
23
|
+
if (serviceKeyHex) {
|
|
30
24
|
try {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
const keypair = await Secp256k1Keypair.import(serviceKeyHex.trim());
|
|
26
|
+
publicKeyMultibase = formatMultikey(keypair.jwtAlg, keypair.publicKeyBytes());
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.warn('Failed to encode service signing key', error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!publicKeyMultibase) {
|
|
33
|
+
const repoPubKeyB64 = await resolveSecret((env as any).REPO_SIGNING_KEY_PUBLIC);
|
|
34
|
+
if (repoPubKeyB64) {
|
|
35
|
+
try {
|
|
36
|
+
const bin = atob(repoPubKeyB64.replace(/\s+/g, ''));
|
|
37
|
+
const raw = new Uint8Array(bin.length);
|
|
38
|
+
for (let i = 0; i < bin.length; i++) raw[i] = bin.charCodeAt(i);
|
|
39
|
+
if (raw.byteLength === 32) {
|
|
40
|
+
const prefixed = new Uint8Array(2 + raw.byteLength);
|
|
41
|
+
prefixed[0] = 0xed;
|
|
42
|
+
prefixed[1] = 0x01;
|
|
43
|
+
prefixed.set(raw, 2);
|
|
44
|
+
publicKeyMultibase = base58btc.encode(prefixed);
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.warn('Failed to encode repo signing key', error);
|
|
39
48
|
}
|
|
40
|
-
}
|
|
49
|
+
}
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
const verificationMethods = publicKeyMultibase
|
|
53
|
+
? [
|
|
54
|
+
{
|
|
55
|
+
id: `${did}#atproto`,
|
|
56
|
+
type: 'Multikey',
|
|
57
|
+
controller: did,
|
|
58
|
+
publicKeyMultibase,
|
|
59
|
+
},
|
|
60
|
+
]
|
|
61
|
+
: [];
|
|
62
|
+
|
|
44
63
|
const didDocument = {
|
|
45
64
|
'@context': [
|
|
46
65
|
'https://www.w3.org/ns/did/v1',
|
|
47
|
-
'https://w3id.org/security/
|
|
66
|
+
'https://w3id.org/security/multikey/v1',
|
|
48
67
|
],
|
|
49
68
|
id: did,
|
|
50
69
|
alsoKnownAs: [`at://${handle}`],
|
|
51
|
-
verificationMethod:
|
|
52
|
-
{
|
|
53
|
-
id: `${did}#atproto`,
|
|
54
|
-
type: 'Ed25519VerificationKey2020',
|
|
55
|
-
controller: did,
|
|
56
|
-
publicKeyMultibase,
|
|
57
|
-
},
|
|
58
|
-
] : [],
|
|
70
|
+
verificationMethod: verificationMethods,
|
|
59
71
|
service: [
|
|
60
72
|
{
|
|
61
73
|
id: `${did}#atproto_pds`,
|
|
@@ -71,6 +83,7 @@ export async function GET({ locals, request }: APIContext) {
|
|
|
71
83
|
},
|
|
72
84
|
});
|
|
73
85
|
},
|
|
74
|
-
CACHE_CONFIGS.DID_DOCUMENT
|
|
86
|
+
CACHE_CONFIGS.DID_DOCUMENT,
|
|
75
87
|
);
|
|
76
88
|
}
|
|
89
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
4
|
+
import { getActorPreferences } from '../../lib/preferences';
|
|
5
|
+
|
|
6
|
+
export const prerender = false;
|
|
7
|
+
|
|
8
|
+
export async function GET({ locals, request }: APIContext) {
|
|
9
|
+
const { env } = locals.runtime;
|
|
10
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
11
|
+
|
|
12
|
+
return proxyAppView({
|
|
13
|
+
request,
|
|
14
|
+
env,
|
|
15
|
+
lxm: 'app.bsky.actor.getPreferences',
|
|
16
|
+
fallback: async () => {
|
|
17
|
+
const { preferences } = await getActorPreferences(env);
|
|
18
|
+
return new Response(JSON.stringify({ preferences }), {
|
|
19
|
+
headers: { 'Content-Type': 'application/json' },
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { buildProfileViewDetailed, getPrimaryActor, matchesPrimaryActor } from '../../lib/actor';
|
|
4
|
+
import { countPosts } from '../../lib/feed';
|
|
5
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
6
|
+
|
|
7
|
+
export const prerender = false;
|
|
8
|
+
|
|
9
|
+
export async function GET({ locals, request }: APIContext) {
|
|
10
|
+
const { env } = locals.runtime;
|
|
11
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
12
|
+
|
|
13
|
+
return proxyAppView({
|
|
14
|
+
request,
|
|
15
|
+
env,
|
|
16
|
+
lxm: 'app.bsky.actor.getProfile',
|
|
17
|
+
fallback: async () => {
|
|
18
|
+
const url = new URL(request.url);
|
|
19
|
+
const identifier = url.searchParams.get('actor');
|
|
20
|
+
const actor = await getPrimaryActor(env);
|
|
21
|
+
if (!matchesPrimaryActor(identifier, actor)) {
|
|
22
|
+
return new Response(JSON.stringify({ error: 'ProfileNotFound' }), { status: 404 });
|
|
23
|
+
}
|
|
24
|
+
const profile = buildProfileViewDetailed(actor, {
|
|
25
|
+
followers: 0,
|
|
26
|
+
follows: 0,
|
|
27
|
+
posts: await countPosts(env),
|
|
28
|
+
});
|
|
29
|
+
return new Response(JSON.stringify(profile), {
|
|
30
|
+
headers: { 'Content-Type': 'application/json' },
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import {
|
|
4
|
+
buildProfileViewDetailed,
|
|
5
|
+
getPrimaryActor,
|
|
6
|
+
matchesPrimaryActor,
|
|
7
|
+
} from '../../lib/actor';
|
|
8
|
+
import { countPosts } from '../../lib/feed';
|
|
9
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
10
|
+
|
|
11
|
+
export const prerender = false;
|
|
12
|
+
|
|
13
|
+
export async function GET({ locals, request }: APIContext) {
|
|
14
|
+
const { env } = locals.runtime;
|
|
15
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
16
|
+
|
|
17
|
+
return proxyAppView({
|
|
18
|
+
request,
|
|
19
|
+
env,
|
|
20
|
+
lxm: 'app.bsky.actor.getProfiles',
|
|
21
|
+
fallback: async () => {
|
|
22
|
+
const url = new URL(request.url);
|
|
23
|
+
const actors = url.searchParams.getAll('actors');
|
|
24
|
+
const actor = await getPrimaryActor(env);
|
|
25
|
+
const posts = await countPosts(env);
|
|
26
|
+
|
|
27
|
+
const profiles = actors
|
|
28
|
+
.filter((identifier) => matchesPrimaryActor(identifier, actor))
|
|
29
|
+
.map(() =>
|
|
30
|
+
buildProfileViewDetailed(actor, {
|
|
31
|
+
followers: 0,
|
|
32
|
+
follows: 0,
|
|
33
|
+
posts,
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return new Response(JSON.stringify({ profiles }), {
|
|
38
|
+
headers: { 'Content-Type': 'application/json' },
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
4
|
+
import { readJsonBounded } from '../../lib/util';
|
|
5
|
+
import { setActorPreferences } from '../../lib/preferences';
|
|
6
|
+
|
|
7
|
+
export const prerender = false;
|
|
8
|
+
|
|
9
|
+
export async function POST({ locals, request }: APIContext) {
|
|
10
|
+
const { env } = locals.runtime;
|
|
11
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
12
|
+
|
|
13
|
+
return proxyAppView({
|
|
14
|
+
request,
|
|
15
|
+
env,
|
|
16
|
+
lxm: 'app.bsky.actor.putPreferences',
|
|
17
|
+
fallback: async () => {
|
|
18
|
+
let body: any;
|
|
19
|
+
try {
|
|
20
|
+
body = await readJsonBounded(env, request);
|
|
21
|
+
} catch (err: any) {
|
|
22
|
+
if (err?.code === 'PayloadTooLarge') {
|
|
23
|
+
return new Response(JSON.stringify({ error: 'PayloadTooLarge' }), { status: 413 });
|
|
24
|
+
}
|
|
25
|
+
return new Response(JSON.stringify({ error: 'BadRequest' }), { status: 400 });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const preferences = Array.isArray(body?.preferences) ? body.preferences : [];
|
|
29
|
+
await setActorPreferences(env, preferences);
|
|
30
|
+
|
|
31
|
+
return new Response(JSON.stringify({}), {
|
|
32
|
+
headers: { 'Content-Type': 'application/json' },
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { matchesPrimaryActor, getPrimaryActor } from '../../lib/actor';
|
|
4
|
+
import { buildFeedViewPosts, listPosts } from '../../lib/feed';
|
|
5
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
6
|
+
|
|
7
|
+
export const prerender = false;
|
|
8
|
+
|
|
9
|
+
export async function GET({ locals, request }: APIContext) {
|
|
10
|
+
const { env } = locals.runtime;
|
|
11
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
12
|
+
|
|
13
|
+
return proxyAppView({
|
|
14
|
+
request,
|
|
15
|
+
env,
|
|
16
|
+
lxm: 'app.bsky.feed.getAuthorFeed',
|
|
17
|
+
fallback: async () => {
|
|
18
|
+
const url = new URL(request.url);
|
|
19
|
+
const identifier = url.searchParams.get('actor');
|
|
20
|
+
const actor = await getPrimaryActor(env);
|
|
21
|
+
if (!matchesPrimaryActor(identifier, actor)) {
|
|
22
|
+
return new Response(JSON.stringify({ error: 'ActorNotFound' }), { status: 404 });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const limitParam = Number.parseInt(url.searchParams.get('limit') ?? '', 10);
|
|
26
|
+
const limitInput = Number.isFinite(limitParam) ? limitParam : 50;
|
|
27
|
+
const limit = Math.max(1, Math.min(limitInput, 100));
|
|
28
|
+
const cursor = url.searchParams.get('cursor') ?? undefined;
|
|
29
|
+
|
|
30
|
+
const posts = await listPosts(env, limit, cursor);
|
|
31
|
+
const feed = await buildFeedViewPosts(env, posts);
|
|
32
|
+
const nextCursor = posts.length === limit ? String(posts[posts.length - 1].rowid) : undefined;
|
|
33
|
+
|
|
34
|
+
const payload: Record<string, unknown> = { feed };
|
|
35
|
+
if (nextCursor) payload.cursor = nextCursor;
|
|
36
|
+
|
|
37
|
+
return new Response(JSON.stringify(payload), {
|
|
38
|
+
headers: { 'Content-Type': 'application/json' },
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { buildThreadView, getPostByUri } from '../../lib/feed';
|
|
4
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
5
|
+
|
|
6
|
+
export const prerender = false;
|
|
7
|
+
|
|
8
|
+
export async function GET({ locals, request }: APIContext) {
|
|
9
|
+
const { env } = locals.runtime;
|
|
10
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
11
|
+
|
|
12
|
+
return proxyAppView({
|
|
13
|
+
request,
|
|
14
|
+
env,
|
|
15
|
+
lxm: 'app.bsky.feed.getPostThread',
|
|
16
|
+
fallback: async () => {
|
|
17
|
+
const url = new URL(request.url);
|
|
18
|
+
const uri = url.searchParams.get('uri');
|
|
19
|
+
if (!uri) {
|
|
20
|
+
return new Response(
|
|
21
|
+
JSON.stringify({ error: 'BadRequest', message: 'uri parameter required' }),
|
|
22
|
+
{ status: 400 },
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const post = await getPostByUri(env, uri);
|
|
27
|
+
if (!post) {
|
|
28
|
+
return new Response(JSON.stringify({ error: 'NotFound' }), { status: 404 });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const thread = await buildThreadView(env, post);
|
|
32
|
+
return new Response(JSON.stringify({ thread }), {
|
|
33
|
+
headers: { 'Content-Type': 'application/json' },
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { buildPostViews, getPostsByUris } from '../../lib/feed';
|
|
4
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
5
|
+
|
|
6
|
+
export const prerender = false;
|
|
7
|
+
|
|
8
|
+
export async function GET({ locals, request }: APIContext) {
|
|
9
|
+
const { env } = locals.runtime;
|
|
10
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
11
|
+
|
|
12
|
+
return proxyAppView({
|
|
13
|
+
request,
|
|
14
|
+
env,
|
|
15
|
+
lxm: 'app.bsky.feed.getPosts',
|
|
16
|
+
fallback: async () => {
|
|
17
|
+
const url = new URL(request.url);
|
|
18
|
+
const uris = url.searchParams.getAll('uris').filter(Boolean);
|
|
19
|
+
const posts = await getPostsByUris(env, uris.slice(0, 25));
|
|
20
|
+
const views = await buildPostViews(env, posts);
|
|
21
|
+
return new Response(JSON.stringify({ posts: views }), {
|
|
22
|
+
headers: { 'Content-Type': 'application/json' },
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { buildFeedViewPosts, listPosts } from '../../lib/feed';
|
|
4
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
5
|
+
|
|
6
|
+
export const prerender = false;
|
|
7
|
+
|
|
8
|
+
export async function GET({ locals, request }: APIContext) {
|
|
9
|
+
const { env } = locals.runtime;
|
|
10
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
11
|
+
|
|
12
|
+
return proxyAppView({
|
|
13
|
+
request,
|
|
14
|
+
env,
|
|
15
|
+
lxm: 'app.bsky.feed.getTimeline',
|
|
16
|
+
fallback: async () => {
|
|
17
|
+
const url = new URL(request.url);
|
|
18
|
+
const cursor = url.searchParams.get('cursor') ?? undefined;
|
|
19
|
+
const limitParam = Number.parseInt(url.searchParams.get('limit') ?? '', 10);
|
|
20
|
+
const limitInput = Number.isFinite(limitParam) ? limitParam : 50;
|
|
21
|
+
const limit = Math.max(1, Math.min(limitInput, 100));
|
|
22
|
+
|
|
23
|
+
const posts = await listPosts(env, limit, cursor);
|
|
24
|
+
const feed = await buildFeedViewPosts(env, posts);
|
|
25
|
+
const nextCursor = posts.length === limit ? String(posts[posts.length - 1].rowid) : undefined;
|
|
26
|
+
|
|
27
|
+
const payload: Record<string, unknown> = { feed };
|
|
28
|
+
if (nextCursor) payload.cursor = nextCursor;
|
|
29
|
+
|
|
30
|
+
return new Response(JSON.stringify(payload), {
|
|
31
|
+
headers: { 'Content-Type': 'application/json' },
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { buildProfileView, getPrimaryActor, matchesPrimaryActor } from '../../lib/actor';
|
|
4
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
5
|
+
|
|
6
|
+
export const prerender = false;
|
|
7
|
+
|
|
8
|
+
export async function GET({ locals, request }: APIContext) {
|
|
9
|
+
const { env } = locals.runtime;
|
|
10
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
11
|
+
|
|
12
|
+
return proxyAppView({
|
|
13
|
+
request,
|
|
14
|
+
env,
|
|
15
|
+
lxm: 'app.bsky.graph.getFollowers',
|
|
16
|
+
fallback: async () => {
|
|
17
|
+
const url = new URL(request.url);
|
|
18
|
+
const identifier = url.searchParams.get('actor');
|
|
19
|
+
const actor = await getPrimaryActor(env);
|
|
20
|
+
if (!matchesPrimaryActor(identifier, actor)) {
|
|
21
|
+
return new Response(JSON.stringify({ error: 'ActorNotFound' }), { status: 404 });
|
|
22
|
+
}
|
|
23
|
+
return new Response(
|
|
24
|
+
JSON.stringify({ subject: buildProfileView(actor), followers: [] }),
|
|
25
|
+
{ headers: { 'Content-Type': 'application/json' } },
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { buildProfileView, getPrimaryActor, matchesPrimaryActor } from '../../lib/actor';
|
|
4
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
5
|
+
|
|
6
|
+
export const prerender = false;
|
|
7
|
+
|
|
8
|
+
export async function GET({ locals, request }: APIContext) {
|
|
9
|
+
const { env } = locals.runtime;
|
|
10
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
11
|
+
|
|
12
|
+
return proxyAppView({
|
|
13
|
+
request,
|
|
14
|
+
env,
|
|
15
|
+
lxm: 'app.bsky.graph.getFollows',
|
|
16
|
+
fallback: async () => {
|
|
17
|
+
const url = new URL(request.url);
|
|
18
|
+
const identifier = url.searchParams.get('actor');
|
|
19
|
+
const actor = await getPrimaryActor(env);
|
|
20
|
+
if (!matchesPrimaryActor(identifier, actor)) {
|
|
21
|
+
return new Response(JSON.stringify({ error: 'ActorNotFound' }), { status: 404 });
|
|
22
|
+
}
|
|
23
|
+
return new Response(
|
|
24
|
+
JSON.stringify({ subject: buildProfileView(actor), follows: [] }),
|
|
25
|
+
{ headers: { 'Content-Type': 'application/json' } },
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { getLabelerServiceViews } from '../../lib/labeler';
|
|
3
|
+
|
|
4
|
+
export const prerender = false;
|
|
5
|
+
|
|
6
|
+
export async function GET({ locals, request }: APIContext) {
|
|
7
|
+
const { env } = locals.runtime;
|
|
8
|
+
const url = new URL(request.url);
|
|
9
|
+
|
|
10
|
+
const didParams = url.searchParams.getAll('dids');
|
|
11
|
+
const dids = didParams
|
|
12
|
+
.flatMap((entry) => entry.split(',').map((did) => did.trim()))
|
|
13
|
+
.filter(Boolean);
|
|
14
|
+
|
|
15
|
+
if (dids.length === 0) {
|
|
16
|
+
return new Response(JSON.stringify({ error: 'BadRequest', message: 'dids parameter required' }), {
|
|
17
|
+
status: 400,
|
|
18
|
+
headers: { 'Content-Type': 'application/json' },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const detailed = url.searchParams.get('detailed') === 'true';
|
|
23
|
+
|
|
24
|
+
const views = await getLabelerServiceViews(env, dids, { detailed });
|
|
25
|
+
|
|
26
|
+
return new Response(JSON.stringify({ views }), {
|
|
27
|
+
headers: { 'Content-Type': 'application/json' },
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
4
|
+
|
|
5
|
+
export const prerender = false;
|
|
6
|
+
|
|
7
|
+
export async function GET({ locals, request }: APIContext) {
|
|
8
|
+
const { env } = locals.runtime;
|
|
9
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
10
|
+
|
|
11
|
+
return proxyAppView({
|
|
12
|
+
request,
|
|
13
|
+
env,
|
|
14
|
+
lxm: 'app.bsky.notification.getUnreadCount',
|
|
15
|
+
fallback: async () =>
|
|
16
|
+
new Response(JSON.stringify({ count: 0 }), {
|
|
17
|
+
headers: { 'Content-Type': 'application/json' },
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { proxyAppView } from '../../lib/appview';
|
|
3
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
4
|
+
|
|
5
|
+
export const prerender = false;
|
|
6
|
+
|
|
7
|
+
export async function GET({ locals, request }: APIContext) {
|
|
8
|
+
const { env } = locals.runtime;
|
|
9
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
10
|
+
|
|
11
|
+
return proxyAppView({
|
|
12
|
+
request,
|
|
13
|
+
env,
|
|
14
|
+
lxm: 'app.bsky.notification.listNotifications',
|
|
15
|
+
fallback: async () =>
|
|
16
|
+
new Response(
|
|
17
|
+
JSON.stringify({
|
|
18
|
+
notifications: [],
|
|
19
|
+
priority: false,
|
|
20
|
+
seenAt: new Date(0).toISOString(),
|
|
21
|
+
}),
|
|
22
|
+
{
|
|
23
|
+
headers: { 'Content-Type': 'application/json' },
|
|
24
|
+
},
|
|
25
|
+
),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
3
|
+
|
|
4
|
+
export const prerender = false;
|
|
5
|
+
|
|
6
|
+
export async function GET({ locals, request }: APIContext) {
|
|
7
|
+
const { env } = locals.runtime;
|
|
8
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
9
|
+
|
|
10
|
+
return new Response(
|
|
11
|
+
JSON.stringify({
|
|
12
|
+
status: 'unknown',
|
|
13
|
+
lastInitiatedAt: new Date(0).toISOString(),
|
|
14
|
+
}),
|
|
15
|
+
{
|
|
16
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17
|
+
},
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
|
|
3
|
+
export const prerender = false;
|
|
4
|
+
|
|
5
|
+
export async function GET() {
|
|
6
|
+
return new Response(
|
|
7
|
+
JSON.stringify({
|
|
8
|
+
checkEmailConfirmed: false,
|
|
9
|
+
liveNow: [],
|
|
10
|
+
}),
|
|
11
|
+
{
|
|
12
|
+
headers: { 'Content-Type': 'application/json' },
|
|
13
|
+
},
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
3
|
+
import { getPrimaryActor } from '../../lib/actor';
|
|
4
|
+
import { listChatConvoLogs } from '../../lib/chat';
|
|
5
|
+
|
|
6
|
+
export const prerender = false;
|
|
7
|
+
|
|
8
|
+
export async function GET({ locals, request }: APIContext) {
|
|
9
|
+
const { env } = locals.runtime;
|
|
10
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
11
|
+
|
|
12
|
+
const url = new URL(request.url);
|
|
13
|
+
const cursorParam = url.searchParams.get('cursor');
|
|
14
|
+
const parsedCursor = Number.parseInt(cursorParam ?? '', 10);
|
|
15
|
+
const cursor = Number.isFinite(parsedCursor) ? parsedCursor : undefined;
|
|
16
|
+
|
|
17
|
+
const actor = await getPrimaryActor(env);
|
|
18
|
+
const { logs, cursor: nextCursor } = await listChatConvoLogs(env, actor.did, cursor);
|
|
19
|
+
|
|
20
|
+
const payload: Record<string, unknown> = { logs };
|
|
21
|
+
if (nextCursor) payload.cursor = nextCursor;
|
|
22
|
+
|
|
23
|
+
return new Response(JSON.stringify(payload), {
|
|
24
|
+
headers: { 'Content-Type': 'application/json' },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { APIContext } from 'astro';
|
|
2
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
3
|
+
import { listChatConvos } from '../../lib/chat';
|
|
4
|
+
import { getPrimaryActor } from '../../lib/actor';
|
|
5
|
+
|
|
6
|
+
export const prerender = false;
|
|
7
|
+
|
|
8
|
+
export async function GET({ locals, request }: APIContext) {
|
|
9
|
+
const { env } = locals.runtime;
|
|
10
|
+
if (!(await isAuthorized(request, env))) return unauthorized();
|
|
11
|
+
|
|
12
|
+
const url = new URL(request.url);
|
|
13
|
+
const limitInput = Number.parseInt(url.searchParams.get('limit') ?? '', 10);
|
|
14
|
+
const limit = Math.max(1, Math.min(Number.isFinite(limitInput) ? limitInput : 50, 100));
|
|
15
|
+
const cursorParam = url.searchParams.get('cursor');
|
|
16
|
+
const cursor = cursorParam ? Number.parseInt(cursorParam, 10) : undefined;
|
|
17
|
+
const readStateParam = url.searchParams.get('readState');
|
|
18
|
+
const statusParam = url.searchParams.get('status');
|
|
19
|
+
|
|
20
|
+
const filters = {
|
|
21
|
+
readState: readStateParam === 'unread' ? 'unread' : null,
|
|
22
|
+
status:
|
|
23
|
+
statusParam === 'request' || statusParam === 'accepted' ? statusParam : null,
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
const actor = await getPrimaryActor(env);
|
|
27
|
+
const { convos, cursor: nextCursor } = await listChatConvos(env, actor.did, limit, cursor, filters);
|
|
28
|
+
|
|
29
|
+
const payload: Record<string, unknown> = {
|
|
30
|
+
convos,
|
|
31
|
+
};
|
|
32
|
+
if (nextCursor) payload.cursor = nextCursor;
|
|
33
|
+
|
|
34
|
+
return new Response(JSON.stringify(payload), {
|
|
35
|
+
headers: { 'Content-Type': 'application/json' },
|
|
36
|
+
});
|
|
37
|
+
}
|