- Configure workspace Git repository with comprehensive .gitignore - Add Odoo as submodule for ERP reference code - Include documentation: SETUP.md, GIT-STRUCTURE.md - Add gitignore templates for projects (backend, frontend, database) - Structure supports independent repos per project/subproject level Workspace includes: - core/ - Reusable patterns, modules, orchestration system - projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.) - knowledge-base/ - Reference code and patterns (includes Odoo submodule) - devtools/ - Development tools and templates - customers/ - Client implementations template 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
21 KiB
21 KiB
Especificaciones Técnicas - Admin Portal Extendido
Arquitectura y Diseño Técnico - Sprints 1, 2, 3
Epic: EXT-002 Fecha: 2025-11-19 Estado: ✅ IMPLEMENTADO
1. Arquitectura General
1.1 Stack Tecnológico
┌─────────────────────────────────────────────┐
│ FRONTEND (React/Next.js) │
│ - AdminDashboardPage │
│ - RolesManagementPage │
│ - ReportsPage │
│ - MaintenancePage │
└─────────────────────────────────────────────┘
│ HTTP/REST
▼
┌─────────────────────────────────────────────┐
│ BACKEND (NestJS + TypeORM) │
│ ┌──────────────────────────────────────┐ │
│ │ Controllers (8 new/modified) │ │
│ │ - AdminDashboardController │ │
│ │ - AdminRolesController │ │
│ │ - AdminReportsController │ │
│ │ - AdminLogsController │ │
│ │ - AdminSystemController (mod) │ │
│ │ - AdminUsersController (mod) │ │
│ │ - AdminContentController (mod) │ │
│ └──────────────────────────────────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ Services (7 new/modified) │ │
│ │ - AdminDashboardService │ │
│ │ - AdminRolesService │ │
│ │ - AdminReportsService │ │
│ │ - AdminSystemService (mod) │ │
│ │ - AdminUsersService (mod) │ │
│ │ - AdminContentService (mod) │ │
│ │ - BulkOperationsService (reused) │ │
│ └──────────────────────────────────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ Repositories (TypeORM) │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
│ SQL
▼
┌─────────────────────────────────────────────┐
│ DATABASE (PostgreSQL 14+) │
│ ┌──────────────────────────────────────┐ │
│ │ Tables (3 used) │ │
│ │ - system_settings │ │
│ │ - content_approvals (new) │ │
│ │ - bulk_operations │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ Views (6 consumed) │ │
│ │ - recent_activity │ │
│ │ - user_stats_summary │ │
│ │ - organization_stats_summary │ │
│ │ - moderation_queue │ │
│ │ - classroom_overview │ │
│ │ - assignment_submission_stats │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ Functions (2 used) │ │
│ │ - cleanup_old_system_logs() │ │
│ │ - cleanup_old_user_activity() │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
1.2 Patrones de Diseño Aplicados
1. Controller → Service → Repository (Layered Architecture)
// Controller Layer (HTTP handling)
@Controller('admin/dashboard')
export class AdminDashboardController {
constructor(private readonly service: AdminDashboardService) {}
@Get()
async getDashboard(): Promise<DashboardDataDto> {
return await this.service.getDashboard();
}
}
// Service Layer (Business logic)
@Injectable()
export class AdminDashboardService {
constructor(
@InjectRepository(User) private userRepo: Repository<User>,
@InjectConnection('auth') private connection: Connection
) {}
async getDashboard(): Promise<DashboardDataDto> {
const [stats, activity] = await Promise.all([
this.getDashboardStats(),
this.getRecentActivity()
]);
return { stats, activity };
}
}
// Repository Layer (Data access via TypeORM)
2. DTO Pattern (Data Transfer Objects)
- Input validation con class-validator
- Transformación automática con class-transformer
- Documentación Swagger con decoradores @ApiProperty
3. Dependency Injection
- Constructor injection para servicios y repositorios
- @InjectRepository para repositorios TypeORM
- @InjectConnection para conexiones de DB
4. Delegation Pattern
- Controladores alias delegan a servicios existentes
- No duplicación de lógica de negocio
- Mantiene principio DRY
5. Repository Pattern (TypeORM)
- Abstracción de acceso a datos
- Queries type-safe
- Soporte para múltiples conexiones de DB
2. Estructuras de Datos
2.1 Entidades TypeORM
ContentApproval (Nueva)
@Entity({ schema: 'educational_content', name: 'content_approvals' })
export class ContentApproval {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ type: 'varchar', length: 50 })
content_type: ContentApprovalType; // module, exercise, assignment, resource
@Column('uuid')
content_id: string;
@Column('uuid')
submitted_by: string;
@ManyToOne(() => User)
@JoinColumn({ name: 'submitted_by' })
submitter: User;
@Column({ type: 'timestamp with time zone' })
submitted_at: Date;
@Column({ type: 'uuid', nullable: true })
reviewed_by?: string;
@ManyToOne(() => User, { nullable: true })
@JoinColumn({ name: 'reviewed_by' })
reviewer?: User;
@Column({ type: 'timestamp with time zone', nullable: true })
reviewed_at?: Date;
@Column({ type: 'varchar', length: 50 })
status: ContentApprovalStatus; // pending, approved, rejected, needs_revision
@Column({ type: 'text', nullable: true })
reviewer_notes?: string;
@Column({ type: 'text', nullable: true })
revision_notes?: string;
@CreateDateColumn()
created_at: Date;
@UpdateDateColumn()
updated_at: Date;
}
2.2 DTOs Principales
Dashboard DTOs
export class DashboardStatsDto {
totalUsers: number;
activeUsers: number;
newUsersToday: number;
totalOrganizations: number;
totalExercises: number;
totalModules: number;
exercisesCompleted24h: number;
systemHealth: 'healthy' | 'warning' | 'critical';
avgResponseTime: number;
}
export class DashboardDataDto {
stats: DashboardStatsDto;
recentActivity: AdminActionDto[];
timestamp: string;
}
Roles & Permissions DTOs
export class PermissionDto {
key: string; // can_create_content, can_manage_users, etc.
displayName: string;
category: 'content' | 'users' | 'system' | 'reports' | 'gamification' | 'analytics';
}
export class RoleDto {
id: string;
name: string;
permissions: Record<string, boolean>;
users_count?: number;
}
Reports DTOs
export enum ReportType {
USERS = 'users',
PROGRESS = 'progress',
GAMIFICATION = 'gamification',
SYSTEM = 'system'
}
export enum ReportFormat {
CSV = 'csv',
EXCEL = 'excel',
PDF = 'pdf'
}
export class ReportDto {
id: string;
type: ReportType;
format: ReportFormat;
status: 'generating' | 'completed' | 'failed';
file_size?: number;
download_url?: string;
created_at: string;
completed_at?: string;
}
2.3 Enums y Tipos
// Content Approval
export enum ContentApprovalType {
MODULE = 'module',
EXERCISE = 'exercise',
ASSIGNMENT = 'assignment',
RESOURCE = 'resource'
}
export enum ContentApprovalStatus {
PENDING = 'pending',
APPROVED = 'approved',
REJECTED = 'rejected',
NEEDS_REVISION = 'needs_revision'
}
// System Health
export type SystemHealthStatus = 'healthy' | 'degraded' | 'down';
// Permission Categories
export type PermissionCategory =
| 'content'
| 'users'
| 'system'
| 'reports'
| 'gamification'
| 'analytics';
3. API Endpoints
3.1 Dashboard Module
| Método | Endpoint | Descripción | Response Type |
|---|---|---|---|
| GET | /admin/dashboard |
Dashboard completo | DashboardDataDto |
| GET | /admin/dashboard/stats |
Solo estadísticas | DashboardStatsDto |
| GET | /admin/dashboard/recent-activity |
Actividad paginada | PaginatedActivityDto |
| GET | /admin/dashboard/user-stats |
Stats de usuarios (vista) | UserStatsSummaryDto |
| GET | /admin/dashboard/organization-stats |
Stats de orgs (vista) | OrganizationStatsSummaryDto |
| GET | /admin/dashboard/moderation-queue |
Cola de moderación | PaginatedModerationQueueDto |
| GET | /admin/dashboard/classroom-overview |
Overview de aulas | PaginatedClassroomOverviewDto |
| GET | /admin/dashboard/assignment-stats |
Stats de asignaciones | PaginatedAssignmentSubmissionStatsDto |
3.2 Roles Module
| Método | Endpoint | Descripción | Response Type |
|---|---|---|---|
| GET | /admin/roles |
Listar roles | RoleDto[] |
| GET | /admin/roles/permissions |
Permisos disponibles | PermissionDto[] |
| GET | /admin/roles/:id/permissions |
Permisos de rol | RolePermissionsDto |
| PUT | /admin/roles/:id/permissions |
Actualizar permisos | RolePermissionsDto |
3.3 Reports Module
| Método | Endpoint | Descripción | Response Type |
|---|---|---|---|
| POST | /admin/reports/generate |
Generar reporte | ReportDto |
| GET | /admin/reports |
Listar reportes | PaginatedReportsDto |
| GET | /admin/reports/:id/download |
Descargar reporte | ReportDto |
| DELETE | /admin/reports/:id |
Eliminar reporte | 204 No Content |
3.4 System Config Module
| Método | Endpoint | Descripción | Response Type |
|---|---|---|---|
| GET | /admin/system/config |
Config completa | SystemConfigDto |
| POST | /admin/system/config |
Actualizar config | SystemConfigDto |
| GET | /admin/system/config/:category |
Config por categoría | Record<string, any> |
| PUT | /admin/system/config/:category |
Actualizar categoría | Record<string, any> |
3.5 Maintenance Module
| Método | Endpoint | Descripción | Response Type |
|---|---|---|---|
| POST | /admin/system/maintenance/cleanup-logs |
Limpiar logs | MaintenanceOperationResultDto |
| POST | /admin/system/maintenance/cleanup-activity |
Limpiar actividad | MaintenanceOperationResultDto |
| POST | /admin/system/maintenance/optimize-database |
Optimizar DB | DatabaseOptimizationResultDto |
| POST | /admin/system/maintenance/clear-cache |
Limpiar caché | CacheClearResultDto |
| POST | /admin/system/maintenance/cleanup-sessions |
Limpiar sesiones | SessionCleanupResultDto |
3.6 Users Bulk Ops (Aliases)
| Método | Endpoint | Descripción | Delegado a |
|---|---|---|---|
| POST | /admin/users/bulk/suspend |
Suspender en lote | BulkOperationsService.bulkSuspendUsers() |
| POST | /admin/users/bulk/delete |
Eliminar en lote | BulkOperationsService.bulkDeleteUsers() |
| POST | /admin/users/bulk/update-role |
Actualizar rol | BulkOperationsService.bulkUpdateRole() |
3.7 Content Approval
| Método | Endpoint | Descripción | Response Type |
|---|---|---|---|
| GET | /admin/content/approval-history |
Historial de aprobaciones | PaginatedApprovalHistoryDto |
| GET | /admin/content/exercises/pending |
Ejercicios pendientes (alias) | PaginatedContentDto |
| POST | /admin/content/exercises/:id/approve |
Aprobar ejercicio (alias) | ContentDto |
| POST | /admin/content/exercises/:id/reject |
Rechazar ejercicio (alias) | ContentDto |
3.8 Logs (Alias)
| Método | Endpoint | Descripción | Delegado a |
|---|---|---|---|
| GET | /admin/logs |
Audit logs (alias) | AdminSystemService.getAuditLog() |
Total endpoints: 33
4. Seguridad y Autenticación
4.1 Guards Aplicados
@Controller('admin/*')
@UseGuards(JwtAuthGuard, AdminGuard)
@ApiBearerAuth()
export class AdminController {
// Todos los endpoints protegidos
}
1. JwtAuthGuard
- Valida token JWT en header Authorization
- Extrae user del token
- Inyecta user en request
2. AdminGuard
- Verifica que user.role sea admin
- Bloquea acceso a no-admins
- Retorna 403 Forbidden si no es admin
4.2 Permisos Granulares (Futuro)
Actualmente implementado solo a nivel de roles. Para futura implementación:
@RequiresPermission('can_manage_users')
async suspendUser() { }
@RequiresPermission(['can_create_content', 'can_approve_content'])
async approveContent() { }
4.3 Audit Trail
Registro de acciones:
- Todos los cambios en
system_settingsregistranupdated_by - Aprobaciones/rechazos se registran en
content_approvals - Bulk operations se rastrean en
bulk_operations - Activity log registra acciones importantes
5. Optimizaciones de Performance
5.1 Queries Paralelas
// Dashboard: 2 queries en paralelo
const [stats, activity] = await Promise.all([
this.getDashboardStats(),
this.getRecentActivity()
]);
// Stats: 7 queries en paralelo
const [totalUsers, activeUsers, newUsers, ...] = await Promise.all([
this.userRepo.count(),
this.getActiveUsers24h(),
this.userRepo.count({ where: { created_at: MoreThanOrEqual(today) } }),
// ... más queries
]);
5.2 Índices de Base de Datos
Creados en tablas relevantes:
-- content_approvals
CREATE INDEX idx_content_approvals_content
ON content_approvals(content_type, content_id);
CREATE INDEX idx_content_approvals_status
ON content_approvals(status);
CREATE INDEX idx_content_approvals_submitted_by
ON content_approvals(submitted_by);
CREATE INDEX idx_content_approvals_reviewed_by
ON content_approvals(reviewed_by) WHERE reviewed_by IS NOT NULL;
5.3 Vistas Materializadas (Potencial)
Actualmente vistas normales. Para mayor performance en producción:
CREATE MATERIALIZED VIEW admin_dashboard.user_stats_summary_mat AS
SELECT * FROM admin_dashboard.user_stats_summary;
-- Refresh automático cada hora
CREATE OR REPLACE FUNCTION refresh_user_stats()
RETURNS void AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY admin_dashboard.user_stats_summary_mat;
END;
$$ LANGUAGE plpgsql;
5.4 Paginación
Todas las listas implementan paginación:
interface PaginatedResponse<T> {
data: T[];
total: number;
page: number;
limit: number;
total_pages: number;
}
Uso de skip/take en TypeORM:
const skip = (page - 1) * limit;
const [data, total] = await repo.findAndCount({
skip,
take: limit
});
6. Manejo de Errores
6.1 Estrategia de Errores
// Errores esperados con excepciones NestJS
throw new NotFoundException(`User ${id} not found`);
throw new BadRequestException('User is already deactivated');
// Errores de validación automáticos (class-validator)
@IsString()
@IsNotEmpty()
name: string; // Lanza ValidationException si falla
// Errores de DB se propagan como InternalServerException
6.2 Try-Catch para Operaciones Críticas
async getRecentActivity(): Promise<AdminActionDto[]> {
try {
const results = await this.connection.query(
'SELECT * FROM admin_dashboard.recent_activity LIMIT $1',
[limit]
);
return results.map(mapToDto);
} catch (error) {
console.error('Error fetching activity:', error);
return []; // Graceful degradation
}
}
6.3 Validación de DTOs
export class CreateReportDto {
@IsEnum(ReportType)
type: ReportType; // Valida que sea users, progress, etc.
@IsEnum(ReportFormat)
format: ReportFormat; // Valida que sea csv, excel, pdf
@IsOptional()
@IsObject()
filters?: Record<string, any>;
}
7. Testing Considerations
7.1 Tests Unitarios (Recomendado)
describe('AdminDashboardService', () => {
let service: AdminDashboardService;
let userRepo: Repository<User>;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
AdminDashboardService,
{
provide: getRepositoryToken(User, 'auth'),
useValue: mockUserRepository
}
]
}).compile();
service = module.get(AdminDashboardService);
});
it('should return dashboard stats', async () => {
mockUserRepository.count.mockResolvedValue(100);
const stats = await service.getDashboardStats();
expect(stats.totalUsers).toBe(100);
});
});
7.2 Tests E2E (Recomendado)
describe('Admin Dashboard (e2e)', () => {
it('/admin/dashboard (GET) should require admin role', () => {
return request(app.getHttpServer())
.get('/admin/dashboard')
.set('Authorization', `Bearer ${studentToken}`)
.expect(403);
});
it('/admin/dashboard (GET) should return stats for admin', () => {
return request(app.getHttpServer())
.get('/admin/dashboard')
.set('Authorization', `Bearer ${adminToken}`)
.expect(200)
.expect((res) => {
expect(res.body).toHaveProperty('stats');
expect(res.body).toHaveProperty('recentActivity');
});
});
});
8. Deployment Considerations
8.1 Variables de Entorno
# Database Connections
DATABASE_URL_AUTH=postgresql://user:pass@host:5432/auth_db
DATABASE_URL_EDUCATIONAL=postgresql://user:pass@host:5432/educational_db
# JWT
JWT_SECRET=your-secret-key
JWT_EXPIRATION=1h
# System Config
DEFAULT_RETENTION_DAYS_LOGS=90
DEFAULT_RETENTION_DAYS_ACTIVITY=180
8.2 Migraciones
# Crear migración para content_approvals
npm run migration:generate -- -n CreateContentApprovals
# Ejecutar migraciones
npm run migration:run
# Revertir si es necesario
npm run migration:revert
8.3 Seeds de Datos
-- Permisos iniciales
INSERT INTO auth.permissions (key, display_name, category) VALUES
('can_create_content', 'Can Create Content', 'content'),
('can_approve_content', 'Can Approve Content', 'content'),
-- ... 14 más
-- Asignar permisos a super_admin
INSERT INTO auth.role_permissions (role_id, permission_key)
SELECT r.id, p.key
FROM auth.roles r, auth.permissions p
WHERE r.name = 'super_admin';
9. Mejoras Futuras
9.1 Sistema de Reportes
- Migrar de Map a tabla
admin.reports - Implementar almacenamiento de archivos (S3)
- Cola de jobs con Bull/BullMQ
- Más tipos de reportes y filtros
9.2 Caché
- Implementar Redis para caché distribuido
- Cache de estadísticas del dashboard (TTL 5 min)
- Cache de permisos por rol
- Invalidación inteligente
9.3 Websockets
- Updates en tiempo real para dashboard
- Notificaciones de reportes completados
- Progress de operaciones bulk en tiempo real
9.4 Audit Logging Mejorado
- Decorador @AuditLog() para acciones críticas
- Logging automático de cambios en entidades
- Diferencias antes/después en metadata
Documento generado: 2025-11-19 Versión: 1.0 Estado: Arquitectura implementada y validada