diff options
| author | syuilo <syuilotan@yahoo.co.jp> | 2018-04-17 14:52:28 +0900 |
|---|---|---|
| committer | syuilo <syuilotan@yahoo.co.jp> | 2018-04-17 14:52:28 +0900 |
| commit | a0e640b1189a55c28aafe7d586d531731ad450a4 (patch) | |
| tree | c8d0ed34511646f1b5a1e68ff24d7510b1c64e7b /src/server/api | |
| parent | キューのメモリ使用量を削減 (diff) | |
| download | sharkey-a0e640b1189a55c28aafe7d586d531731ad450a4.tar.gz sharkey-a0e640b1189a55c28aafe7d586d531731ad450a4.tar.bz2 sharkey-a0e640b1189a55c28aafe7d586d531731ad450a4.zip | |
ローカルタイムラインとグローバルタイムラインを実装
Diffstat (limited to 'src/server/api')
| -rw-r--r-- | src/server/api/endpoints.ts | 16 | ||||
| -rw-r--r-- | src/server/api/endpoints/mute/create.ts | 5 | ||||
| -rw-r--r-- | src/server/api/endpoints/mute/delete.ts | 15 | ||||
| -rw-r--r-- | src/server/api/endpoints/notes/global-timeline.ts | 91 | ||||
| -rw-r--r-- | src/server/api/endpoints/notes/local-timeline.ts | 94 | ||||
| -rw-r--r-- | src/server/api/endpoints/notes/timeline.ts | 9 | ||||
| -rw-r--r-- | src/server/api/stream/global-timeline.ts | 39 | ||||
| -rw-r--r-- | src/server/api/stream/home.ts | 7 | ||||
| -rw-r--r-- | src/server/api/stream/local-timeline.ts | 39 | ||||
| -rw-r--r-- | src/server/api/streaming.ts | 6 |
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 : |