Enhance: アカウント移行機能を使用したユーザーに対してのモデレーションの強化 (#719)
* fix * fix * fix * Feat: アカウント移行機能のモデレーションを行いやすくした * コミット忘れ * 文章を組み立てるのやめた * Fix test * Fix test * updateModerationNote から mergeModerationNote に * updateAccountMoveLogs から insertAccountMoveLog に --------- Co-authored-by: nenohi <nenohi@nenohi.net> Co-authored-by: nenohi <kimutipartylove@gmail.com>
This commit is contained in:
parent
2fe5bb0bb3
commit
6c732d1bfd
29 changed files with 593 additions and 13 deletions
|
@ -0,0 +1,19 @@
|
|||
export class Useraccountmovelogs1724749627479 {
|
||||
name = 'Useraccountmovelogs1724749627479'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`CREATE TABLE "user_account_move_log" ("id" character varying(32) NOT NULL, "movedToId" character varying(32) NOT NULL, "movedFromId" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_8ffd4ae965a5e3a0fbf4b084212" PRIMARY KEY ("id")); COMMENT ON COLUMN "user_account_move_log"."createdAt" IS 'The created date of the UserIp.'`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_d5ee7d4d1b5e7a69d8855ab069" ON "user_account_move_log" ("movedToId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_82930731d6390e7bb429a1938f" ON "user_account_move_log" ("movedFromId") `);
|
||||
await queryRunner.query(`ALTER TABLE "user_account_move_log" ADD CONSTRAINT "FK_d5ee7d4d1b5e7a69d8855ab0696" FOREIGN KEY ("movedToId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "user_account_move_log" ADD CONSTRAINT "FK_82930731d6390e7bb429a1938f8" FOREIGN KEY ("movedFromId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "user_account_move_log" DROP CONSTRAINT "FK_82930731d6390e7bb429a1938f8"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_account_move_log" DROP CONSTRAINT "FK_d5ee7d4d1b5e7a69d8855ab0696"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_82930731d6390e7bb429a1938f"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_d5ee7d4d1b5e7a69d8855ab069"`);
|
||||
await queryRunner.query(`DROP TABLE "user_account_move_log"`);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ import { bindThis } from '@/decorators.js';
|
|||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js';
|
||||
import type { BlockingsRepository, FollowingsRepository, InstancesRepository, MutingsRepository, UserListMembershipsRepository, UsersRepository } from '@/models/_.js';
|
||||
import type { BlockingsRepository, FollowingsRepository, InstancesRepository, MutingsRepository, UserListMembershipsRepository, UserAccountMoveLogRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import type { RelationshipJobData, ThinUser } from '@/queue/types.js';
|
||||
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
|
@ -48,6 +48,15 @@ export class AccountMoveService {
|
|||
@Inject(DI.instancesRepository)
|
||||
private instancesRepository: InstancesRepository,
|
||||
|
||||
@Inject(DI.userProfilesRepository)
|
||||
private userProfilesRepository: UserProfilesRepository,
|
||||
|
||||
@Inject(DI.userAccountMoveLogRepository)
|
||||
private userAccountMoveLogRepository: UserAccountMoveLogRepository,
|
||||
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
|
||||
private userEntityService: UserEntityService,
|
||||
private idService: IdService,
|
||||
private apPersonService: ApPersonService,
|
||||
|
@ -119,6 +128,8 @@ export class AccountMoveService {
|
|||
this.copyBlocking(src, dst),
|
||||
this.copyMutings(src, dst),
|
||||
this.updateLists(src, dst),
|
||||
this.mergeModerationNote(src, dst),
|
||||
this.insertAccountMoveLog(src, dst),
|
||||
]);
|
||||
} catch {
|
||||
/* skip if any error happens */
|
||||
|
@ -256,6 +267,32 @@ export class AccountMoveService {
|
|||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async mergeModerationNote(src: ThinUser, dst: MiUser): Promise<void> {
|
||||
const srcprofile = await this.userProfilesRepository.findOneBy({ userId: src.id });
|
||||
const dstprofile = await this.userProfilesRepository.findOneBy({ userId: dst.id });
|
||||
|
||||
if (!srcprofile || !dstprofile) return;
|
||||
|
||||
await this.userProfilesRepository.update({ userId: dst.id }, {
|
||||
moderationNote: srcprofile.moderationNote + '\n' + dstprofile.moderationNote,
|
||||
});
|
||||
|
||||
await this.userProfilesRepository.update({ userId: src.id }, {
|
||||
moderationNote: srcprofile.moderationNote + '\n' + dstprofile.moderationNote,
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async insertAccountMoveLog(src: ThinUser, dst: MiUser): Promise<void> {
|
||||
await this.userAccountMoveLogRepository.insert({
|
||||
id: this.idService.gen(),
|
||||
movedToId: dst.id,
|
||||
movedFromId: src.id,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async adjustFollowingCounts(localFollowerIds: string[], oldAccount: MiUser): Promise<void> {
|
||||
if (localFollowerIds.length === 0) return;
|
||||
|
|
|
@ -101,6 +101,7 @@ import { HashtagEntityService } from './entities/HashtagEntityService.js';
|
|||
import { InstanceEntityService } from './entities/InstanceEntityService.js';
|
||||
import { InviteCodeEntityService } from './entities/InviteCodeEntityService.js';
|
||||
import { ModerationLogEntityService } from './entities/ModerationLogEntityService.js';
|
||||
import { UserAccountMoveLogEntityService } from './entities/UserAccountMoveLogEntityService.js';
|
||||
import { MutingEntityService } from './entities/MutingEntityService.js';
|
||||
import { RenoteMutingEntityService } from './entities/RenoteMutingEntityService.js';
|
||||
import { NoteEntityService } from './entities/NoteEntityService.js';
|
||||
|
@ -242,6 +243,7 @@ const $HashtagEntityService: Provider = { provide: 'HashtagEntityService', useEx
|
|||
const $InstanceEntityService: Provider = { provide: 'InstanceEntityService', useExisting: InstanceEntityService };
|
||||
const $InviteCodeEntityService: Provider = { provide: 'InviteCodeEntityService', useExisting: InviteCodeEntityService };
|
||||
const $ModerationLogEntityService: Provider = { provide: 'ModerationLogEntityService', useExisting: ModerationLogEntityService };
|
||||
const $UserAccountMoveLogEntityService: Provider = { provide: 'UserAccountMoveLogEntityService', useExisting: UserAccountMoveLogEntityService };
|
||||
const $MutingEntityService: Provider = { provide: 'MutingEntityService', useExisting: MutingEntityService };
|
||||
const $RenoteMutingEntityService: Provider = { provide: 'RenoteMutingEntityService', useExisting: RenoteMutingEntityService };
|
||||
const $NoteEntityService: Provider = { provide: 'NoteEntityService', useExisting: NoteEntityService };
|
||||
|
@ -382,6 +384,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
InstanceEntityService,
|
||||
InviteCodeEntityService,
|
||||
ModerationLogEntityService,
|
||||
UserAccountMoveLogEntityService,
|
||||
MutingEntityService,
|
||||
RenoteMutingEntityService,
|
||||
NoteEntityService,
|
||||
|
@ -518,6 +521,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
$InstanceEntityService,
|
||||
$InviteCodeEntityService,
|
||||
$ModerationLogEntityService,
|
||||
$UserAccountMoveLogEntityService,
|
||||
$MutingEntityService,
|
||||
$RenoteMutingEntityService,
|
||||
$NoteEntityService,
|
||||
|
@ -654,6 +658,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
InstanceEntityService,
|
||||
InviteCodeEntityService,
|
||||
ModerationLogEntityService,
|
||||
UserAccountMoveLogEntityService,
|
||||
MutingEntityService,
|
||||
RenoteMutingEntityService,
|
||||
NoteEntityService,
|
||||
|
@ -789,6 +794,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
$InstanceEntityService,
|
||||
$InviteCodeEntityService,
|
||||
$ModerationLogEntityService,
|
||||
$UserAccountMoveLogEntityService,
|
||||
$MutingEntityService,
|
||||
$RenoteMutingEntityService,
|
||||
$NoteEntityService,
|
||||
|
|
|
@ -682,10 +682,7 @@ export class ApPersonService implements OnModuleInit {
|
|||
// まずサーバー内で検索して様子見
|
||||
let dst = await this.fetchPerson(src.movedToUri);
|
||||
|
||||
if (dst && this.userEntityService.isLocalUser(dst)) {
|
||||
// targetがローカルユーザーだった場合データベースから引っ張ってくる
|
||||
dst = await this.usersRepository.findOneByOrFail({ uri: src.movedToUri }) as MiLocalUser;
|
||||
} else if (dst) {
|
||||
if (dst) {
|
||||
if (movePreventUris.includes(src.movedToUri)) return 'skip: circular move';
|
||||
|
||||
// targetを見つけたことがあるならtargetをupdatePersonする
|
||||
|
@ -702,13 +699,15 @@ export class ApPersonService implements OnModuleInit {
|
|||
dst = await this.resolvePerson(src.movedToUri);
|
||||
}
|
||||
|
||||
if (dst.movedToUri === dst.uri) return 'skip: movedTo itself (dst)'; // ???
|
||||
if (src.movedToUri !== dst.uri) return 'skip: missmatch uri'; // ???
|
||||
if (dst.movedToUri === src.uri) return 'skip: dst.movedToUri === src.uri';
|
||||
const dstUri = this.userEntityService.getUserUri(dst);
|
||||
const srcUri = this.userEntityService.getUserUri(src);
|
||||
if (dst.movedToUri === dstUri) return 'skip: movedTo itself (dst)'; // ???
|
||||
if (src.movedToUri !== dstUri) return 'skip: missmatch uri'; // ???
|
||||
if (dst.movedToUri === srcUri) return 'skip: dst.movedToUri === src.uri';
|
||||
if (!dst.alsoKnownAs || dst.alsoKnownAs.length === 0) {
|
||||
return 'skip: dst.alsoKnownAs is empty';
|
||||
}
|
||||
if (!dst.alsoKnownAs.includes(src.uri)) {
|
||||
if (!dst.alsoKnownAs.includes(srcUri)) {
|
||||
return 'skip: alsoKnownAs does not include from.uri';
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { MiUserAccountMoveLog, UserAccountMoveLogRepository } from '@/models/_.js';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { Packed } from '@/misc/json-schema.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
export class UserAccountMoveLogEntityService {
|
||||
constructor(
|
||||
@Inject(DI.userAccountMoveLogRepository)
|
||||
private userAccountMoveLogRepository: UserAccountMoveLogRepository,
|
||||
|
||||
private userEntityService: UserEntityService,
|
||||
private idService: IdService,
|
||||
) {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async pack(
|
||||
src: MiUserAccountMoveLog['id'] | MiUserAccountMoveLog,
|
||||
me: { id: MiUser['id'] } | null | undefined,
|
||||
) : Promise<Packed<'UserAccountMoveLog'>> {
|
||||
const log = typeof src === 'object' ? src : await this.userAccountMoveLogRepository.findOneByOrFail({ id: src });
|
||||
|
||||
return await awaitAll({
|
||||
id: log.id,
|
||||
createdAt: this.idService.parse(log.id).date.toISOString(),
|
||||
movedFromId: log.movedFromId,
|
||||
movedFrom: this.userEntityService.pack(log.movedFrom ?? log.movedFromId, me, {
|
||||
schema: 'UserDetailed',
|
||||
}),
|
||||
movedToId: log.movedToId,
|
||||
movedTo: this.userEntityService.pack(log.movedTo ?? log.movedToId, me, {
|
||||
schema: 'UserDetailed',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packMany(
|
||||
reports: (MiUserAccountMoveLog['id'] | MiUserAccountMoveLog)[],
|
||||
me: { id: MiUser['id'] } | null | undefined,
|
||||
) : Promise<Packed<'UserAccountMoveLog'>[]> {
|
||||
return (await Promise.allSettled(reports.map(x => this.pack(x, me))))
|
||||
.filter(result => result.status === 'fulfilled')
|
||||
.map(result => (result as PromiseFulfilledResult<Packed<'UserAccountMoveLog'>>).value);
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@ export const DI = {
|
|||
userListMembershipsRepository: Symbol('userListMembershipsRepository'),
|
||||
userNotePiningsRepository: Symbol('userNotePiningsRepository'),
|
||||
userIpsRepository: Symbol('userIpsRepository'),
|
||||
userAccountMoveLogRepository: Symbol('userAccountMoveLogRepository'),
|
||||
usedUsernamesRepository: Symbol('usedUsernamesRepository'),
|
||||
followingsRepository: Symbol('followingsRepository'),
|
||||
followRequestsRepository: Symbol('followRequestsRepository'),
|
||||
|
|
|
@ -37,6 +37,7 @@ import { packedQueueCountSchema } from '@/models/json-schema/queue.js';
|
|||
import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/json-schema/emoji.js';
|
||||
import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js';
|
||||
import { packedUserListMembershipSchema, packedUserListSchema } from '@/models/json-schema/user-list.js';
|
||||
import { packedUserAccountMoveLogSchema } from '@/models/json-schema/user-account-move-log.js';
|
||||
import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
|
||||
import { packedSigninSchema } from '@/models/json-schema/signin.js';
|
||||
import {
|
||||
|
@ -71,6 +72,7 @@ export const refs = {
|
|||
|
||||
UserList: packedUserListSchema,
|
||||
UserListMembership: packedUserListMembershipSchema,
|
||||
UserAccountMoveLog: packedUserAccountMoveLogSchema,
|
||||
Ad: packedAdSchema,
|
||||
Announcement: packedAnnouncementSchema,
|
||||
App: packedAppSchema,
|
||||
|
|
|
@ -73,6 +73,7 @@ import {
|
|||
MiUserProfile,
|
||||
MiUserPublickey,
|
||||
MiUserSecurityKey,
|
||||
MiUserAccountMoveLog,
|
||||
MiWebhook,
|
||||
MiBubbleGameRecord,
|
||||
MiReversiGame,
|
||||
|
@ -200,6 +201,12 @@ const $userListMembershipsRepository: Provider = {
|
|||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userAccountMoveLogRepository: Provider = {
|
||||
provide: DI.userAccountMoveLogRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserAccountMoveLog),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userNotePiningsRepository: Provider = {
|
||||
provide: DI.userNotePiningsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserNotePining),
|
||||
|
@ -524,6 +531,7 @@ const $abuseReportResolversRepository: Provider = {
|
|||
$userListsRepository,
|
||||
$userListFavoritesRepository,
|
||||
$userListMembershipsRepository,
|
||||
$userAccountMoveLogRepository,
|
||||
$userNotePiningsRepository,
|
||||
$userIpsRepository,
|
||||
$usedUsernamesRepository,
|
||||
|
@ -596,6 +604,7 @@ const $abuseReportResolversRepository: Provider = {
|
|||
$userListsRepository,
|
||||
$userListFavoritesRepository,
|
||||
$userListMembershipsRepository,
|
||||
$userAccountMoveLogRepository,
|
||||
$userNotePiningsRepository,
|
||||
$userIpsRepository,
|
||||
$usedUsernamesRepository,
|
||||
|
|
35
packages/backend/src/models/UserAccountMoveLog.ts
Normal file
35
packages/backend/src/models/UserAccountMoveLog.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { Entity, Index, Column, ManyToOne, JoinColumn, PrimaryColumn } from 'typeorm';
|
||||
import { id } from './util/id.js';
|
||||
import { MiUser } from './User.js';
|
||||
|
||||
@Entity('user_account_move_log')
|
||||
export class MiUserAccountMoveLog {
|
||||
@PrimaryColumn(id())
|
||||
public id: string;
|
||||
|
||||
@Index()
|
||||
@Column(id())
|
||||
public movedToId: MiUser['id'];
|
||||
|
||||
@ManyToOne(type => MiUser, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
public movedTo: MiUser | null;
|
||||
|
||||
@Index()
|
||||
@Column(id())
|
||||
public movedFromId: MiUser['id'];
|
||||
|
||||
@ManyToOne(type => MiUser, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
public movedFrom: MiUser | null;
|
||||
|
||||
@Column('timestamp with time zone', {
|
||||
comment: 'The created date of the UserIp.',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
public createdAt: Date;
|
||||
}
|
|
@ -63,6 +63,7 @@ import { MiUserProfile } from '@/models/UserProfile.js';
|
|||
import { MiUserPublickey } from '@/models/UserPublickey.js';
|
||||
import { MiUserSecurityKey } from '@/models/UserSecurityKey.js';
|
||||
import { MiUserMemo } from '@/models/UserMemo.js';
|
||||
import { MiUserAccountMoveLog } from '@/models/UserAccountMoveLog.js';
|
||||
import { MiWebhook } from '@/models/Webhook.js';
|
||||
import { MiChannel } from '@/models/Channel.js';
|
||||
import { MiRetentionAggregation } from '@/models/RetentionAggregation.js';
|
||||
|
@ -146,6 +147,7 @@ export {
|
|||
MiUserMemo,
|
||||
MiBubbleGameRecord,
|
||||
MiReversiGame,
|
||||
MiUserAccountMoveLog,
|
||||
};
|
||||
|
||||
export type AbuseReportResolversRepository = Repository<MiAbuseReportResolver>;
|
||||
|
@ -208,6 +210,7 @@ export type UserPendingsRepository = Repository<MiUserPending>;
|
|||
export type UserProfilesRepository = Repository<MiUserProfile>;
|
||||
export type UserPublickeysRepository = Repository<MiUserPublickey>;
|
||||
export type UserSecurityKeysRepository = Repository<MiUserSecurityKey>;
|
||||
export type UserAccountMoveLogRepository = Repository<MiUserAccountMoveLog>;
|
||||
export type WebhooksRepository = Repository<MiWebhook>;
|
||||
export type ChannelsRepository = Repository<MiChannel>;
|
||||
export type RetentionAggregationsRepository = Repository<MiRetentionAggregation>;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
export const packedUserAccountMoveLogSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'id',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'date-time',
|
||||
},
|
||||
movedToId: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'id',
|
||||
},
|
||||
movedTo: {
|
||||
type: 'object',
|
||||
ref: 'UserDetailed',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
movedFromId: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'id',
|
||||
},
|
||||
movedFrom: {
|
||||
type: 'object',
|
||||
ref: 'UserDetailed',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
},
|
||||
} as const;
|
|
@ -73,6 +73,7 @@ import { MiUserPending } from '@/models/UserPending.js';
|
|||
import { MiUserProfile } from '@/models/UserProfile.js';
|
||||
import { MiUserPublickey } from '@/models/UserPublickey.js';
|
||||
import { MiUserSecurityKey } from '@/models/UserSecurityKey.js';
|
||||
import { MiUserAccountMoveLog } from '@/models/UserAccountMoveLog.js';
|
||||
import { MiWebhook } from '@/models/Webhook.js';
|
||||
import { MiChannel } from '@/models/Channel.js';
|
||||
import { MiRetentionAggregation } from '@/models/RetentionAggregation.js';
|
||||
|
@ -153,6 +154,7 @@ export const entities = [
|
|||
MiUserListMembership,
|
||||
MiUserNotePining,
|
||||
MiUserSecurityKey,
|
||||
MiUserAccountMoveLog,
|
||||
MiUsedUsername,
|
||||
MiFollowing,
|
||||
MiFollowRequest,
|
||||
|
|
|
@ -77,6 +77,7 @@ import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-ab
|
|||
import * as ep___admin_sendEmail from './endpoints/admin/send-email.js';
|
||||
import * as ep___admin_serverInfo from './endpoints/admin/server-info.js';
|
||||
import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js';
|
||||
import * as ep___admin_showUserAccountMoveLogs from './endpoints/admin/show-user-account-move-logs.js';
|
||||
import * as ep___admin_showUser from './endpoints/admin/show-user.js';
|
||||
import * as ep___admin_showUsers from './endpoints/admin/show-users.js';
|
||||
import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js';
|
||||
|
@ -468,6 +469,7 @@ const $admin_resolveAbuseUserReport: Provider = { provide: 'ep:admin/resolve-abu
|
|||
const $admin_sendEmail: Provider = { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default };
|
||||
const $admin_serverInfo: Provider = { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default };
|
||||
const $admin_showModerationLogs: Provider = { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default };
|
||||
const $admin_showUserAccountMoveLogs: Provider = { provide: 'ep:admin/show-user-account-move-logs', useClass: ep___admin_showUserAccountMoveLogs.default };
|
||||
const $admin_showUser: Provider = { provide: 'ep:admin/show-user', useClass: ep___admin_showUser.default };
|
||||
const $admin_showUsers: Provider = { provide: 'ep:admin/show-users', useClass: ep___admin_showUsers.default };
|
||||
const $admin_suspendUser: Provider = { provide: 'ep:admin/suspend-user', useClass: ep___admin_suspendUser.default };
|
||||
|
@ -863,6 +865,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
|||
$admin_sendEmail,
|
||||
$admin_serverInfo,
|
||||
$admin_showModerationLogs,
|
||||
$admin_showUserAccountMoveLogs,
|
||||
$admin_showUser,
|
||||
$admin_showUsers,
|
||||
$admin_suspendUser,
|
||||
|
@ -1252,6 +1255,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
|||
$admin_sendEmail,
|
||||
$admin_serverInfo,
|
||||
$admin_showModerationLogs,
|
||||
$admin_showUserAccountMoveLogs,
|
||||
$admin_showUser,
|
||||
$admin_showUsers,
|
||||
$admin_suspendUser,
|
||||
|
|
|
@ -77,6 +77,7 @@ import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-ab
|
|||
import * as ep___admin_sendEmail from './endpoints/admin/send-email.js';
|
||||
import * as ep___admin_serverInfo from './endpoints/admin/server-info.js';
|
||||
import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js';
|
||||
import * as ep___admin_showUserAccountMoveLogs from './endpoints/admin/show-user-account-move-logs.js';
|
||||
import * as ep___admin_showUser from './endpoints/admin/show-user.js';
|
||||
import * as ep___admin_showUsers from './endpoints/admin/show-users.js';
|
||||
import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js';
|
||||
|
@ -466,6 +467,7 @@ const eps = [
|
|||
['admin/send-email', ep___admin_sendEmail],
|
||||
['admin/server-info', ep___admin_serverInfo],
|
||||
['admin/show-moderation-logs', ep___admin_showModerationLogs],
|
||||
['admin/show-user-account-move-logs', ep___admin_showUserAccountMoveLogs],
|
||||
['admin/show-user', ep___admin_showUser],
|
||||
['admin/show-users', ep___admin_showUsers],
|
||||
['admin/suspend-user', ep___admin_suspendUser],
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { QueryService } from '@/core/QueryService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { UserAccountMoveLogRepository } from '@/models/_.js';
|
||||
import { UserAccountMoveLogEntityService } from '@/core/entities/UserAccountMoveLogEntityService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
kind: 'read:admin:show-account-move-log',
|
||||
|
||||
res: {
|
||||
type: 'array',
|
||||
optional: false, nullable: false,
|
||||
items: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'id',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'date-time',
|
||||
},
|
||||
movedToId: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'id',
|
||||
},
|
||||
movedTo: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
ref: 'UserDetailed',
|
||||
},
|
||||
movedFromId: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'id',
|
||||
},
|
||||
movedFrom: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
ref: 'UserDetailed',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
movedFromId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
movedToId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
constructor(
|
||||
@Inject(DI.userAccountMoveLogRepository)
|
||||
private userAccountMoveLogRepository: UserAccountMoveLogRepository,
|
||||
|
||||
private userAccountMoveLogEntityService: UserAccountMoveLogEntityService,
|
||||
private queryService: QueryService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const query = this.queryService.makePaginationQuery(this.userAccountMoveLogRepository.createQueryBuilder('accountMoveLogs'), ps.sinceId, ps.untilId);
|
||||
|
||||
if (ps.movedFromId != null) {
|
||||
query.andWhere('accountMoveLogs.movedFromId = :movedFromId', { movedFromId: ps.movedFromId });
|
||||
}
|
||||
|
||||
if (ps.movedToId != null) {
|
||||
query.andWhere('accountMoveLogs.movedToId = :movedToId', { movedToId: ps.movedToId });
|
||||
}
|
||||
|
||||
const accountMoveLogs = await query.limit(ps.limit).getMany();
|
||||
|
||||
return await this.userAccountMoveLogEntityService.packMany(accountMoveLogs, me);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue