workspace-v1/orchestration/analisis/PLAN-IMPLEMENTACION-PENDIENTES-ADMIN-2026-01-07.md
rckrdmrd e56e927a4d [MAINT-001] docs(orchestration): Actualizacion directivas SIMCO, perfiles y documentacion
Cambios incluidos:
- INDICE-DIRECTIVAS-WORKSPACE.yml actualizado
- Perfiles de agentes: PERFIL-ML.md, PERFIL-SECURITY.md
- Directivas SIMCO actualizadas:
  - SIMCO-ASIGNACION-PERFILES.md
  - SIMCO-CCA-SUBAGENTE.md
  - SIMCO-CONTEXT-ENGINEERING.md
  - SIMCO-CONTEXT-RESOLUTION.md
  - SIMCO-DELEGACION-PARALELA.md
- Inventarios actualizados: DEVENV-MASTER, DEVENV-PORTS
- Documentos de analisis agregados:
  - Analisis y planes de fix student portal
  - Analisis scripts BD
  - Analisis achievements, duplicados, gamification
  - Auditoria documentacion gamilit
  - Backlog discrepancias NEXUS
  - Planes maestros de resolucion
- Reportes de ejecucion agregados
- Knowledge base gamilit README actualizado
- Referencia submodulo gamilit actualizada (commit beb94f7)

Validaciones:
- Plan validado contra directivas SIMCO-GIT
- Dependencias verificadas
- Build gamilit: EXITOSO
2026-01-10 04:51:28 -06:00

692 lines
15 KiB
Markdown

