fix: always respect instance mutes (#8854)

* fix: muted user query also checks instances

This way it can be ensured that the instance mute is used everywhere it
is required without checking the whole codebase again. Muted users and
muted instances should be used together anyways.

* fix lint
This commit is contained in:
Johann150 2022-06-25 07:23:59 +02:00 committed by GitHub
parent e3461f1b58
commit 5728350267
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 71 additions and 73 deletions

View file

@ -1,40 +0,0 @@
import { User } from '@/models/entities/user.js';
import { id } from '@/models/id.js';
import { UserProfiles } from '@/models/index.js';
import { SelectQueryBuilder, Brackets } from 'typeorm';
function createMutesQuery(id: string) {
return UserProfiles.createQueryBuilder('user_profile')
.select('user_profile.mutedInstances')
.where('user_profile.userId = :muterId', { muterId: id });
}
export function generateMutedInstanceQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }) {
const mutingQuery = createMutesQuery(me.id);
q
.andWhere(new Brackets(qb => { qb
.andWhere('note.userHost IS NULL')
.orWhere(`NOT((${ mutingQuery.getQuery() })::jsonb ? note.userHost)`);
}))
.andWhere(new Brackets(qb => { qb
.where(`note.replyUserHost IS NULL`)
.orWhere(`NOT ((${ mutingQuery.getQuery() })::jsonb ? note.replyUserHost)`);
}))
.andWhere(new Brackets(qb => { qb
.where(`note.renoteUserHost IS NULL`)
.orWhere(`NOT ((${ mutingQuery.getQuery() })::jsonb ? note.renoteUserHost)`);
}));
q.setParameters(mutingQuery.getParameters());
}
export function generateMutedInstanceNotificationQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }) {
const mutingQuery = createMutesQuery(me.id);
q.andWhere(new Brackets(qb => { qb
.andWhere('notifier.host IS NULL')
.orWhere(`NOT (( ${mutingQuery.getQuery()} )::jsonb ? notifier.host)`);
}));
q.setParameters(mutingQuery.getParameters());
}

View file

@ -1,6 +1,6 @@
import { User } from '@/models/entities/user.js';
import { Mutings } from '@/models/index.js';
import { SelectQueryBuilder, Brackets } from 'typeorm'; import { SelectQueryBuilder, Brackets } from 'typeorm';
import { User } from '@/models/entities/user.js';
import { Mutings, UserProfiles } from '@/models/index.js';
export function generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }, exclude?: User) { export function generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }, exclude?: User) {
const mutingQuery = Mutings.createQueryBuilder('muting') const mutingQuery = Mutings.createQueryBuilder('muting')
@ -11,21 +11,39 @@ export function generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: Use
mutingQuery.andWhere('muting.muteeId != :excludeId', { excludeId: exclude.id }); mutingQuery.andWhere('muting.muteeId != :excludeId', { excludeId: exclude.id });
} }
const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile')
.select('user_profile.mutedInstances')
.where('user_profile.userId = :muterId', { muterId: me.id });
// 投稿の作者をミュートしていない かつ // 投稿の作者をミュートしていない かつ
// 投稿の返信先の作者をミュートしていない かつ // 投稿の返信先の作者をミュートしていない かつ
// 投稿の引用元の作者をミュートしていない // 投稿の引用元の作者をミュートしていない
q q
.andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`) .andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`)
.andWhere(new Brackets(qb => { qb .andWhere(new Brackets(qb => { qb
.where(`note.replyUserId IS NULL`) .where('note.replyUserId IS NULL')
.orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`); .orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`);
})) }))
.andWhere(new Brackets(qb => { qb .andWhere(new Brackets(qb => { qb
.where(`note.renoteUserId IS NULL`) .where('note.renoteUserId IS NULL')
.orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`); .orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`);
}))
// mute instances
.andWhere(new Brackets(qb => { qb
.andWhere('note.userHost IS NULL')
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`);
}))
.andWhere(new Brackets(qb => { qb
.where('note.replyUserHost IS NULL')
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`);
}))
.andWhere(new Brackets(qb => { qb
.where('note.renoteUserHost IS NULL')
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`);
})); }));
q.setParameters(mutingQuery.getParameters()); q.setParameters(mutingQuery.getParameters());
q.setParameters(mutingInstanceQuery.getParameters());
} }
export function generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }) { export function generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }) {
@ -33,8 +51,26 @@ export function generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: {
.select('muting.muteeId') .select('muting.muteeId')
.where('muting.muterId = :muterId', { muterId: me.id }); .where('muting.muterId = :muterId', { muterId: me.id });
const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile')
.select('user_profile.mutedInstances')
.where('user_profile.userId = :muterId', { muterId: me.id });
q q
.andWhere(`user.id NOT IN (${ mutingQuery.getQuery() })`); .andWhere(`user.id NOT IN (${ mutingQuery.getQuery() })`)
// mute instances
.andWhere(new Brackets(qb => { qb
.andWhere('note.userHost IS NULL')
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`);
}))
.andWhere(new Brackets(qb => { qb
.where('note.replyUserHost IS NULL')
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`);
}))
.andWhere(new Brackets(qb => { qb
.where('note.renoteUserHost IS NULL')
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`);
}));
q.setParameters(mutingQuery.getParameters()); q.setParameters(mutingQuery.getParameters());
q.setParameters(mutingInstanceQuery.getParameters());
} }

