Merge pull request #335 from cloudflare/sven/add-cache-to-webfinger

add Actor cache to webfinger
pull/338/head
Sven Sauleau 2023-02-23 15:38:05 +01:00 zatwierdzone przez GitHub
commit b05f9513fc
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
10 zmienionych plików z 30 dodań i 23 usunięć

Wyświetl plik

@ -18,17 +18,17 @@ export async function getAccount(domain: string, accountId: string, db: Database
} else if (handle.domain !== null) { } else if (handle.domain !== null) {
// Retrieve the statuses of a remote actor // Retrieve the statuses of a remote actor
const acct = `${handle.localPart}@${handle.domain}` const acct = `${handle.localPart}@${handle.domain}`
return getRemoteAccount(handle, acct) return getRemoteAccount(handle, acct, db)
} else { } else {
return null return null
} }
} }
async function getRemoteAccount(handle: Handle, acct: string): Promise<MastodonAccount | null> { async function getRemoteAccount(handle: Handle, acct: string, db: D1Database): Promise<MastodonAccount | null> {
// TODO: using webfinger isn't the optimal implementation. We could cache // TODO: using webfinger isn't the optimal implementation. We could cache
// the object in D1 and directly query the remote API, indicated by the actor's // the object in D1 and directly query the remote API, indicated by the actor's
// url field. For now, let's keep it simple. // url field. For now, let's keep it simple.
const actor = await queryAcct(handle.domain!, acct) const actor = await queryAcct(handle.domain!, db, acct)
if (actor === null) { if (actor === null) {
return null return null
} }

Wyświetl plik

@ -19,7 +19,7 @@ import type { APObject } from 'wildebeest/backend/src/activitypub/objects'
import type { Actor } from 'wildebeest/backend/src/activitypub/actors' import type { Actor } from 'wildebeest/backend/src/activitypub/actors'
import { type Database } from 'wildebeest/backend/src/database' import { type Database } from 'wildebeest/backend/src/database'
export async function getMentions(input: string, instanceDomain: string): Promise<Array<Actor>> { export async function getMentions(input: string, instanceDomain: string, db: Database): Promise<Array<Actor>> {
const mentions: Array<Actor> = [] const mentions: Array<Actor> = []
for (let i = 0, len = input.length; i < len; i++) { for (let i = 0, len = input.length; i < len; i++) {
@ -34,7 +34,7 @@ export async function getMentions(input: string, instanceDomain: string): Promis
const handle = parseHandle(buffer) const handle = parseHandle(buffer)
const domain = handle.domain ? handle.domain : instanceDomain const domain = handle.domain ? handle.domain : instanceDomain
const acct = `${handle.localPart}@${domain}` const acct = `${handle.localPart}@${domain}`
const targetActor = await queryAcct(domain!, acct) const targetActor = await queryAcct(domain!, db, acct)
if (targetActor === null) { if (targetActor === null) {
console.warn(`actor ${acct} not found`) console.warn(`actor ${acct} not found`)
continue continue

Wyświetl plik

@ -11,12 +11,12 @@ const headers = {
accept: 'application/jrd+json', accept: 'application/jrd+json',
} }
export async function queryAcct(domain: string, acct: string): Promise<Actor | null> { export async function queryAcct(domain: string, db: D1Database, acct: string): Promise<Actor | null> {
const url = await queryAcctLink(domain, acct) const url = await queryAcctLink(domain, acct)
if (url === null) { if (url === null) {
return null return null
} }
return actors.get(url) return actors.getAndCache(url, db)
} }
export async function queryAcctLink(domain: string, acct: string): Promise<URL | null> { export async function queryAcctLink(domain: string, acct: string): Promise<URL | null> {

Wyświetl plik

@ -976,24 +976,24 @@ describe('Mastodon APIs', () => {
{ {
rel: 'self', rel: 'self',
type: 'application/activity+json', type: 'application/activity+json',
href: 'https://social.com/sven', href: `https://${domain}/ap/users/actor`,
}, },
], ],
}) })
) )
} }
if (request.url === 'https://social.com/sven') { if (request.url === `https://${domain}/ap/users/actor`) {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
id: `https://${domain}/ap/users/actor`, id: `https://${domain}/ap/users/actor`,
type: 'Person', type: 'Person',
inbox: 'https://example.com/inbox', inbox: `https://${domain}/ap/users/actor/inbox`,
}) })
) )
} }
if (request.url === 'https://example.com/inbox') { if (request.url === `https://${domain}/ap/users/actor/inbox`) {
assert.equal(request.method, 'POST') assert.equal(request.method, 'POST')
receivedActivity = await request.json() receivedActivity = await request.json()
return new Response('') return new Response('')
@ -1040,7 +1040,7 @@ describe('Mastodon APIs', () => {
const connectedActor = actor const connectedActor = actor
const req = new Request('https://example.com', { method: 'POST' }) const req = new Request('https://' + domain, { method: 'POST' })
const res = await accounts_unfollow.handleRequest(req, db, 'actor@' + domain, connectedActor, userKEK) const res = await accounts_unfollow.handleRequest(req, db, 'actor@' + domain, connectedActor, userKEK)
assert.equal(res.status, 200) assert.equal(res.status, 200)
assertCORS(res) assertCORS(res)

Wyświetl plik

@ -202,6 +202,7 @@ describe('Mastodon APIs', () => {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
id: 'https://social.com/users/sven', id: 'https://social.com/users/sven',
type: 'Person',
inbox: 'https://social.com/sven/inbox', inbox: 'https://social.com/sven/inbox',
}) })
) )
@ -404,6 +405,7 @@ describe('Mastodon APIs', () => {
}) })
test('get mentions from status', async () => { test('get mentions from status', async () => {
const db = await makeDB()
globalThis.fetch = async (input: RequestInfo) => { globalThis.fetch = async (input: RequestInfo) => {
if (input.toString() === 'https://instance.horse/.well-known/webfinger?resource=acct%3Asven%40instance.horse') { if (input.toString() === 'https://instance.horse/.well-known/webfinger?resource=acct%3Asven%40instance.horse') {
return new Response( return new Response(
@ -467,6 +469,7 @@ describe('Mastodon APIs', () => {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
id: 'https://instance.horse/users/sven', id: 'https://instance.horse/users/sven',
type: 'Person',
}) })
) )
} }
@ -474,6 +477,7 @@ describe('Mastodon APIs', () => {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
id: 'https://cloudflare.com/users/sven', id: 'https://cloudflare.com/users/sven',
type: 'Person',
}) })
) )
} }
@ -481,6 +485,7 @@ describe('Mastodon APIs', () => {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
id: 'https://cloudflare.com/users/a', id: 'https://cloudflare.com/users/a',
type: 'Person',
}) })
) )
} }
@ -488,6 +493,7 @@ describe('Mastodon APIs', () => {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
id: 'https://cloudflare.com/users/b', id: 'https://cloudflare.com/users/b',
type: 'Person',
}) })
) )
} }
@ -496,42 +502,42 @@ describe('Mastodon APIs', () => {
} }
{ {
const mentions = await getMentions('test status', domain) const mentions = await getMentions('test status', domain, db)
assert.equal(mentions.length, 0) assert.equal(mentions.length, 0)
} }
{ {
const mentions = await getMentions('no-json@actor.com', domain) const mentions = await getMentions('no-json@actor.com', domain, db)
assert.equal(mentions.length, 0) assert.equal(mentions.length, 0)
} }
{ {
const mentions = await getMentions('@sven@instance.horse test status', domain) const mentions = await getMentions('@sven@instance.horse test status', domain, db)
assert.equal(mentions.length, 1) assert.equal(mentions.length, 1)
assert.equal(mentions[0].id.toString(), 'https://instance.horse/users/sven') assert.equal(mentions[0].id.toString(), 'https://instance.horse/users/sven')
} }
{ {
const mentions = await getMentions('@sven test status', domain) const mentions = await getMentions('@sven test status', domain, db)
assert.equal(mentions.length, 1) assert.equal(mentions.length, 1)
assert.equal(mentions[0].id.toString(), 'https://' + domain + '/users/sven') assert.equal(mentions[0].id.toString(), 'https://' + domain + '/users/sven')
} }
{ {
const mentions = await getMentions('@a @b', domain) const mentions = await getMentions('@a @b', domain, db)
assert.equal(mentions.length, 2) assert.equal(mentions.length, 2)
assert.equal(mentions[0].id.toString(), 'https://' + domain + '/users/a') assert.equal(mentions[0].id.toString(), 'https://' + domain + '/users/a')
assert.equal(mentions[1].id.toString(), 'https://' + domain + '/users/b') assert.equal(mentions[1].id.toString(), 'https://' + domain + '/users/b')
} }
{ {
const mentions = await getMentions('<p>@sven</p>', domain) const mentions = await getMentions('<p>@sven</p>', domain, db)
assert.equal(mentions.length, 1) assert.equal(mentions.length, 1)
assert.equal(mentions[0].id.toString(), 'https://' + domain + '/users/sven') assert.equal(mentions[0].id.toString(), 'https://' + domain + '/users/sven')
} }
{ {
const mentions = await getMentions('<p>@unknown</p>', domain) const mentions = await getMentions('<p>@unknown</p>', domain, db)
assert.equal(mentions.length, 0) assert.equal(mentions.length, 0)
} }
}) })

