summaryrefslogtreecommitdiff
path: root/packages/backend/src
diff options
context:
space:
mode:
authorHazelnoot <acomputerdog@gmail.com>2025-03-30 01:58:17 -0400
committerHazelnoot <acomputerdog@gmail.com>2025-03-30 01:58:17 -0400
commit4ae26e6e185e52ac186ac10ccd4eda7718bf6e26 (patch)
tree8d556072f3876f0bfbab9d24e7cd209ca6bd091a /packages/backend/src
parentrestore following feed deck UI (diff)
parentNew Crowdin updates (#15721) (diff)
downloadsharkey-4ae26e6e185e52ac186ac10ccd4eda7718bf6e26.tar.gz
sharkey-4ae26e6e185e52ac186ac10ccd4eda7718bf6e26.tar.bz2
sharkey-4ae26e6e185e52ac186ac10ccd4eda7718bf6e26.zip
Merge branch 'misskey-develop' into merge/2025-03-24
Diffstat (limited to 'packages/backend/src')
-rw-r--r--packages/backend/src/core/QueryService.ts4
-rw-r--r--packages/backend/src/core/SearchService.ts4
-rw-r--r--packages/backend/src/core/UserSearchService.ts100
-rw-r--r--packages/backend/src/core/WebhookTestService.ts1
-rw-r--r--packages/backend/src/core/entities/UserEntityService.ts1
-rw-r--r--packages/backend/src/models/json-schema/user.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/notes.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/channels/timeline.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/clips/notes.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/children.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/notes/global-timeline.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/local-timeline.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/mentions.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/renotes.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/replies.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/search-by-tag.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/timeline.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/roles/notes.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/users/notes.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/users/recommendation.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/search.ts81
24 files changed, 147 insertions, 111 deletions
diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts
index c4feeaf971..412ab33b3f 100644
--- a/packages/backend/src/core/QueryService.ts
+++ b/packages/backend/src/core/QueryService.ts
@@ -69,7 +69,7 @@ export class QueryService {
// ここでいうBlockedは被Blockedの意
@bindThis
- public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
+ public generateBlockedUserQueryForNotes(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking')
.select('blocking.blockerId')
.where('blocking.blockeeId = :blockeeId', { blockeeId: me.id });
@@ -127,7 +127,7 @@ export class QueryService {
}
@bindThis
- public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void {
+ public generateMutedUserQueryForNotes(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void {
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
.select('muting.muteeId')
.where('muting.muterId = :muterId', { muterId: me.id });
diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts
index 4782a6c7b0..e17677bcb5 100644
--- a/packages/backend/src/core/SearchService.ts
+++ b/packages/backend/src/core/SearchService.ts
@@ -300,8 +300,8 @@ export class SearchService {
}
this.queryService.generateVisibilityQuery(query, me);
- if (me) this.queryService.generateMutedUserQuery(query, me);
- if (me) this.queryService.generateBlockedUserQuery(query, me);
+ if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
+ if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
return await query.limit(pagination.limit).getMany();
}
diff --git a/packages/backend/src/core/UserSearchService.ts b/packages/backend/src/core/UserSearchService.ts
index 0d03cf6ee0..4be7bd9bdb 100644
--- a/packages/backend/src/core/UserSearchService.ts
+++ b/packages/backend/src/core/UserSearchService.ts
@@ -6,7 +6,7 @@
import { Inject, Injectable } from '@nestjs/common';
import { Brackets, SelectQueryBuilder } from 'typeorm';
import { DI } from '@/di-symbols.js';
-import { type FollowingsRepository, MiUser, type UsersRepository } from '@/models/_.js';
+import { type FollowingsRepository, MiUser, type MutingsRepository, type UserProfilesRepository, type UsersRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
import type { Config } from '@/config.js';
@@ -22,10 +22,19 @@ export class UserSearchService {
constructor(
@Inject(DI.config)
private config: Config,
+
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
+
+ @Inject(DI.userProfilesRepository)
+ private userProfilesRepository: UserProfilesRepository,
+
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,
+
+ @Inject(DI.mutingsRepository)
+ private mutingsRepository: MutingsRepository,
+
private userEntityService: UserEntityService,
) {
}
@@ -58,7 +67,7 @@ export class UserSearchService {
* @see {@link UserSearchService#buildSearchUserNoLoginQueries}
*/
@bindThis
- public async search(
+ public async searchByUsernameAndHost(
params: {
username?: string | null,
host?: string | null,
@@ -202,4 +211,91 @@ export class UserSearchService {
return userQuery;
}
+
+ @bindThis
+ public async search(query: string, meId: MiUser['id'] | null, options: Partial<{
+ limit: number;
+ offset: number;
+ origin: 'local' | 'remote' | 'combined';
+ }> = {}) {
+ const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日
+
+ const isUsername = query.startsWith('@') && !query.includes(' ') && query.indexOf('@', 1) === -1;
+
+ let users: MiUser[] = [];
+
+ const mutingQuery = meId == null ? null : this.mutingsRepository.createQueryBuilder('muting')
+ .select('muting.muteeId')
+ .where('muting.muterId = :muterId', { muterId: meId });
+
+ const nameQuery = this.usersRepository.createQueryBuilder('user')
+ .where(new Brackets(qb => {
+ qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(query) + '%' });
+
+ if (isUsername) {
+ qb.orWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(query.replace('@', '').toLowerCase()) + '%' });
+ } else if (this.userEntityService.validateLocalUsername(query)) { // Also search username if it qualifies as username
+ qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(query.toLowerCase()) + '%' });
+ }
+ }))
+ .andWhere(new Brackets(qb => {
+ qb
+ .where('user.updatedAt IS NULL')
+ .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
+ }))
+ .andWhere('user.isSuspended = FALSE');
+
+ if (mutingQuery) {
+ nameQuery.andWhere(`user.id NOT IN (${mutingQuery.getQuery()})`);
+ nameQuery.setParameters(mutingQuery.getParameters());
+ }
+
+ if (options.origin === 'local') {
+ nameQuery.andWhere('user.host IS NULL');
+ } else if (options.origin === 'remote') {
+ nameQuery.andWhere('user.host IS NOT NULL');
+ }
+
+ users = await nameQuery
+ .orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
+ .limit(options.limit)
+ .offset(options.offset)
+ .getMany();
+
+ if (users.length < (options.limit ?? 30)) {
+ const profQuery = this.userProfilesRepository.createQueryBuilder('prof')
+ .select('prof.userId')
+ .where('prof.description ILIKE :query', { query: '%' + sqlLikeEscape(query) + '%' });
+
+ if (mutingQuery) {
+ profQuery.andWhere(`prof.userId NOT IN (${mutingQuery.getQuery()})`);
+ profQuery.setParameters(mutingQuery.getParameters());
+ }
+
+ if (options.origin === 'local') {
+ profQuery.andWhere('prof.userHost IS NULL');
+ } else if (options.origin === 'remote') {
+ profQuery.andWhere('prof.userHost IS NOT NULL');
+ }
+
+ const userQuery = this.usersRepository.createQueryBuilder('user')
+ .where(`user.id IN (${ profQuery.getQuery() })`)
+ .andWhere(new Brackets(qb => {
+ qb
+ .where('user.updatedAt IS NULL')
+ .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
+ }))
+ .andWhere('user.isSuspended = FALSE')
+ .setParameters(profQuery.getParameters());
+
+ users = users.concat(await userQuery
+ .orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
+ .limit(options.limit)
+ .offset(options.offset)
+ .getMany(),
+ );
+ }
+
+ return users;
+ }
}
diff --git a/packages/backend/src/core/WebhookTestService.ts b/packages/backend/src/core/WebhookTestService.ts
index 8d2e181aa5..3e89595155 100644
--- a/packages/backend/src/core/WebhookTestService.ts
+++ b/packages/backend/src/core/WebhookTestService.ts
@@ -482,6 +482,7 @@ export class WebhookTestService {
followersVisibility: 'public',
followingVisibility: 'public',
chatScope: 'mutual',
+ canChat: true,
twoFactorEnabled: false,
usePasswordLessLogin: false,
securityKeys: false,
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index cfcf10a0f4..2dc2c50e4a 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -664,6 +664,7 @@ export class UserEntityService implements OnModuleInit {
followersVisibility: profile!.followersVisibility,
followingVisibility: profile!.followingVisibility,
chatScope: user.chatScope,
+ canChat: this.roleService.getUserPolicies(user.id).then(r => r.canChat),
roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).sort((a, b) => b.displayOrder - a.displayOrder).map(role => ({
id: role.id,
name: role.name,
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index e6f85afcce..964a179244 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -423,6 +423,10 @@ export const packedUserDetailedNotMeOnlySchema = {
nullable: false, optional: false,
enum: ['everyone', 'following', 'followers', 'mutual', 'none'],
},
+ canChat: {
+ type: 'boolean',
+ nullable: false, optional: false,
+ },
roles: {
type: 'array',
nullable: false, optional: false,
diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts
index 8ad2315209..00448297fb 100644
--- a/packages/backend/src/server/api/endpoints/antennas/notes.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts
@@ -115,8 +115,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
- this.queryService.generateMutedUserQuery(query, me);
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
const notes = await query.getMany();
if (sinceId != null && untilId == null) {
diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts
index 0bd01d712c..5fc0ae00b2 100644
--- a/packages/backend/src/server/api/endpoints/channels/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts
@@ -138,8 +138,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('note.channel', 'channel');
if (me) {
- this.queryService.generateMutedUserQuery(query, me);
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
}
if (ps.withRenotes === false) {
diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts
index 6175c4d0e5..69ff45a1c6 100644
--- a/packages/backend/src/server/api/endpoints/clips/notes.ts
+++ b/packages/backend/src/server/api/endpoints/clips/notes.ts
@@ -93,8 +93,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (me) {
this.queryService.generateVisibilityQuery(query, me);
- this.queryService.generateMutedUserQuery(query, me);
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
}
const notes = await query
diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts
index e69ba9be7e..c97a0c0bc7 100644
--- a/packages/backend/src/server/api/endpoints/notes/children.ts
+++ b/packages/backend/src/server/api/endpoints/notes/children.ts
@@ -80,7 +80,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
this.queryService.generateVisibilityQuery(query, me);
if (me) {
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
}
const notes = await query.limit(ps.limit).getMany();
diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
index 0f2592bd78..efecf0b3c1 100644
--- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
@@ -94,8 +94,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
if (me) {
- this.queryService.generateMutedUserQuery(query, me);
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
}
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index 3c66154e19..b0e3327411 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -254,8 +254,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
this.queryService.generateVisibilityQuery(query, me);
- this.queryService.generateMutedUserQuery(query, me);
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
if (ps.includeMyRenotes === false) {
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 1f986079c2..864592ed9b 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -167,8 +167,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
- if (me) this.queryService.generateMutedUserQuery(query, me);
- if (me) this.queryService.generateBlockedUserQuery(query, me);
+ if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
+ if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
if (ps.withFiles) {
diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts
index 30baa0edf8..bfcd120f49 100644
--- a/packages/backend/src/server/api/endpoints/notes/mentions.ts
+++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts
@@ -78,9 +78,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
- this.queryService.generateMutedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me);
this.queryService.generateMutedNoteThreadQuery(query, me);
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
if (ps.visibility) {
query.andWhere('note.visibility = :visibility', { visibility: ps.visibility });
diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts
index 15f114266a..01f09433a9 100644
--- a/packages/backend/src/server/api/endpoints/notes/renotes.ts
+++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts
@@ -91,8 +91,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
this.queryService.generateVisibilityQuery(query, me);
- if (me) this.queryService.generateMutedUserQuery(query, me);
- if (me) this.queryService.generateBlockedUserQuery(query, me);
+ if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
+ if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
const renotes = await query.limit(ps.limit).getMany();
diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts
index 3f0a8157c4..f04c9b0ec4 100644
--- a/packages/backend/src/server/api/endpoints/notes/replies.ts
+++ b/packages/backend/src/server/api/endpoints/notes/replies.ts
@@ -62,8 +62,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
- if (me) this.queryService.generateMutedUserQuery(query, me);
- if (me) this.queryService.generateBlockedUserQuery(query, me);
+ if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
+ if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
const timeline = await query.limit(ps.limit).getMany();
diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
index 6bba7bf37e..50711bc2bd 100644
--- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
+++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
@@ -97,8 +97,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (!this.serverSettings.enableBotTrending) query.andWhere('user.isBot = FALSE');
this.queryService.generateVisibilityQuery(query, me);
- if (me) this.queryService.generateMutedUserQuery(query, me);
- if (me) this.queryService.generateBlockedUserQuery(query, me);
+ if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
+ if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
const [
followings,
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index 5a46f66f9e..9f19117426 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -209,8 +209,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}));
this.queryService.generateVisibilityQuery(query, me);
- this.queryService.generateMutedUserQuery(query, me);
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
if (ps.includeMyRenotes === false) {
diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
index 55cda135e2..4c37edfdec 100644
--- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -190,8 +190,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}));
this.queryService.generateVisibilityQuery(query, me);
- this.queryService.generateMutedUserQuery(query, me);
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
if (ps.includeMyRenotes === false) {
diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts
index b3c73e0391..dd40e49d3d 100644
--- a/packages/backend/src/server/api/endpoints/roles/notes.ts
+++ b/packages/backend/src/server/api/endpoints/roles/notes.ts
@@ -108,8 +108,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
- this.queryService.generateMutedUserQuery(query, me);
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
const notes = await query.getMany();
notes.sort((a, b) => a.id > b.id ? -1 : 1);
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index 6416e43ff1..a4eee544f0 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -217,8 +217,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
this.queryService.generateVisibilityQuery(query, me);
if (me) {
- this.queryService.generateMutedUserQuery(query, me, { id: ps.userId });
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQueryForNotes(query, me, { id: ps.userId });
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
}
if (ps.withFiles) {
diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts
index 46af1f38ac..642d788459 100644
--- a/packages/backend/src/server/api/endpoints/users/recommendation.ts
+++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts
@@ -69,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
this.queryService.generateMutedUserQueryForUsers(query, me);
this.queryService.generateBlockQueryForUsers(query, me);
- this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateBlockedUserQueryForNotes(query, me);
const followingQuery = this.followingsRepository.createQueryBuilder('following')
.select('following.followeeId')
diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
index fda56ea6fe..f1a0fc5ddb 100644
--- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
+++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
@@ -52,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private userSearchService: UserSearchService,
) {
super(meta, paramDef, (ps, me) => {
- return this.userSearchService.search({
+ return this.userSearchService.searchByUsernameAndHost({
username: ps.username,
host: ps.host,
}, {
diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts
index 2d17c91e1d..138cef2ec5 100644
--- a/packages/backend/src/server/api/endpoints/users/search.ts
+++ b/packages/backend/src/server/api/endpoints/users/search.ts
@@ -3,14 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Brackets } from 'typeorm';
import { Inject, Injectable } from '@nestjs/common';
-import type { UsersRepository, UserProfilesRepository } from '@/models/_.js';
-import type { MiUser } from '@/models/User.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DI } from '@/di-symbols.js';
-import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
+import { UserSearchService } from '@/core/UserSearchService.js';
export const meta = {
tags: ['users'],
@@ -51,79 +48,15 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
- @Inject(DI.usersRepository)
- private usersRepository: UsersRepository,
-
- @Inject(DI.userProfilesRepository)
- private userProfilesRepository: UserProfilesRepository,
-
private userEntityService: UserEntityService,
+ private userSearchService: UserSearchService,
) {
super(meta, paramDef, async (ps, me) => {
- const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日
-
- ps.query = ps.query.trim();
- const isUsername = ps.query.startsWith('@') && !ps.query.includes(' ') && ps.query.indexOf('@', 1) === -1;
-
- let users: MiUser[] = [];
-
- const nameQuery = this.usersRepository.createQueryBuilder('user')
- .where(new Brackets(qb => {
- qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' });
-
- if (isUsername) {
- qb.orWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.query.replace('@', '').toLowerCase()) + '%' });
- } else if (this.userEntityService.validateLocalUsername(ps.query)) { // Also search username if it qualifies as username
- qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(ps.query.toLowerCase()) + '%' });
- }
- }))
- .andWhere(new Brackets(qb => {
- qb
- .where('user.updatedAt IS NULL')
- .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
- }))
- .andWhere('user.isSuspended = FALSE');
-
- if (ps.origin === 'local') {
- nameQuery.andWhere('user.host IS NULL');
- } else if (ps.origin === 'remote') {
- nameQuery.andWhere('user.host IS NOT NULL');
- }
-
- users = await nameQuery
- .orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
- .limit(ps.limit)
- .offset(ps.offset)
- .getMany();
-
- if (users.length < ps.limit) {
- const profQuery = this.userProfilesRepository.createQueryBuilder('prof')
- .select('prof.userId')
- .where('prof.description ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' });
-
- if (ps.origin === 'local') {
- profQuery.andWhere('prof.userHost IS NULL');
- } else if (ps.origin === 'remote') {
- profQuery.andWhere('prof.userHost IS NOT NULL');
- }
-
- const query = this.usersRepository.createQueryBuilder('user')
- .where(`user.id IN (${ profQuery.getQuery() })`)
- .andWhere(new Brackets(qb => {
- qb
- .where('user.updatedAt IS NULL')
- .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
- }))
- .andWhere('user.isSuspended = FALSE')
- .setParameters(profQuery.getParameters());
-
- users = users.concat(await query
- .orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
- .limit(ps.limit)
- .offset(ps.offset)
- .getMany(),
- );
- }
+ const users = await this.userSearchService.search(ps.query.trim(), me?.id ?? null, {
+ offset: ps.offset,
+ limit: ps.limit,
+ origin: ps.origin,
+ });
return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' });
});