RPC to do AP fetches
ci/woodpecker/tag/ociImageTag Pipeline failed
Details
ci/woodpecker/tag/ociImageTag Pipeline failed
Details
This commit is contained in:
parent
eb355cf51d
commit
bc5d85d6d4
|
@ -29,6 +29,10 @@ url: https://example.com/
|
||||||
# The port that your Calckey server should listen on.
|
# The port that your Calckey server should listen on.
|
||||||
port: 3000
|
port: 3000
|
||||||
|
|
||||||
|
# ┌────────────────────────────┐
|
||||||
|
#───┘ Magnetar RPC configuration └──────────────────────────────
|
||||||
|
rpcHost: magnetar:4935
|
||||||
|
|
||||||
# ┌──────────────────────────┐
|
# ┌──────────────────────────┐
|
||||||
#───┘ PostgreSQL configuration └────────────────────────────────
|
#───┘ PostgreSQL configuration └────────────────────────────────
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.astolfo.cool/natty/calckey"
|
"url": "https://git.astolfo.cool/natty/calckey"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.6.3",
|
"packageManager": "pnpm@8.6.3+sha512.d18e277ae8072091046bccbca0931f77dc3080791cd6122ae890bf504125d8af76b37fb33da287dba9fbbb6da6ebb13e314e9fa4a464c7effe3d8599cebe7243",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"rebuild": "pnpm run clean && pnpm node ./scripts/build-greet.js && pnpm -r run build",
|
"rebuild": "pnpm run clean && pnpm node ./scripts/build-greet.js && pnpm -r run build",
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/swcrc",
|
"$schema": "https://swc.rs/schema.json",
|
||||||
"jsc": {
|
"jsc": {
|
||||||
"parser": {
|
"parser": {
|
||||||
"syntax": "typescript",
|
"syntax": "typescript",
|
||||||
"dynamicImport": true,
|
"dynamicImport": true,
|
||||||
"decorators": true
|
"decorators": true
|
||||||
|
|
||||||
},
|
},
|
||||||
"transform": {
|
"transform": {
|
||||||
"legacyDecorator": true,
|
"legacyDecorator": true,
|
||||||
|
@ -21,5 +22,10 @@
|
||||||
},
|
},
|
||||||
"target": "es2022"
|
"target": "es2022"
|
||||||
},
|
},
|
||||||
"minify": false
|
"module": {
|
||||||
|
"type": "es6",
|
||||||
|
"importInterop": "node",
|
||||||
|
"resolveFully": true
|
||||||
|
},
|
||||||
|
"minify": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,16 @@
|
||||||
"@koa/cors": "3.4.3",
|
"@koa/cors": "3.4.3",
|
||||||
"@koa/multer": "3.0.2",
|
"@koa/multer": "3.0.2",
|
||||||
"@koa/router": "9.0.1",
|
"@koa/router": "9.0.1",
|
||||||
|
"@msgpack/msgpack": "3.0.0-beta2",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@redocly/openapi-core": "1.0.0-beta.120",
|
"@redocly/openapi-core": "1.0.0-beta.120",
|
||||||
"@sinonjs/fake-timers": "9.1.2",
|
"@sinonjs/fake-timers": "9.1.2",
|
||||||
"@syuilo/aiscript": "0.11.1",
|
"@syuilo/aiscript": "0.11.1",
|
||||||
"adm-zip": "^0.5.10",
|
"adm-zip": "^0.5.16",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"archiver": "5.3.1",
|
"archiver": "5.3.1",
|
||||||
"argon2": "^0.30.3",
|
"argon2": "^0.30.3",
|
||||||
"async-mutex": "^0.4.0",
|
"async-mutex": "^0.4.1",
|
||||||
"autobind-decorator": "2.4.0",
|
"autobind-decorator": "2.4.0",
|
||||||
"autolinker": "4.0.0",
|
"autolinker": "4.0.0",
|
||||||
"autwh": "0.1.0",
|
"autwh": "0.1.0",
|
||||||
|
@ -40,7 +41,7 @@
|
||||||
"cbor": "8.1.0",
|
"cbor": "8.1.0",
|
||||||
"chalk": "5.2.0",
|
"chalk": "5.2.0",
|
||||||
"chalk-template": "0.4.0",
|
"chalk-template": "0.4.0",
|
||||||
"chokidar": "3.5.3",
|
"chokidar": "^3.6.0",
|
||||||
"cli-highlight": "2.1.11",
|
"cli-highlight": "2.1.11",
|
||||||
"color-convert": "2.0.1",
|
"color-convert": "2.0.1",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
|
@ -79,7 +80,7 @@
|
||||||
"nodemailer": "6.9.3",
|
"nodemailer": "6.9.3",
|
||||||
"oauth": "^0.10.0",
|
"oauth": "^0.10.0",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"otpauth": "^9.1.2",
|
"otpauth": "^9.3.2",
|
||||||
"parse5": "7.1.2",
|
"parse5": "7.1.2",
|
||||||
"pg": "8.11.0",
|
"pg": "8.11.0",
|
||||||
"private-ip": "2.3.4",
|
"private-ip": "2.3.4",
|
||||||
|
@ -100,6 +101,7 @@
|
||||||
"sanitize-html": "2.10.0",
|
"sanitize-html": "2.10.0",
|
||||||
"semver": "7.5.1",
|
"semver": "7.5.1",
|
||||||
"sharp": "0.32.6",
|
"sharp": "0.32.6",
|
||||||
|
"smart-buffer": "^4.2.0",
|
||||||
"sonic-channel": "^1.3.1",
|
"sonic-channel": "^1.3.1",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"syslog-pro": "1.0.0",
|
"syslog-pro": "1.0.0",
|
||||||
|
@ -116,9 +118,9 @@
|
||||||
"xev": "3.0.2"
|
"xev": "3.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@swc/cli": "^0.1.62",
|
"@swc/cli": "^0.1.65",
|
||||||
"@swc/core": "^1.3.62",
|
"@swc/core": "^1.7.23",
|
||||||
"@types/adm-zip": "^0.5.0",
|
"@types/adm-zip": "^0.5.5",
|
||||||
"@types/bcryptjs": "2.4.2",
|
"@types/bcryptjs": "2.4.2",
|
||||||
"@types/bull": "3.15.9",
|
"@types/bull": "3.15.9",
|
||||||
"@types/cbor": "6.0.0",
|
"@types/cbor": "6.0.0",
|
||||||
|
@ -143,7 +145,7 @@
|
||||||
"@types/node-fetch": "3.0.3",
|
"@types/node-fetch": "3.0.3",
|
||||||
"@types/nodemailer": "6.4.8",
|
"@types/nodemailer": "6.4.8",
|
||||||
"@types/oauth": "0.9.1",
|
"@types/oauth": "0.9.1",
|
||||||
"@types/probe-image-size": "^7.2.0",
|
"@types/probe-image-size": "^7.2.5",
|
||||||
"@types/punycode": "2.1.0",
|
"@types/punycode": "2.1.0",
|
||||||
"@types/qrcode": "1.5.0",
|
"@types/qrcode": "1.5.0",
|
||||||
"@types/qs": "6.9.7",
|
"@types/qs": "6.9.7",
|
||||||
|
@ -163,17 +165,17 @@
|
||||||
"@types/ws": "8.5.4",
|
"@types/ws": "8.5.4",
|
||||||
"autobind-decorator": "2.4.0",
|
"autobind-decorator": "2.4.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "^8.42.0",
|
"eslint": "^8.57.0",
|
||||||
"execa": "6.1.0",
|
"execa": "6.1.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"json5-loader": "4.0.1",
|
"json5-loader": "4.0.1",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"swc-loader": "^0.2.3",
|
"swc-loader": "^0.2.6",
|
||||||
"ts-loader": "9.4.3",
|
"ts-loader": "9.4.3",
|
||||||
"ts-node": "10.9.1",
|
"ts-node": "10.9.1",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "5.1.3",
|
"typescript": "5.1.3",
|
||||||
"webpack": "^5.85.1",
|
"webpack": "^5.94.0",
|
||||||
"ws": "8.13.0"
|
"ws": "8.13.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ export type Source = {
|
||||||
url: string;
|
url: string;
|
||||||
port: number;
|
port: number;
|
||||||
disableHsts?: boolean;
|
disableHsts?: boolean;
|
||||||
|
rpcHost: string;
|
||||||
db: {
|
db: {
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
import {Socket} from "node:net"
|
||||||
|
import {SmartBuffer} from "smart-buffer";
|
||||||
|
|
||||||
|
import {decode, encode} from "@msgpack/msgpack";
|
||||||
|
import Logger from "@/services/logger.js";
|
||||||
|
import config from "@/config/index.js";
|
||||||
|
import type {IObject} from "@/remote/activitypub/type";
|
||||||
|
|
||||||
|
let client: Socket | null = null;
|
||||||
|
let exponentialBackoff = 0;
|
||||||
|
let serial: bigint = BigInt(0);
|
||||||
|
|
||||||
|
const logger = new Logger("RpcLog");
|
||||||
|
|
||||||
|
function getRpcClient(): Socket {
|
||||||
|
if (client) {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [host, portStr] = config.rpcHost.trim().split(/:(?=[0-9]+$)/, 2);
|
||||||
|
const port = parseInt(portStr);
|
||||||
|
client = new Socket();
|
||||||
|
const reconnectWithBackoff = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
client!.connect(port, host);
|
||||||
|
}, 1000 * (1 + Math.pow(1.5, exponentialBackoff)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const buf = new SmartBuffer({
|
||||||
|
encoding: "binary"
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("connect", () => {
|
||||||
|
exponentialBackoff = 0;
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on("error", e => {
|
||||||
|
logger.warn(`RPC connection error: ${e}`);
|
||||||
|
client!.removeAllListeners("data");
|
||||||
|
buf.clear();
|
||||||
|
exponentialBackoff = Math.min(12, exponentialBackoff + 1);
|
||||||
|
reconnectWithBackoff();
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on("close", () => {
|
||||||
|
client!.removeAllListeners("data");
|
||||||
|
buf.clear();
|
||||||
|
exponentialBackoff = Math.min(12, exponentialBackoff + 1);
|
||||||
|
reconnectWithBackoff();
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("data", (recv) => {
|
||||||
|
buf.writeBuffer(recv);
|
||||||
|
|
||||||
|
if (buf.length < 1 + 4 + 8) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = buf.readUInt8();
|
||||||
|
if (header != 77) {
|
||||||
|
logger.error(`Invalid header: ${header}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const serial = buf.readBigUInt64BE();
|
||||||
|
|
||||||
|
const dataLen = buf.readUInt32BE();
|
||||||
|
if (buf.remaining() < dataLen) {
|
||||||
|
buf.readOffset = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = buf.readBuffer(dataLen);
|
||||||
|
const dataDecoded: any = decode(data);
|
||||||
|
|
||||||
|
// Move the rest of the data to the beginning of the buffer
|
||||||
|
const rest = buf.readBuffer();
|
||||||
|
buf.clear();
|
||||||
|
buf.writeBuffer(rest);
|
||||||
|
client!.emit(`mag-data:${serial}`, dataDecoded);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.connect(port, host);
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rpcCall<D, T>(method: string, data: D): Promise<T> {
|
||||||
|
const header = new Uint8Array([77]);
|
||||||
|
const textEncoder = new TextEncoder();
|
||||||
|
const methodBuf = textEncoder.encode(method);
|
||||||
|
const dataBuf = encode(data);
|
||||||
|
|
||||||
|
const serialLength = 8;
|
||||||
|
const sizeLength = 4;
|
||||||
|
|
||||||
|
const packetBuf = new Uint8Array(header.length + serialLength + sizeLength + methodBuf.length + sizeLength + dataBuf.length);
|
||||||
|
packetBuf.set(header, 0);
|
||||||
|
packetBuf.set(methodBuf, header.length + serialLength + sizeLength)
|
||||||
|
packetBuf.set(dataBuf, header.length + serialLength + sizeLength + methodBuf.length + sizeLength);
|
||||||
|
|
||||||
|
const packetDataView = new DataView(packetBuf.buffer);
|
||||||
|
packetDataView.setBigUint64(header.length, serial);
|
||||||
|
packetDataView.setUint32(header.length + serialLength, methodBuf.length);
|
||||||
|
packetDataView.setUint32(header.length + serialLength + sizeLength + methodBuf.length, dataBuf.length);
|
||||||
|
|
||||||
|
const client = getRpcClient();
|
||||||
|
client.write(packetBuf);
|
||||||
|
|
||||||
|
const fut = new Promise((resolve, reject) => {
|
||||||
|
client.once(`mag-data:${serial}`, resolve);
|
||||||
|
client.once("close", reject);
|
||||||
|
client.once("error", reject);
|
||||||
|
});
|
||||||
|
|
||||||
|
serial++;
|
||||||
|
|
||||||
|
const result = await fut as { success: false, data: any } | { success: true, data: T };
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
throw result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function magApGet(userId: string, url: string): Promise<IObject> {
|
||||||
|
logger.info(`AP GET to: ${url}`);
|
||||||
|
return await rpcCall("/ap/get", {
|
||||||
|
user_id: userId,
|
||||||
|
url
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function magApPost(userId: string, url: string, body: string): Promise<IObject> {
|
||||||
|
logger.info(`AP POST to: ${url}`);
|
||||||
|
return await rpcCall("/ap/post", {
|
||||||
|
user_id: userId,
|
||||||
|
url,
|
||||||
|
body
|
||||||
|
});
|
||||||
|
}
|
|
@ -18,25 +18,3 @@ export async function genRsaKeyPair(modulusLength = 2048) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function genEcKeyPair(
|
|
||||||
namedCurve:
|
|
||||||
| "prime256v1"
|
|
||||||
| "secp384r1"
|
|
||||||
| "secp521r1"
|
|
||||||
| "curve25519" = "prime256v1",
|
|
||||||
) {
|
|
||||||
return await generateKeyPair("ec", {
|
|
||||||
namedCurve,
|
|
||||||
publicKeyEncoding: {
|
|
||||||
type: "spki",
|
|
||||||
format: "pem",
|
|
||||||
},
|
|
||||||
privateKeyEncoding: {
|
|
||||||
type: "pkcs8",
|
|
||||||
format: "pem",
|
|
||||||
cipher: undefined,
|
|
||||||
passphrase: undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
const map: Record<string, string> = {
|
|
||||||
"&": "&",
|
|
||||||
"<": "<",
|
|
||||||
">": ">",
|
|
||||||
'"': """,
|
|
||||||
"'": "'",
|
|
||||||
};
|
|
||||||
|
|
||||||
const beginingOfCDATA = "<![CDATA[";
|
|
||||||
const endOfCDATA = "]]>";
|
|
||||||
|
|
||||||
export function escapeValue(x: string): string {
|
|
||||||
let insideOfCDATA = false;
|
|
||||||
let builder = "";
|
|
||||||
for (let i = 0; i < x.length; ) {
|
|
||||||
if (insideOfCDATA) {
|
|
||||||
if (x.slice(i, i + beginingOfCDATA.length) === beginingOfCDATA) {
|
|
||||||
insideOfCDATA = true;
|
|
||||||
i += beginingOfCDATA.length;
|
|
||||||
} else {
|
|
||||||
builder += x[i++];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (x.slice(i, i + endOfCDATA.length) === endOfCDATA) {
|
|
||||||
insideOfCDATA = false;
|
|
||||||
i += endOfCDATA.length;
|
|
||||||
} else {
|
|
||||||
const b = x[i++];
|
|
||||||
builder += map[b] || b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function escapeAttribute(x: string): string {
|
|
||||||
return Object.entries(map).reduce((a, [k, v]) => a.replace(k, v), x);
|
|
||||||
}
|
|
|
@ -1,152 +0,0 @@
|
||||||
import * as crypto from "node:crypto";
|
|
||||||
import { URL } from "node:url";
|
|
||||||
|
|
||||||
type Request = {
|
|
||||||
url: string;
|
|
||||||
method: string;
|
|
||||||
headers: Record<string, string>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type PrivateKey = {
|
|
||||||
privateKeyPem: string;
|
|
||||||
keyId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function createSignedPost(args: {
|
|
||||||
key: PrivateKey;
|
|
||||||
url: string;
|
|
||||||
body: string;
|
|
||||||
additionalHeaders: Record<string, string>;
|
|
||||||
}) {
|
|
||||||
const u = new URL(args.url);
|
|
||||||
const digestHeader = `SHA-256=${crypto
|
|
||||||
.createHash("sha256")
|
|
||||||
.update(args.body)
|
|
||||||
.digest("base64")}`;
|
|
||||||
|
|
||||||
const request: Request = {
|
|
||||||
url: u.href,
|
|
||||||
method: "POST",
|
|
||||||
headers: objectAssignWithLcKey(
|
|
||||||
{
|
|
||||||
Date: new Date().toUTCString(),
|
|
||||||
Host: u.hostname,
|
|
||||||
"Content-Type": "application/activity+json",
|
|
||||||
Digest: digestHeader,
|
|
||||||
},
|
|
||||||
args.additionalHeaders,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = signToRequest(request, args.key, [
|
|
||||||
"(request-target)",
|
|
||||||
"date",
|
|
||||||
"host",
|
|
||||||
"digest",
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
request,
|
|
||||||
signingString: result.signingString,
|
|
||||||
signature: result.signature,
|
|
||||||
signatureHeader: result.signatureHeader,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createSignedGet(args: {
|
|
||||||
key: PrivateKey;
|
|
||||||
url: string;
|
|
||||||
additionalHeaders: Record<string, string>;
|
|
||||||
}) {
|
|
||||||
const u = new URL(args.url);
|
|
||||||
|
|
||||||
const request: Request = {
|
|
||||||
url: u.href,
|
|
||||||
method: "GET",
|
|
||||||
headers: objectAssignWithLcKey(
|
|
||||||
{
|
|
||||||
Accept: "application/activity+json, application/ld+json",
|
|
||||||
Date: new Date().toUTCString(),
|
|
||||||
Host: new URL(args.url).hostname,
|
|
||||||
},
|
|
||||||
args.additionalHeaders,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = signToRequest(request, args.key, [
|
|
||||||
"(request-target)",
|
|
||||||
"date",
|
|
||||||
"host",
|
|
||||||
"accept",
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
request,
|
|
||||||
signingString: result.signingString,
|
|
||||||
signature: result.signature,
|
|
||||||
signatureHeader: result.signatureHeader,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function signToRequest(
|
|
||||||
request: Request,
|
|
||||||
key: PrivateKey,
|
|
||||||
includeHeaders: string[],
|
|
||||||
) {
|
|
||||||
const signingString = genSigningString(request, includeHeaders);
|
|
||||||
const signature = crypto
|
|
||||||
.sign("sha256", Buffer.from(signingString), key.privateKeyPem)
|
|
||||||
.toString("base64");
|
|
||||||
const signatureHeader = `keyId="${
|
|
||||||
key.keyId
|
|
||||||
}",algorithm="rsa-sha256",headers="${includeHeaders.join(
|
|
||||||
" ",
|
|
||||||
)}",signature="${signature}"`;
|
|
||||||
|
|
||||||
request.headers = objectAssignWithLcKey(request.headers, {
|
|
||||||
Signature: signatureHeader,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
request,
|
|
||||||
signingString,
|
|
||||||
signature,
|
|
||||||
signatureHeader,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function genSigningString(request: Request, includeHeaders: string[]) {
|
|
||||||
request.headers = lcObjectKey(request.headers);
|
|
||||||
|
|
||||||
const results: string[] = [];
|
|
||||||
|
|
||||||
for (const key of includeHeaders.map((x) => x.toLowerCase())) {
|
|
||||||
if (key === "(request-target)") {
|
|
||||||
results.push(
|
|
||||||
`(request-target): ${request.method.toLowerCase()} ${
|
|
||||||
new URL(request.url).pathname
|
|
||||||
}`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
results.push(`${key}: ${request.headers[key]}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
function lcObjectKey(src: Record<string, string>) {
|
|
||||||
const dst: Record<string, string> = {};
|
|
||||||
for (const key of Object.keys(src).filter(
|
|
||||||
(x) => x !== "__proto__" && typeof src[x] === "string",
|
|
||||||
))
|
|
||||||
dst[key.toLowerCase()] = src[key];
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
function objectAssignWithLcKey(
|
|
||||||
a: Record<string, string>,
|
|
||||||
b: Record<string, string>,
|
|
||||||
) {
|
|
||||||
return Object.assign(lcObjectKey(a), lcObjectKey(b));
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
export type IIcon = {
|
|
||||||
type: string;
|
|
||||||
mediaType?: string;
|
|
||||||
url?: string;
|
|
||||||
};
|
|
|
@ -1,32 +1,10 @@
|
||||||
import config from "@/config/index.js";
|
import type {User} from "@/models/entities/user.js";
|
||||||
import { getUserKeypair } from "@/misc/keypair-store.js";
|
import {magApGet, magApPost} from "@/mag/rpc-client.js";
|
||||||
import type { User } from "@/models/entities/user.js";
|
|
||||||
import { getResponse } from "../../misc/fetch.js";
|
|
||||||
import { createSignedPost, createSignedGet } from "./ap-request.js";
|
|
||||||
|
|
||||||
export default async (user: { id: User["id"] }, url: string, object: any) => {
|
export default async (user: { id: User["id"] }, url: string, object: any) => {
|
||||||
const body = JSON.stringify(object);
|
const body = JSON.stringify(object);
|
||||||
|
|
||||||
const keypair = await getUserKeypair(user.id);
|
return await magApPost(user.id, url, body);
|
||||||
|
|
||||||
const req = createSignedPost({
|
|
||||||
key: {
|
|
||||||
privateKeyPem: keypair.privateKey,
|
|
||||||
keyId: `${config.url}/users/${user.id}#main-key`,
|
|
||||||
},
|
|
||||||
url,
|
|
||||||
body,
|
|
||||||
additionalHeaders: {
|
|
||||||
"User-Agent": config.userAgent,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await getResponse({
|
|
||||||
url,
|
|
||||||
method: req.request.method,
|
|
||||||
headers: req.request.headers,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,24 +13,5 @@ export default async (user: { id: User["id"] }, url: string, object: any) => {
|
||||||
* @param url URL to fetch
|
* @param url URL to fetch
|
||||||
*/
|
*/
|
||||||
export async function signedGet(url: string, user: { id: User["id"] }) {
|
export async function signedGet(url: string, user: { id: User["id"] }) {
|
||||||
const keypair = await getUserKeypair(user.id);
|
return await magApGet(user.id, url);
|
||||||
|
|
||||||
const req = createSignedGet({
|
|
||||||
key: {
|
|
||||||
privateKeyPem: keypair.privateKey,
|
|
||||||
keyId: `${config.url}/users/${user.id}#main-key`,
|
|
||||||
},
|
|
||||||
url,
|
|
||||||
additionalHeaders: {
|
|
||||||
"User-Agent": config.userAgent,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const res = await getResponse({
|
|
||||||
url,
|
|
||||||
method: req.request.method,
|
|
||||||
headers: req.request.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
return await res.json();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,31 @@
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import { getJson } from "@/misc/fetch.js";
|
import type {ILocalUser} from "@/models/entities/user.js";
|
||||||
import type { ILocalUser } from "@/models/entities/user.js";
|
import {getInstanceActor} from "@/services/instance-actor.js";
|
||||||
import { getInstanceActor } from "@/services/instance-actor.js";
|
import {fetchMeta} from "@/misc/fetch-meta.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import {extractDbHost, isSelfHost} from "@/misc/convert-host.js";
|
||||||
import { extractDbHost, isSelfHost } from "@/misc/convert-host.js";
|
import {signedGet} from "./request.js";
|
||||||
import { signedGet } from "./request.js";
|
import type {ICollection, IObject, IOrderedCollection} from "./type.js";
|
||||||
import type { IObject, ICollection, IOrderedCollection } from "./type.js";
|
import {getApId, isCollectionOrOrderedCollection} from "./type.js";
|
||||||
import { isCollectionOrOrderedCollection, getApId } from "./type.js";
|
import {NoteReactions, Notes, Polls, Users,} from "@/models/index.js";
|
||||||
import {
|
import {parseUri} from "./db-resolver.js";
|
||||||
FollowRequests,
|
|
||||||
Notes,
|
|
||||||
NoteReactions,
|
|
||||||
Polls,
|
|
||||||
Users,
|
|
||||||
} from "@/models/index.js";
|
|
||||||
import { parseUri } from "./db-resolver.js";
|
|
||||||
import renderNote from "@/remote/activitypub/renderer/note.js";
|
import renderNote from "@/remote/activitypub/renderer/note.js";
|
||||||
import { renderLike } from "@/remote/activitypub/renderer/like.js";
|
import {renderLike} from "@/remote/activitypub/renderer/like.js";
|
||||||
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
|
import {renderPerson} from "@/remote/activitypub/renderer/person.js";
|
||||||
import renderQuestion from "@/remote/activitypub/renderer/question.js";
|
import renderQuestion from "@/remote/activitypub/renderer/question.js";
|
||||||
import renderCreate from "@/remote/activitypub/renderer/create.js";
|
import renderCreate from "@/remote/activitypub/renderer/create.js";
|
||||||
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
|
import {renderActivity} from "@/remote/activitypub/renderer/index.js";
|
||||||
import renderFollow from "@/remote/activitypub/renderer/follow.js";
|
import renderFollow from "@/remote/activitypub/renderer/follow.js";
|
||||||
import { shouldBlockInstance } from "@/misc/should-block-instance.js";
|
import {shouldBlockInstance} from "@/misc/should-block-instance.js";
|
||||||
|
|
||||||
export default class Resolver {
|
export default class Resolver {
|
||||||
private history: Set<string>;
|
private history: Set<string>;
|
||||||
private user?: ILocalUser;
|
private user?: ILocalUser;
|
||||||
private recursionLimit?: number;
|
private recursionLimit?: number;
|
||||||
|
|
||||||
constructor(recursionLimit = 100) {
|
constructor(recursionLimit = 100, user?: ILocalUser) {
|
||||||
this.history = new Set();
|
this.history = new Set();
|
||||||
this.recursionLimit = recursionLimit;
|
this.recursionLimit = recursionLimit;
|
||||||
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getHistory(): string[] {
|
public getHistory(): string[] {
|
||||||
|
@ -102,11 +96,7 @@ export default class Resolver {
|
||||||
this.user = await getInstanceActor();
|
this.user = await getInstanceActor();
|
||||||
}
|
}
|
||||||
|
|
||||||
const object = (
|
const object = await signedGet(value, this.user);
|
||||||
this.user
|
|
||||||
? await signedGet(value, this.user)
|
|
||||||
: await getJson(value, "application/activity+json, application/ld+json")
|
|
||||||
) as IObject;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
object == null ||
|
object == null ||
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import define from "../../define.js";
|
import define from "../../define.js";
|
||||||
import Resolver from "@/remote/activitypub/resolver.js";
|
import Resolver from "@/remote/activitypub/resolver.js";
|
||||||
import { HOUR } from "@/const.js";
|
import {HOUR} from "@/const.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["federation"],
|
tags: ["federation"],
|
||||||
|
@ -29,8 +29,7 @@ export const paramDef = {
|
||||||
required: ["uri"],
|
required: ["uri"],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export default define(meta, paramDef, async (ps) => {
|
export default define(meta, paramDef, async (ps, me) => {
|
||||||
const resolver = new Resolver();
|
const resolver = new Resolver(undefined, me);
|
||||||
const object = await resolver.resolve(ps.uri);
|
return await resolver.resolve(ps.uri);
|
||||||
return object;
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -126,7 +126,7 @@ async function fetchAny(
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetching Object once from remote
|
// fetching Object once from remote
|
||||||
const resolver = new Resolver();
|
const resolver = new Resolver(undefined, me ?? undefined);
|
||||||
const object = await resolver.resolve(uri);
|
const object = await resolver.resolve(uri);
|
||||||
|
|
||||||
// /@user If a URI other than the id is specified,
|
// /@user If a URI other than the id is specified,
|
||||||
|
|
|
@ -1,38 +1,13 @@
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
|
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import {escapeAttribute, escapeValue} from "@/prelude/xml.js";
|
|
||||||
|
|
||||||
// Init router
|
// Init router
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
const XRD = (
|
|
||||||
...x: {
|
|
||||||
element: string;
|
|
||||||
value?: string;
|
|
||||||
attributes?: Record<string, string>;
|
|
||||||
}[]
|
|
||||||
) =>
|
|
||||||
`<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">${x
|
|
||||||
.map(
|
|
||||||
({ element, value, attributes }) =>
|
|
||||||
`<${Object.entries(
|
|
||||||
(typeof attributes === "object" && attributes) || {}
|
|
||||||
).reduce(
|
|
||||||
(a, [k, v]) => `${a} ${k}="${escapeAttribute(v)}"`,
|
|
||||||
element
|
|
||||||
)}${
|
|
||||||
typeof value === "string"
|
|
||||||
? `>${escapeValue(value)}</${element}`
|
|
||||||
: "/"
|
|
||||||
}>`
|
|
||||||
)
|
|
||||||
.reduce((a, c) => a + c, "")}</XRD>`;
|
|
||||||
|
|
||||||
const allPath = "/.well-known/(.*)";
|
const allPath = "/.well-known/(.*)";
|
||||||
const webFingerPath = "/.well-known/webfinger";
|
const webFingerPath = "/.well-known/webfinger";
|
||||||
const jrd = "application/jrd+json";
|
const jrd = "application/jrd+json";
|
||||||
const xrd = "application/xrd+xml";
|
|
||||||
|
|
||||||
router.use(allPath, async (ctx, next) => {
|
router.use(allPath, async (ctx, next) => {
|
||||||
ctx.set({
|
ctx.set({
|
||||||
|
@ -48,18 +23,6 @@ router.options(allPath, async (ctx) => {
|
||||||
ctx.status = 204;
|
ctx.status = 204;
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/.well-known/host-meta", async (ctx) => {
|
|
||||||
ctx.set("Content-Type", xrd);
|
|
||||||
ctx.body = XRD({
|
|
||||||
element: "Link",
|
|
||||||
attributes: {
|
|
||||||
rel: "lrdd",
|
|
||||||
type: xrd,
|
|
||||||
template: `${config.url}${webFingerPath}?resource={uri}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/.well-known/host-meta.json", async (ctx) => {
|
router.get("/.well-known/host-meta.json", async (ctx) => {
|
||||||
ctx.set("Content-Type", jrd);
|
ctx.set("Content-Type", jrd);
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
|
|
10820
pnpm-lock.yaml
10820
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue