mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-29 09:58:36 +01:00
add role name
This commit is contained in:
parent
089682c08d
commit
4bbf0457fa
5 changed files with 132 additions and 32 deletions
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||
import { In, IsNull } from 'typeorm';
|
||||
import { Brackets, In, IsNull, SelectQueryBuilder, WhereExpressionBuilder } from 'typeorm';
|
||||
import * as Redis from 'ioredis';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
|
@ -40,6 +40,7 @@ export const fetchEmojisSortKeys = [
|
|||
'license',
|
||||
'isSensitive',
|
||||
'localOnly',
|
||||
'roleIdsThatCanBeUsedThisEmojiAsReaction',
|
||||
] as const;
|
||||
export type FetchEmojisSortKeys = typeof fetchEmojisSortKeys[number];
|
||||
export type FetchEmojisParams = {
|
||||
|
@ -57,14 +58,15 @@ export type FetchEmojisParams = {
|
|||
isSensitive?: boolean;
|
||||
localOnly?: boolean;
|
||||
hostType?: FetchEmojisHostTypes;
|
||||
roleIds?: string[];
|
||||
},
|
||||
sinceId?: string;
|
||||
untilId?: string;
|
||||
limit?: number;
|
||||
page?: number;
|
||||
sort?: {
|
||||
key : FetchEmojisSortKeys;
|
||||
direction : 'ASC' | 'DESC';
|
||||
key: FetchEmojisSortKeys;
|
||||
direction: 'ASC' | 'DESC';
|
||||
}[]
|
||||
}
|
||||
|
||||
|
@ -76,10 +78,8 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||
constructor(
|
||||
@Inject(DI.redis)
|
||||
private redisClient: Redis.Redis,
|
||||
|
||||
@Inject(DI.emojisRepository)
|
||||
private emojisRepository: EmojisRepository,
|
||||
|
||||
private utilityService: UtilityService,
|
||||
private idService: IdService,
|
||||
private emojiEntityService: EmojiEntityService,
|
||||
|
@ -441,6 +441,19 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||
|
||||
@bindThis
|
||||
public async fetchEmojis(params?: FetchEmojisParams) {
|
||||
function multipleWordsToQuery(
|
||||
query: string,
|
||||
builder: SelectQueryBuilder<MiEmoji>,
|
||||
action: (qb: WhereExpressionBuilder, word: string) => void,
|
||||
) {
|
||||
const words = query.split(/\s/);
|
||||
builder.andWhere(new Brackets((qb => {
|
||||
for (const word of words) {
|
||||
action(qb, word);
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
||||
const builder = this.emojisRepository.createQueryBuilder('emoji');
|
||||
if (params?.query) {
|
||||
const q = params.query;
|
||||
|
@ -453,41 +466,64 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||
builder.andWhere('emoji.updatedAt <= :updateAtTo', { updateAtTo: q.updatedAtTo });
|
||||
}
|
||||
if (q.name) {
|
||||
builder.andWhere('emoji.name LIKE :name', { name: `%${q.name}%` });
|
||||
multipleWordsToQuery(q.name, builder, (qb, word) => {
|
||||
qb.orWhere('emoji.name LIKE :name', { name: `%${word}%` });
|
||||
});
|
||||
}
|
||||
if (q.hostType === 'local') {
|
||||
builder.andWhere('emoji.host IS NULL');
|
||||
} else {
|
||||
if (q.host) {
|
||||
// noIndexScan
|
||||
builder.andWhere('emoji.host LIKE :host', { host: `%${q.host}%` });
|
||||
} else {
|
||||
builder.andWhere('emoji.host IS NOT NULL');
|
||||
|
||||
switch (true) {
|
||||
case q.hostType === 'local': {
|
||||
builder.andWhere('emoji.host IS NULL');
|
||||
break;
|
||||
}
|
||||
case q.hostType === 'remote': {
|
||||
if (q.host) {
|
||||
// noIndexScan
|
||||
multipleWordsToQuery(q.host, builder, (qb, word) => {
|
||||
qb.orWhere('emoji.host LIKE :host', { host: `%${word}%` });
|
||||
});
|
||||
} else {
|
||||
builder.andWhere('emoji.host IS NOT NULL');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (q.uri) {
|
||||
// noIndexScan
|
||||
builder.andWhere('emoji.uri LIKE :uri', { url: `%${q.uri}%` });
|
||||
multipleWordsToQuery(q.uri, builder, (qb, word) => {
|
||||
qb.orWhere('emoji.uri LIKE :uri', { uri: `%${word}%` });
|
||||
});
|
||||
}
|
||||
if (q.publicUrl) {
|
||||
// noIndexScan
|
||||
builder.andWhere('emoji.publicUrl LIKE :publicUrl', { publicUrl: `%${q.publicUrl}%` });
|
||||
multipleWordsToQuery(q.publicUrl, builder, (qb, word) => {
|
||||
qb.orWhere('emoji.publicUrl LIKE :publicUrl', { publicUrl: `%${word}%` });
|
||||
});
|
||||
}
|
||||
if (q.type) {
|
||||
// noIndexScan
|
||||
builder.andWhere('emoji.type LIKE :type', { type: `%${q.type}%` });
|
||||
multipleWordsToQuery(q.type, builder, (qb, word) => {
|
||||
qb.orWhere('emoji.type LIKE :type', { type: `%${word}%` });
|
||||
});
|
||||
}
|
||||
if (q.aliases) {
|
||||
// noIndexScan
|
||||
builder.andWhere('emoji.aliases ANY(:aliases)', { aliases: q.aliases });
|
||||
multipleWordsToQuery(q.aliases, builder, (qb, word) => {
|
||||
qb.orWhere('emoji.aliases LIKE :aliases', { aliases: `%${word}%` });
|
||||
});
|
||||
}
|
||||
if (q.category) {
|
||||
// noIndexScan
|
||||
builder.andWhere('emoji.category LIKE :category', { category: `%${q.category}%` });
|
||||
multipleWordsToQuery(q.category, builder, (qb, word) => {
|
||||
qb.orWhere('emoji.category LIKE :category', { category: `%${word}%` });
|
||||
});
|
||||
}
|
||||
if (q.license) {
|
||||
// noIndexScan
|
||||
builder.andWhere('emoji.license LIKE :license', { license: `%${q.license}%` });
|
||||
multipleWordsToQuery(q.license, builder, (qb, word) => {
|
||||
qb.orWhere('emoji.license LIKE :license', { license: `%${word}%` });
|
||||
});
|
||||
}
|
||||
if (q.isSensitive != null) {
|
||||
// noIndexScan
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { In } from 'typeorm';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { EmojisRepository } from '@/models/_.js';
|
||||
import type { EmojisRepository, MiRole, RolesRepository } from '@/models/_.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import type { } from '@/models/Blocking.js';
|
||||
import type { MiEmoji } from '@/models/Emoji.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
|
||||
|
@ -16,6 +16,8 @@ export class EmojiEntityService {
|
|||
constructor(
|
||||
@Inject(DI.emojisRepository)
|
||||
private emojisRepository: EmojisRepository,
|
||||
@Inject(DI.rolesRepository)
|
||||
private rolesRepository: RolesRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -75,9 +77,28 @@ export class EmojiEntityService {
|
|||
@bindThis
|
||||
public async packDetailedAdmin(
|
||||
src: MiEmoji['id'] | MiEmoji,
|
||||
hint?: {
|
||||
roles?: Map<MiRole['id'], MiRole>
|
||||
},
|
||||
): Promise<Packed<'EmojiDetailedAdmin'>> {
|
||||
const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const roles = Array.of<MiRole>();
|
||||
if (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0) {
|
||||
if (hint?.roles) {
|
||||
const hintRoles = hint.roles;
|
||||
roles.push(
|
||||
...emoji.roleIdsThatCanBeUsedThisEmojiAsReaction
|
||||
.filter(x => hintRoles.has(x))
|
||||
.map(x => hintRoles.get(x)!),
|
||||
);
|
||||
} else {
|
||||
roles.push(
|
||||
...await this.rolesRepository.findBy({ id: In(emoji.roleIdsThatCanBeUsedThisEmojiAsReaction) }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: emoji.id,
|
||||
updatedAt: emoji.updatedAt?.toISOString() ?? null,
|
||||
|
@ -92,15 +113,39 @@ export class EmojiEntityService {
|
|||
license: emoji.license,
|
||||
localOnly: emoji.localOnly,
|
||||
isSensitive: emoji.isSensitive,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: roles.map(it => ({ id: it.id, name: it.name })),
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packDetailedAdminMany(
|
||||
emojis: any[],
|
||||
public async packDetailedAdminMany(
|
||||
emojis: MiEmoji['id'][] | MiEmoji[],
|
||||
hint?: {
|
||||
roles?: Map<MiRole['id'], MiRole>
|
||||
},
|
||||
): Promise<Packed<'EmojiDetailedAdmin'>[]> {
|
||||
return Promise.all(emojis.map(x => this.packDetailedAdmin(x)));
|
||||
// IDのみの要素をピックアップし、DBからレコードを取り出して他の値を補完する
|
||||
const emojiEntities = emojis.filter(x => typeof x === 'object') as MiEmoji[];
|
||||
const emojiIdOnlyList = emojis.filter(x => typeof x === 'string') as string[];
|
||||
if (emojiIdOnlyList.length > 0) {
|
||||
emojiEntities.push(...await this.emojisRepository.findBy({ id: In(emojiIdOnlyList) }));
|
||||
}
|
||||
|
||||
// 特定ロール専用の絵文字である場合、そのロール情報をあらかじめまとめて取得しておく(pack側で都度取得も出来るが負荷が高いので)
|
||||
let hintRoles: Map<MiRole['id'], MiRole>;
|
||||
if (hint?.roles) {
|
||||
hintRoles = hint.roles;
|
||||
} else {
|
||||
const roles = Array.of<MiRole>();
|
||||
const roleIds = [...new Set(emojiEntities.flatMap(x => x.roleIdsThatCanBeUsedThisEmojiAsReaction))];
|
||||
if (roleIds.length > 0) {
|
||||
roles.push(...await this.rolesRepository.findBy({ id: In(roleIds) }));
|
||||
}
|
||||
|
||||
hintRoles = new Map(roles.map(x => [x.id, x]));
|
||||
}
|
||||
|
||||
return Promise.all(emojis.map(x => this.packDetailedAdmin(x, { roles: hintRoles })));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -170,11 +170,19 @@ export const packedEmojiDetailedAdminSchema = {
|
|||
},
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: {
|
||||
type: 'array',
|
||||
optional: false, nullable: false,
|
||||
items: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'id',
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
format: 'misskey:id',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -53,6 +53,10 @@ export const paramDef = {
|
|||
isSensitive: { type: 'boolean' },
|
||||
localOnly: { type: 'boolean' },
|
||||
hostType: { type: 'string', enum: ['local', 'remote', 'all'], default: 'all' },
|
||||
roleIds: {
|
||||
type: 'array',
|
||||
items: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
},
|
||||
},
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
|
@ -79,6 +83,7 @@ export const paramDef = {
|
|||
'license',
|
||||
'isSensitive',
|
||||
'localOnly',
|
||||
'roleIdsThatCanBeUsedThisEmojiAsReaction',
|
||||
],
|
||||
default: 'id',
|
||||
},
|
||||
|
@ -119,6 +124,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
isSensitive: ps.query.isSensitive,
|
||||
localOnly: ps.query.localOnly,
|
||||
hostType: ps.query.hostType,
|
||||
roleIds: ps.query.roleIds,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -4462,7 +4462,11 @@ export type components = {
|
|||
license: string | null;
|
||||
localOnly: boolean;
|
||||
isSensitive: boolean;
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: string[];
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: {
|
||||
/** Format: misskey:id */
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
};
|
||||
Flash: {
|
||||
/**
|
||||
|
@ -6963,6 +6967,7 @@ export type operations = {
|
|||
* @enum {string}
|
||||
*/
|
||||
hostType?: 'local' | 'remote' | 'all';
|
||||
roleIds?: string[];
|
||||
}) | null;
|
||||
/** Format: misskey:id */
|
||||
sinceId?: string;
|
||||
|
@ -6976,7 +6981,7 @@ export type operations = {
|
|||
* @default id
|
||||
* @enum {string}
|
||||
*/
|
||||
key: 'id' | 'updatedAt' | 'name' | 'host' | 'uri' | 'publicUrl' | 'type' | 'aliases' | 'category' | 'license' | 'isSensitive' | 'localOnly';
|
||||
key: 'id' | 'updatedAt' | 'name' | 'host' | 'uri' | 'publicUrl' | 'type' | 'aliases' | 'category' | 'license' | 'isSensitive' | 'localOnly' | 'roleIdsThatCanBeUsedThisEmojiAsReaction';
|
||||
/**
|
||||
* @default DESC
|
||||
* @enum {string}
|
||||
|
|
Loading…
Reference in a new issue