summaryrefslogtreecommitdiff
path: root/src/server/api
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-04-17 14:52:28 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-04-17 14:52:28 +0900
commita0e640b1189a55c28aafe7d586d531731ad450a4 (patch)
treec8d0ed34511646f1b5a1e68ff24d7510b1c64e7b /src/server/api
parentキューのメモリ使用量を削減 (diff)
downloadsharkey-a0e640b1189a55c28aafe7d586d531731ad450a4.tar.gz
sharkey-a0e640b1189a55c28aafe7d586d531731ad450a4.tar.bz2
sharkey-a0e640b1189a55c28aafe7d586d531731ad450a4.zip
ローカルタイムラインとグローバルタイムラインを実装
Diffstat (limited to 'src/server/api')
-rw-r--r--src/server/api/endpoints.ts16
-rw-r--r--src/server/api/endpoints/mute/create.ts5
-rw-r--r--src/server/api/endpoints/mute/delete.ts15
-rw-r--r--src/server/api/endpoints/notes/global-timeline.ts91
-rw-r--r--src/server/api/endpoints/notes/local-timeline.ts94
-rw-r--r--src/server/api/endpoints/notes/timeline.ts9
-rw-r--r--src/server/api/stream/global-timeline.ts39
-rw-r--r--src/server/api/stream/home.ts7
-rw-r--r--src/server/api/stream/local-timeline.ts39
-rw-r--r--src/server/api/streaming.ts6
10 files changed, 293 insertions, 28 deletions
diff --git a/src/server/api/endpoints.ts b/src/server/api/endpoints.ts
index 67f3217faf..e0223c23e0 100644
--- a/src/server/api/endpoints.ts
+++ b/src/server/api/endpoints.ts
@@ -464,6 +464,22 @@ const endpoints: Endpoint[] = [
}
},
{
+ name: 'notes/local-timeline',
+ withCredential: true,
+ limit: {
+ duration: ms('10minutes'),
+ max: 100
+ }
+ },
+ {
+ name: 'notes/global-timeline',
+ withCredential: true,
+ limit: {
+ duration: ms('10minutes'),
+ max: 100
+ }
+ },
+ {
name: 'notes/mentions',
withCredential: true,
limit: {
diff --git a/src/server/api/endpoints/mute/create.ts b/src/server/api/endpoints/mute/create.ts
index 19894d07af..26ae612cab 100644
--- a/src/server/api/endpoints/mute/create.ts
+++ b/src/server/api/endpoints/mute/create.ts
@@ -30,7 +30,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
}, {
fields: {
data: false,
- 'profile': false
+ profile: false
}
});
@@ -41,8 +41,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
// Check if already muting
const exist = await Mute.findOne({
muterId: muter._id,
- muteeId: mutee._id,
- deletedAt: { $exists: false }
+ muteeId: mutee._id
});
if (exist !== null) {
diff --git a/src/server/api/endpoints/mute/delete.ts b/src/server/api/endpoints/mute/delete.ts
index 10096352ba..6f617416c8 100644
--- a/src/server/api/endpoints/mute/delete.ts
+++ b/src/server/api/endpoints/mute/delete.ts
@@ -7,10 +7,6 @@ import Mute from '../../../../models/mute';
/**
* Unmute a user
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
*/
module.exports = (params, user) => new Promise(async (res, rej) => {
const muter = user;
@@ -30,7 +26,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
}, {
fields: {
data: false,
- 'profile': false
+ profile: false
}
});
@@ -41,8 +37,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
// Check not muting
const exist = await Mute.findOne({
muterId: muter._id,
- muteeId: mutee._id,
- deletedAt: { $exists: false }
+ muteeId: mutee._id
});
if (exist === null) {
@@ -50,12 +45,8 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
}
// Delete mute
- await Mute.update({
+ await Mute.remove({
_id: exist._id
- }, {
- $set: {
- deletedAt: new Date()
- }
});
// Send response
diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts
new file mode 100644
index 0000000000..07e138ec54
--- /dev/null
+++ b/src/server/api/endpoints/notes/global-timeline.ts
@@ -0,0 +1,91 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import Note from '../../../../models/note';
+import Mute from '../../../../models/mute';
+import { pack } from '../../../../models/note';
+
+/**
+ * Get timeline of global
+ */
+module.exports = async (params, user, app) => {
+ // Get 'limit' parameter
+ const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$;
+ if (limitErr) throw 'invalid limit param';
+
+ // Get 'sinceId' parameter
+ const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+ if (sinceIdErr) throw 'invalid sinceId param';
+
+ // Get 'untilId' parameter
+ const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+ if (untilIdErr) throw 'invalid untilId param';
+
+ // Get 'sinceDate' parameter
+ const [sinceDate, sinceDateErr] = $(params.sinceDate).optional.number().$;
+ if (sinceDateErr) throw 'invalid sinceDate param';
+
+ // Get 'untilDate' parameter
+ const [untilDate, untilDateErr] = $(params.untilDate).optional.number().$;
+ if (untilDateErr) throw 'invalid untilDate param';
+
+ // Check if only one of sinceId, untilId, sinceDate, untilDate specified
+ if ([sinceId, untilId, sinceDate, untilDate].filter(x => x != null).length > 1) {
+ throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
+ }
+
+ // ミュートしているユーザーを取得
+ const mutedUserIds = (await Mute.find({
+ muterId: user._id
+ })).map(m => m.muteeId);
+
+ //#region Construct query
+ const sort = {
+ _id: -1
+ };
+
+ const query = {
+ // mute
+ userId: {
+ $nin: mutedUserIds
+ },
+ '_reply.userId': {
+ $nin: mutedUserIds
+ },
+ '_renote.userId': {
+ $nin: mutedUserIds
+ }
+ } as any;
+
+ if (sinceId) {
+ sort._id = 1;
+ query._id = {
+ $gt: sinceId
+ };
+ } else if (untilId) {
+ query._id = {
+ $lt: untilId
+ };
+ } else if (sinceDate) {
+ sort._id = 1;
+ query.createdAt = {
+ $gt: new Date(sinceDate)
+ };
+ } else if (untilDate) {
+ query.createdAt = {
+ $lt: new Date(untilDate)
+ };
+ }
+ //#endregion
+
+ // Issue query
+ const timeline = await Note
+ .find(query, {
+ limit: limit,
+ sort: sort
+ });
+
+ // Serialize
+ return await Promise.all(timeline.map(note => pack(note, user)));
+};
diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts
new file mode 100644
index 0000000000..d63528c3cd
--- /dev/null
+++ b/src/server/api/endpoints/notes/local-timeline.ts
@@ -0,0 +1,94 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import Note from '../../../../models/note';
+import Mute from '../../../../models/mute';
+import { pack } from '../../../../models/note';
+
+/**
+ * Get timeline of local
+ */
+module.exports = async (params, user, app) => {
+ // Get 'limit' parameter
+ const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$;
+ if (limitErr) throw 'invalid limit param';
+
+ // Get 'sinceId' parameter
+ const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+ if (sinceIdErr) throw 'invalid sinceId param';
+
+ // Get 'untilId' parameter
+ const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+ if (untilIdErr) throw 'invalid untilId param';
+
+ // Get 'sinceDate' parameter
+ const [sinceDate, sinceDateErr] = $(params.sinceDate).optional.number().$;
+ if (sinceDateErr) throw 'invalid sinceDate param';
+
+ // Get 'untilDate' parameter
+ const [untilDate, untilDateErr] = $(params.untilDate).optional.number().$;
+ if (untilDateErr) throw 'invalid untilDate param';
+
+ // Check if only one of sinceId, untilId, sinceDate, untilDate specified
+ if ([sinceId, untilId, sinceDate, untilDate].filter(x => x != null).length > 1) {
+ throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
+ }
+
+ // ミュートしているユーザーを取得
+ const mutedUserIds = (await Mute.find({
+ muterId: user._id
+ })).map(m => m.muteeId);
+
+ //#region Construct query
+ const sort = {
+ _id: -1
+ };
+
+ const query = {
+ // mute
+ userId: {
+ $nin: mutedUserIds
+ },
+ '_reply.userId': {
+ $nin: mutedUserIds
+ },
+ '_renote.userId': {
+ $nin: mutedUserIds
+ },
+
+ // local
+ '_user.host': null
+ } as any;
+
+ if (sinceId) {
+ sort._id = 1;
+ query._id = {
+ $gt: sinceId
+ };
+ } else if (untilId) {
+ query._id = {
+ $lt: untilId
+ };
+ } else if (sinceDate) {
+ sort._id = 1;
+ query.createdAt = {
+ $gt: new Date(sinceDate)
+ };
+ } else if (untilDate) {
+ query.createdAt = {
+ $lt: new Date(untilDate)
+ };
+ }
+ //#endregion
+
+ // Issue query
+ const timeline = await Note
+ .find(query, {
+ limit: limit,
+ sort: sort
+ });
+
+ // Serialize
+ return await Promise.all(timeline.map(note => pack(note, user)));
+};
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index 5263cfb2aa..b5feaac817 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -11,11 +11,6 @@ import { pack } from '../../../../models/note';
/**
* Get timeline of myself
- *
- * @param {any} params
- * @param {any} user
- * @param {any} app
- * @return {Promise<any>}
*/
module.exports = async (params, user, app) => {
// Get 'limit' parameter
@@ -56,9 +51,7 @@ module.exports = async (params, user, app) => {
// ミュートしているユーザーを取得
mutedUserIds: Mute.find({
- muterId: user._id,
- // 削除されたドキュメントは除く
- deletedAt: { $exists: false }
+ muterId: user._id
}).then(ms => ms.map(m => m.muteeId))
});
diff --git a/src/server/api/stream/global-timeline.ts b/src/server/api/stream/global-timeline.ts
new file mode 100644
index 0000000000..f31ce17752
--- /dev/null
+++ b/src/server/api/stream/global-timeline.ts
@@ -0,0 +1,39 @@
+import * as websocket from 'websocket';
+import * as redis from 'redis';
+
+import { IUser } from '../../../models/user';
+import Mute from '../../../models/mute';
+
+export default async function(
+ request: websocket.request,
+ connection: websocket.connection,
+ subscriber: redis.RedisClient,
+ user: IUser
+) {
+ // Subscribe stream
+ subscriber.subscribe(`misskey:global-timeline`);
+
+ const mute = await Mute.find({ muterId: user._id });
+ const mutedUserIds = mute.map(m => m.muteeId.toString());
+
+ subscriber.on('message', async (_, data) => {
+ const note = JSON.parse(data);
+
+ //#region 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
+ if (mutedUserIds.indexOf(note.userId) != -1) {
+ return;
+ }
+ if (note.reply != null && mutedUserIds.indexOf(note.reply.userId) != -1) {
+ return;
+ }
+ if (note.renote != null && mutedUserIds.indexOf(note.renote.userId) != -1) {
+ return;
+ }
+ //#endregion
+
+ connection.send(JSON.stringify({
+ type: 'note',
+ body: note
+ }));
+ });
+}
diff --git a/src/server/api/stream/home.ts b/src/server/api/stream/home.ts
index e9c0924f31..a9d6ff241e 100644
--- a/src/server/api/stream/home.ts
+++ b/src/server/api/stream/home.ts
@@ -21,10 +21,7 @@ export default async function(
// Subscribe Home stream channel
subscriber.subscribe(`misskey:user-stream:${user._id}`);
- const mute = await Mute.find({
- muterId: user._id,
- deletedAt: { $exists: false }
- });
+ const mute = await Mute.find({ muterId: user._id });
const mutedUserIds = mute.map(m => m.muteeId.toString());
subscriber.on('message', async (channel, data) => {
@@ -33,6 +30,7 @@ export default async function(
try {
const x = JSON.parse(data);
+ //#region 流れてきたメッセージがミュートしているユーザーが関わるものだったら無視する
if (x.type == 'note') {
if (mutedUserIds.indexOf(x.body.userId) != -1) {
return;
@@ -48,6 +46,7 @@ export default async function(
return;
}
}
+ //#endregion
connection.send(data);
} catch (e) {
diff --git a/src/server/api/stream/local-timeline.ts b/src/server/api/stream/local-timeline.ts
new file mode 100644
index 0000000000..a790ba878b
--- /dev/null
+++ b/src/server/api/stream/local-timeline.ts
@@ -0,0 +1,39 @@
+import * as websocket from 'websocket';
+import * as redis from 'redis';
+
+import { IUser } from '../../../models/user';
+import Mute from '../../../models/mute';
+
+export default async function(
+ request: websocket.request,
+ connection: websocket.connection,
+ subscriber: redis.RedisClient,
+ user: IUser
+) {
+ // Subscribe stream
+ subscriber.subscribe(`misskey:local-timeline`);
+
+ const mute = await Mute.find({ muterId: user._id });
+ const mutedUserIds = mute.map(m => m.muteeId.toString());
+
+ subscriber.on('message', async (_, data) => {
+ const note = JSON.parse(data);
+
+ //#region 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
+ if (mutedUserIds.indexOf(note.userId) != -1) {
+ return;
+ }
+ if (note.reply != null && mutedUserIds.indexOf(note.reply.userId) != -1) {
+ return;
+ }
+ if (note.renote != null && mutedUserIds.indexOf(note.renote.userId) != -1) {
+ return;
+ }
+ //#endregion
+
+ connection.send(JSON.stringify({
+ type: 'note',
+ body: note
+ }));
+ });
+}
diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts
index d586d7c08f..ce13253649 100644
--- a/src/server/api/streaming.ts
+++ b/src/server/api/streaming.ts
@@ -4,6 +4,8 @@ import * as redis from 'redis';
import config from '../../config';
import homeStream from './stream/home';
+import localTimelineStream from './stream/local-timeline';
+import globalTimelineStream from './stream/global-timeline';
import driveStream from './stream/drive';
import messagingStream from './stream/messaging';
import messagingIndexStream from './stream/messaging-index';
@@ -64,8 +66,10 @@ module.exports = (server: http.Server) => {
return;
}
- const channel =
+ const channel: any =
request.resourceURL.pathname === '/' ? homeStream :
+ request.resourceURL.pathname === '/local-timeline' ? localTimelineStream :
+ request.resourceURL.pathname === '/global-timeline' ? globalTimelineStream :
request.resourceURL.pathname === '/drive' ? driveStream :
request.resourceURL.pathname === '/messaging' ? messagingStream :
request.resourceURL.pathname === '/messaging-index' ? messagingIndexStream :