throw error when creating too many dialog announcements by updating one

This commit is contained in:
bunnybeam 2025-07-23 20:46:13 +01:00
parent c811f3a9b9
commit ec62953224
No known key found for this signature in database
3 changed files with 40 additions and 16 deletions

View file

@ -131,6 +131,17 @@ export class AnnouncementService {
@bindThis
public async update(announcement: MiAnnouncement, values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<void> {
// Check if this operation would produce an active dialog announcement
if ((values.display ?? announcement.display) === 'dialog' && (values.isActive ?? announcement.isActive)) {
// Check how many active dialog queries already exist, to enforce a limit
const query = this.announcementsRepository.createQueryBuilder('announcement');
query.andWhere('announcement.isActive = true');
const count = await query.getCount();
if (count >= 5) {
throw new IdentifiableError('c0d15f15-f18e-4a40-bcb1-f310d58204ee', 'Too many dialog announcements.');
}
}
await this.announcementsRepository.update(announcement.id, {
updatedAt: new Date(),
title: values.title,

View file

@ -52,7 +52,7 @@ export const meta = {
},
errors: {
tooManyDialogs: {
dialogLimitExceeded: {
message: 'Cannot create the announcement because there are too many active dialog-style announcements.',
code: 'DIALOG_LIMIT_EXCEEDED',
id: '7c1bc084-9c14-4bcf-a910-978cd8e99b5a',
@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
return packed;
} catch (e) {
if (e instanceof IdentifiableError) {
if (e.id === 'c0d15f15-f18e-4a40-bcb1-f310d58204ee') throw new ApiError(meta.errors.tooManyDialogs);
if (e.id === 'c0d15f15-f18e-4a40-bcb1-f310d58204ee') throw new ApiError(meta.errors.dialogLimitExceeded);
}
throw e;
}

View file

@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import type { AnnouncementsRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { AnnouncementService } from '@/core/AnnouncementService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { ApiError } from '../../../error.js';
export const meta = {
@ -23,6 +24,11 @@ export const meta = {
code: 'NO_SUCH_ANNOUNCEMENT',
id: 'd3aae5a7-6372-4cb4-b61c-f511ffc2d7cc',
},
dialogLimitExceeded: {
message: 'Cannot create the announcement because there are too many active dialog-style announcements.',
code: 'DIALOG_LIMIT_EXCEEDED',
id: '1a5db7ca-6d3f-44bc-ac51-05cae93b643c',
},
},
} as const;
@ -57,20 +63,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement);
await this.announcementService.update(announcement, {
updatedAt: new Date(),
title: ps.title,
text: ps.text,
/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */
imageUrl: ps.imageUrl || null,
display: ps.display,
icon: ps.icon,
forExistingUsers: ps.forExistingUsers,
silence: ps.silence,
needConfirmationToRead: ps.needConfirmationToRead,
confetti: ps.confetti,
isActive: ps.isActive,
}, me);
try {
await this.announcementService.update(announcement, {
updatedAt: new Date(),
title: ps.title,
text: ps.text,
/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */
imageUrl: ps.imageUrl || null,
display: ps.display,
icon: ps.icon,
forExistingUsers: ps.forExistingUsers,
silence: ps.silence,
needConfirmationToRead: ps.needConfirmationToRead,
confetti: ps.confetti,
isActive: ps.isActive,
}, me);
} catch (e) {
if (e instanceof IdentifiableError) {
if (e.id === 'c0d15f15-f18e-4a40-bcb1-f310d58204ee') throw new ApiError(meta.errors.dialogLimitExceeded);
}
throw e;
}
});
}
}