enhance: クリップおよびクリップ内のノートの作成可能数を設定可能に
This commit is contained in:
		
							parent
							
								
									46d2a8726e
								
							
						
					
					
						commit
						c2009acb2d
					
				
					 7 changed files with 90 additions and 0 deletions
				
			
		|  | @ -75,6 +75,7 @@ You should also include the user name that made the change. | |||
| - Add Cloudflare Turnstile CAPTCHA support @CyberRex0 | ||||
| - 非モデレーターでも、権限を持つロールをアサインされたユーザーはインスタンスの招待コードを発行できるように @syuilo | ||||
| - 非モデレーターでも、権限を持つロールをアサインされたユーザーはカスタム絵文字の追加、編集、削除を行えるように @syuilo | ||||
| - クリップおよびクリップ内のノートの作成可能数を設定可能に @syuilo | ||||
| - ハードワードミュートの最大文字数を設定可能に @syuilo | ||||
| - Webhookの作成可能数を設定可能に @syuilo | ||||
| - Server: signToActivityPubGet is set to true by default @syuilo | ||||
|  |  | |||
|  | @ -964,6 +964,8 @@ _role: | |||
|     antennaMax: "アンテナの作成可能数" | ||||
|     wordMuteMax: "ワードミュートの最大文字数" | ||||
|     webhookMax: "Webhookの作成可能数" | ||||
|     clipMax: "クリップの作成可能数" | ||||
|     noteEachClipsMax: "クリップ内のノートの最大数" | ||||
|   _condition: | ||||
|     isLocal: "ローカルユーザー" | ||||
|     isRemote: "リモートユーザー" | ||||
|  |  | |||
|  | @ -23,6 +23,8 @@ export type RoleOptions = { | |||
| 	antennaLimit: number; | ||||
| 	wordMuteLimit: number; | ||||
| 	webhookLimit: number; | ||||
| 	clipLimit: number; | ||||
| 	noteEachClipsLimit: number; | ||||
| }; | ||||
| 
 | ||||
| export const DEFAULT_ROLE: RoleOptions = { | ||||
|  | @ -35,6 +37,8 @@ export const DEFAULT_ROLE: RoleOptions = { | |||
| 	antennaLimit: 5, | ||||
| 	wordMuteLimit: 200, | ||||
| 	webhookLimit: 3, | ||||
| 	clipLimit: 10, | ||||
| 	noteEachClipsLimit: 200, | ||||
| }; | ||||
| 
 | ||||
| @Injectable() | ||||
|  | @ -206,6 +210,8 @@ export class RoleService implements OnApplicationShutdown { | |||
| 			antennaLimit: Math.max(...getOptionValues('antennaLimit')), | ||||
| 			wordMuteLimit: Math.max(...getOptionValues('wordMuteLimit')), | ||||
| 			webhookLimit: Math.max(...getOptionValues('webhookLimit')), | ||||
| 			clipLimit: Math.max(...getOptionValues('clipLimit')), | ||||
| 			noteEachClipsLimit: Math.max(...getOptionValues('noteEachClipsLimit')), | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import { IdService } from '@/core/IdService.js'; | |||
| import { DI } from '@/di-symbols.js'; | ||||
| import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; | ||||
| import { GetterService } from '@/server/api/GetterService.js'; | ||||
| import { RoleService } from '@/core/RoleService.js'; | ||||
| import { ApiError } from '../../error.js'; | ||||
| 
 | ||||
| export const meta = { | ||||
|  | @ -37,6 +38,12 @@ export const meta = { | |||
| 			code: 'ALREADY_CLIPPED', | ||||
| 			id: '734806c4-542c-463a-9311-15c512803965', | ||||
| 		}, | ||||
| 
 | ||||
| 		tooManyClipNotes: { | ||||
| 			message: 'You cannot add notes to the clip any more.', | ||||
| 			code: 'TOO_MANY_CLIP_NOTES', | ||||
| 			id: 'f0dba960-ff73-4615-8df4-d6ac5d9dc118', | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
| 
 | ||||
|  | @ -60,6 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | |||
| 		private clipNotesRepository: ClipNotesRepository, | ||||
| 
 | ||||
| 		private idService: IdService, | ||||
| 		private roleService: RoleService, | ||||
| 		private getterService: GetterService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
|  | @ -86,6 +94,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | |||
| 				throw new ApiError(meta.errors.alreadyClipped); | ||||
| 			} | ||||
| 
 | ||||
| 			const currentCount = await this.clipNotesRepository.countBy({ | ||||
| 				clipId: clip.id, | ||||
| 			}); | ||||
| 			if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).noteEachClipsLimit) { | ||||
| 				throw new ApiError(meta.errors.tooManyClipNotes); | ||||
| 			} | ||||
| 
 | ||||
| 			await this.clipNotesRepository.insert({ | ||||
| 				id: this.idService.genId(), | ||||
| 				noteId: note.id, | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ import { IdService } from '@/core/IdService.js'; | |||
| import type { ClipsRepository } from '@/models/index.js'; | ||||
| import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import { RoleService } from '@/core/RoleService.js'; | ||||
| import { ApiError } from '@/server/api/error.js'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	tags: ['clips'], | ||||
|  | @ -17,6 +19,14 @@ export const meta = { | |||
| 		optional: false, nullable: false, | ||||
| 		ref: 'Clip', | ||||
| 	}, | ||||
| 
 | ||||
| 	errors: { | ||||
| 		tooManyClips: { | ||||
| 			message: 'You cannot create clip any more.', | ||||
| 			code: 'TOO_MANY_CLIPS', | ||||
| 			id: '920f7c2d-6208-4b76-8082-e632020f5883', | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
| 
 | ||||
| export const paramDef = { | ||||
|  | @ -37,9 +47,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | |||
| 		private clipsRepository: ClipsRepository, | ||||
| 
 | ||||
| 		private clipEntityService: ClipEntityService, | ||||
| 		private roleService: RoleService, | ||||
| 		private idService: IdService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const currentCount = await this.clipsRepository.countBy({ | ||||
| 				userId: me.id, | ||||
| 			}); | ||||
| 			if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).clipLimit) { | ||||
| 				throw new ApiError(meta.errors.tooManyClips); | ||||
| 			} | ||||
| 	 | ||||
| 			const clip = await this.clipsRepository.insert({ | ||||
| 				id: this.idService.genId(), | ||||
| 				createdAt: new Date(), | ||||
|  |  | |||
|  | @ -152,6 +152,30 @@ | |||
| 					</MkInput> | ||||
| 				</div> | ||||
| 			</MkFolder> | ||||
| 
 | ||||
| 			<MkFolder> | ||||
| 				<template #label>{{ i18n.ts._role._options.clipMax }}</template> | ||||
| 				<template #suffix>{{ options_clipLimit_useDefault ? i18n.ts._role.useBaseValue : (options_clipLimit_value) }}</template> | ||||
| 				<div class="_gaps"> | ||||
| 					<MkSwitch v-model="options_clipLimit_useDefault" :readonly="readonly"> | ||||
| 						<template #label>{{ i18n.ts._role.useBaseValue }}</template> | ||||
| 					</MkSwitch> | ||||
| 					<MkInput v-model="options_clipLimit_value" :disabled="options_clipLimit_useDefault" type="number" :readonly="readonly"> | ||||
| 					</MkInput> | ||||
| 				</div> | ||||
| 			</MkFolder> | ||||
| 
 | ||||
| 			<MkFolder> | ||||
| 				<template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> | ||||
| 				<template #suffix>{{ options_noteEachClipsLimit_useDefault ? i18n.ts._role.useBaseValue : (options_noteEachClipsLimit_value) }}</template> | ||||
| 				<div class="_gaps"> | ||||
| 					<MkSwitch v-model="options_noteEachClipsLimit_useDefault" :readonly="readonly"> | ||||
| 						<template #label>{{ i18n.ts._role.useBaseValue }}</template> | ||||
| 					</MkSwitch> | ||||
| 					<MkInput v-model="options_noteEachClipsLimit_value" :disabled="options_noteEachClipsLimit_useDefault" type="number" :readonly="readonly"> | ||||
| 					</MkInput> | ||||
| 				</div> | ||||
| 			</MkFolder> | ||||
| 		</div> | ||||
| 	</FormSlot> | ||||
| 
 | ||||
|  | @ -223,6 +247,10 @@ let options_wordMuteLimit_useDefault = $ref(role?.options?.wordMuteLimit?.useDef | |||
| let options_wordMuteLimit_value = $ref(role?.options?.wordMuteLimit?.value ?? 0); | ||||
| let options_webhookLimit_useDefault = $ref(role?.options?.webhookLimit?.useDefault ?? true); | ||||
| let options_webhookLimit_value = $ref(role?.options?.webhookLimit?.value ?? 0); | ||||
| let options_clipLimit_useDefault = $ref(role?.options?.clipLimit?.useDefault ?? true); | ||||
| let options_clipLimit_value = $ref(role?.options?.clipLimit?.value ?? 0); | ||||
| let options_noteEachClipsLimit_useDefault = $ref(role?.options?.noteEachClipsLimit?.useDefault ?? true); | ||||
| let options_noteEachClipsLimit_value = $ref(role?.options?.noteEachClipsLimit?.value ?? 0); | ||||
| 
 | ||||
| if (_DEV_) { | ||||
| 	watch($$(condFormula), () => { | ||||
|  | @ -241,6 +269,8 @@ function getOptions() { | |||
| 		antennaLimit: { useDefault: options_antennaLimit_useDefault, value: options_antennaLimit_value }, | ||||
| 		wordMuteLimit: { useDefault: options_wordMuteLimit_useDefault, value: options_wordMuteLimit_value }, | ||||
| 		webhookLimit: { useDefault: options_webhookLimit_useDefault, value: options_webhookLimit_value }, | ||||
| 		clipLimit: { useDefault: options_clipLimit_useDefault, value: options_clipLimit_value }, | ||||
| 		noteEachClipsLimit: { useDefault: options_noteEachClipsLimit_useDefault, value: options_noteEachClipsLimit_value }, | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -78,6 +78,20 @@ | |||
| 							</MkInput> | ||||
| 						</MkFolder> | ||||
| 
 | ||||
| 						<MkFolder> | ||||
| 							<template #label>{{ i18n.ts._role._options.clipMax }}</template> | ||||
| 							<template #suffix>{{ options_clipLimit }}</template> | ||||
| 							<MkInput v-model="options_clipLimit" type="number"> | ||||
| 							</MkInput> | ||||
| 						</MkFolder> | ||||
| 
 | ||||
| 						<MkFolder> | ||||
| 							<template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> | ||||
| 							<template #suffix>{{ options_noteEachClipsLimit }}</template> | ||||
| 							<MkInput v-model="options_noteEachClipsLimit" type="number"> | ||||
| 							</MkInput> | ||||
| 						</MkFolder> | ||||
| 
 | ||||
| 						<MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton> | ||||
| 					</div> | ||||
| 				</MkFolder> | ||||
|  | @ -119,6 +133,8 @@ let options_driveCapacityMb = $ref(instance.baseRole.driveCapacityMb); | |||
| let options_antennaLimit = $ref(instance.baseRole.antennaLimit); | ||||
| let options_wordMuteLimit = $ref(instance.baseRole.wordMuteLimit); | ||||
| let options_webhookLimit = $ref(instance.baseRole.webhookLimit); | ||||
| let options_clipLimit = $ref(instance.baseRole.clipLimit); | ||||
| let options_noteEachClipsLimit = $ref(instance.baseRole.noteEachClipsLimit); | ||||
| 
 | ||||
| async function updateBaseRole() { | ||||
| 	await os.apiWithDialog('admin/roles/update-default-role-override', { | ||||
|  | @ -132,6 +148,8 @@ async function updateBaseRole() { | |||
| 			antennaLimit: options_antennaLimit, | ||||
| 			wordMuteLimit: options_wordMuteLimit, | ||||
| 			webhookLimit: options_webhookLimit, | ||||
| 			clipLimit: options_clipLimit, | ||||
| 			noteEachClipsLimit: options_noteEachClipsLimit, | ||||
| 		}, | ||||
| 	}); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue