mirror of
https://github.com/MisskeyIO/misskey
synced 2024-11-30 15:58:16 +09:00
fix(backend): アンテナ等がポリシーで定められた上限を超えている場合、変更や追加ができないように (MisskeyIO#646)
This commit is contained in:
parent
5ff34d2f8f
commit
2b2975c0dd
@ -20,6 +20,8 @@ export class ClipService {
|
|||||||
public static AlreadyAddedError = class extends Error {};
|
public static AlreadyAddedError = class extends Error {};
|
||||||
public static TooManyClipNotesError = class extends Error {};
|
public static TooManyClipNotesError = class extends Error {};
|
||||||
public static TooManyClipsError = class extends Error {};
|
public static TooManyClipsError = class extends Error {};
|
||||||
|
public static ClipLimitExceededError = class extends Error {};
|
||||||
|
public static ClipNotesLimitExceededError = class extends Error {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.clipsRepository)
|
@Inject(DI.clipsRepository)
|
||||||
@ -38,13 +40,26 @@ export class ClipService {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async create(me: MiLocalUser, name: string, isPublic: boolean, description: string | null): Promise<MiClip> {
|
public async create(me: MiLocalUser, name: string, isPublic: boolean, description: string | null): Promise<MiClip> {
|
||||||
|
const policies = await this.roleService.getUserPolicies(me.id);
|
||||||
|
|
||||||
const currentCount = await this.clipsRepository.countBy({
|
const currentCount = await this.clipsRepository.countBy({
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
});
|
});
|
||||||
if (currentCount > (await this.roleService.getUserPolicies(me.id)).clipLimit) {
|
if (currentCount >= policies.clipLimit) {
|
||||||
throw new ClipService.TooManyClipsError();
|
throw new ClipService.TooManyClipsError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentNoteCounts = await this.clipNotesRepository
|
||||||
|
.createQueryBuilder('cn')
|
||||||
|
.select('COUNT(*)')
|
||||||
|
.innerJoin('cn.clip', 'c')
|
||||||
|
.where('c.userId = :userId', { userId: me.id })
|
||||||
|
.groupBy('cn.clipId')
|
||||||
|
.getRawMany<{ count: number }>();
|
||||||
|
if (currentNoteCounts.some((x) => x.count > policies.noteEachClipsLimit)) {
|
||||||
|
throw new ClipService.ClipNotesLimitExceededError();
|
||||||
|
}
|
||||||
|
|
||||||
const clip = await this.clipsRepository.insert({
|
const clip = await this.clipsRepository.insert({
|
||||||
id: this.idService.gen(),
|
id: this.idService.gen(),
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
@ -67,6 +82,26 @@ export class ClipService {
|
|||||||
throw new ClipService.NoSuchClipError();
|
throw new ClipService.NoSuchClipError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const policies = await this.roleService.getUserPolicies(me.id);
|
||||||
|
|
||||||
|
const currentCount = await this.clipsRepository.countBy({
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
if (currentCount > policies.clipLimit) {
|
||||||
|
throw new ClipService.ClipLimitExceededError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentNoteCounts = await this.clipNotesRepository
|
||||||
|
.createQueryBuilder('cn')
|
||||||
|
.select('COUNT(*)')
|
||||||
|
.innerJoin('cn.clip', 'c')
|
||||||
|
.where('c.userId = :userId', { userId: me.id })
|
||||||
|
.groupBy('cn.clipId')
|
||||||
|
.getRawMany<{ count: number }>();
|
||||||
|
if (currentNoteCounts.some((x) => x.count > policies.noteEachClipsLimit)) {
|
||||||
|
throw new ClipService.ClipNotesLimitExceededError();
|
||||||
|
}
|
||||||
|
|
||||||
await this.clipsRepository.update(clip.id, {
|
await this.clipsRepository.update(clip.id, {
|
||||||
name: name,
|
name: name,
|
||||||
description: description,
|
description: description,
|
||||||
@ -99,10 +134,30 @@ export class ClipService {
|
|||||||
throw new ClipService.NoSuchClipError();
|
throw new ClipService.NoSuchClipError();
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentCount = await this.clipNotesRepository.countBy({
|
const policies = await this.roleService.getUserPolicies(me.id);
|
||||||
|
|
||||||
|
const currentClipCount = await this.clipsRepository.countBy({
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
if (currentClipCount > policies.clipLimit) {
|
||||||
|
throw new ClipService.ClipLimitExceededError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentNoteCounts = await this.clipNotesRepository
|
||||||
|
.createQueryBuilder('cn')
|
||||||
|
.select('COUNT(*)')
|
||||||
|
.innerJoin('cn.clip', 'c')
|
||||||
|
.where('c.userId = :userId', { userId: me.id })
|
||||||
|
.groupBy('cn.clipId')
|
||||||
|
.getRawMany<{ count: number }>();
|
||||||
|
if (currentNoteCounts.some((x) => x.count > policies.noteEachClipsLimit)) {
|
||||||
|
throw new ClipService.ClipNotesLimitExceededError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentNoteCount = await this.clipNotesRepository.countBy({
|
||||||
clipId: clip.id,
|
clipId: clip.id,
|
||||||
});
|
});
|
||||||
if (currentCount > (await this.roleService.getUserPolicies(me.id)).noteEachClipsLimit) {
|
if (currentNoteCount >= policies.noteEachClipsLimit) {
|
||||||
throw new ClipService.TooManyClipNotesError();
|
throw new ClipService.TooManyClipNotesError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit {
|
|||||||
const currentCount = await this.userListMembershipsRepository.countBy({
|
const currentCount = await this.userListMembershipsRepository.countBy({
|
||||||
userListId: list.id,
|
userListId: list.id,
|
||||||
});
|
});
|
||||||
if (currentCount > (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) {
|
if (currentCount >= (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) {
|
||||||
throw new UserListService.TooManyUsersError();
|
throw new UserListService.TooManyUsersError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
const currentAntennasCount = await this.antennasRepository.countBy({
|
const currentAntennasCount = await this.antennasRepository.countBy({
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
});
|
});
|
||||||
if (currentAntennasCount > (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
|
if (currentAntennasCount >= (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
|
||||||
throw new ApiError(meta.errors.tooManyAntennas);
|
throw new ApiError(meta.errors.tooManyAntennas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import type { AntennasRepository, UserListsRepository } from '@/models/_.js';
|
|||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js';
|
import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
@ -33,6 +34,12 @@ export const meta = {
|
|||||||
code: 'NO_SUCH_USER_LIST',
|
code: 'NO_SUCH_USER_LIST',
|
||||||
id: '1c6b35c9-943e-48c2-81e4-2844989407f7',
|
id: '1c6b35c9-943e-48c2-81e4-2844989407f7',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
antennaLimitExceeded: {
|
||||||
|
message: 'You cannot update the antenna because you have exceeded the limit of antennas.',
|
||||||
|
code: 'ANTENNA_LIMIT_EXCEEDED',
|
||||||
|
id: '3166a92e-09d9-4c09-afa3-1dbe34a3afcf',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
@ -83,6 +90,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
|
|
||||||
private antennaEntityService: AntennaEntityService,
|
private antennaEntityService: AntennaEntityService,
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
|
private roleService: RoleService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
if (ps.keywords && ps.excludeKeywords) {
|
if (ps.keywords && ps.excludeKeywords) {
|
||||||
@ -100,6 +108,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw new ApiError(meta.errors.noSuchAntenna);
|
throw new ApiError(meta.errors.noSuchAntenna);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentAntennasCount = await this.antennasRepository.countBy({
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
if (currentAntennasCount > (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
|
||||||
|
throw new ApiError(meta.errors.antennaLimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
let userList;
|
let userList;
|
||||||
|
|
||||||
if ((ps.src === 'list' || antenna.src === 'list') && ps.userListId) {
|
if ((ps.src === 'list' || antenna.src === 'list') && ps.userListId) {
|
||||||
|
@ -48,6 +48,18 @@ export const meta = {
|
|||||||
code: 'TOO_MANY_CLIP_NOTES',
|
code: 'TOO_MANY_CLIP_NOTES',
|
||||||
id: 'f0dba960-ff73-4615-8df4-d6ac5d9dc118',
|
id: 'f0dba960-ff73-4615-8df4-d6ac5d9dc118',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clipLimitExceeded: {
|
||||||
|
message: 'You cannot add a note to the clip because you have exceeded the limit of clips.',
|
||||||
|
code: 'CLIP_LIMIT_EXCEEDED',
|
||||||
|
id: '456cd06d-9f5b-4793-8108-dffe6e257d98',
|
||||||
|
},
|
||||||
|
|
||||||
|
clipNotesLimitExceeded: {
|
||||||
|
message: 'You cannot add a note to the clip because you have exceeded the limit of notes in other clips.',
|
||||||
|
code: 'CLIP_NOTES_LIMIT_EXCEEDED',
|
||||||
|
id: 'f3d6de24-ad27-418d-9d13-b50165dbce66',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@ -77,6 +89,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw new ApiError(meta.errors.alreadyClipped);
|
throw new ApiError(meta.errors.alreadyClipped);
|
||||||
} else if (e instanceof ClipService.TooManyClipNotesError) {
|
} else if (e instanceof ClipService.TooManyClipNotesError) {
|
||||||
throw new ApiError(meta.errors.tooManyClipNotes);
|
throw new ApiError(meta.errors.tooManyClipNotes);
|
||||||
|
} else if (e instanceof ClipService.ClipLimitExceededError) {
|
||||||
|
throw new ApiError(meta.errors.clipLimitExceeded);
|
||||||
|
} else if (e instanceof ClipService.ClipNotesLimitExceededError) {
|
||||||
|
throw new ApiError(meta.errors.clipNotesLimitExceeded);
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,12 @@ export const meta = {
|
|||||||
code: 'TOO_MANY_CLIPS',
|
code: 'TOO_MANY_CLIPS',
|
||||||
id: '920f7c2d-6208-4b76-8082-e632020f5883',
|
id: '920f7c2d-6208-4b76-8082-e632020f5883',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clipNotesLimitExceeded: {
|
||||||
|
message: 'You cannot create a clip any more because you have exceeded the limit of notes in a clip.',
|
||||||
|
code: 'CLIP_NOTES_LIMIT_EXCEEDED',
|
||||||
|
id: '1fdd390f-dcd3-4b65-88d9-6476159bc5c8',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@ -58,6 +64,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ClipService.TooManyClipsError) {
|
if (e instanceof ClipService.TooManyClipsError) {
|
||||||
throw new ApiError(meta.errors.tooManyClips);
|
throw new ApiError(meta.errors.tooManyClips);
|
||||||
|
} else if (e instanceof ClipService.ClipNotesLimitExceededError) {
|
||||||
|
throw new ApiError(meta.errors.clipNotesLimitExceeded);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,18 @@ export const meta = {
|
|||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: 'b4d92d70-b216-46fa-9a3f-a8c811699257',
|
id: 'b4d92d70-b216-46fa-9a3f-a8c811699257',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clipLimitExceeded: {
|
||||||
|
message: 'You cannot update the clip because you have exceeded the limit of clips.',
|
||||||
|
code: 'CLIP_LIMIT_EXCEEDED',
|
||||||
|
id: 'fed46dd9-d99a-4a88-b23f-8d31c80b5b25',
|
||||||
|
},
|
||||||
|
|
||||||
|
clipNotesLimitExceeded: {
|
||||||
|
message: 'You cannot update the clip because you have exceeded the limit of notes in a clip.',
|
||||||
|
code: 'CLIP_NOTES_LIMIT_EXCEEDED',
|
||||||
|
id: '6f02ab37-66a4-4285-afaf-a8b1000e8f3f',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
@ -58,6 +70,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ClipService.NoSuchClipError) {
|
if (e instanceof ClipService.NoSuchClipError) {
|
||||||
throw new ApiError(meta.errors.noSuchClip);
|
throw new ApiError(meta.errors.noSuchClip);
|
||||||
|
} else if (e instanceof ClipService.ClipLimitExceededError) {
|
||||||
|
throw new ApiError(meta.errors.clipLimitExceeded);
|
||||||
|
} else if (e instanceof ClipService.ClipNotesLimitExceededError) {
|
||||||
|
throw new ApiError(meta.errors.clipNotesLimitExceeded);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
if (file.size === 0) throw new ApiError(meta.errors.emptyFile);
|
if (file.size === 0) throw new ApiError(meta.errors.emptyFile);
|
||||||
const antennas: (_Antenna & { userListAccts: string[] | null })[] = JSON.parse(await this.downloadService.downloadTextFile(file.url));
|
const antennas: (_Antenna & { userListAccts: string[] | null })[] = JSON.parse(await this.downloadService.downloadTextFile(file.url));
|
||||||
const currentAntennasCount = await this.antennasRepository.countBy({ userId: me.id });
|
const currentAntennasCount = await this.antennasRepository.countBy({ userId: me.id });
|
||||||
if (currentAntennasCount + antennas.length > (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
|
if (currentAntennasCount + antennas.length >= (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
|
||||||
throw new ApiError(meta.errors.tooManyAntennas);
|
throw new ApiError(meta.errors.tooManyAntennas);
|
||||||
}
|
}
|
||||||
this.queueService.createImportAntennasJob(me, antennas);
|
this.queueService.createImportAntennasJob(me, antennas);
|
||||||
|
@ -91,7 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
const currentWebhooksCount = await this.webhooksRepository.countBy({
|
const currentWebhooksCount = await this.webhooksRepository.countBy({
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
});
|
});
|
||||||
if (currentWebhooksCount > (await this.roleService.getUserPolicies(me.id)).webhookLimit) {
|
if (currentWebhooksCount >= (await this.roleService.getUserPolicies(me.id)).webhookLimit) {
|
||||||
throw new ApiError(meta.errors.tooManyWebhooks);
|
throw new ApiError(meta.errors.tooManyWebhooks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,11 @@ export const meta = {
|
|||||||
code: 'YOU_ARE_NOT_ADMIN',
|
code: 'YOU_ARE_NOT_ADMIN',
|
||||||
id: 'a70c7643-1db5-4ebf-becd-ff4b4223cf23',
|
id: 'a70c7643-1db5-4ebf-becd-ff4b4223cf23',
|
||||||
},
|
},
|
||||||
|
webhookLimitExceeded: {
|
||||||
|
message: 'You cannot update the webhook because you have exceeded the limit of webhooks.',
|
||||||
|
code: 'WEBHOOK_LIMIT_EXCEEDED',
|
||||||
|
id: 'a261cb2d-867d-47a8-a743-8bbd2c1438b1',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
} as const;
|
} as const;
|
||||||
@ -62,6 +67,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
private roleService: RoleService,
|
private roleService: RoleService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const currentWebhooksCount = await this.webhooksRepository.countBy({
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
if (currentWebhooksCount > (await this.roleService.getUserPolicies(me.id)).webhookLimit) {
|
||||||
|
throw new ApiError(meta.errors.webhookLimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
const webhook = await this.webhooksRepository.findOneBy({
|
const webhook = await this.webhooksRepository.findOneBy({
|
||||||
id: ps.webhookId,
|
id: ps.webhookId,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -61,6 +61,12 @@ export const meta = {
|
|||||||
code: 'TOO_MANY_USERS',
|
code: 'TOO_MANY_USERS',
|
||||||
id: '1845ea77-38d1-426e-8e4e-8b83b24f5bd7',
|
id: '1845ea77-38d1-426e-8e4e-8b83b24f5bd7',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
listUsersLimitExceeded: {
|
||||||
|
message: 'You cannot create a list because you have exceeded the limit of users in a list.',
|
||||||
|
code: 'LIST_USERS_LIMIT_EXCEEDED',
|
||||||
|
id: '3e205e58-0798-40f2-a589-a78a619ee3d4',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@ -99,13 +105,25 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!listExist) throw new ApiError(meta.errors.noSuchList);
|
if (!listExist) throw new ApiError(meta.errors.noSuchList);
|
||||||
|
|
||||||
|
const policies = await this.roleService.getUserPolicies(me.id);
|
||||||
const currentCount = await this.userListsRepository.countBy({
|
const currentCount = await this.userListsRepository.countBy({
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
});
|
});
|
||||||
if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) {
|
if (currentCount >= policies.userListLimit) {
|
||||||
throw new ApiError(meta.errors.tooManyUserLists);
|
throw new ApiError(meta.errors.tooManyUserLists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentUserCounts = await this.userListMembershipsRepository
|
||||||
|
.createQueryBuilder('ulm')
|
||||||
|
.select('COUNT(*)')
|
||||||
|
.where('ulm.userListUserId = :userId', { userId: me.id })
|
||||||
|
.groupBy('ulm.userListId')
|
||||||
|
.getRawMany<{ count: number }>();
|
||||||
|
if (currentUserCounts.some((x) => x.count > policies.userEachUserListsLimit)) {
|
||||||
|
throw new ApiError(meta.errors.listUsersLimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
const userList = await this.userListsRepository.insert({
|
const userList = await this.userListsRepository.insert({
|
||||||
id: this.idService.gen(),
|
id: this.idService.gen(),
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { UserListsRepository } from '@/models/_.js';
|
import type { UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import type { MiUserList } from '@/models/UserList.js';
|
import type { MiUserList } from '@/models/UserList.js';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
@ -37,6 +37,12 @@ export const meta = {
|
|||||||
code: 'TOO_MANY_USERLISTS',
|
code: 'TOO_MANY_USERLISTS',
|
||||||
id: '0cf21a28-7715-4f39-a20d-777bfdb8d138',
|
id: '0cf21a28-7715-4f39-a20d-777bfdb8d138',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
listUsersLimitExceeded: {
|
||||||
|
message: 'You cannot create a list because you have exceeded the limit of users in a list.',
|
||||||
|
code: 'LIST_USERS_LIMIT_EXCEEDED',
|
||||||
|
id: 'af66c10d-b0e6-418c-a205-4dd46a482e30',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@ -54,18 +60,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
@Inject(DI.userListsRepository)
|
@Inject(DI.userListsRepository)
|
||||||
private userListsRepository: UserListsRepository,
|
private userListsRepository: UserListsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userListMembershipsRepository)
|
||||||
|
private userListMembershipsRepository: UserListMembershipsRepository,
|
||||||
|
|
||||||
private userListEntityService: UserListEntityService,
|
private userListEntityService: UserListEntityService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private roleService: RoleService,
|
private roleService: RoleService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const policies = await this.roleService.getUserPolicies(me.id);
|
||||||
const currentCount = await this.userListsRepository.countBy({
|
const currentCount = await this.userListsRepository.countBy({
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
});
|
});
|
||||||
if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) {
|
if (currentCount >= policies.userListLimit) {
|
||||||
throw new ApiError(meta.errors.tooManyUserLists);
|
throw new ApiError(meta.errors.tooManyUserLists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentUserCounts = await this.userListMembershipsRepository
|
||||||
|
.createQueryBuilder('ulm')
|
||||||
|
.select('COUNT(*)')
|
||||||
|
.where('ulm.userListUserId = :userId', { userId: me.id })
|
||||||
|
.groupBy('ulm.userListId')
|
||||||
|
.getRawMany<{ count: number }>();
|
||||||
|
if (currentUserCounts.some((x) => x.count > policies.userEachUserListsLimit)) {
|
||||||
|
throw new ApiError(meta.errors.listUsersLimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
const userList = await this.userListsRepository.insert({
|
const userList = await this.userListsRepository.insert({
|
||||||
id: this.idService.gen(),
|
id: this.idService.gen(),
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -10,6 +10,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
|
|||||||
import { GetterService } from '@/server/api/GetterService.js';
|
import { GetterService } from '@/server/api/GetterService.js';
|
||||||
import { UserListService } from '@/core/UserListService.js';
|
import { UserListService } from '@/core/UserListService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
@ -59,6 +60,18 @@ export const meta = {
|
|||||||
code: 'TOO_MANY_USERS',
|
code: 'TOO_MANY_USERS',
|
||||||
id: '2dd9752e-a338-413d-8eec-41814430989b',
|
id: '2dd9752e-a338-413d-8eec-41814430989b',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
listLimitExceeded: {
|
||||||
|
message: 'You cannot add a user to the list because you have exceeded the limit of lists.',
|
||||||
|
code: 'LIST_LIMIT_EXCEEDED',
|
||||||
|
id: '5906ab2d-c164-44bc-a60a-464beba52be9',
|
||||||
|
},
|
||||||
|
|
||||||
|
listUsersLimitExceeded: {
|
||||||
|
message: 'You cannot add a user to the list because you have exceeded the limit of users in other list.',
|
||||||
|
code: 'LIST_USERS_LIMIT_EXCEEDED',
|
||||||
|
id: 'd1054b77-908a-4f7d-a9cc-44d665267108',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@ -85,6 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
|
|
||||||
private getterService: GetterService,
|
private getterService: GetterService,
|
||||||
private userListService: UserListService,
|
private userListService: UserListService,
|
||||||
|
private roleService: RoleService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
// Fetch the list
|
// Fetch the list
|
||||||
@ -97,6 +111,25 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw new ApiError(meta.errors.noSuchList);
|
throw new ApiError(meta.errors.noSuchList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the list limit
|
||||||
|
const policies = await this.roleService.getUserPolicies(me.id);
|
||||||
|
const currentCount = await this.userListsRepository.countBy({
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
if (currentCount > policies.userListLimit) {
|
||||||
|
throw new ApiError(meta.errors.listLimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentUserCounts = await this.userListMembershipsRepository
|
||||||
|
.createQueryBuilder('ulm')
|
||||||
|
.select('COUNT(*)')
|
||||||
|
.where('ulm.userListUserId = :userId', { userId: me.id })
|
||||||
|
.groupBy('ulm.userListId')
|
||||||
|
.getRawMany<{ count: number }>();
|
||||||
|
if (currentUserCounts.some((x) => x.count > policies.userEachUserListsLimit)) {
|
||||||
|
throw new ApiError(meta.errors.listUsersLimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch the user
|
// Fetch the user
|
||||||
const user = await this.getterService.getUser(ps.userId).catch(err => {
|
const user = await this.getterService.getUser(ps.userId).catch(err => {
|
||||||
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { UserListsRepository } from '@/models/_.js';
|
import type { UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { GetterService } from '@/server/api/GetterService.js';
|
import { GetterService } from '@/server/api/GetterService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { UserListService } from '@/core/UserListService.js';
|
import { UserListService } from '@/core/UserListService.js';
|
||||||
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
@ -32,6 +33,18 @@ export const meta = {
|
|||||||
code: 'NO_SUCH_USER',
|
code: 'NO_SUCH_USER',
|
||||||
id: '588e7f72-c744-4a61-b180-d354e912bda2',
|
id: '588e7f72-c744-4a61-b180-d354e912bda2',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
listLimitExceeded: {
|
||||||
|
message: 'You cannot update the user because you have exceeded the limit of lists.',
|
||||||
|
code: 'LIST_LIMIT_EXCEEDED',
|
||||||
|
id: 'd4005118-e773-4132-bafb-10ba22c78da3',
|
||||||
|
},
|
||||||
|
|
||||||
|
listUsersLimitExceeded: {
|
||||||
|
message: 'You cannot update the user because you have exceeded the limit of users in a list.',
|
||||||
|
code: 'LIST_USERS_LIMIT_EXCEEDED',
|
||||||
|
id: 'db7fe164-73d0-4788-8ed1-b6a19e95990d',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@ -51,8 +64,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
@Inject(DI.userListsRepository)
|
@Inject(DI.userListsRepository)
|
||||||
private userListsRepository: UserListsRepository,
|
private userListsRepository: UserListsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userListMembershipsRepository)
|
||||||
|
private userListMembershipsRepository: UserListMembershipsRepository,
|
||||||
|
|
||||||
private userListService: UserListService,
|
private userListService: UserListService,
|
||||||
private getterService: GetterService,
|
private getterService: GetterService,
|
||||||
|
private roleService: RoleService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
// Fetch the list
|
// Fetch the list
|
||||||
@ -65,6 +82,25 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw new ApiError(meta.errors.noSuchList);
|
throw new ApiError(meta.errors.noSuchList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the list limit
|
||||||
|
const policies = await this.roleService.getUserPolicies(me.id);
|
||||||
|
const currentCount = await this.userListsRepository.countBy({
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
if (currentCount > policies.userListLimit) {
|
||||||
|
throw new ApiError(meta.errors.listLimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentUserCounts = await this.userListMembershipsRepository
|
||||||
|
.createQueryBuilder('ulm')
|
||||||
|
.select('COUNT(*)')
|
||||||
|
.where('ulm.userListUserId = :userId', { userId: me.id })
|
||||||
|
.groupBy('ulm.userListId')
|
||||||
|
.getRawMany<{ count: number }>();
|
||||||
|
if (currentUserCounts.some((x) => x.count > policies.userEachUserListsLimit)) {
|
||||||
|
throw new ApiError(meta.errors.listUsersLimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch the user
|
// Fetch the user
|
||||||
const user = await this.getterService.getUser(ps.userId).catch(err => {
|
const user = await this.getterService.getUser(ps.userId).catch(err => {
|
||||||
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
||||||
|
@ -4,10 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { UserListsRepository } from '@/models/_.js';
|
import type { UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { UserListEntityService } from '@/core/entities/UserListEntityService.js';
|
import { UserListEntityService } from '@/core/entities/UserListEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
@ -32,6 +33,18 @@ export const meta = {
|
|||||||
code: 'NO_SUCH_LIST',
|
code: 'NO_SUCH_LIST',
|
||||||
id: '796666fe-3dff-4d39-becb-8a5932c1d5b7',
|
id: '796666fe-3dff-4d39-becb-8a5932c1d5b7',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
listLimitExceeded: {
|
||||||
|
message: 'You cannot update the list because you have exceeded the limit of lists.',
|
||||||
|
code: 'LIST_LIMIT_EXCEEDED',
|
||||||
|
id: '0a1fa63e-3e4c-4bc2-afd1-1ff853b4560e',
|
||||||
|
},
|
||||||
|
|
||||||
|
listUsersLimitExceeded: {
|
||||||
|
message: 'You cannot update the list because you have exceeded the limit of users in a list.',
|
||||||
|
code: 'LIST_USERS_LIMIT_EXCEEDED',
|
||||||
|
id: '831fd3b2-4ac8-421e-89db-bfd98944e529',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@ -51,7 +64,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
@Inject(DI.userListsRepository)
|
@Inject(DI.userListsRepository)
|
||||||
private userListsRepository: UserListsRepository,
|
private userListsRepository: UserListsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userListMembershipsRepository)
|
||||||
|
private userListMembershipsRepository: UserListMembershipsRepository,
|
||||||
|
|
||||||
private userListEntityService: UserListEntityService,
|
private userListEntityService: UserListEntityService,
|
||||||
|
private roleService: RoleService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const userList = await this.userListsRepository.findOneBy({
|
const userList = await this.userListsRepository.findOneBy({
|
||||||
@ -63,6 +80,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw new ApiError(meta.errors.noSuchList);
|
throw new ApiError(meta.errors.noSuchList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const policies = await this.roleService.getUserPolicies(me.id);
|
||||||
|
const currentCount = await this.userListsRepository.countBy({
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
if (currentCount > policies.userListLimit) {
|
||||||
|
throw new ApiError(meta.errors.listLimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentUserCounts = await this.userListMembershipsRepository
|
||||||
|
.createQueryBuilder('ulm')
|
||||||
|
.select('COUNT(*)')
|
||||||
|
.where('ulm.userListUserId = :userId', { userId: me.id })
|
||||||
|
.groupBy('ulm.userListId')
|
||||||
|
.getRawMany<{ count: number }>();
|
||||||
|
if (currentUserCounts.some((x) => x.count > policies.userEachUserListsLimit)) {
|
||||||
|
throw new ApiError(meta.errors.listUsersLimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
await this.userListsRepository.update(userList.id, {
|
await this.userListsRepository.update(userList.id, {
|
||||||
name: ps.name,
|
name: ps.name,
|
||||||
isPublic: ps.isPublic,
|
isPublic: ps.isPublic,
|
||||||
|
@ -167,8 +167,7 @@ describe('アンテナ', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('が上限いっぱいまで作成できること', async () => {
|
test('が上限いっぱいまで作成できること', async () => {
|
||||||
// antennaLimit + 1まで作れるのがキモ
|
const response = await Promise.all([...Array(DEFAULT_POLICIES.antennaLimit)].map(() => successfulApiCall({
|
||||||
const response = await Promise.all([...Array(DEFAULT_POLICIES.antennaLimit + 1)].map(() => successfulApiCall({
|
|
||||||
endpoint: 'antennas/create',
|
endpoint: 'antennas/create',
|
||||||
parameters: { ...defaultParam },
|
parameters: { ...defaultParam },
|
||||||
user: alice,
|
user: alice,
|
||||||
|
@ -153,8 +153,7 @@ describe('クリップ', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('の作成はポリシーで定められた数以上はできない。', async () => {
|
test('の作成はポリシーで定められた数以上はできない。', async () => {
|
||||||
// ポリシー + 1まで作れるという所がミソ
|
const clipLimit = DEFAULT_POLICIES.clipLimit;
|
||||||
const clipLimit = DEFAULT_POLICIES.clipLimit + 1;
|
|
||||||
for (let i = 0; i < clipLimit; i++) {
|
for (let i = 0; i < clipLimit; i++) {
|
||||||
await create();
|
await create();
|
||||||
}
|
}
|
||||||
@ -327,10 +326,10 @@ describe('クリップ', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('の一覧(clips/list)が取得できる(上限いっぱい)', async () => {
|
test('の一覧(clips/list)が取得できる(上限いっぱい)', async () => {
|
||||||
const clipLimit = DEFAULT_POLICIES.clipLimit + 1;
|
const clipLimit = DEFAULT_POLICIES.clipLimit;
|
||||||
const clips = await createMany({}, clipLimit);
|
const clips = await createMany({}, clipLimit);
|
||||||
const res = await list({
|
const res = await list({
|
||||||
parameters: { limit: 1 }, // FIXME: 無視されて11全部返ってくる
|
parameters: { limit: 1 }, // FIXME: 無視されて10全部返ってくる
|
||||||
});
|
});
|
||||||
|
|
||||||
// 返ってくる配列には順序保障がないのでidでソートして厳密比較
|
// 返ってくる配列には順序保障がないのでidでソートして厳密比較
|
||||||
@ -705,7 +704,7 @@ describe('クリップ', () => {
|
|||||||
|
|
||||||
// TODO: 17000msくらいかかる...
|
// TODO: 17000msくらいかかる...
|
||||||
test('をポリシーで定められた上限いっぱい(200)を超えて追加はできない。', async () => {
|
test('をポリシーで定められた上限いっぱい(200)を超えて追加はできない。', async () => {
|
||||||
const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit + 1;
|
const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit;
|
||||||
const noteList = await Promise.all([...Array(noteLimit)].map((_, i) => post(alice, {
|
const noteList = await Promise.all([...Array(noteLimit)].map((_, i) => post(alice, {
|
||||||
text: `test ${i}`,
|
text: `test ${i}`,
|
||||||
}) as unknown)) as Misskey.entities.Note[];
|
}) as unknown)) as Misskey.entities.Note[];
|
||||||
|
Loading…
Reference in New Issue
Block a user