summaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-04-08 06:55:26 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-04-08 06:55:26 +0900
commit6e34e77372bd74c85ebf5a6b4214c818231dbe8b (patch)
tree614e46573693bb4e0b6d097fde88a52d1cd0fa1f /src/server
parentFix bug (diff)
downloadsharkey-6e34e77372bd74c85ebf5a6b4214c818231dbe8b.tar.gz
sharkey-6e34e77372bd74c85ebf5a6b4214c818231dbe8b.tar.bz2
sharkey-6e34e77372bd74c85ebf5a6b4214c818231dbe8b.zip
Implement announce
And bug fixes
Diffstat (limited to 'src/server')
-rw-r--r--src/server/activitypub/note.ts23
-rw-r--r--src/server/activitypub/outbox.ts2
-rw-r--r--src/server/api/endpoints/posts/create.ts251
3 files changed, 5 insertions, 271 deletions
diff --git a/src/server/activitypub/note.ts b/src/server/activitypub/note.ts
index cea9be52da..1c2e695b80 100644
--- a/src/server/activitypub/note.ts
+++ b/src/server/activitypub/note.ts
@@ -1,40 +1,25 @@
import * as express from 'express';
import context from '../../remote/activitypub/renderer/context';
import render from '../../remote/activitypub/renderer/note';
-import parseAcct from '../../acct/parse';
import Note from '../../models/note';
-import User from '../../models/user';
const app = express.Router();
-app.get('/@:user/:note', async (req, res, next) => {
+app.get('/notes/:note', async (req, res, next) => {
const accepted = req.accepts(['html', 'application/activity+json', 'application/ld+json']);
if (!(['application/activity+json', 'application/ld+json'] as any[]).includes(accepted)) {
return next();
}
- 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 note = await Note.findOne({
- _id: req.params.note,
- userId: user._id
+ _id: req.params.note
});
+
if (note === null) {
return res.sendStatus(404);
}
- const rendered = await render(user, note);
+ const rendered = await render(note);
rendered['@context'] = context;
res.json(rendered);
diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts
index b6f3a3f9db..4557871bc5 100644
--- a/src/server/activitypub/outbox.ts
+++ b/src/server/activitypub/outbox.ts
@@ -16,7 +16,7 @@ app.get('/@:user/outbox', withUser(username => {
sort: { _id: -1 }
});
- const renderedNotes = await Promise.all(notes.map(note => renderNote(user, note)));
+ const renderedNotes = await Promise.all(notes.map(note => renderNote(note)));
const rendered = renderOrderedCollection(`${config.url}/@${user.username}/inbox`, user.notesCount, renderedNotes);
rendered['@context'] = context;
diff --git a/src/server/api/endpoints/posts/create.ts b/src/server/api/endpoints/posts/create.ts
deleted file mode 100644
index 7e79912b1b..0000000000
--- a/src/server/api/endpoints/posts/create.ts
+++ /dev/null
@@ -1,251 +0,0 @@
-/**
- * Module dependencies
- */
-import $ from 'cafy';
-import deepEqual = require('deep-equal');
-import Note, { INote, isValidText, isValidCw, pack } from '../../../../models/note';
-import { ILocalUser } from '../../../../models/user';
-import Channel, { IChannel } from '../../../../models/channel';
-import DriveFile from '../../../../models/drive-file';
-import create from '../../../../services/note/create';
-import { IApp } from '../../../../models/app';
-
-/**
- * Create a note
- *
- * @param {any} params
- * @param {any} user
- * @param {any} app
- * @return {Promise<any>}
- */
-module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => {
- // Get 'visibility' parameter
- const [visibility = 'public', visibilityErr] = $(params.visibility).optional.string().or(['public', 'unlisted', 'private', 'direct']).$;
- if (visibilityErr) return rej('invalid visibility');
-
- // Get 'text' parameter
- const [text, textErr] = $(params.text).optional.string().pipe(isValidText).$;
- if (textErr) return rej('invalid text');
-
- // Get 'cw' parameter
- const [cw, cwErr] = $(params.cw).optional.string().pipe(isValidCw).$;
- if (cwErr) return rej('invalid cw');
-
- // Get 'viaMobile' parameter
- const [viaMobile = false, viaMobileErr] = $(params.viaMobile).optional.boolean().$;
- if (viaMobileErr) return rej('invalid viaMobile');
-
- // Get 'tags' parameter
- const [tags = [], tagsErr] = $(params.tags).optional.array('string').unique().eachQ(t => t.range(1, 32)).$;
- if (tagsErr) return rej('invalid tags');
-
- // Get 'geo' parameter
- const [geo, geoErr] = $(params.geo).optional.nullable.strict.object()
- .have('coordinates', $().array().length(2)
- .item(0, $().number().range(-180, 180))
- .item(1, $().number().range(-90, 90)))
- .have('altitude', $().nullable.number())
- .have('accuracy', $().nullable.number())
- .have('altitudeAccuracy', $().nullable.number())
- .have('heading', $().nullable.number().range(0, 360))
- .have('speed', $().nullable.number())
- .$;
- if (geoErr) return rej('invalid geo');
-
- // Get 'mediaIds' parameter
- const [mediaIds, mediaIdsErr] = $(params.mediaIds).optional.array('id').unique().range(1, 4).$;
- if (mediaIdsErr) return rej('invalid mediaIds');
-
- let files = [];
- if (mediaIds !== undefined) {
- // Fetch files
- // forEach だと途中でエラーなどがあっても return できないので
- // 敢えて for を使っています。
- for (const mediaId of mediaIds) {
- // Fetch file
- // SELECT _id
- const entity = await DriveFile.findOne({
- _id: mediaId,
- 'metadata.userId': user._id
- });
-
- if (entity === null) {
- return rej('file not found');
- } else {
- files.push(entity);
- }
- }
- } else {
- files = null;
- }
-
- // Get 'renoteId' parameter
- const [renoteId, renoteIdErr] = $(params.renoteId).optional.id().$;
- if (renoteIdErr) return rej('invalid renoteId');
-
- let renote: INote = null;
- let isQuote = false;
- if (renoteId !== undefined) {
- // Fetch renote to note
- renote = await Note.findOne({
- _id: renoteId
- });
-
- if (renote == null) {
- return rej('renoteee is not found');
- } else if (renote.renoteId && !renote.text && !renote.mediaIds) {
- return rej('cannot renote to renote');
- }
-
- // Fetch recently note
- const latestNote = await Note.findOne({
- userId: user._id
- }, {
- sort: {
- _id: -1
- }
- });
-
- isQuote = text != null || files != null;
-
- // 直近と同じRenote対象かつ引用じゃなかったらエラー
- if (latestNote &&
- latestNote.renoteId &&
- latestNote.renoteId.equals(renote._id) &&
- !isQuote) {
- return rej('cannot renote same note that already reposted in your latest note');
- }
-
- // 直近がRenote対象かつ引用じゃなかったらエラー
- if (latestNote &&
- latestNote._id.equals(renote._id) &&
- !isQuote) {
- return rej('cannot renote your latest note');
- }
- }
-
- // Get 'replyId' parameter
- const [replyId, replyIdErr] = $(params.replyId).optional.id().$;
- if (replyIdErr) return rej('invalid replyId');
-
- let reply: INote = null;
- if (replyId !== undefined) {
- // Fetch reply
- reply = await Note.findOne({
- _id: replyId
- });
-
- if (reply === null) {
- return rej('in reply to note is not found');
- }
-
- // 返信対象が引用でないRenoteだったらエラー
- if (reply.renoteId && !reply.text && !reply.mediaIds) {
- return rej('cannot reply to renote');
- }
- }
-
- // Get 'channelId' parameter
- const [channelId, channelIdErr] = $(params.channelId).optional.id().$;
- if (channelIdErr) return rej('invalid channelId');
-
- let channel: IChannel = null;
- if (channelId !== undefined) {
- // Fetch channel
- channel = await Channel.findOne({
- _id: channelId
- });
-
- if (channel === null) {
- return rej('channel not found');
- }
-
- // 返信対象の投稿がこのチャンネルじゃなかったらダメ
- if (reply && !channelId.equals(reply.channelId)) {
- return rej('チャンネル内部からチャンネル外部の投稿に返信することはできません');
- }
-
- // Renote対象の投稿がこのチャンネルじゃなかったらダメ
- if (renote && !channelId.equals(renote.channelId)) {
- return rej('チャンネル内部からチャンネル外部の投稿をRenoteすることはできません');
- }
-
- // 引用ではないRenoteはダメ
- if (renote && !isQuote) {
- return rej('チャンネル内部では引用ではないRenoteをすることはできません');
- }
- } else {
- // 返信対象の投稿がチャンネルへの投稿だったらダメ
- if (reply && reply.channelId != null) {
- return rej('チャンネル外部からチャンネル内部の投稿に返信することはできません');
- }
-
- // Renote対象の投稿がチャンネルへの投稿だったらダメ
- if (renote && renote.channelId != null) {
- return rej('チャンネル外部からチャンネル内部の投稿をRenoteすることはできません');
- }
- }
-
- // Get 'poll' parameter
- const [poll, pollErr] = $(params.poll).optional.strict.object()
- .have('choices', $().array('string')
- .unique()
- .range(2, 10)
- .each(c => c.length > 0 && c.length < 50))
- .$;
- if (pollErr) return rej('invalid poll');
-
- if (poll) {
- (poll as any).choices = (poll as any).choices.map((choice, i) => ({
- id: i, // IDを付与
- text: choice.trim(),
- votes: 0
- }));
- }
-
- // テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー
- if (text === undefined && files === null && renote === null && poll === undefined) {
- return rej('text, mediaIds, renoteId or poll is required');
- }
-
- // 直近の投稿と重複してたらエラー
- // TODO: 直近の投稿が一日前くらいなら重複とは見なさない
- if (user.latestNote) {
- if (deepEqual({
- text: user.latestNote.text,
- reply: user.latestNote.replyId ? user.latestNote.replyId.toString() : null,
- renote: user.latestNote.renoteId ? user.latestNote.renoteId.toString() : null,
- mediaIds: (user.latestNote.mediaIds || []).map(id => id.toString())
- }, {
- text: text,
- reply: reply ? reply._id.toString() : null,
- renote: renote ? renote._id.toString() : null,
- mediaIds: (files || []).map(file => file._id.toString())
- })) {
- return rej('duplicate');
- }
- }
-
- // 投稿を作成
- const note = await create(user, {
- createdAt: new Date(),
- media: files,
- poll: poll,
- text: text,
- reply,
- renote,
- cw: cw,
- tags: tags,
- app: app,
- viaMobile: viaMobile,
- visibility,
- geo
- });
-
- const noteObj = await pack(note, user);
-
- // Reponse
- res({
- createdNote: noteObj
- });
-});