162 lines
4.3 KiB
TypeScript
162 lines
4.3 KiB
TypeScript
import express, { Request } from "express";
|
|
import bodyParser from "body-parser";
|
|
import { toCollection as getOutbox } from "./outbox.js";
|
|
import { getById as getUserById, getNickname, get as getUserByNickname, getId as getUserId, User } from "./user.js";
|
|
import { Routes } from "./router.js";
|
|
import { getBySlug as getArticleBySlug, getById, getByUserId } from "./article.js";
|
|
import { userToPerson, TYPE as ACTIVITYPUB_TYPE, handleInboxPost, createArticleObject, CONTEXT, createArticleActivity } from "./activity.js";
|
|
import { handleFollowerGet } from "./follower.js";
|
|
import { handleWebfingerGet } from "./net.js";
|
|
|
|
const escapeHtml = (unsafe: string) => unsafe
|
|
.replaceAll("&", "&")
|
|
.replaceAll("<", "<")
|
|
.replaceAll(">", ">")
|
|
.replaceAll("\"", """)
|
|
.replace("'", "'");
|
|
|
|
const port = parseInt(process.env.port || "8080");
|
|
const app = express();
|
|
|
|
app.use(
|
|
bodyParser.text({ type: "*/*" })
|
|
);
|
|
|
|
app.post(Routes.inbox, handleInboxPost);
|
|
app.get(Routes.followers, handleFollowerGet);
|
|
|
|
app.get(Routes.object, async (req, res) => {
|
|
const article = await getById(parseInt(req.params.id));
|
|
|
|
if (article) {
|
|
if (req.accepts("html")) {
|
|
res.redirect(`/${article.slug}.html`);
|
|
}
|
|
else if (req.accepts(["text/plain", "text/markdown"])) {
|
|
res.redirect(`/${article.slug}.md`);
|
|
}
|
|
else {
|
|
const nickname = await getNickname(article.users_id) as string;
|
|
|
|
const obj = createArticleObject(article, nickname);
|
|
obj["@context"] = CONTEXT;
|
|
|
|
res.append("Content-Type", ACTIVITYPUB_TYPE);
|
|
res.send(JSON.stringify(obj, null, 4));
|
|
}
|
|
}
|
|
else {
|
|
res.status(404).end();
|
|
}
|
|
});
|
|
|
|
app.get(Routes.activity, async (req, res) => {
|
|
let id = parseInt(req.params.id);
|
|
if (id >= 1_000_000_000) {
|
|
// it's a delete. TODO: implement.
|
|
res.status(501).end();
|
|
}
|
|
else {
|
|
const article = await getById(id);
|
|
|
|
if (article) {
|
|
if (req.accepts("html")) {
|
|
res.redirect(`/${article.slug}.html`);
|
|
}
|
|
else if (req.accepts(["text/plain", "text/markdown"])) {
|
|
res.redirect(`/${article.slug}.md`);
|
|
}
|
|
else {
|
|
const user = await getUserById(article.users_id) as User;
|
|
const activity = createArticleActivity(article, user);
|
|
|
|
res.append("Content-Type", ACTIVITYPUB_TYPE);
|
|
res.send(JSON.stringify(activity, null, 4));
|
|
}
|
|
}
|
|
else {
|
|
res.status(404).end();
|
|
}
|
|
}
|
|
});
|
|
|
|
app.get("/.well-known/webfinger", handleWebfingerGet);
|
|
// app.get("/.well-known/nodeinfo", SITE.net.nodeInfoLocation.get);
|
|
// app.get("/nodeinfo/:version", SITE.net.nodeInfo.get);
|
|
|
|
app.get(Routes.outbox, async (req: Request<{ actor: string }>, res) => {
|
|
const body = await getOutbox(req.params.actor);
|
|
|
|
if (body) {
|
|
res.append("Content-Type", ACTIVITYPUB_TYPE);
|
|
res.send(body);
|
|
}
|
|
else {
|
|
res.status(404).send("not found");
|
|
}
|
|
|
|
});
|
|
|
|
app.get(Routes.actor, async (req, res) => {
|
|
const nickname = req.params.actor;
|
|
const actor = await getUserByNickname(nickname);
|
|
|
|
if (actor) {
|
|
if (req.accepts("html")) {
|
|
const posts = (await getByUserId(actor.id)).map((a) => `<li><a href="/${a.slug}.html">${escapeHtml(a.title)}</a></li>`).join("");
|
|
|
|
const body = `<!DOCTYPE html>
|
|
<html>
|
|
username: ${escapeHtml(actor.nickname)}
|
|
<br>
|
|
Name: ${escapeHtml(actor.name)}
|
|
<p>
|
|
Bio: ${escapeHtml(actor.bio)}
|
|
<p>
|
|
Posts:<br>
|
|
<ul>
|
|
${posts}
|
|
</ul>
|
|
`;
|
|
|
|
res.set("Content-Type", "text/html; charset=utf-8");
|
|
res.send(body);
|
|
}
|
|
else {
|
|
const obj = userToPerson(actor);
|
|
const body = JSON.stringify(obj, null, 4);
|
|
|
|
res.set("Content-Type", ACTIVITYPUB_TYPE);
|
|
res.send(body);
|
|
}
|
|
}
|
|
else {
|
|
res.status(404).send("actor not found");
|
|
}
|
|
});
|
|
|
|
app.get("/:slug.html", async (req, res) => {
|
|
const article = await getArticleBySlug(req.params.slug);
|
|
|
|
if (!article || article.deleted) {
|
|
res.status(404).send("not found");
|
|
}
|
|
else {
|
|
const filename = article.file.replace(/\.md$/, ".html");
|
|
res.sendFile(process.env.base_dir + "/" + filename);
|
|
}
|
|
});
|
|
|
|
app.get("/:slug.md", async (req, res) => {
|
|
const article = await getArticleBySlug(req.params.slug);
|
|
|
|
if (!article || article.deleted) {
|
|
res.status(404).send("not found");
|
|
}
|
|
else {
|
|
res.sendFile(process.env.base_dir + "/" + article.file);
|
|
}
|
|
});
|
|
|
|
const SERVER = app.listen(port);
|