summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-04-01 19:53:38 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-04-01 19:53:38 +0900
commiteea3efccc4dd09e0a9d22ea9e72bbbf44f851490 (patch)
tree47fe256b5ccd872a440646bcf44254300bf8f9cf
parentClean up (diff)
parentUpdate README.md (diff)
downloadmisskey-eea3efccc4dd09e0a9d22ea9e72bbbf44f851490.tar.gz
misskey-eea3efccc4dd09e0a9d22ea9e72bbbf44f851490.tar.bz2
misskey-eea3efccc4dd09e0a9d22ea9e72bbbf44f851490.zip
Merge branch 'master' of https://github.com/syuilo/misskey
-rw-r--r--README.md4
-rw-r--r--src/common/remote/activitypub/renderer/context.ts (renamed from src/common/remote/activitypub/context.ts)0
-rw-r--r--src/common/remote/activitypub/renderer/document.ts7
-rw-r--r--src/common/remote/activitypub/renderer/hashtag.ts7
-rw-r--r--src/common/remote/activitypub/renderer/image.ts6
-rw-r--r--src/common/remote/activitypub/renderer/key.ts9
-rw-r--r--src/common/remote/activitypub/renderer/note.ts44
-rw-r--r--src/common/remote/activitypub/renderer/ordered-collection.ts6
-rw-r--r--src/common/remote/activitypub/renderer/person.ts20
-rw-r--r--src/server/activitypub/index.ts6
-rw-r--r--src/server/activitypub/outbox.ts45
-rw-r--r--src/server/activitypub/post.ts51
-rw-r--r--src/server/activitypub/user.ts36
13 files changed, 164 insertions, 77 deletions
diff --git a/README.md b/README.md
index f7d67247a0..4c0506709d 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ Misskey
[![][dependencies-badge]][dependencies-link]
[![][himawari-badge]][himasaku]
[![][sakurako-badge]][himasaku]
-[![][agpl-3.0-badge]][AGPL-3.0]
+[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
> Lead Maintainer: [syuilo][syuilo-link]
@@ -50,6 +50,8 @@ If you want to donate to Misskey, please see [this](./docs/donate.ja.md).
Misskey is an open-source software licensed under [GNU AGPLv3](LICENSE).
+[![][agpl-3.0-badge]][AGPL-3.0]
+
[agpl-3.0]: https://www.gnu.org/licenses/agpl-3.0.en.html
[agpl-3.0-badge]: https://img.shields.io/badge/license-AGPL--3.0-444444.svg?style=flat-square
[travis-link]: https://travis-ci.org/syuilo/misskey
diff --git a/src/common/remote/activitypub/context.ts b/src/common/remote/activitypub/renderer/context.ts
index b56f727ae7..b56f727ae7 100644
--- a/src/common/remote/activitypub/context.ts
+++ b/src/common/remote/activitypub/renderer/context.ts
diff --git a/src/common/remote/activitypub/renderer/document.ts b/src/common/remote/activitypub/renderer/document.ts
new file mode 100644
index 0000000000..4a456416a9
--- /dev/null
+++ b/src/common/remote/activitypub/renderer/document.ts
@@ -0,0 +1,7 @@
+import config from '../../../../conf';
+
+export default ({ _id, contentType }) => ({
+ type: 'Document',
+ mediaType: contentType,
+ url: `${config.drive_url}/${_id}`
+});
diff --git a/src/common/remote/activitypub/renderer/hashtag.ts b/src/common/remote/activitypub/renderer/hashtag.ts
new file mode 100644
index 0000000000..ad42700204
--- /dev/null
+++ b/src/common/remote/activitypub/renderer/hashtag.ts
@@ -0,0 +1,7 @@
+import config from '../../../../conf';
+
+export default tag => ({
+ type: 'Hashtag',
+ href: `${config.url}/search?q=#${encodeURIComponent(tag)}`,
+ name: '#' + tag
+});
diff --git a/src/common/remote/activitypub/renderer/image.ts b/src/common/remote/activitypub/renderer/image.ts
new file mode 100644
index 0000000000..345fbbec59
--- /dev/null
+++ b/src/common/remote/activitypub/renderer/image.ts
@@ -0,0 +1,6 @@
+import config from '../../../../conf';
+
+export default ({ _id }) => ({
+ type: 'Image',
+ url: `${config.drive_url}/${_id}`
+});
diff --git a/src/common/remote/activitypub/renderer/key.ts b/src/common/remote/activitypub/renderer/key.ts
new file mode 100644
index 0000000000..7148c59745
--- /dev/null
+++ b/src/common/remote/activitypub/renderer/key.ts
@@ -0,0 +1,9 @@
+import config from '../../../../conf';
+import { extractPublic } from '../../../../crypto_key';
+import { ILocalAccount } from '../../../../models/user';
+
+export default ({ username, account }) => ({
+ type: 'Key',
+ owner: `${config.url}/@${username}`,
+ publicKeyPem: extractPublic((account as ILocalAccount).keypair)
+});
diff --git a/src/common/remote/activitypub/renderer/note.ts b/src/common/remote/activitypub/renderer/note.ts
new file mode 100644
index 0000000000..2fe20b2136
--- /dev/null
+++ b/src/common/remote/activitypub/renderer/note.ts
@@ -0,0 +1,44 @@
+import renderDocument from './document';
+import renderHashtag from './hashtag';
+import config from '../../../../conf';
+import DriveFile from '../../../../models/drive-file';
+import Post from '../../../../models/post';
+import User from '../../../../models/user';
+
+export default async (user, post) => {
+ const promisedFiles = DriveFile.find({ _id: { $in: post.mediaIds } });
+ let inReplyTo;
+
+ if (post.replyId) {
+ const inReplyToPost = await Post.findOne({
+ _id: post.replyId,
+ });
+
+ if (inReplyToPost !== null) {
+ const inReplyToUser = await User.findOne({
+ _id: post.userId,
+ });
+
+ if (inReplyToUser !== null) {
+ inReplyTo = `${config.url}@${inReplyToUser.username}/${inReplyToPost._id}`;
+ }
+ }
+ } else {
+ inReplyTo = null;
+ }
+
+ const attributedTo = `${config.url}/@${user.username}`;
+
+ return {
+ id: `${attributedTo}/${post._id}`,
+ type: 'Note',
+ attributedTo,
+ content: post.textHtml,
+ published: post.createdAt.toISOString(),
+ to: 'https://www.w3.org/ns/activitystreams#Public',
+ cc: `${attributedTo}/followers`,
+ inReplyTo,
+ attachment: (await promisedFiles).map(renderDocument),
+ tag: post.tags.map(renderHashtag)
+ };
+};
diff --git a/src/common/remote/activitypub/renderer/ordered-collection.ts b/src/common/remote/activitypub/renderer/ordered-collection.ts
new file mode 100644
index 0000000000..2ca0f77354
--- /dev/null
+++ b/src/common/remote/activitypub/renderer/ordered-collection.ts
@@ -0,0 +1,6 @@
+export default (id, totalItems, orderedItems) => ({
+ id,
+ type: 'OrderedCollection',
+ totalItems,
+ orderedItems
+});
diff --git a/src/common/remote/activitypub/renderer/person.ts b/src/common/remote/activitypub/renderer/person.ts
new file mode 100644
index 0000000000..7303b30385
--- /dev/null
+++ b/src/common/remote/activitypub/renderer/person.ts
@@ -0,0 +1,20 @@
+import renderImage from './image';
+import renderKey from './key';
+import config from '../../../../conf';
+
+export default user => {
+ const id = `${config.url}/@${user.username}`;
+
+ return {
+ type: 'Person',
+ id,
+ inbox: `${id}/inbox`,
+ outbox: `${id}/outbox`,
+ preferredUsername: user.username,
+ name: user.name,
+ summary: user.description,
+ icon: user.avatarId && renderImage({ _id: user.avatarId }),
+ image: user.bannerId && renderImage({ _id: user.bannerId }),
+ publicKey: renderKey(user)
+ };
+};
diff --git a/src/server/activitypub/index.ts b/src/server/activitypub/index.ts
index 6618c291f7..c81024d15f 100644
--- a/src/server/activitypub/index.ts
+++ b/src/server/activitypub/index.ts
@@ -1,14 +1,16 @@
import * as express from 'express';
-import post from './post';
import user from './user';
import inbox from './inbox';
+import outbox from './outbox';
+import post from './post';
const app = express();
app.disable('x-powered-by');
-app.use(post);
app.use(user);
app.use(inbox);
+app.use(outbox);
+app.use(post);
export default app;
diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts
new file mode 100644
index 0000000000..c5a42ae0a9
--- /dev/null
+++ b/src/server/activitypub/outbox.ts
@@ -0,0 +1,45 @@
+import * as express from 'express';
+import context from '../../common/remote/activitypub/renderer/context';
+import renderNote from '../../common/remote/activitypub/renderer/note';
+import renderOrderedCollection from '../../common/remote/activitypub/renderer/ordered-collection';
+import parseAcct from '../../common/user/parse-acct';
+import config from '../../conf';
+import Post from '../../models/post';
+import User from '../../models/user';
+
+const app = express();
+app.disable('x-powered-by');
+
+app.get('/@:user/outbox', async (req, res) => {
+ const { username, host } = parseAcct(req.params.user);
+ if (host !== null) {
+ return res.sendStatus(422);
+ }
+
+ const user = await User.findOne({
+ usernameLower: username.toLowerCase(),
+ host: null
+ });
+ if (user === null) {
+ return res.sendStatus(404);
+ }
+
+ const id = `${config.url}/@${user.username}/inbox`;
+
+ if (username !== user.username) {
+ return res.redirect(id);
+ }
+
+ const posts = await Post.find({ userId: user._id }, {
+ limit: 20,
+ sort: { _id: -1 }
+ });
+
+ const renderedPosts = await Promise.all(posts.map(post => renderNote(user, post)));
+ const rendered = renderOrderedCollection(id, user.postsCount, renderedPosts);
+ rendered['@context'] = context;
+
+ res.json(rendered);
+});
+
+export default app;
diff --git a/src/server/activitypub/post.ts b/src/server/activitypub/post.ts
index bdfce0606d..6644563d8c 100644
--- a/src/server/activitypub/post.ts
+++ b/src/server/activitypub/post.ts
@@ -1,8 +1,7 @@
import * as express from 'express';
-import context from '../../common/remote/activitypub/context';
+import context from '../../common/remote/activitypub/renderer/context';
+import render from '../../common/remote/activitypub/renderer/note';
import parseAcct from '../../common/user/parse-acct';
-import config from '../../conf';
-import DriveFile from '../../models/drive-file';
import Post from '../../models/post';
import User from '../../models/user';
@@ -36,50 +35,10 @@ app.get('/@:user/:post', async (req, res, next) => {
return res.sendStatus(404);
}
- const promisedFiles = DriveFile.find({ _id: { $in: post.mediaIds } });
- let inReplyTo;
+ const rendered = await render(user, post);
+ rendered['@context'] = context;
- if (post.replyId) {
- const inReplyToPost = await Post.findOne({
- _id: post.replyId,
- });
-
- if (inReplyToPost !== null) {
- const inReplyToUser = await User.findOne({
- _id: post.userId,
- });
-
- if (inReplyToUser !== null) {
- inReplyTo = `${config.url}@${inReplyToUser.username}/${inReplyToPost._id}`;
- }
- }
- } else {
- inReplyTo = null;
- }
-
- const attributedTo = `${config.url}/@${user.username}`;
-
- res.json({
- '@context': context,
- id: `${attributedTo}/${post._id}`,
- type: 'Note',
- attributedTo,
- content: post.textHtml,
- published: post.createdAt.toISOString(),
- to: 'https://www.w3.org/ns/activitystreams#Public',
- cc: `${attributedTo}/followers`,
- inReplyTo,
- attachment: (await promisedFiles).map(({ _id, contentType }) => ({
- type: 'Document',
- mediaType: contentType,
- url: `${config.drive_url}/${_id}`
- })),
- tag: post.tags.map(tag => ({
- type: 'Hashtag',
- href: `${config.url}/search?q=#${encodeURIComponent(tag)}`,
- name: '#' + tag
- }))
- });
+ res.json(rendered);
});
export default app;
diff --git a/src/server/activitypub/user.ts b/src/server/activitypub/user.ts
index ef365c2078..d43a9793d4 100644
--- a/src/server/activitypub/user.ts
+++ b/src/server/activitypub/user.ts
@@ -1,9 +1,9 @@
import * as express from 'express';
import config from '../../conf';
-import { extractPublic } from '../../crypto_key';
-import context from '../../common/remote/activitypub/context';
+import context from '../../common/remote/activitypub/renderer/context';
+import render from '../../common/remote/activitypub/renderer/person';
import parseAcct from '../../common/user/parse-acct';
-import User, { ILocalAccount } from '../../models/user';
+import User from '../../models/user';
const app = express();
app.disable('x-powered-by');
@@ -27,34 +27,14 @@ app.get('/@:user', async (req, res, next) => {
return res.sendStatus(404);
}
- const id = `${config.url}/@${user.username}`;
-
if (username !== user.username) {
- return res.redirect(id);
+ return res.redirect(`${config.url}/@${user.username}`);
}
- res.json({
- '@context': context,
- type: 'Person',
- id,
- inbox: `${id}/inbox`,
- preferredUsername: user.username,
- name: user.name,
- summary: user.description,
- icon: user.avatarId && {
- type: 'Image',
- url: `${config.drive_url}/${user.avatarId}`
- },
- image: user.bannerId && {
- type: 'Image',
- url: `${config.drive_url}/${user.bannerId}`
- },
- publicKey: {
- type: 'Key',
- owner: id,
- publicKeyPem: extractPublic((user.account as ILocalAccount).keypair)
- }
- });
+ const rendered = render(user);
+ rendered['@context'] = context;
+
+ res.json(rendered);
});
export default app;