fix: admin/emoji/update で不正なエラーが発生する (#14750)

* fix emoji updating bug

* update changelog

* type fix

* " -> '

* conprehensiveness check

* lint

* undefined -> null
This commit is contained in:
FineArchs 2024-10-11 17:17:45 +09:00 committed by GitHub
parent d376aab45e
commit 12bc671511
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 40 additions and 25 deletions

View File

@ -4,6 +4,9 @@
- Enhance: l10nの更新 - Enhance: l10nの更新
- Fix: メールアドレス不要でCaptchaが有効な場合にアカウント登録完了後自動でのログインに失敗する問題を修正 - Fix: メールアドレス不要でCaptchaが有効な場合にアカウント登録完了後自動でのログインに失敗する問題を修正
### Server
- Fix: `admin/emoji/update`エンドポイントのidのみ指定した時不正なエラーが発生するバグを修正
## 2024.10.0 ## 2024.10.0
### Note ### Note

View File

@ -103,19 +103,33 @@ export class CustomEmojiService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async update(id: MiEmoji['id'], data: { public async update(data: (
{ id: MiEmoji['id'], name?: string; } | { name: string; id?: MiEmoji['id'], }
) & {
driveFile?: MiDriveFile; driveFile?: MiDriveFile;
name?: string;
category?: string | null; category?: string | null;
aliases?: string[]; aliases?: string[];
license?: string | null; license?: string | null;
isSensitive?: boolean; isSensitive?: boolean;
localOnly?: boolean; localOnly?: boolean;
roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][]; roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][];
}, moderator?: MiUser): Promise<void> { }, moderator?: MiUser): Promise<
const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); null
const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() }); | 'NO_SUCH_EMOJI'
if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); | 'SAME_NAME_EMOJI_EXISTS'
> {
const emoji = data.id
? await this.getEmojiById(data.id)
: await this.getEmojiByName(data.name!);
if (emoji === null) return 'NO_SUCH_EMOJI';
const id = emoji.id;
// IDと絵文字名が両方指定されている場合は絵文字名の変更を行うため重複チェックが必要
const doNameUpdate = data.id && data.name && (data.name !== emoji.name);
if (doNameUpdate) {
const isDuplicate = await this.checkDuplicate(data.name!);
if (isDuplicate) return 'SAME_NAME_EMOJI_EXISTS';
}
await this.emojisRepository.update(emoji.id, { await this.emojisRepository.update(emoji.id, {
updatedAt: new Date(), updatedAt: new Date(),
@ -135,7 +149,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
const packed = await this.emojiEntityService.packDetailed(emoji.id); const packed = await this.emojiEntityService.packDetailed(emoji.id);
if (emoji.name === data.name) { if (!doNameUpdate) {
this.globalEventService.publishBroadcastStream('emojiUpdated', { this.globalEventService.publishBroadcastStream('emojiUpdated', {
emojis: [packed], emojis: [packed],
}); });
@ -157,6 +171,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
after: updated, after: updated,
}); });
} }
return null;
} }
@bindThis @bindThis

View File

@ -6,7 +6,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import type { DriveFilesRepository } from '@/models/_.js'; import type { DriveFilesRepository, MiEmoji } from '@/models/_.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
@ -78,25 +78,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile);
} }
let emojiId; // JSON schemeのanyOfの型変換がうまくいっていないらしい
if (ps.id) { const required = { id: ps.id, name: ps.name } as
emojiId = ps.id; | { id: MiEmoji['id']; name?: string }
const emoji = await this.customEmojiService.getEmojiById(ps.id); | { id?: MiEmoji['id']; name: string };
if (!emoji) throw new ApiError(meta.errors.noSuchEmoji);
if (ps.name && (ps.name !== emoji.name)) {
const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name);
if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists);
}
} else {
if (!ps.name) throw new Error('Invalid Params unexpectedly passed. This is a BUG. Please report it to the development team.');
const emoji = await this.customEmojiService.getEmojiByName(ps.name);
if (!emoji) throw new ApiError(meta.errors.noSuchEmoji);
emojiId = emoji.id;
}
await this.customEmojiService.update(emojiId, { const error = await this.customEmojiService.update({
...required,
driveFile, driveFile,
name: ps.name,
category: ps.category, category: ps.category,
aliases: ps.aliases, aliases: ps.aliases,
license: ps.license, license: ps.license,
@ -104,6 +93,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
localOnly: ps.localOnly, localOnly: ps.localOnly,
roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction,
}, me); }, me);
switch (error) {
case null: return;
case 'NO_SUCH_EMOJI': throw new ApiError(meta.errors.noSuchEmoji);
case 'SAME_NAME_EMOJI_EXISTS': throw new ApiError(meta.errors.sameNameEmojiExists);
}
// 網羅性チェック
const mustBeNever: never = error;
}); });
} }
} }