add role name
This commit is contained in:
parent
089682c08d
commit
4bbf0457fa
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
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 * as Redis from 'ioredis';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
@ -40,6 +40,7 @@ export const fetchEmojisSortKeys = [
|
||||||
'license',
|
'license',
|
||||||
'isSensitive',
|
'isSensitive',
|
||||||
'localOnly',
|
'localOnly',
|
||||||
|
'roleIdsThatCanBeUsedThisEmojiAsReaction',
|
||||||
] as const;
|
] as const;
|
||||||
export type FetchEmojisSortKeys = typeof fetchEmojisSortKeys[number];
|
export type FetchEmojisSortKeys = typeof fetchEmojisSortKeys[number];
|
||||||
export type FetchEmojisParams = {
|
export type FetchEmojisParams = {
|
||||||
|
@ -57,14 +58,15 @@ export type FetchEmojisParams = {
|
||||||
isSensitive?: boolean;
|
isSensitive?: boolean;
|
||||||
localOnly?: boolean;
|
localOnly?: boolean;
|
||||||
hostType?: FetchEmojisHostTypes;
|
hostType?: FetchEmojisHostTypes;
|
||||||
|
roleIds?: string[];
|
||||||
},
|
},
|
||||||
sinceId?: string;
|
sinceId?: string;
|
||||||
untilId?: string;
|
untilId?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
page?: number;
|
page?: number;
|
||||||
sort?: {
|
sort?: {
|
||||||
key : FetchEmojisSortKeys;
|
key: FetchEmojisSortKeys;
|
||||||
direction : 'ASC' | 'DESC';
|
direction: 'ASC' | 'DESC';
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,10 +78,8 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.redis)
|
@Inject(DI.redis)
|
||||||
private redisClient: Redis.Redis,
|
private redisClient: Redis.Redis,
|
||||||
|
|
||||||
@Inject(DI.emojisRepository)
|
@Inject(DI.emojisRepository)
|
||||||
private emojisRepository: EmojisRepository,
|
private emojisRepository: EmojisRepository,
|
||||||
|
|
||||||
private utilityService: UtilityService,
|
private utilityService: UtilityService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private emojiEntityService: EmojiEntityService,
|
private emojiEntityService: EmojiEntityService,
|
||||||
|
@ -441,6 +441,19 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetchEmojis(params?: FetchEmojisParams) {
|
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');
|
const builder = this.emojisRepository.createQueryBuilder('emoji');
|
||||||
if (params?.query) {
|
if (params?.query) {
|
||||||
const q = params.query;
|
const q = params.query;
|
||||||
|
@ -453,41 +466,64 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
builder.andWhere('emoji.updatedAt <= :updateAtTo', { updateAtTo: q.updatedAtTo });
|
builder.andWhere('emoji.updatedAt <= :updateAtTo', { updateAtTo: q.updatedAtTo });
|
||||||
}
|
}
|
||||||
if (q.name) {
|
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');
|
switch (true) {
|
||||||
} else {
|
case q.hostType === 'local': {
|
||||||
if (q.host) {
|
builder.andWhere('emoji.host IS NULL');
|
||||||
// noIndexScan
|
break;
|
||||||
builder.andWhere('emoji.host LIKE :host', { host: `%${q.host}%` });
|
}
|
||||||
} else {
|
case q.hostType === 'remote': {
|
||||||
builder.andWhere('emoji.host IS NOT NULL');
|
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) {
|
if (q.uri) {
|
||||||
// noIndexScan
|
// 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) {
|
if (q.publicUrl) {
|
||||||
// noIndexScan
|
// 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) {
|
if (q.type) {
|
||||||
// noIndexScan
|
// 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) {
|
if (q.aliases) {
|
||||||
// noIndexScan
|
// 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) {
|
if (q.category) {
|
||||||
// noIndexScan
|
// 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) {
|
if (q.license) {
|
||||||
// noIndexScan
|
// 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) {
|
if (q.isSensitive != null) {
|
||||||
// noIndexScan
|
// noIndexScan
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { In } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
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 { Packed } from '@/misc/json-schema.js';
|
||||||
import type { } from '@/models/Blocking.js';
|
|
||||||
import type { MiEmoji } from '@/models/Emoji.js';
|
import type { MiEmoji } from '@/models/Emoji.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ export class EmojiEntityService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.emojisRepository)
|
@Inject(DI.emojisRepository)
|
||||||
private emojisRepository: EmojisRepository,
|
private emojisRepository: EmojisRepository,
|
||||||
|
@Inject(DI.rolesRepository)
|
||||||
|
private rolesRepository: RolesRepository,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,9 +77,28 @@ export class EmojiEntityService {
|
||||||
@bindThis
|
@bindThis
|
||||||
public async packDetailedAdmin(
|
public async packDetailedAdmin(
|
||||||
src: MiEmoji['id'] | MiEmoji,
|
src: MiEmoji['id'] | MiEmoji,
|
||||||
|
hint?: {
|
||||||
|
roles?: Map<MiRole['id'], MiRole>
|
||||||
|
},
|
||||||
): Promise<Packed<'EmojiDetailedAdmin'>> {
|
): Promise<Packed<'EmojiDetailedAdmin'>> {
|
||||||
const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src });
|
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 {
|
return {
|
||||||
id: emoji.id,
|
id: emoji.id,
|
||||||
updatedAt: emoji.updatedAt?.toISOString() ?? null,
|
updatedAt: emoji.updatedAt?.toISOString() ?? null,
|
||||||
|
@ -92,15 +113,39 @@ export class EmojiEntityService {
|
||||||
license: emoji.license,
|
license: emoji.license,
|
||||||
localOnly: emoji.localOnly,
|
localOnly: emoji.localOnly,
|
||||||
isSensitive: emoji.isSensitive,
|
isSensitive: emoji.isSensitive,
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
roleIdsThatCanBeUsedThisEmojiAsReaction: roles.map(it => ({ id: it.id, name: it.name })),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public packDetailedAdminMany(
|
public async packDetailedAdminMany(
|
||||||
emojis: any[],
|
emojis: MiEmoji['id'][] | MiEmoji[],
|
||||||
|
hint?: {
|
||||||
|
roles?: Map<MiRole['id'], MiRole>
|
||||||
|
},
|
||||||
): Promise<Packed<'EmojiDetailedAdmin'>[]> {
|
): 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: {
|
roleIdsThatCanBeUsedThisEmojiAsReaction: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
optional: false, nullable: false,
|
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
properties: {
|
||||||
format: 'id',
|
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' },
|
isSensitive: { type: 'boolean' },
|
||||||
localOnly: { type: 'boolean' },
|
localOnly: { type: 'boolean' },
|
||||||
hostType: { type: 'string', enum: ['local', 'remote', 'all'], default: 'all' },
|
hostType: { type: 'string', enum: ['local', 'remote', 'all'], default: 'all' },
|
||||||
|
roleIds: {
|
||||||
|
type: 'array',
|
||||||
|
items: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
|
@ -79,6 +83,7 @@ export const paramDef = {
|
||||||
'license',
|
'license',
|
||||||
'isSensitive',
|
'isSensitive',
|
||||||
'localOnly',
|
'localOnly',
|
||||||
|
'roleIdsThatCanBeUsedThisEmojiAsReaction',
|
||||||
],
|
],
|
||||||
default: 'id',
|
default: 'id',
|
||||||
},
|
},
|
||||||
|
@ -119,6 +124,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
isSensitive: ps.query.isSensitive,
|
isSensitive: ps.query.isSensitive,
|
||||||
localOnly: ps.query.localOnly,
|
localOnly: ps.query.localOnly,
|
||||||
hostType: ps.query.hostType,
|
hostType: ps.query.hostType,
|
||||||
|
roleIds: ps.query.roleIds,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4462,7 +4462,11 @@ export type components = {
|
||||||
license: string | null;
|
license: string | null;
|
||||||
localOnly: boolean;
|
localOnly: boolean;
|
||||||
isSensitive: boolean;
|
isSensitive: boolean;
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: string[];
|
roleIdsThatCanBeUsedThisEmojiAsReaction: {
|
||||||
|
/** Format: misskey:id */
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
};
|
};
|
||||||
Flash: {
|
Flash: {
|
||||||
/**
|
/**
|
||||||
|
@ -6963,6 +6967,7 @@ export type operations = {
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
hostType?: 'local' | 'remote' | 'all';
|
hostType?: 'local' | 'remote' | 'all';
|
||||||
|
roleIds?: string[];
|
||||||
}) | null;
|
}) | null;
|
||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
sinceId?: string;
|
sinceId?: string;
|
||||||
|
@ -6976,7 +6981,7 @@ export type operations = {
|
||||||
* @default id
|
* @default id
|
||||||
* @enum {string}
|
* @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
|
* @default DESC
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
|
|
Loading…
Reference in New Issue