Wyświetl plik

@ -31,6 +31,7 @@ describe('Wildebeest', () => {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
id: 'https://social.com/someone', id: 'https://social.com/someone',
type: 'Person',
}) })
) )
} }

Wyświetl plik

@ -35,7 +35,7 @@ export async function handleRequest(
} }
const acct = `${handle.localPart}@${handle.domain}` const acct = `${handle.localPart}@${handle.domain}`
const targetActor = await webfinger.queryAcct(handle.domain!, acct) const targetActor = await webfinger.queryAcct(handle.domain!, db, acct)
if (targetActor === null) { if (targetActor === null) {
return new Response('', { status: 404 }) return new Response('', { status: 404 })
} }

Wyświetl plik

@ -112,7 +112,7 @@ export async function handleRequest(
const hashtags = getHashtags(body.status) const hashtags = getHashtags(body.status)
const mentions = await getMentions(body.status, domain) const mentions = await getMentions(body.status, domain, db)
if (mentions.length > 0) { if (mentions.length > 0) {
extraProperties.tag = mentions.map(newMention) extraProperties.tag = mentions.map(newMention)
} }

Wyświetl plik

@ -50,7 +50,7 @@ export async function handleRequest(db: Database, request: Request): Promise<Res
if (useWebFinger && query.domain !== null) { if (useWebFinger && query.domain !== null) {
const acct = `${query.localPart}@${query.domain}` const acct = `${query.localPart}@${query.domain}`
const res = await queryAcct(query.domain, acct) const res = await queryAcct(query.domain, db, acct)
if (res !== null) { if (res !== null) {
out.accounts.push(await loadExternalMastodonAccount(acct, res)) out.accounts.push(await loadExternalMastodonAccount(acct, res))
} }

Wyświetl plik

@ -24,7 +24,7 @@ export async function handleRequestPost(db: Database, request: Request, connecte
console.warn("account migration within an instance isn't supported") console.warn("account migration within an instance isn't supported")
return new Response('', { status: 400 }) return new Response('', { status: 400 })
} }
const actor = await queryAcct(handle.domain, acct) const actor = await queryAcct(handle.domain, db, acct)
if (actor === null) { if (actor === null) {
return errors.resourceNotFound('actor', acct) return errors.resourceNotFound('actor', acct)
} }