enhance: make configurable to whether notes search available by role

Resolve #10318
This commit is contained in:
syuilo 2023-03-13 17:52:24 +09:00
parent 7f16b50e73
commit 5d1ccb9bdc
7 changed files with 48 additions and 19 deletions

View file

@ -15,6 +15,7 @@ You should also include the user name that made the change.
### Improvements ### Improvements
- ユーザーごとにRenoteをミュートできるように - ユーザーごとにRenoteをミュートできるように
- ノートごとに絵文字リアクションを受け取るか設定できるように - ノートごとに絵文字リアクションを受け取るか設定できるように
- ノート検索の利用可否をロールで制御可能に(デフォルトでオフ)
- ロールの並び順を設定可能に - ロールの並び順を設定可能に
- 指定した文字列を含む投稿の公開範囲をホームにできるように - 指定した文字列を含む投稿の公開範囲をホームにできるように
- enhance(client): 設定から自分のロールを確認できるように - enhance(client): 設定から自分のロールを確認できるように

View file

@ -1261,6 +1261,7 @@ _role:
rateLimitFactor: "レートリミット" rateLimitFactor: "レートリミット"
descriptionOfRateLimitFactor: "小さいほど制限が緩和され、大きいほど制限が強化されます。" descriptionOfRateLimitFactor: "小さいほど制限が緩和され、大きいほど制限が強化されます。"
canHideAds: "広告の非表示" canHideAds: "広告の非表示"
canSearchNotes: "ノート検索の利用可否"
_condition: _condition:
isLocal: "ローカルユーザー" isLocal: "ローカルユーザー"
isRemote: "リモートユーザー" isRemote: "リモートユーザー"

View file

@ -21,6 +21,7 @@ export type RolePolicies = {
canPublicNote: boolean; canPublicNote: boolean;
canInvite: boolean; canInvite: boolean;
canManageCustomEmojis: boolean; canManageCustomEmojis: boolean;
canSearchNotes: boolean;
canHideAds: boolean; canHideAds: boolean;
driveCapacityMb: number; driveCapacityMb: number;
pinLimit: number; pinLimit: number;
@ -40,6 +41,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
canPublicNote: true, canPublicNote: true,
canInvite: false, canInvite: false,
canManageCustomEmojis: false, canManageCustomEmojis: false,
canSearchNotes: false,
canHideAds: false, canHideAds: false,
driveCapacityMb: 100, driveCapacityMb: 100,
pinLimit: 5, pinLimit: 5,
@ -264,6 +266,7 @@ export class RoleService implements OnApplicationShutdown {
canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)), canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)),
canInvite: calc('canInvite', vs => vs.some(v => v === true)), canInvite: calc('canInvite', vs => vs.some(v => v === true)),
canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)), canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)),
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)), canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)), driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
pinLimit: calc('pinLimit', vs => Math.max(...vs)), pinLimit: calc('pinLimit', vs => Math.max(...vs)),

View file

@ -6,6 +6,8 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
import { RoleService } from '@/core/RoleService.js';
import { ApiError } from '../../error.js';
export const meta = { export const meta = {
tags: ['notes'], tags: ['notes'],
@ -23,6 +25,11 @@ export const meta = {
}, },
errors: { errors: {
unavailable: {
message: 'Search of notes unavailable.',
code: 'UNAVAILABLE',
id: '0b44998d-77aa-4427-80d0-d2c9b8523011',
},
}, },
} as const; } as const;
@ -59,8 +66,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private noteEntityService: NoteEntityService, private noteEntityService: NoteEntityService,
private queryService: QueryService, private queryService: QueryService,
private roleService: RoleService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const policies = await this.roleService.getUserPolicies(me ? me.id : null);
if (!policies.canSearchNotes) {
throw new ApiError(meta.errors.unavailable);
}
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId); const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId);
if (ps.userId) { if (ps.userId) {

View file

@ -53,6 +53,7 @@ export const ROLE_POLICIES = [
'canPublicNote', 'canPublicNote',
'canInvite', 'canInvite',
'canManageCustomEmojis', 'canManageCustomEmojis',
'canSearchNotes',
'canHideAds', 'canHideAds',
'driveCapacityMb', 'driveCapacityMb',
'pinLimit', 'pinLimit',

View file

@ -187,6 +187,26 @@
</div> </div>
</MkFolder> </MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])">
<template #label>{{ i18n.ts._role._options.canSearchNotes }}</template>
<template #suffix>
<span v-if="role.policies.canSearchNotes.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
<span v-else>{{ role.policies.canSearchNotes.value ? i18n.ts.yes : i18n.ts.no }}</span>
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canSearchNotes)"></i></span>
</template>
<div class="_gaps">
<MkSwitch v-model="role.policies.canSearchNotes.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
</MkSwitch>
<MkSwitch v-model="role.policies.canSearchNotes.value" :disabled="role.policies.canSearchNotes.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
<MkRange v-model="role.policies.canSearchNotes.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
<template #label>{{ i18n.ts._role.priority }}</template>
</MkRange>
</div>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])"> <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])">
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template> <template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
<template #suffix> <template #suffix>

View file

@ -55,6 +55,14 @@
</MkSwitch> </MkSwitch>
</MkFolder> </MkFolder>
<MkFolder>
<template #label>{{ i18n.ts._role._options.canSearchNotes }}</template>
<template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template>
<MkSwitch v-model="policies.canSearchNotes">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</MkFolder>
<MkFolder> <MkFolder>
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template> <template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
<template #suffix>{{ policies.driveCapacityMb }}MB</template> <template #suffix>{{ policies.driveCapacityMb }}MB</template>
@ -167,25 +175,7 @@ import { definePageMetadata } from '@/scripts/page-metadata';
import { instance } from '@/instance'; import { instance } from '@/instance';
import { useRouter } from '@/router'; import { useRouter } from '@/router';
import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue';
import { ROLE_POLICIES } from '@/const';
const ROLE_POLICIES = [
'gtlAvailable',
'ltlAvailable',
'canPublicNote',
'canInvite',
'canManageCustomEmojis',
'canHideAds',
'driveCapacityMb',
'pinLimit',
'antennaLimit',
'wordMuteLimit',
'webhookLimit',
'clipLimit',
'noteEachClipsLimit',
'userListLimit',
'userEachUserListsLimit',
'rateLimitFactor',
] as const;
const router = useRouter(); const router = useRouter();