From e69554f8101b138284749ec1e335eaf35ff77c6c Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 17 Apr 2018 19:43:49 +0900 Subject: Delete publicKey of remote user --- src/models/user.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/models/user.ts') diff --git a/src/models/user.ts b/src/models/user.ts index 741306fd27..9638d15932 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -359,6 +359,8 @@ export const pack = ( if (!opts.detail) { delete _user.twoFactorEnabled; } + } else { + delete _user.publicKey; } _user.avatarUrl = _user.avatarId != null -- cgit v1.2.3-freya From 50d56bdc2585a3bbd4ad996426f62d6ed186a34d Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 17 Apr 2018 22:31:52 +0900 Subject: Clean up --- src/models/user.ts | 4 --- src/server/api/endpoints/notes/create.ts | 42 -------------------------------- src/services/note/create.ts | 6 +---- 3 files changed, 1 insertion(+), 51 deletions(-) (limited to 'src/models/user.ts') diff --git a/src/models/user.ts b/src/models/user.ts index 9638d15932..00c249849e 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -47,7 +47,6 @@ type IUserBase = { bannerId: mongo.ObjectID; data: any; description: string; - latestNote: INote; pinnedNoteId: mongo.ObjectID; isSuspended: boolean; keywords: string[]; @@ -332,9 +331,6 @@ export const pack = ( _user.id = _user._id; delete _user._id; - // Remove needless properties - delete _user.latestNote; - if (_user.host == null) { // Remove private properties delete _user.keypair; diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts index 7e79912b1b..4435a6a3c8 100644 --- a/src/server/api/endpoints/notes/create.ts +++ b/src/server/api/endpoints/notes/create.ts @@ -97,31 +97,7 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res 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 @@ -208,24 +184,6 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res 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(), diff --git a/src/services/note/create.ts b/src/services/note/create.ts index e35e5ecfbd..391a3d7647 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -89,14 +89,10 @@ export default async (user: IUser, data: { res(note); + // Increment notes count User.update({ _id: user._id }, { - // Increment notes count $inc: { notesCount: 1 - }, - // Update latest note - $set: { - latestNote: note } }); -- cgit v1.2.3-freya From a447ed1d106451b1e55f106d6aa4545680de3b82 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 17 Apr 2018 22:33:27 +0900 Subject: oops --- src/models/user.ts | 2 +- src/server/api/endpoints/notes/create.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src/models/user.ts') diff --git a/src/models/user.ts b/src/models/user.ts index 00c249849e..bcb2a73e24 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -2,7 +2,7 @@ import * as mongo from 'mongodb'; import deepcopy = require('deepcopy'); import rap from '@prezzemolo/rap'; import db from '../db/mongodb'; -import Note, { INote, pack as packNote, deleteNote } from './note'; +import Note, { pack as packNote, deleteNote } from './note'; import Following, { deleteFollowing } from './following'; import Mute, { deleteMute } from './mute'; import getFriends from '../server/api/common/get-friends'; diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts index 4435a6a3c8..14e906eb9d 100644 --- a/src/server/api/endpoints/notes/create.ts +++ b/src/server/api/endpoints/notes/create.ts @@ -2,7 +2,6 @@ * 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'; -- cgit v1.2.3-freya From 0d5bc3be6611a6877a71233ac57c0987027c0398 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Apr 2018 12:43:25 +0900 Subject: ストーキング実装 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #1511 --- locales/en.yml | 3 ++ locales/fr.yml | 3 ++ locales/ja.yml | 3 ++ migration/2018-04-19.js | 49 ++++++++++++++++++ .../app/desktop/views/pages/user/user.profile.vue | 30 ++++++++++- src/models/following.ts | 11 ++++ src/models/note.ts | 5 +- src/models/user.ts | 43 ++++++++-------- src/server/api/common/get-friends.ts | 29 +++++++++-- src/server/api/endpoints.ts | 18 +++++++ src/server/api/endpoints/following/stalk.ts | 36 +++++++++++++ src/server/api/endpoints/following/unstalk.ts | 35 +++++++++++++ src/server/api/endpoints/i/notifications.ts | 4 +- src/server/api/endpoints/mute/list.ts | 4 +- src/server/api/endpoints/notes/mentions.ts | 4 +- src/server/api/endpoints/notes/search.ts | 4 +- src/server/api/endpoints/notes/timeline.ts | 60 +++++++++++++++------- src/server/api/endpoints/users/followers.ts | 4 +- src/server/api/endpoints/users/following.ts | 4 +- src/server/api/endpoints/users/recommendation.ts | 4 +- src/services/following/create.ts | 13 ++++- src/services/note/create.ts | 24 +++------ 22 files changed, 310 insertions(+), 80 deletions(-) create mode 100644 migration/2018-04-19.js create mode 100644 src/server/api/endpoints/following/stalk.ts create mode 100644 src/server/api/endpoints/following/unstalk.ts (limited to 'src/models/user.ts') diff --git a/locales/en.yml b/locales/en.yml index 9388aedbf8..4eb0a3446c 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -419,6 +419,9 @@ desktop/views/pages/user/user.photos.vue: desktop/views/pages/user/user.profile.vue: follows-you: "Follows you" + stalk: "Stalk" + stalking: "Stalking" + unstalk: "Unstalk" mute: "Mute" muted: "Muting" unmute: "Unmute" diff --git a/locales/fr.yml b/locales/fr.yml index cd756194ab..0e0019e565 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -419,6 +419,9 @@ desktop/views/pages/user/user.photos.vue: desktop/views/pages/user/user.profile.vue: follows-you: "Vous suis" + stalk: "ストークする" + stalking: "ストーキングしています" + unstalk: "ストーク解除" mute: "Mettre en sourdine" muted: "Muting" unmute: "Enlever la sourdine" diff --git a/locales/ja.yml b/locales/ja.yml index 96b4a1d0ea..ed32deae57 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -419,6 +419,9 @@ desktop/views/pages/user/user.photos.vue: desktop/views/pages/user/user.profile.vue: follows-you: "フォローされています" + stalk: "ストークする" + stalking: "ストーキングしています" + unstalk: "ストーク解除" mute: "ミュートする" muted: "ミュートしています" unmute: "ミュート解除" diff --git a/migration/2018-04-19.js b/migration/2018-04-19.js new file mode 100644 index 0000000000..b0df22c009 --- /dev/null +++ b/migration/2018-04-19.js @@ -0,0 +1,49 @@ +// for Node.js interpret + +const { default: User } = require('../built/models/user'); +const { default: Following } = require('../built/models/following'); +const { default: zip } = require('@prezzemolo/zip') + +const migrate = async (following) => { + const follower = await User.findOne({ _id: following.followerId }); + const followee = await User.findOne({ _id: following.followeeId }); + const result = await Following.update(following._id, { + $set: { + stalk: true, + _follower: { + host: follower.host, + inbox: follower.host != null ? follower.inbox : undefined + }, + _followee: { + host: followee.host, + inbox: followee.host != null ? followee.inbox : undefined + } + } + }); + return result.ok === 1; +} + +async function main() { + const count = await Following.count({}); + + const dop = Number.parseInt(process.argv[2]) || 5 + const idop = ((count - (count % dop)) / dop) + 1 + + return zip( + 1, + async (time) => { + console.log(`${time} / ${idop}`) + const doc = await Following.find({}, { + limit: dop, skip: time * dop + }) + return Promise.all(doc.map(migrate)) + }, + idop + ).then(a => { + const rv = [] + a.forEach(e => rv.push(...e)) + return rv + }) +} + +main().then(console.dir).catch(console.error) diff --git a/src/client/app/desktop/views/pages/user/user.profile.vue b/src/client/app/desktop/views/pages/user/user.profile.vue index 72750e1b3d..774f300a38 100644 --- a/src/client/app/desktop/views/pages/user/user.profile.vue +++ b/src/client/app/desktop/views/pages/user/user.profile.vue @@ -3,8 +3,14 @@

