+38
-18
index/spacedust.ts
+38
-18
index/spacedust.ts
···
1
-
import { handleIndex, spacedustManager } from "../main.ts";
2
import { parseAtUri } from "../utils/aturi.ts";
3
import { resolveRecordFromURI } from "../utils/records.ts";
4
···
59
console.log("Received Spacedust message: ", msg);
60
61
const op = msg.link.operation;
62
-
// @ts-expect-error i really should enforce some smarter types for this
63
-
const doer = parseAtUri(msg.link.source_record).did;
64
-
if (!doer) return;
65
-
const rev = msg.link.source_rev;
66
const aturi = msg.link.source_record;
67
-
const value = await resolveRecordFromURI({uri: msg.link.source_record});
68
-
69
-
const subject = msg.link.subject;
70
-
71
-
if (!value) return;
72
73
-
handleIndex({
74
-
op,
75
-
doer,
76
-
rev,
77
-
aturi,
78
-
value,
79
-
indexsrc: "spacedust",
80
-
})
81
return;
82
83
// switch (msg.link.source) {
···
1
+
import { db, handleIndex, spacedustManager } from "../main.ts";
2
import { parseAtUri } from "../utils/aturi.ts";
3
import { resolveRecordFromURI } from "../utils/records.ts";
4
···
59
console.log("Received Spacedust message: ", msg);
60
61
const op = msg.link.operation;
62
+
const srcdid = parseAtUri(msg.link.source_record)?.did;
63
+
const srccol = parseAtUri(msg.link.source_record)?.collection;
64
+
if (!srcdid || !srccol) return;
65
+
const subject = msg.link.subject;
66
+
const subdid = parseAtUri(subject)?.did;
67
+
const subscol = parseAtUri(subject)?.collection;
68
+
if (!subdid || !subscol) return;
69
+
//const rev = msg.link.source_rev;
70
const aturi = msg.link.source_record;
71
+
//const value = await resolveRecordFromURI({uri: msg.link.source_record});
72
+
db.exec(`
73
+
INSERT INTO backlink_skeleton (
74
+
srcuri,
75
+
srcdid,
76
+
srcfield,
77
+
srccol,
78
+
suburi,
79
+
subdid,
80
+
subcol
81
+
) VALUES (
82
+
'${aturi}',
83
+
'${srcdid}',
84
+
'${srccol}',
85
+
'${msg.link.source}',
86
+
'${subject}',
87
+
'${subdid}',
88
+
'${subscol}'
89
+
);
90
+
`);
91
+
//if (!value) return;
92
93
+
// handleIndex({
94
+
// op,
95
+
// doer,
96
+
// rev,
97
+
// aturi,
98
+
// value,
99
+
// indexsrc: "spacedust",
100
+
// })
101
return;
102
103
// switch (msg.link.source) {
+562
-35
main.ts
+562
-35
main.ts
···
5
import { handleSpacedust, startSpacedust } from "./index/spacedust.ts";
6
import { handleJetstream, startJetstream } from "./index/jetstream.ts";
7
import { Database } from "jsr:@db/sqlite@0.11";
8
-
import express from "npm:express";
9
-
import { createServer } from "./xrpc/index.ts";
10
import { indexHandlerContext } from "./index/types.ts";
11
-
import * as XRPCTypes from "./utils/xrpc.ts"
12
import { didDocument } from "./utils/diddoc.ts";
13
14
// ------------------------------------------
···
33
//keyCacheTTL: 10 * 60 * 1000,
34
});
35
36
-
const app = express();
37
-
const server = createServer();
38
-
app.use(server.xrpc.router);
39
-
app.listen(3768);
40
41
-
app.get('/.well-known/did.json', (req, res) => {
42
-
res.setHeader('Access-Control-Allow-Origin', '*');
43
-
res.setHeader('Content-Type', 'application/did+json');
44
-
res.json(didDocument);
45
-
});
46
47
-
app.get('/health', (req, res) => {
48
-
res.send('OK');
49
-
});
50
51
-
// ------------------------------------------
52
-
// XRPC Method Implementations
53
-
// ------------------------------------------
54
55
-
server.app.bsky.actor.getProfile({
56
-
auth: authVerifier,
57
-
handler: async ({ auth, params }): Promise<XRPCTypes.AppBskyActorGetProfile.HandlerSuccess> => {
58
-
console.log("xrpcbaby",auth,params)
59
-
return {
60
-
encoding: "application/json",
61
-
body: {
62
-
did: params.actor,
63
-
handle: "example.com",
64
-
displayName: "baby's first XRPC Method Implemented",
65
-
avatar: undefined,
66
-
description: `the auth is [${JSON.stringify(auth)}] and params are [${JSON.stringify(params)}]`,
67
-
// @ts-expect-error its safe, probably
68
-
"testudefinedfield": "wow youre are an idiotee",
69
-
},
70
-
};
71
-
},
72
});
73
74
// ------------------------------------------
75
// Indexer
···
5
import { handleSpacedust, startSpacedust } from "./index/spacedust.ts";
6
import { handleJetstream, startJetstream } from "./index/jetstream.ts";
7
import { Database } from "jsr:@db/sqlite@0.11";
8
+
//import express from "npm:express";
9
+
//import { createServer } from "./xrpc/index.ts";
10
import { indexHandlerContext } from "./index/types.ts";
11
+
import * as XRPCTypes from "./utils/xrpc.ts";
12
import { didDocument } from "./utils/diddoc.ts";
13
14
// ------------------------------------------
···
33
//keyCacheTTL: 10 * 60 * 1000,
34
});
35
36
+
// ------------------------------------------
37
+
// XRPC Method Implementations
38
+
// ------------------------------------------
39
+
40
+
// begin the hell of implementing api requests and incoming records
41
+
//const seenStrings = new Set<string>();
42
+
43
+
let preferences: any = undefined;
44
+
Deno.serve({ port: 3768 }, async (req: Request): Promise<Response> => {
45
+
const url = new URL(req.url);
46
+
const pathname = new URL(req.url).pathname;
47
+
let reqBody: undefined | string;
48
+
let jsonbody: undefined | Record<string, unknown>;
49
+
if (req.body) {
50
+
const body = await req.json();
51
+
jsonbody = body;
52
+
console.log(
53
+
`called at euh reqreqreqreq: ${pathname}\n\n${JSON.stringify(body)}`
54
+
);
55
+
reqBody = JSON.stringify(body, null, 2);
56
+
}
57
+
if (pathname === "/.well-known/did.json") {
58
+
return new Response(JSON.stringify(didDocument), {
59
+
headers: withCors({ "Content-Type": "application/json" }),
60
+
});
61
+
}
62
+
if (pathname === "/health") {
63
+
return new Response("OK", {
64
+
status: 200,
65
+
headers: withCors({
66
+
"Content-Type": "text/plain",
67
+
}),
68
+
});
69
+
}
70
+
71
+
// if (seenStrings.has(url.hash)) {
72
+
// // The string has been seen before
73
+
// seenStrings.delete(url.hash);
74
+
// return new Response("OK", {
75
+
// status: 204,
76
+
// headers: withCors({
77
+
// "Content-Type": "text/plain",
78
+
// }),
79
+
// });
80
+
// }
81
+
// seenStrings.add(url.hash);
82
+
//const reqBody = req.body ? await req.text() : null;
83
+
84
+
console.log("→ Path:", pathname);
85
+
console.log("→ Auth:", req.headers.get("authorization"));
86
+
console.log("→ Body:", reqBody);
87
+
88
+
const bskyUrl = `https://api.bsky.app${pathname}${url.search}`;
89
+
const hasAuth = req.headers.has("authorization");
90
+
const xrpcMethod = pathname.startsWith("/xrpc/")
91
+
? pathname.slice("/xrpc/".length)
92
+
: null;
93
+
94
+
//if (xrpcMethod !== 'app.bsky.actor.getPreferences' && xrpcMethod !== 'app.bsky.notification.listNotifications') {
95
+
if (
96
+
(!hasAuth ||
97
+
xrpcMethod === "app.bsky.labeler.getServices" ||
98
+
xrpcMethod === "app.bsky.unspecced.getConfig") &&
99
+
xrpcMethod !== "app.bsky.notification.putPreferences"
100
+
) {
101
+
const proxyHeaders = new Headers(req.headers);
102
+
103
+
// Remove Authorization and set browser-like User-Agent
104
+
proxyHeaders.delete("authorization");
105
+
proxyHeaders.delete("Access-Control-Allow-Origin"),
106
+
proxyHeaders.set(
107
+
"user-agent",
108
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
109
+
);
110
+
proxyHeaders.set("Access-Control-Allow-Origin", "*");
111
+
112
+
const proxyRes = await fetch(bskyUrl, {
113
+
method: req.method,
114
+
headers: proxyHeaders,
115
+
body: ["GET", "HEAD"].includes(req.method.toUpperCase())
116
+
? undefined
117
+
: reqBody,
118
+
});
119
+
120
+
const resBody = await proxyRes.text();
121
+
122
+
console.log(
123
+
"← Response:",
124
+
JSON.stringify(await JSON.parse(resBody), null, 2)
125
+
);
126
+
127
+
return new Response(resBody, {
128
+
status: proxyRes.status,
129
+
headers: proxyRes.headers,
130
+
});
131
+
}
132
+
133
+
const authDID = "did:plc:mn45tewwnse5btfftvd3powc"; //getAuthenticatedDid(req);
134
+
135
+
const jsonUntyped = jsonbody;
136
+
137
+
switch (xrpcMethod) {
138
+
case "app.bsky.actor.getPreferences": {
139
+
const jsonTyped =
140
+
jsonUntyped as XRPCTypes.AppBskyActorGetPreferences.QueryParams;
141
+
// if (!(await authDID))
142
+
// return new Response("Unauthorized", { status: 401 });
143
+
144
+
const response: XRPCTypes.AppBskyActorGetPreferences.OutputSchema = {
145
+
preferences: [
146
+
{
147
+
$type: "app.bsky.actor.defs#savedFeedsPrefV2",
148
+
items: [
149
+
{
150
+
$type: "app.bsky.actor.defs#savedFeed",
151
+
id: "3l6wlykrwdk2w",
152
+
type: "feed",
153
+
value:
154
+
"at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot",
155
+
pinned: true,
156
+
},
157
+
],
158
+
},
159
+
{
160
+
$type: "app.bsky.actor.defs#savedFeedsPref",
161
+
pinned: [
162
+
"at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot",
163
+
],
164
+
saved: [
165
+
"at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot",
166
+
],
167
+
},
168
+
{
169
+
$type: "app.bsky.actor.defs#bskyAppStatePref",
170
+
nuxs: [
171
+
{
172
+
$type: "app.bsky.actor.defs#nux",
173
+
id: "TenMillionDialog",
174
+
completed: true,
175
+
},
176
+
{ id: "NeueTypography", completed: true },
177
+
{ id: "NeueChar", completed: true },
178
+
{ id: "InitialVerificationAnnouncement", completed: true },
179
+
{ id: "ActivitySubscriptions", completed: true },
180
+
{ id: "PolicyUpdate202508", completed: true },
181
+
],
182
+
},
183
+
],
184
+
};
185
+
// {
186
+
// preferences: [
187
+
// {
188
+
// $type: "app.bsky.actor.defs#savedFeedsPref",
189
+
// pinned: [""],
190
+
// saved: [""],
191
+
// },
192
+
// ],
193
+
// };
194
+
195
+
if (preferences === undefined) {
196
+
preferences = response.preferences;
197
+
}
198
+
199
+
const newprefswowowowow: XRPCTypes.AppBskyActorGetPreferences.OutputSchema =
200
+
{
201
+
preferences: preferences,
202
+
};
203
+
204
+
return new Response(JSON.stringify(newprefswowowowow), {
205
+
headers: withCors({ "Content-Type": "application/json" }),
206
+
});
207
+
}
208
+
case "app.bsky.actor.getProfile": {
209
+
const jsonTyped =
210
+
jsonUntyped as XRPCTypes.AppBskyActorGetProfile.QueryParams;
211
+
212
+
const response: XRPCTypes.AppBskyActorGetProfile.OutputSchema = {
213
+
$type: "app.bsky.actor.defs#profileViewDetailed",
214
+
did: "did:plc:mn45tewwnse5btfftvd3powc",
215
+
handle: "whey.party",
216
+
displayName: "Whey!?@??#?",
217
+
description: "idiot piece of shit",
218
+
avatar:
219
+
"https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:mn45tewwnse5btfftvd3powc/bafkreid4nhd5pdbzqshkcfxwwcpfz4a5xk2n4gk5truu6hyfk6abynpaze@jpeg",
220
+
associated: {
221
+
$type: "app.bsky.actor.defs#profileAssociated",
222
+
lists: 2,
223
+
feedgens: 4,
224
+
starterPacks: 6,
225
+
labeler: false,
226
+
chat: {
227
+
$type: "app.bsky.actor.defs#profileAssociatedChat",
228
+
allowIncoming: "all",
229
+
},
230
+
activitySubscription: {
231
+
$type: "app.bsky.actor.defs#profileAssociatedActivitySubscription",
232
+
allowSubscriptions: "followers",
233
+
},
234
+
},
235
+
indexedAt: "2024-10-23T08:55:16.641Z",
236
+
createdAt: "2024-10-23T08:55:16.641Z",
237
+
banner:
238
+
"https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:mn45tewwnse5btfftvd3powc/bafkreibqkz2eq3wzpqh44vuicbezckeo6i4g6m66v5ifek5hc5frc6ihdq@jpeg",
239
+
followersCount: 69420,
240
+
followsCount: 69420,
241
+
postsCount: 69420,
242
+
//associated?: ProfileAssociated
243
+
//joinedViaStarterPack?: AppBskyGraphDefs.StarterPackViewBasic
244
+
//indexedAt?: string
245
+
//createdAt?: string
246
+
viewer: {
247
+
$type: "app.bsky.actor.defs#viewerState",
248
+
muted: false,
249
+
//mutedByList: AppBskyGraphDefs.ListViewBasic
250
+
blockedBy: false,
251
+
//blocking?: string
252
+
//blockingByList?: AppBskyGraphDefs.ListViewBasic
253
+
//following?: string
254
+
//followedBy?: string
255
+
//knownFollowers?: KnownFollowers
256
+
activitySubscription: {
257
+
$type: "app.bsky.notification.defs#activitySubscription",
258
+
post: true,
259
+
reply: true,
260
+
},
261
+
},
262
+
labels: [
263
+
{
264
+
$type: "com.atproto.label.defs#label",
265
+
/** The AT Protocol version of the label object. */
266
+
ver: 1,
267
+
/** DID of the actor who created this label. */
268
+
src: "did:plc:ar7c4by46qjdydhdevvrndac",
269
+
/** AT URI of the record, repository (account), or other resource that this label applies to. */
270
+
uri: "at://did:plc:ar7c4by46qjdydhdevvrndac",
271
+
/** Optionally, CID specifying the specific version of 'uri' resource this label applies to. */
272
+
//cid?: string
273
+
/** The short string name of the value or type of this label. */
274
+
val: "idiot",
275
+
/** If true, this is a negation label, overwriting a previous label. */
276
+
//neg?: boolean
277
+
/** Timestamp when this label was created. */
278
+
cts: "2024-10-23T08:55:16.641Z",
279
+
/** Timestamp at which this label expires (no longer applies). */
280
+
//exp?: string
281
+
/** Signature of dag-cbor encoded label. */
282
+
//sig?: Uint8Array
283
+
},
284
+
],
285
+
pinnedPost: {
286
+
$type: "com.atproto.repo.strongRef",
287
+
uri: "at://did:plc:mn45tewwnse5btfftvd3powc/app.bsky.feed.post/3lvybv7b6ic2h",
288
+
cid: "bafyreie44fqjarvwv3n3se6fhpf2mvodlguiwahqh7ugm3nyyujlmd36ce",
289
+
},
290
+
verification: {
291
+
$type: "app.bsky.actor.defs#verificationState",
292
+
/** All verifications issued by trusted verifiers on behalf of this user. Verifications by untrusted verifiers are not included. */
293
+
verifications: [
294
+
{
295
+
$type: "app.bsky.actor.defs#verificationView",
296
+
/** The user who issued this verification. */
297
+
issuer: "did:plc:ar7c4by46qjdydhdevvrndac",
298
+
/** The AT-URI of the verification record. */
299
+
uri: "at://did:plc:mn45tewwnse5btfftvd3powc/app.bsky.feed.post/3lvybv7b6ic2h",
300
+
/** True if the verification passes validation, otherwise false. */
301
+
isValid: true,
302
+
/** Timestamp when the verification was created. */
303
+
createdAt: "2024-10-23T08:55:16.641Z",
304
+
},
305
+
],
306
+
/** The user's status as a verified account. */
307
+
verifiedStatus: "valid",
308
+
/** The user's status as a trusted verifier. */
309
+
trustedVerifierStatus: "valid",
310
+
},
311
+
status: {
312
+
$type: "app.bsky.actor.defs#statusView",
313
+
/** The status for the account. */
314
+
status: "app.bsky.actor.status#live",
315
+
record: {
316
+
$type: "app.bsky.graph.verification",
317
+
createdAt: "2025-05-02T18:12:17.199Z",
318
+
displayName: "teq (lowercase)",
319
+
handle: "quilling.dev",
320
+
subject: "did:plc:jrtgsidnmxaen4offglr5lsh",
321
+
},
322
+
//embed?: $Typed<AppBskyEmbedExternal.View> | { $type: string }
323
+
/** The date when this status will expire. The application might choose to no longer return the status after expiration. */
324
+
expiresAt: "2028-10-23T08:55:16.641Z",
325
+
/** True if the status is not expired, false if it is expired. Only present if expiration was set. */
326
+
isActive: true,
327
+
},
328
+
};
329
+
330
+
return new Response(JSON.stringify(response), {
331
+
headers: withCors({ "Content-Type": "application/json" }),
332
+
});
333
+
}
334
+
case "app.bsky.actor.getProfiles": {
335
+
const jsonTyped =
336
+
jsonUntyped as XRPCTypes.AppBskyActorGetProfiles.QueryParams;
337
+
338
+
const response: XRPCTypes.AppBskyActorGetProfiles.OutputSchema = {
339
+
profiles: [
340
+
{
341
+
$type: "app.bsky.actor.defs#profileViewDetailed",
342
+
did: "did:plc:mn45tewwnse5btfftvd3powc",
343
+
handle: "whey.party",
344
+
displayName: "Whey!?@??#?",
345
+
description: "idiot piece of shit",
346
+
avatar:
347
+
"https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:mn45tewwnse5btfftvd3powc/bafkreid4nhd5pdbzqshkcfxwwcpfz4a5xk2n4gk5truu6hyfk6abynpaze@jpeg",
348
+
//associated?: ProfileAssociated
349
+
indexedAt: "2024-10-23T08:55:16.641Z",
350
+
createdAt: "2024-10-23T08:55:16.641Z",
351
+
//banner?: string
352
+
followersCount: 69420,
353
+
followsCount: 69420,
354
+
postsCount: 69420,
355
+
//associated?: ProfileAssociated
356
+
//joinedViaStarterPack?: AppBskyGraphDefs.StarterPackViewBasic
357
+
//indexedAt?: string
358
+
//createdAt?: string
359
+
//viewer?: ViewerState
360
+
//labels?: ComAtprotoLabelDefs.Label[]
361
+
//pinnedPost?: ComAtprotoRepoStrongRef.Main
362
+
//verification?: VerificationState
363
+
//status?: StatusView
364
+
},
365
+
],
366
+
};
367
+
368
+
return new Response(JSON.stringify(response), {
369
+
headers: withCors({ "Content-Type": "application/json" }),
370
+
});
371
+
}
372
+
case "app.bsky.notification.listNotifications": {
373
+
const jsonTyped =
374
+
jsonUntyped as XRPCTypes.AppBskyNotificationListNotifications.QueryParams;
375
+
376
+
const response: XRPCTypes.AppBskyNotificationListNotifications.OutputSchema =
377
+
{
378
+
notifications: [
379
+
{
380
+
$type: "app.bsky.notification.listNotifications#notification",
381
+
uri: "at://did:plc:mn45tewwnse5btfftvd3powc/app.bsky.feed.post/3lvybv7b6ic2h",
382
+
cid: "bafyreie44fqjarvwv3n3se6fhpf2mvodlguiwahqh7ugm3nyyujlmd36ce",
383
+
author: {
384
+
$type: "app.bsky.actor.defs#profileView",
385
+
did: "did:plc:mn45tewwnse5btfftvd3powc",
386
+
handle: "whey.party",
387
+
displayName: "Whey!?@??#?",
388
+
description: "idiot piece of shit",
389
+
avatar:
390
+
"https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:mn45tewwnse5btfftvd3powc/bafkreid4nhd5pdbzqshkcfxwwcpfz4a5xk2n4gk5truu6hyfk6abynpaze@jpeg",
391
+
//associated?: ProfileAssociated
392
+
indexedAt: "2024-10-23T08:55:16.641Z",
393
+
createdAt: "2024-10-23T08:55:16.641Z",
394
+
viewer: {
395
+
$type: "app.bsky.actor.defs#viewerState",
396
+
muted: false,
397
+
//mutedByList?: AppBskyGraphDefs.ListViewBasic
398
+
//blockedBy?: boolean
399
+
//blocking?: string
400
+
//blockingByList?: AppBskyGraphDefs.ListViewBasic
401
+
//following?: string
402
+
//followedBy?: string
403
+
//knownFollowers?: KnownFollowers
404
+
//activitySubscription?: AppBskyNotificationDefs.ActivitySubscription
405
+
},
406
+
//labels?: ComAtprotoLabelDefs.Label[]
407
+
//verification?: VerificationState
408
+
//status?: StatusView
409
+
},
410
+
reason: "follow",
411
+
//reasonSubject?: string
412
+
record: {
413
+
$type: "app.bsky.graph.follow",
414
+
subject: "did:plc:lulmyldiq4sb2ikags5sfb25",
415
+
createdAt: "2025-08-12T04:58:14.657Z",
416
+
},
417
+
isRead: false,
418
+
indexedAt: "2024-10-23T08:55:16.641Z",
419
+
//labels?: ComAtprotoLabelDefs.Label[]
420
+
},
421
+
],
422
+
};
423
424
+
return new Response(JSON.stringify(response), {
425
+
headers: withCors({ "Content-Type": "application/json" }),
426
+
});
427
+
}
428
+
case "app.bsky.notification.putPreferences": {
429
+
if (jsonbody) {
430
+
const body = jsonbody;
431
+
//console.log("Body:", body);
432
+
preferences = body.preferences;
433
+
}
434
435
+
const response: XRPCTypes.AppBskyUnspeccedGetConfig.OutputSchema = {
436
+
checkEmailConfirmed: true,
437
+
liveNow: [
438
+
{
439
+
$type: "app.bsky.unspecced.getConfig#liveNowConfig",
440
+
did: "did:plc:mn45tewwnse5btfftvd3powc",
441
+
domains: ["local3768forumtest.whey.party"],
442
+
},
443
+
],
444
+
};
445
446
+
return new Response(JSON.stringify(response), {
447
+
headers: withCors({ "Content-Type": "application/json" }),
448
+
});
449
450
+
// const response: XRPCTypes.AppBskyActorPutPreferences.OutputSchema =
451
+
// undefined;
452
+
return new Response(`{"hello":"world"}`, {
453
+
status: 200,
454
+
headers: withCors(),
455
+
});
456
+
}
457
+
case "app.bsky.actor.putPreferencesshittyholleeeheoeoelelllo": {
458
+
if (jsonbody) {
459
+
const body = jsonbody;
460
+
//console.log("Body:", body);
461
+
preferences = body.preferences;
462
+
}
463
+
464
+
const response: XRPCTypes.AppBskyUnspeccedGetConfig.OutputSchema = {
465
+
checkEmailConfirmed: true,
466
+
liveNow: [
467
+
{
468
+
$type: "app.bsky.unspecced.getConfig#liveNowConfig",
469
+
did: "did:plc:mn45tewwnse5btfftvd3powc",
470
+
domains: ["local3768forumtest.whey.party"],
471
+
},
472
+
],
473
+
};
474
+
475
+
return new Response(JSON.stringify(response), {
476
+
headers: withCors({ "Content-Type": "application/json" }),
477
+
});
478
+
479
+
// const response: XRPCTypes.AppBskyActorPutPreferences.OutputSchema =
480
+
// undefined;
481
+
return new Response(`{"hello":"world"}`, {
482
+
status: 200,
483
+
headers: withCors(),
484
+
});
485
+
}
486
+
case "chat.bsky.convo.getLog": {
487
+
const jsonTyped =
488
+
jsonUntyped as XRPCTypes.ChatBskyConvoGetLog.QueryParams;
489
+
490
+
const response: XRPCTypes.ChatBskyConvoGetLog.OutputSchema = {
491
+
logs: [],
492
+
};
493
+
494
+
return new Response(JSON.stringify(response), {
495
+
headers: withCors({ "Content-Type": "application/json" }),
496
+
});
497
+
}
498
+
case "chat.bsky.convo.listConvos": {
499
+
const jsonTyped =
500
+
jsonUntyped as XRPCTypes.ChatBskyConvoListConvos.QueryParams;
501
+
502
+
const response: XRPCTypes.ChatBskyConvoListConvos.OutputSchema = {
503
+
convos: [],
504
+
};
505
+
506
+
return new Response(JSON.stringify(response), {
507
+
headers: withCors({ "Content-Type": "application/json" }),
508
+
});
509
+
}
510
+
case "app.bsky.unspecced.getConfig": {
511
+
const jsonTyped =
512
+
jsonUntyped as XRPCTypes.AppBskyUnspeccedGetConfig.QueryParams;
513
+
514
+
const response: XRPCTypes.AppBskyUnspeccedGetConfig.OutputSchema = {
515
+
checkEmailConfirmed: true,
516
+
liveNow: [
517
+
{
518
+
$type: "app.bsky.unspecced.getConfig#liveNowConfig",
519
+
did: "did:plc:mn45tewwnse5btfftvd3powc",
520
+
domains: ["local3768forumtest.whey.party"],
521
+
},
522
+
],
523
+
};
524
+
525
+
return new Response(JSON.stringify(response), {
526
+
headers: withCors({ "Content-Type": "application/json" }),
527
+
});
528
+
}
529
+
case "app.bsky.graph.getLists": {
530
+
const jsonTyped =
531
+
jsonUntyped as XRPCTypes.AppBskyGraphGetLists.QueryParams;
532
+
533
+
const response: XRPCTypes.AppBskyGraphGetLists.OutputSchema = {
534
+
lists: [],
535
+
};
536
+
537
+
return new Response(JSON.stringify(response), {
538
+
headers: withCors({ "Content-Type": "application/json" }),
539
+
});
540
+
}
541
+
//https://shimeji.us-east.host.bsky.network/xrpc/app.bsky.unspecced.getTrendingTopics?limit=14
542
+
case "app.bsky.unspecced.getTrendingTopics": {
543
+
const jsonTyped =
544
+
jsonUntyped as XRPCTypes.AppBskyUnspeccedGetTrendingTopics.QueryParams;
545
+
546
+
const response: XRPCTypes.AppBskyUnspeccedGetTrendingTopics.OutputSchema =
547
+
{
548
+
topics: [
549
+
{
550
+
$type: "app.bsky.unspecced.defs#trendingTopic",
551
+
topic: "coolio",
552
+
displayName: "coolio",
553
+
description: "coolio",
554
+
link: "https://custom-appview.deer-social.pages.dev/lists",
555
+
},
556
+
],
557
+
suggested: [
558
+
{
559
+
$type: "app.bsky.unspecced.defs#trendingTopic",
560
+
topic: "coolio",
561
+
displayName: "coolio",
562
+
description: "coolio",
563
+
link: "https://custom-appview.deer-social.pages.dev/lists",
564
+
},
565
+
],
566
+
};
567
+
568
+
return new Response(JSON.stringify(response), {
569
+
headers: withCors({ "Content-Type": "application/json" }),
570
+
});
571
+
}
572
+
default: {
573
+
return new Response(
574
+
JSON.stringify({
575
+
error: "XRPCNotSupported",
576
+
message: "HEY hello there my name is whey dot party and you have used my custom appview that is very cool but have you considered that XRPC Not Supported",
577
+
}),
578
+
{
579
+
status: 404,
580
+
headers: withCors({ "Content-Type": "application/json" }),
581
+
}
582
+
);
583
+
}
584
+
}
585
});
586
+
587
+
function withCors(headers: HeadersInit = {}) {
588
+
return {
589
+
"Access-Control-Allow-Origin": "*",
590
+
...headers,
591
+
};
592
+
}
593
+
const corsfree = {
594
+
"Access-Control-Allow-Origin": "*",
595
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
596
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
597
+
};
598
+
const json = "application/json";
599
+
600
601
// ------------------------------------------
602
// Indexer
+34
-13
utils/auth.ts
+34
-13
utils/auth.ts
···
1
-
import { AuthResult, MethodAuthVerifier, XRPCError } from "npm:@atproto/xrpc-server";
2
import * as borrowed from "./auth.borrowed.ts";
3
4
export interface AuthConfig {
···
18
console.log("Authentication module initialized.");
19
}
20
21
-
export async function getAuthenticatedDid(req: Request): Promise<string | null> {
22
const authHeader = req.headers.get("Authorization");
23
return await internalGetAuthenticatedDid(authHeader ?? undefined);
24
}
25
26
-
async function internalGetAuthenticatedDid(authHeader: string | undefined): Promise<string | null> {
27
if (!isInitialized) {
28
-
console.error("Authentication module has not been initialized. Call setupAuth() first.");
29
return null;
30
}
31
if (!authHeader || !authHeader.startsWith("Bearer ")) {
···
44
45
return result.payload.iss as string;
46
} catch (err) {
47
-
console.warn("JWT verification failed:", err instanceof Error ? err.message : String(err));
48
return null;
49
}
50
}
51
52
export const authVerifier: MethodAuthVerifier<AuthResult> = async ({ req }) => {
53
-
console.log("help us all fuck you",req)
54
-
const authHeader = (req as any).headers['authorization'];
55
-
56
const did = await internalGetAuthenticatedDid(authHeader);
57
58
-
if (!did) {
59
-
// i dont know the correct xrpc spec for this
60
-
throw new XRPCError(401, 'AuthenticationRequired', 'Invalid or missing authentication token.');
61
-
}
62
63
console.log(`Successfully authenticated DID: ${did}`);
64
···
67
did: did,
68
},
69
};
70
-
};
···
1
+
import {
2
+
AuthResult,
3
+
MethodAuthVerifier,
4
+
XRPCError,
5
+
} from "npm:@atproto/xrpc-server";
6
import * as borrowed from "./auth.borrowed.ts";
7
8
export interface AuthConfig {
···
22
console.log("Authentication module initialized.");
23
}
24
25
+
export async function getAuthenticatedDid(
26
+
req: Request
27
+
): Promise<string | null> {
28
const authHeader = req.headers.get("Authorization");
29
return await internalGetAuthenticatedDid(authHeader ?? undefined);
30
}
31
32
+
async function internalGetAuthenticatedDid(
33
+
authHeader: string | undefined
34
+
): Promise<string | null> {
35
if (!isInitialized) {
36
+
console.error(
37
+
"Authentication module has not been initialized. Call setupAuth() first."
38
+
);
39
return null;
40
}
41
if (!authHeader || !authHeader.startsWith("Bearer ")) {
···
54
55
return result.payload.iss as string;
56
} catch (err) {
57
+
console.warn(
58
+
"JWT verification failed:",
59
+
err instanceof Error ? err.message : String(err)
60
+
);
61
return null;
62
}
63
}
64
65
export const authVerifier: MethodAuthVerifier<AuthResult> = async ({ req }) => {
66
+
//console.log("help us all fuck you",req)
67
+
console.log("you are doing well")
68
+
const url = (req as any).url;
69
+
const params = (req as any).params ?? {};
70
+
console.log("Request info:", { url, params });
71
+
return {
72
+
credentials: "did:plc:mn45tewwnse5btfftvd3powc",
73
+
};
74
+
const authHeader = (req as any).headers["authorization"];
75
+
76
const did = await internalGetAuthenticatedDid(authHeader);
77
78
+
// throw this later dont do it here
79
+
// if (!did) {
80
+
// // i dont know the correct xrpc spec for this
81
+
// throw new XRPCError(401, 'AuthenticationRequired', 'Invalid or missing authentication token.');
82
+
// }
83
84
console.log(`Successfully authenticated DID: ${did}`);
85
···
88
did: did,
89
},
90
};
91
+
};
+21
utils/dbsetup.ts
+21
utils/dbsetup.ts
···
26
handle TEXT
27
);
28
${createIndexINE} idx_did_handle ON did(handle);
29
+
30
+
${createTableINE} prefs (
31
+
did TEXT PRIMARY KEY NOT NULL,
32
+
json TEXT
33
+
);
34
+
${createIndexINE} idx_prefs_did ON prefs(did);
35
+
36
+
${createTableINE} backlink_skeleton (
37
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
38
+
srcuri TEXT,
39
+
srcdid TEXT,
40
+
srcfield TEXT,
41
+
srccol TEXT,
42
+
suburi TEXT,
43
+
subdid TEXT,
44
+
subcol TEXT
45
+
);
46
+
${createIndexINE} idx_backlink_subdid_mod ON backlink_skeleton(subdid, srcdid);
47
+
${createIndexINE} idx_backlink_suburi_mod ON backlink_skeleton(suburi, srcdid);
48
+
${createIndexINE} idx_backlink_subdid_filter_mod ON backlink_skeleton(subdid, srccol, srcdid);
49
+
${createIndexINE} idx_backlink_suburi_filter_mod ON backlink_skeleton(suburi, srccol, srcdid);
50
51
${createTableINE} app_bsky_actor_profile (
52
${baseColumns},