View file

@ -1,11 +1,10 @@
import { Brackets } from 'typeorm'; import { Brackets } from 'typeorm';
import { Notifications, Followings, Mutings, Users } from '@/models/index.js'; import { Notifications, Followings, Mutings, Users, UserProfiles } from '@/models/index.js';
import { notificationTypes } from '@/types.js'; import { notificationTypes } from '@/types.js';
import read from '@/services/note/read.js'; import read from '@/services/note/read.js';
import { readNotification } from '../../common/read-notification.js'; import { readNotification } from '../../common/read-notification.js';
import define from '../../define.js'; import define from '../../define.js';
import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js';
import { generateMutedInstanceNotificationQuery } from '../../common/generate-muted-instance-query.js';
export const meta = { export const meta = {
tags: ['account', 'notifications'], tags: ['account', 'notifications'],
@ -67,6 +66,10 @@ export default define(meta, paramDef, async (ps, user) => {
.select('muting.muteeId') .select('muting.muteeId')
.where('muting.muterId = :muterId', { muterId: user.id }); .where('muting.muterId = :muterId', { muterId: user.id });
const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile')
.select('user_profile.mutedInstances')
.where('user_profile.userId = :muterId', { muterId: user.id });
const suspendedQuery = Users.createQueryBuilder('users') const suspendedQuery = Users.createQueryBuilder('users')
.select('users.id') .select('users.id')
.where('users.isSuspended = TRUE'); .where('users.isSuspended = TRUE');
@ -89,14 +92,21 @@ export default define(meta, paramDef, async (ps, user) => {
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
// muted users
query.andWhere(new Brackets(qb => { qb query.andWhere(new Brackets(qb => { qb
.where(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`) .where(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`)
.orWhere('notification.notifierId IS NULL'); .orWhere('notification.notifierId IS NULL');
})); }));
query.setParameters(mutingQuery.getParameters()); query.setParameters(mutingQuery.getParameters());
generateMutedInstanceNotificationQuery(query, user); // muted instances
query.andWhere(new Brackets(qb => { qb
.andWhere('notifier.host IS NULL')
.orWhere(`NOT (( ${mutingInstanceQuery.getQuery()} )::jsonb ? notifier.host)`);
}));
query.setParameters(mutingInstanceQuery.getParameters());
// suspended users
query.andWhere(new Brackets(qb => { qb query.andWhere(new Brackets(qb => { qb
.where(`notification.notifierId NOT IN (${ suspendedQuery.getQuery() })`) .where(`notification.notifierId NOT IN (${ suspendedQuery.getQuery() })`)
.orWhere('notification.notifierId IS NULL'); .orWhere('notification.notifierId IS NULL');

View file

@ -5,7 +5,6 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js';
import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js';
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js';
import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js';
export const meta = { export const meta = {
tags: ['notes'], tags: ['notes'],
@ -61,9 +60,10 @@ export default define(meta, paramDef, async (ps, user) => {
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
generateVisibilityQuery(query, user); generateVisibilityQuery(query, user);
if (user) generateMutedUserQuery(query, user); if (user) {
if (user) generateBlockedUserQuery(query, user); generateMutedUserQuery(query, user);
if (user) generateMutedInstanceQuery(query, user); generateBlockedUserQuery(query, user);
}
const notes = await query.take(ps.limit).getMany(); const notes = await query.take(ps.limit).getMany();

View file

@ -1,11 +1,10 @@
import { fetchMeta } from '@/misc/fetch-meta.js'; import { fetchMeta } from '@/misc/fetch-meta.js';
import { Notes, Users } from '@/models/index.js'; import { Notes } from '@/models/index.js';
import { activeUsersChart } from '@/services/chart/index.js'; import { activeUsersChart } from '@/services/chart/index.js';
import define from '../../define.js'; import define from '../../define.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js';
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js';
import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js';
import { generateRepliesQuery } from '../../common/generate-replies-query.js'; import { generateRepliesQuery } from '../../common/generate-replies-query.js';
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js';
import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
@ -76,10 +75,11 @@ export default define(meta, paramDef, async (ps, user) => {
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
generateRepliesQuery(query, user); generateRepliesQuery(query, user);
if (user) generateMutedUserQuery(query, user); if (user) {
if (user) generateMutedNoteQuery(query, user); generateMutedUserQuery(query, user);
if (user) generateBlockedUserQuery(query, user); generateMutedNoteQuery(query, user);
if (user) generateMutedInstanceQuery(query, user); generateBlockedUserQuery(query, user);
}
if (ps.withFiles) { if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\''); query.andWhere('note.fileIds != \'{}\'');

View file

@ -1,13 +1,12 @@
import { Brackets } from 'typeorm'; import { Brackets } from 'typeorm';
import { fetchMeta } from '@/misc/fetch-meta.js'; import { fetchMeta } from '@/misc/fetch-meta.js';
import { Followings, Notes, Users } from '@/models/index.js'; import { Followings, Notes } from '@/models/index.js';
import { activeUsersChart } from '@/services/chart/index.js'; import { activeUsersChart } from '@/services/chart/index.js';
import define from '../../define.js'; import define from '../../define.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js';
import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js';
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js';
import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js';
import { generateRepliesQuery } from '../../common/generate-replies-query.js'; import { generateRepliesQuery } from '../../common/generate-replies-query.js';
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js';
import { generateChannelQuery } from '../../common/generate-channel-query.js'; import { generateChannelQuery } from '../../common/generate-channel-query.js';
@ -92,7 +91,6 @@ export default define(meta, paramDef, async (ps, user) => {
generateRepliesQuery(query, user); generateRepliesQuery(query, user);
generateVisibilityQuery(query, user); generateVisibilityQuery(query, user);
generateMutedUserQuery(query, user); generateMutedUserQuery(query, user);
generateMutedInstanceQuery(query, user);
generateMutedNoteQuery(query, user); generateMutedNoteQuery(query, user);
generateBlockedUserQuery(query, user); generateBlockedUserQuery(query, user);
@ -134,9 +132,7 @@ export default define(meta, paramDef, async (ps, user) => {
const timeline = await query.take(ps.limit).getMany(); const timeline = await query.take(ps.limit).getMany();
process.nextTick(() => { process.nextTick(() => {
if (user) { activeUsersChart.read(user);
activeUsersChart.read(user);
}
}); });
return await Notes.packMany(timeline, user); return await Notes.packMany(timeline, user);

View file

@ -5,7 +5,6 @@ import define from '../../define.js';
import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js';
import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js';
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js';
import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js';
import { generateRepliesQuery } from '../../common/generate-replies-query.js'; import { generateRepliesQuery } from '../../common/generate-replies-query.js';
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js';
import { generateChannelQuery } from '../../common/generate-channel-query.js'; import { generateChannelQuery } from '../../common/generate-channel-query.js';
@ -84,7 +83,6 @@ export default define(meta, paramDef, async (ps, user) => {
generateRepliesQuery(query, user); generateRepliesQuery(query, user);
generateVisibilityQuery(query, user); generateVisibilityQuery(query, user);
generateMutedUserQuery(query, user); generateMutedUserQuery(query, user);
generateMutedInstanceQuery(query, user);
generateMutedNoteQuery(query, user); generateMutedNoteQuery(query, user);
generateBlockedUserQuery(query, user); generateBlockedUserQuery(query, user);
@ -126,9 +124,7 @@ export default define(meta, paramDef, async (ps, user) => {
const timeline = await query.take(ps.limit).getMany(); const timeline = await query.take(ps.limit).getMany();
process.nextTick(() => { process.nextTick(() => {
if (user) { activeUsersChart.read(user);
activeUsersChart.read(user);
}
}); });
return await Notes.packMany(timeline, user); return await Notes.packMany(timeline, user);

View file

@ -7,7 +7,6 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js';
import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js';
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js';
import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js';
export const meta = { export const meta = {
tags: ['users', 'notes'], tags: ['users', 'notes'],
@ -77,9 +76,10 @@ export default define(meta, paramDef, async (ps, me) => {
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
generateVisibilityQuery(query, me); generateVisibilityQuery(query, me);
if (me) generateMutedUserQuery(query, me, user); if (me) {
if (me) generateBlockedUserQuery(query, me); generateMutedUserQuery(query, me, user);
if (me) generateMutedInstanceQuery(query, me); generateBlockedUserQuery(query, me);
}
if (ps.withFiles) { if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\''); query.andWhere('note.fileIds != \'{}\'');