Platform Marketing Content: - Add PaginationParams, PaginationMeta, PaginatedResponse interfaces - Fix JwtAuthGuard import paths (common/guards instead of modules/auth) - Add missing fields to CRM interfaces (address, keywords, features, benefits) - Install @nestjs/throttler dependency ERP Suite - Construccion: - Create tsconfig.node.json for web frontend - Add vite-env.d.ts for Vite types - Fix implicit return errors in Express controllers - Prefix unused parameters with underscore ERP Suite - ERP Core: - Export PoolClient type from database config - Fix invoice type comparison (customer/supplier vs out_invoice) - Refactor base.service.ts query handling for proper type inference - Rename Role type to RoleType to avoid conflict with entity - Fix ProtectedRoute to use role?.name instead of roles array ERP Suite - POS Micro: - Add vite-env.d.ts for Vite types - Fix Sale property names (discountAmount, changeAmount) - Export TodaySummary interface from sales service All projects now pass npm install and npm run build successfully. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
import {
|
|
Injectable,
|
|
CanActivate,
|
|
ExecutionContext,
|
|
ForbiddenException,
|
|
UnauthorizedException,
|
|
} from '@nestjs/common';
|
|
|
|
/**
|
|
* Account Status Guard
|
|
*
|
|
* Verifica que la cuenta del usuario esté activa
|
|
* Bloquea acceso a cuentas:
|
|
* - Inactivas
|
|
* - Suspendidas
|
|
* - Eliminadas (soft delete)
|
|
*/
|
|
@Injectable()
|
|
export class AccountStatusGuard implements CanActivate {
|
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
const request = context.switchToHttp().getRequest();
|
|
const user = request.user;
|
|
|
|
// Si no hay usuario, dejar que AuthGuard lo maneje
|
|
if (!user) {
|
|
return true;
|
|
}
|
|
|
|
// Verificar status de la cuenta
|
|
const status = user.status || user.accountStatus;
|
|
|
|
if (!status) {
|
|
// Si no hay información de status, asumir que está activo
|
|
return true;
|
|
}
|
|
|
|
// Estados bloqueados
|
|
switch (status.toLowerCase()) {
|
|
case 'inactive':
|
|
case 'deactivated':
|
|
throw new ForbiddenException(
|
|
'Your account has been deactivated. Please contact support.',
|
|
);
|
|
|
|
case 'suspended': {
|
|
const suspensionDetails = user.suspensionDetails || {};
|
|
const { isPermanent, suspendedUntil, reason } = suspensionDetails;
|
|
|
|
if (isPermanent) {
|
|
throw new ForbiddenException(
|
|
`Your account has been permanently suspended. Reason: ${reason || 'Policy violation'}`,
|
|
);
|
|
}
|
|
|
|
if (suspendedUntil) {
|
|
const now = new Date();
|
|
const suspensionEnd = new Date(suspendedUntil);
|
|
|
|
if (now < suspensionEnd) {
|
|
throw new ForbiddenException(
|
|
`Your account is suspended until ${suspensionEnd.toLocaleDateString()}. Reason: ${reason || 'Policy violation'}`,
|
|
);
|
|
}
|
|
// Si la suspensión ya expiró, permitir acceso
|
|
// (idealmente debería actualizarse el status automáticamente)
|
|
} else {
|
|
throw new ForbiddenException(
|
|
`Your account has been suspended. Reason: ${reason || 'Policy violation'}`,
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'deleted':
|
|
case 'banned':
|
|
throw new ForbiddenException(
|
|
'Your account has been permanently banned. Please contact support.',
|
|
);
|
|
|
|
case 'pending_verification':
|
|
case 'pending':
|
|
throw new UnauthorizedException(
|
|
'Please verify your email before accessing this resource.',
|
|
);
|
|
|
|
case 'active':
|
|
case 'verified':
|
|
// Estados válidos, permitir acceso
|
|
return true;
|
|
|
|
default:
|
|
// Estado desconocido, denegar por seguridad
|
|
throw new ForbiddenException(
|
|
`Account status '${status}' is not recognized. Please contact support.`,
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|