summaryrefslogtreecommitdiff
path: root/src/server/api/endpoints/notes
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2018-10-08 15:37:24 +0900
committerGitHub <noreply@github.com>2018-10-08 15:37:24 +0900
commit9c170c426be01773afb15a9868ff3c278e09409c (patch)
tree0229bb52dd9197308d193f4e41bbc11d3dcb95a1 /src/server/api/endpoints/notes
parentNew translations ja-JP.yml (Norwegian) (diff)
parentfix(package): update @types/mongodb to version 3.1.10 (#2849) (diff)
downloadmisskey-9c170c426be01773afb15a9868ff3c278e09409c.tar.gz
misskey-9c170c426be01773afb15a9868ff3c278e09409c.tar.bz2
misskey-9c170c426be01773afb15a9868ff3c278e09409c.zip
Merge branch 'develop' into l10n_develop
Diffstat (limited to 'src/server/api/endpoints/notes')
-rw-r--r--src/server/api/endpoints/notes/conversation.ts4
-rw-r--r--src/server/api/endpoints/notes/create.ts40
-rw-r--r--src/server/api/endpoints/notes/delete.ts11
-rw-r--r--src/server/api/endpoints/notes/favorites/create.ts18
-rw-r--r--src/server/api/endpoints/notes/global-timeline.ts86
-rw-r--r--src/server/api/endpoints/notes/hybrid-timeline.ts30
-rw-r--r--src/server/api/endpoints/notes/local-timeline.ts113
-rw-r--r--src/server/api/endpoints/notes/mentions.ts71
-rw-r--r--src/server/api/endpoints/notes/polls/vote.ts5
-rw-r--r--src/server/api/endpoints/notes/reactions/create.ts4
-rw-r--r--src/server/api/endpoints/notes/replies.ts4
-rw-r--r--src/server/api/endpoints/notes/reposts.ts5
-rw-r--r--src/server/api/endpoints/notes/search.ts4
-rw-r--r--src/server/api/endpoints/notes/search_by_tag.ts250
-rw-r--r--src/server/api/endpoints/notes/timeline.ts27
-rw-r--r--src/server/api/endpoints/notes/trend.ts2
-rw-r--r--src/server/api/endpoints/notes/user-list-timeline.ts24
17 files changed, 421 insertions, 277 deletions
diff --git a/src/server/api/endpoints/notes/conversation.ts b/src/server/api/endpoints/notes/conversation.ts
index 2782d14155..0c23f9e5fc 100644
--- a/src/server/api/endpoints/notes/conversation.ts
+++ b/src/server/api/endpoints/notes/conversation.ts
@@ -1,5 +1,5 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
-import Note, { pack, INote } from '../../../../models/note';
+import Note, { packMany, INote } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
/**
@@ -52,5 +52,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
}
// Serialize
- res(await Promise.all(conversation.map(note => pack(note, user))));
+ res(await packMany(conversation, user));
});
diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts
index 04f5f7562e..96745132a3 100644
--- a/src/server/api/endpoints/notes/create.ts
+++ b/src/server/api/endpoints/notes/create.ts
@@ -71,9 +71,15 @@ export const meta = {
ref: 'geo'
}),
+ fileIds: $.arr($.type(ID)).optional.unique().range(1, 4).note({
+ desc: {
+ 'ja-JP': '添付するファイル'
+ }
+ }),
+
mediaIds: $.arr($.type(ID)).optional.unique().range(1, 4).note({
desc: {
- 'ja-JP': '添付するメディア'
+ 'ja-JP': '添付するファイル (このパラメータは廃止予定です。代わりに fileIds を使ってください。)'
}
}),
@@ -124,26 +130,16 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
}
let files: IDriveFile[] = [];
- if (ps.mediaIds !== undefined) {
- // Fetch files
- // forEach だと途中でエラーなどがあっても return できないので
- // 敢えて for を使っています。
- for (const mediaId of ps.mediaIds) {
- // Fetch file
- // SELECT _id
- const entity = await DriveFile.findOne({
- _id: mediaId,
+ const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null;
+ if (fileIds != null) {
+ files = await Promise.all(fileIds.map(fileId => {
+ return DriveFile.findOne({
+ _id: fileId,
'metadata.userId': user._id
});
+ }));
- if (entity === null) {
- return rej('file not found');
- } else {
- files.push(entity);
- }
- }
- } else {
- files = null;
+ files = files.filter(file => file != null);
}
let renote: INote = null;
@@ -155,7 +151,7 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
if (renote == null) {
return rej('renoteee is not found');
- } else if (renote.renoteId && !renote.text && !renote.mediaIds) {
+ } else if (renote.renoteId && !renote.text && !renote.fileIds) {
return rej('cannot renote to renote');
}
}
@@ -176,7 +172,7 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
}
// 返信対象が引用でないRenoteだったらエラー
- if (reply.renoteId && !reply.text && !reply.mediaIds) {
+ if (reply.renoteId && !reply.text && !reply.fileIds) {
return rej('cannot reply to renote');
}
}
@@ -191,13 +187,13 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
// テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー
if ((ps.text === undefined || ps.text === null) && files === null && renote === null && ps.poll === undefined) {
- return rej('text, mediaIds, renoteId or poll is required');
+ return rej('text, fileIds, renoteId or poll is required');
}
// 投稿を作成
const note = await create(user, {
createdAt: new Date(),
- media: files,
+ files: files,
poll: ps.poll,
text: ps.text,
reply,
diff --git a/src/server/api/endpoints/notes/delete.ts b/src/server/api/endpoints/notes/delete.ts
index 6d9826cf7b..2fe36897c0 100644
--- a/src/server/api/endpoints/notes/delete.ts
+++ b/src/server/api/endpoints/notes/delete.ts
@@ -1,7 +1,7 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
import Note from '../../../../models/note';
import deleteNote from '../../../../services/note/delete';
-import { ILocalUser } from '../../../../models/user';
+import User, { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
@@ -21,15 +21,18 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
// Fetch note
const note = await Note.findOne({
- _id: noteId,
- userId: user._id
+ _id: noteId
});
if (note === null) {
return rej('note not found');
}
- await deleteNote(user, note);
+ if (!user.isAdmin && !note.userId.equals(user._id)) {
+ return rej('access denied');
+ }
+
+ await deleteNote(await User.findOne({ _id: note.userId }), note);
res();
});
diff --git a/src/server/api/endpoints/notes/favorites/create.ts b/src/server/api/endpoints/notes/favorites/create.ts
index daf7780abc..9aefb701ae 100644
--- a/src/server/api/endpoints/notes/favorites/create.ts
+++ b/src/server/api/endpoints/notes/favorites/create.ts
@@ -2,6 +2,7 @@ import $ from 'cafy'; import ID from '../../../../../misc/cafy-id';
import Favorite from '../../../../../models/favorite';
import Note from '../../../../../models/note';
import { ILocalUser } from '../../../../../models/user';
+import getParams from '../../../get-params';
export const meta = {
desc: {
@@ -11,17 +12,24 @@ export const meta = {
requireCredential: true,
- kind: 'favorite-write'
+ kind: 'favorite-write',
+
+ params: {
+ noteId: $.type(ID).note({
+ desc: {
+ 'ja-JP': '対象の投稿のID'
+ }
+ })
+ }
};
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
- // Get 'noteId' parameter
- const [noteId, noteIdErr] = $.type(ID).get(params.noteId);
- if (noteIdErr) return rej('invalid noteId param');
+ const [ps, psErr] = getParams(meta, params);
+ if (psErr) return rej(psErr);
// Get favoritee
const note = await Note.findOne({
- _id: noteId
+ _id: ps.noteId
});
if (note === null) {
diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts
index 8f7233e308..8362143bb2 100644
--- a/src/server/api/endpoints/notes/global-timeline.ts
+++ b/src/server/api/endpoints/notes/global-timeline.ts
@@ -1,42 +1,52 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
import Note from '../../../../models/note';
import Mute from '../../../../models/mute';
-import { pack } from '../../../../models/note';
+import { packMany } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
+import getParams from '../../get-params';
+import { countIf } from '../../../../prelude/array';
-/**
- * Get timeline of global
- */
-export default async (params: any, user: ILocalUser) => {
- // Get 'limit' parameter
- const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
- if (limitErr) throw 'invalid limit param';
+export const meta = {
+ desc: {
+ 'ja-JP': 'グローバルタイムラインを取得します。'
+ },
+
+ params: {
+ withFiles: $.bool.optional.note({
+ desc: {
+ 'ja-JP': 'ファイルが添付された投稿に限定するか否か'
+ }
+ }),
+
+ mediaOnly: $.bool.optional.note({
+ desc: {
+ 'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
+ }
+ }),
+
+ limit: $.num.optional.range(1, 100).note({
+ default: 10
+ }),
+
+ sinceId: $.type(ID).optional.note({}),
- // Get 'sinceId' parameter
- const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
- if (sinceIdErr) throw 'invalid sinceId param';
+ untilId: $.type(ID).optional.note({}),
- // Get 'untilId' parameter
- const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
- if (untilIdErr) throw 'invalid untilId param';
+ sinceDate: $.num.optional.note({}),
- // Get 'sinceDate' parameter
- const [sinceDate, sinceDateErr] = $.num.optional.get(params.sinceDate);
- if (sinceDateErr) throw 'invalid sinceDate param';
+ untilDate: $.num.optional.note({}),
+ }
+};
- // Get 'untilDate' parameter
- const [untilDate, untilDateErr] = $.num.optional.get(params.untilDate);
- if (untilDateErr) throw 'invalid untilDate param';
+export default async (params: any, user: ILocalUser) => {
+ const [ps, psErr] = getParams(meta, params);
+ if (psErr) throw psErr;
// Check if only one of sinceId, untilId, sinceDate, untilDate specified
- if ([sinceId, untilId, sinceDate, untilDate].filter(x => x != null).length > 1) {
+ if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) {
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
}
- // Get 'mediaOnly' parameter
- const [mediaOnly, mediaOnlyErr] = $.bool.optional.get(params.mediaOnly);
- if (mediaOnlyErr) throw 'invalid mediaOnly param';
-
// ミュートしているユーザーを取得
const mutedUserIds = user ? (await Mute.find({
muterId: user._id
@@ -68,27 +78,29 @@ export default async (params: any, user: ILocalUser) => {
};
}
- if (mediaOnly) {
- query.mediaIds = { $exists: true, $ne: [] };
+ const withFiles = ps.withFiles != null ? ps.withFiles : ps.mediaOnly;
+
+ if (withFiles) {
+ query.fileIds = { $exists: true, $ne: [] };
}
- if (sinceId) {
+ if (ps.sinceId) {
sort._id = 1;
query._id = {
- $gt: sinceId
+ $gt: ps.sinceId
};
- } else if (untilId) {
+ } else if (ps.untilId) {
query._id = {
- $lt: untilId
+ $lt: ps.untilId
};
- } else if (sinceDate) {
+ } else if (ps.sinceDate) {
sort._id = 1;
query.createdAt = {
- $gt: new Date(sinceDate)
+ $gt: new Date(ps.sinceDate)
};
- } else if (untilDate) {
+ } else if (ps.untilDate) {
query.createdAt = {
- $lt: new Date(untilDate)
+ $lt: new Date(ps.untilDate)
};
}
//#endregion
@@ -96,10 +108,10 @@ export default async (params: any, user: ILocalUser) => {
// Issue query
const timeline = await Note
.find(query, {
- limit: limit,
+ limit: ps.limit,
sort: sort
});
// Serialize
- return await Promise.all(timeline.map(note => pack(note, user)));
+ return await packMany(timeline, user);
};
diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts
index 2dbb1190c1..14b4432b33 100644
--- a/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -2,13 +2,12 @@ import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
import Note from '../../../../models/note';
import Mute from '../../../../models/mute';
import { getFriends } from '../../common/get-friends';
-import { pack } from '../../../../models/note';
+import { packMany } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
+import { countIf } from '../../../../prelude/array';
export const meta = {
- name: 'notes/hybrid-timeline',
-
desc: {
'ja-JP': 'ハイブリッドタイムラインを取得します。'
},
@@ -66,23 +65,26 @@ export const meta = {
}
}),
+ withFiles: $.bool.optional.note({
+ desc: {
+ 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
+ }
+ }),
+
mediaOnly: $.bool.optional.note({
desc: {
- 'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します'
+ 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
}
}),
}
};
-/**
- * Get hybrid timeline of myself
- */
export default async (params: any, user: ILocalUser) => {
const [ps, psErr] = getParams(meta, params);
if (psErr) throw psErr;
// Check if only one of sinceId, untilId, sinceDate, untilDate specified
- if ([ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate].filter(x => x != null).length > 1) {
+ if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) {
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
}
@@ -164,7 +166,7 @@ export default async (params: any, user: ILocalUser) => {
}, {
text: { $ne: null }
}, {
- mediaIds: { $ne: [] }
+ fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
@@ -180,7 +182,7 @@ export default async (params: any, user: ILocalUser) => {
}, {
text: { $ne: null }
}, {
- mediaIds: { $ne: [] }
+ fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
@@ -196,16 +198,16 @@ export default async (params: any, user: ILocalUser) => {
}, {
text: { $ne: null }
}, {
- mediaIds: { $ne: [] }
+ fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
}
- if (ps.mediaOnly) {
+ if (ps.withFiles || ps.mediaOnly) {
query.$and.push({
- mediaIds: { $exists: true, $ne: [] }
+ fileIds: { $exists: true, $ne: [] }
});
}
@@ -238,5 +240,5 @@ export default async (params: any, user: ILocalUser) => {
});
// Serialize
- return await Promise.all(timeline.map(note => pack(note, user)));
+ return await packMany(timeline, user);
};
diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts
index bbcc6303ca..8ab07d8ea7 100644
--- a/src/server/api/endpoints/notes/local-timeline.ts
+++ b/src/server/api/endpoints/notes/local-timeline.ts
@@ -1,42 +1,65 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
import Note from '../../../../models/note';
import Mute from '../../../../models/mute';
-import { pack } from '../../../../models/note';
+import { packMany } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
+import getParams from '../../get-params';
+import { countIf } from '../../../../prelude/array';
-/**
- * Get timeline of local
- */
-export default async (params: any, user: ILocalUser) => {
- // Get 'limit' parameter
- const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
- if (limitErr) throw 'invalid limit param';
+export const meta = {
+ desc: {
+ 'ja-JP': 'ローカルタイムラインを取得します。'
+ },
+
+ params: {
+ withFiles: $.bool.optional.note({
+ desc: {
+ 'ja-JP': 'ファイルが添付された投稿に限定するか否か'
+ }
+ }),
+
+ mediaOnly: $.bool.optional.note({
+ desc: {
+ 'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
+ }
+ }),
+
+ fileType: $.arr($.str).optional.note({
+ desc: {
+ 'ja-JP': '指定された種類のファイルが添付された投稿のみを取得します'
+ }
+ }),
+
+ excludeNsfw: $.bool.optional.note({
+ default: false,
+ desc: {
+ 'ja-JP': 'true にすると、NSFW指定されたファイルを除外します(fileTypeが指定されている場合のみ有効)'
+ }
+ }),
- // Get 'sinceId' parameter
- const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
- if (sinceIdErr) throw 'invalid sinceId param';
+ limit: $.num.optional.range(1, 100).note({
+ default: 10
+ }),
- // Get 'untilId' parameter
- const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
- if (untilIdErr) throw 'invalid untilId param';
+ sinceId: $.type(ID).optional.note({}),
- // Get 'sinceDate' parameter
- const [sinceDate, sinceDateErr] = $.num.optional.get(params.sinceDate);
- if (sinceDateErr) throw 'invalid sinceDate param';
+ untilId: $.type(ID).optional.note({}),
- // Get 'untilDate' parameter
- const [untilDate, untilDateErr] = $.num.optional.get(params.untilDate);
- if (untilDateErr) throw 'invalid untilDate param';
+ sinceDate: $.num.optional.note({}),
+
+ untilDate: $.num.optional.note({}),
+ }
+};
+
+export default async (params: any, user: ILocalUser) => {
+ const [ps, psErr] = getParams(meta, params);
+ if (psErr) throw psErr;
// Check if only one of sinceId, untilId, sinceDate, untilDate specified
- if ([sinceId, untilId, sinceDate, untilDate].filter(x => x != null).length > 1) {
+ if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) {
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
}
- // Get 'mediaOnly' parameter
- const [mediaOnly, mediaOnlyErr] = $.bool.optional.get(params.mediaOnly);
- if (mediaOnlyErr) throw 'invalid mediaOnly param';
-
// ミュートしているユーザーを取得
const mutedUserIds = user ? (await Mute.find({
muterId: user._id
@@ -69,27 +92,43 @@ export default async (params: any, user: ILocalUser) => {
};
}
- if (mediaOnly) {
- query.mediaIds = { $exists: true, $ne: [] };
+ const withFiles = ps.withFiles != null ? ps.withFiles : ps.mediaOnly;
+
+ if (withFiles) {
+ query.fileIds = { $exists: true, $ne: [] };
+ }
+
+ if (ps.fileType) {
+ query.fileIds = { $exists: true, $ne: [] };
+
+ query['_files.contentType'] = {
+ $in: ps.fileType
+ };
+
+ if (ps.excludeNsfw) {
+ query['_files.metadata.isSensitive'] = {
+ $ne: true
+ };
+ }
}
- if (sinceId) {
+ if (ps.sinceId) {
sort._id = 1;
query._id = {
- $gt: sinceId
+ $gt: ps.sinceId
};
- } else if (untilId) {
+ } else if (ps.untilId) {
query._id = {
- $lt: untilId
+ $lt: ps.untilId
};
- } else if (sinceDate) {
+ } else if (ps.sinceDate) {
sort._id = 1;
query.createdAt = {
- $gt: new Date(sinceDate)
+ $gt: new Date(ps.sinceDate)
};
- } else if (untilDate) {
+ } else if (ps.untilDate) {
query.createdAt = {
- $lt: new Date(untilDate)
+ $lt: new Date(ps.untilDate)
};
}
//#endregion
@@ -97,10 +136,10 @@ export default async (params: any, user: ILocalUser) => {
// Issue query
const timeline = await Note
.find(query, {
- limit: limit,
+ limit: ps.limit,
sort: sort
});
// Serialize
- return await Promise.all(timeline.map(note => pack(note, user)));
+ return await packMany(timeline, user);
};
diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts
index a7fb14d8a9..592a94263d 100644
--- a/src/server/api/endpoints/notes/mentions.ts
+++ b/src/server/api/endpoints/notes/mentions.ts
@@ -1,8 +1,10 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
import Note from '../../../../models/note';
import { getFriendIds } from '../../common/get-friends';
-import { pack } from '../../../../models/note';
+import { packMany } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
+import getParams from '../../get-params';
+import read from '../../../../services/note/read';
export const meta = {
desc: {
@@ -10,42 +12,55 @@ export const meta = {
'en-US': 'Get mentions of myself.'
},
- requireCredential: true
-};
+ requireCredential: true,
-export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
- // Get 'following' parameter
- const [following = false, followingError] =
- $.bool.optional.get(params.following);
- if (followingError) return rej('invalid following param');
+ params: {
+ following: $.bool.optional.note({
+ default: false
+ }),
+
+ limit: $.num.optional.range(1, 100).note({
+ default: 10
+ }),
- // Get 'limit' parameter
- const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
- if (limitErr) return rej('invalid limit param');
+ sinceId: $.type(ID).optional.note({
+ }),
- // Get 'sinceId' parameter
- const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
- if (sinceIdErr) return rej('invalid sinceId param');
+ untilId: $.type(ID).optional.note({
+ }),
+
+ visibility: $.str.optional.note({
+ }),
+ }
+};
- // Get 'untilId' parameter
- const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
- if (untilIdErr) return rej('invalid untilId param');
+export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
+ const [ps, psErr] = getParams(meta, params);
+ if (psErr) throw psErr;
// Check if both of sinceId and untilId is specified
- if (sinceId && untilId) {
+ if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId');
}
// Construct query
const query = {
- mentions: user._id
+ $or: [{
+ mentions: user._id
+ }, {
+ visibleUserIds: user._id
+ }]
} as any;
const sort = {
_id: -1
};
- if (following) {
+ if (ps.visibility) {
+ query.visibility = ps.visibility;
+ }
+
+ if (ps.following) {
const followingIds = await getFriendIds(user._id);
query.userId = {
@@ -53,26 +68,26 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
};
}
- if (sinceId) {
+ if (ps.sinceId) {
sort._id = 1;
query._id = {
- $gt: sinceId
+ $gt: ps.sinceId
};
- } else if (untilId) {
+ } else if (ps.untilId) {
query._id = {
- $lt: untilId
+ $lt: ps.untilId
};
}
// Issue query
const mentions = await Note
.find(query, {
- limit: limit,
+ limit: ps.limit,
sort: sort
});
+ mentions.forEach(note => read(user._id, note._id));
+
// Serialize
- res(await Promise.all(mentions.map(async mention =>
- await pack(mention, user)
- )));
+ res(await packMany(mentions, user));
});
diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts
index ab80e7f5d0..3b78d62fd3 100644
--- a/src/server/api/endpoints/notes/polls/vote.ts
+++ b/src/server/api/endpoints/notes/polls/vote.ts
@@ -72,7 +72,10 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
$inc: inc
});
- publishNoteStream(note._id, 'poll_voted');
+ publishNoteStream(note._id, 'pollVoted', {
+ choice: choice,
+ userId: user._id.toHexString()
+ });
// Notify
notify(note.userId, user._id, 'poll_vote', {
diff --git a/src/server/api/endpoints/notes/reactions/create.ts b/src/server/api/endpoints/notes/reactions/create.ts
index 0781db16c5..ec68f065d8 100644
--- a/src/server/api/endpoints/notes/reactions/create.ts
+++ b/src/server/api/endpoints/notes/reactions/create.ts
@@ -43,6 +43,10 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
return rej('note not found');
}
+ if (note.deletedAt != null) {
+ return rej('this not is already deleted');
+ }
+
try {
await create(user, note, ps.reaction);
} catch (e) {
diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts
index 44c80afc4a..b2f8f94f69 100644
--- a/src/server/api/endpoints/notes/replies.ts
+++ b/src/server/api/endpoints/notes/replies.ts
@@ -1,5 +1,5 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
-import Note, { pack } from '../../../../models/note';
+import Note, { packMany } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
/**
@@ -30,5 +30,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
const ids = (note._replyIds || []).slice(offset, offset + limit);
// Serialize
- res(await Promise.all(ids.map(id => pack(id, user))));
+ res(await packMany(ids, user));
});
diff --git a/src/server/api/endpoints/notes/reposts.ts b/src/server/api/endpoints/notes/reposts.ts
index 05e68302ba..2c6e1a499f 100644
--- a/src/server/api/endpoints/notes/reposts.ts
+++ b/src/server/api/endpoints/notes/reposts.ts
@@ -1,5 +1,5 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
-import Note, { pack } from '../../../../models/note';
+import Note, { packMany } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
/**
@@ -62,6 +62,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
});
// Serialize
- res(await Promise.all(renotes.map(async note =>
- await pack(note, user))));
+ res(await packMany(renotes, user));
});
diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts
index 9124899ad8..2755a70483 100644
--- a/src/server/api/endpoints/notes/search.ts
+++ b/src/server/api/endpoints/notes/search.ts
@@ -2,7 +2,7 @@ import $ from 'cafy';
import * as mongo from 'mongodb';
import Note from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
-import { pack } from '../../../../models/note';
+import { packMany } from '../../../../models/note';
import es from '../../../../db/elasticsearch';
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
@@ -60,6 +60,6 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
}
});
- res(await Promise.all(notes.map(note => pack(note, me))));
+ res(await packMany(notes, me));
});
});
diff --git a/src/server/api/endpoints/notes/search_by_tag.ts b/src/server/api/endpoints/notes/search_by_tag.ts
index e092275fe8..d380f27f9c 100644
--- a/src/server/api/endpoints/notes/search_by_tag.ts
+++ b/src/server/api/endpoints/notes/search_by_tag.ts
@@ -3,120 +3,171 @@ import Note from '../../../../models/note';
import User, { ILocalUser } from '../../../../models/user';
import Mute from '../../../../models/mute';
import { getFriendIds } from '../../common/get-friends';
-import { pack } from '../../../../models/note';
+import { packMany } from '../../../../models/note';
+import getParams from '../../get-params';
+import { erase } from '../../../../prelude/array';
-/**
- * Search notes by tag
- */
-export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
- // Get 'tag' parameter
- const [tag, tagError] = $.str.get(params.tag);
- if (tagError) return rej('invalid tag param');
+export const meta = {
+ desc: {
+ 'ja-JP': '指定されたタグが付けられた投稿を取得します。'
+ },
- // Get 'includeUserIds' parameter
- const [includeUserIds = [], includeUserIdsErr] = $.arr($.type(ID)).optional.get(params.includeUserIds);
- if (includeUserIdsErr) return rej('invalid includeUserIds param');
+ params: {
+ tag: $.str.optional.note({
+ desc: {
+ 'ja-JP': 'タグ'
+ }
+ }),
- // Get 'excludeUserIds' parameter
- const [excludeUserIds = [], excludeUserIdsErr] = $.arr($.type(ID)).optional.get(params.excludeUserIds);
- if (excludeUserIdsErr) return rej('invalid excludeUserIds param');
+ query: $.arr($.arr($.str)).optional.note({
+ desc: {
+ 'ja-JP': 'クエリ'
+ }
+ }),
- // Get 'includeUserUsernames' parameter
- const [includeUserUsernames = [], includeUserUsernamesErr] = $.arr($.str).optional.get(params.includeUserUsernames);
- if (includeUserUsernamesErr) return rej('invalid includeUserUsernames param');
+ includeUserIds: $.arr($.type(ID)).optional.note({
+ default: []
+ }),
- // Get 'excludeUserUsernames' parameter
- const [excludeUserUsernames = [], excludeUserUsernamesErr] = $.arr($.str).optional.get(params.excludeUserUsernames);
- if (excludeUserUsernamesErr) return rej('invalid excludeUserUsernames param');
+ excludeUserIds: $.arr($.type(ID)).optional.note({
+ default: []
+ }),
- // Get 'following' parameter
- const [following = null, followingErr] = $.bool.optional.nullable.get(params.following);
- if (followingErr) return rej('invalid following param');
+ includeUserUsernames: $.arr($.str).optional.note({
+ default: []
+ }),
- // Get 'mute' parameter
- const [mute = 'mute_all', muteErr] = $.str.optional.get(params.mute);
- if (muteErr) return rej('invalid mute param');
+ excludeUserUsernames: $.arr($.str).optional.note({
+ default: []
+ }),
- // Get 'reply' parameter
- const [reply = null, replyErr] = $.bool.optional.nullable.get(params.reply);
- if (replyErr) return rej('invalid reply param');
+ following: $.bool.optional.nullable.note({
+ default: null
+ }),
- // Get 'renote' parameter
- const [renote = null, renoteErr] = $.bool.optional.nullable.get(params.renote);
- if (renoteErr) return rej('invalid renote param');
+ mute: $.str.optional.note({
+ default: 'mute_all'
+ }),
- // Get 'media' parameter
- const [media = null, mediaErr] = $.bool.optional.nullable.get(params.media);
- if (mediaErr) return rej('invalid media param');
+ reply: $.bool.optional.nullable.note({
+ default: null,
- // Get 'poll' parameter
- const [poll = null, pollErr] = $.bool.optional.nullable.get(params.poll);
- if (pollErr) return rej('invalid poll param');
+ desc: {
+ 'ja-JP': '返信に限定するか否か'
+ }
+ }),
- // Get 'sinceDate' parameter
- const [sinceDate, sinceDateErr] = $.num.optional.get(params.sinceDate);
- if (sinceDateErr) throw 'invalid sinceDate param';
+ renote: $.bool.optional.nullable.note({
+ default: null,
- // Get 'untilDate' parameter
- const [untilDate, untilDateErr] = $.num.optional.get(params.untilDate);
- if (untilDateErr) throw 'invalid untilDate param';
+ desc: {
+ 'ja-JP': 'Renoteに限定するか否か'
+ }
+ }),
- // Get 'offset' parameter
- const [offset = 0, offsetErr] = $.num.optional.min(0).get(params.offset);
- if (offsetErr) return rej('invalid offset param');
+ withFiles: $.bool.optional.note({
+ desc: {
+ 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
+ }
+ }),
- // Get 'limit' parameter
- const [limit = 10, limitErr] = $.num.optional.range(1, 30).get(params.limit);
- if (limitErr) return rej('invalid limit param');
+ media: $.bool.optional.nullable.note({
+ default: null,
+
+ desc: {
+ 'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
+ }
+ }),
+
+ poll: $.bool.optional.nullable.note({
+ default: null,
+
+ desc: {
+ 'ja-JP': 'アンケートが添付された投稿に限定するか否か'
+ }
+ }),
- if (includeUserUsernames != null) {
- const ids = (await Promise.all(includeUserUsernames.map(async (username) => {
+ untilId: $.type(ID).optional.note({
+ desc: {
+ 'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
+ }
+ }),
+
+ sinceDate: $.num.optional.note({
+ }),
+
+ untilDate: $.num.optional.note({
+ }),
+
+ offset: $.num.optional.min(0).note({
+ default: 0
+ }),
+
+ limit: $.num.optional.range(1, 30).note({
+ default: 10
+ }),
+ }
+};
+
+export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
+ const [ps, psErr] = getParams(meta, params);
+ if (psErr) throw psErr;
+
+ if (ps.includeUserUsernames != null) {
+ const ids = erase(null, await Promise.all(ps.includeUserUsernames.map(async (username) => {
const _user = await User.findOne({
usernameLower: username.toLowerCase()
});
return _user ? _user._id : null;
- }))).filter(id => id != null);
+ })));
- ids.forEach(id => includeUserIds.push(id));
+ ids.forEach(id => ps.includeUserIds.push(id));
}
- if (excludeUserUsernames != null) {
- const ids = (await Promise.all(excludeUserUsernames.map(async (username) => {
+ if (ps.excludeUserUsernames != null) {
+ const ids = erase(null, await Promise.all(ps.excludeUserUsernames.map(async (username) => {
const _user = await User.findOne({
usernameLower: username.toLowerCase()
});
return _user ? _user._id : null;
- }))).filter(id => id != null);
+ })));
- ids.forEach(id => excludeUserIds.push(id));
+ ids.forEach(id => ps.excludeUserIds.push(id));
}
- let q: any = {
- $and: [{
- tagsLower: tag.toLowerCase()
- }]
+ const q: any = {
+ $and: [ps.tag ? {
+ tagsLower: ps.tag.toLowerCase()
+ } : {
+ $or: ps.query.map(tags => ({
+ $and: tags.map(t => ({
+ tagsLower: t.toLowerCase()
+ }))
+ }))
+ }],
+ deletedAt: { $exists: false }
};
const push = (x: any) => q.$and.push(x);
- if (includeUserIds && includeUserIds.length != 0) {
+ if (ps.includeUserIds && ps.includeUserIds.length != 0) {
push({
userId: {
- $in: includeUserIds
+ $in: ps.includeUserIds
}
});
- } else if (excludeUserIds && excludeUserIds.length != 0) {
+ } else if (ps.excludeUserIds && ps.excludeUserIds.length != 0) {
push({
userId: {
- $nin: excludeUserIds
+ $nin: ps.excludeUserIds
}
});
}
- if (following != null && me != null) {
+ if (ps.following != null && me != null) {
const ids = await getFriendIds(me._id, false);
push({
- userId: following ? {
+ userId: ps.following ? {
$in: ids
} : {
$nin: ids.concat(me._id)
@@ -131,7 +182,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
});
const mutedUserIds = mutes.map(m => m.muteeId);
- switch (mute) {
+ switch (ps.mute) {
case 'mute_all':
push({
userId: {
@@ -202,8 +253,8 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
}
}
- if (reply != null) {
- if (reply) {
+ if (ps.reply != null) {
+ if (ps.reply) {
push({
replyId: {
$exists: true,
@@ -223,8 +274,8 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
}
}
- if (renote != null) {
- if (renote) {
+ if (ps.renote != null) {
+ if (ps.renote) {
push({
renoteId: {
$exists: true,
@@ -244,29 +295,16 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
}
}
- if (media != null) {
- if (media) {
- push({
- mediaIds: {
- $exists: true,
- $ne: null
- }
- });
- } else {
- push({
- $or: [{
- mediaIds: {
- $exists: false
- }
- }, {
- mediaIds: null
- }]
- });
- }
+ const withFiles = ps.withFiles != null ? ps.withFiles : ps.media;
+
+ if (withFiles) {
+ push({
+ fileIds: { $exists: true, $ne: [] }
+ });
}
- if (poll != null) {
- if (poll) {
+ if (ps.poll != null) {
+ if (ps.poll) {
push({
poll: {
$exists: true,
@@ -286,24 +324,32 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
}
}
- if (sinceDate) {
+ if (ps.untilId) {
+ push({
+ _id: {
+ $lt: ps.untilId
+ }
+ });
+ }
+
+ if (ps.sinceDate) {
push({
createdAt: {
- $gt: new Date(sinceDate)
+ $gt: new Date(ps.sinceDate)
}
});
}
- if (untilDate) {
+ if (ps.untilDate) {
push({
createdAt: {
- $lt: new Date(untilDate)
+ $lt: new Date(ps.untilDate)
}
});
}
if (q.$and.length == 0) {
- q = {};
+ delete q.$and;
}
// Search notes
@@ -312,10 +358,10 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
sort: {
_id: -1
},
- limit: limit,
- skip: offset
+ limit: ps.limit,
+ skip: ps.offset
});
// Serialize
- res(await Promise.all(notes.map(note => pack(note, me))));
+ res(await packMany(notes, me));
});
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index 099bf2010b..44a504eb18 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -2,9 +2,10 @@ import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
import Note from '../../../../models/note';
import Mute from '../../../../models/mute';
import { getFriends } from '../../common/get-friends';
-import { pack } from '../../../../models/note';
+import { packMany } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
+import { countIf } from '../../../../prelude/array';
export const meta = {
desc: {
@@ -67,9 +68,15 @@ export const meta = {
}
}),
+ withFiles: $.bool.optional.note({
+ desc: {
+ 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
+ }
+ }),
+
mediaOnly: $.bool.optional.note({
desc: {
- 'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します'
+ 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
}
}),
}
@@ -80,7 +87,7 @@ export default async (params: any, user: ILocalUser) => {
if (psErr) throw psErr;
// Check if only one of sinceId, untilId, sinceDate, untilDate specified
- if ([ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate].filter(x => x != null).length > 1) {
+ if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) {
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
}
@@ -154,7 +161,7 @@ export default async (params: any, user: ILocalUser) => {
}, {
text: { $ne: null }
}, {
- mediaIds: { $ne: [] }
+ fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
@@ -170,7 +177,7 @@ export default async (params: any, user: ILocalUser) => {
}, {
text: { $ne: null }
}, {
- mediaIds: { $ne: [] }
+ fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
@@ -186,16 +193,18 @@ export default async (params: any, user: ILocalUser) => {
}, {
text: { $ne: null }
}, {
- mediaIds: { $ne: [] }
+ fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
}
- if (ps.mediaOnly) {
+ const withFiles = ps.withFiles != null ? ps.withFiles : ps.mediaOnly;
+
+ if (withFiles) {
query.$and.push({
- mediaIds: { $exists: true, $ne: [] }
+ fileIds: { $exists: true, $ne: [] }
});
}
@@ -228,5 +237,5 @@ export default async (params: any, user: ILocalUser) => {
});
// Serialize
- return await Promise.all(timeline.map(note => pack(note, user)));
+ return await packMany(timeline, user);
};
diff --git a/src/server/api/endpoints/notes/trend.ts b/src/server/api/endpoints/notes/trend.ts
index 7a0a098f28..9f55ed3243 100644
--- a/src/server/api/endpoints/notes/trend.ts
+++ b/src/server/api/endpoints/notes/trend.ts
@@ -52,7 +52,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
}
if (media != undefined) {
- query.mediaIds = media ? { $exists: true, $ne: null } : null;
+ query.fileIds = media ? { $exists: true, $ne: null } : null;
}
if (poll != undefined) {
diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts
index a7b43014ed..6758b4eb73 100644
--- a/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -1,7 +1,7 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
import Note from '../../../../models/note';
import Mute from '../../../../models/mute';
-import { pack } from '../../../../models/note';
+import { packMany } from '../../../../models/note';
import UserList from '../../../../models/user-list';
import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
@@ -73,9 +73,15 @@ export const meta = {
}
}),
+ withFiles: $.bool.optional.note({
+ desc: {
+ 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
+ }
+ }),
+
mediaOnly: $.bool.optional.note({
desc: {
- 'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します'
+ 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
}
}),
}
@@ -160,7 +166,7 @@ export default async (params: any, user: ILocalUser) => {
}, {
text: { $ne: null }
}, {
- mediaIds: { $ne: [] }
+ fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
@@ -176,7 +182,7 @@ export default async (params: any, user: ILocalUser) => {
}, {
text: { $ne: null }
}, {
- mediaIds: { $ne: [] }
+ fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
@@ -192,16 +198,18 @@ export default async (params: any, user: ILocalUser) => {
}, {
text: { $ne: null }
}, {
- mediaIds: { $ne: [] }
+ fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
}
- if (ps.mediaOnly) {
+ const withFiles = ps.withFiles != null ? ps.withFiles : ps.mediaOnly;
+
+ if (withFiles) {
query.$and.push({
- mediaIds: { $exists: true, $ne: [] }
+ fileIds: { $exists: true, $ne: [] }
});
}
@@ -234,5 +242,5 @@ export default async (params: any, user: ILocalUser) => {
});
// Serialize
- return await Promise.all(timeline.map(note => pack(note, user)));
+ return await packMany(timeline, user);
};