From c0ff46559b8ca7bff6622c6b1601089b7a68728d Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Sun, 15 Oct 2023 20:04:03 -1000 Subject: [PATCH] Refactor configs and activitypub parts --- benchmarks/fetch.ts | 18 +++ benchmarks/posting.ts | 2 +- config/config.example.toml | 51 ++++++--- database/entities/RawActor.ts | 2 +- database/entities/Status.ts | 10 +- database/entities/User.ts | 16 +-- server/api/.well-known/webfinger/index.ts | 10 +- server/api/api/v1/accounts/index.ts | 14 ++- .../v1/accounts/update_credentials/index.ts | 14 +++ server/api/api/v1/statuses/index.ts | 5 + .../api/{ => users}/[username]/actor/index.ts | 16 +-- .../api/{ => users}/[username]/inbox/index.ts | 105 ++++++++++++++++++ .../[username]/outbox.json/index.ts | 0 tests/actor.test.ts | 16 +-- tests/api.test.ts | 4 +- tests/inbox.test.ts | 34 +++--- utils/config.ts | 4 + 17 files changed, 251 insertions(+), 70 deletions(-) create mode 100644 benchmarks/fetch.ts rename server/api/{ => users}/[username]/actor/index.ts (86%) rename server/api/{ => users}/[username]/inbox/index.ts (59%) rename server/api/{ => users}/[username]/outbox.json/index.ts (100%) diff --git a/benchmarks/fetch.ts b/benchmarks/fetch.ts new file mode 100644 index 00000000..19612fc1 --- /dev/null +++ b/benchmarks/fetch.ts @@ -0,0 +1,18 @@ +const timeBefore = performance.now(); + +const requests: Promise[] = []; + +// Repeat 1000 times +for (let i = 0; i < 1000; i++) { + requests.push( + fetch(`https://mastodon.social`, { + method: "GET", + }) + ); +} + +await Promise.all(requests); + +const timeAfter = performance.now(); + +console.log(`Time taken: ${timeAfter - timeBefore}ms`); diff --git a/benchmarks/posting.ts b/benchmarks/posting.ts index 9e80192a..fdb405ab 100644 --- a/benchmarks/posting.ts +++ b/benchmarks/posting.ts @@ -72,7 +72,7 @@ const timeAfter = performance.now(); const activities = await RawActivity.createQueryBuilder("activity") .where("activity.data->>'actor' = :actor", { - actor: `${config.http.base_url}/@test`, + actor: `${config.http.base_url}/users/test`, }) .leftJoinAndSelect("activity.objects", "objects") .getMany(); diff --git a/config/config.example.toml b/config/config.example.toml index f37d023a..15c63a1e 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -13,6 +13,25 @@ bind_port = "8080" # Bans IPv4 or IPv6 IPs (wildcards, networks and ranges are supported) banned_ips = [] +[smtp] +# SMTP server to use for sending emails +server = "smtp.example.com" +port = 465 +username = "test@example.com" +password = "password123" +tls = true + +[email] +# Sends an email to moderators when a report is received +# NOT IMPLEMENTED +send_on_report = false +# Sends an email to moderators when a user is suspended +# NOT IMPLEMENTED +send_on_suspend = false +# Sends an email to moderators when a user is unsuspended +# NOT IMPLEMENTED +send_on_unsuspend = false + [validation] # Self explanatory max_displayname_size = 50 @@ -20,9 +39,9 @@ max_bio_size = 160 max_note_size = 5000 max_avatar_size = 5_000_000 max_header_size = 5_000_000 -max_media_size = 40_000_000 -max_media_attachments = 4 -max_media_description_size = 1000 +max_media_size = 40_000_000 # MEDIA NOT IMPLEMENTED +max_media_attachments = 4 # MEDIA NOT IMPLEMENTED +max_media_description_size = 1000 # MEDIA NOT IMPLEMENTED max_username_size = 30 # An array of strings, defaults are from Akkoma username_blacklist = [ ".well-known", "~", "about", "activities" , "api", @@ -36,12 +55,12 @@ email_blacklist = [] # Valid URL schemes, otherwise the URL is parsed as text url_scheme_whitelist = [ "http", "https", "ftp", "dat", "dweb", "gopher", "hyper", "ipfs", "ipns", "irc", "xmpp", "ircs", "magnet", "mailto", "mumble", "ssb", - "gemini" ] + "gemini" ] # NOT IMPLEMENTED allowed_mime_types = [ "image/jpeg", "image/png", "image/gif", "image/heic", "image/heif", "image/webp", "image/avif", "video/webm", "video/mp4", "video/quicktime", "video/ogg", "audio/wave", "audio/wav", "audio/x-wav", "audio/x-pn-wave", "audio/vnd.wave", "audio/ogg", "audio/vorbis", "audio/mpeg", "audio/mp3", "audio/webm", "audio/flac", - "audio/aac", "audio/m4a", "audio/x-m4a", "audio/mp4", "audio/3gpp", "video/x-ms-asf" ] + "audio/aac", "audio/m4a", "audio/x-m4a", "audio/mp4", "audio/3gpp", "video/x-ms-asf" ] # MEDIA NOT IMPLEMENTED [defaults] # Default visibility for new notes @@ -58,25 +77,29 @@ header = "" use_tombstones = true # Fetch all members of collections (followers, following, etc) when receiving them # WARNING: This can be a lot of data, and is not recommended -fetch_all_collection_members = false +fetch_all_collection_members = false # NOT IMPLEMENTED # The following values must be instance domain names without "https" or glob patterns -# Rejects all activities from these instances, simply doesnt save them at all +# Rejects all activities from these instances (fediblocking) reject_activities = [] # Force posts from this instance to be followers only -force_followers_only = [] +force_followers_only = [] # NOT IMPLEMENTED # Discard all reports from these instances -discard_reports = [] +discard_reports = [] # NOT IMPLEMENTED # Discard all deletes from these instances discard_deletes = [] +# Discard all updates (edits) from these instances +discard_updates = [] # Discard all banners from these instances -discard_banners = [] +discard_banners = [] # NOT IMPLEMENTED # Discard all avatars from these instances -discard_avatars = [] +discard_avatars = [] # NOT IMPLEMENTED +# Discard all follow requests from these instances +discard_follows = [] # Force set these instances' media as sensitive -force_sensitive = [] +force_sensitive = [] # NOT IMPLEMENTED # Remove theses instances' media -remove_media = [] +remove_media = [] # NOT IMPLEMENTED [filters] @@ -91,7 +114,7 @@ username_filters = [] displayname_filters = [] # Drop users with these regex filters (only applies to new activities) bio_filters = [] -emoji_filters = [] +emoji_filters = [] # NOT IMPLEMENTED [logging] # Log all requests (warning: this is a lot of data) diff --git a/database/entities/RawActor.ts b/database/entities/RawActor.ts index a1a5b0be..2b174aad 100644 --- a/database/entities/RawActor.ts +++ b/database/entities/RawActor.ts @@ -116,7 +116,7 @@ export class RawActor extends BaseEntity { username: preferredUsername ?? "", display_name: name ?? preferredUsername ?? "", note: summary ?? "", - url: `${config.http.base_url}/@${preferredUsername}${ + url: `${config.http.base_url}/users/${preferredUsername}${ isLocalUser ? "" : `@${this.getInstanceDomain()}` }`, avatar: diff --git a/database/entities/Status.ts b/database/entities/Status.ts index 391b890d..57baa756 100644 --- a/database/entities/Status.ts +++ b/database/entities/Status.ts @@ -220,7 +220,7 @@ export class Status extends BaseEntity { } newStatus.object.data = { - id: `${config.http.base_url}/@${data.account.username}/statuses/${newStatus.id}`, + id: `${config.http.base_url}/users/${data.account.username}/statuses/${newStatus.id}`, type: "Note", summary: data.spoiler_text, content: data.content, // TODO: Format as HTML @@ -229,14 +229,14 @@ export class Status extends BaseEntity { : undefined, published: new Date().toISOString(), tag: [], - attributedTo: `${config.http.base_url}/@${data.account.username}`, + attributedTo: `${config.http.base_url}/users/${data.account.username}`, }; // Get people mentioned in the content const mentionedPeople = [ ...data.content.matchAll(/@([a-zA-Z0-9_]+)/g), ].map(match => { - return `${config.http.base_url}/@${match[1]}`; + return `${config.http.base_url}/users/${match[1]}`; }); // Map this to Users @@ -286,7 +286,7 @@ export class Status extends BaseEntity { if (data.visibility === "private") { newStatus.object.data.cc = [ - `${config.http.base_url}/@${data.account.username}/followers`, + `${config.http.base_url}/users/${data.account.username}/followers`, ]; } else if (data.visibility === "direct") { // Add nothing else @@ -296,7 +296,7 @@ export class Status extends BaseEntity { "https://www.w3.org/ns/activitystreams#Public", ]; newStatus.object.data.cc = [ - `${config.http.base_url}/@${data.account.username}/followers`, + `${config.http.base_url}/users/${data.account.username}/followers`, ]; } // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition diff --git a/database/entities/User.ts b/database/entities/User.ts index 1b4862ba..ed245970 100644 --- a/database/entities/User.ts +++ b/database/entities/User.ts @@ -379,7 +379,7 @@ export class User extends BaseEntity { // Check if actor exists const actorExists = await RawActor.getByActorId( - `${config.http.base_url}/@${this.username}` + `${config.http.base_url}/users/${this.username}` ); let actor: RawActor; @@ -395,14 +395,14 @@ export class User extends BaseEntity { "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", ], - id: `${config.http.base_url}/@${this.username}`, + id: `${config.http.base_url}/users/${this.username}`, type: "Person", preferredUsername: this.username, name: this.display_name, - inbox: `${config.http.base_url}/@${this.username}/inbox`, - outbox: `${config.http.base_url}/@${this.username}/outbox`, - followers: `${config.http.base_url}/@${this.username}/followers`, - following: `${config.http.base_url}/@${this.username}/following`, + inbox: `${config.http.base_url}/users/${this.username}/inbox`, + outbox: `${config.http.base_url}/users/${this.username}/outbox`, + followers: `${config.http.base_url}/users/${this.username}/followers`, + following: `${config.http.base_url}/users/${this.username}/following`, manuallyApprovesFollowers: false, summary: this.note, icon: { @@ -414,8 +414,8 @@ export class User extends BaseEntity { url: this.header, }, publicKey: { - id: `${config.http.base_url}/@${this.username}/actor#main-key`, - owner: `${config.http.base_url}/@${this.username}/actor`, + id: `${config.http.base_url}/users/${this.username}/actor#main-key`, + owner: `${config.http.base_url}/users/${this.username}/actor`, publicKeyPem: this.public_key, }, } as APActor; diff --git a/server/api/.well-known/webfinger/index.ts b/server/api/.well-known/webfinger/index.ts index fde58f9a..9bf6c3a5 100644 --- a/server/api/.well-known/webfinger/index.ts +++ b/server/api/.well-known/webfinger/index.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { User } from "~database/entities/User"; -import { getHost } from "@config"; +import { getConfig, getHost } from "@config"; /** * ActivityPub WebFinger endpoint @@ -14,6 +14,8 @@ export default async ( const resource = matchedRoute.query.resource; const requestedUser = resource.split("acct:")[1]; + const config = getConfig(); + // Check if user is a local user if (requestedUser.split("@")[1] !== getHost()) { return errorResponse("User is a remote user", 404); @@ -32,17 +34,17 @@ export default async ( { rel: "self", type: "application/activity+json", - href: `https://${getHost()}/@${user.username}/actor` + href: `${config.http.base_url}/users/${user.username}/actor` }, { rel: "https://webfinger.net/rel/profile-page", type: "text/html", - href: `https://${getHost()}/@${user.username}` + href: `${config.http.base_url}/users/${user.username}` }, { rel: "self", type: "application/activity+json; profile=\"https://www.w3.org/ns/activitystreams\"", - href: `https://${getHost()}/@${user.username}/actor` + href: `${config.http.base_url}/users/${user.username}/actor` } ] }) diff --git a/server/api/api/v1/accounts/index.ts b/server/api/api/v1/accounts/index.ts index 32f52c35..ce81d0ab 100644 --- a/server/api/api/v1/accounts/index.ts +++ b/server/api/api/v1/accounts/index.ts @@ -74,8 +74,6 @@ export default async (req: Request): Promise => { } ); - config.validation.max_username_size; - // Check if username is valid if (!body.username?.match(/^[a-zA-Z0-9_]+$/)) errors.details.username.push({ @@ -83,6 +81,18 @@ export default async (req: Request): Promise => { description: `must only contain letters, numbers, and underscores`, }); + // Check if username doesnt match filters + if ( + config.filters.username_filters.some( + filter => body.username?.match(filter) + ) + ) { + errors.details.username.push({ + error: "ERR_INVALID", + description: `contains blocked words`, + }); + } + // Check if username is too long if ((body.username?.length ?? 0) > config.validation.max_username_size) errors.details.username.push({ diff --git a/server/api/api/v1/accounts/update_credentials/index.ts b/server/api/api/v1/accounts/update_credentials/index.ts index d515d7dc..ffcdc0d2 100644 --- a/server/api/api/v1/accounts/update_credentials/index.ts +++ b/server/api/api/v1/accounts/update_credentials/index.ts @@ -62,6 +62,15 @@ export default async (req: Request): Promise => { ); } + // Check if display name doesnt match filters + if ( + config.filters.displayname_filters.some(filter => + display_name.match(filter) + ) + ) { + return errorResponse("Display name contains blocked words", 422); + } + user.actor.data.name = display_name; user.display_name = display_name; } @@ -75,6 +84,11 @@ export default async (req: Request): Promise => { ); } + // Check if bio doesnt match filters + if (config.filters.bio_filters.some(filter => note.match(filter))) { + return errorResponse("Bio contains blocked words", 422); + } + user.actor.data.summary = note; user.note = note; } diff --git a/server/api/api/v1/statuses/index.ts b/server/api/api/v1/statuses/index.ts index 7fc3a3bb..f8fe63c9 100644 --- a/server/api/api/v1/statuses/index.ts +++ b/server/api/api/v1/statuses/index.ts @@ -125,6 +125,11 @@ export default async (req: Request): Promise => { ); } + // Check if status body doesnt match filters + if (config.filters.note_filters.some(filter => status.match(filter))) { + return errorResponse("Status contains blocked words", 422); + } + // Create status const newStatus = await Status.createNew({ account: user, diff --git a/server/api/[username]/actor/index.ts b/server/api/users/[username]/actor/index.ts similarity index 86% rename from server/api/[username]/actor/index.ts rename to server/api/users/[username]/actor/index.ts index 052f2175..8199cdd7 100644 --- a/server/api/[username]/actor/index.ts +++ b/server/api/users/[username]/actor/index.ts @@ -89,7 +89,7 @@ export default async ( Hashtag: "as:Hashtag", }, ], - id: `${config.http.base_url}/@${user.username}`, + id: `${config.http.base_url}/users/${user.username}`, type: "Person", preferredUsername: user.username, // TODO: Add user display name name: user.username, @@ -104,21 +104,21 @@ export default async ( url: user.header, mediaType: "image/png", // TODO: Set user header mimetype }, - inbox: `${config.http.base_url}/@${user.username}/inbox`, - outbox: `${config.http.base_url}/@${user.username}/outbox`, - followers: `${config.http.base_url}/@${user.username}/followers`, - following: `${config.http.base_url}/@${user.username}/following`, - liked: `${config.http.base_url}/@${user.username}/liked`, + inbox: `${config.http.base_url}/users/${user.username}/inbox`, + outbox: `${config.http.base_url}/users/${user.username}/outbox`, + followers: `${config.http.base_url}/users/${user.username}/followers`, + following: `${config.http.base_url}/users/${user.username}/following`, + liked: `${config.http.base_url}/users/${user.username}/liked`, discoverable: true, alsoKnownAs: [ // TODO: Add accounts from which the user migrated ], manuallyApprovesFollowers: false, // TODO: Change publicKey: { - id: `${getHost()}${config.http.base_url}/@${ + id: `${getHost()}${config.http.base_url}/users/${ user.username }/actor#main-key`, - owner: `${config.http.base_url}/@${user.username}`, + owner: `${config.http.base_url}/users/${user.username}`, // Split the public key into PEM format publicKeyPem: `-----BEGIN PUBLIC KEY-----\n${user.public_key .match(/.{1,64}/g) diff --git a/server/api/[username]/inbox/index.ts b/server/api/users/[username]/inbox/index.ts similarity index 59% rename from server/api/[username]/inbox/index.ts rename to server/api/users/[username]/inbox/index.ts index 6105d01b..4678356c 100644 --- a/server/api/[username]/inbox/index.ts +++ b/server/api/users/[username]/inbox/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { getConfig } from "@config"; import { errorResponse, jsonResponse } from "@response"; @@ -30,11 +31,61 @@ export default async ( return errorResponse("Method not allowed", 405); } + const username = matchedRoute.params.username; + const config = getConfig(); + try { + if ( + config.activitypub.reject_activities.includes( + new URL(req.headers.get("Origin") ?? "").hostname + ) + ) { + // Discard request + return jsonResponse({}); + } + } catch (e) { + console.error( + `[-] Error parsing Origin header of incoming Activity from ${req.headers.get( + "Origin" + )}` + ); + console.error(e); + } + // Process request body const body: APActivity = await req.json(); + // Verify HTTP signature + const signature = req.headers.get("Signature") ?? ""; + const signatureParams = signature + .split(",") + .reduce>((params, param) => { + const [key, value] = param.split("="); + params[key] = value.replace(/"/g, ""); + return params; + }, {}); + + const signedString = `(request-target): post /users/${username}/inbox\nhost: ${ + config.http.base_url + }\ndate: ${req.headers.get("Date")}`; + const signatureBuffer = new TextEncoder().encode(signatureParams.signature); + const signatureBytes = new Uint8Array(signatureBuffer).buffer; + const publicKeyBuffer = (body.actor as any).publicKey.publicKeyPem; + const publicKey = await crypto.subtle.importKey( + "spki", + publicKeyBuffer, + { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, + false, + ["verify"] + ); + const verified = await crypto.subtle.verify( + { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, + publicKey, + signatureBytes, + new TextEncoder().encode(signedString) + ); + // Get the object's ActivityPub type const type = body.type; @@ -57,6 +108,24 @@ export default async ( // Replace the object in database with the new provided object // TODO: Add authentication + try { + if ( + config.activitypub.discard_updates.includes( + new URL(req.headers.get("Origin") ?? "").hostname + ) + ) { + // Discard request + return jsonResponse({}); + } + } catch (e) { + console.error( + `[-] Error parsing Origin header of incoming Update Activity from ${req.headers.get( + "Origin" + )}` + ); + console.error(e); + } + const object = await RawActivity.updateObjectIfExists( body.object as APObject ); @@ -78,6 +147,24 @@ export default async ( // Delete the object from database // TODO: Add authentication + try { + if ( + config.activitypub.discard_deletes.includes( + new URL(req.headers.get("Origin") ?? "").hostname + ) + ) { + // Discard request + return jsonResponse({}); + } + } catch (e) { + console.error( + `[-] Error parsing Origin header of incoming Delete Activity from ${req.headers.get( + "Origin" + )}` + ); + console.error(e); + } + const response = await RawActivity.deleteObjectIfExists( body.object as APObject ); @@ -152,6 +239,24 @@ export default async ( // Body is an APFollow object // Add the actor to the object actor's followers list + try { + if ( + config.activitypub.discard_follows.includes( + new URL(req.headers.get("Origin") ?? "").hostname + ) + ) { + // Reject request + return jsonResponse({}); + } + } catch (e) { + console.error( + `[-] Error parsing Origin header of incoming Delete Activity from ${req.headers.get( + "Origin" + )}` + ); + console.error(e); + } + const user = await User.getByActorId( (body.actor as APActor).id ?? "" ); diff --git a/server/api/[username]/outbox.json/index.ts b/server/api/users/[username]/outbox.json/index.ts similarity index 100% rename from server/api/[username]/outbox.json/index.ts rename to server/api/users/[username]/outbox.json/index.ts diff --git a/tests/actor.test.ts b/tests/actor.test.ts index 9df4dd32..0d8391ea 100644 --- a/tests/actor.test.ts +++ b/tests/actor.test.ts @@ -23,7 +23,7 @@ beforeAll(async () => { describe("POST /@test/actor", () => { test("should return a valid ActivityPub Actor when querying an existing user", async () => { - const response = await fetch(`${config.http.base_url}/@test/actor`, { + const response = await fetch(`${config.http.base_url}/users/test/actor`, { method: "GET", headers: { Accept: "application/activity+json", @@ -38,16 +38,16 @@ describe("POST /@test/actor", () => { const actor: APActor = await response.json(); expect(actor.type).toBe("Person"); - expect(actor.id).toBe(`${config.http.base_url}/@test`); + expect(actor.id).toBe(`${config.http.base_url}/users/test`); expect(actor.preferredUsername).toBe("test"); - expect(actor.inbox).toBe(`${config.http.base_url}/@test/inbox`); - expect(actor.outbox).toBe(`${config.http.base_url}/@test/outbox`); - expect(actor.followers).toBe(`${config.http.base_url}/@test/followers`); - expect(actor.following).toBe(`${config.http.base_url}/@test/following`); + expect(actor.inbox).toBe(`${config.http.base_url}/users/test/inbox`); + expect(actor.outbox).toBe(`${config.http.base_url}/users/test/outbox`); + expect(actor.followers).toBe(`${config.http.base_url}/users/test/followers`); + expect(actor.following).toBe(`${config.http.base_url}/users/test/following`); expect((actor as any).publicKey).toBeDefined(); expect((actor as any).publicKey.id).toBeDefined(); expect((actor as any).publicKey.owner).toBe( - `${config.http.base_url}/@test` + `${config.http.base_url}/users/test` ); expect((actor as any).publicKey.publicKeyPem).toBeDefined(); expect((actor as any).publicKey.publicKeyPem).toMatch( @@ -64,7 +64,7 @@ afterAll(async () => { const activities = await RawActivity.createQueryBuilder("activity") .where("activity.data->>'actor' = :actor", { - actor: `${config.http.base_url}/@test`, + actor: `${config.http.base_url}/users/test`, }) .leftJoinAndSelect("activity.objects", "objects") .getMany(); diff --git a/tests/api.test.ts b/tests/api.test.ts index 28be75a3..b579b518 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -200,7 +200,7 @@ describe("GET /api/v1/accounts/verify_credentials", () => { expect(account.following_count).toBe(0); expect(account.statuses_count).toBe(0); expect(account.note).toBe(""); - expect(account.url).toBe(`${config.http.base_url}/@${user.username}`); + expect(account.url).toBe(`${config.http.base_url}/users/${user.username}`); expect(account.avatar).toBeDefined(); expect(account.avatar_static).toBeDefined(); expect(account.header).toBeDefined(); @@ -719,7 +719,7 @@ describe("GET /api/v1/custom_emojis", () => { afterAll(async () => { const activities = await RawActivity.createQueryBuilder("activity") .where("activity.data->>'actor' = :actor", { - actor: `${config.http.base_url}/@test`, + actor: `${config.http.base_url}/users/test`, }) .leftJoinAndSelect("activity.objects", "objects") .getMany(); diff --git a/tests/inbox.test.ts b/tests/inbox.test.ts index 0f9e4437..7a6b3d7b 100644 --- a/tests/inbox.test.ts +++ b/tests/inbox.test.ts @@ -23,7 +23,7 @@ describe("POST /@test/inbox", () => { test("should store a new Note object", async () => { const activityId = `https://example.com/objects/${crypto.randomUUID()}`; - const response = await fetch(`${config.http.base_url}/@test/inbox/`, { + const response = await fetch(`${config.http.base_url}/users/test/inbox/`, { method: "POST", headers: { "Content-Type": "application/activity+json", @@ -33,7 +33,7 @@ describe("POST /@test/inbox", () => { type: "Create", id: activityId, actor: { - id: `${config.http.base_url}/@test`, + id: `${config.http.base_url}/users/test`, type: "Person", preferredUsername: "test", }, @@ -82,7 +82,7 @@ describe("POST /@test/inbox", () => { test("should try to update that Note object", async () => { const activityId = `https://example.com/objects/${crypto.randomUUID()}`; - const response = await fetch(`${config.http.base_url}/@test/inbox/`, { + const response = await fetch(`${config.http.base_url}/users/test/inbox/`, { method: "POST", headers: { "Content-Type": "application/activity+json", @@ -92,7 +92,7 @@ describe("POST /@test/inbox", () => { type: "Update", id: activityId, actor: { - id: `${config.http.base_url}/@test`, + id: `${config.http.base_url}/users/test`, type: "Person", preferredUsername: "test", }, @@ -140,7 +140,7 @@ describe("POST /@test/inbox", () => { test("should delete the Note object", async () => { const activityId = `https://example.com/objects/${crypto.randomUUID()}`; - const response = await fetch(`${config.http.base_url}/@test/inbox/`, { + const response = await fetch(`${config.http.base_url}/users/test/inbox/`, { method: "POST", headers: { "Content-Type": "application/activity+json", @@ -150,7 +150,7 @@ describe("POST /@test/inbox", () => { type: "Delete", id: activityId, actor: { - id: `${config.http.base_url}/@test`, + id: `${config.http.base_url}/users/test`, type: "Person", preferredUsername: "test", }, @@ -187,17 +187,17 @@ describe("POST /@test/inbox", () => { expect(activity?.actors).toHaveLength(1); expect(activity?.actors[0].data).toEqual({ preferredUsername: "test", - id: `${config.http.base_url}/@test`, + id: `${config.http.base_url}/users/test`, summary: "", publicKey: { - id: `${config.http.base_url}/@test/actor#main-key`, - owner: `${config.http.base_url}/@test/actor`, + id: `${config.http.base_url}/users/test/actor#main-key`, + owner: `${config.http.base_url}/users/test/actor`, publicKeyPem: expect.any(String), }, - outbox: `${config.http.base_url}/@test/outbox`, + outbox: `${config.http.base_url}/users/test/outbox`, manuallyApprovesFollowers: false, - followers: `${config.http.base_url}/@test/followers`, - following: `${config.http.base_url}/@test/following`, + followers: `${config.http.base_url}/users/test/followers`, + following: `${config.http.base_url}/users/test/following`, name: "", "@context": [ "https://www.w3.org/ns/activitystreams", @@ -211,7 +211,7 @@ describe("POST /@test/inbox", () => { type: "Image", url: "", }, - inbox: `${config.http.base_url}/@test/inbox`, + inbox: `${config.http.base_url}/users/test/inbox`, type: "Person", }); @@ -226,7 +226,7 @@ describe("POST /@test/inbox", () => { test("should return a 404 error when trying to delete a non-existent Note object", async () => { const activityId = `https://example.com/objects/${crypto.randomUUID()}`; - const response = await fetch(`${config.http.base_url}/@test/inbox/`, { + const response = await fetch(`${config.http.base_url}/users/test/inbox/`, { method: "POST", headers: { "Content-Type": "application/activity+json", @@ -236,7 +236,7 @@ describe("POST /@test/inbox", () => { type: "Delete", id: activityId, actor: { - id: `${config.http.base_url}/@test`, + id: `${config.http.base_url}/users/test`, type: "Person", preferredUsername: "test", }, @@ -274,10 +274,10 @@ afterAll(async () => { .leftJoinAndSelect("activity.objects", "objects") .leftJoinAndSelect("activity.actors", "actors") // activity.actors is a many-to-many relationship with Actor objects (it is an array of Actor objects) - // Get the actors of the activity that have data.id as `${config.http.base_url}/@test` + // Get the actors of the activity that have data.id as `${config.http.base_url}/users/test` .where("actors.data @> :data", { data: JSON.stringify({ - id: `${config.http.base_url}/@test`, + id: `${config.http.base_url}/users/test`, }), }) .getMany(); diff --git a/utils/config.ts b/utils/config.ts index 51398852..6949bc99 100644 --- a/utils/config.ts +++ b/utils/config.ts @@ -50,6 +50,8 @@ export interface ConfigType { discard_deletes: string[]; discard_banners: string[]; discard_avatars: string[]; + discard_updates: string[]; + discard_follows: string[]; force_sensitive: string[]; remove_media: string[]; fetch_all_colletion_members: boolean; @@ -178,6 +180,8 @@ export const configDefaults: ConfigType = { discard_banners: [], discard_avatars: [], force_sensitive: [], + discard_updates: [], + discard_follows: [], remove_media: [], fetch_all_colletion_members: false, },