# PLAN DE IMPLEMENTACION: Endpoints Pendientes Admin Portal
**Fecha:** 2026-01-07
**Proyecto:** GAMILIT - Admin Portal Backend
**Agente:** Claude Code (Opus 4.5)
**Tipo:** Plan de Implementacion Detallado
---
## RESUMEN EJECUTIVO
```yaml
total_pendientes: 5
p1_criticos: 3
p2_mejoras: 2
archivos_a_modificar:
controladores: 2
servicios: 2
dtos: 4 (nuevos)
archivos_nuevos:
dtos: 4
tablas_bd: 0 # No se requieren nuevas tablas
verificacion_duplicados:
validate_config: "No existe - CREAR"
config_categories: "No existe - CREAR"
system_logs: "admin-logs.controller.ts existe pero retorna audit_logs, no system_logs - CREAR"
reports_schedule: "No existe - CREAR"
```
---
## 1. TASK-SETTINGS-VALIDATE-CONFIG (P1)
### 1.1 Analisis
```yaml
endpoint: "POST /admin/system/config/validate"
frontend_espera: "/admin/system/config/validate"
estado_actual: "NO IMPLEMENTADO"
duplicados_encontrados: "Ninguno"
proposito: |
Validar configuraciones ANTES de aplicarlas.
Retorna errores de validacion sin persistir cambios.
```
### 1.2 Verificacion de Duplicados
| Archivo | Patron Buscado | Encontrado | Accion |
|---------|----------------|------------|--------|
| admin-system.controller.ts | validateConfig | NO | Crear endpoint |
| admin-system.service.ts | validateConfig | NO | Crear metodo |
| dto/system/*.ts | ValidateConfigDto | NO | Crear DTO |
### 1.3 Implementacion Propuesta
**Archivo:** `admin-system.controller.ts`
```typescript
@Post('config/validate')
@ApiOperation({
summary: 'Validate system configuration',
description: 'Validate configuration values before applying. Returns validation errors if any.',
})
async validateConfig(
@Body() configDto: ValidateConfigDto,
): Promise<ConfigValidationResultDto> {
return this.adminSystemService.validateConfig(configDto);
}
```
**Nuevo DTO:** `validate-config.dto.ts`
```typescript
export class ValidateConfigDto {
@IsOptional()
@IsBoolean()
maintenance_mode?: boolean;
@IsOptional()
@IsString()
maintenance_message?: string;
@IsOptional()
@IsBoolean()
allow_registrations?: boolean;
@IsOptional()
@IsNumber()
@Min(1)
@Max(100)
max_login_attempts?: number;
@IsOptional()
@IsNumber()
@Min(1)
@Max(1440)
lockout_duration_minutes?: number;
@IsOptional()
@IsNumber()
@Min(5)
@Max(10080)
session_timeout_minutes?: number;
@IsOptional()
@IsObject()
custom_settings?: Record<string, unknown>;
}
export class ConfigValidationResultDto {
@ApiProperty()
valid: boolean;
@ApiProperty()
errors: ConfigValidationError[];
@ApiProperty()
warnings: string[];
}
export class ConfigValidationError {
@ApiProperty()
field: string;
@ApiProperty()
message: string;
@ApiProperty()
value?: unknown;
}
```
**Metodo en Service:**
```typescript
async validateConfig(configDto: ValidateConfigDto): Promise<ConfigValidationResultDto> {
const errors: ConfigValidationError[] = [];
const warnings: string[] = [];
// Validar maintenance_mode
if (configDto.maintenance_mode === true && !configDto.maintenance_message) {
warnings.push('Se recomienda proporcionar un mensaje de mantenimiento');
}
// Validar login attempts
if (configDto.max_login_attempts && configDto.max_login_attempts < 3) {
warnings.push('Un numero bajo de intentos puede bloquear usuarios legitimos');
}
// Validar session timeout
if (configDto.session_timeout_minutes && configDto.session_timeout_minutes > 1440) {
warnings.push('Sesiones largas pueden ser un riesgo de seguridad');
}
// Validar custom_settings si existen
if (configDto.custom_settings) {
for (const [key, value] of Object.entries(configDto.custom_settings)) {
if (typeof key !== 'string' || key.length > 100) {
errors.push({
field: `custom_settings.${key}`,
message: 'Key debe ser string de maximo 100 caracteres',
value: key,
});
}
}
}
return {
valid: errors.length === 0,
errors,
warnings,
};
}
```
---
## 2. TASK-SETTINGS-CONFIG-CATEGORIES (P1)
### 2.1 Analisis
```yaml
endpoint: "GET /admin/system/config/categories"
frontend_espera: "/admin/system/config/categories"
estado_actual: "NO IMPLEMENTADO"
duplicados_encontrados: "Ninguno"
proposito: |
Retornar lista de categorias de configuracion disponibles.
El frontend lo usa para renderizar tabs/secciones.
```
### 2.2 Verificacion de Duplicados
| Archivo | Patron Buscado | Encontrado | Accion |
|---------|----------------|------------|--------|
| admin-system.controller.ts | getCategories | NO | Crear endpoint |
| admin-system.service.ts | getCategories | NO | Crear metodo |
| system_settings tabla | setting_category CHECK | SI | Usar valores existentes |
### 2.3 Implementacion Propuesta
**Archivo:** `admin-system.controller.ts`
```typescript
@Get('config/categories')
@ApiOperation({
summary: 'Get available configuration categories',
description: 'Retrieve list of available configuration categories with metadata',
})
async getConfigCategories(): Promise<ConfigCategoryDto[]> {
return this.adminSystemService.getConfigCategories();
}
```
**Nuevo DTO:** `config-category.dto.ts`
```typescript
export class ConfigCategoryDto {
@ApiProperty()
key: string;
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiProperty()
icon?: string;
@ApiProperty()
order: number;
}
```
**Metodo en Service:**
```typescript
async getConfigCategories(): Promise<ConfigCategoryDto[]> {
// Categorias definidas en CHECK constraint de system_settings
// Ver: system_settings_setting_category_check
return [
{
key: 'general',
name: 'General',
description: 'Configuracion general de la plataforma',
icon: 'settings',
order: 1,
},
{
key: 'security',
name: 'Seguridad',
description: 'Politicas de autenticacion y acceso',
icon: 'security',
order: 2,
},
{
key: 'email',
name: 'Correo Electronico',
description: 'Configuracion de correo y notificaciones',
icon: 'email',
order: 3,
},
{
key: 'gamification',
name: 'Gamificacion',
description: 'Parametros del sistema de gamificacion',
icon: 'stars',
order: 4,
},
{
key: 'storage',
name: 'Almacenamiento',
description: 'Configuracion de archivos y media',
icon: 'storage',
order: 5,
},
{
key: 'analytics',
name: 'Analiticas',
description: 'Configuracion de metricas y reportes',
icon: 'analytics',
order: 6,
},
{
key: 'integrations',
name: 'Integraciones',
description: 'Servicios externos y APIs',
icon: 'integration_instructions',
order: 7,
},
];
}
```
---
## 3. TASK-SETTINGS-LOGS-ENDPOINT (P1)
### 3.1 Analisis
```yaml
endpoint: "GET /admin/system/logs"
frontend_espera: "/admin/system/logs"
estado_actual: "PARCIAL - admin-logs.controller retorna audit_logs (auth_attempts)"
duplicados_encontrados: "admin-logs.controller.ts existe pero retorna auth attempts, no system logs"
proposito: |
Retornar system_logs de audit_logging.system_logs
Diferente de audit_logs que son intentos de autenticacion.
nota_importante: |
- GET /admin/logs -> admin-logs.controller -> getAuditLog() -> auth_attempts
- GET /admin/system/logs -> NUEVO -> getSystemLogs() -> system_logs
Son endpoints DIFERENTES para datos DIFERENTES.
```
### 3.2 Verificacion de Duplicados
| Archivo | Patron Buscado | Encontrado | Proposito |
|---------|----------------|------------|-----------|
| admin-logs.controller.ts | getLogs | SI | Retorna auth_attempts (audit) |
| admin-system.controller.ts | getSystemLogs | NO | Debe retornar system_logs |
| entities/system-log.entity.ts | SystemLog | SI | Entity existe |
### 3.3 Tabla de Base de Datos
```sql
-- audit_logging.system_logs ya existe
-- Ver: ddl/schemas/audit_logging/tables/01-system_logs.sql
CREATE TABLE audit_logging.system_logs (
id UUID PRIMARY KEY,
log_level VARCHAR(20),
message TEXT,
context JSONB,
source VARCHAR(100),
user_id UUID,
tenant_id UUID,
ip_address INET,
user_agent TEXT,
request_path VARCHAR(255),
request_method VARCHAR(10),
response_status INTEGER,
duration_ms INTEGER,
stack_trace TEXT,
created_at TIMESTAMPTZ
);
```
### 3.4 Implementacion Propuesta
**Archivo:** `admin-system.controller.ts`
```typescript
@Get('logs')
@ApiOperation({
summary: 'Get system logs',
description: 'Retrieve paginated system logs with filtering options',
})
async getSystemLogs(
@Query() query: SystemLogsQueryDto,
): Promise<PaginatedSystemLogsDto> {
return this.adminSystemService.getSystemLogs(query);
}
```
**Nuevo DTO:** `system-logs-query.dto.ts`
```typescript
export class SystemLogsQueryDto {
@IsOptional()
@IsString()
log_level?: 'debug' | 'info' | 'warn' | 'error' | 'fatal';
@IsOptional()
@IsString()
source?: string;
@IsOptional()
@IsUUID()
user_id?: string;
@IsOptional()
@IsDateString()
start_date?: string;
@IsOptional()
@IsDateString()
end_date?: string;
@IsOptional()
@IsString()
search?: string;
@IsOptional()
@Type(() => Number)
@IsNumber()
@Min(1)
page?: number = 1;
@IsOptional()
@Type(() => Number)
@IsNumber()
@Min(1)
@Max(100)
limit?: number = 50;
}
export class SystemLogDto {
id: string;
log_level: string;
message: string;
context?: Record<string, unknown>;
source: string;
user_id?: string;
tenant_id?: string;
ip_address?: string;
user_agent?: string;
request_path?: string;
request_method?: string;
response_status?: number;
duration_ms?: number;
stack_trace?: string;
created_at: string;
}
export class PaginatedSystemLogsDto {
data: SystemLogDto[];
total: number;
page: number;
limit: number;
total_pages: number;
}
```
**Metodo en Service:**
```typescript
async getSystemLogs(query: SystemLogsQueryDto): Promise<PaginatedSystemLogsDto> {
const { log_level, source, user_id, start_date, end_date, search, page = 1, limit = 50 } = query;
const skip = (page - 1) * limit;
// Query audit_logging.system_logs
let sql = `
SELECT * FROM audit_logging.system_logs
WHERE 1=1
`;
const params: any[] = [];
let paramIndex = 1;
if (log_level) {
sql += ` AND log_level = $${paramIndex++}`;
params.push(log_level);
}
if (source) {
sql += ` AND source ILIKE $${paramIndex++}`;
params.push(`%${source}%`);
}
if (user_id) {
sql += ` AND user_id = $${paramIndex++}`;
params.push(user_id);
}
if (start_date) {
sql += ` AND created_at >= $${paramIndex++}`;
params.push(start_date);
}
if (end_date) {
sql += ` AND created_at <= $${paramIndex++}`;
params.push(end_date);
}
if (search) {
sql += ` AND (message ILIKE $${paramIndex} OR source ILIKE $${paramIndex})`;
params.push(`%${search}%`);
paramIndex++;
}
// Count total
const countResult = await this.authConnection.query(
`SELECT COUNT(*) as count FROM (${sql}) subq`,
params,
);
const total = parseInt(countResult[0].count, 10);
// Get paginated data
sql += ` ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex}`;
params.push(limit, skip);
const data = await this.authConnection.query(sql, params);
return {
data: data.map(row => ({
...row,
created_at: row.created_at?.toISOString(),
})),
total,
page,
limit,
total_pages: Math.ceil(total / limit),
};
}
```
---
## 4. TASK-ADMIN-REPORTS-SCHEDULE (P2)
### 4.1 Analisis
```yaml
endpoint: "POST /admin/reports/:id/schedule"
frontend_espera: "/admin/reports/:id/schedule"
estado_actual: "NO IMPLEMENTADO"
duplicados_encontrados: "Ninguno"
proposito: |
Programar generacion automatica de reportes.
Requiere tabla de scheduled_reports o campo en admin_reports.
```
### 4.2 Opcion de Implementacion
```yaml
opcion_a_nueva_tabla:
pros:
- Separacion de responsabilidades
- Historial de ejecuciones
contras:
- Requiere migracion de BD
- Mas complejidad
opcion_b_campo_en_reports:
pros:
- Sin cambios en BD (campos JSONB)
- Implementacion rapida
contras:
- Menos estructurado
decision: "Opcion B - Usar campo schedule_config JSONB en admin_reports"
```
### 4.3 Implementacion Propuesta
**Archivo:** `admin-reports.controller.ts`
```typescript
@Post(':id/schedule')
@ApiOperation({
summary: 'Schedule report generation',
description: 'Configure automatic periodic generation of a report',
})
async scheduleReport(
@Param('id') id: string,
@Body() scheduleDto: ScheduleReportDto,
@Request() req: AuthRequest,
): Promise<ReportDto> {
const userId = req.user!.id;
return this.adminReportsService.scheduleReport(id, scheduleDto, userId);
}
```
**Nuevo DTO:** `schedule-report.dto.ts`
```typescript
export class ScheduleReportDto {
@IsBoolean()
enabled: boolean;
@IsString()
@IsIn(['daily', 'weekly', 'monthly'])
frequency: 'daily' | 'weekly' | 'monthly';
@IsOptional()
@IsNumber()
@Min(0)
@Max(23)
hour?: number = 8; // Default 8 AM
@IsOptional()
@IsNumber()
@Min(0)
@Max(6)
day_of_week?: number; // 0=Sunday, for weekly
@IsOptional()
@IsNumber()
@Min(1)
@Max(28)
day_of_month?: number; // for monthly
@IsOptional()
@IsArray()
@IsEmail({}, { each: true })
recipients?: string[]; // Emails para enviar reporte
}
```
**Nota:** Esta implementacion P2 requiere modificar admin_reports para agregar schedule_config JSONB. Se documenta para sprint futuro.
---
## 5. RESUMEN DE ARCHIVOS A MODIFICAR
### 5.1 Archivos Existentes a Modificar
| Archivo | Cambios |
|---------|---------|
| admin-system.controller.ts | +3 endpoints |
| admin-system.service.ts | +3 metodos |
| admin-reports.controller.ts | +1 endpoint (P2) |
| admin-reports.service.ts | +1 metodo (P2) |
| dto/system/index.ts | +4 exports |
### 5.2 Archivos Nuevos a Crear
| Archivo | Proposito |
|---------|-----------|
| dto/system/validate-config.dto.ts | DTOs para validacion |
| dto/system/config-category.dto.ts | DTO para categorias |
| dto/system/system-logs.dto.ts | DTOs para system logs |
| dto/reports/schedule-report.dto.ts | DTO para schedule (P2) |
---
## 6. PLAN DE EJECUCION
### Fase 1: Implementar P1 (Inmediato)
```yaml
paso_1:
accion: "Crear DTOs para system"
archivos:
- validate-config.dto.ts
- config-category.dto.ts
- system-logs.dto.ts
validacion: "npm run build"
paso_2:
accion: "Agregar metodos a admin-system.service.ts"
metodos:
- validateConfig()
- getConfigCategories()
- getSystemLogs()
validacion: "npm run build"
paso_3:
accion: "Agregar endpoints a admin-system.controller.ts"
endpoints:
- POST config/validate
- GET config/categories
- GET logs
validacion: "npm run build && npm run test"
```
### Fase 2: Implementar P2 (Sprint Futuro)
```yaml
paso_1:
accion: "Evaluar si admin_reports tiene schedule_config"
decision: "Agregar columna o usar metadata existente"
paso_2:
accion: "Crear DTO schedule-report.dto.ts"
paso_3:
accion: "Agregar endpoint y service method"
```
---
## 7. VALIDACION POST-IMPLEMENTACION
```yaml
validaciones:
- build_backend: "npm run build (0 errors)"
- lint: "npm run lint (0 warnings)"
- tests: "npm run test (all pass)"
- swagger: "Verificar endpoints en /api/docs"
- frontend: "Verificar llamadas desde AdminSettingsPage"
documentacion:
- Actualizar BACKEND_INVENTORY.yml
- Crear reporte de ejecucion
- Actualizar README de admin module
```
---
**Plan creado:** 2026-01-07
**Agente:** Claude Code (Opus 4.5)
**Estado:** LISTO PARA IMPLEMENTACION