diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index 805dba7f83..42a55f850e 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -4,9 +4,9 @@ import $ from 'cafy'; import deepEqual = require('deep-equal'); import parse from '../../common/text'; -import Post from '../../models/post'; -import { isValidText } from '../../models/post'; +import { default as Post, IPost, isValidText } from '../../models/post'; import { default as User, IUser } from '../../models/user'; +import { default as Channel, IChannel } from '../../models/channel'; import Following from '../../models/following'; import DriveFile from '../../models/drive-file'; import Watching from '../../models/post-watching'; @@ -62,7 +62,8 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { const [repostId, repostIdErr] = $(params.repost_id).optional.id().$; if (repostIdErr) return rej('invalid repost_id'); - let repost = null; + let repost: IPost = null; + let isQuote = false; if (repostId !== undefined) { // Fetch repost to post repost = await Post.findOne({ @@ -84,18 +85,20 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { } }); + isQuote = text != null || files != null; + // 直近と同じRepost対象かつ引用じゃなかったらエラー if (latestPost && latestPost.repost_id && latestPost.repost_id.equals(repost._id) && - text === undefined && files === null) { + !isQuote) { return rej('cannot repost same post that already reposted in your latest post'); } // 直近がRepost対象かつ引用じゃなかったらエラー if (latestPost && latestPost._id.equals(repost._id) && - text === undefined && files === null) { + !isQuote) { return rej('cannot repost your latest post'); } } @@ -104,7 +107,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { const [inReplyToPostId, inReplyToPostIdErr] = $(params.reply_to_id).optional.id().$; if (inReplyToPostIdErr) return rej('invalid in_reply_to_post_id'); - let inReplyToPost = null; + let inReplyToPost: IPost = null; if (inReplyToPostId !== undefined) { // Fetch reply inReplyToPost = await Post.findOne({ @@ -121,6 +124,37 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { } } + // Get 'channel_id' parameter + const [channelId, channelIdErr] = $(params.channel_id).optional.id().$; + if (channelIdErr) return rej('invalid channel_id'); + + let channel: IChannel = null; + if (channelId !== undefined) { + // Fetch channel + channel = await Channel.findOne({ + _id: channelId + }); + + if (channel === null) { + return rej('channel not found'); + } + + // 返信対象の投稿がこのチャンネルじゃなかったらダメ + if (inReplyToPost && !channelId.equals(inReplyToPost.channel_id)) { + return rej('チャンネル内部からチャンネル外部の投稿に返信することはできません'); + } + + // Repost対象の投稿がこのチャンネルじゃなかったらダメ + if (repost && !channelId.equals(repost.channel_id)) { + return rej('チャンネル内部からチャンネル外部の投稿をRepostすることはできません'); + } + + // 引用ではないRepostはダメ + if (repost && !isQuote) { + return rej('チャンネル内部では引用ではないRepostをすることはできません'); + } + } + // Get 'poll' parameter const [poll, pollErr] = $(params.poll).optional.strict.object() .have('choices', $().array('string') @@ -164,6 +198,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { // 投稿を作成 const post = await Post.insert({ created_at: new Date(), + channel_id: channel ? channel._id : undefined, media_ids: files ? files.map(file => file._id) : undefined, reply_to_id: inReplyToPost ? inReplyToPost._id : undefined, repost_id: repost ? repost._id : undefined, diff --git a/src/api/models/post.ts b/src/api/models/post.ts index 8b9f7f5ef6..fe07dcb0b1 100644 --- a/src/api/models/post.ts +++ b/src/api/models/post.ts @@ -10,6 +10,7 @@ export function isValidText(text: string): boolean { export type IPost = { _id: mongo.ObjectID; + channel_id: mongo.ObjectID; created_at: Date; media_ids: mongo.ObjectID[]; reply_to_id: mongo.ObjectID;