Files
fluxer/fluxer_api/src/api/guild/services/data/GuildOwnershipService.ts
T

70 lines
2.8 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-or-later
import type {GuildID, UserID} from '@app/api/BrandedTypes';
import {mapGuildToGuildResponse} from '@app/api/guild/GuildModel';
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
import type {GuildDataHelpers} from '@app/api/guild/services/data/GuildDataHelpers';
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
import {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
import {MissingAccessError} from '@fluxer/errors/src/domains/core/MissingAccessError';
import {MissingPermissionsError} from '@fluxer/errors/src/domains/core/MissingPermissionsError';
import {CannotTransferOwnershipToBotError} from '@fluxer/errors/src/domains/guild/CannotTransferOwnershipToBotError';
import {UnknownGuildError} from '@fluxer/errors/src/domains/guild/UnknownGuildError';
import {UnknownGuildMemberError} from '@fluxer/errors/src/domains/guild/UnknownGuildMemberError';
import type {GuildResponse} from '@fluxer/schema/src/domains/guild/GuildResponseSchemas';
const OWNERSHIP_AUDIT_KEYS: ReadonlySet<string> = new Set(['owner_id']);
export class GuildOwnershipService {
constructor(
private readonly guildRepository: GuildRepository,
private readonly userRepository: UserRepository,
private readonly helpers: GuildDataHelpers,
) {}
async transferOwnership(
params: {
userId: UserID;
guildId: GuildID;
newOwnerId: UserID;
},
auditLogReason?: string | null,
): Promise<GuildResponse> {
const {userId, guildId, newOwnerId} = params;
const {guildData} = await this.helpers.getGuildAuthenticated({userId, guildId});
if (guildData.owner_id !== userId.toString()) {
throw new MissingPermissionsError();
}
const user = await this.userRepository.findUnique(userId);
if (!user) throw new MissingAccessError();
const newOwner = await this.guildRepository.getMember(guildId, newOwnerId);
if (!newOwner) {
throw new UnknownGuildMemberError();
}
const newOwnerUser = await this.userRepository.findUnique(newOwnerId);
if (newOwnerUser?.isBot) {
throw new CannotTransferOwnershipToBotError();
}
const guild = await this.guildRepository.findUnique(guildId);
if (!guild) throw new UnknownGuildError();
const previousSnapshot = this.helpers.serializeGuildForAudit(guild);
const previousOwnerId = guild.ownerId;
const updatedGuild = await this.guildRepository.upsertPartial(
guildId,
{owner_id: newOwnerId},
guild.toRow(),
previousOwnerId,
);
await this.helpers.dispatchGuildUpdate(updatedGuild);
await this.helpers.recordAuditLog({
guildId,
userId,
action: AuditLogActionType.GUILD_UPDATE,
targetId: guildId,
auditLogReason: auditLogReason ?? null,
changes: this.helpers.computeGuildChanges(previousSnapshot, updatedGuild, OWNERSHIP_AUDIT_KEYS),
});
return mapGuildToGuildResponse(updatedGuild);
}
}