Add remote interaction

This commit is contained in:
Shadowfacts 2022-05-16 00:41:01 -04:00
parent b5f101a4e7
commit 7d8754ad82
8 changed files with 175 additions and 3 deletions

View File

@ -4,6 +4,7 @@ import comments from "./comments";
import federate from "./federate"; import federate from "./federate";
import followers from "./followers"; import followers from "./followers";
import inbox from "./inbox"; import inbox from "./inbox";
import interact from "./interact";
import nodeinfo from "./nodeinfo"; import nodeinfo from "./nodeinfo";
import webfinger from "./webfinger"; import webfinger from "./webfinger";
@ -14,6 +15,7 @@ export = {
federate, federate,
followers, followers,
inbox, inbox,
interact,
nodeinfo, nodeinfo,
webfinger webfinger
}; };

View File

@ -0,0 +1,18 @@
import {Router} from "express";
import {queryWebfinger} from "./webfinger";
const domain = process.env.DOMAIN;
export default async function interact(router: Router) {
router.post("/interact", async (req, res) => {
const permalink = req.body.permalink;
const acct = req.body.remote_follow.acct;
const webfingerResult = await queryWebfinger(acct);
const link = webfingerResult.links.find((l) => l.rel === "http://ostatus.org/schema/1.0/subscribe");
if (link && 'template' in link) {
res.redirect(link.template.replace("{uri}", `https://${domain}${permalink}`));
} else {
res.status(400).send("Unable to find remote subscribe URL");
}
});
}

View File

@ -1,9 +1,21 @@
import express, { Router } from "express"; import express, { Router } from "express";
import fetch from "node-fetch";
const domain = process.env.DOMAIN; const domain = process.env.DOMAIN;
export default function webfinger(router: Router) { export default function webfinger(router: Router) {
router.get("/.well-known/webfinger", (req, res) => { router.get("/.well-known/webfinger", (req, res) => {
res.json({
subject: `acct:block@${domain}`,
aliases: [`https://${domain}/ap/actor`],
links: [
{
rel: "self",
type: "application/activity+json",
href: `https://${domain}/ap/actor`,
}
]
} as WebfingerResult);
res.json({ res.json({
"subject": `acct:blog@${domain}`, "subject": `acct:blog@${domain}`,
"links": [ "links": [
@ -16,4 +28,23 @@ export default function webfinger(router: Router) {
}); });
res.end(); res.end();
}); });
} }
export async function queryWebfinger(acct: string): Promise<WebfingerResult> {
if (acct.startsWith("@")) {
acct = acct.substring(1);
}
const parts = acct.split("@");
if (parts.length !== 2) {
throw "Invalid account";
}
const response = await fetch(`https://${parts[1]}/.well-known/webfinger?resource=${acct}`);
const json = await response.json();
return json as WebfingerResult;
}
export interface WebfingerResult {
subject: string;
aliases: string[];
links: Array<{rel: string, type: string, href: string} | {rel: "https://ostatus.org/schema/1.0/subscribe", template: string}>;
}

View File

@ -48,6 +48,7 @@ function watch() {
const app = express(); const app = express();
app.use(morgan("dev")); app.use(morgan("dev"));
app.use(bodyParser.json({ type: "application/activity+json" })); app.use(bodyParser.json({ type: "application/activity+json" }));
app.use(bodyParser.urlencoded());
const connection = await createConnection({ const connection = await createConnection({
"type": "postgres", "type": "postgres",
@ -82,6 +83,8 @@ function watch() {
await activitypub.articles.setup(posts); await activitypub.articles.setup(posts);
await activitypub.interact(app);
const apRouter = Router(); const apRouter = Router();
apRouter.use(validateHttpSig); apRouter.use(validateHttpSig);
await activitypub.actor(apRouter); await activitypub.actor(apRouter);

66
package-lock.json generated
View File

@ -31,6 +31,7 @@
"markdown-it": "^8.4.2", "markdown-it": "^8.4.2",
"markdown-it-footnote": "^3.0.2", "markdown-it-footnote": "^3.0.2",
"morgan": "^1.9.1", "morgan": "^1.9.1",
"node-fetch": "^2.6.7",
"pg": "^8.5.1", "pg": "^8.5.1",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"request": "^2.88.0", "request": "^2.88.0",
@ -1746,6 +1747,25 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/normalize-path": { "node_modules/normalize-path": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@ -2511,6 +2531,11 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
}, },
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
},
"node_modules/ts-node": { "node_modules/ts-node": {
"version": "9.1.1", "version": "9.1.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
@ -2877,6 +2902,20 @@
"extsprintf": "^1.2.0" "extsprintf": "^1.2.0"
} }
}, },
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wrappy": { "node_modules/wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@ -4305,6 +4344,14 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
}, },
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": {
"whatwg-url": "^5.0.0"
}
},
"normalize-path": { "normalize-path": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@ -4911,6 +4958,11 @@
} }
} }
}, },
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
},
"ts-node": { "ts-node": {
"version": "9.1.1", "version": "9.1.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
@ -5169,6 +5221,20 @@
"extsprintf": "^1.2.0" "extsprintf": "^1.2.0"
} }
}, },
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",

View File

@ -29,6 +29,7 @@
"markdown-it": "^8.4.2", "markdown-it": "^8.4.2",
"markdown-it-footnote": "^3.0.2", "markdown-it-footnote": "^3.0.2",
"morgan": "^1.9.1", "morgan": "^1.9.1",
"node-fetch": "^2.6.7",
"pg": "^8.5.1", "pg": "^8.5.1",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"request": "^2.88.0", "request": "^2.88.0",

View File

@ -354,7 +354,47 @@ article {
} }
#comments-info { #comments-info {
margin-top: 0; margin: 0;
}
#remote-interact {
display: flex;
flex-direction: row;
align-items: baseline;
input {
margin-left: 4px;
}
input[type=text] {
flex-grow: 1;
padding: 0 4px;
background-color: var(--content-background-color);
border: 1px solid var(--accent-color);
font-size: 1rem;
line-height: 2rem;
color: var(--content-text-color);
}
input[type=submit] {
background-color: var(--ui-background-color);
border: 1px solid var(--accent-color);
color: var(--accent-color);
line-height: 2rem;
padding: 0 1rem;
text-decoration: none;
font-weight: bold;
text-transform: uppercase;
-webkit-transition: 0.3s ease-out;
transition: 0.3s ease-out;
&:hover {
background-color: var(--accent-color);
color: var(--ui-background-color);
cursor: pointer;
}
}
} }
#comments-js-warning { #comments-js-warning {

View File

@ -23,9 +23,20 @@ metadata.layout = "default.html.ejs"
<h2 id="comments-container-title">Comments</h2> <h2 id="comments-container-title">Comments</h2>
</summary> </summary>
<p id="comments-info"> <p id="comments-info">
Comments powered by ActivityPub. To respond to this post or to another comment, copy its URL into the search interface of your client for Mastodon, Pleroma, or other compatible software. Comments powered by ActivityPub. To respond to this post enter your username and instance below, or copy its URL into the search interface of your client for Mastodon, Pleroma, or other compatible software.
<a href="/2019/reincarnation/#activity-pub">Learn more</a>. <a href="/2019/reincarnation/#activity-pub">Learn more</a>.
</p> </p>
<form action="/interact" method="POST" id="remote-interact">
<span>Reply from your instance:</span>
<% if (metadata.useOldPermalinkForComments) { %>
<input type="hidden" name="permalink" value="<%= metadata.oldPermalink %>">
<% } else { %>
<input type="hidden" name="permalink" value="<%= metadata.permalink %>">
<% } %>
<!-- name needs to be exactly that to get the browser to use same completions as mastodon -->
<input type="text" placeholder="Enter your user@domain" required id="acct" name="remote_follow[acct]">
<input type="submit" value="Interact">
</form>
<noscript> <noscript>
<p id="comments-js-warning"> <p id="comments-js-warning">
JavaScript is required to display comments. JavaScript is required to display comments.