feat: ロールによるコンテンツの操作の制限 (#120)
This commit is contained in:
		@@ -20,6 +20,9 @@ export type RolePolicies = {
 | 
			
		||||
	gtlAvailable: boolean;
 | 
			
		||||
	ltlAvailable: boolean;
 | 
			
		||||
	canPublicNote: boolean;
 | 
			
		||||
	canCreateContent: boolean;
 | 
			
		||||
	canUpdateContent: boolean;
 | 
			
		||||
	canDeleteContent: boolean;
 | 
			
		||||
	canInvite: boolean;
 | 
			
		||||
	inviteLimit: number;
 | 
			
		||||
	inviteLimitCycle: number;
 | 
			
		||||
@@ -44,6 +47,9 @@ export const DEFAULT_POLICIES: RolePolicies = {
 | 
			
		||||
	gtlAvailable: true,
 | 
			
		||||
	ltlAvailable: true,
 | 
			
		||||
	canPublicNote: true,
 | 
			
		||||
	canCreateContent: true,
 | 
			
		||||
	canUpdateContent: true,
 | 
			
		||||
	canDeleteContent: true,
 | 
			
		||||
	canInvite: false,
 | 
			
		||||
	inviteLimit: 0,
 | 
			
		||||
	inviteLimitCycle: 60 * 24 * 7,
 | 
			
		||||
@@ -287,6 +293,9 @@ export class RoleService implements OnApplicationShutdown {
 | 
			
		||||
			gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)),
 | 
			
		||||
			ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)),
 | 
			
		||||
			canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)),
 | 
			
		||||
			canCreateContent: calc('canCreateContent', vs => vs.some(v => v === true)),
 | 
			
		||||
			canUpdateContent: calc('canUpdateContent', vs => vs.some(v => v === true)),
 | 
			
		||||
			canDeleteContent: calc('canDeleteContent', vs => vs.some(v => v === true)),
 | 
			
		||||
			canInvite: calc('canInvite', vs => vs.some(v => v === true)),
 | 
			
		||||
			inviteLimit: calc('inviteLimit', vs => Math.max(...vs)),
 | 
			
		||||
			inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)),
 | 
			
		||||
 
 | 
			
		||||
