kopia lustrzana https://github.com/cloudflare/wildebeest
add Actor cache to webfinger
rodzic
dd8662ba57
commit
a876ef9ad2
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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',
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 })
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue