diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-11-12 02:02:25 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2021-11-12 02:02:25 +0900 |
| commit | 0e4a111f81cceed275d9bec2695f6e401fb654d8 (patch) | |
| tree | 40874799472fa07416f17b50a398ac33b7771905 /packages/backend/src/server/api/endpoints/ap | |
| parent | update deps (diff) | |
| download | sharkey-0e4a111f81cceed275d9bec2695f6e401fb654d8.tar.gz sharkey-0e4a111f81cceed275d9bec2695f6e401fb654d8.tar.bz2 sharkey-0e4a111f81cceed275d9bec2695f6e401fb654d8.zip | |
refactoring
Resolve #7779
Diffstat (limited to 'packages/backend/src/server/api/endpoints/ap')
| -rw-r--r-- | packages/backend/src/server/api/endpoints/ap/get.ts | 36 | ||||
| -rw-r--r-- | packages/backend/src/server/api/endpoints/ap/show.ts | 190 |
2 files changed, 226 insertions, 0 deletions
diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts new file mode 100644 index 0000000000..78919f43b0 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -0,0 +1,36 @@ +import $ from 'cafy'; +import define from '../../define'; +import Resolver from '@/remote/activitypub/resolver'; +import { ApiError } from '../../error'; +import * as ms from 'ms'; + +export const meta = { + tags: ['federation'], + + requireCredential: true as const, + + limit: { + duration: ms('1hour'), + max: 30 + }, + + params: { + uri: { + validator: $.str, + }, + }, + + errors: { + }, + + res: { + type: 'object' as const, + optional: false as const, nullable: false as const, + } +}; + +export default define(meta, async (ps) => { + const resolver = new Resolver(); + const object = await resolver.resolve(ps.uri); + return object; +}); diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts new file mode 100644 index 0000000000..2280d93724 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -0,0 +1,190 @@ +import $ from 'cafy'; +import define from '../../define'; +import config from '@/config/index'; +import { createPerson } from '@/remote/activitypub/models/person'; +import { createNote } from '@/remote/activitypub/models/note'; +import Resolver from '@/remote/activitypub/resolver'; +import { ApiError } from '../../error'; +import { extractDbHost } from '@/misc/convert-host'; +import { Users, Notes } from '@/models/index'; +import { Note } from '@/models/entities/note'; +import { User } from '@/models/entities/user'; +import { fetchMeta } from '@/misc/fetch-meta'; +import { isActor, isPost, getApId } from '@/remote/activitypub/type'; +import * as ms from 'ms'; + +export const meta = { + tags: ['federation'], + + requireCredential: true as const, + + limit: { + duration: ms('1hour'), + max: 30 + }, + + params: { + uri: { + validator: $.str, + }, + }, + + errors: { + noSuchObject: { + message: 'No such object.', + code: 'NO_SUCH_OBJECT', + id: 'dc94d745-1262-4e63-a17d-fecaa57efc82' + } + }, + + res: { + type: 'object' as const, + optional: false as const, nullable: false as const, + properties: { + type: { + type: 'string' as const, + optional: false as const, nullable: false as const, + enum: ['User', 'Note'] + }, + object: { + type: 'object' as const, + optional: false as const, nullable: false as const + } + } + } +}; + +export default define(meta, async (ps) => { + const object = await fetchAny(ps.uri); + if (object) { + return object; + } else { + throw new ApiError(meta.errors.noSuchObject); + } +}); + +/*** + * URIからUserかNoteを解決する + */ +async function fetchAny(uri: string) { + // URIがこのサーバーを指しているなら、ローカルユーザーIDとしてDBからフェッチ + if (uri.startsWith(config.url + '/')) { + const parts = uri.split('/'); + const id = parts.pop(); + const type = parts.pop(); + + if (type === 'notes') { + const note = await Notes.findOne(id); + + if (note) { + return { + type: 'Note', + object: await Notes.pack(note, null, { detail: true }) + }; + } + } else if (type === 'users') { + const user = await Users.findOne(id); + + if (user) { + return { + type: 'User', + object: await Users.pack(user, null, { detail: true }) + }; + } + } + } + + // ブロックしてたら中断 + const meta = await fetchMeta(); + if (meta.blockedHosts.includes(extractDbHost(uri))) return null; + + // URI(AP Object id)としてDB検索 + { + const [user, note] = await Promise.all([ + Users.findOne({ uri: uri }), + Notes.findOne({ uri: uri }) + ]); + + const packed = await mergePack(user, note); + if (packed !== null) return packed; + } + + // リモートから一旦オブジェクトフェッチ + const resolver = new Resolver(); + const object = await resolver.resolve(uri) as any; + + // /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する + // これはDBに存在する可能性があるため再度DB検索 + if (uri !== object.id) { + if (object.id.startsWith(config.url + '/')) { + const parts = object.id.split('/'); + const id = parts.pop(); + const type = parts.pop(); + + if (type === 'notes') { + const note = await Notes.findOne(id); + + if (note) { + return { + type: 'Note', + object: await Notes.pack(note, null, { detail: true }) + }; + } + } else if (type === 'users') { + const user = await Users.findOne(id); + + if (user) { + return { + type: 'User', + object: await Users.pack(user, null, { detail: true }) + }; + } + } + } + + const [user, note] = await Promise.all([ + Users.findOne({ uri: object.id }), + Notes.findOne({ uri: object.id }) + ]); + + const packed = await mergePack(user, note); + if (packed !== null) return packed; + } + + // それでもみつからなければ新規であるため登録 + if (isActor(object)) { + const user = await createPerson(getApId(object)); + return { + type: 'User', + object: await Users.pack(user, null, { detail: true }) + }; + } + + if (isPost(object)) { + const note = await createNote(getApId(object), undefined, true); + return { + type: 'Note', + object: await Notes.pack(note!, null, { detail: true }) + }; + } + + return null; +} + +async function mergePack(user: User | null | undefined, note: Note | null | undefined) { + if (user != null) { + return { + type: 'User', + object: await Users.pack(user, null, { detail: true }) + }; + } + + if (note != null) { + return { + type: 'Note', + object: await Notes.pack(note, null, { detail: true }) + }; + } + + return null; +} |