feat: fase 2+3 — expansion masiva multi-perfil + bug fixes + hardening

Fase 2 (expansion):
- Francesca: +6 etapas matematicas (5-10), +7 etapas lectura (3-9), +2 ortografia
- Sebastian: +8 etapas matematicas (4-11), +6 lectura, +3 historia, +3 geografia, +1 banderas
- Nuevas paginas: ortografia, lectura, geografia, matematicas
- ~900 ejercicios nuevos, paridad ~500 por perfil

Fase 3 (bug fixes + hardening):
- Fix: SessionLog auto-create en grade route
- Fix: engine cache (no borrar antes de revisar)
- Fix: FSRS cards actualizadas en flujo curriculum
- Fix: responseMs real (no hardcodeado 3000)
- Fix: options dentro de content en sebastian etapa-1
- Fix: trazo-letra consonantes crash
- Security: execFileSync anti-command-injection en TTS
- DB: 18 cascade deletes + indices + unique constraints
- APIs debug: reset, state, seed
- Docker: espeak-ng + prisma + user/group
- E2E: 23 tests nuevos (curriculum-flow.spec.ts)
- Code review: tailwind colors, middleware dev mode, eslint config

Verificado: tsc exit 0, build exit 0, 61 E2E tests pass
This commit is contained in:
renato97
2026-07-25 22:07:28 -03:00
parent 25614704dc
commit a3360ec6e4
67 changed files with 5294 additions and 213 deletions
+33 -18
View File
@@ -23,7 +23,7 @@ model Parent {
updatedAt DateTime @updatedAt
familyId String?
family Family? @relation(fields: [familyId], references: [id])
family Family? @relation(fields: [familyId], references: [id], onDelete: Cascade)
sessions Session[]
accounts Account[]
@@ -34,7 +34,7 @@ model Session {
email String?
userId String?
parentId String?
parent Parent? @relation(fields: [parentId], references: [id])
parent Parent? @relation(fields: [parentId], references: [id], onDelete: Cascade)
token String @unique
expiresAt DateTime
ipAddress String?
@@ -48,7 +48,7 @@ model Account {
accountId String
providerId String
userId String
user Parent @relation(fields: [userId], references: [id])
user Parent @relation(fields: [userId], references: [id], onDelete: Cascade)
accessToken String?
refreshToken String?
idToken String?
@@ -74,8 +74,8 @@ model Family {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
parents Parent[]
children Child[]
parents Parent[] @relation(onDelete: Cascade)
children Child[] @relation(onDelete: Cascade)
}
model Child {
@@ -89,13 +89,13 @@ model Child {
updatedAt DateTime @updatedAt
familyId String
family Family @relation(fields: [familyId], references: [id])
family Family @relation(fields: [familyId], references: [id], onDelete: Cascade)
devices Device[]
sessions SessionLog[]
fsrsCards FsrsCard[]
milestones Milestone[]
curriculumProgress CurriculumProgress[]
devices Device[] @relation(onDelete: Cascade)
sessions SessionLog[] @relation(onDelete: Cascade)
fsrsCards FsrsCard[] @relation(onDelete: Cascade)
milestones Milestone[] @relation(onDelete: Cascade)
curriculumProgress CurriculumProgress[] @relation(onDelete: Cascade)
}
model Device {
@@ -107,7 +107,10 @@ model Device {
createdAt DateTime @default(now())
childId String
child Child @relation(fields: [childId], references: [id])
child Child @relation(fields: [childId], references: [id], onDelete: Cascade)
@@index([childId])
@@index([deviceFingerprint])
}
model SessionLog {
@@ -118,9 +121,11 @@ model SessionLog {
skillsPracticed String?
childId String
child Child @relation(fields: [childId], references: [id])
child Child @relation(fields: [childId], references: [id], onDelete: Cascade)
skillAttempts SkillAttempt[]
skillAttempts SkillAttempt[] @relation(onDelete: Cascade)
@@index([childId])
}
model SkillAttempt {
@@ -135,7 +140,10 @@ model SkillAttempt {
createdAt DateTime @default(now())
sessionLogId String
sessionLog SessionLog @relation(fields: [sessionLogId], references: [id])
sessionLog SessionLog @relation(fields: [sessionLogId], references: [id], onDelete: Cascade)
@@index([sessionLogId])
@@index([skillCode])
}
model CurriculumProgress {
@@ -146,9 +154,10 @@ model CurriculumProgress {
completedAt DateTime?
childId String
child Child @relation(fields: [childId], references: [id])
child Child @relation(fields: [childId], references: [id], onDelete: Cascade)
@@unique([childId, topic, stage])
@@index([childId])
}
model FsrsCard {
@@ -163,7 +172,10 @@ model FsrsCard {
updatedAt DateTime @updatedAt
childId String
child Child @relation(fields: [childId], references: [id])
child Child @relation(fields: [childId], references: [id], onDelete: Cascade)
@@unique([childId, skillCode])
@@index([childId])
}
model Milestone {
@@ -172,9 +184,10 @@ model Milestone {
reachedAt DateTime @default(now())
childId String
child Child @relation(fields: [childId], references: [id])
child Child @relation(fields: [childId], references: [id], onDelete: Cascade)
@@unique([childId, skillCode])
@@index([childId])
}
model PendingPairing {
@@ -184,4 +197,6 @@ model PendingPairing {
childId String?
createdAt DateTime @default(now())
expiresAt DateTime
@@index([expiresAt])
}