parent
1d7e0293a8
commit
10e526ba56
10 changed files with 27 additions and 15 deletions
|
@ -99,6 +99,7 @@ You should also include the user name that made the change.
|
||||||
- Server: アンテナの作成数上限を追加 @syuilo
|
- Server: アンテナの作成数上限を追加 @syuilo
|
||||||
- Server: pages/likeのエラーIDが重複しているのを修正 @syuilo
|
- Server: pages/likeのエラーIDが重複しているのを修正 @syuilo
|
||||||
- Server: pages/updateのパラメータによってはsummaryの値が更新されないのを修正 @syuilo
|
- Server: pages/updateのパラメータによってはsummaryの値が更新されないのを修正 @syuilo
|
||||||
|
- Server: Escape SQL LIKE @mei23
|
||||||
- Client: case insensitive emoji search @saschanaz
|
- Client: case insensitive emoji search @saschanaz
|
||||||
- Client: InAppウィンドウが操作できなくなることがあるのを修正 @tamaina
|
- Client: InAppウィンドウが操作できなくなることがあるのを修正 @tamaina
|
||||||
- Client: use proxied image for instance icon @syuilo
|
- Client: use proxied image for instance icon @syuilo
|
||||||
|
|
3
packages/backend/src/misc/sql-like-escape.ts
Normal file
3
packages/backend/src/misc/sql-like-escape.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export function sqlLikeEscape(s: string) {
|
||||||
|
return s.replace(/([%_])/g, '\\$1');
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import { QueryService } from '@/core/QueryService.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { sqlLikeEscape } from '@/misc/sql-like-escape';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
@ -92,7 +93,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.query) {
|
if (ps.query) {
|
||||||
q.andWhere('emoji.name like :query', { query: '%' + ps.query + '%' });
|
q.andWhere('emoji.name like :query', { query: '%' + sqlLikeEscape(ps.query) + '%' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const emojis = await q
|
const emojis = await q
|
||||||
|
|
|
@ -5,6 +5,7 @@ import type { Emoji } from '@/models/entities/Emoji.js';
|
||||||
import { QueryService } from '@/core/QueryService.js';
|
import { QueryService } from '@/core/QueryService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||||
|
//import { sqlLikeEscape } from '@/misc/sql-like-escape';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
@ -82,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
let emojis: Emoji[];
|
let emojis: Emoji[];
|
||||||
|
|
||||||
if (ps.query) {
|
if (ps.query) {
|
||||||
//q.andWhere('emoji.name ILIKE :q', { q: `%${ps.query}%` });
|
//q.andWhere('emoji.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` });
|
||||||
//const emojis = await q.take(ps.limit).getMany();
|
//const emojis = await q.take(ps.limit).getMany();
|
||||||
|
|
||||||
emojis = await q.getMany();
|
emojis = await q.getMany();
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { UsersRepository } from '@/models/index.js';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
|
import { sqlLikeEscape } from '@/misc/sql-like-escape';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
@ -68,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.username) {
|
if (ps.username) {
|
||||||
query.andWhere('user.usernameLower like :username', { username: ps.username.toLowerCase() + '%' });
|
query.andWhere('user.usernameLower like :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.hostname) {
|
if (ps.hostname) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import type { InstancesRepository } from '@/models/index.js';
|
||||||
import { InstanceEntityService } from '@/core/entities/InstanceEntityService.js';
|
import { InstanceEntityService } from '@/core/entities/InstanceEntityService.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { sqlLikeEscape } from '@/misc/sql-like-escape';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['federation'],
|
tags: ['federation'],
|
||||||
|
@ -120,7 +121,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.host) {
|
if (ps.host) {
|
||||||
query.andWhere('instance.host like :host', { host: '%' + ps.host.toLowerCase() + '%' });
|
query.andWhere('instance.host like :host', { host: '%' + sqlLikeEscape(ps.host.toLowerCase()) + '%' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const instances = await query.take(ps.limit).skip(ps.offset).getMany();
|
const instances = await query.take(ps.limit).skip(ps.offset).getMany();
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { HashtagsRepository } from '@/models/index.js';
|
import type { HashtagsRepository } from '@/models/index.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { sqlLikeEscape } from '@/misc/sql-like-escape';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['hashtags'],
|
tags: ['hashtags'],
|
||||||
|
@ -37,7 +38,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const hashtags = await this.hashtagsRepository.createQueryBuilder('tag')
|
const hashtags = await this.hashtagsRepository.createQueryBuilder('tag')
|
||||||
.where('tag.name like :q', { q: ps.query.toLowerCase() + '%' })
|
.where('tag.name like :q', { q: sqlLikeEscape(ps.query.toLowerCase()) + '%' })
|
||||||
.orderBy('tag.count', 'DESC')
|
.orderBy('tag.count', 'DESC')
|
||||||
.groupBy('tag.id')
|
.groupBy('tag.id')
|
||||||
.take(ps.limit)
|
.take(ps.limit)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { QueryService } from '@/core/QueryService.js';
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { sqlLikeEscape } from '@/misc/sql-like-escape';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['notes'],
|
tags: ['notes'],
|
||||||
|
@ -70,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
query
|
query
|
||||||
.andWhere('note.text ILIKE :q', { q: `%${ps.query}%` })
|
.andWhere('note.text ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` })
|
||||||
.innerJoinAndSelect('note.user', 'user')
|
.innerJoinAndSelect('note.user', 'user')
|
||||||
.leftJoinAndSelect('user.avatar', 'avatar')
|
.leftJoinAndSelect('user.avatar', 'avatar')
|
||||||
.leftJoinAndSelect('user.banner', 'banner')
|
.leftJoinAndSelect('user.banner', 'banner')
|
||||||
|
|
|
@ -6,6 +6,7 @@ import type { User } from '@/models/entities/User.js';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { sqlLikeEscape } from '@/misc/sql-like-escape';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['users'],
|
tags: ['users'],
|
||||||
|
@ -59,10 +60,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
if (ps.host) {
|
if (ps.host) {
|
||||||
const q = this.usersRepository.createQueryBuilder('user')
|
const q = this.usersRepository.createQueryBuilder('user')
|
||||||
.where('user.isSuspended = FALSE')
|
.where('user.isSuspended = FALSE')
|
||||||
.andWhere('user.host LIKE :host', { host: ps.host.toLowerCase() + '%' });
|
.andWhere('user.host LIKE :host', { host: sqlLikeEscape(ps.host.toLowerCase()) + '%' });
|
||||||
|
|
||||||
if (ps.username) {
|
if (ps.username) {
|
||||||
q.andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' });
|
q.andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' });
|
||||||
}
|
}
|
||||||
|
|
||||||
q.andWhere('user.updatedAt IS NOT NULL');
|
q.andWhere('user.updatedAt IS NOT NULL');
|
||||||
|
@ -83,7 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
.where(`user.id IN (${ followingQuery.getQuery() })`)
|
.where(`user.id IN (${ followingQuery.getQuery() })`)
|
||||||
.andWhere('user.id != :meId', { meId: me.id })
|
.andWhere('user.id != :meId', { meId: me.id })
|
||||||
.andWhere('user.isSuspended = FALSE')
|
.andWhere('user.isSuspended = FALSE')
|
||||||
.andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' })
|
.andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' })
|
||||||
.andWhere(new Brackets(qb => { qb
|
.andWhere(new Brackets(qb => { qb
|
||||||
.where('user.updatedAt IS NULL')
|
.where('user.updatedAt IS NULL')
|
||||||
.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
|
.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
|
||||||
|
@ -101,7 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
.where(`user.id NOT IN (${ followingQuery.getQuery() })`)
|
.where(`user.id NOT IN (${ followingQuery.getQuery() })`)
|
||||||
.andWhere('user.id != :meId', { meId: me.id })
|
.andWhere('user.id != :meId', { meId: me.id })
|
||||||
.andWhere('user.isSuspended = FALSE')
|
.andWhere('user.isSuspended = FALSE')
|
||||||
.andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' })
|
.andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' })
|
||||||
.andWhere('user.updatedAt IS NOT NULL');
|
.andWhere('user.updatedAt IS NOT NULL');
|
||||||
|
|
||||||
otherQuery.setParameters(followingQuery.getParameters());
|
otherQuery.setParameters(followingQuery.getParameters());
|
||||||
|
@ -116,7 +117,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
} else {
|
} else {
|
||||||
users = await this.usersRepository.createQueryBuilder('user')
|
users = await this.usersRepository.createQueryBuilder('user')
|
||||||
.where('user.isSuspended = FALSE')
|
.where('user.isSuspended = FALSE')
|
||||||
.andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' })
|
.andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' })
|
||||||
.andWhere('user.updatedAt IS NOT NULL')
|
.andWhere('user.updatedAt IS NOT NULL')
|
||||||
.orderBy('user.updatedAt', 'DESC')
|
.orderBy('user.updatedAt', 'DESC')
|
||||||
.take(ps.limit - users.length)
|
.take(ps.limit - users.length)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import type { User } from '@/models/entities/User.js';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { sqlLikeEscape } from '@/misc/sql-like-escape';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['users'],
|
tags: ['users'],
|
||||||
|
@ -57,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
|
||||||
if (isUsername) {
|
if (isUsername) {
|
||||||
const usernameQuery = this.usersRepository.createQueryBuilder('user')
|
const usernameQuery = this.usersRepository.createQueryBuilder('user')
|
||||||
.where('user.usernameLower LIKE :username', { username: ps.query.replace('@', '').toLowerCase() + '%' })
|
.where('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.query.replace('@', '').toLowerCase()) + '%' })
|
||||||
.andWhere(new Brackets(qb => { qb
|
.andWhere(new Brackets(qb => { qb
|
||||||
.where('user.updatedAt IS NULL')
|
.where('user.updatedAt IS NULL')
|
||||||
.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
|
.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
|
||||||
|
@ -78,11 +79,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
} else {
|
} else {
|
||||||
const nameQuery = this.usersRepository.createQueryBuilder('user')
|
const nameQuery = this.usersRepository.createQueryBuilder('user')
|
||||||
.where(new Brackets(qb => {
|
.where(new Brackets(qb => {
|
||||||
qb.where('user.name ILIKE :query', { query: '%' + ps.query + '%' });
|
qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' });
|
||||||
|
|
||||||
// Also search username if it qualifies as username
|
// Also search username if it qualifies as username
|
||||||
if (this.userEntityService.validateLocalUsername(ps.query)) {
|
if (this.userEntityService.validateLocalUsername(ps.query)) {
|
||||||
qb.orWhere('user.usernameLower LIKE :username', { username: '%' + ps.query.toLowerCase() + '%' });
|
qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(ps.query.toLowerCase()) + '%' });
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.andWhere(new Brackets(qb => { qb
|
.andWhere(new Brackets(qb => { qb
|
||||||
|
@ -106,7 +107,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
if (users.length < ps.limit) {
|
if (users.length < ps.limit) {
|
||||||
const profQuery = this.userProfilesRepository.createQueryBuilder('prof')
|
const profQuery = this.userProfilesRepository.createQueryBuilder('prof')
|
||||||
.select('prof.userId')
|
.select('prof.userId')
|
||||||
.where('prof.description ILIKE :query', { query: '%' + ps.query + '%' });
|
.where('prof.description ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' });
|
||||||
|
|
||||||
if (ps.origin === 'local') {
|
if (ps.origin === 'local') {
|
||||||
profQuery.andWhere('prof.userHost IS NULL');
|
profQuery.andWhere('prof.userHost IS NULL');
|
||||||
|
|
Loading…
Reference in a new issue