126 lines
4.4 KiB
TypeScript
126 lines
4.4 KiB
TypeScript
import { IDeckRepository } from '../../../Domain/IRepository/IDeckRepository';
|
|
import { IUserRepository } from '../../../Domain/IRepository/IUserRepository';
|
|
import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository';
|
|
import { CreateDeckCommand } from './CreateDeckCommand';
|
|
import { ShortDeckDto } from '../../DTOs/DeckDto';
|
|
import { DeckAggregate, State, CType } from '../../../Domain/Deck/DeckAggregate';
|
|
import { UserState } from '../../../Domain/User/UserAggregate';
|
|
import { DeckMapper } from '../../DTOs/Mappers/DeckMapper';
|
|
import { AdminBypassService } from '../../Services/AdminBypassService';
|
|
import { logRequest } from '../../Services/Logger';
|
|
|
|
export class CreateDeckCommandHandler {
|
|
constructor(
|
|
private readonly deckRepo: IDeckRepository,
|
|
private readonly userRepo: IUserRepository,
|
|
private readonly orgRepo: IOrganizationRepository
|
|
) {}
|
|
|
|
async execute(cmd: CreateDeckCommand): Promise<ShortDeckDto> {
|
|
try {
|
|
// 1. Get user details
|
|
const user = await this.userRepo.findById(cmd.userid);
|
|
if (!user) {
|
|
throw new Error('User not found');
|
|
}
|
|
|
|
// 2. ADMIN BYPASS - Skip all restrictions
|
|
if (AdminBypassService.shouldBypassRestrictions(user.state)) {
|
|
AdminBypassService.logAdminBypass(
|
|
'CREATE_DECK_BYPASS',
|
|
user.id,
|
|
'new-deck',
|
|
{
|
|
deckName: cmd.name,
|
|
deckType: cmd.type,
|
|
cardCount: cmd.cards.length,
|
|
ctype: cmd.ctype
|
|
}
|
|
);
|
|
return this.createDeck(cmd);
|
|
}
|
|
|
|
// 3. Check deck count limits for regular users
|
|
const userDeckCount = await this.deckRepo.countActiveByUserId(cmd.userid);
|
|
const maxDecks = user.state === UserState.VERIFIED_PREMIUM ? 12 : 8;
|
|
|
|
if (userDeckCount >= maxDecks) {
|
|
throw new Error(`Deck limit exceeded. Maximum ${maxDecks} decks allowed for your account type.`);
|
|
}
|
|
|
|
// 4. Organizational deck restrictions
|
|
if (cmd.ctype === CType.ORGANIZATION) {
|
|
// Only premium users can create organizational decks
|
|
if (user.state !== UserState.VERIFIED_PREMIUM) {
|
|
throw new Error('Only premium users can create organizational decks.');
|
|
}
|
|
|
|
// User must belong to an organization
|
|
if (!user.orgid) {
|
|
throw new Error('You must be a member of an organization to create organizational decks.');
|
|
}
|
|
|
|
// Check organization limits
|
|
const org = await this.orgRepo.findById(user.orgid);
|
|
if (!org) {
|
|
throw new Error('Organization not found.');
|
|
}
|
|
|
|
if (org.maxOrganizationalDecks === null) {
|
|
throw new Error('Organization deck limit not configured. Contact administrator.');
|
|
}
|
|
|
|
const userOrgDeckCount = await this.deckRepo.countOrganizationalByUserId(cmd.userid);
|
|
if (userOrgDeckCount >= org.maxOrganizationalDecks) {
|
|
throw new Error(`Organization deck limit exceeded. Maximum ${org.maxOrganizationalDecks} organizational decks allowed.`);
|
|
}
|
|
}
|
|
|
|
// 5. Create deck with restrictions passed
|
|
return this.createDeck(cmd);
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
throw error; // Re-throw known errors with original message
|
|
}
|
|
throw new Error('Failed to create deck');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Private method to create deck after all validations
|
|
*/
|
|
private async createDeck(cmd: CreateDeckCommand): Promise<ShortDeckDto> {
|
|
const deck = new DeckAggregate();
|
|
deck.name = cmd.name;
|
|
deck.type = cmd.type;
|
|
deck.userid = cmd.userid;
|
|
deck.cards = cmd.cards;
|
|
deck.ctype = cmd.ctype ?? CType.PUBLIC;
|
|
deck.state = State.ACTIVE;
|
|
|
|
// Set organization reference for organizational decks
|
|
if (cmd.ctype === CType.ORGANIZATION) {
|
|
const user = await this.userRepo.findById(cmd.userid);
|
|
if (user?.orgid) {
|
|
const org = await this.orgRepo.findById(user.orgid);
|
|
if (org) {
|
|
deck.organization = org;
|
|
}
|
|
}
|
|
}
|
|
|
|
const created = await this.deckRepo.create(deck);
|
|
|
|
logRequest('Deck created successfully', undefined, undefined, {
|
|
deckId: created.id,
|
|
userId: cmd.userid,
|
|
deckName: cmd.name,
|
|
deckType: cmd.type,
|
|
ctype: cmd.ctype,
|
|
cardCount: cmd.cards.length
|
|
});
|
|
|
|
return DeckMapper.toShortDto(created);
|
|
}
|
|
}
|