🎓 Initial commit: Math2 Platform - Plataforma de Álgebra Lineal PRO
Some checks failed
Test Suite / test-backend (push) Has been cancelled
Test Suite / test-frontend (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / coverage-check (push) Has been cancelled

 Características:
- 45 ejercicios universitarios (Basic → Advanced)
- Renderizado LaTeX profesional
- IA generativa (Z.ai/DashScope)
- Docker 9 servicios
- Tests 123/123 pasando
- Seguridad enterprise (JWT, XSS, Rate limiting)

🐳 Infraestructura:
- Next.js 14 + Node.js 20
- PostgreSQL 15 + Redis 7
- Docker Compose completo
- Nginx + SSL ready

📚 Documentación:
- 5 informes técnicos completos
- README profesional
- Scripts de deployment automatizados

Estado: Producción lista 
This commit is contained in:
Renato
2026-03-31 11:27:11 -03:00
commit bc43c9e772
309 changed files with 84845 additions and 0 deletions

195
shared/types/README.md Normal file
View File

@@ -0,0 +1,195 @@
# @math-platform/shared-types
TypeScript type definitions shared between frontend and backend applications.
## Purpose
Eliminates duplication and misalignment between frontend and backend type definitions.
## Structure
```
shared/types/
├── package.json # Package configuration
├── tsconfig.json # TypeScript configuration
└── src/
├── index.ts # Main entry point - exports all types
├── auth.ts # Authentication types
├── exercise.ts # Exercise and attempt types
├── module.ts # Module and topic types
├── progress.ts # Progress tracking types
├── achievement.ts # Achievement and badge types
├── ranking.ts # Leaderboard types
├── api.ts # Generic API types
├── error.ts # Error types and classes
└── utils.ts # Utility types
```
## Installation
### Frontend
Types are accessed via path mapping in `tsconfig.json`:
```json
{
"compilerOptions": {
"paths": {
"@math-platform/shared-types": ["../shared/types/src"],
"@math-platform/shared-types/*": ["../shared/types/src/*"]
}
}
}
```
### Backend
Same path mapping in `tsconfig.json`:
```json
{
"compilerOptions": {
"paths": {
"@math-platform/shared-types": ["../../shared/types/src"],
"@math-platform/shared-types/*": ["../../shared/types/src/*"]
}
}
}
```
## Usage
### Import Types
```typescript
// Import specific types
import type {
User,
Exercise,
LoginRequest,
LoginResponse,
SubmitAttemptRequest,
SubmitAttemptResponse,
ApiResponse
} from '@math-platform/shared-types';
// Import from specific module
import type { LoginRequest, LoginResponse } from '@math-platform/shared-types/auth';
import type { Exercise, SubmitAttemptRequest } from '@math-platform/shared-types/exercise';
```
### Import Classes
```typescript
// Error classes are exported as values
import {
ApplicationError,
ValidationError,
NotFoundError
} from '@math-platform/shared-types';
```
## Key Contracts
### 1. Submit Attempt
**Request:**
```typescript
interface SubmitAttemptRequest {
answer: string;
timeSpent: number;
hintsUsed?: number;
skipped?: boolean;
}
```
**Response:**
```typescript
interface SubmitAttemptResponse {
isCorrect: boolean;
points: number;
message: string;
correctAnswer?: string;
solutionSteps?: SolutionStep[];
newAchievements?: AchievementUnlock[];
}
```
### 2. Auth
**Login Request:**
```typescript
interface LoginRequest {
email: string;
password: string;
}
```
**Login Response:**
```typescript
interface LoginResponse {
token: string;
refreshToken?: string;
expiresIn?: number;
user: User;
}
```
### 3. API Response
```typescript
interface ApiResponse<T = unknown> {
success: boolean;
data?: T;
error?: ApiError;
meta?: ApiMeta;
}
```
## Migration Guide
### Before (Duplicated)
**Frontend:** `frontend/src/types/index.ts`
```typescript
export interface LoginData {
email: string;
password: string;
}
```
**Backend:** `backend/src/shared/types/index.ts`
```typescript
export interface LoginInput {
email: string;
password: string;
}
```
### After (Shared)
Both use:
```typescript
import type { LoginRequest } from '@math-platform/shared-types';
```
## Best Practices
1. **Always use `type` imports** for type-only imports to avoid circular dependencies
2. **Keep types serializable** - use `string` for dates, not `Date` objects
3. **Document breaking changes** in CHANGELOG.md
4. **Don't import from specific backend/frontend packages** - keep this package independent
## Building
```bash
cd shared/types
npm install
npm run build
```
## Type Checking
```bash
npm run type-check
```

22
shared/types/package.json Normal file
View File

@@ -0,0 +1,22 @@
{
"name": "@math-platform/shared-types",
"version": "1.0.0",
"description": "Shared TypeScript types between frontend and backend",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"type-check": "tsc --noEmit"
},
"devDependencies": {
"typescript": "^5.3.0"
},
"peerDependencies": {
"zod": "^3.22.0"
},
"peerDependenciesMeta": {
"zod": {
"optional": true
}
}
}

