Centro de familias — Referencia técnica
Dónde vive esto
Backend
apps/backend/graphql/types/family-supervision.type.js— tiposFamilySupervision,ChildTimeLimitResult,ChildUsageStats/ChildUsageDay, y los campos de query/mutation de abajo. Reutiliza el tipo globalUsery el escalarDateTimeya declarados en otro lugar.apps/backend/graphql/resolvers/family-supervision.resolver.js— solo verifica auth y delega al manager; no necesita registro explícito (graphql/resolvers.js/typeDefs.jsdescubren automáticamente cada archivo bajo sus directorios víaloadFilesSync).apps/backend/managers/user-managers/family-supervision.manager.js— toda la lógica de negocio: invitar/aceptar/rechazar por consentimiento mutuo, terminar por cualquiera de las dos partes, y autorización de solo-el-padre para los datos de tiempo en pantalla del hijo. Delega el límite de tiempo en pantalla en sí ausage-tracking.manager.js(la funcionalidad ya existente de "Gestión del tiempo", JSONBuser.settings.time_management) en lugar de guardar una copia duplicada — este manager solo agrega la verificación de autorización de que quien llama es el padre activo del vínculo antes de leer/escribir la configuración de otra persona.apps/backend/data-access-services/user/family-supervision.access-service.js— envoltorio de Sequelize sobre la tablafamily_supervision.apps/backend/database/models/FamilySupervision.js/apps/backend/database/migrations/20260802130000-create-family-supervision.js— crea la tablafamily_supervision(ver Modelo de datos abajo). Ejecutanpx sequelize-cli db:migratedespués de actualizar el código si aún no lo has hecho — esta migración no se aplica automáticamente en todos los entornos.
Frontend — apps/frontend-nextjs/src/page-components/settings/FamilyCenterPage.tsx, enrutado en Configuración → Centro de familias (app/settings/family-center/page.tsx, enlazado desde la sección "Aplicación y contenido multimedia" del menú de configuración). Usa los documentos generados de @closegram/apollo-web/operations a partir de packages/graphql/operations/Web/Family/*.graphql — ejecuta npm run build -w @closegram/apollo-web después de actualizar el código si ves errores de exportación faltante en estos imports; ese paso de build (que ejecuta codegen) debe volver a ejecutarse cada vez que se agregan archivos .graphql nuevos, y no es automático.
Checklist de implementación técnica
-
requestFamilySupervision(targetUsername, requestingUserRole)— cualquiera de los dos usuarios puede invitar;requestingUserRole('parent' | 'child') es el rol que quien llama quiere jugar. Rechaza auto-invitaciones y un vínculo pendiente/activo duplicado entre el mismo par (verificado en ambas direcciones). Envía una notificaciónfamily_supervision_inviteal usuario invitado. -
respondToFamilySupervision(linkId, accept)— solo la parte invitada (no quien invitó) puede responder, y solo mientras el vínculo sigapending. Notifica a quien invitó del resultado. -
endFamilySupervision(linkId)— cualquiera de las dos partes puede terminar un vínculo activo o cancelar uno pendiente, en cualquier momento (una decisión de producto deliberada — la supervisión siempre es revocable por ambos lados, no solo por el padre). Es idempotente para un vínculo ya terminado/rechazado. Notifica a la otra parte solo cuando termina un vínculo que estaba realmenteactive. -
mySupervisionAsParent/mySupervisionAsChild— vínculos donde quien llama es el padre/hijo respectivamente, cualquier estado, más recientes primero. -
setChildTimeLimit(linkId, minutes)— quien llama debe ser el padre activo dellinkId; delega enusage-tracking.manager.js#updateTimeManagementSettings. Pasaminutes: nullpara borrar el límite. -
childUsageStats(linkId, days)— de solo lectura, solo para el padre activo; combinausage-tracking.manager.js#getUsageStatscon eldailyLimitMinutesactual.
Referencia de GraphQL
type FamilySupervision {
id: ID!
parent: User
child: User
status: String! # pending | active | declined | ended
requestedByUserId: ID!
respondedAt: DateTime
endedAt: DateTime
createdAt: DateTime!
updatedAt: DateTime
}
query MySupervision {
mySupervisionAsParent { id status child { id username } }
mySupervisionAsChild { id status parent { id username } }
}
mutation RequestSupervision($targetUsername: String!, $requestingUserRole: String!) {
requestFamilySupervision(targetUsername: $targetUsername, requestingUserRole: $requestingUserRole) {
id status
}
}
mutation RespondToSupervision($linkId: ID!, $accept: Boolean!) {
respondToFamilySupervision(linkId: $linkId, accept: $accept) { id status }
}
mutation EndSupervision($linkId: ID!) {
endFamilySupervision(linkId: $linkId) { id status endedAt }
}
mutation SetLimit($linkId: ID!, $minutes: Int) {
setChildTimeLimit(linkId: $linkId, minutes: $minutes) {
linkId childUserId dailyLimitMinutes
}
}
query ChildUsage($linkId: ID!, $days: Int) {
childUsageStats(linkId: $linkId, days: $days) {
linkId userId days dailyAverageSeconds todaySeconds dailyLimitMinutes
series { date totalSeconds }
}
}
Modelo de datos
La tabla family_supervision guarda una fila por vínculo (consentimiento mutuo, en cualquier dirección):
| Columna | Tipo | Notas |
|---|---|---|
id | UUID (PK) | |
parent_user_id, child_user_id | UUID | qué lado es "el padre" se decide en el momento de la invitación por requestSupervision, no está fijado por quién inició |
status | STRING(20) | pending (por defecto) → active (aceptado) / declined, o active → ended |
requested_by_user_id | UUID | cuál de los dos usuarios envió la invitación — el otro debe responder |
responded_at, ended_at | DATE | anulable |
ended_by_user_id | UUID | anulable |
created_at, updated_at | DATE |
Indexada por parent_user_id, child_user_id, y (parent_user_id, child_user_id, status) (acelera la verificación de invitación duplicada).
El límite de tiempo en pantalla en sí no se guarda en esta tabla — vive en el propio JSONB user.settings.time_management del hijo (ver la funcionalidad de Gestión del tiempo), y esta funcionalidad solo agrega la verificación de autorización que controla que el padre vinculado pueda leerlo o escribirlo.