summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/api/endpoints/ap
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-11-12 02:02:25 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-11-12 02:02:25 +0900
commit0e4a111f81cceed275d9bec2695f6e401fb654d8 (patch)
tree40874799472fa07416f17b50a398ac33b7771905 /packages/backend/src/server/api/endpoints/ap
parentupdate deps (diff)
downloadsharkey-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.ts36
-rw-r--r--packages/backend/src/server/api/endpoints/ap/show.ts190
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;
+}