forked from shadowfacts/shadowfacts.net
102 lines
2.8 KiB
TypeScript
102 lines
2.8 KiB
TypeScript
|
import { Router, Request, Response } from "express";
|
||
|
import uuidv4 from "uuid/v4";
|
||
|
import { fetchActor, signAndSend } from "./federate";
|
||
|
import { Activity, Follow, Accept, Undo, Create, Note } from "./activity";
|
||
|
import { Database } from "sqlite3";
|
||
|
import { URL } from "url";
|
||
|
|
||
|
const domain = process.env.DOMAIN;
|
||
|
|
||
|
export default function inbox(): Router {
|
||
|
const router = Router();
|
||
|
|
||
|
router.post("/ap/inbox", handleInbox);
|
||
|
router.post("/inbox", handleInbox);
|
||
|
|
||
|
return router;
|
||
|
}
|
||
|
|
||
|
async function handleInbox(req: Request, res: Response) {
|
||
|
console.log(req.body);
|
||
|
const activity = req.body as Activity;
|
||
|
if (activity.type === "Follow") {
|
||
|
handleFollow(activity, req, res);
|
||
|
} else if (activity.type === "Create") {
|
||
|
handleCreate(activity, req, res);
|
||
|
} else if (activity.type === "Undo") {
|
||
|
handleUndo(activity, req, res);
|
||
|
} else {
|
||
|
res.end(); // TODO: handle this better
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async function handleFollow(activity: Activity, req: Request, res: Response) {
|
||
|
if (typeof req.body.object !== "string") {
|
||
|
res.end(); // TODO: handle this better
|
||
|
return;
|
||
|
}
|
||
|
const follow = activity as Follow;
|
||
|
if (follow.object !== `https://${domain}/ap/actor`) {
|
||
|
res.end();
|
||
|
return;
|
||
|
}
|
||
|
const actor = await fetchActor(follow.actor);
|
||
|
const acceptObject = <Accept>{
|
||
|
"@context": [
|
||
|
"https://www.w3.org/ns/activitystreams",
|
||
|
// "https://w3id.org/security/v1"
|
||
|
],
|
||
|
"id": `https://${domain}/ap/${uuidv4()}`,
|
||
|
"type": "Accept",
|
||
|
"actor": `https://${domain}/ap/actor`,
|
||
|
"object": follow
|
||
|
};
|
||
|
signAndSend(acceptObject, actor.inbox);
|
||
|
const db = req.app.get("db") as Database;
|
||
|
const serverInbox = new URL("/inbox", actor.inbox).toString();
|
||
|
db.run("INSERT OR IGNORE INTO followers(id, inbox) VALUES($id, $inbox)", {
|
||
|
$id: actor.id,
|
||
|
$inbox: serverInbox
|
||
|
}, (err) => {
|
||
|
if (err) console.error(`Encountered error adding follower ${follow.actor}`, err);
|
||
|
});
|
||
|
res.end();
|
||
|
}
|
||
|
|
||
|
async function handleCreate(activity: Activity, req: Request, res: Response) {
|
||
|
const create = activity as Create;
|
||
|
if (create.object.type == "Note") {
|
||
|
handleCreateNote(create, req, res);
|
||
|
} else {
|
||
|
res.end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async function handleCreateNote(create: Create, req: Request, res: Response) {
|
||
|
const note = create.object as Note;
|
||
|
console.log(note);
|
||
|
}
|
||
|
|
||
|
async function handleUndo(activity: Activity, req: Request, res: Response) {
|
||
|
const undo = activity as Undo;
|
||
|
if (undo.object.type === "Follow") {
|
||
|
handleUndoFollow(undo, req, res);
|
||
|
} else {
|
||
|
res.end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async function handleUndoFollow(undo: Undo, req: Request, res: Response) {
|
||
|
const follow = undo.object as Follow;
|
||
|
if (follow.object !== `https://${domain}/ap/actor`) {
|
||
|
res.end();
|
||
|
return;
|
||
|
}
|
||
|
const db = req.app.get("db") as Database;
|
||
|
db.run("DELETE FROM followers WHERE id = $id", {
|
||
|
$id: follow.actor
|
||
|
}, (err) => {
|
||
|
if (err) console.error(`Error unfollowing ${follow.actor}`, err);
|
||
|
});
|
||
|
res.end();
|
||
|
}
|