fixed error?

This commit is contained in:
Deva Midhun 2025-02-08 21:27:00 +00:00
parent 0995069a74
commit ce942c6476
6 changed files with 64 additions and 88 deletions

View File

@ -1,6 +1,6 @@
import type { VercelRequest, VercelResponse } from '@vercel/node'; import type { VercelRequest, VercelResponse } from '@vercel/node';
export default function (req: VercelRequest, res: VercelResponse) { export default async function (req: VercelRequest, res: VercelResponse) {
const { headers } = req; const { headers } = req;
if ("accept" in headers) { if ("accept" in headers) {
@ -13,12 +13,13 @@ export default function (req: VercelRequest, res: VercelResponse) {
res.statusCode = 200; res.statusCode = 200;
res.setHeader("Content-Type", `application/activity+json`); res.setHeader("Content-Type", `application/activity+json`);
res.json({ res.json({
"@context": ["https://www.w3.org/ns/activitystreams", { "@language": "en- US" }], "@context": ["https://www.w3.org/ns/activitystreams", { "@language": "en-US" }],
"type": "Person", "type": "Person",
"id": "https://coderrrrr.site/coder", "id": "https://coderrrrr.site/coder",
"outbox": "https://coderrrrr.site/outbox", "outbox": "https://coderrrrr.site/outbox",
"following": "https://coderrrrr.site/following", "following": "https://coderrrrr.site/following",
"followers": "https://coderrrrr.site/followers", "followers": "https://coderrrrr.site/followers",
"sharedInbox": "https://coderrrrr.site/sharedInbox"
"inbox": "https://coderrrrr.site/inbox", "inbox": "https://coderrrrr.site/inbox",
"preferredUsername": "coder", "preferredUsername": "coder",
"name": "Deva Midhun's blog", "name": "Deva Midhun's blog",

View File

@ -1,6 +1,6 @@
import type { VercelRequest, VercelResponse } from '@vercel/node'; import type { VercelRequest, VercelResponse } from '@vercel/node';
export default function (req: VercelRequest, res: VercelResponse) { export default async function (req: VercelRequest, res: VercelResponse) {
res.statusCode = 200; res.statusCode = 200;
res.setHeader("Content-Type", `application/jrd+json`); res.setHeader("Content-Type", `application/jrd+json`);
res.end('ok'); res.end('ok');

View File

@ -1,6 +1,6 @@
import type { VercelRequest, VercelResponse } from '@vercel/node'; import type { VercelRequest, VercelResponse } from '@vercel/node';
export default function (req: VercelRequest, res: VercelResponse) { export default async function (req: VercelRequest, res: VercelResponse) {
const output = { const output = {
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
"id": "https://coderrrrr.site/api/activitypub/following", "id": "https://coderrrrr.site/api/activitypub/following",

View File

@ -8,7 +8,7 @@ import { readFileSync } from 'fs';
It's a GET request. This doesn't post it to anyone's timeline. It's a GET request. This doesn't post it to anyone's timeline.
*/ */
export default function (req: VercelRequest, res: VercelResponse) { export default async function (req: VercelRequest, res: VercelResponse) {
// All of the outbox data is generated at build time, so just return that static file. // All of the outbox data is generated at build time, so just return that static file.
const file = join(cwd(), 'public', 'outbox.ajson'); const file = join(cwd(), 'public', 'outbox.ajson');
const stringified = readFileSync(file, 'utf8'); const stringified = readFileSync(file, 'utf8');

View File

@ -19,102 +19,73 @@ if (!admin.apps.length) {
const db = admin.firestore(); const db = admin.firestore();
/*
Sends the latest not that hasn't yet been sent.
*/
export default async function (req: VercelRequest, res: VercelResponse) { export default async function (req: VercelRequest, res: VercelResponse) {
const { body, query, method, url, headers } = req; if (req.headers.authorization !== `Bearer ${process.env.CRON_SECRET}`) {
if (
headers.authorization !== `Bearer ${process.env.CRON_SECRET}`
) {
return res.status(401).end("Unauthorized"); return res.status(401).end("Unauthorized");
} }
const configCollection = db.collection('config'); const configRef = db.collection('config').doc("config");
const configRef = configCollection.doc("config");
const config = await configRef.get(); const config = await configRef.get();
const lastId = config.exists ? config.data()?.lastId || "" : "";
if (config.exists == false) { // Fetch notes from outbox
// Config doesn't exist, make something const outboxResponse = await fetch("https://coderrrrr.site/api/activitypub/outbox");
configRef.set({ const outbox = <OrderedCollection> await outboxResponse.json();
"lastId": ""
});
}
const configData = config.data();
let lastId = "";
if (configData != undefined) {
lastId = configData.lastId;
}
// Get my outbox because it contains all my notes.
const outboxResponse = await fetch('https://coderrrrr.site/outbox');
const outbox = <OrderedCollection>(await outboxResponse.json());
const followersCollection = db.collection('followers');
const followersQuerySnapshot = await followersCollection.get();
const followersSnapshot = await db.collection('followers').get();
let lastSuccessfulSentId = ""; let lastSuccessfulSentId = "";
for (const followerDoc of followersQuerySnapshot.docs) { const inboxes = new Set<string>(); // Track unique inboxes to avoid duplicate sending
for (const followerDoc of followersSnapshot.docs) {
const follower = followerDoc.data(); const follower = followerDoc.data();
try { const actorUrl = typeof follower.actor === "string" ? follower.actor : follower.actor.id;
const actorUrl = (typeof follower.actor == "string") ? follower.actor : follower.actor.id;
console.log(`Fetching actor information for ${actorUrl}`)
const actorInformation = await fetchActorInformation(actorUrl);
if (actorInformation == undefined) {
// We can't send to this actor, so skip the actor. We should log it.
continue;
}
if (actorInformation.inbox == undefined) {
console.log(
`Actor ${actorUrl} doesn't have an inbox, so we can't send to them. ${actorInformation}`
);
}
const actorInbox = new URL(actorInformation.inbox.toString());
for (const iteIdx in (<AP.EntityReference[]>outbox.orderedItems)) { console.log(`Fetching actor information for ${actorUrl}`);
// We have to break somewhere... do it after the first. const actorInformation = await fetchActorInformation(actorUrl);
const item = (<AP.EntityReference[]>outbox.orderedItems)[iteIdx]; if (!actorInformation || !actorInformation.inbox) {
console.log(`Skipping ${actorUrl}: No valid inbox`);
console.log(`Checking ID ${item.id}, ${lastId}`); continue;
if (item.id == `${lastId}`) {
lastSuccessfulSentId = item.id;
// We've already posted this, don't try and send it again.
console.log(`${item.id} has already been posted - don't attempt`)
break;
}
if (item.object != undefined) {
// We might not need this.
item.object.published = (new Date()).toISOString();
}
// Item will be an entity, i.e, { Create { Note } }
try {
console.log(`Sending to ${actorInbox}`);
const response = await sendSignedRequest(actorInbox, <AP.Activity> item);
console.log(`Send result: ${actorInbox}`, response.status, response.statusText, await response.text());
// It's not been sent.
lastSuccessfulSentId = item.id; // we shouldn't really set this every time.
} catch (sendSignedError) {
console.log("Error sending signed request", sendSignedError)
}
break; // At some point we might want to post more than one post, so remove this.
}
} catch (ex) {
console.log("Error", ex);
} }
const inboxUrl = actorInformation.sharedInbox || actorInformation.inbox;
inboxes.add(inboxUrl.toString()); // Add to set to ensure unique delivery
} }
configRef.set({ for (const item of <AP.EntityReference[]>outbox.orderedItems) {
"lastId": lastSuccessfulSentId if (item.id === lastId) {
}); console.log(`${item.id} has already been posted - skipping`);
break;
}
if (item.object) {
item.object.published = new Date().toISOString();
}
for (const inboxUrl of inboxes) {
try {
console.log(`Sending to ${inboxUrl}`);
const response = await sendSignedRequest(new URL(inboxUrl), <AP.Activity> item, {
headers: {
"Accept": "application/activity+json",
"Content-Type": "application/activity+json"
}
});
console.log(`Send result: ${response.status} ${response.statusText}`);
const responseText = await response.text();
console.log("Response body:", responseText);
lastSuccessfulSentId = item.id;
} catch (error) {
console.error(`Error sending to ${inboxUrl}:`, error);
}
}
break; // Only send the latest post for now
}
await configRef.set({ "lastId": lastSuccessfulSentId });
res.status(200).end("ok"); res.status(200).end("ok");
}; };

View File

@ -47,6 +47,10 @@
"source": "/inbox", "source": "/inbox",
"destination": "/api/activitypub/inbox.ts" "destination": "/api/activitypub/inbox.ts"
}, },
{
"source": "/sharedInbox",
"destination": "/api/activitypub/sharedInbox.ts"
},
{ {
"source": "/outbox", "source": "/outbox",
"destination": "/api/activitypub/outbox.ts" "destination": "/api/activitypub/outbox.ts"
@ -54,7 +58,7 @@
], ],
"headers": [ "headers": [
{ {
"source": "/(.*).ajson", "source": "/(.*).json",
"headers": [ "headers": [
{ {
"key": "content-type", "key": "content-type",