From f50f8830a873eb4c2c456adeb07a3b064528d367 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Thu, 15 Jun 2023 22:15:16 -0700 Subject: [PATCH 1/8] refactor: :arrow_up: use custom version of vue3-otp-input This enables the use of number inputs without the slider --- packages/client/package.json | 2 +- packages/client/src/components/MkSignin.vue | 2 +- pnpm-lock.yaml | 25 ++++++++++++--------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/client/package.json b/packages/client/package.json index 9816f6963..d024277dd 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -87,7 +87,7 @@ "vue-isyourpasswordsafe": "^2.0.0", "vue-plyr": "^7.0.0", "vue-prism-editor": "2.0.0-alpha.2", - "vue3-otp-input": "^0.4.1", + "vue3-otp-input": "github:thatonecalculator/vue3-otp-input", "vuedraggable": "4.1.0" } } diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue index 0c8fffcaf..70c7ecd59 100644 --- a/packages/client/src/components/MkSignin.vue +++ b/packages/client/src/components/MkSignin.vue @@ -101,7 +101,7 @@ =16.0.0', npm: '>=8.0.0'} - peerDependencies: - vue: ^3.0.* - dependencies: - vue: 3.3.4 - dev: true - /vue@2.7.14: resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==} dependencies: @@ -15906,3 +15897,15 @@ packages: rangetouch: 2.0.1 url-polyfill: 1.1.12 dev: true + + github.com/thatonecalculator/vue3-otp-input/098b24b224661884983a5035ef3b245a519dff14(vue@3.3.4): + resolution: {tarball: https://codeload.github.com/thatonecalculator/vue3-otp-input/tar.gz/098b24b224661884983a5035ef3b245a519dff14} + id: github.com/thatonecalculator/vue3-otp-input/098b24b224661884983a5035ef3b245a519dff14 + name: vue3-otp-input + version: 0.4.1 + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + vue: ^3.0.* + dependencies: + vue: 3.3.4 + dev: true From 074e6333069bbb84e59e408d31534ebeda9eb700 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Thu, 15 Jun 2023 22:16:21 -0700 Subject: [PATCH 2/8] fix: :adhesive_bandage: convert numeric input to string --- packages/client/src/components/MkSignin.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue index 70c7ecd59..3d2e05dfa 100644 --- a/packages/client/src/components/MkSignin.vue +++ b/packages/client/src/components/MkSignin.vue @@ -186,7 +186,7 @@ let hCaptchaResponse = $ref(null); let reCaptchaResponse = $ref(null); const updateToken = (value: string) => { - token = value; + token = value.toString(); }; const meta = $computed(() => instance); From 0c9cce7c7c74631cf7316e771469ea25b0fbef3e Mon Sep 17 00:00:00 2001 From: Namekuji Date: Thu, 15 Jun 2023 20:59:17 -0400 Subject: [PATCH 3/8] fix aode-relay compatibility --- .../src/server/api/mastodon/endpoints/meta.ts | 2 +- packages/backend/src/services/note/create.ts | 33 ++++++++++++++++--- packages/backend/src/services/relay.ts | 26 +++++++++++---- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/meta.ts b/packages/backend/src/server/api/mastodon/endpoints/meta.ts index d362d1b9e..c68a09585 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/meta.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/meta.ts @@ -12,7 +12,7 @@ export async function getInstance(response: Entity.Instance) { uri: response.uri, title: response.title || "Calckey", short_description: - response.description.substring(0, 50) || "See real server website", + response.description?.substring(0, 50) || "See real server website", description: response.description || "This is a vanilla Calckey Instance. It doesnt seem to have a description. BTW you are using the Mastodon api to access this server :)", diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index ab3f36570..35548a174 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -36,6 +36,7 @@ import { ChannelFollowings, Blockings, NoteThreadMutings, + Relays, } from "@/models/index.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; import type { App } from "@/models/entities/app.js"; @@ -56,7 +57,7 @@ import { checkHitAntenna } from "@/misc/check-hit-antenna.js"; import { getWordHardMute } from "@/misc/check-word-mute.js"; import { addNoteToAntenna } from "../add-note-to-antenna.js"; import { countSameRenotes } from "@/misc/count-same-renotes.js"; -import { deliverToRelays } from "../relay.js"; +import { deliverToRelays, getCachedRelays } from "../relay.js"; import type { Channel } from "@/models/entities/channel.js"; import { normalizeForSearch } from "@/misc/normalize-for-search.js"; import { getAntennas } from "@/misc/antenna-cache.js"; @@ -72,6 +73,7 @@ import meilisearch from "../../db/meilisearch.js"; const mutedWordsCache = new Cache< { userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[] >(1000 * 60 * 5); +const publishedNoteCache = new Cache(1000 * 10); type NotificationType = "reply" | "renote" | "quote" | "mention"; @@ -165,6 +167,7 @@ export default async ( isSilenced: User["isSilenced"]; createdAt: User["createdAt"]; isBot: User["isBot"]; + inbox?: User["inbox"]; }, data: Option, silent = false, @@ -453,7 +456,29 @@ export default async ( } if (!dontFederateInitially) { - publishNotesStream(note); + const relays = await getCachedRelays(); + if (!note.uri) { + publishNotesStream(note); + } else if ( + data.renote?.uri && + publishedNoteCache.get(data.renote.uri) !== true && + user.inbox && + relays.map((relay) => relay.inbox).includes(user.inbox) + ) { + const uri = data.renote.uri; + publishNotesStream(data.renote); + publishedNoteCache.set(uri, true); + setTimeout(() => { + publishedNoteCache.delete(uri); + }, 1000 * 10); + } else if (note.uri && publishedNoteCache.get(note.uri) !== true) { + const uri = note.uri; + publishNotesStream(note); + publishedNoteCache.set(uri, true); + setTimeout(() => { + publishedNoteCache.delete(uri); + }, 1000 * 10); + } } if (note.replyId != null) { // Only provide the reply note id here as the recipient may not be authorized to see the note. @@ -524,7 +549,6 @@ export default async ( nm.push(data.renote.userId, type); } } - // Fetch watchers nmRelatedPromises.push( notifyToWatchersOfRenotee(data.renote, user, nm, type), @@ -537,8 +561,9 @@ export default async ( }); publishMainStream(data.renote.userId, "renote", packedRenote); + const renote = data.renote; const webhooks = (await getActiveWebhooks()).filter( - (x) => x.userId === data.renote!.userId && x.on.includes("renote"), + (x) => x.userId === renote.userId && x.on.includes("renote"), ); for (const webhook of webhooks) { webhookDeliver(webhook, "renote", { diff --git a/packages/backend/src/services/relay.ts b/packages/backend/src/services/relay.ts index 244e05c03..bec4b1f86 100644 --- a/packages/backend/src/services/relay.ts +++ b/packages/backend/src/services/relay.ts @@ -37,7 +37,7 @@ export async function addRelay(inbox: string) { }).then((x) => Relays.findOneByOrFail(x.identifiers[0])); const relayActor = await getRelayActor(); - const follow = await renderFollowRelay(relay, relayActor); + const follow = renderFollowRelay(relay, relayActor); const activity = renderActivity(follow); deliver(relayActor, activity, relay.inbox); @@ -60,6 +60,7 @@ export async function removeRelay(inbox: string) { deliver(relayActor, activity, relay.inbox); await Relays.delete(relay.id); + await updateRelaysCache(); } export async function listRelay() { @@ -67,14 +68,31 @@ export async function listRelay() { return relays; } +export async function getCachedRelays(): Promise { + return await relaysCache.fetch(null, () => + Relays.findBy({ + status: "accepted", + }), + ); +} + export async function relayAccepted(id: string) { const result = await Relays.update(id, { status: "accepted", }); + await updateRelaysCache(); + return JSON.stringify(result); } +async function updateRelaysCache() { + const relays = await Relays.findBy({ + status: "accepted", + }); + relaysCache.set(null, relays); +} + export async function relayRejected(id: string) { const result = await Relays.update(id, { status: "rejected", @@ -89,11 +107,7 @@ export async function deliverToRelays( ) { if (activity == null) return; - const relays = await relaysCache.fetch(null, () => - Relays.findBy({ - status: "accepted", - }), - ); + const relays = await getCachedRelays(); if (relays.length === 0) return; // TODO From 6236661d6233a3f48fd9c3a2cf7bfc03a4850a08 Mon Sep 17 00:00:00 2001 From: Namekuji Date: Thu, 15 Jun 2023 21:56:24 -0400 Subject: [PATCH 4/8] use redis --- packages/backend/src/services/note/create.ts | 31 ++++++++++---------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 35548a174..9a7d057ee 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -69,11 +69,11 @@ import { db } from "@/db/postgre.js"; import { getActiveWebhooks } from "@/misc/webhook-cache.js"; import { shouldSilenceInstance } from "@/misc/should-block-instance.js"; import meilisearch from "../../db/meilisearch.js"; +import { redisClient } from "@/db/redis.js"; const mutedWordsCache = new Cache< { userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[] >(1000 * 60 * 5); -const publishedNoteCache = new Cache(1000 * 10); type NotificationType = "reply" | "renote" | "quote" | "mention"; @@ -457,27 +457,28 @@ export default async ( if (!dontFederateInitially) { const relays = await getCachedRelays(); + const boostedByRelay = + !!user.inbox && + relays.map((relay) => relay.inbox).includes(user.inbox); + if (!note.uri) { publishNotesStream(note); } else if ( + boostedByRelay && data.renote?.uri && - publishedNoteCache.get(data.renote.uri) !== true && - user.inbox && - relays.map((relay) => relay.inbox).includes(user.inbox) + (await redisClient.exists(`publishedNote:${data.renote.uri}`)) === 0 ) { - const uri = data.renote.uri; publishNotesStream(data.renote); - publishedNoteCache.set(uri, true); - setTimeout(() => { - publishedNoteCache.delete(uri); - }, 1000 * 10); - } else if (note.uri && publishedNoteCache.get(note.uri) !== true) { - const uri = note.uri; + const key = `publishedNote:${data.renote.uri}`; + await redisClient.set(key, 1, "EX", 10); + } else if ( + !boostedByRelay && + note.uri && + (await redisClient.exists(`publishedNote:${note.uri}`)) === 0 + ) { + const key = `publishedNote:${note.uri}`; publishNotesStream(note); - publishedNoteCache.set(uri, true); - setTimeout(() => { - publishedNoteCache.delete(uri); - }, 1000 * 10); + await redisClient.set(key, 1, "EX", 10); } } if (note.replyId != null) { From fda6a1b1d2d833488c7b0ea4901f8df4961e712d Mon Sep 17 00:00:00 2001 From: Namekuji Date: Fri, 16 Jun 2023 02:48:57 -0400 Subject: [PATCH 5/8] wait a bit more --- packages/backend/src/services/note/create.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 9a7d057ee..3423ef01d 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -470,7 +470,7 @@ export default async ( ) { publishNotesStream(data.renote); const key = `publishedNote:${data.renote.uri}`; - await redisClient.set(key, 1, "EX", 10); + await redisClient.set(key, 1, "EX", 30); } else if ( !boostedByRelay && note.uri && @@ -478,7 +478,7 @@ export default async ( ) { const key = `publishedNote:${note.uri}`; publishNotesStream(note); - await redisClient.set(key, 1, "EX", 10); + await redisClient.set(key, 1, "EX", 30); } } if (note.replyId != null) { From 2b1e16d48991cb8c9bfe15043a297ab1c783b863 Mon Sep 17 00:00:00 2001 From: Namekuji Date: Fri, 16 Jun 2023 03:45:53 -0400 Subject: [PATCH 6/8] add comments --- packages/backend/src/services/note/create.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 3423ef01d..b493ae5e0 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -457,17 +457,21 @@ export default async ( if (!dontFederateInitially) { const relays = await getCachedRelays(); + // Some relays (e.g., aode-relay) deliver posts by boosting them as + // Announce activities. In that case, user is the relay's actor. const boostedByRelay = !!user.inbox && relays.map((relay) => relay.inbox).includes(user.inbox); if (!note.uri) { + // Publish if the post is local publishNotesStream(note); } else if ( boostedByRelay && data.renote?.uri && (await redisClient.exists(`publishedNote:${data.renote.uri}`)) === 0 ) { + // Publish if the post was boosted by a relay and not yet published. publishNotesStream(data.renote); const key = `publishedNote:${data.renote.uri}`; await redisClient.set(key, 1, "EX", 30); @@ -476,6 +480,9 @@ export default async ( note.uri && (await redisClient.exists(`publishedNote:${note.uri}`)) === 0 ) { + // Publish if the post came directly from a remote server, or from a + // relay that doesn't boost the post (e.g, YUKIMOCHI Activity-Relay), + // and not yet published. const key = `publishedNote:${note.uri}`; publishNotesStream(note); await redisClient.set(key, 1, "EX", 30); From c5ccd68c08b940d186ea13ec9f78c0173ed5d662 Mon Sep 17 00:00:00 2001 From: Namekuji Date: Fri, 16 Jun 2023 04:17:32 -0400 Subject: [PATCH 7/8] remove unused import --- packages/backend/src/services/note/create.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index b493ae5e0..9696c3cca 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -36,7 +36,6 @@ import { ChannelFollowings, Blockings, NoteThreadMutings, - Relays, } from "@/models/index.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; import type { App } from "@/models/entities/app.js"; From aa46f5f1bb9e2d5170079282081977a8048b5392 Mon Sep 17 00:00:00 2001 From: Namekuji Date: Fri, 16 Jun 2023 04:36:11 -0400 Subject: [PATCH 8/8] skip if actor is not found or has been already deleted --- .../backend/src/queue/processors/db/delete-account.ts | 4 +--- .../backend/src/remote/activitypub/kernel/delete/actor.ts | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/queue/processors/db/delete-account.ts b/packages/backend/src/queue/processors/db/delete-account.ts index a356ca7ab..1cd7642ba 100644 --- a/packages/backend/src/queue/processors/db/delete-account.ts +++ b/packages/backend/src/queue/processors/db/delete-account.ts @@ -17,9 +17,7 @@ export async function deleteAccount( logger.info(`Deleting account of ${job.data.user.id} ...`); const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - return; - } + if (!user) return; { // Delete notes diff --git a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts index 3571135aa..83c6442dd 100644 --- a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts +++ b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts @@ -15,9 +15,11 @@ export async function deleteActor( return `skip: delete actor ${actor.uri} !== ${uri}`; } - const user = await Users.findOneByOrFail({ id: actor.id }); - if (user.isDeleted) { - logger.info("skip: already deleted"); + const user = await Users.findOneBy({ id: actor.id }); + if (!user) { + return `skip: actor ${actor.id} not found in the local database`; + } else if (user.isDeleted) { + return `skip: user ${user.id} already deleted`; } const job = await createDeleteAccountJob(actor);