@@ -351,8 +351,9 @@ export class UserEntityService implements OnModuleInit {
 | 
			
		||||
			(profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
 | 
			
		||||
			null;
 | 
			
		||||
 | 
			
		||||
		const isModerator = isMe && opts.detail ? this.roleService.isModerator(user) : null;
 | 
			
		||||
		const isAdmin = isMe && opts.detail ? this.roleService.isAdministrator(user) : null;
 | 
			
		||||
		const isModerator = isMe && opts.detail ? await this.roleService.isModerator(user) : null;
 | 
			
		||||
		const isAdmin = isMe && opts.detail ? await this.roleService.isAdministrator(user) : null;
 | 
			
		||||
		const policies = opts.detail ? await this.roleService.getUserPolicies(user.id) : null;
 | 
			
		||||
 | 
			
		||||
		const falsy = opts.detail ? false : undefined;
 | 
			
		||||
 | 
			
		||||
@@ -396,7 +397,8 @@ export class UserEntityService implements OnModuleInit {
 | 
			
		||||
				bannerUrl: user.bannerUrl,
 | 
			
		||||
				bannerBlurhash: user.bannerBlurhash,
 | 
			
		||||
				isLocked: user.isLocked,
 | 
			
		||||
				isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote),
 | 
			
		||||
				isSilenced: !policies?.canPublicNote,
 | 
			
		||||
				isLimited: !(policies?.canCreateContent && policies.canUpdateContent && policies.canDeleteContent),
 | 
			
		||||
				isSuspended: user.isSuspended ?? falsy,
 | 
			
		||||
				description: profile!.description,
 | 
			
		||||
				location: profile!.location,
 | 
			
		||||
@@ -473,7 +475,7 @@ export class UserEntityService implements OnModuleInit {
 | 
			
		||||
				emailNotificationTypes: profile!.emailNotificationTypes,
 | 
			
		||||
				achievements: profile!.achievements,
 | 
			
		||||
				loggedInDays: profile!.loggedInDates.length,
 | 
			
		||||
				policies: this.roleService.getUserPolicies(user.id),
 | 
			
		||||
				policies: policies,
 | 
			
		||||
			} : {}),
 | 
			
		||||
 | 
			
		||||
			...(opts.includeSecrets ? {
 | 
			
		||||
 
 | 
			
		||||
@@ -121,6 +121,10 @@ export const packedUserDetailedNotMeOnlySchema = {
 | 
			
		||||
			type: 'boolean',
 | 
			
		||||
			nullable: false, optional: false,
 | 
			
		||||
		},
 | 
			
		||||
		isLimited: {
 | 
			
		||||
			type: 'boolean',
 | 
			
		||||
			nullable: false, optional: false,
 | 
			
		||||
		},
 | 
			
		||||
		isSuspended: {
 | 
			
		||||
			type: 'boolean',
 | 
			
		||||
			nullable: false, optional: false,
 | 
			
		||||
 
 | 
			
		||||
@@ -51,8 +51,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 | 
			
		||||
				throw new Error('user not found');
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const policies = await this.roleService.getUserPolicies(user.id);
 | 
			
		||||
			const isModerator = await this.roleService.isModerator(user);
 | 
			
		||||
			const isSilenced = !(await this.roleService.getUserPolicies(user.id)).canPublicNote;
 | 
			
		||||
			const isLimited = !(policies.canCreateContent && policies.canUpdateContent && policies.canDeleteContent);
 | 
			
		||||
			const isSilenced = !policies.canPublicNote;
 | 
			
		||||
 | 
			
		||||
			const _me = await this.usersRepository.findOneByOrFail({ id: me.id });
 | 
			
		||||
			if (!await this.roleService.isAdministrator(_me) && await this.roleService.isAdministrator(user)) {
 | 
			
		||||
@@ -80,6 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 | 
			
		||||
				mutingNotificationTypes: profile.mutingNotificationTypes,
 | 
			
		||||
				isModerator: isModerator,
 | 
			
		||||
				isSilenced: isSilenced,
 | 
			
		||||
				isLimited: isLimited,
 | 
			
		||||
				isSuspended: user.isSuspended,
 | 
			
		||||
				lastActiveDate: user.lastActiveDate,
 | 
			
		||||
				moderationNote: profile.moderationNote ?? '',
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ export const meta = {
 | 
			
		||||
	tags: ['antennas'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['antennas'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['antennas'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ export const meta = {
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:blocks',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ export const meta = {
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:blocks',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ export const meta = {
 | 
			
		||||
	tags: ['channels'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['channels'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['channels'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['channels'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['channels'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['channels'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:channels',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ export const meta = {
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ export const meta = {
 | 
			
		||||
	tags: ['clips'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['clips'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['account', 'notes', 'clips'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['clip'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['clips'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ export const meta = {
 | 
			
		||||
	tags: ['drive'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ export const meta = {
 | 
			
		||||
	tags: ['drive'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:drive',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ export const meta = {
 | 
			
		||||
	tags: ['drive'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:drive',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ export const meta = {
 | 
			
		||||
	description: 'Request the server to download a new drive file from the specified URL.',
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ export const meta = {
 | 
			
		||||
	tags: ['drive'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:drive',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['drive'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:drive',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['drive'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:drive',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['flash'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['flashs'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:flash',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['flash'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['flash'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['flash'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ export const meta = {
 | 
			
		||||
	tags: ['gallery'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['gallery'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:gallery',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['gallery'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['gallery'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['gallery'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import { DI } from '@/di-symbols.js';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	secure: true,
 | 
			
		||||
} as const;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,8 @@ import { ApiError } from '../../error.js';
 | 
			
		||||
export const meta = {
 | 
			
		||||
	secure: true,
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
	limit: {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,8 @@ import { ApiError } from '../../error.js';
 | 
			
		||||
export const meta = {
 | 
			
		||||
	secure: true,
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
	limit: {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,8 @@ import { ApiError } from '../../error.js';
 | 
			
		||||
export const meta = {
 | 
			
		||||
	secure: true,
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
	limit: {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,10 @@ import { ApiError } from '../../error.js';
 | 
			
		||||
export const meta = {
 | 
			
		||||
	secure: true,
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
	limit: {
 | 
			
		||||
		duration: ms('1hour'),
 | 
			
		||||
		max: 1,
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,10 @@ export const meta = {
 | 
			
		||||
 | 
			
		||||
	secure: true,
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
	limit: {
 | 
			
		||||
		duration: ms('1day'),
 | 
			
		||||
		max: 5,
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@ export const meta = {
 | 
			
		||||
	tags: ['account', 'notes'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['account', 'notes'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,10 +12,11 @@ import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
 | 
			
		||||
import { ApiError } from '../../error.js';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
 | 
			
		||||
	secure: true,
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	limit: {
 | 
			
		||||
		duration: ms('1hour'),
 | 
			
		||||
		max: 3,
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ export const meta = {
 | 
			
		||||
	tags: ['account'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ export const meta = {
 | 
			
		||||
	tags: ['webhooks'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['webhooks'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['webhooks'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,8 @@ export const meta = {
 | 
			
		||||
	tags: ['account'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
	kind: 'write:mutes',
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['account'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:mutes',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ export const meta = {
 | 
			
		||||
	tags: ['notes'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ export const meta = {
 | 
			
		||||
	tags: ['notes'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:notes',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,8 @@ export const meta = {
 | 
			
		||||
	tags: ['notes', 'favorites'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
	kind: 'write:favorites',
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['notes', 'favorites'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:favorites',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ export const meta = {
 | 
			
		||||
	tags: ['notes'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['reactions', 'notes'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['reactions', 'notes'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:reactions',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ export const meta = {
 | 
			
		||||
	tags: ['notes'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['notes'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ export const meta = {
 | 
			
		||||
	tags: ['notes'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:notes',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ export const meta = {
 | 
			
		||||
	tags: ['pages'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['pages'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:pages',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['pages'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['pages'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['pages'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@ export const meta = {
 | 
			
		||||
	tags: ['account'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
	kind: 'write:mutes',
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['account'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:mutes',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,10 @@ import { UserListService } from '@/core/UserListService.js';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
	res: {
 | 
			
		||||
		type: 'object',
 | 
			
		||||
		optional: false, nullable: false,
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ export const meta = {
 | 
			
		||||
	tags: ['lists'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canCreateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ export const meta = {
 | 
			
		||||
	tags: ['lists'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canDeleteContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@ import { DI } from '@/di-symbols.js';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	errors: {
 | 
			
		||||
		noSuchList: {
 | 
			
		||||
			message: 'No such user list.',
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ export const meta = {
 | 
			
		||||
	tags: ['lists', 'users'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ export const meta = {
 | 
			
		||||
	tags: ['lists', 'users'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	prohibitMoved: true,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@ import { DI } from '@/di-symbols.js';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	errors: {
 | 
			
		||||
		noSuchList: {
 | 
			
		||||
			message: 'No such user list.',
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ export const meta = {
 | 
			
		||||
	tags: ['lists'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ export const meta = {
 | 
			
		||||
	tags: ['account'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
	requireRolePolicy: 'canUpdateContent',
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -91,6 +91,7 @@ describe('ユーザー', () => {
 | 
			
		||||
			bannerBlurhash: user.bannerBlurhash,
 | 
			
		||||
			isLocked: user.isLocked,
 | 
			
		||||
			isSilenced: user.isSilenced,
 | 
			
		||||
			isLimited: user.isLimited,
 | 
			
		||||
			isSuspended: user.isSuspended,
 | 
			
		||||
			description: user.description,
 | 
			
		||||
			location: user.location,
 | 
			
		||||
@@ -356,6 +357,7 @@ describe('ユーザー', () => {
 | 
			
		||||
		assert.strictEqual(response.bannerBlurhash, null);
 | 
			
		||||
		assert.strictEqual(response.isLocked, false);
 | 
			
		||||
		assert.strictEqual(response.isSilenced, false);
 | 
			
		||||
		assert.strictEqual(response.isLimited, false);
 | 
			
		||||
		assert.strictEqual(response.isSuspended, false);
 | 
			
		||||
		assert.strictEqual(response.description, null);
 | 
			
		||||
		assert.strictEqual(response.location, null);
 | 
			
		||||
 
 | 
			
		||||
@@ -99,6 +99,7 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host = 'mi
 | 
			
		||||
		isModerator: false,
 | 
			
		||||
		isMuted: false,
 | 
			
		||||
		isSilenced: false,
 | 
			
		||||
		isLimited: false,
 | 
			
		||||
		isSuspended: false,
 | 
			
		||||
		lang: 'en',
 | 
			
		||||
		location: 'Fediverse',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div v-adaptive-bg :class="[$style.root, { yellow: user.isSilenced, red: user.isSuspended, gray: false }]">
 | 
			
		||||
<div v-adaptive-bg :class="[$style.root, { yellow: user.isSilenced, gray: user.isLimited, red: user.isSuspended }]">
 | 
			
		||||
	<MkAvatar class="avatar" :user="user" indicator/>
 | 
			
		||||
	<div class="body">
 | 
			
		||||
		<span class="name"><MkUserName class="name" :user="user"/></span>
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,9 @@ export const ROLE_POLICIES = [
 | 
			
		||||
	'gtlAvailable',
 | 
			
		||||
	'ltlAvailable',
 | 
			
		||||
	'canPublicNote',
 | 
			
		||||
	'canCreateContent',
 | 
			
		||||
	'canUpdateContent',
 | 
			
		||||
	'canDeleteContent',
 | 
			
		||||
	'canInvite',
 | 
			
		||||
	'inviteLimit',
 | 
			
		||||
	'inviteLimitCycle',
 | 
			
		||||
 
 | 
			
		||||
@@ -155,6 +155,66 @@
 | 
			
		||||
				</div>
 | 
			
		||||
			</MkFolder>
 | 
			
		||||
 | 
			
		||||
			<MkFolder v-if="matchQuery([i18n.ts._role._options.canCreateContent, 'canCreateContent'])">
 | 
			
		||||
				<template #label>{{ i18n.ts._role._options.canCreateContent }}</template>
 | 
			
		||||
				<template #suffix>
 | 
			
		||||
					<span v-if="role.policies.canCreateContent.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
 | 
			
		||||
					<span v-else>{{ role.policies.canCreateContent.value ? i18n.ts.yes : i18n.ts.no }}</span>
 | 
			
		||||
					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canCreateContent)"></i></span>
 | 
			
		||||
				</template>
 | 
			
		||||
				<div class="_gaps">
 | 
			
		||||
					<MkSwitch v-model="role.policies.canCreateContent.useDefault" :readonly="readonly">
 | 
			
		||||
						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 | 
			
		||||
					</MkSwitch>
 | 
			
		||||
					<MkSwitch v-model="role.policies.canCreateContent.value" :disabled="role.policies.canCreateContent.useDefault" :readonly="readonly">
 | 
			
		||||
						<template #label>{{ i18n.ts.enable }}</template>
 | 
			
		||||
					</MkSwitch>
 | 
			
		||||
					<MkRange v-model="role.policies.canCreateContent.priority" :min="0" :max="2" :step="1" easing :textConverter="(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.canUpdateContent, 'canUpdateContent'])">
 | 
			
		||||
				<template #label>{{ i18n.ts._role._options.canUpdateContent }}</template>
 | 
			
		||||
				<template #suffix>
 | 
			
		||||
					<span v-if="role.policies.canUpdateContent.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
 | 
			
		||||
					<span v-else>{{ role.policies.canUpdateContent.value ? i18n.ts.yes : i18n.ts.no }}</span>
 | 
			
		||||
					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canUpdateContent)"></i></span>
 | 
			
		||||
				</template>
 | 
			
		||||
				<div class="_gaps">
 | 
			
		||||
					<MkSwitch v-model="role.policies.canUpdateContent.useDefault" :readonly="readonly">
 | 
			
		||||
						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 | 
			
		||||
					</MkSwitch>
 | 
			
		||||
					<MkSwitch v-model="role.policies.canUpdateContent.value" :disabled="role.policies.canUpdateContent.useDefault" :readonly="readonly">
 | 
			
		||||
						<template #label>{{ i18n.ts.enable }}</template>
 | 
			
		||||
					</MkSwitch>
 | 
			
		||||
					<MkRange v-model="role.policies.canUpdateContent.priority" :min="0" :max="2" :step="1" easing :textConverter="(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.canDeleteContent, 'canDeleteContent'])">
 | 
			
		||||
				<template #label>{{ i18n.ts._role._options.canDeleteContent }}</template>
 | 
			
		||||
				<template #suffix>
 | 
			
		||||
					<span v-if="role.policies.canDeleteContent.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
 | 
			
		||||
					<span v-else>{{ role.policies.canDeleteContent.value ? i18n.ts.yes : i18n.ts.no }}</span>
 | 
			
		||||
					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canDeleteContent)"></i></span>
 | 
			
		||||
				</template>
 | 
			
		||||
				<div class="_gaps">
 | 
			
		||||
					<MkSwitch v-model="role.policies.canDeleteContent.useDefault" :readonly="readonly">
 | 
			
		||||
						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 | 
			
		||||
					</MkSwitch>
 | 
			
		||||
					<MkSwitch v-model="role.policies.canDeleteContent.value" :disabled="role.policies.canDeleteContent.useDefault" :readonly="readonly">
 | 
			
		||||
						<template #label>{{ i18n.ts.enable }}</template>
 | 
			
		||||
					</MkSwitch>
 | 
			
		||||
					<MkRange v-model="role.policies.canDeleteContent.priority" :min="0" :max="2" :step="1" easing :textConverter="(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.canInvite, 'canInvite'])">
 | 
			
		||||
				<template #label>{{ i18n.ts._role._options.canInvite }}</template>
 | 
			
		||||
				<template #suffix>
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,30 @@
 | 
			
		||||
							</MkSwitch>
 | 
			
		||||
						</MkFolder>
 | 
			
		||||
 | 
			
		||||
						<MkFolder v-if="matchQuery([i18n.ts._role._options.canCreateContent, 'canCreateContent'])">
 | 
			
		||||
							<template #label>{{ i18n.ts._role._options.canCreateContent }}</template>
 | 
			
		||||
							<template #suffix>{{ policies.canCreateContent ? i18n.ts.yes : i18n.ts.no }}</template>
 | 
			
		||||
							<MkSwitch v-model="policies.canCreateContent">
 | 
			
		||||
								<template #label>{{ i18n.ts.enable }}</template>
 | 
			
		||||
							</MkSwitch>
 | 
			
		||||
						</MkFolder>
 | 
			
		||||
 | 
			
		||||
						<MkFolder v-if="matchQuery([i18n.ts._role._options.canUpdateContent, 'canUpdateContent'])">
 | 
			
		||||
							<template #label>{{ i18n.ts._role._options.canUpdateContent }}</template>
 | 
			
		||||
							<template #suffix>{{ policies.canUpdateContent ? i18n.ts.yes : i18n.ts.no }}</template>
 | 
			
		||||
							<MkSwitch v-model="policies.canUpdateContent">
 | 
			
		||||
								<template #label>{{ i18n.ts.enable }}</template>
 | 
			
		||||
							</MkSwitch>
 | 
			
		||||
						</MkFolder>
 | 
			
		||||
 | 
			
		||||
						<MkFolder v-if="matchQuery([i18n.ts._role._options.canDeleteContent, 'canDeleteContent'])">
 | 
			
		||||
							<template #label>{{ i18n.ts._role._options.canDeleteContent }}</template>
 | 
			
		||||
							<template #suffix>{{ policies.canDeleteContent ? i18n.ts.yes : i18n.ts.no }}</template>
 | 
			
		||||
							<MkSwitch v-model="policies.canDeleteContent">
 | 
			
		||||
								<template #label>{{ i18n.ts.enable }}</template>
 | 
			
		||||
							</MkSwitch>
 | 
			
		||||
						</MkFolder>
 | 
			
		||||
 | 
			
		||||
						<MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])">
 | 
			
		||||
							<template #label>{{ i18n.ts._role._options.canInvite }}</template>
 | 
			
		||||
							<template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template>
 | 
			
		||||
@@ -57,7 +81,7 @@
 | 
			
		||||
							<MkInput v-model="policies.inviteLimit" type="number">
 | 
			
		||||
							</MkInput>
 | 
			
		||||
						</MkFolder>
 | 
			
		||||
						
 | 
			
		||||
 | 
			
		||||
						<MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimitCycle, 'inviteLimitCycle'])">
 | 
			
		||||
							<template #label>{{ i18n.ts._role._options.inviteLimitCycle }}</template>
 | 
			
		||||
							<template #suffix>{{ policies.inviteLimitCycle + i18n.ts._time.minute }}</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
						<span class="sub"><span class="acct _monospace">@{{ acct(user) }}</span></span>
 | 
			
		||||
						<span class="state">
 | 
			
		||||
							<span v-if="suspended" class="suspended">Suspended</span>
 | 
			
		||||
							<span v-if="limited" class="limited">Limited</span>
 | 
			
		||||
							<span v-if="silenced" class="silenced">Silenced</span>
 | 
			
		||||
							<span v-if="moderator" class="moderator">Moderator</span>
 | 
			
		||||
						</span>
 | 
			
		||||
@@ -219,6 +220,7 @@ let ips = $ref(null);
 | 
			
		||||
let ap = $ref(null);
 | 
			
		||||
let moderator = $ref(false);
 | 
			
		||||
let silenced = $ref(false);
 | 
			
		||||
let limited = $ref(false);
 | 
			
		||||
let suspended = $ref(false);
 | 
			
		||||
let moderationNote = $ref('');
 | 
			
		||||
const filesPagination = {
 | 
			
		||||
@@ -244,6 +246,7 @@ function createFetcher() {
 | 
			
		||||
			ips = _ips;
 | 
			
		||||
			moderator = info.isModerator;
 | 
			
		||||
			silenced = info.isSilenced;
 | 
			
		||||
			limited = info.isLimited;
 | 
			
		||||
			suspended = info.isSuspended;
 | 
			
		||||
			moderationNote = info.moderationNote;
 | 
			
		||||
 | 
			
		||||
@@ -485,7 +488,7 @@ definePageMetadata(computed(() => ({
 | 
			
		||||
				display: none;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			> .suspended, > .silenced, > .moderator {
 | 
			
		||||
			> .suspended, > .limited, > .silenced, > .moderator {
 | 
			
		||||
				display: inline-block;
 | 
			
		||||
				border: solid 1px;
 | 
			
		||||
				border-radius: 6px;
 | 
			
		||||
@@ -498,6 +501,11 @@ definePageMetadata(computed(() => ({
 | 
			
		||||
				border-color: var(--error);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			> .limited {
 | 
			
		||||
				color: var(--error);
 | 
			
		||||
				border-color: var(--error);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			> .silenced {
 | 
			
		||||
				color: var(--warn);
 | 
			
		||||
				border-color: var(--warn);
 | 
			
		||||
 
 | 
			
		||||
@@ -2770,6 +2770,7 @@ type UserDetailed = UserLite & {
 | 
			
		||||
    isModerator: boolean;
 | 
			
		||||
    isMuted: boolean;
 | 
			
		||||
    isSilenced: boolean;
 | 
			
		||||
    isLimited: boolean;
 | 
			
		||||
    isSuspended: boolean;
 | 
			
		||||
    lang: string | null;
 | 
			
		||||
    lastFetchedAt?: DateString;
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,7 @@ export type UserDetailed = UserLite & {
 | 
			
		||||
	isModerator: boolean;
 | 
			
		||||
	isMuted: boolean;
 | 
			
		||||
	isSilenced: boolean;
 | 
			
		||||
	isLimited: boolean;
 | 
			
		||||
	isSuspended: boolean;
 | 
			
		||||
	lang: string | null;
 | 
			
		||||
	lastFetchedAt?: DateString;
 | 
			
		||||
@@ -410,7 +411,7 @@ export type Announcement = {
 | 
			
		||||
	imageUrl: string | null;
 | 
			
		||||
	isRead?: boolean;
 | 
			
		||||
	isPrivate: boolean;
 | 
			
		||||
	closeDuration: number; 
 | 
			
		||||
	closeDuration: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type Antenna = {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user