getAndCache Actor of any types

Closes https://github.com/cloudflare/wildebeest/issues/178
pull/179/head
Sven Sauleau 2023-02-03 10:13:46 +00:00
rodzic a122cb99af
commit 646b83cf93
19 zmienionych plików z 90 dodań i 36 usunięć

Wyświetl plik

@ -188,7 +188,7 @@ export async function handle(
continue
}
const person = await actors.getPersonById(db, actorURL(domain, handle.localPart))
const person = await actors.getActorById(db, actorURL(domain, handle.localPart))
if (person === null) {
console.warn(`person ${recipients[i]} not found`)
continue
@ -210,7 +210,7 @@ export async function handle(
requireComplexObject()
const actorId = getActorAsId()
const actor = await actors.getPersonById(db, activity.object.actor)
const actor = await actors.getActorById(db, activity.object.actor)
if (actor !== null) {
const follower = await actors.getAndCache(new URL(actorId), db)
await acceptFollowing(db, actor, follower)
@ -226,7 +226,7 @@ export async function handle(
const objectId = getObjectAsId()
const actorId = getActorAsId()
const receiver = await actors.getPersonById(db, objectId)
const receiver = await actors.getActorById(db, objectId)
if (receiver !== null) {
const originalActor = await actors.getAndCache(new URL(actorId), db)
const receiverAcct = `${receiver.preferredUsername}@${domain}`
@ -279,7 +279,7 @@ export async function handle(
const fromActor = await actors.getAndCache(actorId, db)
// notify the user
const targetActor = await actors.getPersonById(db, new URL(obj[originalActorIdSymbol]))
const targetActor = await actors.getActorById(db, new URL(obj[originalActorIdSymbol]))
if (targetActor === null) {
console.warn('object actor not found')
break
@ -307,7 +307,7 @@ export async function handle(
}
const fromActor = await actors.getAndCache(actorId, db)
const targetActor = await actors.getPersonById(db, new URL(obj[originalActorIdSymbol]))
const targetActor = await actors.getActorById(db, new URL(obj[originalActorIdSymbol]))
if (targetActor === null) {
console.warn('object actor not found')
break

Wyświetl plik

@ -88,10 +88,13 @@ export async function get(url: string | URL): Promise<Actor> {
return actor
}
// Get and cache the Actor locally
export async function getAndCache(url: URL, db: D1Database): Promise<Actor> {
const person = await getPersonById(db, url)
if (person !== null) {
return person
{
const actor = await getActorById(db, url)
if (actor !== null) {
return actor
}
}
const actor = await get(url)
@ -197,8 +200,8 @@ export async function updateActorProperty(db: D1Database, actorId: URL, key: str
}
}
export async function getPersonById(db: D1Database, id: URL): Promise<Person | null> {
const stmt = db.prepare('SELECT * FROM actors WHERE id=? AND type=?').bind(id.toString(), PERSON)
export async function getActorById(db: D1Database, id: URL): Promise<Actor | null> {
const stmt = db.prepare('SELECT * FROM actors WHERE id=?').bind(id.toString())
const { results } = await stmt.all()
if (!results || results.length === 0) {
return null

Wyświetl plik

@ -9,6 +9,7 @@ export const mastodonIdSymbol = Symbol()
export interface APObject {
type: string
// ObjectId, URL used for federation. Called `uri` in Mastodon APIs.
// https://www.w3.org/TR/activitypub/#obj-id
id: URL
// Link to the HTML representation of the object
url: URL

Wyświetl plik

@ -4,7 +4,7 @@ import * as actors from 'wildebeest/backend/src/activitypub/actors'
import { urlToHandle } from 'wildebeest/backend/src/utils/handle'
import { loadExternalMastodonAccount } from 'wildebeest/backend/src/mastodon/account'
import { generateWebPushMessage } from 'wildebeest/backend/src/webpush'
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
import { getActorById } from 'wildebeest/backend/src/activitypub/actors'
import type { WebPushInfos, WebPushMessage } from 'wildebeest/backend/src/webpush/webpushinfos'
import { WebPushResult } from 'wildebeest/backend/src/webpush/webpushinfos'
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'
@ -206,7 +206,7 @@ export async function getNotifications(db: D1Database, actor: Actor, domain: str
const properties = JSON.parse(result.properties)
const notifFromActorId = new URL(result.notif_from_actor_id)
const notifFromActor = await getPersonById(db, notifFromActorId)
const notifFromActor = await getActorById(db, notifFromActorId)
if (!notifFromActor) {
console.warn('unknown actor')
continue

Wyświetl plik

@ -159,6 +159,56 @@ describe('ActivityPub', () => {
assert.equal(results.length, 1)
assert.equal(results[0].domain, 'example.com')
})
test('getAndCache supports any Actor types', async () => {
// While Actor ObjectID MUST be globally unique, the Object can
// change type and Mastodon uses this behavior as a feature.
// We need to make sure our caching works with Actor that change
// types.
const actorId = new URL('https://example.com/user/foo')
globalThis.fetch = async (input: RequestInfo) => {
if (input.toString() === actorId.toString()) {
return new Response(
JSON.stringify({
id: actorId,
type: 'Service',
preferredUsername: 'sven',
name: 'sven ssss',
icon: { url: 'icon.jpg' },
image: { url: 'image.jpg' },
})
)
}
if (input.toString() === actorId.toString()) {
return new Response(
JSON.stringify({
id: actorId,
type: 'Person',
preferredUsername: 'sven',
name: 'sven ssss',
icon: { url: 'icon.jpg' },
image: { url: 'image.jpg' },
})
)
}
throw new Error(`unexpected request to "${input}"`)
}
const db = await makeDB()
await actors.getAndCache(actorId, db)
const { results } = (await db.prepare('SELECT * FROM actors').all()) as any
assert.equal(results.length, 1)
assert.equal(results[0].id, actorId.toString())
assert.equal(results[0].type, 'Service')
})
})
describe('Objects', () => {

Wyświetl plik

@ -15,7 +15,7 @@ import * as accounts_get from 'wildebeest/functions/api/v1/accounts/[id]'
import { isUUID, isUrlValid, makeDB, assertCORS, assertJSON, makeQueue } from '../utils'
import * as accounts_verify_creds from 'wildebeest/functions/api/v1/accounts/verify_credentials'
import * as accounts_update_creds from 'wildebeest/functions/api/v1/accounts/update_credentials'
import { createPerson, getPersonById } from 'wildebeest/backend/src/activitypub/actors'
import { createPerson, getActorById } from 'wildebeest/backend/src/activitypub/actors'
import { addFollowing, acceptFollowing } from 'wildebeest/backend/src/mastodon/follow'
import { insertLike } from 'wildebeest/backend/src/mastodon/like'
import { insertReblog } from 'wildebeest/backend/src/mastodon/reblog'
@ -125,7 +125,7 @@ describe('Mastodon APIs', () => {
assert.equal(data.display_name, 'newsven')
assert.equal(data.note, 'hein')
const updatedActor: any = await getPersonById(db, connectedActor.id)
const updatedActor: any = await getActorById(db, connectedActor.id)
assert(updatedActor)
assert.equal(updatedActor.name, 'newsven')
assert.equal(updatedActor.summary, 'hein')

Wyświetl plik

@ -18,7 +18,7 @@ export type Env = {
export default {
async queue(batch: MessageBatch<MessageBody>, env: Env, ctx: ExecutionContext) {
for (const message of batch.messages) {
const actor = await actors.getPersonById(env.DATABASE, new URL(message.body.actorId))
const actor = await actors.getActorById(env.DATABASE, new URL(message.body.actorId))
if (actor === null) {
console.warn(`actor ${message.body.actorId} is missing`)
return

Wyświetl plik

@ -1,7 +1,7 @@
// https://www.rfc-editor.org/rfc/rfc7033
import { parseHandle } from '../../backend/src/utils/parse'
import { getPersonById, actorURL } from 'wildebeest/backend/src/activitypub/actors'
import { getActorById, actorURL } from 'wildebeest/backend/src/activitypub/actors'
import type { Env } from '../../backend/src/types/env'
import type { WebFingerResponse } from '../../backend/src/webfinger'
@ -36,12 +36,12 @@ export async function handleRequest(request: Request, db: D1Database): Promise<R
return new Response('', { status: 403 })
}
const person = await getPersonById(db, actorURL(domain, handle.localPart))
if (person === null) {
const actor = await getActorById(db, actorURL(domain, handle.localPart))
if (actor === null) {
return new Response('', { status: 404 })
}
const jsonLink = person.id.toString()
const jsonLink = actor.id.toString()
const res: WebFingerResponse = {
subject: `acct:${handle.localPart}@${handle.domain}`,

Wyświetl plik

@ -22,7 +22,7 @@ export async function handleRequest(domain: string, db: D1Database, id: string):
return new Response('', { status: 403 })
}
const person = await actors.getPersonById(db, actorURL(domain, handle.localPart))
const person = await actors.getActorById(db, actorURL(domain, handle.localPart))
if (person === null) {
return new Response('', { status: 404 })
}

Wyświetl plik

@ -21,7 +21,7 @@ export async function handleRequest(domain: string, db: D1Database, id: string):
}
const actorId = actorURL(domain, handle.localPart)
const actor = await actors.getPersonById(db, actorId)
const actor = await actors.getActorById(db, actorId)
if (actor === null) {
return new Response('', { status: 404 })
}

Wyświetl plik

@ -1,6 +1,6 @@
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
import { getFollowers } from 'wildebeest/backend/src/mastodon/follow'
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
import { getActorById } from 'wildebeest/backend/src/activitypub/actors'
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
import type { ContextData } from 'wildebeest/backend/src/types/context'
import type { Env } from 'wildebeest/backend/src/types/env'
@ -22,7 +22,7 @@ export async function handleRequest(domain: string, db: D1Database, id: string):
}
const actorId = actorURL(domain, handle.localPart)
const actor = await getPersonById(db, actorId)
const actor = await getActorById(db, actorId)
if (actor === null) {
return new Response('', { status: 404 })
}

Wyświetl plik

@ -21,7 +21,7 @@ export async function handleRequest(domain: string, db: D1Database, id: string):
}
const actorId = actorURL(domain, handle.localPart)
const actor = await actors.getPersonById(db, actorId)
const actor = await actors.getActorById(db, actorId)
if (actor === null) {
return new Response('', { status: 404 })
}

Wyświetl plik

@ -1,6 +1,6 @@
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
import { getFollowingId } from 'wildebeest/backend/src/mastodon/follow'
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
import { getActorById } from 'wildebeest/backend/src/activitypub/actors'
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
import type { ContextData } from 'wildebeest/backend/src/types/context'
import type { Env } from 'wildebeest/backend/src/types/env'
@ -22,7 +22,7 @@ export async function handleRequest(domain: string, db: D1Database, id: string):
}
const actorId = actorURL(domain, handle.localPart)
const actor = await getPersonById(db, actorId)
const actor = await getActorById(db, actorId)
if (actor === null) {
return new Response('', { status: 404 })
}

Wyświetl plik

@ -52,7 +52,7 @@ export async function handleRequest(
}
const actorId = actorURL(domain, handle.localPart)
const actor = await actors.getPersonById(db, actorId)
const actor = await actors.getActorById(db, actorId)
if (actor === null) {
return new Response('', { status: 404 })
}

Wyświetl plik

@ -1,5 +1,5 @@
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
import { getActorById } from 'wildebeest/backend/src/activitypub/actors'
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
import type { ContextData } from 'wildebeest/backend/src/types/context'
import type { Env } from 'wildebeest/backend/src/types/env'
@ -22,7 +22,7 @@ export async function handleRequest(domain: string, db: D1Database, id: string,
}
const actorId = actorURL(domain, handle.localPart)
const actor = await getPersonById(db, actorId)
const actor = await getActorById(db, actorId)
if (actor === null) {
return new Response('', { status: 404 })
}

Wyświetl plik

@ -2,7 +2,7 @@ import { parseHandle } from 'wildebeest/backend/src/utils/parse'
import { cors } from 'wildebeest/backend/src/utils/cors'
import * as objects from 'wildebeest/backend/src/activitypub/objects'
import type { Activity } from 'wildebeest/backend/src/activitypub/activities'
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
import { getActorById } from 'wildebeest/backend/src/activitypub/actors'
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
import type { ContextData } from 'wildebeest/backend/src/types/context'
import type { Env } from 'wildebeest/backend/src/types/env'
@ -30,7 +30,7 @@ export async function handleRequest(domain: string, db: D1Database, id: string):
}
const actorId = actorURL(domain, handle.localPart)
const actor = await getPersonById(db, actorId)
const actor = await getActorById(db, actorId)
if (actor === null) {
return new Response('', { status: 404 })
}

Wyświetl plik

@ -2,7 +2,7 @@
import { cors } from 'wildebeest/backend/src/utils/cors'
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
import { getActorById } from 'wildebeest/backend/src/activitypub/actors'
import type { ContextData } from 'wildebeest/backend/src/types/context'
import type { Env } from 'wildebeest/backend/src/types/env'
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
@ -51,7 +51,7 @@ async function getRemoteAccount(handle: Handle, acct: string): Promise<Response>
async function getLocalAccount(domain: string, db: D1Database, handle: Handle): Promise<Response> {
const actorId = actorURL(domain, handle.localPart)
const actor = await getPersonById(db, actorId)
const actor = await getActorById(db, actorId)
if (actor === null) {
return new Response('', { status: 404 })
}

Wyświetl plik

@ -85,7 +85,7 @@ export async function handleRequest(
// reload the current user and sent back updated infos
{
const actor = await actors.getPersonById(db, connectedActor.id)
const actor = await actors.getActorById(db, connectedActor.id)
if (actor === null) {
return errors.notAuthorized('user not found')
}

Wyświetl plik

@ -2,7 +2,7 @@
import type { Notification, NotificationsQueryResult } from 'wildebeest/backend/src/types/notification'
import { urlToHandle } from 'wildebeest/backend/src/utils/handle'
import { getPersonById } from 'wildebeest/backend/src/activitypub/actors'
import { getActorById } from 'wildebeest/backend/src/activitypub/actors'
import { loadExternalMastodonAccount } from 'wildebeest/backend/src/mastodon/account'
import type { Person } from 'wildebeest/backend/src/activitypub/actors'
import type { Env } from 'wildebeest/backend/src/types/env'
@ -39,7 +39,7 @@ export async function handleRequest(
const row = await db.prepare(query).bind(id, connectedActor.id.toString()).first<NotificationsQueryResult>()
const from_actor_id = new URL(row.from_actor_id)
const fromActor = await getPersonById(db, from_actor_id)
const fromActor = await getActorById(db, from_actor_id)
if (!fromActor) {
throw new Error('unknown from actor')
}