Compare commits

...

7 Commits

Author SHA1 Message Date
lamp 74b06c1441 0.2.3 2022-12-26 23:41:02 -06:00
lamp 25837658eb Update 'README.md' 2022-12-26 23:40:48 -06:00
lamp 011ba4c248 npm 2022-12-26 23:23:19 -06:00
lamp fbd6762aba redirect instead of block? 2022-12-26 21:57:23 -06:00
lamp 1a1b5a5179 make it better
fix pleroma 500
fix user block
block html proxying
2022-12-26 20:26:45 -06:00
lamp 0a5ca65ade Merge branch 'master' of gitea.moe:lamp/activitypub-proxy 2022-12-24 16:40:45 -06:00
lamp 4e577d3b7b convert to mjs 2022-12-24 16:40:31 -06:00
6 changed files with 3449 additions and 3244 deletions
+1
View File
@@ -1,2 +1,3 @@
node_modules
keys.sqlite
.env
+2
View File
@@ -0,0 +1,2 @@
keys.sqlite
.env
+3 -5
View File
@@ -8,19 +8,17 @@ The intended usage of this is as an alternative to using alt accounts or moving
The major caveat with this particular implementation is that if you boost or reply to proxied posts, your followers will see and interact with those authors via masquerade as well, causing a bit of a mess. This is unless you limit the usage to whitelisted domains/users, which you will almost definitely have to do or someone will abuse it and get it blocked. In that case, anyone who isn't whitelisted just won't see the boosts.
With that said, this initial implementation is more of an experimental proof-of-concept of what can be done with so little code, but to implement this concept properly, it probably needs to be rewritten with a deeper, stricter integration into the ActivityPub protocol. Access controls need to be more flexible and reliable, and a solution needs to be found for the boosting issue.
## Installation
You will need a host with Node.js 15 or newer, and a wildcard domain with HTTPS pointed to your server. Cloudflare may be easiest, as you can bind the app to an extra IP address and connect Cloudflare directly to it.
Download the repository and `npm i`. Then you can run it with the following environment variables.
Download the repository and `npm i`. Then you can run it with the following environment variables (you can create a .env file).
- `PORT`: The port to listen for HTTP, default: 80.
- `BIND_IP`: The IP address to bind to. Default: all.
- `DOMAIN_WHITELIST`: Comma-separated list of domains that can use the proxy. Recommended to use this to prevent abuse as otherwise anyone can proxy anything. Remember to include the domains you want to follow from as well as the domains you want to follow, as it needs to work both ways.
- `USER_WHITELIST`: Comma-separated list of names that can be looked up. Maybe useful if you don't want other users messing with other users... 🤷 Note that this only restricts the webfinger. (todo could be circumvented? 🤔) Default: any
- `DOMAIN_WHITELIST`: Comma-separated list of domains that can be proxied. Recommended to use this to prevent abuse as otherwise anyone can proxy anything. Remember to include the domains you want to follow from as well as the domains you want to follow, as it needs to work both ways. Default: any
- `USER_WHITELIST`: Comma-separated list of usernames that can be proxied (case-sensitive, name only without `@`). Default: any
- `NODE_ENV`: Set to `development` to see debug logs.
Install by copying and pasting this example systemd file to `/etc/systemd/system/ap-proxy.service` (or similar), and editing as needed:
+97 -61
View File
@@ -1,17 +1,23 @@
import * as dotenv from "dotenv";
dotenv.config();
if (process.env.NODE_ENV != "development") {
process.env.NODE_ENV = "production";
console.debug = () => {};
}
if (process.env.DOMAIN_WHITELIST) var DOMAIN_WHITELIST = process.env.DOMAIN_WHITELIST.split(',').map(x=>x.trim().toLowerCase()).filter(x=>x);
if (process.env.USER_WHITELIST) var USER_WHITELIST = process.env.USER_WHITELIST.split(',').map(x=>x.trim().toLowerCase()).filter(x=>x);
if (process.env.USER_WHITELIST) var USER_WHITELIST = process.env.USER_WHITELIST.split(',').map(x=>x.trim()).filter(x=>x);
var express = require("express");
require("express-async-errors");
var fetch = require("node-fetch");
var Keyv = require("keyv");
var crypto = require("crypto");
var generateKeyPair = require("util").promisify(crypto.generateKeyPair);
import express from "express";
import "express-async-errors";
import fetch from "node-fetch";
import Keyv from "keyv";
import {Sha256Signer, Parser} from "activitypub-http-signatures";
import * as crypto from "crypto";
import * as util from "util";
var generateKeyPair = util.promisify(crypto.generateKeyPair);
var parser = new Parser();
var keystore = new Keyv("sqlite://keys.sqlite");
@@ -41,40 +47,40 @@ app.use(async (req, res, next) => {
if (!req.subdomains[0]) return next();
var TARGET_NODE = req.subdomains[0].replaceAll(/(?<!-)-(?!-)/g, '.').replaceAll('--','-');
if (DOMAIN_WHITELIST && !DOMAIN_WHITELIST.includes(TARGET_NODE)) return res.status(403).send(`target ${TARGET_NODE} is not whitelisted`);
var TARGET_REGEXP = new RegExp(`(?<!\\.)${TARGET_NODE.replaceAll('.','\\.')}`, 'gi');
var TARGET_MASQUERADE = req.hostname;
var TARGET_URL = `https://${TARGET_NODE}${req.url.replaceAll(TARGET_MASQUERADE, TARGET_NODE)}`;
if (DOMAIN_WHITELIST && !DOMAIN_WHITELIST.includes(TARGET_NODE)) {
console.debug(`target ${TARGET_NODE} blocked by whitelist`);
//res.status(403).send(`target ${TARGET_NODE} is not whitelisted`);
res.redirect(308, TARGET_URL);
return;
}
var CLIENT_NODE = req.get("User-Agent").match(/(?<=https:\/\/)[a-z0-9-\.]+/i)?.[0];
if (DOMAIN_WHITELIST && !DOMAIN_WHITELIST.includes(CLIENT_NODE)) return res.status(403).send(`client ${CLIENT_NODE} is not whitelisted`);
if (CLIENT_NODE) {
if (DOMAIN_WHITELIST && !DOMAIN_WHITELIST.includes(CLIENT_NODE)) {
console.debug(`client ${CLIENT_NODE} blocked by whitelist`);
//res.status(403).send(`client ${CLIENT_NODE} is not whitelisted`);
res.redirect(308, TARGET_URL);
return;
}
var CLIENT_REGEXP = new RegExp(`(?<!\\.)${CLIENT_NODE.replaceAll('.','\\.')}`, 'gi');
var CLIENT_MASQUERADE = [CLIENT_NODE.replaceAll('-','--').replaceAll('.','-'), ...req.hostname.split('.').slice(-2)].join('.');
}
if (USER_WHITELIST && req.url.startsWith("/.well-known/webfinger") && !USER_WHITELIST.includes(req.query.resource?.replace('acct:','').split('@')[0])) return res.status(403).send("user not whitelisted");
var url = `https://${TARGET_NODE}${req.url.replaceAll(TARGET_MASQUERADE, TARGET_NODE)}`;
var opts = {method: req.method, headers: {
host: TARGET_NODE,
date: new Date().toUTCString()
"host": TARGET_NODE,
"date": new Date().toUTCString(),
}};
if (req.method == "POST") {
var {Sha256Signer, Parser} = await import("activitypub-http-signatures");
var parser = new Parser();
var signature = parser.parse({url: req.url, method: req.method, headers: req.headers});
console.debug({signature});
var publicKeyPem = (await keystore.get(signature.keyId))?.publicKey;
if (!publicKeyPem) {
console.debug("fetching public key", signature.keyId);
let user_res = await fetch(signature.keyId, {headers:{Accept:"application/json"}});
if (!user_res.ok || !user_res.headers.get("content-type").includes("json")) return res.status(400).send("cannot verify");
let user_json = await user_res.json();
console.debug({user_json});
publicKeyPem = user_json.publicKey.publicKeyPem;
keystore.set(signature.keyId, {publicKey: publicKeyPem});
}
var publicKeyPem = await getRemotePubkey(signature.keyId);
if (!publicKeyPem) return res.status(400).send("could not get pubkey");
if (!signature.verify(publicKeyPem)) {
res.status(400).send("bad signature");
console.debug("bad signature");
@@ -82,65 +88,62 @@ app.use(async (req, res, next) => {
}
var modifiedPayload = req.body.replaceAll(TARGET_MASQUERADE, TARGET_NODE).replaceAll(CLIENT_REGEXP, CLIENT_MASQUERADE);
console.debug({CLIENT_NODE,CLIENT_REGEXP,CLIENT_MASQUERADE,original:req.body,modified:modifiedPayload});
console.debug({CLIENT_NODE, CLIENT_REGEXP, CLIENT_MASQUERADE, original:req.body, modified:modifiedPayload});
var digest = crypto.createHash("sha256").update(modifiedPayload, "utf-8").digest("base64");
opts.headers.digest = `sha-256=${digest}`;
opts.headers["digest"] = `sha-256=${digest}`;
var clientMasqueradeKeyId = signature.keyId.replaceAll(CLIENT_NODE, CLIENT_MASQUERADE);
var clientMasqueradePrivateKeyPem = (await keystore.get(clientMasqueradeKeyId))?.privateKey;
if (!clientMasqueradePrivateKeyPem) {
console.debug("making new masquerade key (client)");
var {publicKey, privateKey} = await generateKeyPair('rsa', {
publicKeyEncoding: {type:'pkcs1', format: 'pem'},
privateKeyEncoding: {type:'pkcs1', format: 'pem'},
modulusLength: 2048
});
clientMasqueradePrivateKeyPem = privateKey;
await keystore.set(clientMasqueradeKeyId, {publicKey, privateKey});
}
var clientMasqueradePrivateKeyPem = (await getLocalKeypair(clientMasqueradeKeyId))?.privateKey;
var signer = new Sha256Signer({
publicKeyId: clientMasqueradeKeyId,
privateKey: clientMasqueradePrivateKeyPem,
headerNames: ['(request-target)', 'host', 'date', 'digest']
});
opts.headers.signature = signer.sign({url, method: opts.method, headers: opts.headers});
opts.headers["signature"] = signer.sign({url: TARGET_URL, method: opts.method, headers: opts.headers});
opts.headers["content-tength"] = modifiedPayload.length;
if (req.get("Content-Type")) opts.headers["content-type"] = req.get("Content-Type");
opts.body = modifiedPayload;
}
if (req.get("Accept")) opts.headers.Accept = req.get("Accept");
var target_res = await fetch(url, opts);
console.debug(target_res.status, target_res.statusText, target_res.headers.get("content-type"));
if (req.get("User-Agent")) opts.headers["user-agent"] = req.get("User-Agent").replaceAll(CLIENT_NODE, CLIENT_MASQUERADE);
if (req.get("Accept")) opts.headers["accept"] = req.get("Accept");
var target_res = await fetch(TARGET_URL, opts);
var contentType = target_res.headers.get("content-type");
console.debug(target_res.status, target_res.statusText, contentType);
//note this affects html attachments from pleroma
if (contentType.startsWith("text/html")) {
//res.status(403).send("html is not allowed");
res.redirect(308, TARGET_URL);
return;
}
if ([
"application/jrd+json",
"application/activity+json",
"application/json",
"application/xrd+xml",
"application/xml",
"text/",
"charset=utf-8"
].some(t => target_res.headers.get("content-type")?.toLowerCase().includes(t))) {
"application/xml"
].some(t => contentType?.toLowerCase().startsWith(t))) {
var originalText = await target_res.text(), modifiedText = originalText;
console.debug({originalText})
if (target_res.headers.get("content-type").includes("json")) {
if (contentType.includes("json")) {
var json = JSON.parse(originalText);
if (json.preferredUsername && USER_WHITELIST && !USER_WHITELIST.includes(json.preferredUsername)) {
console.debug(`user ${json.preferredUsername} blocked by whitelist`);
//res.status(403).send(`${json.preferredUsername} is not whitelisted`);
res.redirect(308, TARGET_URL);
return;
}
if (json.publicKey) {
console.debug("has key");
await keystore.set(json.publicKey.id, {publicKey: json.publicKey.publicKeyPem});
var masqueradeKeyId = json.publicKey.id.replaceAll(TARGET_REGEXP, TARGET_MASQUERADE);
var masqueradeKeyPem = (await keystore.get(masqueradeKeyId))?.publicKey;
if (!masqueradeKeyPem) {
console.debug("making new masquerade key (target)");
var {publicKey, privateKey} = await generateKeyPair('rsa', {
publicKeyEncoding: {type:'pkcs1', format: 'pem'},
privateKeyEncoding: {type:'pkcs1', format: 'pem'},
modulusLength: 2048
});
masqueradeKeyPem = publicKey;
await keystore.set(masqueradeKeyId, {publicKey, privateKey});
}
var masqueradeKeyPem = (await getLocalKeypair(masqueradeKeyId)).publicKey;
json.publicKey.id = masqueradeKeyId;
json.publicKey.publicKeyPem = masqueradeKeyPem;
modifiedText = JSON.stringify(json);
@@ -148,9 +151,42 @@ app.use(async (req, res, next) => {
}
modifiedText = modifiedText.replaceAll(TARGET_REGEXP, TARGET_MASQUERADE);
console.debug({modifiedText});
} else console.debug("binary");
} else console.debug("passthrough");
if (!target_res.ok && !modifiedText && contentType.startsWith("text/")) console.debug("response:", await target_res.text());
res.status(target_res.status);
res.header("Content-Type", target_res.headers.get("content-type"));
res.header("Content-Type", contentType);
if (modifiedText) res.send(modifiedText);
else target_res.body.pipe(res);
});
async function getLocalKeypair(id) {
var keys = await keystore.get(id);
if (keys) return keys;
console.debug("making new masquerade key");
keys = await generateKeyPair('rsa', {
publicKeyEncoding: {type:'pkcs1', format: 'pem'},
privateKeyEncoding: {type:'pkcs1', format: 'pem'},
modulusLength: 2048
});
await keystore.set(id, keys);
return keys;
}
async function getRemotePubkey(id) {
var publicKey = (await keystore.get(id))?.publicKey;
if (publicKey) return publicKey;
console.debug("fetching public key", id);
var res = await fetch(id, {headers: {Accept: "application/activity+json"}});
console.debug(res.status, res.statusText, res.headers.get("content-type"));
if (!res.ok || !res.headers.get("content-type").includes("json")) {
console.debug("could not get key");
return false;
};
var json = await res.json();
console.debug(json);
var publicKey = json?.publicKey?.publicKeyPem;
if (publicKey) keystore.set(id, {publicKey});
return publicKey;
}
+166 -18
View File
@@ -1,16 +1,23 @@
{
"name": "ap-proxy",
"name": "activitypub-proxy",
"version": "0.2.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "activitypub-proxy",
"version": "0.2.3",
"dependencies": {
"@keyv/sqlite": "^3.6.4",
"activitypub-http-signatures": "^2.0.1",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"keyv": "^4.5.2",
"node-fetch": "^2.6.7"
"node-fetch": "^3.3.0"
},
"engines": {
"node": ">=15.0.0"
}
},
"node_modules/@gar/promisify": {
@@ -50,6 +57,25 @@
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/@mapbox/node-pre-gyp/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/@npmcli/fs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz",
@@ -382,6 +408,14 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
"integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==",
"engines": {
"node": ">= 12"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -420,6 +454,14 @@
"node": ">=8"
}
},
"node_modules/dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -536,6 +578,28 @@
"express": "^4.16.2"
}
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
@@ -553,6 +617,17 @@
"node": ">= 0.8"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -1109,23 +1184,39 @@
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
"integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"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==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
"dependencies": {
"whatwg-url": "^5.0.0"
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "4.x || >=6.0.0"
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/node-gyp": {
@@ -1751,6 +1842,14 @@
"node": ">= 0.8"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
"engines": {
"node": ">= 8"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
@@ -1829,6 +1928,16 @@
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.11"
},
"dependencies": {
"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"
}
}
}
},
"@npmcli/fs": {
@@ -2089,6 +2198,11 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"data-uri-to-buffer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
"integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA=="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -2117,6 +2231,11 @@
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
"integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w=="
},
"dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -2218,6 +2337,15 @@
"integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==",
"requires": {}
},
"fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"requires": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
}
},
"finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
@@ -2232,6 +2360,14 @@
"unpipe": "~1.0.0"
}
},
"formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"requires": {
"fetch-blob": "^3.1.2"
}
},
"forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -2656,12 +2792,19 @@
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
"integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="
},
"node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
},
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
"requires": {
"whatwg-url": "^5.0.0"
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
}
},
"node-gyp": {
@@ -3125,6 +3268,11 @@
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
},
"web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
},
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+21 -1
View File
@@ -1,10 +1,30 @@
{
"name": "activitypub-proxy",
"version": "0.2.3",
"keywords": [
"activitypub",
"mastodon",
"misskey",
"pleroma",
"fediblock"
],
"homepage": "https://activitypub-proxy.cf",
"bugs": "https://gitea.moe/lamp/activitypub-proxy/issues",
"repository": {
"type": "git",
"url": "https://gitea.moe/lamp/activitypub-proxy"
},
"dependencies": {
"@keyv/sqlite": "^3.6.4",
"activitypub-http-signatures": "^2.0.1",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"keyv": "^4.5.2",
"node-fetch": "^2.6.7"
"node-fetch": "^3.3.0"
},
"type": "module",
"engines": {
"node": ">=15.0.0"
}
}