https://project.mdnd-it.cc/work_packages/94
This commit is contained in:
2025-08-23 04:25:28 +02:00
parent 725516ad6c
commit 19cfa031d0
25823 changed files with 1095587 additions and 2801760 deletions
@@ -0,0 +1,8 @@
export interface CreateOrganizationCommand {
name: string;
contactfname: string;
contactlname: string;
contactphone: string;
contactemail: string;
url?: string;
}
@@ -0,0 +1,32 @@
import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository';
import { CreateOrganizationCommand } from './CreateOrganizationCommand';
import { ShortOrganizationDto } from '../../DTOs/OrganizationDto';
import { OrganizationAggregate, OrganizationState } from '../../../Domain/Organization/OrganizationAggregate';
import { OrganizationMapper } from '../../DTOs/Mappers/OrganizationMapper';
export class CreateOrganizationCommandHandler {
constructor(private readonly orgRepo: IOrganizationRepository) {}
async execute(cmd: CreateOrganizationCommand): Promise<ShortOrganizationDto> {
try {
const org = new OrganizationAggregate();
org.name = cmd.name;
org.contactfname = cmd.contactfname;
org.contactlname = cmd.contactlname;
org.contactphone = cmd.contactphone;
org.contactemail = cmd.contactemail;
org.url = cmd.url || null;
org.state = OrganizationState.REGISTERED;
const created = await this.orgRepo.create(org);
return OrganizationMapper.toShortDto(created);
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('duplicate key value violates unique constraint')) {
throw new Error('Organization with this name or contact email already exists');
}
}
throw new Error('Failed to create organization');
}
}
}
@@ -0,0 +1,4 @@
export interface DeleteOrganizationCommand {
id: string;
soft?: boolean;
}
@@ -0,0 +1,16 @@
import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository';
import { DeleteOrganizationCommand } from './DeleteOrganizationCommand';
export class DeleteOrganizationCommandHandler {
constructor(private readonly orgRepo: IOrganizationRepository) {}
async execute(cmd: DeleteOrganizationCommand): Promise<boolean> {
if (cmd.soft) {
await this.orgRepo.softDelete(cmd.id);
} else {
await this.orgRepo.delete(cmd.id);
}
return true;
}
}
@@ -0,0 +1,6 @@
export interface ProcessOrgAuthCallbackCommand {
organizationId: string;
userId: string;
status: 'ok' | 'not_ok';
authToken?: string;
}
@@ -0,0 +1,123 @@
import { IUserRepository } from '../../../Domain/IRepository/IUserRepository';
import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository';
import { ProcessOrgAuthCallbackCommand } from './ProcessOrgAuthCallbackCommand';
import { logAuth, logDatabase, logError, logWarning } from '../../Services/Logger';
export interface ProcessOrgAuthCallbackResponse {
success: boolean;
message: string;
updatedFields?: string[];
}
export class ProcessOrgAuthCallbackCommandHandler {
constructor(
private readonly userRepo: IUserRepository,
private readonly orgRepo: IOrganizationRepository
) {}
async execute(cmd: ProcessOrgAuthCallbackCommand): Promise<ProcessOrgAuthCallbackResponse> {
const startTime = Date.now();
try {
logAuth('Processing organization authentication callback', cmd.userId, {
organizationId: cmd.organizationId,
status: cmd.status,
hasAuthToken: !!cmd.authToken
});
// Verify organization exists
const organization = await this.orgRepo.findById(cmd.organizationId);
if (!organization) {
logWarning('Organization not found for auth callback', {
organizationId: cmd.organizationId,
userId: cmd.userId
});
return {
success: false,
message: 'Organization not found'
};
}
// Verify user exists
const user = await this.userRepo.findById(cmd.userId);
if (!user) {
logWarning('User not found for auth callback', {
organizationId: cmd.organizationId,
userId: cmd.userId
});
return {
success: false,
message: 'User not found'
};
}
// Verify user belongs to the organization
if (user.orgid !== cmd.organizationId) {
logWarning('User does not belong to organization for auth callback', {
organizationId: cmd.organizationId,
userId: cmd.userId,
userOrgId: user.orgid
});
return {
success: false,
message: 'User does not belong to this organization'
};
}
if (cmd.status === 'not_ok') {
logAuth('Organization authentication failed', cmd.userId, {
organizationId: cmd.organizationId,
organizationName: organization.name
});
return {
success: false,
message: 'Organization authentication failed'
};
}
// Update user's organization login date
const now = new Date();
const updatedUser = await this.userRepo.update(cmd.userId, {
Orglogindate: now
});
if (!updatedUser) {
logError('Failed to update user organization login date', new Error('User update returned null'));
return {
success: false,
message: 'Failed to update user login information'
};
}
logAuth('Organization authentication successful', cmd.userId, {
organizationId: cmd.organizationId,
organizationName: organization.name,
orgLoginDate: now.toISOString(),
executionTime: Date.now() - startTime
});
logDatabase('User organization login date updated',
`userId: ${cmd.userId}, orgId: ${cmd.organizationId}`,
Date.now() - startTime,
{
userId: cmd.userId,
organizationId: cmd.organizationId,
newOrgLoginDate: now.toISOString()
}
);
return {
success: true,
message: 'Organization authentication successful',
updatedFields: ['Orglogindate']
};
} catch (error) {
logError('ProcessOrgAuthCallbackCommandHandler error', error as Error);
return {
success: false,
message: 'Internal error processing authentication callback'
};
}
}
}
@@ -0,0 +1,14 @@
import { OrganizationStateType } from '../../../Domain/Organization/OrganizationAggregate';
export interface UpdateOrganizationCommand {
id: string;
name?: string;
contactfname?: string;
contactlname?: string;
contactphone?: string;
contactemail?: string;
url?: string;
state?: OrganizationStateType;
userinorg?: number;
maxOrganizationalDecks?: number | null;
}
@@ -0,0 +1,15 @@
import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository';
import { UpdateOrganizationCommand } from './UpdateOrganizationCommand';
import { ShortOrganizationDto } from '../../DTOs/OrganizationDto';
import { OrganizationMapper } from '../../DTOs/Mappers/OrganizationMapper';
export class UpdateOrganizationCommandHandler {
constructor(private readonly orgRepo: IOrganizationRepository) {}
async execute(cmd: UpdateOrganizationCommand): Promise<ShortOrganizationDto | null> {
const updated = await this.orgRepo.update(cmd.id, { ...cmd });
if (!updated) return null;
return OrganizationMapper.toShortDto(updated);
}
}
@@ -0,0 +1,3 @@
export interface GetOrganizationByIdQuery {
id: string;
}
@@ -0,0 +1,15 @@
import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository';
import { GetOrganizationByIdQuery } from './GetOrganizationByIdQuery';
import { ShortOrganizationDto } from '../../DTOs/OrganizationDto';
import { OrganizationMapper } from '../../DTOs/Mappers/OrganizationMapper';
export class GetOrganizationByIdQueryHandler {
constructor(private readonly orgRepo: IOrganizationRepository) {}
async execute(query: GetOrganizationByIdQuery): Promise<ShortOrganizationDto | null> {
const org = await this.orgRepo.findById(query.id);
if (!org) return null;
return OrganizationMapper.toShortDto(org);
}
}
@@ -0,0 +1,3 @@
export interface GetOrganizationLoginUrlQuery {
organizationId: string;
}
@@ -0,0 +1,56 @@
import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository';
import { GetOrganizationLoginUrlQuery } from './GetOrganizationLoginUrlQuery';
import { OrganizationLoginUrlDto } from '../../DTOs/OrganizationDto';
import { logDatabase, logError, logWarning } from '../../Services/Logger';
export class GetOrganizationLoginUrlQueryHandler {
constructor(private readonly orgRepo: IOrganizationRepository) {}
async execute(query: GetOrganizationLoginUrlQuery): Promise<OrganizationLoginUrlDto | null> {
const startTime = Date.now();
try {
logDatabase('Getting organization login URL', `organizationId: ${query.organizationId}`, 0, {
organizationId: query.organizationId
});
const organization = await this.orgRepo.findById(query.organizationId);
if (!organization) {
logWarning('Organization not found for login URL request', {
organizationId: query.organizationId
});
return null;
}
if (!organization.url) {
logWarning('Organization has no configured login URL', {
organizationId: query.organizationId,
organizationName: organization.name
});
return null;
}
const result: OrganizationLoginUrlDto = {
organizationId: organization.id,
organizationName: organization.name,
loginUrl: organization.url
};
logDatabase('Organization login URL retrieved successfully',
`organizationId: ${query.organizationId}`,
Date.now() - startTime,
{
organizationId: organization.id,
organizationName: organization.name,
hasUrl: !!organization.url
}
);
return result;
} catch (error) {
logError('GetOrganizationLoginUrlQueryHandler error', error as Error);
throw new Error('Failed to retrieve organization login URL');
}
}
}
@@ -0,0 +1,5 @@
export interface GetOrganizationsByPageQuery {
from: number;
to: number;
includeDeleted?: boolean;
}
@@ -0,0 +1,60 @@
import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository';
import { GetOrganizationsByPageQuery } from './GetOrganizationsByPageQuery';
import { ShortOrganizationDto } from '../../DTOs/OrganizationDto';
import { OrganizationMapper } from '../../DTOs/Mappers/OrganizationMapper';
import { logError, logRequest } from '../../Services/Logger';
export class GetOrganizationsByPageQueryHandler {
constructor(private readonly orgRepo: IOrganizationRepository) {}
async execute(query: GetOrganizationsByPageQuery): Promise<{ organizations: ShortOrganizationDto[], totalCount: number }> {
try {
// Validate pagination parameters
if (query.from < 0 || query.to < query.from) {
throw new Error('Invalid pagination parameters');
}
const limit = query.to - query.from + 1;
if (limit > 100) {
throw new Error('Page size too large. Maximum 100 records per request');
}
logRequest('Get organizations by page query started', undefined, undefined, {
from: query.from,
to: query.to,
includeDeleted: query.includeDeleted || false
});
const result = query.includeDeleted
? await this.orgRepo.findByPageIncludingDeleted(query.from, query.to)
: await this.orgRepo.findByPage(query.from, query.to);
logRequest('Get organizations by page query completed', undefined, undefined, {
from: query.from,
to: query.to,
returned: result.organizations.length,
totalCount: result.totalCount,
includeDeleted: query.includeDeleted || false
});
return {
organizations: OrganizationMapper.toShortDtoList(result.organizations),
totalCount: result.totalCount
};
} catch (error) {
logError('GetOrganizationsByPageQueryHandler error', error instanceof Error ? error : new Error(String(error)));
// Handle database errors
if (error instanceof Error && error.message.includes('database')) {
throw new Error('Database connection error');
}
// Re-throw validation errors as-is
if (error instanceof Error && (error.message.includes('Invalid pagination') || error.message.includes('Page size'))) {
throw error;
}
throw new Error('Failed to retrieve organizations');
}
}
}