mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-25 11:43:07 +01:00
switch to own activitypub types library
This commit is contained in:
parent
b64163f769
commit
0bc905a222
1092139
assets/schemas.json
1092139
assets/schemas.json
File diff suppressed because it is too large
Load Diff
159
package-lock.json
generated
159
package-lock.json
generated
@ -13,7 +13,7 @@
|
||||
"@aws-sdk/client-s3": "^3.385.0",
|
||||
"@sentry/integrations": "^7.66.0",
|
||||
"@sentry/node": "^7.66.0",
|
||||
"activitypub-core-types": "^0.3.2",
|
||||
"activitypub-types": "github:spacebarchat/activitypub-types#dist",
|
||||
"ajv": "8.6.2",
|
||||
"ajv-formats": "2.1.1",
|
||||
"amqplib": "^0.10.3",
|
||||
@ -43,6 +43,7 @@
|
||||
"node-fetch": "^2.6.12",
|
||||
"node-os-utils": "^1.3.7",
|
||||
"nodemailer": "^6.9.4",
|
||||
"pg": "^8.11.3",
|
||||
"picocolors": "^1.0.0",
|
||||
"probe-image-size": "^7.2.3",
|
||||
"proxy-agent": "^6.3.0",
|
||||
@ -2504,13 +2505,10 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/activitypub-core-types": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/activitypub-core-types/-/activitypub-core-types-0.3.2.tgz",
|
||||
"integrity": "sha512-hAWCkRIzLJ3eVEjnibPYNHQaM+vD0SCK29gqMEt5pEb/2pbEetkv+4MpVMi0CKtr/GWRCSjIw1C6YAph7yT0pA==",
|
||||
"dependencies": {
|
||||
"formidable": "^2.1.1"
|
||||
}
|
||||
"node_modules/activitypub-types": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "git+ssh://git@github.com/spacebarchat/activitypub-types.git#67d3162d8a982f1d64c18412835b54169682b523",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/addressparser": {
|
||||
"version": "1.0.1",
|
||||
@ -2742,7 +2740,8 @@
|
||||
"node_modules/asap": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
|
||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/asn1js": {
|
||||
"version": "3.0.5",
|
||||
@ -2980,6 +2979,14 @@
|
||||
"resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz",
|
||||
"integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg=="
|
||||
},
|
||||
"node_modules/buffer-writer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
|
||||
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
@ -3605,6 +3612,7 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
|
||||
"integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"asap": "^2.0.0",
|
||||
"wrappy": "1"
|
||||
@ -4464,6 +4472,7 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz",
|
||||
"integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"dezalgo": "^1.0.4",
|
||||
"hexoid": "^1.0.0",
|
||||
@ -4742,6 +4751,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
|
||||
"integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@ -6280,6 +6290,11 @@
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/packet-reader": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
|
||||
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@ -6375,6 +6390,89 @@
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
|
||||
"integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
|
||||
"dependencies": {
|
||||
"buffer-writer": "2.0.0",
|
||||
"packet-reader": "1.0.0",
|
||||
"pg-connection-string": "^2.6.2",
|
||||
"pg-pool": "^3.6.1",
|
||||
"pg-protocol": "^1.6.0",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
|
||||
"integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
|
||||
"integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
|
||||
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
@ -6407,6 +6505,41 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@ -7109,6 +7242,14 @@
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sqlite3": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.6.tgz",
|
||||
|
@ -69,7 +69,7 @@
|
||||
"@aws-sdk/client-s3": "^3.385.0",
|
||||
"@sentry/integrations": "^7.66.0",
|
||||
"@sentry/node": "^7.66.0",
|
||||
"activitypub-core-types": "^0.3.2",
|
||||
"activitypub-types": "github:spacebarchat/activitypub-types#dist",
|
||||
"ajv": "8.6.2",
|
||||
"ajv-formats": "2.1.1",
|
||||
"amqplib": "^0.10.3",
|
||||
@ -99,6 +99,7 @@
|
||||
"node-fetch": "^2.6.12",
|
||||
"node-os-utils": "^1.3.7",
|
||||
"nodemailer": "^6.9.4",
|
||||
"pg": "^8.11.3",
|
||||
"picocolors": "^1.0.0",
|
||||
"probe-image-size": "^7.2.3",
|
||||
"proxy-agent": "^6.3.0",
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { AP } from "activitypub-core-types";
|
||||
import { APOrderedCollection, AnyAPObject } from "activitypub-types";
|
||||
import { ACTIVITYSTREAMS_CONTEXT } from "./utils";
|
||||
|
||||
export const makeOrderedCollection = async <T extends AP.CoreObject>(opts: {
|
||||
export const makeOrderedCollection = async <T extends AnyAPObject>(opts: {
|
||||
page: boolean;
|
||||
min_id?: string;
|
||||
max_id?: string;
|
||||
id: URL;
|
||||
id: string;
|
||||
getTotalElements: () => Promise<number>;
|
||||
getElements: (before?: string, after?: string) => Promise<T[]>;
|
||||
}): Promise<AP.OrderedCollection> => {
|
||||
}): Promise<APOrderedCollection> => {
|
||||
const { page, min_id, max_id, id, getTotalElements, getElements } = opts;
|
||||
|
||||
if (!page)
|
||||
@ -28,7 +28,7 @@ export const makeOrderedCollection = async <T extends AP.CoreObject>(opts: {
|
||||
|
||||
return {
|
||||
"@context": ACTIVITYSTREAMS_CONTEXT,
|
||||
id: new URL(`${id}?page=true`),
|
||||
id: `${id}?page=true`,
|
||||
type: "OrderedCollection",
|
||||
first: new URL(`${id}?page=true`),
|
||||
last: new URL(`${id}?page=true&min_id=0`),
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Responsible for dispatching activitypub events to external instances
|
||||
*/
|
||||
|
||||
import { AP } from "activitypub-core-types";
|
||||
import { APActivity } from "activitypub-types";
|
||||
import { federationQueue } from "./queue";
|
||||
|
||||
export * from "./OrderedCollection";
|
||||
@ -11,7 +11,7 @@ export * from "./transforms";
|
||||
export * from "./utils";
|
||||
|
||||
export class Federation {
|
||||
static async distribute(activity: AP.Activity) {
|
||||
static async distribute(activity: APActivity) {
|
||||
await federationQueue.distribute(activity);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { Config, FederationKey } from "@spacebar/util";
|
||||
import { AP } from "activitypub-core-types";
|
||||
import fetch from "node-fetch";
|
||||
import { APError, signActivity, splitQualifiedMention } from "./utils";
|
||||
import { APActivity } from "activitypub-types";
|
||||
|
||||
//
|
||||
type Instance = string;
|
||||
|
||||
class FederationQueue {
|
||||
// TODO: queue messages and send them to shared inbox
|
||||
private queue: Map<Instance, Array<AP.Activity>> = new Map();
|
||||
private queue: Map<Instance, Array<APActivity>> = new Map();
|
||||
|
||||
public async distribute(activity: AP.Activity) {
|
||||
public async distribute(activity: APActivity) {
|
||||
let { to, actor } = activity;
|
||||
|
||||
if (!to)
|
||||
|
@ -10,20 +10,19 @@ import {
|
||||
User,
|
||||
UserSettings,
|
||||
} from "@spacebar/util";
|
||||
import { AP } from "activitypub-core-types";
|
||||
import TurndownService from "turndown";
|
||||
import { In } from "typeorm";
|
||||
import {
|
||||
ACTIVITYSTREAMS_CONTEXT,
|
||||
APError,
|
||||
APObjectIsPerson,
|
||||
APObjectIsSpacebarActor,
|
||||
resolveAPObject,
|
||||
} from "./utils";
|
||||
import { APAnnounce, APGroup, APNote, APPerson } from "activitypub-types";
|
||||
|
||||
export const transformMessageToAnnounceNoce = async (
|
||||
message: Message,
|
||||
): Promise<AP.Announce> => {
|
||||
): Promise<APAnnounce> => {
|
||||
const { host } = Config.get().federation;
|
||||
|
||||
const channel = await Channel.findOneOrFail({
|
||||
@ -34,10 +33,9 @@ export const transformMessageToAnnounceNoce = async (
|
||||
});
|
||||
|
||||
let to = [
|
||||
new URL(
|
||||
`https://${host}/federation/channels/${message.channel_id}/followers`,
|
||||
),
|
||||
`https://${host}/federation/channels/${message.channel_id}/followers`,
|
||||
];
|
||||
|
||||
if (channel.isDm()) {
|
||||
const otherUsers = channel.recipients?.filter(
|
||||
(x) => x.user_id != message.author_id,
|
||||
@ -48,27 +46,25 @@ export const transformMessageToAnnounceNoce = async (
|
||||
});
|
||||
|
||||
to = remoteUsersKeys.map((x) =>
|
||||
x.inbox ? new URL(x.inbox!) : new URL(`${x.federatedId}/inbox`),
|
||||
x.inbox ? x.inbox! : `${x.federatedId}/inbox`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
"@context": ACTIVITYSTREAMS_CONTEXT,
|
||||
type: "Announce",
|
||||
id: new URL(
|
||||
`https://${host}/federation/channels/${message.channel_id}/messages/${message.id}`,
|
||||
),
|
||||
id: `https://${host}/federation/channels/${message.channel_id}/messages/${message.id}`,
|
||||
// this is wrong for remote users
|
||||
actor: new URL(`https://${host}/federation/users/${message.author_id}`),
|
||||
actor: `https://${host}/federation/users/${message.author_id}`,
|
||||
published: message.timestamp,
|
||||
to,
|
||||
object: await transformMessageToNote(message),
|
||||
};
|
||||
} as APAnnounce;
|
||||
};
|
||||
|
||||
export const transformMessageToNote = async (
|
||||
message: Message,
|
||||
): Promise<AP.Note> => {
|
||||
): Promise<APNote> => {
|
||||
const { host } = Config.get().federation;
|
||||
|
||||
const referencedMessage = message.message_reference
|
||||
@ -78,23 +74,18 @@ export const transformMessageToNote = async (
|
||||
: null;
|
||||
|
||||
return {
|
||||
id: new URL(`https://${host}/federation/messages/${message.id}`),
|
||||
id: `https://${host}/federation/messages/${message.id}`,
|
||||
type: "Note",
|
||||
content: message.content, // TODO: convert markdown to html
|
||||
inReplyTo: referencedMessage
|
||||
? await transformMessageToNote(referencedMessage)
|
||||
: undefined,
|
||||
published: message.timestamp,
|
||||
attributedTo: new URL(
|
||||
`https://${host}/federation/users/${message.author_id}`,
|
||||
),
|
||||
to: [
|
||||
new URL(
|
||||
`https://${host}/federation/channels/${message.channel_id}`,
|
||||
),
|
||||
],
|
||||
attributedTo: `https://${host}/federation/users/${message.author_id}`,
|
||||
|
||||
to: [`https://${host}/federation/channels/${message.channel_id}`],
|
||||
tag: message.mentions?.map(
|
||||
(x) => new URL(`https://${host}/federation/users/${x.id}`),
|
||||
(x) => `https://${host}/federation/users/${x.id}`,
|
||||
),
|
||||
attachment: [],
|
||||
// replies: [],
|
||||
@ -105,7 +96,7 @@ export const transformMessageToNote = async (
|
||||
};
|
||||
|
||||
// TODO: this was copied from the previous implemention. refactor it.
|
||||
export const transformNoteToMessage = async (note: AP.Note) => {
|
||||
export const transformNoteToMessage = async (note: APNote) => {
|
||||
if (!note.id) throw new APError("Note must have ID");
|
||||
if (note.type != "Note") throw new APError("Message must be Note");
|
||||
|
||||
@ -177,7 +168,7 @@ export const transformNoteToMessage = async (note: AP.Note) => {
|
||||
|
||||
export const transformChannelToGroup = async (
|
||||
channel: Channel,
|
||||
): Promise<AP.Group> => {
|
||||
): Promise<APGroup> => {
|
||||
const { host, accountDomain } = Config.get().federation;
|
||||
|
||||
const keys = await FederationKey.findOneOrFail({
|
||||
@ -187,7 +178,7 @@ export const transformChannelToGroup = async (
|
||||
return {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
type: "Group",
|
||||
id: new URL(`https://${host}/fed/channels/${channel.id}`),
|
||||
id: `https://${host}/fed/channels/${channel.id}`,
|
||||
name: channel.name,
|
||||
preferredUsername: channel.id,
|
||||
summary: channel.topic,
|
||||
@ -200,15 +191,13 @@ export const transformChannelToGroup = async (
|
||||
publicKeyPem: keys.publicKey,
|
||||
},
|
||||
|
||||
inbox: new URL(`https://${host}/fed/channels/${channel.id}/inbox`),
|
||||
outbox: new URL(`https://${host}/fed/channels/${channel.id}/outbox`),
|
||||
followers: new URL(
|
||||
`https://${host}/fed/channels/${channel.id}/followers`,
|
||||
),
|
||||
inbox: `https://${host}/fed/channels/${channel.id}/inbox`,
|
||||
outbox: `https://${host}/fed/channels/${channel.id}/outbox`,
|
||||
followers: `https://${host}/fed/channels/${channel.id}/followers`,
|
||||
};
|
||||
};
|
||||
|
||||
export const transformUserToPerson = async (user: User): Promise<AP.Person> => {
|
||||
export const transformUserToPerson = async (user: User): Promise<APPerson> => {
|
||||
const { host, accountDomain } = Config.get().federation;
|
||||
|
||||
const keys = await FederationKey.findOneOrFail({
|
||||
@ -218,7 +207,7 @@ export const transformUserToPerson = async (user: User): Promise<AP.Person> => {
|
||||
return {
|
||||
"@context": ACTIVITYSTREAMS_CONTEXT,
|
||||
type: "Person",
|
||||
id: new URL(`https://${host}/federation/users/${user.id}`),
|
||||
id: `https://${host}/federation/users/${user.id}`,
|
||||
|
||||
name: user.username,
|
||||
preferredUsername: user.id,
|
||||
@ -233,11 +222,9 @@ export const transformUserToPerson = async (user: User): Promise<AP.Person> => {
|
||||
]
|
||||
: undefined,
|
||||
|
||||
inbox: new URL(`https://${host}/federation/users/${user.id}/inbox`),
|
||||
outbox: new URL(`https://${host}/federation/users/${user.id}/outbox`),
|
||||
followers: new URL(
|
||||
`https://${host}/federation/users/${user.id}/followers`,
|
||||
),
|
||||
inbox: `https://${host}/federation/users/${user.id}/inbox`,
|
||||
outbox: `https://${host}/federation/users/${user.id}/outbox`,
|
||||
followers: `https://${host}/federation/users/${user.id}/followers`,
|
||||
publicKey: {
|
||||
id: `https://${host}/federation/users/${user.id}#main-key`,
|
||||
owner: `https://${host}/federation/users/${user.id}`,
|
||||
@ -247,7 +234,7 @@ export const transformUserToPerson = async (user: User): Promise<AP.Person> => {
|
||||
};
|
||||
|
||||
// TODO: this was copied from previous implementation. refactor.
|
||||
export const transformPersonToUser = async (person: AP.Person) => {
|
||||
export const transformPersonToUser = async (person: APPerson) => {
|
||||
if (!person.id) throw new APError("User must have ID");
|
||||
|
||||
const url = new URL(person.id.toString());
|
||||
|
@ -5,7 +5,13 @@ import {
|
||||
OrmUtils,
|
||||
WebfingerResponse,
|
||||
} from "@spacebar/util";
|
||||
import { AP } from "activitypub-core-types";
|
||||
import {
|
||||
APActivity,
|
||||
APActor,
|
||||
APObject,
|
||||
APPerson,
|
||||
AnyAPObject,
|
||||
} from "activitypub-types";
|
||||
import crypto from "crypto";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import fetch from "node-fetch";
|
||||
@ -30,7 +36,7 @@ export const hasAPContext = (data: object) => {
|
||||
return context == activitystreams;
|
||||
};
|
||||
|
||||
export const resolveAPObject = async <T extends object>(
|
||||
export const resolveAPObject = async <T extends AnyAPObject>(
|
||||
data: string | T,
|
||||
): Promise<T> => {
|
||||
// we were already given an AP object
|
||||
@ -79,7 +85,7 @@ export const splitQualifiedMention = (lookup: string) => {
|
||||
|
||||
export const resolveWebfinger = async (
|
||||
lookup: string,
|
||||
): Promise<AP.CoreObject> => {
|
||||
): Promise<AnyAPObject> => {
|
||||
const { domain } = splitQualifiedMention(lookup);
|
||||
|
||||
const agent = new ProxyAgent();
|
||||
@ -94,7 +100,7 @@ export const resolveWebfinger = async (
|
||||
const link = wellknown.links.find((x) => x.rel == "self");
|
||||
if (!link) throw new APError(".well-known did not contain rel=self link");
|
||||
|
||||
return await resolveAPObject<AP.CoreObject>(link.href);
|
||||
return await resolveAPObject<AnyAPObject>(link.href);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -107,7 +113,7 @@ export const resolveWebfinger = async (
|
||||
export const signActivity = async (
|
||||
inbox: string,
|
||||
sender: FederationKey,
|
||||
message: AP.Activity,
|
||||
message: APActivity,
|
||||
) => {
|
||||
if (!sender.privateKey)
|
||||
throw new APError("cannot sign without private key");
|
||||
@ -152,27 +158,23 @@ export const signActivity = async (
|
||||
};
|
||||
|
||||
// fetch from remote server?
|
||||
export const APObjectIsPerson = (
|
||||
object: AP.EntityReference,
|
||||
): object is AP.Person => {
|
||||
export const APObjectIsPerson = (object: AnyAPObject): object is APPerson => {
|
||||
return "type" in object && object.type == "Person";
|
||||
};
|
||||
|
||||
export const APObjectIsGroup = (
|
||||
object: AP.EntityReference,
|
||||
): object is AP.Person => {
|
||||
export const APObjectIsGroup = (object: AnyAPObject): object is APPerson => {
|
||||
return "type" in object && object.type == "Group";
|
||||
};
|
||||
|
||||
export const APObjectIsOrganisation = (
|
||||
object: AP.EntityReference,
|
||||
): object is AP.Person => {
|
||||
object: AnyAPObject,
|
||||
): object is APPerson => {
|
||||
return "type" in object && object.type == "Organization";
|
||||
};
|
||||
|
||||
export const APObjectIsSpacebarActor = (
|
||||
object: AP.EntityReference,
|
||||
): object is AP.Person => {
|
||||
object: AnyAPObject,
|
||||
): object is APPerson => {
|
||||
return (
|
||||
APObjectIsGroup(object) ||
|
||||
APObjectIsOrganisation(object) ||
|
||||
|
@ -1,35 +1,14 @@
|
||||
import { transformNoteToMessage } from "@spacebar/ap";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Message, emitEvent } from "@spacebar/util";
|
||||
import { AP } from "activitypub-core-types";
|
||||
import { APCreate, APNote } from "activitypub-types";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
const router = Router();
|
||||
|
||||
// TODO: check if the activity exists on the remote server
|
||||
router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
const body = req.body as AP.Create;
|
||||
|
||||
if (body.type != "Create") throw new HTTPError("not implemented");
|
||||
|
||||
const object = Array.isArray(body.object) ? body.object[0] : body.object;
|
||||
if (!object) return res.status(400);
|
||||
if (!("type" in object) || object.type != "Note")
|
||||
throw new HTTPError("must be Note");
|
||||
const message = await transformNoteToMessage(object as AP.Note);
|
||||
|
||||
if ((await Message.count({ where: { nonce: object.id!.toString() } })) != 0)
|
||||
return res.status(200);
|
||||
|
||||
await message.save();
|
||||
|
||||
await emitEvent({
|
||||
event: "MESSAGE_CREATE",
|
||||
channel_id: message.channel_id,
|
||||
data: message.toJSON(),
|
||||
});
|
||||
|
||||
return res.status(200);
|
||||
// TODO: check if the activity exists on the remote server
|
||||
// TODO: refactor
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
} from "@spacebar/ap";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Config, Message, Snowflake } from "@spacebar/util";
|
||||
import { AP } from "activitypub-core-types";
|
||||
import { APAnnounce } from "activitypub-types";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { FindManyOptions, FindOperator, LessThan, MoreThan } from "typeorm";
|
||||
const router = Router();
|
||||
@ -19,9 +19,9 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
page: page != undefined,
|
||||
min_id: min_id?.toString(),
|
||||
max_id: max_id?.toString(),
|
||||
id: new URL(`https://${host}/federation/channels/${channel_id}/outbox`),
|
||||
id: `https://${host}/federation/channels/${channel_id}/outbox`,
|
||||
getTotalElements: () => Message.count({ where: { channel_id } }),
|
||||
getElements: async (before, after): Promise<AP.Announce[]> => {
|
||||
getElements: async (before, after): Promise<APAnnounce[]> => {
|
||||
const query: FindManyOptions<Message> & {
|
||||
where: { id?: FindOperator<string> | FindOperator<string>[] };
|
||||
} = {
|
||||
|
@ -1,36 +1,10 @@
|
||||
import { transformNoteToMessage } from "@spacebar/ap";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Message, emitEvent } from "@spacebar/util";
|
||||
import { AP } from "activitypub-core-types";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
const router = Router();
|
||||
|
||||
// TODO: check if the activity exists on the remote server
|
||||
// TODO: support lemmy ChatMessage type?
|
||||
router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
const body = req.body as AP.Create;
|
||||
|
||||
if (body.type != "Create") throw new HTTPError("not implemented");
|
||||
|
||||
const object = Array.isArray(body.object) ? body.object[0] : body.object;
|
||||
if (!object) return res.status(400);
|
||||
if (!("type" in object) || object.type != "Note")
|
||||
throw new HTTPError("must be Note");
|
||||
const message = await transformNoteToMessage(object as AP.Note);
|
||||
|
||||
if ((await Message.count({ where: { nonce: object.id!.toString() } })) != 0)
|
||||
return res.status(200);
|
||||
|
||||
await message.save();
|
||||
|
||||
await emitEvent({
|
||||
event: "MESSAGE_CREATE",
|
||||
channel_id: message.channel_id,
|
||||
data: message.toJSON(),
|
||||
});
|
||||
|
||||
return res.status(200);
|
||||
// TODO: support lemmy ChatMessage type?
|
||||
// TODO: check if the activity exists on the remote server
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
Loading…
Reference in New Issue
Block a user