diff options
| author | Hazelnoot <acomputerdog@gmail.com> | 2025-06-05 08:00:32 +0000 |
|---|---|---|
| committer | Hazelnoot <acomputerdog@gmail.com> | 2025-06-05 08:00:32 +0000 |
| commit | f88253b95f5ed16f23a23796f76ba4e8ea5f99b3 (patch) | |
| tree | bec1ce21bd1b2de423b110a74f4c0dd4199583d4 /packages/backend/src/core | |
| parent | merge: Add option to keep CWs with "RE:" prefix (!1093) (diff) | |
| parent | support link attributions in SkUrlPreviewGroup (diff) | |
| download | sharkey-f88253b95f5ed16f23a23796f76ba4e8ea5f99b3.tar.gz sharkey-f88253b95f5ed16f23a23796f76ba4e8ea5f99b3.tar.bz2 sharkey-f88253b95f5ed16f23a23796f76ba4e8ea5f99b3.zip | |
merge: Report admin UX improvements (!1060)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1060
Approved-by: Marie <github@yuugi.dev>
Approved-by: dakkar <dakkar@thenautilus.net>
Diffstat (limited to 'packages/backend/src/core')
5 files changed, 90 insertions, 14 deletions
diff --git a/packages/backend/src/core/ReversiService.ts b/packages/backend/src/core/ReversiService.ts index e31d9e5b1a..b57ab6d9cb 100644 --- a/packages/backend/src/core/ReversiService.ts +++ b/packages/backend/src/core/ReversiService.ts @@ -588,6 +588,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit { lastFetchedAt: parsed.user1.lastFetchedAt != null ? new Date(parsed.user1.lastFetchedAt) : null, movedAt: parsed.user1.movedAt != null ? new Date(parsed.user1.movedAt) : null, instance: null, + userProfile: null, } : null, user2: parsed.user2 != null ? { ...parsed.user2, @@ -599,6 +600,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit { lastFetchedAt: parsed.user2.lastFetchedAt != null ? new Date(parsed.user2.lastFetchedAt) : null, movedAt: parsed.user2.movedAt != null ? new Date(parsed.user2.movedAt) : null, instance: null, + userProfile: null, } : null, }; } else { diff --git a/packages/backend/src/core/WebhookTestService.ts b/packages/backend/src/core/WebhookTestService.ts index 8c1508df24..c4b01d535b 100644 --- a/packages/backend/src/core/WebhookTestService.ts +++ b/packages/backend/src/core/WebhookTestService.ts @@ -77,6 +77,7 @@ function generateDummyUser(override?: Partial<MiUser>): MiUser { mandatoryCW: null, rejectQuotes: false, allowUnsignedFetch: 'staff', + userProfile: null, attributionDomains: [], ...override, }; @@ -363,8 +364,10 @@ export class WebhookTestService { id: 'dummy-abuse-report1', targetUserId: 'dummy-target-user', targetUser: null, + targetUserInstance: null, reporterId: 'dummy-reporter-user', reporter: null, + reporterInstance: null, assigneeId: null, assignee: null, resolved: false, diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 70ead890ab..c1d877aa12 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -5,13 +5,14 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { AbuseUserReportsRepository } from '@/models/_.js'; +import type { AbuseUserReportsRepository, InstancesRepository, MiInstance, MiUser } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; import type { Packed } from '@/misc/json-schema.js'; import { UserEntityService } from './UserEntityService.js'; +import { InstanceEntityService } from './InstanceEntityService.js'; @Injectable() export class AbuseUserReportEntityService { @@ -19,6 +20,10 @@ export class AbuseUserReportEntityService { @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: AbuseUserReportsRepository, + @Inject(DI.instancesRepository) + private instancesRepository: InstancesRepository, + + private readonly instanceEntityService: InstanceEntityService, private userEntityService: UserEntityService, private idService: IdService, ) { @@ -30,11 +35,14 @@ export class AbuseUserReportEntityService { hint?: { packedReporter?: Packed<'UserDetailedNotMe'>, packedTargetUser?: Packed<'UserDetailedNotMe'>, + packedTargetInstance?: Packed<'FederationInstance'>, packedAssignee?: Packed<'UserDetailedNotMe'>, }, + me?: MiUser | null, ) { const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src }); + // noinspection ES6MissingAwait return await awaitAll({ id: report.id, createdAt: this.idService.parse(report.id).date.toISOString(), @@ -43,13 +51,22 @@ export class AbuseUserReportEntityService { reporterId: report.reporterId, targetUserId: report.targetUserId, assigneeId: report.assigneeId, - reporter: hint?.packedReporter ?? this.userEntityService.pack(report.reporter ?? report.reporterId, null, { + reporter: hint?.packedReporter ?? this.userEntityService.pack(report.reporter ?? report.reporterId, me, { schema: 'UserDetailedNotMe', }), - targetUser: hint?.packedTargetUser ?? this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, { + targetUser: hint?.packedTargetUser ?? this.userEntityService.pack(report.targetUser ?? report.targetUserId, me, { schema: 'UserDetailedNotMe', }), - assignee: report.assigneeId ? hint?.packedAssignee ?? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, { + // return hint, or pack by relation, or fetch and pack by id, or null + targetInstance: hint?.packedTargetInstance ?? ( + report.targetUserInstance + ? this.instanceEntityService.pack(report.targetUserInstance, me) + : report.targetUserHost + ? this.instancesRepository.findOneBy({ host: report.targetUserHost }).then(instance => instance + ? this.instanceEntityService.pack(instance, me) + : null) + : null), + assignee: report.assigneeId ? hint?.packedAssignee ?? this.userEntityService.pack(report.assignee ?? report.assigneeId, me, { schema: 'UserDetailedNotMe', }) : null, forwarded: report.forwarded, @@ -61,21 +78,28 @@ export class AbuseUserReportEntityService { @bindThis public async packMany( reports: MiAbuseUserReport[], + me?: MiUser | null, ) { const _reporters = reports.map(({ reporter, reporterId }) => reporter ?? reporterId); const _targetUsers = reports.map(({ targetUser, targetUserId }) => targetUser ?? targetUserId); const _assignees = reports.map(({ assignee, assigneeId }) => assignee ?? assigneeId).filter(x => x != null); const _userMap = await this.userEntityService.packMany( [..._reporters, ..._targetUsers, ..._assignees], - null, + me, { schema: 'UserDetailedNotMe' }, ).then(users => new Map(users.map(u => [u.id, u]))); + const _targetInstances = reports + .map(({ targetUserInstance, targetUserHost }) => targetUserInstance ?? targetUserHost) + .filter((i): i is MiInstance | string => i != null); + const _instanceMap = await this.instanceEntityService.packMany(await this.instanceEntityService.fetchInstancesByHost(_targetInstances), me) + .then(instances => new Map(instances.map(i => [i.host, i]))); return Promise.all( reports.map(report => { const packedReporter = _userMap.get(report.reporterId); const packedTargetUser = _userMap.get(report.targetUserId); + const packedTargetInstance = report.targetUserHost ? _instanceMap.get(report.targetUserHost) : undefined; const packedAssignee = report.assigneeId != null ? _userMap.get(report.assigneeId) : undefined; - return this.pack(report, { packedReporter, packedTargetUser, packedAssignee }); + return this.pack(report, { packedReporter, packedTargetUser, packedAssignee, packedTargetInstance }, me); }), ); } diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index a2ee4b0505..4ca4ff650b 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -4,6 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; import type { Packed } from '@/misc/json-schema.js'; import type { MiInstance } from '@/models/Instance.js'; import { bindThis } from '@/decorators.js'; @@ -11,7 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { RoleService } from '@/core/RoleService.js'; import { MiUser } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; -import { MiMeta } from '@/models/_.js'; +import type { InstancesRepository, MiMeta } from '@/models/_.js'; @Injectable() export class InstanceEntityService { @@ -19,6 +20,9 @@ export class InstanceEntityService { @Inject(DI.meta) private meta: MiMeta, + @Inject(DI.instancesRepository) + private readonly instancesRepository: InstancesRepository, + private roleService: RoleService, private utilityService: UtilityService, @@ -73,5 +77,28 @@ export class InstanceEntityService { ) { return Promise.all(instances.map(x => this.pack(x, me))); } + + @bindThis + public async fetchInstancesByHost(instances: (MiInstance | MiInstance['host'])[]): Promise<MiInstance[]> { + const result: MiInstance[] = []; + + const toFetch: string[] = []; + for (const instance of instances) { + if (typeof(instance) === 'string') { + toFetch.push(instance); + } else { + result.push(instance); + } + } + + if (toFetch.length > 0) { + const fetched = await this.instancesRepository.findBy({ + host: In(toFetch), + }); + result.push(...fetched); + } + + return result; + } } diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 3524119ba1..326baaefd4 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -487,7 +487,10 @@ export class UserEntityService implements OnModuleInit { includeSecrets: false, }, options); - const user = typeof src === 'object' ? src : await this.usersRepository.findOneByOrFail({ id: src }); + const user = typeof src === 'object' ? src : await this.usersRepository.findOneOrFail({ + where: { id: src }, + relations: { userProfile: true }, + }); // migration if (user.avatarId != null && user.avatarUrl === null) { @@ -521,7 +524,7 @@ export class UserEntityService implements OnModuleInit { const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false; const profile = isDetailed - ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) + ? (opts.userProfile ?? user.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null; let relation: UserRelation | null = null; @@ -556,7 +559,7 @@ export class UserEntityService implements OnModuleInit { } } - const mastoapi = !isDetailed ? opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id }) : null; + const mastoapi = !isDetailed ? opts.userProfile ?? user.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id }) : null; const followingCount = profile == null ? null : (profile.followingVisibility === 'public') || isMe || iAmModerator ? user.followingCount : @@ -785,8 +788,13 @@ export class UserEntityService implements OnModuleInit { const _users = users.filter((user): user is MiUser => typeof user !== 'string'); if (_users.length !== users.length) { _users.push( - ...await this.usersRepository.findBy({ - id: In(users.filter((user): user is string => typeof user === 'string')), + ...await this.usersRepository.find({ + where: { + id: In(users.filter((user): user is string => typeof user === 'string')), + }, + relations: { + userProfile: true, + }, }), ); } @@ -800,8 +808,20 @@ export class UserEntityService implements OnModuleInit { let pinNotes: Map<MiUser['id'], MiUserNotePining[]> = new Map(); if (options?.schema !== 'UserLite') { - profilesMap = await this.userProfilesRepository.findBy({ userId: In(_userIds) }) - .then(profiles => new Map(profiles.map(p => [p.userId, p]))); + const _profiles: MiUserProfile[] = []; + const _profilesToFetch: string[] = []; + for (const user of _users) { + if (user.userProfile) { + _profiles.push(user.userProfile); + } else { + _profilesToFetch.push(user.id); + } + } + if (_profilesToFetch.length > 0) { + const fetched = await this.userProfilesRepository.findBy({ userId: In(_profilesToFetch) }); + _profiles.push(...fetched); + } + profilesMap = new Map(_profiles.map(p => [p.userId, p])); const meId = me ? me.id : null; if (meId) { |