View File

@@ -0,0 +1,82 @@
/**
* ============================================
* ACHIEVEMENT TYPES
* ============================================
*
* Types for achievements and badges
* shared between frontend and backend
*/
// ============================================
// ENUMS
// ============================================
export type AchievementCategory =
| 'EXERCISES'
| 'MODULES'
| 'STREAKS'
| 'RANKING'
| 'SPECIAL';
export type AchievementRarity = 'COMMON' | 'RARE' | 'EPIC' | 'LEGENDARY';
// ============================================
// ACHIEVEMENT
// ============================================
export interface Achievement {
code: string;
name: string;
description: string;
icon: string;
rarity: AchievementRarity;
category: AchievementCategory;
requirementType: string;
requirementValue: number;
metadata?: AchievementMetadata;
}
export interface AchievementMetadata {
color: string;
animation?: string;
tooltip: string;
unlockMessage: string;
}
// ============================================
// USER ACHIEVEMENT
// ============================================
export interface UserAchievement {
userId: string;
achievementCode: string;
progress: number;
requirementValue: number;
unlocked: boolean;
unlockedAt: string | null;
}
export interface UserAchievementProgress {
achievementId: string;
code: string;
name: string;
description: string;
category: string;
rarity: string;
icon: string;
progress: number;
requirementValue: number;
unlocked: boolean;
unlockedAt?: string;
}
// ============================================
// BADGE (For rankings)
// ============================================
export interface AchievementBadge {
code: string;
icon: string;
rarity: AchievementRarity;
name?: string;
}

73
shared/types/src/api.ts Normal file
View File

@@ -0,0 +1,73 @@
/**
* ============================================
* API TYPES
* ============================================
*
* Generic API request/response types
* shared between frontend and backend
*/
// ============================================
// API RESPONSE
// ============================================
export interface ApiResponse<T = unknown> {
success: boolean;
data?: T;
error?: ApiError;
meta?: ApiMeta;
}
export interface ApiError {
code: string;
message: string;
details?: unknown;
}
export interface ApiMeta {
timestamp: string;
requestId?: string;
version?: string;
}
// ============================================
// PAGINATION
// ============================================
export interface PaginationParams {
page?: number;
limit?: number;
sortBy?: string;
sortOrder?: 'asc' | 'desc';
}
export interface PaginatedResponse<T> {
data: T[];
pagination: PaginationInfo;
}
export interface PaginationInfo {
page: number;
limit: number;
total: number;
totalPages: number;
hasNext: boolean;
hasPrev: boolean;
}
// ============================================
// FILTERS & SORTING
// ============================================
export interface FilterOptions {
search?: string;
difficulty?: string[];
type?: string[];
moduleId?: string[];
topicId?: string[];
}
export interface SortOptions {
field: 'createdAt' | 'points' | 'difficulty' | 'name';
order: 'asc' | 'desc';
}

93
shared/types/src/auth.ts Normal file
View File

@@ -0,0 +1,93 @@
/**
* ============================================
* AUTHENTICATION TYPES
* ============================================
*
* Types for authentication requests and responses
* shared between frontend and backend
*/
// ============================================
// REQUEST TYPES
// ============================================
export interface LoginRequest {
email: string;
password: string;
}
export interface RegisterRequest {
email: string;
username: string;
password: string;
confirmPassword?: string;
}
export interface RefreshTokenRequest {
refreshToken: string;
}
export interface ChangePasswordRequest {
currentPassword: string;
newPassword: string;
}
// ============================================
// RESPONSE TYPES
// ============================================
export interface LoginResponse {
token: string;
refreshToken?: string;
expiresIn?: number;
user: User;
}
export interface RegisterResponse {
token: string;
user: User;
}
export interface RefreshTokenResponse {
token: string;
refreshToken: string;
expiresIn: number;
}
// ============================================
// USER TYPE (Shared subset)
// ============================================
export interface User {
id: string;
email: string;
username: string;
role?: UserRole;
createdAt: string;
lastLoginAt?: string;
}
export type UserRole = 'STUDENT' | 'ADMIN' | 'INSTRUCTOR';
// ============================================
// JWT PAYLOAD (Backend usage mainly)
// ============================================
export interface JwtPayload {
userId: string;
email: string;
username: string;
role: string;
iat?: number;
exp?: number;
}
// ============================================
// AUTH TOKENS
// ============================================
export interface AuthTokens {
accessToken: string;
refreshToken: string;
expiresIn: number;
}

118
shared/types/src/error.ts Normal file
View File

@@ -0,0 +1,118 @@
/**
* ============================================
* ERROR TYPES
* ============================================
*
* Error types and classes
* Primarily used by backend, but shared for type consistency
*/
// Extend ErrorConstructor for Node.js captureStackTrace
declare global {
interface ErrorConstructor {
captureStackTrace?(targetObject: object, constructorOpt?: Function): void;
}
}
// ============================================
// ERROR CODES
// ============================================
export type ErrorCode =
| 'INTERNAL_ERROR'
| 'VALIDATION_ERROR'
| 'AUTHENTICATION_ERROR'
| 'AUTHORIZATION_ERROR'
| 'NOT_FOUND'
| 'CONFLICT_ERROR'
| 'RATE_LIMIT_ERROR'
| 'BAD_REQUEST'
| 'SERVICE_UNAVAILABLE';
// ============================================
// APP ERROR INTERFACE
// ============================================
export interface AppError extends Error {
code: ErrorCode;
statusCode: number;
isOperational: boolean;
details?: unknown;
}
// ============================================
// ERROR CLASSES (Backend usage)
// ============================================
export class ApplicationError extends Error implements AppError {
code: ErrorCode;
statusCode: number;
isOperational: boolean;
details?: unknown;
constructor(
message: string,
code: ErrorCode = 'INTERNAL_ERROR',
statusCode: number = 500,
isOperational: boolean = true,
details?: unknown
) {
super(message);
this.name = 'ApplicationError';
this.code = code;
this.statusCode = statusCode;
this.isOperational = isOperational;
this.details = details;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
}
export class ValidationError extends ApplicationError {
constructor(message: string, details?: unknown) {
super(message, 'VALIDATION_ERROR', 400, true, details);
this.name = 'ValidationError';
}
}
export class AuthenticationError extends ApplicationError {
constructor(message: string = 'Authentication failed') {
super(message, 'AUTHENTICATION_ERROR', 401, true);
this.name = 'AuthenticationError';
}
}
export class AuthorizationError extends ApplicationError {
constructor(message: string = 'Insufficient permissions') {
super(message, 'AUTHORIZATION_ERROR', 403, true);
this.name = 'AuthorizationError';
}
}
export class NotFoundError extends ApplicationError {
constructor(resource: string) {
super(`${resource} not found`, 'NOT_FOUND', 404, true);
this.name = 'NotFoundError';
}
}
export class ConflictError extends ApplicationError {
constructor(message: string, details?: unknown) {
super(message, 'CONFLICT_ERROR', 409, true, details);
this.name = 'ConflictError';
}
}
export class RateLimitError extends ApplicationError {
constructor(retryAfter?: number) {
super(
'Too many requests',
'RATE_LIMIT_ERROR',
429,
true,
{ retryAfter }
);
this.name = 'RateLimitError';
}
}

View File

@@ -0,0 +1,145 @@
/**
* ============================================
* EXERCISE TYPES
* ============================================
*
* Types for exercises, attempts, and solutions
* shared between frontend and backend
*/
// ============================================
// ENUMS
// ============================================
export type ExerciseType =
| 'MULTIPLE_CHOICE'
| 'OPEN_ENDED'
| 'TRUE_FALSE'
| 'CALCULATION';
export type ExerciseDifficulty = 'EASY' | 'MEDIUM' | 'HARD';
export type ExerciseStatus = 'CORRECT' | 'INCORRECT' | 'PARTIAL' | 'SKIPPED';
export type TopicType =
| 'VECTORES'
| 'MATRICES'
| 'SISTEMAS'
| 'ESPACIOS_VECTORIALES'
| 'PROGRAMACION_LINEAL';
export type ModuleType = 'FUNDAMENTOS' | 'SISTEMAS' | 'APLICACIONES';
// ============================================
// CORE EXERCISE TYPES
// ============================================
export interface Exercise {
id: string;
moduleId: string;
topicId?: string;
type: ExerciseType;
difficulty: ExerciseDifficulty;
statement: string;
correctAnswer: string;
solutionSteps: SolutionStep[];
formulas?: string[];
hints?: string[];
options?: ExerciseOption[];
points: number;
isAIGenerated?: boolean;
}
export interface ExerciseOption {
id: string;
text: string;
isCorrect: boolean;
}
export interface SolutionStep {
step: number;
description: string;
formula?: string;
}
// ============================================
// ATTEMPT SUBMISSION
// ============================================
export interface SubmitAttemptRequest {
answer: string;
timeSpent: number;
hintsUsed?: number;
skipped?: boolean;
}
export interface SubmitAttemptResponse {
isCorrect: boolean;
points: number;
message: string;
correctAnswer?: string;
solutionSteps?: SolutionStep[];
newAchievements?: AchievementUnlock[];
}
export interface AchievementUnlock {
code: string;
name: string;
description: string;
icon: string;
}
// ============================================
// EXERCISE ATTEMPT (History)
// ============================================
export interface ExerciseAttempt {
id: string;
userId: string;
exerciseId: string;
userAnswer: string;
status: ExerciseStatus;
pointsEarned: number;
timeSpentSeconds: number;
attemptedAt: string;
hintsUsed?: number;
}
// ============================================
// EXERCISE CONTENT (Detailed)
// ============================================
export interface ExerciseContent {
statement: string;
correctAnswer: string;
solutionSteps?: SolutionStep[];
formulas?: LatexFormula[];
hints?: ExerciseHint[];
multipleChoiceOptions?: MultipleChoiceOption[];
calculationSteps?: CalculationStep[];
}
export interface LatexFormula {
name: string;
latex: string;
description: string;
category?: string;
}
export interface ExerciseHint {
hint: string;
cost: number;
}
export interface MultipleChoiceOption {
option: string;
isCorrect: boolean;
explanation?: string;
}
export interface CalculationStep {
step: number;
operation: string;
result: string;
latexFormula?: string;
}

158
shared/types/src/index.ts Normal file
View File

@@ -0,0 +1,158 @@
/**
* ============================================
* SHARED TYPES PACKAGE
* ============================================
*
* TypeScript type definitions shared between
* frontend and backend applications
*
* Usage:
* import type { User, Exercise, ApiResponse } from '@math-platform/shared-types';
* import { ApplicationError } from '@math-platform/shared-types';
*
* Or import specific modules:
* import type { LoginRequest, LoginResponse } from '@math-platform/shared-types/auth';
*/
// ============================================
// AUTH
// ============================================
export type {
LoginRequest,
LoginResponse,
RegisterRequest,
RegisterResponse,
RefreshTokenRequest,
RefreshTokenResponse,
ChangePasswordRequest,
User,
UserRole,
JwtPayload,
AuthTokens,
} from './auth';
// ============================================
// EXERCISE
// ============================================
export type {
ExerciseType,
ExerciseDifficulty,
ExerciseStatus,
TopicType,
ModuleType,
Exercise,
ExerciseOption,
SolutionStep,
SubmitAttemptRequest,
SubmitAttemptResponse,
AchievementUnlock,
ExerciseAttempt,
ExerciseContent,
LatexFormula,
ExerciseHint,
MultipleChoiceOption,
CalculationStep,
} from './exercise';
// ============================================
// MODULE
// ============================================
export type {
Module,
ModuleExample,
ModuleExerciseRef,
ModuleAnswer,
Topic,
TheoryContent,
TheorySection,
Formula,
TheoryContentDetailed,
KeyPoint,
CommonMistake,
} from './module';
// ============================================
// PROGRESS
// ============================================
export type {
UserProgress,
ModuleProgress,
ProgressMetrics,
UserStatistics,
Activity,
ActivityType,
} from './progress';
// ============================================
// ACHIEVEMENT
// ============================================
export type {
AchievementCategory,
AchievementRarity,
Achievement,
AchievementMetadata,
UserAchievement,
UserAchievementProgress,
AchievementBadge,
} from './achievement';
// ============================================
// RANKING
// ============================================
export type {
RankingEntry,
GlobalRanking,
UserRankingPosition,
RankingFilters,
} from './ranking';
// ============================================
// API
// ============================================
export type {
ApiResponse,
ApiError,
ApiMeta,
PaginationParams,
PaginatedResponse,
PaginationInfo,
FilterOptions,
SortOptions,
} from './api';
// ============================================
// ERROR
// ============================================
export type {
ErrorCode,
AppError,
} from './error';
// Export error classes as values (not types)
export {
ApplicationError,
ValidationError,
AuthenticationError,
AuthorizationError,
NotFoundError,
ConflictError,
RateLimitError,
} from './error';
// ============================================
// UTILS
// ============================================
export type {
WithId,
WithTimestamps,
PartialBy,
Nullable,
Optional,
UUID,
ISO8601Date,
Email,
AsyncReturnType,
NullableKeys,
EmptyObject,
StringRecord,
} from './utils';

114
shared/types/src/module.ts Normal file
View File

@@ -0,0 +1,114 @@
/**
* ============================================
* MODULE TYPES
* ============================================
*
* Types for course modules and topics
* shared between frontend and backend
*/
import { TopicType, ModuleType } from './exercise';
// ============================================
// MODULE
// ============================================
export interface Module {
id: string;
name: string;
description: string;
type: ModuleType;
order: number;
introduction?: string;
examples?: ModuleExample[];
exercises?: ModuleExerciseRef[];
answers?: ModuleAnswer[];
}
export interface ModuleExample {
title: string;
content: string;
latexFormula?: string;
explanation: string;
difficulty?: 'EASY' | 'MEDIUM' | 'HARD';
}
export interface ModuleExerciseRef {
id: string;
section: string;
content: string;
problems: {
id: string;
statement: string;
type: string;
}[];
}
export interface ModuleAnswer {
id: string;
exerciseId: string;
content: string;
steps: string[];
}
// ============================================
// TOPIC
// ============================================
export interface Topic {
id: string;
moduleId: string;
name: string;
type: TopicType;
theoryContent?: TheoryContent;
formulas?: Formula[];
}
export interface TheoryContent {
title: string;
description: string;
sections: TheorySection[];
}
export interface TheorySection {
title: string;
content: string;
formulas?: string[];
}
export interface Formula {
id: string;
name: string;
latex: string;
description?: string;
}
// ============================================
// THEORY CONTENT (Detailed)
// ============================================
export interface TheoryContentDetailed {
concept: string;
explanation: string;
formulas: LatexFormula[];
examples: ModuleExample[];
}
export interface LatexFormula {
name: string;
latex: string;
description: string;
category?: string;
}
export interface KeyPoint {
title: string;
explanation: string;
latex?: string;
}
export interface CommonMistake {
mistake: string;
correction: string;
explanation: string;
}

View File

@@ -0,0 +1,88 @@
/**
* ============================================
* PROGRESS TYPES
* ============================================
*
* Types for user progress tracking
* shared between frontend and backend
*/
// ============================================
// USER PROGRESS
// ============================================
export interface UserProgress {
userId: string;
moduleId: string;
exercisesCompleted: number;
totalExercises: number;
points: number;
percentage: number;
isStarted: boolean;
isCompleted: boolean;
lastActivityAt: string;
}
// ============================================
// MODULE PROGRESS
// ============================================
export interface ModuleProgress {
moduleId: string;
moduleName: string;
isStarted: boolean;
isCompleted: boolean;
startedAt?: string;
completedAt?: string;
lastAccessedAt?: string;
metrics: ProgressMetrics;
}
export interface ProgressMetrics {
exercisesCompleted: number;
totalExercises: number;
points: number;
percentage: number;
averageScore?: number;
totalTimeSpent: number;
perfectExercises: number;
attemptsCount: number;
}
// ============================================
// USER STATISTICS
// ============================================
export interface UserStatistics {
totalExercisesCompleted: number;
totalPoints: number;
averageScore: number;
totalStudyTime: number;
currentStreak: number;
longestStreak: number;
modulesCompleted: number;
achievementsUnlocked: number;
rankGlobal: number | null;
}
// ============================================
// ACTIVITY
// ============================================
export interface Activity {
id: string;
userId: string;
type: ActivityType;
description: string;
metadata?: Record<string, unknown>;
createdAt: string;
}
export type ActivityType =
| 'EXERCISE_COMPLETED'
| 'EXERCISE_ATTEMPTED'
| 'MODULE_STARTED'
| 'MODULE_COMPLETED'
| 'ACHIEVEMENT_UNLOCKED'
| 'LOGIN'
| 'LOGOUT';

View File

@@ -0,0 +1,64 @@
/**
* ============================================
* RANKING TYPES
* ============================================
*
* Types for leaderboards and rankings
* shared between frontend and backend
*/
import { AchievementBadge } from './achievement';
// ============================================
// RANKING ENTRY
// ============================================
export interface RankingEntry {
position: number;
userId?: string;
username?: string;
points: number;
exercisesCompleted: number;
streak: number;
perfectExercises?: number;
averageScore?: number;
achievementsUnlocked: number;
badges?: AchievementBadge[];
}
// ============================================
// GLOBAL RANKING
// ============================================
export interface GlobalRanking {
global: RankingEntry[];
byModule: Record<string, RankingEntry[]>;
myPosition: {
global: RankingEntry | null;
byModule: Record<string, RankingEntry | null>;
};
}
// ============================================
// USER RANKING POSITION
// ============================================
export interface UserRankingPosition {
global: RankingEntry;
byModule: {
moduleId: string;
moduleName: string;
position: number;
}[];
}
// ============================================
// RANKING FILTERS
// ============================================
export interface RankingFilters {
moduleId?: string;
timeRange?: 'ALL_TIME' | 'WEEKLY' | 'MONTHLY';
page?: number;
limit?: number;
}

53
shared/types/src/utils.ts Normal file
View File

@@ -0,0 +1,53 @@
/**
* ============================================
* UTILITY TYPES
* ============================================
*
* Generic utility types used across the application
*/
// ============================================
// BASIC UTILITIES
// ============================================
export type WithId<T> = T & { id: string };
export type WithTimestamps<T> = T & {
createdAt: string;
updatedAt: string;
};
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
export type Nullable<T> = T | null;
export type Optional<T> = T | undefined;
// ============================================
// STRING UTILITIES
// ============================================
export type UUID = string;
export type ISO8601Date = string;
export type Email = string;
// ============================================
// FUNCTION UTILITIES
// ============================================
export type AsyncReturnType<T extends (...args: any[]) => Promise<any>> =
T extends (...args: any[]) => Promise<infer R> ? R : never;
export type NullableKeys<T, K extends keyof T> = Omit<T, K> & {
[P in K]: T[P] | null;
};
// ============================================
// API UTILITIES
// ============================================
export type EmptyObject = Record<string, never>;
export type StringRecord = Record<string, string>;

View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022"],
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": false
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}