%i18n:@follows-you%

-

%i18n:@muted% %i18n:@unmute%

-

%i18n:@mute%

+

+ %i18n:@stalking% %i18n:@unstalk% + %i18n:@stalk% +

+

+ %i18n:@muted% %i18n:@unmute% + %i18n:@mute% +

{{ user.description }}
@@ -47,6 +53,26 @@ export default Vue.extend({ }); }, + stalk() { + (this as any).api('following/stalk', { + userId: this.user.id + }).then(() => { + this.user.isStalking = true; + }, () => { + alert('error'); + }); + }, + + unstalk() { + (this as any).api('following/unstalk', { + userId: this.user.id + }).then(() => { + this.user.isStalking = false; + }, () => { + alert('error'); + }); + }, + mute() { (this as any).api('mute/create', { userId: this.user.id diff --git a/src/models/following.ts b/src/models/following.ts index f10e349ee9..4712379a70 100644 --- a/src/models/following.ts +++ b/src/models/following.ts @@ -10,6 +10,17 @@ export type IFollowing = { createdAt: Date; followeeId: mongo.ObjectID; followerId: mongo.ObjectID; + stalk: boolean; + + // 非正規化 + _followee: { + host: string; + inbox?: string; + }, + _follower: { + host: string; + inbox?: string; + } }; /** diff --git a/src/models/note.ts b/src/models/note.ts index 3059593540..d4b16afa4c 100644 --- a/src/models/note.ts +++ b/src/models/note.ts @@ -58,6 +58,7 @@ export type INote = { }; uri: string; + // 非正規化 _reply?: { userId: mongo.ObjectID; }; @@ -66,9 +67,7 @@ export type INote = { }; _user: { host: string; - account: { - inbox?: string; - }; + inbox?: string; }; }; diff --git a/src/models/user.ts b/src/models/user.ts index bcb2a73e24..ca1ca28937 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -5,7 +5,7 @@ import db from '../db/mongodb'; import Note, { pack as packNote, deleteNote } from './note'; import Following, { deleteFollowing } from './following'; import Mute, { deleteMute } from './mute'; -import getFriends from '../server/api/common/get-friends'; +import { getFriendIds } from '../server/api/common/get-friends'; import config from '../config'; import AccessToken, { deleteAccessToken } from './access-token'; import NoteWatching, { deleteNoteWatching } from './note-watching'; @@ -375,33 +375,30 @@ export const pack = ( } if (meId && !meId.equals(_user.id)) { - // Whether the user is following - _user.isFollowing = (async () => { - const follow = await Following.findOne({ + const [following1, following2, mute] = await Promise.all([ + Following.findOne({ followerId: meId, followeeId: _user.id - }); - return follow !== null; - })(); - - // Whether the user is followed - _user.isFollowed = (async () => { - const follow2 = await Following.findOne({ + }), + Following.findOne({ followerId: _user.id, followeeId: meId - }); - return follow2 !== null; - })(); + }), + Mute.findOne({ + muterId: meId, + muteeId: _user.id + }) + ]); + + // Whether the user is following + _user.isFollowing = following1 !== null; + _user.isStalking = following1 && following1.stalk; + + // Whether the user is followed + _user.isFollowed = following2 !== null; // Whether the user is muted - _user.isMuted = (async () => { - const mute = await Mute.findOne({ - muterId: meId, - muteeId: _user.id, - deletedAt: { $exists: false } - }); - return mute !== null; - })(); + _user.isMuted = mute !== null; } if (opts.detail) { @@ -413,7 +410,7 @@ export const pack = ( } if (meId && !meId.equals(_user.id)) { - const myFollowingIds = await getFriends(meId); + const myFollowingIds = await getFriendIds(meId); // Get following you know count _user.followingYouKnowCount = Following.count({ diff --git a/src/server/api/common/get-friends.ts b/src/server/api/common/get-friends.ts index c1cc3957d8..50ba71ea96 100644 --- a/src/server/api/common/get-friends.ts +++ b/src/server/api/common/get-friends.ts @@ -1,10 +1,10 @@ import * as mongodb from 'mongodb'; import Following from '../../../models/following'; -export default async (me: mongodb.ObjectID, includeMe: boolean = true) => { +export const getFriendIds = async (me: mongodb.ObjectID, includeMe = true) => { // Fetch relation to other users who the I follows // SELECT followee - const myfollowing = await Following + const followings = await Following .find({ followerId: me }, { @@ -14,7 +14,7 @@ export default async (me: mongodb.ObjectID, includeMe: boolean = true) => { }); // ID list of other users who the I follows - const myfollowingIds = myfollowing.map(follow => follow.followeeId); + const myfollowingIds = followings.map(following => following.followeeId); if (includeMe) { myfollowingIds.push(me); @@ -22,3 +22,26 @@ export default async (me: mongodb.ObjectID, includeMe: boolean = true) => { return myfollowingIds; }; + +export const getFriends = async (me: mongodb.ObjectID, includeMe = true) => { + // Fetch relation to other users who the I follows + const followings = await Following + .find({ + followerId: me + }); + + // ID list of other users who the I follows + const myfollowings = followings.map(following => ({ + id: following.followeeId, + stalk: following.stalk + })); + + if (includeMe) { + myfollowings.push({ + id: me, + stalk: true + }); + } + + return myfollowings; +}; diff --git a/src/server/api/endpoints.ts b/src/server/api/endpoints.ts index e0223c23e0..7cf49debe9 100644 --- a/src/server/api/endpoints.ts +++ b/src/server/api/endpoints.ts @@ -426,6 +426,24 @@ const endpoints: Endpoint[] = [ }, kind: 'following-write' }, + { + name: 'following/stalk', + withCredential: true, + limit: { + duration: ms('1hour'), + max: 100 + }, + kind: 'following-write' + }, + { + name: 'following/unstalk', + withCredential: true, + limit: { + duration: ms('1hour'), + max: 100 + }, + kind: 'following-write' + }, { name: 'notes' diff --git a/src/server/api/endpoints/following/stalk.ts b/src/server/api/endpoints/following/stalk.ts new file mode 100644 index 0000000000..fc8be4924d --- /dev/null +++ b/src/server/api/endpoints/following/stalk.ts @@ -0,0 +1,36 @@ +import $ from 'cafy'; +import Following from '../../../../models/following'; +import { isLocalUser } from '../../../../models/user'; + +/** + * Stalk a user + */ +module.exports = (params, user) => new Promise(async (res, rej) => { + const follower = user; + + // Get 'userId' parameter + const [userId, userIdErr] = $(params.userId).id().$; + if (userIdErr) return rej('invalid userId param'); + + // Fetch following + const following = await Following.findOne({ + followerId: follower._id, + followeeId: userId + }); + + if (following === null) { + return rej('following not found'); + } + + // Stalk + await Following.update({ _id: following._id }, { + $set: { + stalk: true + } + }); + + // Send response + res(); + + // TODO: イベント +}); diff --git a/src/server/api/endpoints/following/unstalk.ts b/src/server/api/endpoints/following/unstalk.ts new file mode 100644 index 0000000000..d7593bcd00 --- /dev/null +++ b/src/server/api/endpoints/following/unstalk.ts @@ -0,0 +1,35 @@ +import $ from 'cafy'; +import Following from '../../../../models/following'; + +/** + * Unstalk a user + */ +module.exports = (params, user) => new Promise(async (res, rej) => { + const follower = user; + + // Get 'userId' parameter + const [userId, userIdErr] = $(params.userId).id().$; + if (userIdErr) return rej('invalid userId param'); + + // Fetch following + const following = await Following.findOne({ + followerId: follower._id, + followeeId: userId + }); + + if (following === null) { + return rej('following not found'); + } + + // Stalk + await Following.update({ _id: following._id }, { + $set: { + stalk: false + } + }); + + // Send response + res(); + + // TODO: イベント +}); diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 3b4899682d..69a8910898 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -5,7 +5,7 @@ import $ from 'cafy'; import Notification from '../../../../models/notification'; import Mute from '../../../../models/mute'; import { pack } from '../../../../models/notification'; -import getFriends from '../../common/get-friends'; +import { getFriendIds } from '../../common/get-friends'; import read from '../../common/read-notification'; /** @@ -62,7 +62,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { if (following) { // ID list of the user itself and other users who the user follows - const followingIds = await getFriends(user._id); + const followingIds = await getFriendIds(user._id); query.$and.push({ notifierId: { diff --git a/src/server/api/endpoints/mute/list.ts b/src/server/api/endpoints/mute/list.ts index bd80401445..0b8262d6c5 100644 --- a/src/server/api/endpoints/mute/list.ts +++ b/src/server/api/endpoints/mute/list.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import Mute from '../../../../models/mute'; import { pack } from '../../../../models/user'; -import getFriends from '../../common/get-friends'; +import { getFriendIds } from '../../common/get-friends'; /** * Get muted users of a user @@ -34,7 +34,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { if (iknow) { // Get my friends - const myFriends = await getFriends(me._id); + const myFriends = await getFriendIds(me._id); query.muteeId = { $in: myFriends diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index c507acbaec..2d95606b3f 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import Note from '../../../../models/note'; -import getFriends from '../../common/get-friends'; +import { getFriendIds } from '../../common/get-friends'; import { pack } from '../../../../models/note'; /** @@ -46,7 +46,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }; if (following) { - const followingIds = await getFriends(user._id); + const followingIds = await getFriendIds(user._id); query.userId = { $in: followingIds diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index bfa17b000e..3ff3fbbafa 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -6,7 +6,7 @@ const escapeRegexp = require('escape-regexp'); import Note from '../../../../models/note'; import User from '../../../../models/user'; import Mute from '../../../../models/mute'; -import getFriends from '../../common/get-friends'; +import { getFriendIds } from '../../common/get-friends'; import { pack } from '../../../../models/note'; /** @@ -156,7 +156,7 @@ async function search( } if (following != null && me != null) { - const ids = await getFriends(me._id, false); + const ids = await getFriendIds(me._id, false); push({ userId: following ? { $in: ids diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index b5feaac817..8cd23fd369 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -2,11 +2,10 @@ * Module dependencies */ import $ from 'cafy'; -import rap from '@prezzemolo/rap'; import Note from '../../../../models/note'; import Mute from '../../../../models/mute'; import ChannelWatching from '../../../../models/channel-watching'; -import getFriends from '../../common/get-friends'; +import { getFriends } from '../../common/get-friends'; import { pack } from '../../../../models/note'; /** @@ -38,41 +37,66 @@ module.exports = async (params, user, app) => { throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified'; } - const { followingIds, watchingChannelIds, mutedUserIds } = await rap({ - // ID list of the user itself and other users who the user follows - followingIds: getFriends(user._id), + const [followings, watchingChannelIds, mutedUserIds] = await Promise.all([ + // フォローを取得 + // Fetch following + getFriends(user._id), // Watchしているチャンネルを取得 - watchingChannelIds: ChannelWatching.find({ + ChannelWatching.find({ userId: user._id, // 削除されたドキュメントは除く deletedAt: { $exists: false } }).then(watches => watches.map(w => w.channelId)), // ミュートしているユーザーを取得 - mutedUserIds: Mute.find({ + Mute.find({ muterId: user._id }).then(ms => ms.map(m => m.muteeId)) - }); + ]); //#region Construct query const sort = { _id: -1 }; + const followQuery = followings.map(f => f.stalk ? { + userId: f.id + } : { + userId: f.id, + + // ストーキングしてないならリプライは含めない(ただし投稿者自身の投稿へのリプライ、自分の投稿へのリプライ、自分のリプライは含める) + $or: [{ + // リプライでない + replyId: null + }, { // または + // リプライだが返信先が投稿者自身の投稿 + $expr: { + '$_reply.userId': '$userId' + } + }, { // または + // リプライだが返信先が自分(フォロワー)の投稿 + '_reply.userId': user._id + }, { // または + // 自分(フォロワー)が送信したリプライ + userId: user._id + }] + }); + const query = { $or: [{ - // フォローしている人のタイムラインへの投稿 - userId: { - $in: followingIds - }, - // 「タイムラインへの」投稿に限定するためにチャンネルが指定されていないもののみに限る - $or: [{ - channelId: { - $exists: false - } + $and: [{ + // フォローしている人のタイムラインへの投稿 + $or: followQuery }, { - channelId: null + // 「タイムラインへの」投稿に限定するためにチャンネルが指定されていないもののみに限る + $or: [{ + channelId: { + $exists: false + } + }, { + channelId: null + }] }] }, { // Watchしているチャンネルへの投稿 diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts index 0222313e81..5f03326be8 100644 --- a/src/server/api/endpoints/users/followers.ts +++ b/src/server/api/endpoints/users/followers.ts @@ -5,7 +5,7 @@ import $ from 'cafy'; import User from '../../../../models/user'; import Following from '../../../../models/following'; import { pack } from '../../../../models/user'; -import getFriends from '../../common/get-friends'; +import { getFriendIds } from '../../common/get-friends'; /** * Get followers of a user @@ -52,7 +52,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // ログインしていてかつ iknow フラグがあるとき if (me && iknow) { // Get my friends - const myFriends = await getFriends(me._id); + const myFriends = await getFriendIds(me._id); query.followerId = { $in: myFriends diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts index 2372f57fbe..9fb135b24d 100644 --- a/src/server/api/endpoints/users/following.ts +++ b/src/server/api/endpoints/users/following.ts @@ -5,7 +5,7 @@ import $ from 'cafy'; import User from '../../../../models/user'; import Following from '../../../../models/following'; import { pack } from '../../../../models/user'; -import getFriends from '../../common/get-friends'; +import { getFriendIds } from '../../common/get-friends'; /** * Get following users of a user @@ -52,7 +52,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // ログインしていてかつ iknow フラグがあるとき if (me && iknow) { // Get my friends - const myFriends = await getFriends(me._id); + const myFriends = await getFriendIds(me._id); query.followeeId = { $in: myFriends diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts index 2a6d36b75b..f72bb04bf1 100644 --- a/src/server/api/endpoints/users/recommendation.ts +++ b/src/server/api/endpoints/users/recommendation.ts @@ -4,7 +4,7 @@ const ms = require('ms'); import $ from 'cafy'; import User, { pack } from '../../../../models/user'; -import getFriends from '../../common/get-friends'; +import { getFriendIds } from '../../common/get-friends'; import Mute from '../../../../models/mute'; /** @@ -24,7 +24,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { if (offsetErr) return rej('invalid offset param'); // ID list of the user itself and other users who the user follows - const followingIds = await getFriends(me._id); + const followingIds = await getFriendIds(me._id); // ミュートしているユーザーを取得 const mutedUserIds = (await Mute.find({ diff --git a/src/services/following/create.ts b/src/services/following/create.ts index 375b028912..3424c55dae 100644 --- a/src/services/following/create.ts +++ b/src/services/following/create.ts @@ -13,7 +13,18 @@ export default async function(follower: IUser, followee: IUser, activity?) { const following = await Following.insert({ createdAt: new Date(), followerId: follower._id, - followeeId: followee._id + followeeId: followee._id, + stalk: true, + + // 非正規化 + _follower: { + host: follower.host, + inbox: isRemoteUser(follower) ? follower.inbox : undefined + }, + _followee: { + host: followee.host, + inbox: isRemoteUser(followee) ? followee.inbox : undefined + } }); //#region Increment following count diff --git a/src/services/note/create.ts b/src/services/note/create.ts index bdef5e09fc..32db77011f 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -124,19 +124,8 @@ export default async (user: IUser, data: { publishGlobalTimelineStream(noteObj); // Fetch all followers - const followers = await Following.aggregate([{ - $lookup: { - from: 'users', - localField: 'followerId', - foreignField: '_id', - as: 'user' - } - }, { - $match: { - followeeId: note.userId - } - }], { - _id: false + const followers = await Following.find({ + followeeId: note.userId }); if (!silent) { @@ -157,12 +146,15 @@ export default async (user: IUser, data: { deliver(user, await render(), data.renote._user.inbox); } - Promise.all(followers.map(async follower => { - follower = follower.user[0]; + Promise.all(followers.map(async following => { + const follower = following._follower; if (isLocalUser(follower)) { + // この投稿が返信かつstalkフォローでないならスキップ + if (note.replyId && !following.stalk) return; + // Publish event to followers stream - stream(follower._id, 'note', noteObj); + stream(following.followerId, 'note', noteObj); } else { // フォロワーがリモートユーザーかつ投稿者がローカルユーザーなら投稿を配信 if (isLocalUser(user)) { -- cgit v1.2.3-freya From 03c824f89348f39447a2240980b80095eeb34d3a Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 24 Apr 2018 10:08:15 +0900 Subject: wip --- src/models/user-list.ts | 40 ++++++++++++++++++++++++++++++++++++++++ src/models/user.ts | 15 +++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/models/user-list.ts (limited to 'src/models/user.ts') diff --git a/src/models/user-list.ts b/src/models/user-list.ts new file mode 100644 index 0000000000..66e2afe213 --- /dev/null +++ b/src/models/user-list.ts @@ -0,0 +1,40 @@ +import * as mongo from 'mongodb'; +import db from '../db/mongodb'; + +const UserList = db.get('userList'); +export default UserList; + +export interface IUserList { + _id: mongo.ObjectID; + createdAt: Date; + title: string; + userId: mongo.ObjectID; + userIds: mongo.ObjectID[]; +} + +/** + * UserListを物理削除します + */ +export async function deleteUserList(userList: string | mongo.ObjectID | IUserList) { + let u: IUserList; + + // Populate + if (mongo.ObjectID.prototype.isPrototypeOf(userList)) { + u = await UserList.findOne({ + _id: userList + }); + } else if (typeof userList === 'string') { + u = await UserList.findOne({ + _id: new mongo.ObjectID(userList) + }); + } else { + u = userList as IUserList; + } + + if (u == null) return; + + // このUserListを削除 + await UserList.remove({ + _id: u._id + }); +} diff --git a/src/models/user.ts b/src/models/user.ts index ca1ca28937..44f41d22fa 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -20,6 +20,7 @@ import FollowingLog, { deleteFollowingLog } from './following-log'; import FollowedLog, { deleteFollowedLog } from './followed-log'; import SwSubscription, { deleteSwSubscription } from './sw-subscription'; import Notification, { deleteNotification } from './notification'; +import UserList, { deleteUserList } from './user-list'; const User = db.get('users'); @@ -260,6 +261,20 @@ export async function deleteUser(user: string | mongo.ObjectID | IUser) { await Notification.find({ notifierId: u._id }) ).map(x => deleteNotification(x))); + // このユーザーのUserListをすべて削除 + await Promise.all(( + await UserList.find({ userId: u._id }) + ).map(x => deleteUserList(x))); + + // このユーザーの入っているすべてのUserListからこのユーザーを削除 + await Promise.all(( + await UserList.find({ userIds: u._id }) + ).map(x => + UserList.update({ _id: x._id }, { + $pull: { userIds: u._id } + }) + )); + // このユーザーを削除 await User.remove({ _id: u._id -- cgit v1.2.3-freya From fd2c45cc021e3a3ab5c5ddc6ce332e0a56d5c604 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 24 Apr 2018 14:12:19 +0900 Subject: Better japanese --- src/models/user.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/models/user.ts') diff --git a/src/models/user.ts b/src/models/user.ts index 44f41d22fa..82110a5475 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -266,7 +266,7 @@ export async function deleteUser(user: string | mongo.ObjectID | IUser) { await UserList.find({ userId: u._id }) ).map(x => deleteUserList(x))); - // このユーザーの入っているすべてのUserListからこのユーザーを削除 + // このユーザーが入っているすべてのUserListからこのユーザーを削除 await Promise.all(( await UserList.find({ userIds: u._id }) ).map(x => -- cgit v1.2.3-freya From 1a13c7e0b1cbc03064cc1710cc350875a77b24e0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 24 Apr 2018 23:34:18 +0900 Subject: wip --- package.json | 2 +- src/cafy-id.ts | 2 +- src/client/app/desktop/views/components/ui.header.account.vue | 2 +- src/models/user.ts | 7 ++++--- src/server/api/endpoints/auth/accept.ts | 2 +- src/server/api/endpoints/notes/trend.ts | 2 +- src/server/api/endpoints/users/recommendation.ts | 2 +- src/server/api/endpoints/users/search.ts | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) (limited to 'src/models/user.ts') diff --git a/package.json b/package.json index ae90d21301..7d0adc3cb6 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "autwh": "0.1.0", "bcryptjs": "2.4.3", "bootstrap-vue": "2.0.0-rc.6", - "cafy": "5.1.0", + "cafy": "6.0.0", "chai": "4.1.2", "chai-http": "4.0.0", "chalk": "2.4.0", diff --git a/src/cafy-id.ts b/src/cafy-id.ts index 1109d42d8f..3faf5cd996 100644 --- a/src/cafy-id.ts +++ b/src/cafy-id.ts @@ -19,7 +19,7 @@ export default class ID extends Query { } }; - this.pushFirstTimeValidator(v => { + this.pushValidator(v => { if (!mongo.ObjectID.prototype.isPrototypeOf(v) && isNotAnId(v)) { return new Error('must-be-an-id'); } diff --git a/src/client/app/desktop/views/components/ui.header.account.vue b/src/client/app/desktop/views/components/ui.header.account.vue index 54da97071a..61197d4017 100644 --- a/src/client/app/desktop/views/components/ui.header.account.vue +++ b/src/client/app/desktop/views/components/ui.header.account.vue @@ -17,7 +17,7 @@ %fa:star%%i18n:@favorites%%fa:angle-right%
  • -

    %fa:cog%%i18n:@lists%%fa:angle-right%

    +

    %fa:list%%i18n:@lists%%fa:angle-right%

    • diff --git a/src/models/user.ts b/src/models/user.ts index 82110a5475..0621b6e736 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -1,5 +1,6 @@ import * as mongo from 'mongodb'; import deepcopy = require('deepcopy'); +import sequential = require('promise-sequential'); import rap from '@prezzemolo/rap'; import db from '../db/mongodb'; import Note, { pack as packNote, deleteNote } from './note'; @@ -167,9 +168,9 @@ export async function deleteUser(user: string | mongo.ObjectID | IUser) { ).map(x => deleteAccessToken(x))); // このユーザーのNoteをすべて削除 - await Promise.all(( - await Note.find({ userId: u._id }) - ).map(x => deleteNote(x))); + //await sequential(( + // await Note.find({ userId: u._id }) + //).map(x => () => deleteNote(x))); // このユーザーのNoteReactionをすべて削除 await Promise.all(( diff --git a/src/server/api/endpoints/auth/accept.ts b/src/server/api/endpoints/auth/accept.ts index e0073b31e6..b6297d663d 100644 --- a/src/server/api/endpoints/auth/accept.ts +++ b/src/server/api/endpoints/auth/accept.ts @@ -3,7 +3,7 @@ */ import rndstr from 'rndstr'; const crypto = require('crypto'); -import $ from 'cafy'; import ID from '../../../../cafy-id'; +import $ from 'cafy'; import App from '../../../../models/app'; import AuthSess from '../../../../models/auth-session'; import AccessToken from '../../../../models/access-token'; diff --git a/src/server/api/endpoints/notes/trend.ts b/src/server/api/endpoints/notes/trend.ts index 6c220fc922..48ecd5b843 100644 --- a/src/server/api/endpoints/notes/trend.ts +++ b/src/server/api/endpoints/notes/trend.ts @@ -2,7 +2,7 @@ * Module dependencies */ const ms = require('ms'); -import $ from 'cafy'; import ID from '../../../../cafy-id'; +import $ from 'cafy'; import Note, { pack } from '../../../../models/note'; /** diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts index 1e8ef83432..f72bb04bf1 100644 --- a/src/server/api/endpoints/users/recommendation.ts +++ b/src/server/api/endpoints/users/recommendation.ts @@ -2,7 +2,7 @@ * Module dependencies */ const ms = require('ms'); -import $ from 'cafy'; import ID from '../../../../cafy-id'; +import $ from 'cafy'; import User, { pack } from '../../../../models/user'; import { getFriendIds } from '../../common/get-friends'; import Mute from '../../../../models/mute'; diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts index faf9b901d1..da30f47c2a 100644 --- a/src/server/api/endpoints/users/search.ts +++ b/src/server/api/endpoints/users/search.ts @@ -2,7 +2,7 @@ * Module dependencies */ import * as mongo from 'mongodb'; -import $ from 'cafy'; import ID from '../../../../cafy-id'; +import $ from 'cafy'; import User, { pack } from '../../../../models/user'; import config from '../../../../config'; const escapeRegexp = require('escape-regexp'); -- cgit v1.2.3-freya From 1625fdc2f463b001abb712ece71484aba2bc806d Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 26 Apr 2018 20:22:51 +0900 Subject: :v: --- src/models/user.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/models/user.ts') diff --git a/src/models/user.ts b/src/models/user.ts index 0621b6e736..a65fcc1878 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -37,7 +37,7 @@ export default User; type IUserBase = { _id: mongo.ObjectID; createdAt: Date; - deletedAt: Date; + deletedAt?: Date; followersCount: number; followingCount: number; name?: string; @@ -51,7 +51,6 @@ type IUserBase = { description: string; pinnedNoteId: mongo.ObjectID; isSuspended: boolean; - keywords: string[]; host: string; }; @@ -81,7 +80,7 @@ export interface ILocalUser extends IUserBase { isPro: boolean; twoFactorSecret: string; twoFactorEnabled: boolean; - twoFactorTempSecret: string; + twoFactorTempSecret?: string; clientSettings: any; settings: any; } -- cgit v1.2.3-freya From 32da569ef6ac93b1e187d9461b04c6d8a31ba0ba Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 26 Apr 2018 20:30:49 +0900 Subject: #1029 --- src/config/types.ts | 6 ++++++ src/models/user.ts | 4 ++++ src/server/api/endpoints/users/lists/push.ts | 12 +++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) (limited to 'src/models/user.ts') diff --git a/src/config/types.ts b/src/config/types.ts index b181f2c8c1..dff3f7d37c 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -40,6 +40,12 @@ export type Source = { site_key: string; secret_key: string; }; + + /** + * ゴーストアカウントのID + */ + ghost?: string; + accesslog?: string; twitter?: { consumer_key: string; diff --git a/src/models/user.ts b/src/models/user.ts index a65fcc1878..352819f618 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -458,3 +458,7 @@ function img(url) { }; } */ + +export function getGhost(): Promise { + return User.findOne({ _id: new mongo.ObjectId(config.ghost) }); +} diff --git a/src/server/api/endpoints/users/lists/push.ts b/src/server/api/endpoints/users/lists/push.ts index 467c08efd4..5d6f201524 100644 --- a/src/server/api/endpoints/users/lists/push.ts +++ b/src/server/api/endpoints/users/lists/push.ts @@ -1,7 +1,10 @@ import $ from 'cafy'; import ID from '../../../../../cafy-id'; import UserList from '../../../../../models/user-list'; -import User, { pack as packUser } from '../../../../../models/user'; +import User, { pack as packUser, isRemoteUser, getGhost } from '../../../../../models/user'; import { publishUserListStream } from '../../../../../publishers/stream'; +import ap from '../../../../../remote/activitypub/renderer'; +import renderFollow from '../../../../../remote/activitypub/renderer/follow'; +import { deliver } from '../../../../../queue'; /** * Add a user to a user list @@ -48,4 +51,11 @@ module.exports = async (params, me) => new Promise(async (res, rej) => { res(); publishUserListStream(userList._id, 'userAdded', await packUser(user)); + + // このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする + if (isRemoteUser(user)) { + const ghost = await getGhost(); + const content = ap(renderFollow(ghost, user)); + deliver(ghost, content, user.inbox); + } }); -- cgit v1.2.3-freya From 34186cf45fef455e5a5361831107c5c7a11b8d80 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 16 May 2018 17:04:39 +0900 Subject: :v: --- src/models/user.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/models/user.ts') diff --git a/src/models/user.ts b/src/models/user.ts index 352819f618..af03faf847 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -114,7 +114,7 @@ export function validatePassword(password: string): boolean { } export function isValidName(name?: string): boolean { - return name === null || (typeof name == 'string' && name.length < 30 && name.trim() != ''); + return name === null || (typeof name == 'string' && name.length < 50 && name.trim() != ''); } export function isValidDescription(description: string): boolean { -- cgit v1.2.3-freya From 66437f31c09f2257e6f683237b71e13d80fa4b50 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 16 May 2018 17:06:12 +0900 Subject: Save remote user's endpoints --- src/models/user.ts | 1 + src/remote/activitypub/models/person.ts | 4 +++- src/remote/activitypub/type.ts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src/models/user.ts') diff --git a/src/models/user.ts b/src/models/user.ts index af03faf847..108111ceca 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -87,6 +87,7 @@ export interface ILocalUser extends IUserBase { export interface IRemoteUser extends IUserBase { inbox: string; + endpoints: string[]; uri: string; url?: string; publicKey: { diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index 20e594ff1d..33280f3d89 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -101,6 +101,7 @@ export async function createPerson(value: any, resolver?: Resolver): Promise -- cgit v1.2.3-freya