enhance(backend): refine moderation log (#10939)
* wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Update DriveService.ts
This commit is contained in:
@@ -12,6 +12,7 @@ import { bindThis } from '@/decorators.js';
|
||||
import { Packed } from '@/misc/json-schema.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
|
||||
@Injectable()
|
||||
export class AnnouncementService {
|
||||
@@ -24,6 +25,7 @@ export class AnnouncementService {
|
||||
|
||||
private idService: IdService,
|
||||
private globalEventService: GlobalEventService,
|
||||
private moderationLogService: ModerationLogService,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -58,7 +60,7 @@ export class AnnouncementService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async create(values: Partial<MiAnnouncement>): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
|
||||
public async create(values: Partial<MiAnnouncement>, moderator: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
|
||||
const announcement = await this.announcementsRepository.insert({
|
||||
id: this.idService.genId(),
|
||||
createdAt: new Date(),
|
||||
@@ -79,10 +81,21 @@ export class AnnouncementService {
|
||||
this.globalEventService.publishMainStream(values.userId, 'announcementCreated', {
|
||||
announcement: packed,
|
||||
});
|
||||
|
||||
this.moderationLogService.log(moderator, 'createUserAnnouncement', {
|
||||
announcementId: announcement.id,
|
||||
announcement: announcement,
|
||||
userId: values.userId,
|
||||
});
|
||||
} else {
|
||||
this.globalEventService.publishBroadcastStream('announcementCreated', {
|
||||
announcement: packed,
|
||||
});
|
||||
|
||||
this.moderationLogService.log(moderator, 'createGlobalAnnouncement', {
|
||||
announcementId: announcement.id,
|
||||
announcement: announcement,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
@@ -42,6 +42,7 @@ import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { correctFilename } from '@/misc/correct-filename.js';
|
||||
import { isMimeImage } from '@/misc/is-mime-image.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
|
||||
type AddFileArgs = {
|
||||
/** User who wish to add file */
|
||||
@@ -119,6 +120,7 @@ export class DriveService {
|
||||
private globalEventService: GlobalEventService,
|
||||
private queueService: QueueService,
|
||||
private roleService: RoleService,
|
||||
private moderationLogService: ModerationLogService,
|
||||
private driveChart: DriveChart,
|
||||
private perUserDriveChart: PerUserDriveChart,
|
||||
private instanceChart: InstanceChart,
|
||||
@@ -648,7 +650,7 @@ export class DriveService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async deleteFile(file: MiDriveFile, isExpired = false) {
|
||||
public async deleteFile(file: MiDriveFile, isExpired = false, deleter?: MiUser) {
|
||||
if (file.storedInternal) {
|
||||
this.internalStorageService.del(file.accessKey!);
|
||||
|
||||
@@ -671,11 +673,11 @@ export class DriveService {
|
||||
}
|
||||
}
|
||||
|
||||
this.deletePostProcess(file, isExpired);
|
||||
this.deletePostProcess(file, isExpired, deleter);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async deleteFileSync(file: MiDriveFile, isExpired = false) {
|
||||
public async deleteFileSync(file: MiDriveFile, isExpired = false, deleter?: MiUser) {
|
||||
if (file.storedInternal) {
|
||||
this.internalStorageService.del(file.accessKey!);
|
||||
|
||||
@@ -702,11 +704,11 @@ export class DriveService {
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
this.deletePostProcess(file, isExpired);
|
||||
this.deletePostProcess(file, isExpired, deleter);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async deletePostProcess(file: MiDriveFile, isExpired = false) {
|
||||
private async deletePostProcess(file: MiDriveFile, isExpired = false, deleter?: MiUser) {
|
||||
// リモートファイル期限切れ削除後は直リンクにする
|
||||
if (isExpired && file.userHost !== null && file.uri != null) {
|
||||
this.driveFilesRepository.update(file.id, {
|
||||
@@ -733,6 +735,17 @@ export class DriveService {
|
||||
this.instanceChart.updateDrive(file, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (file.userId) {
|
||||
this.globalEventService.publishDriveStream(file.userId, 'fileDeleted', file.id);
|
||||
}
|
||||
|
||||
if (deleter && await this.roleService.isModerator(deleter) && (file.userId !== deleter.id)) {
|
||||
this.moderationLogService.log(deleter, 'deleteDriveFile', {
|
||||
fileId: file.id,
|
||||
fileUserId: file.userId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
@@ -9,6 +9,7 @@ import type { ModerationLogsRepository } from '@/models/_.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { ModerationLogPayloads, moderationLogTypes } from '@/types.js';
|
||||
|
||||
@Injectable()
|
||||
export class ModerationLogService {
|
||||
@@ -21,13 +22,13 @@ export class ModerationLogService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async insertModerationLog(moderator: { id: MiUser['id'] }, type: string, info?: Record<string, any>) {
|
||||
public async log<T extends typeof moderationLogTypes[number]>(moderator: { id: MiUser['id'] }, type: T, info?: ModerationLogPayloads[T]) {
|
||||
await this.moderationLogsRepository.insert({
|
||||
id: this.idService.genId(),
|
||||
createdAt: new Date(),
|
||||
userId: moderator.id,
|
||||
type: type,
|
||||
info: info ?? {},
|
||||
info: (info as any) ?? {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { SearchService } from '@/core/SearchService.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
|
||||
@Injectable()
|
||||
export class NoteDeleteService {
|
||||
@@ -48,6 +49,7 @@ export class NoteDeleteService {
|
||||
private apDeliverManagerService: ApDeliverManagerService,
|
||||
private metaService: MetaService,
|
||||
private searchService: SearchService,
|
||||
private moderationLogService: ModerationLogService,
|
||||
private notesChart: NotesChart,
|
||||
private perUserNotesChart: PerUserNotesChart,
|
||||
private instanceChart: InstanceChart,
|
||||
@@ -58,7 +60,7 @@ export class NoteDeleteService {
|
||||
* @param user 投稿者
|
||||
* @param note 投稿
|
||||
*/
|
||||
async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false) {
|
||||
async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false, deleter?: MiUser) {
|
||||
const deletedAt = new Date();
|
||||
const cascadingNotes = await this.findCascadingNotes(note);
|
||||
|
||||
@@ -131,6 +133,14 @@ export class NoteDeleteService {
|
||||
id: note.id,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
if (deleter && (note.userId !== deleter.id)) {
|
||||
this.moderationLogService.log(deleter, 'deleteNote', {
|
||||
noteId: note.id,
|
||||
noteUserId: note.userId,
|
||||
note: note,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
@@ -18,6 +18,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||
|
||||
@@ -98,6 +99,7 @@ export class RoleService implements OnApplicationShutdown {
|
||||
private userEntityService: UserEntityService,
|
||||
private globalEventService: GlobalEventService,
|
||||
private idService: IdService,
|
||||
private moderationLogService: ModerationLogService,
|
||||
) {
|
||||
//this.onMessage = this.onMessage.bind(this);
|
||||
|
||||
@@ -374,9 +376,11 @@ export class RoleService implements OnApplicationShutdown {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null): Promise<void> {
|
||||
public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null, moderator?: MiUser): Promise<void> {
|
||||
const now = new Date();
|
||||
|
||||
const role = await this.rolesRepository.findOneByOrFail({ id: roleId });
|
||||
|
||||
const existing = await this.roleAssignmentsRepository.findOneBy({
|
||||
roleId: roleId,
|
||||
userId: userId,
|
||||
@@ -406,10 +410,19 @@ export class RoleService implements OnApplicationShutdown {
|
||||
});
|
||||
|
||||
this.globalEventService.publishInternalEvent('userRoleAssigned', created);
|
||||
|
||||
if (moderator) {
|
||||
this.moderationLogService.log(moderator, 'assignRole', {
|
||||
roleId: roleId,
|
||||
roleName: role.name,
|
||||
userId: userId,
|
||||
expiresAt: expiresAt ? expiresAt.toISOString() : null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async unassign(userId: MiUser['id'], roleId: MiRole['id']): Promise<void> {
|
||||
public async unassign(userId: MiUser['id'], roleId: MiRole['id'], moderator?: MiUser): Promise<void> {
|
||||
const now = new Date();
|
||||
|
||||
const existing = await this.roleAssignmentsRepository.findOneBy({ roleId, userId });
|
||||
@@ -430,6 +443,15 @@ export class RoleService implements OnApplicationShutdown {
|
||||
});
|
||||
|
||||
this.globalEventService.publishInternalEvent('userRoleUnassigned', existing);
|
||||
|
||||
if (moderator) {
|
||||
const role = await this.rolesRepository.findOneByOrFail({ id: roleId });
|
||||
this.moderationLogService.log(moderator, 'unassignRole', {
|
||||
roleId: roleId,
|
||||
roleName: role.name,
|
||||
userId: userId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -451,6 +473,26 @@ export class RoleService implements OnApplicationShutdown {
|
||||
redisPipeline.exec();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async update(role: MiRole, params: Partial<MiRole>, moderator?: MiUser): Promise<void> {
|
||||
const date = new Date();
|
||||
await this.rolesRepository.update(role.id, {
|
||||
updatedAt: date,
|
||||
...params,
|
||||
});
|
||||
|
||||
const updated = await this.rolesRepository.findOneByOrFail({ id: role.id });
|
||||
this.globalEventService.publishInternalEvent('roleUpdated', updated);
|
||||
|
||||
if (moderator) {
|
||||
this.moderationLogService.log(moderator, 'updateRole', {
|
||||
roleId: role.id,
|
||||
before: role,
|
||||
after: updated,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public dispose(): void {
|
||||
this.redisForSub.off('message', this.onMessage);
|
||||
|
@@ -81,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
forExistingUsers: ps.forExistingUsers,
|
||||
needConfirmationToRead: ps.needConfirmationToRead,
|
||||
userId: ps.userId,
|
||||
});
|
||||
}, me);
|
||||
|
||||
return packed;
|
||||
});
|
||||
|
@@ -79,7 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [],
|
||||
});
|
||||
|
||||
this.moderationLogService.insertModerationLog(me, 'addEmoji', {
|
||||
this.moderationLogService.log(me, 'addCustomEmoji', {
|
||||
emojiId: emoji.id,
|
||||
});
|
||||
|
||||
|
@@ -30,7 +30,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
this.queueService.destroy();
|
||||
|
||||
this.moderationLogService.insertModerationLog(me, 'clearQueue');
|
||||
this.moderationLogService.log(me, 'clearQueue');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
break;
|
||||
}
|
||||
|
||||
this.moderationLogService.insertModerationLog(me, 'promoteQueue');
|
||||
this.moderationLogService.log(me, 'promoteQueue');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
return;
|
||||
}
|
||||
|
||||
await this.roleService.assign(user.id, role.id, ps.expiresAt ? new Date(ps.expiresAt) : null);
|
||||
await this.roleService.assign(user.id, role.id, ps.expiresAt ? new Date(ps.expiresAt) : null, me);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -81,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new ApiError(meta.errors.noSuchUser);
|
||||
}
|
||||
|
||||
await this.roleService.unassign(user.id, role.id);
|
||||
await this.roleService.unassign(user.id, role.id, me);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import type { RolesRepository } from '@/models/_.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin', 'role'],
|
||||
@@ -70,16 +71,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
@Inject(DI.rolesRepository)
|
||||
private rolesRepository: RolesRepository,
|
||||
|
||||
private globalEventService: GlobalEventService,
|
||||
private roleService: RoleService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps) => {
|
||||
const roleExist = await this.rolesRepository.exist({ where: { id: ps.roleId } });
|
||||
if (!roleExist) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const role = await this.rolesRepository.findOneBy({ id: ps.roleId });
|
||||
if (role == null) {
|
||||
throw new ApiError(meta.errors.noSuchRole);
|
||||
}
|
||||
|
||||
const date = new Date();
|
||||
await this.rolesRepository.update(ps.roleId, {
|
||||
await this.roleService.update(role, {
|
||||
updatedAt: date,
|
||||
name: ps.name,
|
||||
description: ps.description,
|
||||
@@ -95,9 +96,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
canEditMembersByModerator: ps.canEditMembersByModerator,
|
||||
displayOrder: ps.displayOrder,
|
||||
policies: ps.policies,
|
||||
});
|
||||
const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId });
|
||||
this.globalEventService.publishInternalEvent('roleUpdated', updated);
|
||||
}, me);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -62,6 +62,8 @@ export const paramDef = {
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
type: { type: 'string', nullable: true },
|
||||
userId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
@@ -78,6 +80,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const query = this.queryService.makePaginationQuery(this.moderationLogsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId);
|
||||
|
||||
if (ps.type != null) {
|
||||
query.andWhere('report.type = :type', { type: ps.type });
|
||||
}
|
||||
|
||||
if (ps.userId != null) {
|
||||
query.andWhere('report.userId = :userId', { userId: ps.userId });
|
||||
}
|
||||
|
||||
const reports = await query.limit(ps.limit).getMany();
|
||||
|
||||
return await this.moderationLogEntityService.packMany(reports);
|
||||
|
@@ -60,7 +60,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
isSuspended: true,
|
||||
});
|
||||
|
||||
this.moderationLogService.insertModerationLog(me, 'suspend', {
|
||||
this.moderationLogService.log(me, 'suspend', {
|
||||
targetId: user.id,
|
||||
});
|
||||
|
||||
|
@@ -45,7 +45,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
isSuspended: false,
|
||||
});
|
||||
|
||||
this.moderationLogService.insertModerationLog(me, 'unsuspend', {
|
||||
this.moderationLogService.log(me, 'unsuspend', {
|
||||
targetId: user.id,
|
||||
});
|
||||
|
||||
|
@@ -441,8 +441,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
set.manifestJsonOverride = ps.manifestJsonOverride;
|
||||
}
|
||||
|
||||
const before = await this.metaService.fetch(true);
|
||||
|
||||
await this.metaService.update(set);
|
||||
this.moderationLogService.insertModerationLog(me, 'updateMeta');
|
||||
|
||||
const after = await this.metaService.fetch(true);
|
||||
|
||||
this.moderationLogService.log(me, 'updateServerSettings', {
|
||||
before,
|
||||
after,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
@@ -32,6 +33,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
|
||||
@Inject(DI.userProfilesRepository)
|
||||
private userProfilesRepository: UserProfilesRepository,
|
||||
|
||||
private moderationLogService: ModerationLogService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const user = await this.usersRepository.findOneBy({ id: ps.userId });
|
||||
@@ -40,9 +43,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new Error('user not found');
|
||||
}
|
||||
|
||||
const currentProfile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
|
||||
|
||||
await this.userProfilesRepository.update({ userId: user.id }, {
|
||||
moderationNote: ps.text,
|
||||
});
|
||||
|
||||
this.moderationLogService.log(me, 'updateUserNote', {
|
||||
userId: user.id,
|
||||
before: currentProfile.moderationNote,
|
||||
after: ps.text,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -65,11 +65,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new ApiError(meta.errors.accessDenied);
|
||||
}
|
||||
|
||||
// Delete
|
||||
await this.driveService.deleteFile(file);
|
||||
|
||||
// Publish fileDeleted event
|
||||
this.globalEventService.publishDriveStream(me.id, 'fileDeleted', file.id);
|
||||
await this.driveService.deleteFile(file, me);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
|
||||
// この操作を行うのが投稿者とは限らない(例えばモデレーター)ため
|
||||
await this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: note.userId }), note);
|
||||
await this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: note.userId }), note, false, me);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -26,3 +26,82 @@ export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as
|
||||
export const mutedNoteReasons = ['word', 'manual', 'spam', 'other'] as const;
|
||||
|
||||
export const ffVisibility = ['public', 'followers', 'private'] as const;
|
||||
|
||||
export const moderationLogTypes = [
|
||||
'updateServerSettings',
|
||||
'suspend',
|
||||
'unsuspend',
|
||||
'updateUserNote',
|
||||
'addCustomEmoji',
|
||||
'assignRole',
|
||||
'unassignRole',
|
||||
'updateRole',
|
||||
'deleteRole',
|
||||
'clearQueue',
|
||||
'promoteQueue',
|
||||
'deleteDriveFile',
|
||||
'deleteNote',
|
||||
'createGlobalAnnouncement',
|
||||
'createUserAnnouncement',
|
||||
] as const;
|
||||
|
||||
export type ModerationLogPayloads = {
|
||||
updateServerSettings: {
|
||||
before: any | null;
|
||||
after: any | null;
|
||||
};
|
||||
suspend: {
|
||||
targetId: string;
|
||||
};
|
||||
unsuspend: {
|
||||
targetId: string;
|
||||
};
|
||||
updateUserNote: {
|
||||
userId: string;
|
||||
before: string | null;
|
||||
after: string | null;
|
||||
};
|
||||
addCustomEmoji: {
|
||||
emojiId: string;
|
||||
};
|
||||
assignRole: {
|
||||
userId: string;
|
||||
roleId: string;
|
||||
roleName: string;
|
||||
expiresAt: string | null;
|
||||
};
|
||||
unassignRole: {
|
||||
userId: string;
|
||||
roleId: string;
|
||||
roleName: string;
|
||||
};
|
||||
updateRole: {
|
||||
roleId: string;
|
||||
before: any;
|
||||
after: any;
|
||||
};
|
||||
deleteRole: {
|
||||
roleId: string;
|
||||
roleName: string;
|
||||
};
|
||||
clearQueue: Record<string, never>;
|
||||
promoteQueue: Record<string, never>;
|
||||
deleteDriveFile: {
|
||||
fileId: string;
|
||||
fileUserId: string | null;
|
||||
};
|
||||
deleteNote: {
|
||||
noteId: string;
|
||||
noteUserId: string;
|
||||
note: any;
|
||||
};
|
||||
createGlobalAnnouncement: {
|
||||
announcementId: string;
|
||||
announcement: any;
|
||||
};
|
||||
createUserAnnouncement: {
|
||||
announcementId: string;
|
||||
announcement: any;
|
||||
userId: string;
|
||||
};
|
||||
};
|
||||
|
Reference in New Issue
Block a user