From d7c13b975f55c85b695b72a3ded3d5de97227414 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 4 Apr 2018 22:45:55 +0900 Subject: Retry HTTP requests --- src/remote/activitypub/act/follow.ts | 4 ++-- src/remote/activitypub/act/undo/unfollow.ts | 4 ++-- src/remote/activitypub/delete/post.ts | 4 ++-- src/remote/activitypub/resolve-person.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/remote') diff --git a/src/remote/activitypub/act/follow.ts b/src/remote/activitypub/act/follow.ts index 23fa41df8e..222a257e1a 100644 --- a/src/remote/activitypub/act/follow.ts +++ b/src/remote/activitypub/act/follow.ts @@ -3,7 +3,7 @@ import parseAcct from '../../../acct/parse'; import Following, { IFollowing } from '../../../models/following'; import User from '../../../models/user'; import config from '../../../config'; -import queue from '../../../queue'; +import { createHttp } from '../../../queue'; import context from '../renderer/context'; import renderAccept from '../renderer/accept'; import request from '../../request'; @@ -44,7 +44,7 @@ export default async (resolver: Resolver, actor, activity, distribute) => { followerId: actor._id, followeeId: followee._id }).then(following => new Promise((resolve, reject) => { - queue.create('http', { + createHttp({ type: 'follow', following: following._id }).save(error => { diff --git a/src/remote/activitypub/act/undo/unfollow.ts b/src/remote/activitypub/act/undo/unfollow.ts index c17e06e8a9..4f15d9a3e4 100644 --- a/src/remote/activitypub/act/undo/unfollow.ts +++ b/src/remote/activitypub/act/undo/unfollow.ts @@ -1,7 +1,7 @@ -import queue from '../../../../queue'; +import { createHttp } from '../../../../queue'; export default ({ $id }) => new Promise((resolve, reject) => { - queue.create('http', { type: 'unfollow', id: $id }).save(error => { + createHttp({ type: 'unfollow', id: $id }).save(error => { if (error) { reject(error); } else { diff --git a/src/remote/activitypub/delete/post.ts b/src/remote/activitypub/delete/post.ts index f6c816647d..59ae8c2b94 100644 --- a/src/remote/activitypub/delete/post.ts +++ b/src/remote/activitypub/delete/post.ts @@ -1,10 +1,10 @@ import Post from '../../../models/post'; -import queue from '../../../queue'; +import { createDb } from '../../../queue'; export default async ({ $id }) => { const promisedDeletion = Post.findOneAndDelete({ _id: $id }); - await new Promise((resolve, reject) => queue.create('db', { + await new Promise((resolve, reject) => createDb({ type: 'deletePostDependents', id: $id }).delay(65536).save(error => error ? reject(error) : resolve())); diff --git a/src/remote/activitypub/resolve-person.ts b/src/remote/activitypub/resolve-person.ts index 59be65908e..2cf3ad32d8 100644 --- a/src/remote/activitypub/resolve-person.ts +++ b/src/remote/activitypub/resolve-person.ts @@ -1,7 +1,7 @@ import { JSDOM } from 'jsdom'; import { toUnicode } from 'punycode'; import User, { validateUsername, isValidName, isValidDescription } from '../../models/user'; -import queue from '../../queue'; +import { createHttp } from '../../queue'; import webFinger from '../webfinger'; import create from './create'; import Resolver from './resolver'; @@ -69,7 +69,7 @@ export default async (value, verifier?: string) => { }, }); - queue.create('http', { + createHttp({ type: 'performActivityPub', actor: user._id, outbox -- cgit v1.2.3-freya From 168b0730b46fd283b900b553dd2eede2aa4c7dac Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 5 Apr 2018 01:24:39 +0900 Subject: Implement Mention object --- src/post/create.ts | 14 ++------------ src/queue/processors/http/process-inbox.ts | 2 +- src/remote/activitypub/create.ts | 12 +++++++++++- src/remote/activitypub/resolve-person.ts | 5 ++--- src/remote/resolve-user.ts | 3 ++- src/server/api/endpoints/posts/create.ts | 10 +++++++--- 6 files changed, 25 insertions(+), 21 deletions(-) (limited to 'src/remote') diff --git a/src/post/create.ts b/src/post/create.ts index ecea37382d..4ad1503e0f 100644 --- a/src/post/create.ts +++ b/src/post/create.ts @@ -1,8 +1,6 @@ -import parseAcct from '../acct/parse'; import Post from '../models/post'; -import User from '../models/user'; -export default async (post, reply, repost, atMentions) => { +export default async (post, reply, repost, mentions) => { post.mentions = []; function addMention(mentionee) { @@ -36,15 +34,7 @@ export default async (post, reply, repost, atMentions) => { post._repost = null; } - await Promise.all(atMentions.map(async mention => { - // Fetch mentioned user - // SELECT _id - const { _id } = await User - .findOne(parseAcct(mention), { _id: true }); - - // Add mention - addMention(_id); - })); + await Promise.all(mentions.map(({ _id }) => addMention(_id))); return Post.insert(post); }; diff --git a/src/queue/processors/http/process-inbox.ts b/src/queue/processors/http/process-inbox.ts index 88fbb97377..7eeaa19f8a 100644 --- a/src/queue/processors/http/process-inbox.ts +++ b/src/queue/processors/http/process-inbox.ts @@ -25,7 +25,7 @@ export default async ({ data }, done) => { }) as IRemoteUser; if (user === null) { - user = await resolvePerson(data.signature.keyId); + user = await resolvePerson(new Resolver(), data.signature.keyId); } } diff --git a/src/remote/activitypub/create.ts b/src/remote/activitypub/create.ts index 97c72860fd..710d56fd3d 100644 --- a/src/remote/activitypub/create.ts +++ b/src/remote/activitypub/create.ts @@ -7,6 +7,7 @@ import { IRemoteUser } from '../../models/user'; import uploadFromUrl from '../../drive/upload-from-url'; import createPost from '../../post/create'; import distributePost from '../../post/distribute'; +import resolvePerson from './resolve-person'; import Resolver from './resolver'; const createDOMPurify = require('dompurify'); @@ -53,6 +54,15 @@ class Creator { .map(({ object }) => object.$id); const { window } = new JSDOM(note.content); + const mentions = []; + + for (const { href, type } of note.tags) { + switch (type) { + case 'Mention': + mentions.push(resolvePerson(resolver, href)); + break; + } + } const inserted = await createPost({ channelId: undefined, @@ -69,7 +79,7 @@ class Creator { viaMobile: false, geo: undefined, uri: note.id - }, null, null, []); + }, null, null, await Promise.all(mentions)); const promises = []; diff --git a/src/remote/activitypub/resolve-person.ts b/src/remote/activitypub/resolve-person.ts index 2cf3ad32d8..7ed01e3222 100644 --- a/src/remote/activitypub/resolve-person.ts +++ b/src/remote/activitypub/resolve-person.ts @@ -4,14 +4,13 @@ import User, { validateUsername, isValidName, isValidDescription } from '../../m import { createHttp } from '../../queue'; import webFinger from '../webfinger'; import create from './create'; -import Resolver from './resolver'; async function isCollection(collection) { return ['Collection', 'OrderedCollection'].includes(collection.type); } -export default async (value, verifier?: string) => { - const { resolver, object } = await new Resolver().resolveOne(value); +export default async (parentResolver, value, verifier?: string) => { + const { resolver, object } = parentResolver.resolveOne(value); if ( object === null || diff --git a/src/remote/resolve-user.ts b/src/remote/resolve-user.ts index 48219e8cb3..097ed66738 100644 --- a/src/remote/resolve-user.ts +++ b/src/remote/resolve-user.ts @@ -1,6 +1,7 @@ import { toUnicode, toASCII } from 'punycode'; import User from '../models/user'; import resolvePerson from './activitypub/resolve-person'; +import Resolver from './activitypub/resolver'; import webFinger from './webfinger'; export default async (username, host, option) => { @@ -19,7 +20,7 @@ export default async (username, host, option) => { throw new Error(); } - user = await resolvePerson(self.href, acctLower); + user = await resolvePerson(new Resolver(), self.href, acctLower); } return user; diff --git a/src/server/api/endpoints/posts/create.ts b/src/server/api/endpoints/posts/create.ts index 03af7ee763..47897626f1 100644 --- a/src/server/api/endpoints/posts/create.ts +++ b/src/server/api/endpoints/posts/create.ts @@ -3,12 +3,13 @@ */ import $ from 'cafy'; import deepEqual = require('deep-equal'); +import parseAcct from '../../../../acct/parse'; import renderAcct from '../../../../acct/render'; import config from '../../../../config'; import html from '../../../../text/html'; import parse from '../../../../text/parse'; import Post, { IPost, isValidText, isValidCw } from '../../../../models/post'; -import { ILocalUser } from '../../../../models/user'; +import User, { ILocalUser } from '../../../../models/user'; import Channel, { IChannel } from '../../../../models/channel'; import DriveFile from '../../../../models/drive-file'; import create from '../../../../post/create'; @@ -267,7 +268,10 @@ module.exports = (params, user: ILocalUser, app) => new Promise(async (res, rej) .filter(t => t.type == 'mention') .map(renderAcct) // Drop dupulicates - .filter((v, i, s) => s.indexOf(v) == i); + .filter((v, i, s) => s.indexOf(v) == i) + // Fetch mentioned user + // SELECT _id + .map(mention => User.findOne(parseAcct(mention), { _id: true })); } // 投稿を作成 @@ -286,7 +290,7 @@ module.exports = (params, user: ILocalUser, app) => new Promise(async (res, rej) viaMobile: viaMobile, visibility, geo - }, reply, repost, atMentions); + }, reply, repost, await Promise.all(atMentions)); const postObj = await distribute(user, post.mentions, post); -- cgit v1.2.3-freya From f44a7389e2b67e524c953843fcb4e007349c76c2 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 5 Apr 2018 01:29:56 +0900 Subject: Implement Hashtag object --- src/remote/activitypub/create.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/remote') diff --git a/src/remote/activitypub/create.ts b/src/remote/activitypub/create.ts index 710d56fd3d..31e9dba863 100644 --- a/src/remote/activitypub/create.ts +++ b/src/remote/activitypub/create.ts @@ -55,9 +55,16 @@ class Creator { const { window } = new JSDOM(note.content); const mentions = []; + const tags = []; - for (const { href, type } of note.tags) { + for (const { href, name, type } of note.tags) { switch (type) { + case 'Hashtag': + if (name.startsWith('#')) { + tags.push(name.slice(1)); + } + break; + case 'Mention': mentions.push(resolvePerson(resolver, href)); break; @@ -78,7 +85,8 @@ class Creator { appId: null, viaMobile: false, geo: undefined, - uri: note.id + uri: note.id, + tags }, null, null, await Promise.all(mentions)); const promises = []; -- cgit v1.2.3-freya From ced6b9a76f71e95a376dc4cb37b6b5617a0321d9 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 5 Apr 2018 01:57:18 +0900 Subject: Handle inReplyTo property --- src/remote/activitypub/create.ts | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'src/remote') diff --git a/src/remote/activitypub/create.ts b/src/remote/activitypub/create.ts index 31e9dba863..3bc0c66f35 100644 --- a/src/remote/activitypub/create.ts +++ b/src/remote/activitypub/create.ts @@ -48,11 +48,6 @@ class Creator { throw new Error(); } - const mediaIds = 'attachment' in note && - (await Promise.all(await this.create(resolver, note.attachment))) - .filter(media => media !== null && media.object.$ref === 'driveFiles.files') - .map(({ object }) => object.$id); - const { window } = new JSDOM(note.content); const mentions = []; const tags = []; @@ -71,13 +66,27 @@ class Creator { } } + const [mediaIds, reply] = await Promise.all([ + 'attachment' in note && this.create(resolver, note.attachment) + .then(collection => Promise.all(collection)) + .then(collection => collection + .filter(media => media !== null && media.object.$ref === 'driveFiles.files') + .map(({ object }: IResult) => object.$id)), + + 'inReplyTo' in note && this.create(resolver, note.inReplyTo) + .then(collection => Promise.all(collection.map(promise => promise.then(result => { + if (result !== null && result.object.$ref === 'posts') { + throw result.object; + } + }, () => { })))) + .then(() => null, ({ $id }) => Post.findOne({ _id: $id })) + ]); + const inserted = await createPost({ channelId: undefined, index: undefined, createdAt: new Date(note.published), mediaIds, - replyId: undefined, - repostId: undefined, poll: undefined, text: window.document.body.textContent, textHtml: note.content && createDOMPurify(window).sanitize(note.content), @@ -87,7 +96,7 @@ class Creator { geo: undefined, uri: note.id, tags - }, null, null, await Promise.all(mentions)); + }, reply, null, await Promise.all(mentions)); const promises = []; -- cgit v1.2.3-freya From 068c0b4cceaa9461cc87a6d44abebdfc429d1861 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 5 Apr 2018 21:16:14 +0900 Subject: Add a missing await expression --- src/remote/activitypub/resolve-person.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/remote') diff --git a/src/remote/activitypub/resolve-person.ts b/src/remote/activitypub/resolve-person.ts index 7ed01e3222..a7c0020dd8 100644 --- a/src/remote/activitypub/resolve-person.ts +++ b/src/remote/activitypub/resolve-person.ts @@ -10,7 +10,7 @@ async function isCollection(collection) { } export default async (parentResolver, value, verifier?: string) => { - const { resolver, object } = parentResolver.resolveOne(value); + const { resolver, object } = await parentResolver.resolveOne(value); if ( object === null || -- cgit v1.2.3-freya From 6752594578e64a26c0cd1c5a9fd2d83313134466 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 6 Apr 2018 12:20:11 +0900 Subject: Resolve local Person ID --- src/remote/activitypub/resolve-person.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src/remote') diff --git a/src/remote/activitypub/resolve-person.ts b/src/remote/activitypub/resolve-person.ts index a7c0020dd8..84746169f5 100644 --- a/src/remote/activitypub/resolve-person.ts +++ b/src/remote/activitypub/resolve-person.ts @@ -1,5 +1,7 @@ import { JSDOM } from 'jsdom'; import { toUnicode } from 'punycode'; +import parseAcct from '../../acct/parse'; +import config from '../../config'; import User, { validateUsername, isValidName, isValidDescription } from '../../models/user'; import { createHttp } from '../../queue'; import webFinger from '../webfinger'; @@ -10,10 +12,18 @@ async function isCollection(collection) { } export default async (parentResolver, value, verifier?: string) => { + const id = value.id || value; + const localPrefix = config.url + '/@'; + + if (id.startsWith(localPrefix)) { + return User.findOne(parseAcct(id.slice(localPrefix))); + } + const { resolver, object } = await parentResolver.resolveOne(value); if ( object === null || + object.id !== id || object.type !== 'Person' || typeof object.preferredUsername !== 'string' || !validateUsername(object.preferredUsername) || @@ -36,7 +46,7 @@ export default async (parentResolver, value, verifier?: string) => { resolved => isCollection(resolved.object) ? resolved.object : null, () => null ), - webFinger(object.id, verifier), + webFinger(id, verifier), ]); const host = toUnicode(finger.subject.replace(/^.*?@/, '')); @@ -64,7 +74,7 @@ export default async (parentResolver, value, verifier?: string) => { publicKeyPem: object.publicKey.publicKeyPem }, inbox: object.inbox, - uri: object.id, + uri: id, }, }); -- cgit v1.2.3-freya From 41acde0d8a5fd59e4b0250d650eade3019947f2e Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 6 Apr 2018 12:53:39 +0900 Subject: Resolve local Object ID --- src/remote/activitypub/create.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'src/remote') diff --git a/src/remote/activitypub/create.ts b/src/remote/activitypub/create.ts index 3bc0c66f35..bbe595a454 100644 --- a/src/remote/activitypub/create.ts +++ b/src/remote/activitypub/create.ts @@ -1,8 +1,10 @@ import { JSDOM } from 'jsdom'; import { ObjectID } from 'mongodb'; +import parseAcct from '../../acct/parse'; import config from '../../config'; import DriveFile from '../../models/drive-file'; import Post from '../../models/post'; +import User from '../../models/user'; import { IRemoteUser } from '../../models/user'; import uploadFromUrl from '../../drive/upload-from-url'; import createPost from '../../post/create'; @@ -133,6 +135,41 @@ class Creator { return collection.object.map(async element => { const uri = element.id || element; + const localPrefix = config.url + '/@'; + + if (uri.startsWith(localPrefix)) { + const [acct, id] = uri.slice(localPrefix).split('/', 2); + const user = await User.aggregate([ + { + $match: parseAcct(acct) + }, + { + $lookup: { + from: 'posts', + localField: '_id', + foreignField: 'userId', + as: 'post' + } + }, + { + $match: { + post: { _id: id } + } + } + ]); + + if (user === null || user.posts.length <= 0) { + throw new Error(); + } + + return { + resolver: collection.resolver, + object: { + $ref: 'posts', + id + } + }; + } try { await Promise.all([ -- cgit v1.2.3-freya