Merge branch 'develop' into upstream/2025.5.0
This commit is contained in:
commit
33aee38a59
125 changed files with 3926 additions and 2148 deletions
|
|
@ -47,7 +47,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
alwaysMarkNsfw: true,
|
||||
});
|
||||
|
||||
await this.cacheService.userProfileCache.refresh(ps.userId);
|
||||
await this.cacheService.userProfileCache.delete(ps.userId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import Parser from 'rss-parser';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { parseFeed } from 'htmlparser2';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
|
||||
const rssParser = new Parser();
|
||||
import { ApiError } from '../error.js';
|
||||
import type { FeedItem } from 'domutils';
|
||||
|
||||
export const meta = {
|
||||
tags: ['meta'],
|
||||
|
|
@ -17,52 +17,32 @@ export const meta = {
|
|||
allowGet: true,
|
||||
cacheSec: 60 * 3,
|
||||
|
||||
errors: {
|
||||
fetchFailed: {
|
||||
id: '88f4356f-719d-4715-b4fc-703a10a812d2',
|
||||
code: 'FETCH_FAILED',
|
||||
message: 'Failed to fetch RSS feed',
|
||||
},
|
||||
},
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
image: {
|
||||
type: 'object',
|
||||
optional: true,
|
||||
properties: {
|
||||
link: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
optional: false,
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
optional: false,
|
||||
},
|
||||
paginationLinks: {
|
||||
type: 'object',
|
||||
id: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
updated: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
author: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
properties: {
|
||||
self: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
first: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
next: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
last: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
prev: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
link: {
|
||||
type: 'string',
|
||||
|
|
@ -94,113 +74,42 @@ export const meta = {
|
|||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
creator: {
|
||||
description: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
isoDate: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
categories: {
|
||||
media: {
|
||||
type: 'array',
|
||||
optional: true,
|
||||
optional: false,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
contentSnippet: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
enclosure: {
|
||||
type: 'object',
|
||||
optional: true,
|
||||
properties: {
|
||||
url: {
|
||||
type: 'string',
|
||||
optional: false,
|
||||
},
|
||||
length: {
|
||||
type: 'number',
|
||||
optional: true,
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
type: 'object',
|
||||
properties: {
|
||||
medium: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
lang: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
feedUrl: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
itunes: {
|
||||
type: 'object',
|
||||
optional: true,
|
||||
additionalProperties: true,
|
||||
properties: {
|
||||
image: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
owner: {
|
||||
type: 'object',
|
||||
optional: true,
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
author: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
explicit: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
categories: {
|
||||
type: 'array',
|
||||
optional: true,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
keywords: {
|
||||
type: 'array',
|
||||
optional: true,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -224,7 +133,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
constructor(
|
||||
private httpRequestService: HttpRequestService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
super(meta, paramDef, async (ps) => {
|
||||
const res = await this.httpRequestService.send(ps.url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
|
|
@ -234,8 +143,38 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
});
|
||||
|
||||
const text = await res.text();
|
||||
const feed = parseFeed(text, {
|
||||
xmlMode: true,
|
||||
});
|
||||
|
||||
return rssParser.parseString(text);
|
||||
if (!feed) {
|
||||
throw new ApiError(meta.errors.fetchFailed);
|
||||
}
|
||||
|
||||
return {
|
||||
type: feed.type,
|
||||
id: feed.id,
|
||||
title: feed.title,
|
||||
link: feed.link,
|
||||
description: feed.description,
|
||||
updated: feed.updated?.toISOString(),
|
||||
author: feed.author,
|
||||
items: feed.items
|
||||
.filter((item): item is FeedItem & { link: string, title: string } => !!item.link && !!item.title)
|
||||
.map(item => ({
|
||||
guid: item.id,
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
description: item.description,
|
||||
pubDate: item.pubDate?.toISOString(),
|
||||
media: item.media.map(media => ({
|
||||
medium: media.medium,
|
||||
url: media.url,
|
||||
type: media.type,
|
||||
lang: media.lang,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { UserFollowingService } from '@/core/UserFollowingService.js';
|
|||
import { DI } from '@/di-symbols.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['following', 'users'],
|
||||
|
|
@ -69,6 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private userEntityService: UserEntityService,
|
||||
private getterService: GetterService,
|
||||
private userFollowingService: UserFollowingService,
|
||||
private readonly cacheService: CacheService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const follower = me;
|
||||
|
|
@ -85,12 +87,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
});
|
||||
|
||||
// Check not following
|
||||
const exist = await this.followingsRepository.exists({
|
||||
where: {
|
||||
followerId: follower.id,
|
||||
followeeId: followee.id,
|
||||
},
|
||||
});
|
||||
const exist = await this.cacheService.userFollowingsCache.fetch(follower.id).then(f => f.has(followee.id));
|
||||
|
||||
if (!exist) {
|
||||
throw new ApiError(meta.errors.notFollowing);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
|||
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
|
@ -69,6 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private userEntityService: UserEntityService,
|
||||
private getterService: GetterService,
|
||||
private userFollowingService: UserFollowingService,
|
||||
private readonly cacheService: CacheService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const followee = me;
|
||||
|
|
@ -85,12 +87,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
});
|
||||
|
||||
// Check not following
|
||||
const exist = await this.followingsRepository.findOneBy({
|
||||
followerId: follower.id,
|
||||
followeeId: followee.id,
|
||||
});
|
||||
const isFollowing = await this.cacheService.userFollowingsCache.fetch(follower.id).then(f => f.has(followee.id));
|
||||
|
||||
if (exist == null) {
|
||||
if (!isFollowing) {
|
||||
throw new ApiError(meta.errors.notFollowing);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { UserFollowingService } from '@/core/UserFollowingService.js';
|
|||
import { DI } from '@/di-symbols.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['following', 'users'],
|
||||
|
|
@ -39,6 +40,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
constructor(
|
||||
@Inject(DI.followingsRepository)
|
||||
private followingsRepository: FollowingsRepository,
|
||||
private readonly cacheService: CacheService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
await this.followingsRepository.update({
|
||||
|
|
@ -48,6 +50,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
withReplies: ps.withReplies != null ? ps.withReplies : undefined,
|
||||
});
|
||||
|
||||
await this.cacheService.refreshFollowRelationsFor(me.id);
|
||||
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
|||
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
|
@ -71,6 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private userEntityService: UserEntityService,
|
||||
private getterService: GetterService,
|
||||
private userFollowingService: UserFollowingService,
|
||||
private readonly cacheService: CacheService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const follower = me;
|
||||
|
|
@ -87,10 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
});
|
||||
|
||||
// Check not following
|
||||
const exist = await this.followingsRepository.findOneBy({
|
||||
followerId: follower.id,
|
||||
followeeId: followee.id,
|
||||
});
|
||||
const exist = await this.cacheService.userFollowingsCache.fetch(follower.id).then(f => f.get(followee.id));
|
||||
|
||||
if (exist == null) {
|
||||
throw new ApiError(meta.errors.notFollowing);
|
||||
|
|
@ -103,6 +102,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
withReplies: ps.withReplies != null ? ps.withReplies : undefined,
|
||||
});
|
||||
|
||||
await this.cacheService.refreshFollowRelationsFor(follower.id);
|
||||
|
||||
return await this.userEntityService.pack(follower.id, me);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as mfm from 'mfm-js';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import ms from 'ms';
|
||||
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
|
||||
|
|
@ -617,7 +617,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
const updatedProfile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
|
||||
|
||||
this.cacheService.userProfileCache.set(user.id, updatedProfile);
|
||||
await this.cacheService.userProfileCache.set(user.id, updatedProfile);
|
||||
|
||||
// Publish meUpdated event
|
||||
this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj);
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchReplyTarget);
|
||||
} else if (isRenote(reply) && !isQuote(reply)) {
|
||||
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
||||
} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id)) {
|
||||
} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id, { me })) {
|
||||
throw new ApiError(meta.errors.cannotReplyToInvisibleNote);
|
||||
} else if (reply.visibility === 'specified' && ps.visibility !== 'specified') {
|
||||
throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility);
|
||||
|
|
|
|||
|
|
@ -402,7 +402,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchReplyTarget);
|
||||
} else if (isRenote(reply) && !isQuote(reply)) {
|
||||
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
||||
} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id)) {
|
||||
} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id, { me })) {
|
||||
throw new ApiError(meta.errors.cannotReplyToInvisibleNote);
|
||||
} else if (reply.visibility === 'specified' && ps.visibility !== 'specified') {
|
||||
throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility);
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
excludeBots: !ps.withBots,
|
||||
noteFilter: note => {
|
||||
if (note.reply && note.reply.visibility === 'followers') {
|
||||
if (!Object.hasOwn(followings, note.reply.userId) && note.reply.userId !== me.id) return false;
|
||||
if (!followings.has(note.reply.userId) && note.reply.userId !== me.id) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
excludePureRenotes: !ps.withRenotes,
|
||||
noteFilter: note => {
|
||||
if (note.reply && note.reply.visibility === 'followers') {
|
||||
if (!Object.hasOwn(followings, note.reply.userId) && note.reply.userId !== me.id) return false;
|
||||
if (!followings.has(note.reply.userId) && note.reply.userId !== me.id) return false;
|
||||
}
|
||||
if (!ps.withBots && note.user?.isBot) return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw err;
|
||||
});
|
||||
|
||||
if (!(await this.noteEntityService.isVisibleForMe(note, me?.id ?? null))) {
|
||||
if (!(await this.noteEntityService.isVisibleForMe(note, me?.id ?? null, { me }))) {
|
||||
throw new ApiError(meta.errors.cannotTranslateInvisibleNote);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
sendReadMessage: ps.sendReadMessage,
|
||||
});
|
||||
|
||||
this.pushNotificationService.refreshCache(me.id);
|
||||
await this.pushNotificationService.refreshCache(me.id);
|
||||
|
||||
return {
|
||||
state: 'subscribed' as const,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
});
|
||||
|
||||
if (me) {
|
||||
this.pushNotificationService.refreshCache(me.id);
|
||||
await this.pushNotificationService.refreshCache(me.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
sendReadMessage: swSubscription.sendReadMessage,
|
||||
});
|
||||
|
||||
this.pushNotificationService.refreshCache(me.id);
|
||||
await this.pushNotificationService.refreshCache(me.id);
|
||||
|
||||
return {
|
||||
userId: swSubscription.userId,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { FollowingEntityService } from '@/core/entities/FollowingEntityService.j
|
|||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
|
@ -89,6 +90,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private followingEntityService: FollowingEntityService,
|
||||
private queryService: QueryService,
|
||||
private roleService: RoleService,
|
||||
private readonly cacheService: CacheService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const user = await this.usersRepository.findOneBy(ps.userId != null
|
||||
|
|
@ -110,12 +112,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
if (me == null) {
|
||||
throw new ApiError(meta.errors.forbidden);
|
||||
} else if (me.id !== user.id) {
|
||||
const isFollowing = await this.followingsRepository.exists({
|
||||
where: {
|
||||
followeeId: user.id,
|
||||
followerId: me.id,
|
||||
},
|
||||
});
|
||||
const isFollowing = await this.cacheService.userFollowingsCache.fetch(me.id).then(f => f.has(user.id));
|
||||
if (!isFollowing) {
|
||||
throw new ApiError(meta.errors.forbidden);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { FollowingEntityService } from '@/core/entities/FollowingEntityService.j
|
|||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
|
@ -98,6 +99,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private followingEntityService: FollowingEntityService,
|
||||
private queryService: QueryService,
|
||||
private roleService: RoleService,
|
||||
private readonly cacheService: CacheService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const user = await this.usersRepository.findOneBy(ps.userId != null
|
||||
|
|
@ -119,12 +121,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
if (me == null) {
|
||||
throw new ApiError(meta.errors.forbidden);
|
||||
} else if (me.id !== user.id) {
|
||||
const isFollowing = await this.followingsRepository.exists({
|
||||
where: {
|
||||
followeeId: user.id,
|
||||
followerId: me.id,
|
||||
},
|
||||
});
|
||||
const isFollowing = await this.cacheService.userFollowingsCache.fetch(me.id).then(f => f.has(user.id));
|
||||
if (!isFollowing) {
|
||||
throw new ApiError(meta.errors.forbidden);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
if (ps.withReplies) redisTimelines.push(`userTimelineWithReplies:${ps.userId}`);
|
||||
if (ps.withChannelNotes) redisTimelines.push(`userTimelineWithChannel:${ps.userId}`);
|
||||
|
||||
const isFollowing = me && Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId);
|
||||
const isFollowing = me && (await this.cacheService.userFollowingsCache.fetch(me.id)).has(ps.userId);
|
||||
|
||||
const timeline = await this.fanoutTimelineEndpointService.timeline({
|
||||
untilId,
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
this.queryService.generateBlockQueryForUsers(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
|
||||
// TODO optimization: replace with exists()
|
||||
const followingQuery = this.followingsRepository.createQueryBuilder('following')
|
||||
.select('following.followeeId')
|
||||
.where('following.followerId = :followerId', { followerId: me.id });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue